Module continuous_delivery_scripts.utils.python.package_helpers

Utilities for retrieving Python's package information.

Functions

def get_all_packages_metadata_lines(package_name: str) ‑> List[list]
Expand source code
def get_all_packages_metadata_lines(package_name: str) -> List[list]:
    """Determines the metadata lines for the present package as well as for all its dependencies."""
    distributions, _ = _get_distributions_and_missing(package_name)
    return [get_package_metadata_lines(package) for package in distributions]

Determines the metadata lines for the present package as well as for all its dependencies.

def get_package_metadata_lines(package: Any) ‑> list
Expand source code
def get_package_metadata_lines(package: Any) -> list:
    """Determines the metadata lines of a package.

    Depending on the package, there may be a METADATA or a PKG-INFO file.
    We need to try both to get the metadata lines and
    the underlying `get_metadata_lines` function used raises an exception if the file does not exist.
    We hence need to try to catch all exceptions
    """
    for filename in ["METADATA", "PKG-INFO"]:
        try:
            metadata = package.read_text(filename)
            if metadata:
                return cast(list, metadata.splitlines())
        except Exception as e:
            logger.warning(e)
    return list()

Determines the metadata lines of a package.

Depending on the package, there may be a METADATA or a PKG-INFO file. We need to try both to get the metadata lines and the underlying get_metadata_lines function used raises an exception if the file does not exist. We hence need to try to catch all exceptions

def parse_package_metadata(metadata: email.message.Message,
distribution: importlib.metadata.Distribution | None = None) ‑> PackageMetadata
Expand source code
def parse_package_metadata(
    metadata: Message, distribution: Optional[importlib_metadata.Distribution] = None
) -> PackageMetadata:
    """Parses structured distribution metadata and preserves repeated licence classifiers."""
    data: Dict[str, Any] = dict(metadata.items())
    classifiers = [
        entry.split("::")[-1].strip()
        for entry in metadata.get_all("Classifier") or []
        if entry.startswith("License ::") and entry.split("::")[-1].strip() != "OSI Approved"
    ]
    if classifiers:
        data["License-Classifiers"] = list(dict.fromkeys(classifiers))
        identifiers = []
        for classifier in classifiers:
            try:
                identifier = cleanse_licence_expression(classifier)
            except ExpressionError:
                break
            if not OPENSOURCE_LICENCES.get_licence(identifier):
                break
            identifiers.append(identifier)
        else:
            data["Licence-Candidates"] = list(dict.fromkeys(identifiers))
            # Multiple classifiers do not establish whether the licences are alternatives or cumulative.
            if len(data["Licence-Candidates"]) == 1:
                data["License-Classifier"] = data["Licence-Candidates"][0]
    evidence = _get_licence_evidence(distribution, metadata) if distribution else []
    return PackageMetadata(data, evidence)

Parses structured distribution metadata and preserves repeated licence classifiers.

def parse_package_metadata_lines(metadata: list) ‑> PackageMetadata
Expand source code
def parse_package_metadata_lines(metadata: list) -> PackageMetadata:
    """Parses package metadata lines and retains relevant information."""
    return parse_package_metadata(Parser().parsestr("\n".join(str(line).rstrip("\r\n") for line in metadata)))

Parses package metadata lines and retains relevant information.

Classes

class CurrentPythonProjectMetadataFetcher
Expand source code
class CurrentPythonProjectMetadataFetcher(PythonProjectMetadataFetcher):
    """Fetches the current python project metadata."""

    def __init__(self) -> None:
        """Constructor."""
        super().__init__(configuration.get_value(ConfigurationVariable.PACKAGE_NAME))

Fetches the current python project metadata.

Constructor.

Ancestors

Inherited members

class PythonProjectMetadataFetcher (package_name: str)
Expand source code
class PythonProjectMetadataFetcher(ProjectMetadataFetcher):
    """Parser of python package metadata."""

    ENTRY_PATTERN = r"^([^:]*):(.*)$"

    def __init__(self, package_name: str) -> None:
        """Initialiser."""
        super().__init__(package_name)

    def fetch_project_metadata(self) -> ProjectMetadata:
        """Parses package metadata."""
        project_metadata = ProjectMetadata(self._package_name)
        distributions, project_metadata.missing_dependencies = _get_distributions_and_missing(self._package_name)
        for distribution in distributions:
            info = parse_package_metadata(cast(Message, distribution.metadata), distribution)
            if canonicalize_name(info.name) == canonicalize_name(self._package_name):
                project_metadata.project_metadata = info
            else:
                project_metadata.add_dependency_metadata(info)
        return project_metadata

Parser of python package metadata.

Initialiser.

Ancestors

Subclasses

Class variables

var ENTRY_PATTERN

The type of the None singleton.

Methods

def fetch_project_metadata(self) ‑> ProjectMetadata
Expand source code
def fetch_project_metadata(self) -> ProjectMetadata:
    """Parses package metadata."""
    project_metadata = ProjectMetadata(self._package_name)
    distributions, project_metadata.missing_dependencies = _get_distributions_and_missing(self._package_name)
    for distribution in distributions:
        info = parse_package_metadata(cast(Message, distribution.metadata), distribution)
        if canonicalize_name(info.name) == canonicalize_name(self._package_name):
            project_metadata.project_metadata = info
        else:
            project_metadata.add_dependency_metadata(info)
    return project_metadata

Parses package metadata.

Inherited members