66from shutil import rmtree
77import delegator
88import json
9+ from functools import lru_cache
10+ from enum import Enum
11+ import pathlib
912
1013log = logging .getLogger (__name__ )
1114
@@ -15,6 +18,7 @@ class CloneWorkspace:
1518 def __init__ (self , fs : FileSystem , project_root : str ,
1619 original_root_path : str = "" ):
1720
21+ self .fs = fs
1822 self .PROJECT_ROOT = project_root
1923 self .repo = None
2024 self .hash = None
@@ -35,40 +39,39 @@ def __init__(self, fs: FileSystem, project_root: str,
3539 if self .hash :
3640 self .key = "." .join ((self .key , self .hash ))
3741
38- # TODO: allow different Python versions per project/workspace
39- self .PYTHON_PATH = GlobalConfig .PYTHON_PATH
42+ self .PYTHON_PATH = os .path .abspath (GlobalConfig .PYTHON_PATH )
4043 self .CLONED_PROJECT_PATH = os .path .abspath (os .path .join (
4144 GlobalConfig .CLONED_PROJECT_PATH , self .key ))
45+
4246 log .debug ("Setting Python path to %s" , self .PYTHON_PATH )
4347 log .debug ("Setting Cloned Project path to %s" ,
4448 self .CLONED_PROJECT_PATH )
4549
46- self .fs = fs
47-
4850 # Clone the project from the provided filesystem into the local
4951 # cache
5052 all_files = self .fs .walk (self .PROJECT_ROOT )
5153 for file_path in all_files :
52- cache_file_path = self .to_cache_path (file_path )
53- if os .path .exists (cache_file_path ):
54- continue
54+ cache_file_path = self .project_to_cache_path (file_path )
5555
5656 os .makedirs (os .path .dirname (cache_file_path ), exist_ok = True )
5757 file_contents = self .fs .open (file_path )
5858 with open (cache_file_path , "w" ) as f :
5959 f .write (file_contents )
6060
61+ @property
62+ @lru_cache ()
63+ def VENV_LOCATION (self ):
6164 self .ensure_venv_created ()
62- self . VENV_LOCATION = self .run_command ("pipenv --venv" ).out
65+ return self .run_command ("pipenv --venv" ).out . rstrip ()
6366
6467 def cleanup (self ):
65- log .info ("Removing project's virtual emvironment %s" , self .VENV_LOCATION )
68+ log .info ("Removing project's virtual environment %s" , self .VENV_LOCATION )
6669 self .remove_venv ()
6770
6871 log .info ("Removing cloned project cache %s" , self .CLONED_PROJECT_PATH )
6972 rmtree (self .CLONED_PROJECT_PATH , ignore_errors = True )
7073
71- def to_cache_path (self , project_path ):
74+ def project_to_cache_path (self , project_path ):
7275 """
7376 Translates a path from the root of the project to the equivalent path in
7477 the local cache.
@@ -80,7 +83,7 @@ def to_cache_path(self, project_path):
8083
8184 return os .path .join (self .CLONED_PROJECT_PATH , file_path )
8285
83- def from_cache_path (self , cache_path ):
86+ def cache_path_to_project_path (self , cache_path ):
8487 """
8588 Translates a path in the cache to the equivalent path in
8689 the project.
@@ -100,7 +103,37 @@ def ensure_venv_created(self):
100103 self .run_command ("true" )
101104
102105 def remove_venv (self ):
103- self .run_command ("pipenv --rm" )
106+ self .run_command ("pipenv --rm" , no_prefix = True )
107+
108+ def get_module_info (self , raw_module_path ):
109+ module_path = pathlib .Path (raw_module_path )
110+
111+ import pdb
112+ pdb .set_trace ()
113+
114+ sys_std_lib_path = pathlib .Path (self .PYTHON_PATH )
115+ if sys_std_lib_path in module_path .parents :
116+ return (ModuleKind .STANDARD_LIBRARY , path .relative_to (sys_std_lib_path ))
117+
118+ venv_path = pathlib .Path (self .VENV_LOCATION ) / "lib"
119+ if venv_path in module_path .parents :
120+ # The python libraries in a venv are stored under
121+ # VENV_LOCATION/lib/(some_python_version)
122+
123+ python_version = module_path .relative_to (venv_path ).parts [0 ]
124+
125+ venv_lib_path = venv_path / python_version
126+ venv_ext_packages_path = venv_lib_path / "site-packages"
127+
128+ if venv_ext_packages_path in module_path .parents :
129+ return (ModuleKind .EXTERNAL_DEPENDENCY , module_path .relative_to (venv_ext_packages_path ))
130+ return (ModuleKind .STANDARD_LIBRARY , module_path .relative_to (venv_lib_path ))
131+
132+ project_path = pathlib .Path (self .CLONED_PROJECT_PATH )
133+ if project_path in module_path .parents :
134+ return (ModuleKind .PROJECT , module_path .relative_to (project_path ))
135+
136+ return (ModuleKind .UNKNOWN , module_path )
104137
105138 def get_package_information (self ):
106139 project_packages = self .project_packages ()
@@ -116,6 +149,7 @@ def get_package_information(self):
116149 })
117150 return out
118151
152+ @lru_cache ()
119153 def project_packages (self ):
120154 '''
121155 Provides a list of all packages declared in the project
@@ -150,11 +184,11 @@ def external_dependencies(self):
150184 for dep in deps :
151185 dep_name = dep ["name" ]
152186 if dep_name not in set (["pip" , "wheel" ]):
153- out .append ({"attributes" : {"name" : dep [ "name" ] }})
187+ out .append ({"attributes" : {"name" : dep_name }})
154188
155189 return out
156190
157- def run_command (self , command , ** kwargs ):
191+ def run_command (self , command , no_prefix = False , ** kwargs ):
158192 '''
159193 Runs the given command inside the context
160194 of the project:
@@ -167,9 +201,17 @@ def run_command(self, command, **kwargs):
167201 kwargs ["cwd" ] = self .CLONED_PROJECT_PATH
168202
169203 # add pipenv prefix
170- if type (command ) is str :
171- command = "pipenv run {}" .format (command )
172- else :
173- command = ["pipenv" , "run" ].append (command )
204+ if not no_prefix :
205+ if type (command ) is str :
206+ command = "pipenv run {}" .format (command )
207+ else :
208+ command = ["pipenv" , "run" ].append (command )
174209
175210 return delegator .run (command , ** kwargs )
211+
212+
213+ class ModuleKind (Enum ):
214+ PROJECT = 1
215+ STANDARD_LIBRARY = 2
216+ EXTERNAL_DEPENDENCY = 3
217+ UNKNOWN = 4
0 commit comments