Movatterモバイル変換


[0]ホーム

URL:


ContentsMenuExpandLight modeDark modeAuto light/dark, in light modeAuto light/dark, in dark modeSkip to content
Python Packaging User Guide
Python Packaging User Guide
Back to top

Dependency Groups

This specification defines dependency groups, a mechanism for storing packagerequirements inpyproject.toml files such that they are not included inproject metadata when it is built.

Dependency groups are suitable for internal development use-cases like lintingand testing, as well as for projects which are not built for distribution, likecollections of related scripts.

Fundamentally, dependency groups should be thought of as being a standardizedsubset of the capabilities ofrequirements.txt files (which arepip-specific).

Specification

Examples

This is a simple table which showsdocs andtest groups:

[dependency-groups]docs=["sphinx"]test=["pytest>7","coverage"]

and a similar table which definesdocs,test, andcoverage groups:

[dependency-groups]docs=["sphinx"]coverage=["coverage[toml]"]test=["pytest>7",{include-group="coverage"}]

The[dependency-groups] Table

Dependency groups are defined as a table inpyproject.toml nameddependency-groups. Thedependency-groups table contains an arbitrarynumber of user-defined keys, each of which has, as its value, a list ofrequirements.

[dependency-groups] keys, sometimes also called “group names”, must bevalid non-normalized names. Tools which handle DependencyGroups MUSTnormalize these names beforecomparisons.

Tools SHOULD prefer to present the original, non-normalized name to users, andif duplicate names are detected after normalization, tools SHOULD emit anerror.

Requirement lists, the values in[dependency-groups], may contain strings,tables (dict in Python), or a mix of strings and tables. Strings must bevaliddependency specifiers, and tables must bevalid Dependency Group Includes.

Dependency Group Include

A Dependency Group Include includes another Dependency Group in the currentgroup.

An include is a table with exactly one key,"include-group", whose value isa string, the name of another Dependency Group.

Includes are defined to be exactly equivalent to the contents of the namedDependency Group, inserted into the current group at the location of the include.For example, iffoo=["a","b"] is one group, andbar=["c",{include-group="foo"},"d"] is another, thenbar shouldevaluate to["c","a","b","d"] when Dependency Group Includes are expanded.

Dependency Group Includes may specify the same package multiple times.Tools SHOULD NOT deduplicate or otherwise alter the list contents produced by theinclude. For example, given the following table:

[dependency-groups]group-a=["foo"]group-b=["foo>1.0"]group-c=["foo<1.0"]all=["foo",{include-group="group-a"},{include-group="group-b"},{include-group="group-c"},]

The resolved value ofall SHOULD be["foo","foo","foo>1.0","foo<1.0"].Tools should handle such a list exactly as they would handle any other case inwhich they are asked to process the same requirement multiple times withdifferent version constraints.

Dependency Group Includes may include groups containing Dependency Group Includes,in which case those includes should be expanded as well. Dependency Group IncludesMUST NOT include cycles, and tools SHOULD report an error if they detect a cycle.

Package Building

Build backends MUST NOT include Dependency Group data in built distributions aspackage metadata. This means that sdistPKG-INFO and wheelMETADATAfiles should not include referenceable fields containing dependency groups.

It is, however, valid to use dependency groups in the evaluation of dynamicmetadata, andpyproject.toml files included in sdists will still contain[dependency-groups]. However, the table’s contents are not part of a builtpackage’s interfaces.

Installing Dependency Groups & Extras

There is no syntax or specification-defined interface for installing orreferring to dependency groups. Tools are expected to provide dedicatedinterfaces for this purpose.

Tools MAY choose to provide the same or similar interfaces for interactingwith dependency groups as they do for managing extras. Tools authors areadvised that the specification does not forbid having an extra whose namematches a Dependency Group. Separately, users are advised to avoid creatingdependency groups whose names match extras, and tools MAY treat such matchingas an error.

Validation and Compatibility

Tools supporting dependency groups may want to validate data before using it.When implementing such validation, authors should be aware of the possibilityof future extensions to the specification, so that they do not unnecessarilyemit errors or warnings.

Tools SHOULD error when evaluating or processing unrecognized data independency groups.

Tools SHOULD NOT eagerly validate the contents ofall dependency groupsunless they have a need to do so.

This means that in the presence of the following data, most tools should allowthefoo group to be used and only error if thebar group is used:

[dependency-groups]foo=["pyparsing"]bar=[{set-phasers-to="stun"}]

Note

There are several known cases of tools which have good cause to bestricter. Linters and validators are an example, as their purpose is tovalidate the contents of all dependency groups.

Reference Implementation

The following Reference Implementation prints the contents of a DependencyGroup to stdout, newline delimited.The output is therefore validrequirements.txt data.

importreimportsysimporttomllibfromcollectionsimportdefaultdictfrompackaging.requirementsimportRequirementdef_normalize_name(name:str)->str:returnre.sub(r"[-_.]+","-",name).lower()def_normalize_group_names(dependency_groups:dict)->dict:original_names=defaultdict(list)normalized_groups={}forgroup_name,valueindependency_groups.items():normed_group_name=_normalize_name(group_name)original_names[normed_group_name].append(group_name)normalized_groups[normed_group_name]=valueerrors=[]fornormed_name,namesinoriginal_names.items():iflen(names)>1:errors.append(f"{normed_name} ({', '.join(names)})")iferrors:raiseValueError(f"Duplicate dependency group names:{', '.join(errors)}")returnnormalized_groupsdef_resolve_dependency_group(dependency_groups:dict,group:str,past_groups:tuple[str,...]=())->list[str]:ifgroupinpast_groups:raiseValueError(f"Cyclic dependency group include:{group} ->{past_groups}")ifgroupnotindependency_groups:raiseLookupError(f"Dependency group '{group}' not found")raw_group=dependency_groups[group]ifnotisinstance(raw_group,list):raiseValueError(f"Dependency group '{group}' is not a list")realized_group=[]foriteminraw_group:ifisinstance(item,str):# packaging.requirements.Requirement parsing ensures that this is a valid# PEP 508 Dependency Specifier# raises InvalidRequirement on failureRequirement(item)realized_group.append(item)elifisinstance(item,dict):iftuple(item.keys())!=("include-group",):raiseValueError(f"Invalid dependency group item:{item}")include_group=_normalize_name(next(iter(item.values())))realized_group.extend(_resolve_dependency_group(dependency_groups,include_group,past_groups+(group,)))else:raiseValueError(f"Invalid dependency group item:{item}")returnrealized_groupdefresolve(dependency_groups:dict,group:str)->list[str]:ifnotisinstance(dependency_groups,dict):raiseTypeError("Dependency Groups table is not a dict")ifnotisinstance(group,str):raiseTypeError("Dependency group name is not a str")return_resolve_dependency_group(dependency_groups,group)if__name__=="__main__":withopen("pyproject.toml","rb")asfp:pyproject=tomllib.load(fp)dependency_groups_raw=pyproject["dependency-groups"]dependency_groups=_normalize_group_names(dependency_groups_raw)print("\n".join(resolve(pyproject["dependency-groups"],sys.argv[1])))

History

  • October 2024: This specification was approved throughPEP 735.

On this page

[8]ページ先頭

©2009-2025 Movatter.jp