Может ли pip (или setuptools, распространять и т.д.) Отображать лицензию, используемую каждым установленным пакетом?
Я пытаюсь выполнить аудит проекта Python с большим количеством зависимостей, и, хотя я могу вручную искать каждую главную страницу проекта/условия лицензии, похоже, что большинство пакетов OSS уже должны содержать имя и версию лицензии в своих метаданных.
К сожалению, я не могу найти какие-либо опции в pip или easy_install, чтобы перечислять больше, чем имя пакета и установленную версию (через зависание pip).
Есть ли у кого-нибудь указатели на инструмент для отображения метаданных лицензии для пакетов Python?
Ответы
Ответ 1
Вы можете использовать pkg_resources
:
import pkg_resources
def get_pkg_license(pkgname):
"""
Given a package reference (as from requirements.txt),
return license listed in package metadata.
NOTE: This function does no error checking and is for
demonstration purposes only.
"""
pkgs = pkg_resources.require(pkgname)
pkg = pkgs[0]
for line in pkg.get_metadata_lines('PKG-INFO'):
(k, v) = line.split(': ', 1)
if k == "License":
return v
return None
Пример использования:
>>> get_pkg_license('mercurial')
'GNU GPLv2+'
>>> get_pkg_license('pytz')
'MIT'
>>> get_pkg_license('django')
'UNKNOWN'
Ответ 2
Вот способ сделать это с помощью yolk3k (инструмент командной строки для запроса пакетов PyPI и Python, установленных в вашей системе.)
pip install yolk3k
yolk -l -f license
#-l lists all installed packages
#-f Show specific metadata fields (In this case, License)
Ответ 3
Вот фрагмент копии, который будет печатать ваши пакеты.
Требуется: prettytable (pip install prettytable
)
Код
import pkg_resources
import prettytable
def get_pkg_license(pkg):
try:
lines = pkg.get_metadata_lines('METADATA')
except:
lines = pkg.get_metadata_lines('PKG-INFO')
for line in lines:
if line.startswith('License:'):
return line[9:]
return '(Licence not found)'
def print_packages_and_licenses():
t = prettytable.PrettyTable(['Package', 'License'])
for pkg in sorted(pkg_resources.working_set, key=lambda x: str(x).lower()):
t.add_row((str(pkg), get_pkg_license(pkg)))
print(t)
if __name__ == "__main__":
print_packages_and_licenses()
Пример вывода
+---------------------------+--------------------------------------------------------------+
| Package | License |
+---------------------------+--------------------------------------------------------------+
| appdirs 1.4.3 | MIT |
| argon2-cffi 16.3.0 | MIT |
| boto3 1.4.4 | Apache License 2.0 |
| botocore 1.5.21 | Apache License 2.0 |
| cffi 1.10.0 | MIT |
| colorama 0.3.9 | BSD |
| decorator 4.0.11 | new BSD License |
| Django 1.11 | BSD |
| django-debug-toolbar 1.7 | BSD |
| django-environ 0.4.3 | MIT License |
| django-storages 1.5.2 | BSD |
| django-uuslug 1.1.8 | BSD |
| djangorestframework 3.6.2 | BSD |
| docutils 0.13.1 | public domain, Python, 2-Clause BSD, GPL 3 (see COPYING.txt) |
| EasyProcess 0.2.3 | BSD |
| ipython 6.0.0 | BSD |
| ipython-genutils 0.2.0 | BSD |
| jedi 0.10.2 | MIT |
| jmespath 0.9.1 | MIT |
| packaging 16.8 | BSD or Apache License, Version 2.0 |
| pickleshare 0.7.4 | MIT |
| pip 9.0.1 | MIT |
| prettytable 0.7.2 | BSD (3 clause) |
| prompt-toolkit 1.0.14 | UNKNOWN |
| psycopg2 2.6.2 | LGPL with exceptions or ZPL |
| pycparser 2.17 | BSD |
| Pygments 2.2.0 | BSD License |
| pyparsing 2.2.0 | MIT License |
| python-dateutil 2.6.0 | Simplified BSD |
| python-slugify 1.2.4 | MIT |
| pytz 2017.2 | MIT |
| PyVirtualDisplay 0.2.1 | BSD |
| s3transfer 0.1.10 | Apache License 2.0 |
| selenium 3.0.2 | UNKNOWN |
| setuptools 35.0.2 | UNKNOWN |
| simplegeneric 0.8.1 | ZPL 2.1 |
| six 1.10.0 | MIT |
| sqlparse 0.2.3 | BSD |
| traitlets 4.3.2 | BSD |
| Unidecode 0.4.20 | GPL |
| wcwidth 0.1.7 | MIT |
| wheel 0.30.0a0 | MIT |
| win-unicode-console 0.5 | MIT |
+---------------------------+--------------------------------------------------------------+
Ответ 4
С пипсом:
pip show django | grep License
Если вы хотите получить классификатор PyPI для лицензии, используйте подробный вариант:
pip show -v django | grep 'License ::'
Ответ 5
1. Выбор
пакет pip-лицензий PyPI.
2. Актуальность
Этот ответ актуален для марта 2018 года. В дальнейшем данные этого ответа могут устареть.
3. Аргументация
- простая установка -
pip install pip-licenses
, - больше возможностей и опций, чем yolk3k,
- активно поддерживается.
4. Демонстрация
Пример вывода:
D:\KristinitaPelican>pipenv run pip-licenses --with-system --order=license --format-markdown
| Name | Version | License |
|---------------------|-----------|--------------------------------------------------------------|
| requests | 2.18.4 | Apache 2.0 |
| actdiag | 0.5.4 | Apache License 2.0 |
| blockdiag | 1.5.3 | Apache License 2.0 |
| nwdiag | 1.0.4 | Apache License 2.0 |
| seqdiag | 0.9.5 | Apache License 2.0 |
| Jinja2 | 2.10 | BSD |
| MarkupSafe | 1.0 | BSD |
| license-info | 0.8.7 | BSD |
| pip-review | 1.0 | BSD |
| pylicense | 1 | BSD |
| PTable | 0.9.2 | BSD (3 clause) |
| webcolors | 1.8.1 | BSD 3-Clause |
| Markdown | 2.6.11 | BSD License |
| Pygments | 2.2.0 | BSD License |
| yolk3k | 0.9 | BSD License |
| packaging | 17.1 | BSD or Apache License, Version 2.0 |
| idna | 2.6 | BSD-like |
| markdown-newtab | 0.2.0 | CC0 |
| pyembed | 1.3.3 | Copyright © 2013 Matt Thomson |
| pyembed-markdown | 1.1.0 | Copyright © 2013 Matt Thomson |
| python-dateutil | 2.7.2 | Dual License |
| Unidecode | 1.0.22 | GPL |
| chardet | 3.0.4 | LGPL |
| beautifulsoup4 | 4.6.0 | MIT |
| funcparserlib | 0.3.6 | MIT |
| gevent | 1.2.2 | MIT |
| markdown-blockdiag | 0.7.0 | MIT |
| pip | 9.0.1 | MIT |
| pkgtools | 0.7.3 | MIT |
| pytz | 2018.3 | MIT |
| six | 1.11.0 | MIT |
| urllib3 | 1.22 | MIT |
| wheel | 0.30.0 | MIT |
| blinker | 1.4 | MIT License |
| greenlet | 0.4.13 | MIT License |
| pip-licenses | 1.7.0 | MIT License |
| pymdown-extensions | 4.9.2 | MIT License |
| pyparsing | 2.2.0 | MIT License |
| certifi | 2018.1.18 | MPL-2.0 |
| markdown-downheader | 1.1.0 | Simplified BSD License |
| Pillow | 5.0.0 | Standard PIL License |
| feedgenerator | 1.9 | UNKNOWN |
| license-lister | 0.1.1 | UNKNOWN |
| md-environ | 0.1.0 | UNKNOWN |
| mdx-cite | 1.0 | UNKNOWN |
| mdx-customspanclass | 1.1.1 | UNKNOWN |
| pelican | 3.7.1 | UNKNOWN |
| setuptools | 38.5.1 | UNKNOWN |
| docutils | 0.14 | public domain, Python, 2-Clause BSD, GPL 3 (see COPYING.txt) |
5. Внешняя ссылка
Ответ 6
Я нашел несколько идей из ответов и комментариев для этого вопроса, чтобы они были релевантными, и написал короткий script для генерации информации о лицензии для применимого virtualenv:
import pkg_resources
import copy
def get_packages_info():
KEY_MAP = {
"Name": 'name',
"Version": 'version',
"License": 'license',
}
empty_info = {}
for key, name in KEY_MAP.iteritems():
empty_info[name] = ""
packages = pkg_resources.working_set.by_key
infos = []
for pkg_name, pkg in packages.iteritems():
info = copy.deepcopy(empty_info)
try:
lines = pkg.get_metadata_lines('METADATA')
except (KeyError, IOError):
lines = pkg.get_metadata_lines('PKG-INFO')
for line in lines:
try:
key, value = line.split(': ', 1)
if KEY_MAP.has_key(key):
info[KEY_MAP[key]] = value
except ValueError:
pass
infos += [info]
return "name,version,license\n%s" % "\n".join(['"%s","%s","%s"' % (info['name'], info['version'], info['license']) for info in sorted(infos, key=(lambda item: item['name'].lower()))])
Ответ 7
На основе ответа, предоставленного @garromark и настроенного для Python 3, я использую его в командной строке:
import pkg_resources import copy
def get_packages_info():
KEY_MAP = {
"Name": 'name',
"Version": 'version',
"License": 'license',
}
empty_info = {}
for key, name in KEY_MAP.items():
empty_info[name] = ""
packages = pkg_resources.working_set.by_key
infos = []
for pkg_name, pkg in packages.items():
info = copy.deepcopy(empty_info)
try:
lines = pkg.get_metadata_lines('METADATA')
except (KeyError, IOError):
lines = pkg.get_metadata_lines('PKG-INFO')
for line in lines:
try:
key, value = line.split(': ', 1)
if key in KEY_MAP:
info[KEY_MAP[key]] = value
except ValueError:
pass
infos += [info]
return "name,version,license\n%s" % "\n".join(['"%s","%s","%s"' % (info['name'], info['version'], info['license']) for info in sorted(infos, key=(lambda item: item['name'].lower()))])
print(get_packages_info())
Ответ 8
Немного лучшая версия для тех, кто работает с jupyter - использует настройки Anaconda по умолчанию - установка не требуется
import pkg_resources
import pandas as pd
def get_pkg_license(pkg):
try:
lines = pkg.get_metadata_lines('METADATA')
except:
lines = pkg.get_metadata_lines('PKG-INFO')
for line in lines:
if line.startswith('License:'):
return line[9:]
return '(Licence not found)'
def print_packages_and_licenses():
table = []
for pkg in sorted(pkg_resources.working_set, key=lambda x: str(x).lower()):
table.append([str(pkg).split(' ',1)[0], str(pkg).split(' ',1)[1], get_pkg_license(pkg)])
df = pd.DataFrame(table, columns=['Package', 'Version', 'License'])
return df
print_packages_and_licenses()
Ответ 9
Другой вариант - использовать Brian Dailey Python Package Check Checker.
git clone https://github.com/briandailey/python-packages-license-check.git
cd python-packages-license-check
... activate your chosen virtualenv ...
./check.py
Ответ 10
Ответ не помог мне для большинства генерируемых библиотек исключений.
Так сделала небольшая грубая сила
def get_pkg_license_use_show(pkgname):
"""
Given a package reference (as from requirements.txt),
return license listed in package metadata.
NOTE: This function does no error checking and is for
demonstration purposes only.
"""
out = subprocess.check_output(["pip", 'show', pkgname])
pattern = re.compile(r"License: (.*)")
license_line = [i for i in out.split("\n") if i.startswith('License')]
match = pattern.match(license_line[0])
license = match.group(1)
return license
Ответ 11
Согласно выводу pip show -v
, существует два возможных места, где находится информация о лицензии для каждого пакета.
Вот некоторые примеры:
$ pip show django -v | grep -i license
License: BSD
License :: OSI Approved :: BSD License
$ pip show setuptools -v | grep -i license
License: UNKNOWN
License :: OSI Approved :: MIT License
$ pip show python-dateutil -v | grep -i license
License: Dual License
License :: OSI Approved :: BSD License
License :: OSI Approved :: Apache Software License
$ pip show ipdb -v | grep -i license
License: BSD
Приведенный ниже код возвращает итератор, который содержит все возможные лицензии пакета, используя pkg_resources
из setuptools
:
from itertools import chain, compress
from pkg_resources import get_distribution
def filters(line):
return compress(
(line[9:], line[39:]),
(line.startswith('License:'), line.startswith('Classifier: License')),
)
def get_pkg_license(pkg):
distribution = get_distribution(pkg)
try:
lines = distribution.get_metadata_lines('METADATA')
except OSError:
lines = distribution.get_metadata_lines('PKG-INFO')
return tuple(chain.from_iterable(map(filters, lines)))
Вот результаты:
>>> tuple(get_pkg_license(get_distribution('django')))
('BSD', 'BSD License')
>>> tuple(get_pkg_license(get_distribution('setuptools')))
('UNKNOWN', 'MIT License')
>>> tuple(get_pkg_license(get_distribution('python-dateutil')))
('Dual License', 'BSD License', 'Apache Software License')
>>> tuple(get_pkg_license(get_distribution('ipdb')))
('BSD',)
Наконец, чтобы получить все лицензии от установленных приложений:
>>> {
p.project_name: get_pkg_license(p)
for p in pkg_resources.working_set
}