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

Python Enhancement Proposals

PEP 633 – Abhängigkeitsspezifikation in pyproject.toml mit aufgeklappter TOML-Tabelle

Autor:
Laurie Opperman <laurie_opperman at hotmail.com>, Arun Babu Neelicattu <arun.neelicattu at gmail.com>
Sponsor:
Brett Cannon <brett at python.org>
Discussions-To:
Discourse thread
Status:
Abgelehnt
Typ:
Standards Track
Thema:
Packaging
Erstellt:
02-Sep-2020
Post-History:
02-Sep-2020
Resolution:
Discourse-Nachricht

Inhaltsverzeichnis

Ablehnungsbescheid

Dieser PEP wurde zugunsten von PEP 631 abgelehnt, da dieser aufgrund seiner Beliebtheit, seiner Konsistenz mit der bestehenden Verwendung von PEP 508-Strings und seiner Kompatibilität mit bestehenden Packaging-Tool-Suiten bevorzugt wurde.

Zusammenfassung

Dieser PEP legt fest, wie die Abhängigkeiten eines Projekts in einer pyproject.toml-Datei für die Nutzung durch Packaging-bezogene Tools unter Verwendung der in PEP 621 definierten Felder geschrieben werden, als Alternative zu dem in PEP 631 definierten, auf PEP 508 basierenden Ansatz.

Motivation

Es gibt mehrere Vorteile bei der Verwendung von TOML-Tabellen und anderen Datentypen zur Darstellung von Anforderungen im Vergleich zu PEP 508-Strings.

  • Einfache anfängliche Validierung durch die TOML-Syntax.
  • Einfache sekundäre Validierung mit einem Schema, z. B. einem JSON-Schema.
  • Potenzial für Benutzer, die Schlüssel von gegebenen Funktionen zu erraten, anstatt eine Syntax auswendig zu lernen.
  • Benutzer mehrerer anderer populärer Sprachen sind möglicherweise bereits mit der TOML-Syntax vertraut.
  • TOML repräsentiert direkt die gleichen Datenstrukturen wie in JSON und damit eine Teilmenge von Python-Literalen, sodass Benutzer die Hierarchie und den Werttyp verstehen können.

Begründung

Ein Großteil davon stammt aus Diskussionen im PEP 621 Dependencies Topic. Dies enthält Elemente aus Pipfile, Poetry, Dart’s Dependencies und Rust’s Cargo. Ein Vergleichsdokument zeigt Vorteile und Nachteile zwischen diesem Format und PEP 508-Style-Spezifizierern.

Bei der Spezifikation mehrerer Anforderungen mit demselben Verteilungsnamen (bei denen Umgebungsmarker die passende Abhängigkeit wählen) ist die gewählte Lösung ähnlich der von Poetry, bei der ein Array von Anforderungen zulässig ist.

Die Schlüssel für direkte Referenzen stimmen eng mit PEP 610 und PEP 440 überein und nutzen diese, um Unterschiede im Packaging-Ökosystem zu reduzieren und auf früheren Spezifikationsarbeiten aufzubauen.

Spezifikation

Wie in PEP 621 gilt: Wenn Metadaten falsch angegeben werden, MÜSSEN Tools einen Fehler ausgeben. Die Metadaten MÜSSEN der TOML-Spezifikation entsprechen.

Um Verwirrung zu vermeiden, da dieses Dokument eine Spezifikation für die Angabe von Abhängigkeiten ist, wird das Wort „Anforderung“ verwendet, um eine PEP 508-Abhängigkeitsspezifikation zu bedeuten.

Die folgenden Tabellen werden zur project-Tabelle hinzugefügt, die in PEP 621 spezifiziert ist.

dependencies

Format: Tabelle

Die Schlüssel in dieser Tabelle sind die Namen der benötigten Distribution. Die Werte können einen der folgenden Typen haben:

  • String: Die Anforderung wird nur durch eine Versionsanforderung definiert, mit der gleichen Spezifikation wie version in der Anforderungstabelle, mit der Ausnahme, dass ein leerer String "" keine Einschränkung der Version bedeutet.
  • Tabelle: eine Anforderungstabelle.
  • Array: ein Array von Anforderungstabellen. Es ist ein Fehler, ein leeres Array [] als Wert anzugeben.

Anforderungstabelle

Die Schlüssel der Anforderungstabelle sind wie folgt (alle sind optional):

  • version (string): Ein PEP 440-Versionsspezifizierer, der eine durch Kommas getrennte Liste von Versionsspezifiziererklauseln ist. Der String MUSS nicht leer sein.
  • extras (Array von Strings): Eine Liste von PEP 508-Extra-Deklarationen für die Distribution. Die Liste MUSS nicht leer sein.
  • markers (string): Ein PEP 508-Umgebungsmarker-Ausdruck. Der String MUSS nicht leer sein.
  • url (string): Die URL des Artefakts zur Installation und Erfüllung der Anforderung. Beachten Sie, dass file:// das Präfix ist, das für Pakete verwendet wird, die aus dem lokalen Dateisystem abgerufen werden.
  • git, hg, bzr oder svn (string): Die URL eines VCS-Repositorys (wie in PEP 440 spezifiziert), das geklont wird und dessen Baum zur Erfüllung der Anforderung installiert wird. Weitere VCS-Schlüssel werden durch Ergänzungen zu PEP 610 hinzugefügt. Tools KÖNNEN sich jedoch dafür entscheiden, andere VCSs mit ihrem Befehlszeilenbefehl vor der Annahme der Ergänzung zu unterstützen.
  • revision (string): Die Kennung einer bestimmten Revision des angegebenen VCS-Repositorys, die vor der Installation ausgecheckt werden muss. Benutzer dürfen dies nur angeben, wenn einer der Schlüssel git, hg, bzr, svn oder ein anderer VCS-Schlüssel zur Identifizierung der zu installierenden Distribution verwendet wird. Revisionskennungen werden in PEP 610 vorgeschlagen.

Höchstens einer der folgenden Schlüssel kann gleichzeitig angegeben werden, da sie sich logisch widersprechen: version, url, git, hg, bzr, svn und jeder andere VCS-Schlüssel.

Eine leere Anforderungstabelle {} stellt zusätzlich zum leeren String "" keine Einschränkung der Anforderung dar.

Alle angegebenen Schlüssel, die in diesem Dokument nicht spezifiziert sind, MÜSSEN zu einem Fehler beim Parsen führen.

optionale-abhängigkeiten

Format: Tabelle

Die Schlüssel in dieser Tabelle sind die Namen der benötigten Distribution eines Extras. Die Werte können einen der folgenden Typen haben:

  • Tabelle: eine Anforderungstabelle.
  • Array: Ein Array von Anforderungstabellen.

Diese Anforderungstabellen haben die gleiche Spezifikation wie oben, mit der zusätzlichen folgenden erforderlichen Schlüssel:

  • for-extra (string): Der Name des PEP 508-Extras, für das diese Anforderung benötigt wird.

Referenzimplementierung

Tools müssen dieses Format in PEP 508-Anforderungsstrings konvertieren. Nachfolgend finden Sie ein Beispiel für die Implementierung dieser Konvertierung (unter der Annahme, dass die Validierung bereits durchgeführt wurde).

def convert_requirement_to_pep508(name, requirement):
    if isinstance(requirement, str):
        requirement = {"version": requirement}
    pep508 = name
    if "extras" in requirement:
        pep508 += " [" + ", ".join(requirement["extras"]) + "]"
    if "version" in requirement:
        pep508 += " " + requirement["version"]
    if "url" in requirement:
        pep508 += " @ " + requirement["url"]
    for vcs in ("git", "hg", "bzr", "svn"):
        if vcs in requirement:
            pep508 += " @ " + vcs + "+" + requirement[vcs]
            if "revision" in requirement:
                pep508 += "@" + requirement["revision"]
    extra = None
    if "for-extra" in requirement:
        extra = requirement["for-extra"]
    if "markers" in requirement:
        markers = requirement["markers"]
        if extra:
            markers = "extra = '" + extra + "' and (" + markers + ")"
        pep508 += "; " + markers
    return pep508, extra


def convert_requirements_to_pep508(dependencies):
    pep508s = []
    extras = set()
    for name, req in dependencies.items():
        if isinstance(req, list):
            for sub_req in req:
                pep508, extra = convert_requirement_to_pep508(name, sub_req)
                pep508s.append(pep508)
                if extra:
                    extras.add(extra)
        else:
            pep508, extra = convert_requirement_to_pep508(name, req)
            pep508s.append(pep508)
            if extra:
                extras.add(extra)
    return pep508s, extras


def convert_project_requirements_to_pep508(project):
    reqs, _ = convert_requirements_to_pep508(project.get("dependencies", {}))
    optional_reqs, extras = convert_requirements_to_pep508(
        project.get("optional-dependencies", {})
    )
    reqs += optional_reqs
    return reqs, extras

JSON-Schema

Für die anfängliche Validierung kann ein JSON-Schema verwendet werden. Dies hilft nicht nur Tools bei einer konsistenten Validierung, sondern ermöglicht es Code-Editoren, Validierungsfehler hervorzuheben, während Benutzer die Abhängigkeitsliste erstellen.

{
    "$id": "spam",
    "$schema": "http://json-schema.org/draft-07/schema#",
    "title": "Project metadata",
    "type": "object",
    "definitions": {
        "requirementTable": {
            "title": "Full project dependency specification",
            "type": "object",
            "properties": {
                "extras": {
                    "title": "Dependency extras",
                    "type": "array",
                    "items": {
                        "title": "Dependency extra",
                        "type": "string"
                    }
                },
                "markers": {
                    "title": "Dependency environment markers",
                    "type": "string"
                }
            },
            "propertyNames": {
                "enum": [
                    "extras",
                    "markers",
                    "version",
                    "url",
                    "git",
                    "hg",
                    "bzr",
                    "svn",
                    "for-extra"
                ]
            },
            "oneOf": [
                {
                    "title": "Version requirement",
                    "properties": {
                        "version": {
                            "title": "Version",
                            "type": "string"
                        }
                    }
                },
                {
                    "title": "URL requirement",
                    "properties": {
                        "url": {
                            "title": "URL",
                            "type": "string",
                            "format": "uri"
                        }
                    },
                    "required": [
                        "url"
                    ]
                },
                {
                    "title": "VCS requirement",
                    "properties": {
                        "revision": {
                            "title": "VCS repository revision",
                            "type": "string"
                        }
                    },
                    "oneOf": [
                        {
                            "title": "Git repository",
                            "properties": {
                                "git": {
                                    "title": "Git URL",
                                    "type": "string",
                                    "format": "uri"
                                }
                            },
                            "required": [
                                "git"
                            ]
                        },
                        {
                            "title": "Mercurial repository",
                            "properties": {
                                "hg": {
                                    "title": "Mercurial URL",
                                    "type": "string",
                                    "format": "uri"
                                }
                            },
                            "required": [
                                "hg"
                            ]
                        },
                        {
                            "title": "Bazaar repository",
                            "properties": {
                                "bzr": {
                                    "title": "Bazaar URL",
                                    "type": "string",
                                    "format": "uri"
                                }
                            },
                            "required": [
                                "bzr"
                            ]
                        },
                        {
                            "title": "Subversion repository",
                            "properties": {
                                "svn": {
                                    "title": "Subversion URL",
                                    "type": "string",
                                    "format": "uri"
                                }
                            },
                            "required": [
                                "svn"
                            ]
                        }
                    ]
                }
            ]
        },
        "requirementVersion": {
            "title": "Version project dependency specification",
            "type": "string"
        },
        "requirement": {
            "title": "Project dependency specification",
            "oneOf": [
                {
                    "$ref": "#/definitions/requirementVersion"
                },
                {
                    "$ref": "#/definitions/requirementTable"
                },
                {
                    "title": "Multiple specifications",
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/requirementTable"
                    },
                    "minLength": 1
                }
            ]
        },
        "optionalRequirementTable": {
            "title": "Project optional dependency specification table",
            "allOf": [
                {
                    "$ref": "#/definitions/requirementTable"
                },
                {
                    "properties": {
                        "for-extra": {
                            "title": "Dependency's extra",
                            "type": "string"
                        }
                    },
                    "required": [
                        "for-extra"
                    ]
                }
            ]
        },
        "optionalRequirement": {
            "title": "Project optional dependency specification",
            "oneOf": [
                {
                    "$ref": "#/definitions/optionalRequirementTable"
                },
                {
                    "title": "Multiple specifications",
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/optionalRequirementTable"
                    },
                    "minLength": 1
                }
            ]
        }
    },
    "properties": {
        "dependencies": {
            "title": "Project dependencies",
            "type": "object",
            "additionalProperties": {
                "$ref": "#/definitions/requirement"
            }
        },
        "optional-dependencies": {
            "title": "Project dependencies",
            "type": "object",
            "additionalProperties": {
                "$ref": "#/definitions/optionalRequirement"
            }
        }
    }
}

Beispiele

Vollständiges künstliches Beispiel

[project.dependencies]
flask = { }
django = { }
requests = { version = ">= 2.8.1, == 2.8.*", extras = ["security", "tests"], markers = "python_version < '2.7'" }
pip = { url = "https://github.com/pypa/pip/archive/1.3.1.zip" }
sphinx = { git = "ssh://git@github.com/sphinx-doc/sphinx.git" }
numpy = "~=1.18"
pytest = [
    { version = "<6", markers = "python_version < '3.5'" },
    { version = ">=6", markers = "python_version >= '3.5'" },
]

[project.optional-dependencies]
pytest-timout = { for-extra = "dev" }
pytest-mock = [
    { version = "<6", markers = "python_version < '3.5'", for-extra = "dev" },
    { version = ">=6", markers = "python_version >= '3.5'", for-extra = "dev" },
]

In Anlehnung an PEP 631 ist das Folgende eine äquivalente Abhängigkeitsspezifikation für docker-compose.

[project.dependencies]
cached-property = ">= 1.2.0, < 2"
distro = ">= 1.2.0, < 2"
docker = { extras = ["ssh"], version = ">= 4.2.2, < 5" }
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" = { version = "== 1.0.0", markers = "python_version < '3.3'" }
"backports.ssl_match_hostname" = { version = ">= 3.5, < 4", markers = "python_version < '3.5'" }
colorama = { version = ">= 0.4, < 1", markers = "sys_platform == 'win32'" }
enum34 = { version = ">= 1.0.4, < 2", markers = "python_version < '3.4'" }
ipaddress = { version = ">= 1.0.16, < 2", markers = "python_version < '3.3'" }
subprocess32 = { version = ">= 3.5.4, < 4", markers = "python_version < '3.2'" }

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

Kompatibilitätsbeispiele

Die Autoren dieses PEP erkennen an, dass verschiedene Tools sowohl aus diesem Format lesen als auch in dieses schreiben müssen, um Abhängigkeiten zu spezifizieren. Dieser Abschnitt zielt darauf ab, einen direkten Vergleich mit dem derzeit verwendeten Standard, PEP 508, sowie Beispiele für die Übersetzung zu und von diesem Standard bereitzustellen.

Hinweis

Zur Vereinfachung und Klarheit werden verschiedene Möglichkeiten, wie TOML die Spezifikation ermöglicht, nicht dargestellt. Diese Beispiele verwenden die standardmäßige Inline-Darstellung.

Zum Beispiel, während die folgenden denkbaren gleichwertigen Darstellungen in TOML sind, wählen wir die zweite Form für die Beispiele in diesem Abschnitt.

aiohttp.version = "== 3.6.2"
aiohttp = { version = "== 3.6.2" }

Versionsbeschränkte Abhängigkeiten

Keine Versionsbeschränkung

aiohttp
aiohttp = {}

Einfache Versionsbeschränkung

aiohttp >= 3.6.2, < 4.0.0
aiohttp = { version = ">= 3.6.2, < 4.0.0" }

Hinweis

Dies kann aus Gründen der Kürze auch als String dargestellt werden.

aiohttp = ">= 3.6.2, < 4.0.0"

Direkte Referenzabhängigkeiten

URL-Abhängigkeit

aiohttp @ https://files.pythonhosted.org/packages/97/d1/1cc7a1f84097d7abdc6c09ee8d2260366f081f8e82da36ebb22a25cdda9f/aiohttp-3.6.2-cp35-cp35m-macosx_10_13_x86_64.whl
aiohttp = { url = "https://files.pythonhosted.org/packages/97/d1/1cc7a1f84097d7abdc6c09ee8d2260366f081f8e82da36ebb22a25cdda9f/aiohttp-3.6.2-cp35-cp35m-macosx_10_13_x86_64.whl" }

VCS-Abhängigkeit

aiohttp @ git+ssh://git@github.com/aio-libs/aiohttp.git@master
aiohttp = { git = "ssh://git@github.com/aio-libs/aiohttp.git", revision = "master" }

Umgebungsmarker

aiohttp >= 3.6.1; python_version >= '3.8'
aiohttp = { version = ">= 3.6.1", markers = "python_version >= '3.8'" }

Ein leicht erweitertes Beispiel für das Obige, bei dem eine bestimmte Version von aiohttp basierend auf der Interpreterversion benötigt wird.

aiohttp >= 3.6.1; python_version >= '3.8'
aiohttp >= 3.0.0, < 3.6.1; python_version < '3.8'
aiohttp = [
    { version = ">= 3.6.1", markers = "python_version >= '3.8'" },
    { version = ">= 3.0.0, < 3.6.1", markers = "python_version < '3.8'" }
]

Paket-Extras

Spezifikation der Abhängigkeit für ein Paket-Extra

aiohttp >= 3.6.2; extra == 'http'
aiohttp = { version = ">= 3.6.2", for-extra = "http" }

Verwendung von Extras aus einer Abhängigkeit

aiohttp [speedups] >= 3.6.2
aiohttp = { version = ">= 3.6.2", extras = ["speedups"] }

Komplexe Beispiele

Versionsbeschränkung

aiohttp [speedups] >= 3.6.2; python_version >= '3.8' and extra == 'http'
aiohttp = { version = ">= 3.6.2", extras = ["speedups"], markers = "python_version >= '3.8'", for-extra = "http" }

Direkte Referenz (VCS)

aiohttp [speedups] @ git+ssh://git@github.com/aio-libs/aiohttp.git@master ; python_version >= '3.8' and extra == 'http'
aiohttp = { git = "ssh://git@github.com/aio-libs/aiohttp.git", revision = "master", extras = ["speedups"], markers = "python_version >= '3.8'", for-extra = "http" }

Abgelehnte Ideen

Wechsel zu einem Array für dependencies

Verwenden Sie ein Array anstelle einer Tabelle, damit jedes Element nur eine Tabelle (mit einem name-Schlüssel) und keine Arrays von Anforderungstabellen ist. Dies war im TOML-Format sehr ausführlich und einschränkend, und das Vorhandensein mehrerer Anforderungen für eine gegebene Distribution ist nicht sehr verbreitet.

Ersetzen von optional-dependencies durch extras

Entfernen Sie die Tabelle optional-dependencies zugunsten der Aufnahme eines optional-Schlüssels in die Anforderung und einer extras-Tabelle, die angibt, welche (optionalen) Anforderungen für ein Projekt-Extra benötigt werden. Dies reduziert die Anzahl der Tabellen mit der gleichen Spezifikation (auf 1) und ermöglicht es, Anforderungen einmal zu spezifizieren, aber in mehreren Extras zu verwenden. Dies entfernt jedoch einige Eigenschaften der Anforderung (zu welchem Extra sie gehört), gruppiert erforderliche und optionale Abhängigkeiten zusammen (möglicherweise gemischt), und es gibt möglicherweise keine einfache Möglichkeit, eine Anforderung auszuwählen, wenn eine Distribution mehrere Anforderungen hat. Dies wurde abgelehnt, da optional-dependencies bereits im Entwurf von PEP 621 verwendet wurde.

direct-Tabelle in der Anforderung

Fügen Sie die Schlüssel für direkte Referenzen in eine direct-Tabelle ein, wobei der VCS als Wert eines vcs-Schlüssels angegeben wird. Dies war expliziter und leichter in eine JSON-Schema-Validierung einzubeziehen, wurde aber als zu umständlich und nicht so lesbar entschieden.

Hash einschließen

Fügen Sie Hashes in Direktverweis-Anforderungen ein. Dies war nur für Paket-Lockfiles gedacht und hatte keinen wirklichen Platz in den Projektmetadaten.

Abhängigkeitstabellen für jedes Extra

Machen Sie optional-dependencies zu einer Tabelle von Abhängigkeitstabellen für jedes Extra, wobei der Tabellenname der Name des Extras ist. Dies macht optional-dependencies zu einem anderen Typ (Tabelle von Tabellen von Anforderungen) als dependencies (Tabelle von Anforderungen), was für Benutzer verwirrend und schwieriger zu parsen sein könnte.

Umgebungsmarker-Schlüssel

Machen Sie jeden PEP 508-Umgebungsmarker als Schlüssel (oder Schlüssel einer Untertabelle) in der Anforderung. Dies erhöht argumentativ die Lesbarkeit und einfache Verarbeitung. Der Schlüssel markers wäre weiterhin für fortgeschrittenere Spezifikationen zulässig, mit denen die schlüsselbasierten Umgebungsmarker mit dem Ergebnis verknüpft werden. Dies wurde zurückgestellt, da mehr Designarbeit erforderlich ist.

Mehrere Extras, die eine Anforderung erfüllen kann

Ersetzen Sie den Schlüssel for-extra durch for-extras, wobei der Wert ein Array von Extras ist, die die Anforderung erfüllt. Dies reduziert eine gewisse Duplizierung, aber in diesem Fall macht diese Duplizierung explizit, welche Extras welche Abhängigkeiten haben.


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

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