Skip to content

Commit

Permalink
Merge pull request #129 from b-kamphorst/master
Browse files Browse the repository at this point in the history
feat: depend on importlib_metadata rather than pip
  • Loading branch information
raimon49 authored Oct 27, 2022
2 parents 12d5d5f + 129cadb commit 715a9f8
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 132 deletions.
5 changes: 2 additions & 3 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.7', '3.8', '3.9', '3.10']
pip-version: ['', '==21.2.4']
python-version: ['3.8', '3.9', '3.10', '3.11']

steps:
- uses: actions/checkout@v2
Expand All @@ -23,7 +22,7 @@ jobs:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip${{ matrix.pip-version }}
python -m pip install --upgrade pip
if [ -f dev-requirements.txt ]; then pip install -r dev-requirements.txt; fi
- name: Test with pytest
run: |
Expand Down
130 changes: 44 additions & 86 deletions piplicenses.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,41 +28,13 @@
"""
import argparse
import codecs
import glob
import os
import re
import sys
from collections import Counter
from email import message_from_string
from email.parser import FeedParser
from enum import Enum, auto
from functools import partial
from typing import List, Optional, Sequence, Text

try:
from pip._internal.utils.misc import get_installed_distributions
except ImportError: # pragma: no cover
try:
from pip import get_installed_distributions
except ImportError:
def get_installed_distributions():
from pip._internal.metadata import (
get_default_environment, get_environment,
)
from pip._internal.metadata.pkg_resources import (
Distribution as _Dist,
)
from pip._internal.utils.compat import stdlib_pkgs

env = get_default_environment()
dists = env.iter_installed_distributions(
local_only=True,
skip=stdlib_pkgs,
include_editables=True,
editables_only=False,
user_only=False,
)
return [dist._dist for dist in dists]

from prettytable import PrettyTable

try:
Expand All @@ -78,6 +50,8 @@ def get_installed_distributions():
from prettytable import NONE as RULE_NONE
PTABLE = False

from importlib import metadata as importlib_metadata

open = open # allow monkey patching

__pkgname__ = 'pip-licenses'
Expand Down Expand Up @@ -151,78 +125,60 @@ def get_installed_distributions():

def get_packages(args: "CustomNamespace"):

def get_pkg_included_file(pkg, file_names):
def get_pkg_included_file(pkg, file_names_rgx):
"""
Attempt to find the package's included file on disk and return the
tuple (included_file_path, included_file_contents).
"""
included_file = LICENSE_UNKNOWN
included_text = LICENSE_UNKNOWN
pkg_dirname = "{}-{}.dist-info".format(
pkg.project_name.replace("-", "_"), pkg.version)
patterns = []
[patterns.extend(sorted(glob.glob(os.path.join(pkg.location,
pkg_dirname,
f))))
for f in file_names]
# Search for path defined in PEP 639 https://peps.python.org/pep-0639/
[patterns.extend(sorted(glob.glob(os.path.join(pkg.location,
pkg_dirname,
"licenses",
f))))
for f in file_names]
for test_file in patterns:
if os.path.exists(test_file) and \
os.path.isdir(test_file) is not True:
included_file = test_file
with open(test_file, encoding='utf-8',
errors='backslashreplace') as included_file_handle:
included_text = included_file_handle.read()
break
return (included_file, included_text)

pkg_files = pkg.files or ()
pattern = re.compile(file_names_rgx)
matched_rel_paths = filter(
lambda file: pattern.match(file.name),
pkg_files
)
for rel_path in matched_rel_paths:
abs_path = pkg.locate_file(rel_path)
if not abs_path.is_file():
continue
included_file = abs_path
with open(
abs_path,
encoding='utf-8',
errors='backslashreplace'
) as included_file_handle:
included_text = included_file_handle.read()
break
return (str(included_file), included_text)

def get_pkg_info(pkg):
(license_file, license_text) = get_pkg_included_file(
pkg,
('LICENSE*', 'LICENCE*', 'COPYING*')
"LICEN[CS]E.*|COPYING.*"
)
(notice_file, notice_text) = get_pkg_included_file(
pkg,
('NOTICE*',)
"NOTICE.*"
)
pkg_info = {
'name': pkg.project_name,
'name': pkg.metadata["name"],
'version': pkg.version,
'namever': str(pkg),
'namever': "{} {}".format(pkg.metadata["name"], pkg.version),
'licensefile': license_file,
'licensetext': license_text,
'noticefile': notice_file,
'noticetext': notice_text,
}
metadata = None
if pkg.has_metadata('METADATA'):
metadata = pkg.get_metadata('METADATA')

if pkg.has_metadata('PKG-INFO') and metadata is None:
metadata = pkg.get_metadata('PKG-INFO')

if metadata is None:
for key in METADATA_KEYS:
pkg_info[key] = LICENSE_UNKNOWN

return pkg_info

feed_parser = FeedParser()
feed_parser.feed(metadata)
parsed_metadata = feed_parser.close()

metadata = pkg.metadata
for key in METADATA_KEYS:
pkg_info[key] = parsed_metadata.get(key, LICENSE_UNKNOWN)
pkg_info[key] = metadata.get(key, LICENSE_UNKNOWN)

if metadata is not None:
message = message_from_string(metadata)
classifiers = metadata.get_all("classifier")
if classifiers:
pkg_info['license_classifier'] = \
find_license_from_classifier(message)
find_license_from_classifier(classifiers)

if args.filter_strings:
for k in pkg_info:
Expand All @@ -238,7 +194,10 @@ def get_pkg_info(pkg):

return pkg_info

pkgs = get_installed_distributions()
pkgs = filter(
lambda pkg: pkg.metadata["name"] != "pip-licenses",
importlib_metadata.distributions()
)
ignore_pkgs_as_lower = [pkg.lower() for pkg in args.ignore_packages]
pkgs_as_lower = [pkg.lower() for pkg in args.packages]

Expand All @@ -251,7 +210,7 @@ def get_pkg_info(pkg):
allow_only_licenses = set(map(str.strip, args.allow_only.split(";")))

for pkg in pkgs:
pkg_name = pkg.project_name
pkg_name = pkg.metadata["name"]

if pkg_name.lower() in ignore_pkgs_as_lower:
continue
Expand Down Expand Up @@ -477,15 +436,14 @@ def factory_styled_table_with_args(
return table


def find_license_from_classifier(message):
def find_license_from_classifier(classifiers):
licenses = []
for k, v in message.items():
if k == 'Classifier' and v.startswith('License'):
license = v.split(' :: ')[-1]
for classifier in filter(lambda c: c.startswith("License"), classifiers):
license = classifier.split(' :: ')[-1]

# Through the declaration of 'Classifier: License :: OSI Approved'
if license != 'OSI Approved':
licenses.append(license)
# Through the declaration of 'Classifier: License :: OSI Approved'
if license != 'OSI Approved':
licenses.append(license)

return licenses

Expand Down
4 changes: 2 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ classifiers =
License :: OSI Approved :: MIT License
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
Topic :: System :: Systems Administration
Topic :: System :: System Shells

[options]
packages = find:
include_package_data = True
python_requires = ~=3.7
python_requires = ~=3.8
py_modules =
piplicenses
setup_requires =
Expand Down
Loading

0 comments on commit 715a9f8

Please sign in to comment.