Skip to content

Commit 387e9e7

Browse files
committed
Update to use pip inspect to get pypi oss info
Signed-off-by: 석지영/책임연구원/SW공학(연)Open Source TP <[email protected]>
1 parent ad1e557 commit 387e9e7

File tree

2 files changed

+91
-95
lines changed

2 files changed

+91
-95
lines changed

src/fosslight_dependency/package_manager/Npm.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ def parse_oss_information(self, f_name):
178178

179179
oss_item.download_location = f"{self.dn_url}{oss_init_name}/v/{oss_item.version}"
180180
dn_loc = f"{self.dn_url}{oss_init_name}"
181-
dep_item.purl = get_url_to_purl(dn_loc, self.package_manager_name)
181+
dep_item.purl = get_url_to_purl(oss_item.download_location, self.package_manager_name)
182182
purl_dict[f'{oss_init_name}({oss_item.version})'] = dep_item.purl
183183
if d[_repository]:
184184
dn_loc = d[_repository]

src/fosslight_dependency/package_manager/Pypi.py

Lines changed: 90 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import fosslight_util.constant as constant
1414
import fosslight_dependency.constant as const
1515
from 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
1717
from fosslight_dependency.dependency_item import DependencyItem, change_dependson_to_purl
1818
from 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

Comments
 (0)