1313import fosslight_util .constant as constant
1414import fosslight_dependency .constant as const
1515from fosslight_dependency ._package_manager import PackageManager
16- from fosslight_dependency ._package_manager import check_license_name , get_url_to_purl
16+ from fosslight_dependency ._package_manager import get_url_to_purl
1717from fosslight_dependency .dependency_item import DependencyItem , change_dependson_to_purl
1818from fosslight_util .oss_item import OssItem
1919
@@ -26,7 +26,6 @@ class Pypi(PackageManager):
2626 dn_url = 'https://pypi.org/project/'
2727 venv_tmp_dir = 'venv_osc_dep_tmp'
2828 tmp_file_name = "tmp_pip_license_output.json"
29- tmp_pip_license_info_file_name = "tmp_pip_license_info_output.json"
3029 tmp_deptree_file = "tmp_pipdeptree.json"
3130 pip_activate_cmd = ''
3231 pip_deactivate_cmd = ''
@@ -41,9 +40,6 @@ def __del__(self):
4140 if os .path .isfile (self .tmp_file_name ):
4241 os .remove (self .tmp_file_name )
4342
44- if os .path .isfile (self .tmp_pip_license_info_file_name ):
45- os .remove (self .tmp_pip_license_info_file_name )
46-
4743 shutil .rmtree (self .venv_tmp_dir , ignore_errors = True )
4844
4945 if os .path .isfile (self .tmp_deptree_file ):
@@ -73,7 +69,7 @@ def run_plugin(self):
7369 ret = self .create_virtualenv ()
7470
7571 if ret :
76- ret = self .start_pip_licenses ()
72+ ret = self .start_pip_inspect ()
7773
7874 return ret
7975
@@ -103,7 +99,7 @@ def create_virtualenv(self):
10399 activate_cmd = os .path .join (self .venv_tmp_dir , "Scripts" , "activate.bat" )
104100 cmd_separator = "&"
105101 else :
106- create_venv_cmd = f"python3 -m venv { self .venv_tmp_dir } "
102+ create_venv_cmd = f"virtualenv -p python3 { self .venv_tmp_dir } "
107103 activate_cmd = ". " + os .path .join (venv_path , "bin" , "activate" )
108104 cmd_separator = ";"
109105
@@ -117,11 +113,12 @@ def create_virtualenv(self):
117113 return False
118114
119115 deactivate_cmd = "deactivate"
116+ pip_upgrade_cmd = "pip install --upgrade pip"
120117
121118 self .set_pip_activate_cmd (activate_cmd )
122119 self .set_pip_deactivate_cmd (deactivate_cmd )
123120
124- cmd_list = [create_venv_cmd , activate_cmd , install_cmd , deactivate_cmd ]
121+ cmd_list = [create_venv_cmd , activate_cmd , install_cmd , pip_upgrade_cmd , deactivate_cmd ]
125122 cmd = cmd_separator .join (cmd_list )
126123
127124 try :
@@ -139,9 +136,9 @@ def create_virtualenv(self):
139136 try :
140137 if (not ret ) and (self .platform != const .WINDOWS ):
141138 ret = True
142- create_venv_cmd = f"virtualenv -p python3 { self .venv_tmp_dir } "
139+ create_venv_cmd = f"python3 -m venv { self .venv_tmp_dir } "
143140
144- cmd_list = [create_venv_cmd , activate_cmd , install_cmd , deactivate_cmd ]
141+ cmd_list = [create_venv_cmd , activate_cmd , install_cmd , pip_upgrade_cmd , deactivate_cmd ]
145142 cmd = cmd_separator .join (cmd_list )
146143 cmd_ret = subprocess .run (cmd , shell = True , stderr = subprocess .PIPE )
147144 if cmd_ret .returncode != 0 :
@@ -160,14 +157,9 @@ def create_virtualenv(self):
160157
161158 return ret
162159
163- def start_pip_licenses (self ):
160+ def start_pip_inspect (self ):
164161 ret = True
165- pip_licenses = 'pip-licenses'
166- prettytable = 'prettytable'
167- wcwidth = 'wcwidth'
168162 pipdeptree = 'pipdeptree'
169- pip_licenses_default_options = ' --from=mixed --with-url --format=json --with-license-file'
170- pip_licenses_system_option = ' --with-system -p '
171163 tmp_pip_list = "tmp_list.txt"
172164 python_cmd = "python -m"
173165
@@ -193,11 +185,21 @@ def start_pip_licenses(self):
193185 command_list = [activate_command , pip_list_command , deactivate_command ]
194186 command = command_separator .join (command_list )
195187
188+ exists_pipdeptree = False
196189 try :
197190 cmd_ret = subprocess .call (command , shell = True )
198191 if cmd_ret != 0 :
199192 ret = False
200193 err_msg = f"cmd ret code({ cmd_ret } )"
194+ else :
195+ if os .path .isfile (tmp_pip_list ):
196+ with open (tmp_pip_list , 'r' , encoding = 'utf-8' ) as pip_list_file :
197+ for pip_list in pip_list_file .readlines ():
198+ pip_list_name = pip_list .split ('==' )[0 ]
199+ if pip_list_name == pipdeptree :
200+ exists_pipdeptree = True
201+ break
202+ os .remove (tmp_pip_list )
201203 except Exception as e :
202204 ret = False
203205 err_msg = str (e )
@@ -206,94 +208,49 @@ def start_pip_licenses(self):
206208 logger .error (f"Failed to freeze dependencies ({ command } ): { err_msg } )" )
207209 return False
208210
209- exists_pip_licenses = False
210- exists_prettytable = False
211- exists_wcwidth = False
212- pip_license_pkg_list = []
213- uninstall_pkg_list = []
214- exists_pipdeptree = False
215-
216- if os .path .isfile (tmp_pip_list ):
217- try :
218- with open (tmp_pip_list , 'r' , encoding = 'utf-8' ) as pip_list_file :
219- for pip_list in pip_list_file .readlines ():
220- pip_list_name = pip_list .split ('==' )[0 ]
221- if pip_list_name == pip_licenses :
222- exists_pip_licenses = True
223- if pip_list_name == prettytable :
224- exists_prettytable = True
225- if pip_list_name == wcwidth :
226- exists_wcwidth = True
227- if pip_list_name == pipdeptree :
228- exists_pipdeptree = True
229- os .remove (tmp_pip_list )
230- except Exception as e :
231- logger .error (f"Failed to read freezed package list file: { e } " )
232- return False
233- if exists_pip_licenses :
234- pip_license_pkg_list .append (pip_licenses )
235- else :
236- uninstall_pkg_list .append (pip_licenses )
237- if exists_prettytable :
238- pip_license_pkg_list .append (prettytable )
239- else :
240- uninstall_pkg_list .append (prettytable )
241- if exists_wcwidth :
242- pip_license_pkg_list .append (wcwidth )
243- else :
244- uninstall_pkg_list .append (wcwidth )
245-
246211 command_list = []
247212 command_list .append (activate_command )
248- if not exists_pip_licenses :
249- install_pip_command = f"{ python_cmd } pip install { pip_licenses } "
250- command_list .append (install_pip_command )
251-
252- pip_licenses_command = f"{ pip_licenses } { pip_licenses_default_options } > { self .tmp_file_name } "
253- command_list .append (pip_licenses_command )
254-
255- if len (pip_license_pkg_list ) != 0 :
256- pip_licenses_info_command = f"{ pip_licenses } { pip_licenses_default_options } { pip_licenses_system_option } "
257- pip_licenses_info_command += " " .join (pip_license_pkg_list )
258213
259- pip_licenses_info_command += f" > { self .tmp_pip_license_info_file_name } "
260- command_list .append (pip_licenses_info_command )
261-
262- if len (uninstall_pkg_list ) > 0 :
263- uninstall_pip_command = f"{ python_cmd } pip uninstall -y "
264- uninstall_pip_command += ' ' .join (uninstall_pkg_list )
265- command_list .append (uninstall_pip_command )
214+ pip_inspect_command = f"{ python_cmd } pip inspect > { self .tmp_file_name } "
215+ command_list .append (pip_inspect_command )
266216
267217 if not exists_pipdeptree :
268218 install_deptree_command = f"{ python_cmd } pip install { pipdeptree } "
269219 command_list .append (install_deptree_command )
270220 uninstall_deptree_command = f"{ python_cmd } pip uninstall -y { pipdeptree } "
271221 pipdeptree_command = f"{ pipdeptree } --json-tree -e 'pipdeptree,pip,wheel,setuptools' > { self .tmp_deptree_file } "
272222 command_list .append (pipdeptree_command )
273- command_list .append (uninstall_deptree_command )
223+
224+ if not exists_pipdeptree :
225+ command_list .append (uninstall_deptree_command )
226+
274227 command_list .append (deactivate_command )
275228 command = command_separator .join (command_list )
276229
277230 try :
278231 cmd_ret = subprocess .call (command , shell = True )
279232 if cmd_ret == 0 :
280- self .append_input_package_list_file (self .tmp_file_name )
281- with open (self .tmp_file_name , 'r' , encoding = 'utf-8' ) as json_f :
282- json_data = json .load (json_f )
283- for d in json_data :
284- self .total_dep_list .append (re .sub (r"[-_.]+" , "-" , d ['Name' ]).lower ())
285- if len (pip_license_pkg_list ) != 0 :
286- self .append_input_package_list_file (self .tmp_pip_license_info_file_name )
287- with open (self .tmp_pip_license_info_file_name , 'r' , encoding = 'utf-8' ) as json_f :
288- json_data = json .load (json_f )
289- for d in json_data :
290- self .total_dep_list .append (re .sub (r"[-_.]+" , "-" , d ['Name' ]).lower ())
233+ if os .path .exists (self .tmp_file_name ):
234+ self .append_input_package_list_file (self .tmp_file_name )
235+
236+ with open (self .tmp_file_name , 'r' , encoding = 'utf-8' ) as json_f :
237+ inspect_data = json .load (json_f )
238+ for package in inspect_data .get ('installed' , []):
239+ metadata = package .get ('metadata' , {})
240+ package_name = metadata .get ('name' , '' )
241+ if package_name :
242+ if package_name in ['pip' , 'setuptools' ]:
243+ continue
244+ self .total_dep_list .append (re .sub (r"[-_.]+" , "-" , package_name ).lower ())
245+ else :
246+ logger .error (f"pip inspect output file not found: { self .tmp_file_name } " )
247+ ret = False
291248 else :
292249 logger .error (f"Failed to run command: { command } " )
293250 ret = False
294251 except Exception as e :
295252 ret = False
296- logger .error (f"Failed to install/uninstall pypi packages : { e } " )
253+ logger .error (f"Failed to get package information using pip inspect : { e } " )
297254
298255 return ret
299256
@@ -302,25 +259,64 @@ def parse_oss_information(self, f_name):
302259 try :
303260 oss_init_name = ''
304261 with open (f_name , 'r' , encoding = 'utf-8' ) as json_file :
305- json_data = json .load (json_file )
262+ inspect_data = json .load (json_file )
306263
307- for d in json_data :
264+ for package in inspect_data . get ( 'installed' , []) :
308265 dep_item = DependencyItem ()
309266 oss_item = OssItem ()
310- oss_init_name = d ['Name' ]
267+ metadata = package .get ('metadata' , {})
268+ if not metadata :
269+ continue
270+
271+ oss_init_name = metadata .get ('name' , '' )
311272 oss_init_name = re .sub (r"[-_.]+" , "-" , oss_init_name ).lower ()
273+ if oss_init_name not in self .total_dep_list :
274+ continue
312275 oss_item .name = f"{ self .package_manager_name } :{ oss_init_name } "
313- license_name = check_UNKNOWN (d ['License' ])
314- oss_item .homepage = check_UNKNOWN (d ['URL' ])
315- oss_item .version = d ['Version' ]
276+ oss_item .version = metadata .get ('version' , '' )
277+
278+ # license_expression > license > classifier
279+ license_info = metadata .get ('license_expression' , '' )
280+ if not license_info :
281+ license_info = metadata .get ('license' , '' )
282+ if not license_info :
283+ classifiers = metadata .get ('classifier' , [])
284+ license_classifiers = [c for c in classifiers if c .startswith ('License ::' )]
285+ if license_classifiers :
286+ license_info_l = []
287+ for license_classifier in license_classifiers :
288+ if license_classifier .startswith ('License :: OSI Approved ::' ):
289+ license_info_l .append (license_classifier .split ('::' )[- 1 ].strip ())
290+ break
291+ license_info = ',' .join (license_info_l )
292+ license_name = check_UNKNOWN (license_info )
293+ if license_name :
294+ license_name = license_name .replace (';' , ',' )
295+ oss_item .license = license_name
296+
297+ # project_url 'source' > download_url > project_url 'homepage' > homepage
298+ homepage_url = metadata .get ('home_page' , '' )
299+ download_url = metadata .get ('download_url' , '' )
300+ project_urls = metadata .get ('project_url' , [])
301+ if project_urls :
302+ for url_entry in project_urls :
303+ url_entry_lower = url_entry .lower ()
304+ if 'source' in url_entry_lower :
305+ download_url = url_entry .split (', ' )[- 1 ]
306+ elif 'homepage' in url_entry_lower :
307+ homepage_url = url_entry .split (', ' )[- 1 ]
308+ oss_item .homepage = download_url or homepage_url
316309 oss_item .download_location = f"{ self .dn_url } { oss_init_name } /{ oss_item .version } "
310+
317311 dep_item .purl = get_url_to_purl (oss_item .download_location , self .package_manager_name )
318312 purl_dict [f'{ oss_init_name } ({ oss_item .version } )' ] = dep_item .purl
319- if license_name is not None :
320- license_name = license_name .replace (';' , ',' )
321- else :
322- license_name = check_license_name (d ['LicenseFile' ], True )
323- oss_item .license = license_name
313+
314+ direct_url = package .get ('direct_url' , {})
315+ if direct_url :
316+ oss_item .download_location = direct_url .get ('url' , '' )
317+ oss_item .homepage = oss_item .download_location
318+ if not package .get ('installer' , '' ):
319+ oss_item .download_location = oss_item .homepage
324320
325321 if oss_init_name == self .package_name :
326322 oss_item .comment = 'root package'
0 commit comments