Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 631 – Abhängigkeitsspezifikation in pyproject.toml basierend auf PEP 508

Autor:
Ofek Lev <ofekmeister at gmail.com>
Sponsor:
Paul Ganssle <paul at ganssle.io>
Discussions-To:
Discourse thread
Status:
Abgelöst
Typ:
Standards Track
Thema:
Packaging
Erstellt:
20-Aug-2020
Post-History:
20-Aug-2020
Ersetzt-Durch:
621
Resolution:
Discourse-Nachricht

Inhaltsverzeichnis

Zusammenfassung

Dieses PEP spezifiziert, wie die Abhängigkeiten eines Projekts in einer pyproject.toml-Datei geschrieben werden, damit sie von Verpackungs-Tools über die in PEP 621 definierten Felder konsumiert werden können.

Hinweis

Dieses PEP wurde angenommen und in PEP 621 integriert.

Einträge

Alle Abhängigkeitseinträge MÜSSEN gültige PEP 508-Strings sein.

Build-Backends SOLLTEN zur Ladezeit bei jeglichen Parsing-Fehlern abbrechen.

from packaging.requirements import InvalidRequirement, Requirement

...

try:
    Requirement(entry)
except InvalidRequirement:
    # exit

Spezifikation

dependencies

  • Format: Array von Strings
  • Zugehörige Kernmetadaten

Jedes Element MUSS ein Eintrag sein.

[project]
dependencies = [
  'PyYAML ~= 5.0',
  'requests[security] < 3',
  'subprocess32; python_version < "3.2"',
]

optional-dependencies

Jeder Schlüssel ist der Name der bereitgestellten Option, wobei jeder Wert denselben Typ hat wie das Feld dependencies, d.h. ein Array von Strings.

[project.optional-dependencies]
tests = [
  'coverage>=5.0.3',
  'pytest',
  'pytest-benchmark[histogram]>=3.2.1',
]

Beispiel

Dies ist eine reale Portierung dessen, was docker-compose definiert.

[project]
dependencies = [
  'cached-property >= 1.2.0, < 2',
  'distro >= 1.5.0, < 2',
  'docker[ssh] >= 4.2.2, < 5',
  'dockerpty >= 0.4.1, < 1',
  'docopt >= 0.6.1, < 1',
  'jsonschema >= 2.5.1, < 4',
  'PyYAML >= 3.10, < 6',
  'python-dotenv >= 0.13.0, < 1',
  'requests >= 2.20.0, < 3',
  'texttable >= 0.9.0, < 2',
  'websocket-client >= 0.32.0, < 1',

  # Conditional
  'backports.shutil_get_terminal_size == 1.0.0; python_version < "3.3"',
  'backports.ssl_match_hostname >= 3.5, < 4; python_version < "3.5"',
  'colorama >= 0.4, < 1; sys_platform == "win32"',
  'enum34 >= 1.0.4, < 2; python_version < "3.4"',
  'ipaddress >= 1.0.16, < 2; python_version < "3.3"',
  'subprocess32 >= 3.5.4, < 4; python_version < "3.2"',
]

[project.optional-dependencies]
socks = [ 'PySocks >= 1.5.6, != 1.5.7, < 2' ]
tests = [
  'ddt >= 1.2.2, < 2',
  'pytest < 6',
  'mock >= 1.0.1, < 4; python_version < "3.4"',
]

Implementierung

Parsing

from packaging.requirements import InvalidRequirement, Requirement

def parse_dependencies(config):
    dependencies = config.get('dependencies', [])
    if not isinstance(dependencies, list):
        raise TypeError('Field `project.dependencies` must be an array')

    for i, entry in enumerate(dependencies, 1):
        if not isinstance(entry, str):
            raise TypeError(f'Dependency #{i} of field `project.dependencies` must be a string')

        try:
            Requirement(entry)
        except InvalidRequirement as e:
            raise ValueError(f'Dependency #{i} of field `project.dependencies` is invalid: {e}')

    return dependencies

def parse_optional_dependencies(config):
    optional_dependencies = config.get('optional-dependencies', {})
    if not isinstance(optional_dependencies, dict):
        raise TypeError('Field `project.optional-dependencies` must be a table')

    optional_dependency_entries = {}

    for option, dependencies in optional_dependencies.items():
        if not isinstance(dependencies, list):
            raise TypeError(
                f'Dependencies for option `{option}` of field '
                '`project.optional-dependencies` must be an array'
            )

        entries = []

        for i, entry in enumerate(dependencies, 1):
            if not isinstance(entry, str):
                raise TypeError(
                    f'Dependency #{i} of option `{option}` of field '
                    '`project.optional-dependencies` must be a string'
                )

            try:
                Requirement(entry)
            except InvalidRequirement as e:
                raise ValueError(
                    f'Dependency #{i} of option `{option}` of field '
                    f'`project.optional-dependencies` is invalid: {e}'
                )
            else:
                entries.append(entry)

        optional_dependency_entries[option] = entries

    return optional_dependency_entries

Metadaten

def construct_metadata_file(metadata_object):
    """
    https://packaging.python.org/specifications/core-metadata/
    """
    metadata_file = 'Metadata-Version: 2.1\n'

    ...

    if metadata_object.dependencies:
        # Sort dependencies to ensure reproducible builds
        for dependency in sorted(metadata_object.dependencies):
            metadata_file += f'Requires-Dist: {dependency}\n'

    if metadata_object.optional_dependencies:
        # Sort extras and dependencies to ensure reproducible builds
        for option, dependencies in sorted(metadata_object.optional_dependencies.items()):
            metadata_file += f'Provides-Extra: {option}\n'
            for dependency in sorted(dependencies):
                if ';' in dependency:
                    metadata_file += f'Requires-Dist: {dependency} and extra == "{option}"\n'
                else:
                    metadata_file += f'Requires-Dist: {dependency}; extra == "{option}"\n'

    ...

    return metadata_file

Quelle: https://github.com/python/peps/blob/main/peps/pep-0631.rst

Zuletzt geändert: 2025-02-01 08:55:40 GMT