Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  requirements.py   Sprache: Python

 
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import re
from pathlib import Path

from packaging.requirements import Requirement

THUNDERBIRD_PYPI_ERROR = """
Thunderbird requirements definitions cannot include PyPI packages.
""".strip()


class PthSpecifier:
    def __init__(self, path: str):
        self.path = path


class PypiSpecifier:
    def __init__(self, requirement):
        self.requirement = requirement


class PypiOptionalSpecifier(PypiSpecifier):
    def __init__(self, repercussion, requirement):
        super().__init__(requirement)
        self.repercussion = repercussion


class MachEnvRequirements:
    """Requirements associated with a "site dependency manifest", as
    defined in "python/sites/".

    Represents the dependencies of a site. The source files consist
    of colon-delimited fields. The first field
    specifies the action. The remaining fields are arguments to that
    action. The following actions are supported:

    requires-python -- Specifies the minimum and/or maximum Python
        versions supported by this site.

    pth -- Adds the path given as argument to "mach.pth" under
        the virtualenv's site packages directory.

    pypi -- Fetch the package, plus dependencies, from PyPI.

    pypi-optional -- Attempt to install the package and dependencies from PyPI.
        Continue using the site, even if the package could not be installed.

    packages.txt -- Denotes that the specified path is a child manifest. It
        will be read and processed as if its contents were concatenated
        into the manifest being read.

    thunderbird-packages.txt -- Denotes a Thunderbird child manifest.
        Thunderbird child manifests are only activated when working on Thunderbird,
        and they can cannot have "pypi" or "pypi-optional" entries.
    """

    def __init__(self):
        self.requires_python = ""
        self.requirements_paths = []
        self.pth_requirements = []
        self.pypi_requirements = []
        self.pypi_optional_requirements = []
        self.vendored_requirements = []
        self.vendored_fallback_requirements = []

    def pths_as_absolute(self, topsrcdir: str):
        return [
            os.path.normcase(Path(topsrcdir) / pth.path)
            for pth in (self.pth_requirements + self.vendored_requirements)
        ]

    def pths_fallback_as_absolute(self, topsrcdir: str):
        return [
            os.path.normcase(Path(topsrcdir) / pth.path)
            for pth in self.vendored_fallback_requirements
        ]

    @classmethod
    def from_requirements_definition(
        cls,
        topsrcdir: str,
        is_thunderbird,
        only_strict_requirements,
        requirements_definition,
    ):
        requirements = cls()
        _parse_mach_env_requirements(
            requirements,
            Path(requirements_definition),
            Path(topsrcdir),
            is_thunderbird,
            only_strict_requirements,
        )
        return requirements


def _parse_mach_env_requirements(
    requirements_output,
    root_requirements_path: Path,
    topsrcdir: Path,
    is_thunderbird,
    only_strict_requirements,
):
    def _parse_requirements_line(
        current_requirements_path: Path, line, line_number, is_thunderbird_packages_txt
    ):
        line = line.strip()
        if not line or line.startswith("#"):
            return

        action, params = line.rstrip().split(":", maxsplit=1)
        if action == "requires-python":
            if requirements_output.requires_python == "":
                requirements_output.requires_python = params
            else:
                raise Exception(
                    f"'requires-python' is already set to "
                    f"'{requirements_output.requires_python}'. "
                    f"Attempted to set it again to '{params}'. "
                    f"Please ensure the file '{root_requirements_path}' "
                    f"contains only one 'requires-python' entry."
                )
        elif action == "pth":
            path = topsrcdir / params
            if not path.exists():
                # In sparse checkouts, not all paths will be populated.
                return

            requirements_output.pth_requirements.append(PthSpecifier(params))
        elif action == "vendored":
            requirements_output.vendored_requirements.append(PthSpecifier(params))
        elif action == "vendored-fallback":
            if is_thunderbird_packages_txt:
                raise Exception(THUNDERBIRD_PYPI_ERROR)

            pypi_pkg, vendored_path, repercussion = params.split(":")
            requirements = topsrcdir / "third_party" / "python" / "requirements.txt"
            with open(requirements) as req:
                content = req.read()
                pattern = re.compile(rf"^({pypi_pkg}==.*) \\$", re.MULTILINE)
                version_matches = pattern.findall(content, re.MULTILINE)
                if len(version_matches) != 1:
                    raise Exception(
                        f"vendored-fallback package {pypi_pkg} is not referenced in {requirements}"
                    )
                (raw_requirement,) = version_matches

            requirements_output.pypi_optional_requirements.append(
                PypiOptionalSpecifier(
                    repercussion,
                    _parse_package_specifier(raw_requirement, only_strict_requirements),
                )
            )

            requirements_output.vendored_fallback_requirements.append(
                PthSpecifier(vendored_path)
            )
        elif action == "packages.txt":
            _parse_requirements_definition_file(
                topsrcdir / params,
                is_thunderbird_packages_txt,
            )
        elif action == "pypi":
            if is_thunderbird_packages_txt:
                raise Exception(THUNDERBIRD_PYPI_ERROR)

            requirements_output.pypi_requirements.append(
                PypiSpecifier(
                    _parse_package_specifier(params, only_strict_requirements)
                )
            )
        elif action == "pypi-optional":
            if is_thunderbird_packages_txt:
                raise Exception(THUNDERBIRD_PYPI_ERROR)

            if len(params.split(":", maxsplit=1)) != 2:
                raise Exception(
                    "Expected pypi-optional package to have a repercussion "
                    'description in the format "package:fallback explanation", '
                    'found "{}"'.format(params)
                )
            raw_requirement, repercussion = params.split(":")
            requirements_output.pypi_optional_requirements.append(
                PypiOptionalSpecifier(
                    repercussion,
                    _parse_package_specifier(raw_requirement, only_strict_requirements),
                )
            )
        elif action == "thunderbird-packages.txt":
            if is_thunderbird:
                _parse_requirements_definition_file(
                    topsrcdir / params, is_thunderbird_packages_txt=True
                )
        else:
            raise Exception("Unknown requirements definition action: %s" % action)

    def _parse_requirements_definition_file(
        requirements_path: Path, is_thunderbird_packages_txt
    ):
        """Parse requirements file into list of requirements"""
        if not requirements_path.is_file():
            raise Exception(f'Missing requirements file at "{requirements_path}"')

        requirements_output.requirements_paths.append(str(requirements_path))

        with open(requirements_path) as requirements_file:
            lines = [line for line in requirements_file]

        for number, line in enumerate(lines, start=1):
            _parse_requirements_line(
                requirements_path, line, number, is_thunderbird_packages_txt
            )

    _parse_requirements_definition_file(root_requirements_path, False)


class UnexpectedFlexibleRequirementException(Exception):
    def __init__(self, raw_requirement):
        self.raw_requirement = raw_requirement


def _parse_package_specifier(raw_requirement, only_strict_requirements):
    requirement = Requirement(raw_requirement)

    if only_strict_requirements and [
        s for s in requirement.specifier if s.operator != "=="
    ]:
        raise UnexpectedFlexibleRequirementException(raw_requirement)
    return requirement

100%


¤ Dauer der Verarbeitung: 0.7 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge