diff --git a/CHANGELOG.md b/CHANGELOG.md index b68843f..787f72f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## CHANGELOG +### 4.3.1 + +* Fix to treat package names as normalized as in [PEP 503](https://peps.python.org/pep-0503/) with `--packages` and `--ignore-packages` option + ### 4.3.0 * Implement new option `--no-version` diff --git a/piplicenses.py b/piplicenses.py index 44e8798..ed028b1 100644 --- a/piplicenses.py +++ b/piplicenses.py @@ -56,7 +56,7 @@ open = open # allow monkey patching __pkgname__ = "pip-licenses" -__version__ = "4.3.0" +__version__ = "4.3.1" __author__ = "raimon" __license__ = "MIT" __summary__ = ( @@ -129,6 +129,24 @@ def extract_homepage(metadata: Message) -> Optional[str]: return None +PATTERN_DELIMITER = re.compile(r"[-_.]+") + + +def normalize_pkg_name(pkg_name: str) -> str: + """Return normalized name according to PEP specification + + See here: https://peps.python.org/pep-0503/#normalized-names + + Args: + pkg_name: Package name it is extracted from the package metadata + or specified in the CLI + + Returns: + normalized packege name + """ + return PATTERN_DELIMITER.sub("-", pkg_name).lower() + + METADATA_KEYS: Dict[str, List[Callable[[Message], Optional[str]]]] = { "home-page": [extract_homepage], "author": [ @@ -254,8 +272,10 @@ def get_python_sys_path(executable: str) -> list[str]: search_paths = get_python_sys_path(args.python) pkgs = importlib_metadata.distributions(path=search_paths) - ignore_pkgs_as_lower = [pkg.lower() for pkg in args.ignore_packages] - pkgs_as_lower = [pkg.lower() for pkg in args.packages] + ignore_pkgs_as_normalize = [ + normalize_pkg_name(pkg) for pkg in args.ignore_packages + ] + pkgs_as_normalize = [normalize_pkg_name(pkg) for pkg in args.packages] fail_on_licenses = set() if args.fail_on: @@ -266,16 +286,16 @@ def get_python_sys_path(executable: str) -> list[str]: allow_only_licenses = set(map(str.strip, args.allow_only.split(";"))) for pkg in pkgs: - pkg_name = pkg.metadata["name"] + pkg_name = normalize_pkg_name(pkg.metadata["name"]) pkg_name_and_version = pkg_name + ":" + pkg.metadata["version"] if ( - pkg_name.lower() in ignore_pkgs_as_lower - or pkg_name_and_version.lower() in ignore_pkgs_as_lower + pkg_name.lower() in ignore_pkgs_as_normalize + or pkg_name_and_version.lower() in ignore_pkgs_as_normalize ): continue - if pkgs_as_lower and pkg_name.lower() not in pkgs_as_lower: + if pkgs_as_normalize and pkg_name.lower() not in pkgs_as_normalize: continue if not args.with_system and pkg_name in SYSTEM_PACKAGES: diff --git a/test_piplicenses.py b/test_piplicenses.py index 0ac2394..f8cba9b 100644 --- a/test_piplicenses.py +++ b/test_piplicenses.py @@ -46,6 +46,7 @@ get_output_fields, get_packages, get_sortby, + normalize_pkg_name, output_colored, save_if_needs, select_license_by_source, @@ -429,6 +430,18 @@ def test_ignore_packages(self) -> None: pkg_name_columns = self._create_pkg_name_columns(table) self.assertNotIn(ignore_pkg_name, pkg_name_columns) + def test_ignore_normalized_packages(self) -> None: + ignore_pkg_name = "pip-licenses" + ignore_packages_args = [ + "--ignore-package=pip_licenses", + "--with-system", + ] + args = self.parser.parse_args(ignore_packages_args) + table = create_licenses_table(args) + + pkg_name_columns = self._create_pkg_name_columns(table) + self.assertNotIn(ignore_pkg_name, pkg_name_columns) + def test_ignore_packages_and_version(self) -> None: # Fictitious version that does not exist ignore_pkg_name = "prettytable" @@ -453,6 +466,18 @@ def test_with_packages(self) -> None: pkg_name_columns = self._create_pkg_name_columns(table) self.assertListEqual([pkg_name], pkg_name_columns) + def test_with_normalized_packages(self) -> None: + pkg_name = "typing_extensions" + only_packages_args = [ + "--package=typing-extensions", + "--with-system", + ] + args = self.parser.parse_args(only_packages_args) + table = create_licenses_table(args) + + pkg_name_columns = self._create_pkg_name_columns(table) + self.assertListEqual([pkg_name], pkg_name_columns) + def test_with_packages_with_system(self) -> None: pkg_name = "prettytable" only_packages_args = ["--packages=" + pkg_name, "--with-system"] @@ -920,6 +945,14 @@ def test_verify_args( assert arg in capture +def test_normalize_pkg_name() -> None: + expected_normalized_name = "pip-licenses" + + assert normalize_pkg_name("pip_licenses") == expected_normalized_name + assert normalize_pkg_name("pip.licenses") == expected_normalized_name + assert normalize_pkg_name("Pip-Licenses") == expected_normalized_name + + def test_extract_homepage_home_page_set() -> None: metadata = MagicMock() metadata.get.return_value = "Foobar"