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 specifiers

This document describes the dependency specifiers format as originally specifiedinPEP 508.

The job of a dependency is to enable tools like pip[1] to find the rightpackage to install. Sometimes this is very loose - just specifying a name, andsometimes very specific - referring to a specific file to install. Sometimesdependencies are only relevant in one platform, or only some versions areacceptable, so the language permits describing all these cases.

The language defined is a compact line based format which is already inwidespread use in pip requirements files, though we do not specify the commandline option handling that those files permit. There is one caveat - theURL reference form, specified inVersioning specifier specificationis not actually implemented in pip, but we use that format ratherthan pip’s current native format.

Specification

Examples

All features of the language shown with a name based lookup:

requests [security,tests] >= 2.8.1, == 2.8.* ; python_version < "2.7"

A minimal URL based lookup:

pip @ https://github.com/pypa/pip/archive/1.3.1.zip#sha1=da9234ee9982d4bbb3c72346a6de940a148ea686

Concepts

A dependency specification always specifies a distribution name. It mayinclude extras, which expand the dependencies of the named distribution toenable optional features. The version installed can be controlled usingversion limits, or giving the URL to a specific artifact to install. Finallythe dependency can be made conditional using environment markers.

Grammar

We first cover the grammar briefly and then drill into the semantics of eachsection later.

A distribution specification is written in ASCII text. We use a parsley[2] grammar to provide a precise grammar. It is expected that thespecification will be embedded into a larger system which offers framing suchas comments, multiple line support via continuations, or other such features.

The full grammar including annotations to build a useful parse tree isincluded at the end of this document.

Versions may be specified according to the rules of theVersion specifier specification. (Note:URI is defined instd-66):

version_cmp   = wsp* '<=' | '<' | '!=' | '===' | '==' | '>=' | '>' | '~='version       = wsp* ( letterOrDigit | '-' | '_' | '.' | '*' | '+' | '!' )+version_one   = version_cmp version wsp*version_many  = version_one (',' version_one)* (',' wsp*)?versionspec   = ( '(' version_many ')' ) | version_manyurlspec       = '@' wsp* <URI_reference>

Environment markers allow making a specification only take effect in someenvironments:

marker_op     = version_cmp | (wsp+ 'in' wsp+) | (wsp+ 'not' wsp+ 'in' wsp+)python_str_c  = (wsp | letter | digit | '(' | ')' | '.' | '{' | '}' |                 '-' | '_' | '*' | '#' | ':' | ';' | ',' | '/' | '?' |                 '[' | ']' | '!' | '~' | '`' | '@' | '$' | '%' | '^' |                 '&' | '=' | '+' | '|' | '<' | '>' )dquote        = '"'squote        = '\\''python_str    = (squote (python_str_c | dquote)* squote |                 dquote (python_str_c | squote)* dquote)env_var       = ('python_version' | 'python_full_version' |                 'os_name' | 'sys_platform' | 'platform_release' |                 'platform_system' | 'platform_version' |                 'platform_machine' | 'platform_python_implementation' |                 'implementation_name' | 'implementation_version' |                 'extra' | 'extras' | 'dependency_groups' # ONLY when defined by a containing layer                 )marker_var    = wsp* (env_var | python_str)marker_expr   = marker_var marker_op marker_var              | wsp* '(' marker wsp* ')'marker_and    = marker_expr wsp* 'and' marker_expr              | marker_exprmarker_or     = marker_and wsp* 'or' marker_and                  | marker_andmarker        = marker_orquoted_marker = ';' wsp* marker

Optional components of a distribution may be specified using the extrasfield:

identifier_end = letterOrDigit | (('-' | '_' | '.' )* letterOrDigit)identifier    = letterOrDigit identifier_end*name          = identifierextras_list   = identifier (wsp* ',' wsp* identifier)*extras        = '[' wsp* extras_list? wsp* ']'

Restrictions on names for extras is defined inPEP 685.

Giving us a rule for name based requirements:

name_req      = name wsp* extras? wsp* versionspec? wsp* quoted_marker?

And a rule for direct reference specifications:

url_req       = name wsp* extras? wsp* urlspec (wsp+ quoted_marker?)?

Leading to the unified rule that can specify a dependency.:

specification = wsp* ( url_req | name_req ) wsp*

Whitespace

Non line-breaking whitespace is mostly optional with no semantic meaning. Thesole exception is detecting the end of a URL requirement.

Names

Python distribution names are currently defined inPEP 345. Namesact as the primary identifier for distributions. They are present in alldependency specifications, and are sufficient to be a specification on theirown. However, PyPI places strict restrictions on names - they must match acase insensitive regex or they won’t be accepted. Accordingly, in thisdocument we limit the acceptable values for identifiers to that regex. A fullredefinition of name may take place in a future metadata PEP. The regex (runwith re.IGNORECASE) is:

^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])\Z

Extras

An extra is an optional part of a distribution. Distributions can specify asmany extras as they wish, and each extra results in the declaration ofadditional dependencies of the distributionwhen the extra is used in adependency specification. For instance:

requests[security,tests]

Extras union in the dependencies they define with the dependencies of thedistribution they are attached to. The example above would result in requestsbeing installed, and requests own dependencies, and also any dependencies thatare listed in the “security” extra of requests.

If multiple extras are listed, all the dependencies are unioned together.

Versions

See theVersion specifier specification formore detail on both version numbers and version comparisons. Versionspecifications limit the versions of a distribution that can beused. They only apply to distributions looked up by name, rather thanvia a URL. Version comparison are also used in the markers feature. Theoptional brackets around a version are present for compatibility withPEP 345 but should not be generated, only accepted.

Environment Markers

Environment markers allow a dependency specification to provide a rule thatdescribes when the dependency should be used. For instance, consider a packagethat needs argparse. In Python 2.7 argparse is always present. On older Pythonversions it has to be installed as a dependency. This can be expressed as so:

argparse;python_version<"2.7"

A marker expression evaluates to either True or False. When it evaluates toFalse, the dependency specification should be ignored.

The marker language is inspired by Python itself, chosen for the ability tosafely evaluate it without running arbitrary code that could become a securityvulnerability. Markers were first standardised inPEP 345. This documentfixes some issues that were observed in the design described inPEP 426.

Comparisons in marker expressions are typed by the comparison operator and thetype of the marker value. The <marker_op> operators that are not in<version_cmp> perform the same as they do for strings or sets in Python based onwhether the marker value is a string or set itself. The <version_cmp> operatorsuse the version comparison rules of theVersion specifier specification when those aredefined (that is when both sides have a valid version specifier). If there is nodefined behaviour of this specification and the operator exists in Python, thenthe operator falls back to the Python behaviour for the types involved.Otherwise an error should be raised. e.g. the following will result in errors:

"dog" ~= "fred"python_version ~= "surprise"

User supplied constants are always encoded as strings with either' or" quote marks. Note that backslash escapes are not defined, but existingimplementations do support them. They are not included in thisspecification because they add complexity and there is no observable need forthem today. Similarly we do not define non-ASCII character support: all theruntime variables we are referencing are expected to be ASCII-only.

The variables in the marker grammar such as “os_name” resolve to values lookedup in the Python runtime. With the exception of “extra” all values are definedon all Python versions today - it is an error in the implementation of markersif a value is not defined.

Unknown variables must raise an error rather than resulting in a comparisonthat evaluates to True or False.

Variables whose value cannot be calculated on a given Python implementationshould evaluate to0 for versions, and an empty string for all othervariables.

The “extra” variable is special. It is used by wheels to signal whichspecifications apply to a given extra in the wheelMETADATA file, butsince theMETADATA file is based on a draft version ofPEP 426, there isno current specification for this. Regardless, outside of a context where thisspecial handling is taking place, the “extra” variable should result in anerror like all other unknown variables.

The “extras” and “dependency_groups” variables are also special. They are usedto specify any requested extras or dependency groups when installing from a lockfile. Outside of the context of lock files, these two variables should result inan error like all other unknown variables.

Marker

Python equivalent

Type

Sample values

os_name

os.name

String

posix,java

sys_platform

sys.platform

String

linux,linux2,darwin,java1.8.0_51 (note that “linux”is from Python3 and “linux2” from Python2)

platform_machine

platform.machine()

String

x86_64

platform_python_implementation

platform.python_implementation()

String

CPython,Jython

platform_release

platform.release()

String

3.14.1-x86_64-linode39,14.5.0,1.8.0_51

platform_system

platform.system()

String

Linux,Windows,Java

platform_version

platform.version()

String

#1SMPFriApr2513:07:35EDT2014JavaHotSpot(TM)64-BitServerVM,25.51-b03,OracleCorporationDarwinKernelVersion14.5.0:WedJul2902:18:53PDT2015;root:xnu-2782.40.9~2/RELEASE_X86_64

python_version

'.'.join(platform.python_version_tuple()[:2])

Version

3.4,2.7

python_full_version

platform.python_version()

Version

3.4.0,3.5.0b1

implementation_name

sys.implementation.name

String

cpython

implementation_version

see definition below

Version

3.4.0,3.5.0b1

extra

An error except when defined by the context interpreting thespecification.

String

toml

extras

An error except when defined by the context interpreting thespecification.

Set of strings

{"toml"}

dependency_groups

An error except when defined by the context interpreting thespecification.

Set of strings

{"test"}

Theimplementation_version marker variable is derived fromsys.implementation.version:

defformat_full_version(info):version='{0.major}.{0.minor}.{0.micro}'.format(info)kind=info.releaselevelifkind!='final':version+=kind[0]+str(info.serial)returnversionifhasattr(sys,'implementation'):implementation_version=format_full_version(sys.implementation.version)else:implementation_version="0"

This environment markers section, initially defined throughPEP 508, supersedes the environment markerssection inPEP 345.

Complete Grammar

The complete parsley grammar:

wsp           = ' ' | '\t'version_cmp   = wsp* <'<=' | '<' | '!=' | '===' | '==' | '>=' | '>' | '~='>version       = wsp* <( letterOrDigit | '-' | '_' | '.' | '*' | '+' | '!' )+>version_one   = version_cmp:op version:v wsp* -> (op, v)version_many  = version_one:v1 (',' version_one)*:v2 (',' wsp*)? -> [v1] + v2versionspec   = ('(' version_many:v ')' ->v) | version_manyurlspec       = '@' wsp* <URI_reference>marker_op     = version_cmp | (wsp* 'in') | (wsp* 'not' wsp+ 'in')python_str_c  = (wsp | letter | digit | '(' | ')' | '.' | '{' | '}' |                 '-' | '_' | '*' | '#' | ':' | ';' | ',' | '/' | '?' |                 '[' | ']' | '!' | '~' | '`' | '@' | '$' | '%' | '^' |                 '&' | '=' | '+' | '|' | '<' | '>' )dquote        = '"'squote        = '\\''python_str    = (squote <(python_str_c | dquote)*>:s squote |                 dquote <(python_str_c | squote)*>:s dquote) -> senv_var       = ('python_version' | 'python_full_version' |                 'os_name' | 'sys_platform' | 'platform_release' |                 'platform_system' | 'platform_version' |                 'platform_machine' | 'platform_python_implementation' |                 'implementation_name' | 'implementation_version' |                 'extra' | 'extras' | 'dependency_groups' # ONLY when defined by a containing layer                 ):varname -> lookup(varname)marker_var    = wsp* (env_var | python_str)marker_expr   = marker_var:l marker_op:o marker_var:r -> (o, l, r)              | wsp* '(' marker:m wsp* ')' -> mmarker_and    = marker_expr:l wsp* 'and' marker_expr:r -> ('and', l, r)              | marker_expr:m -> mmarker_or     = marker_and:l wsp* 'or' marker_and:r -> ('or', l, r)                  | marker_and:m -> mmarker        = marker_orquoted_marker = ';' wsp* markeridentifier_end = letterOrDigit | (('-' | '_' | '.' )* letterOrDigit)identifier    = < letterOrDigit identifier_end* >name          = identifierextras_list   = identifier:i (wsp* ',' wsp* identifier)*:ids -> [i] + idsextras        = '[' wsp* extras_list?:e wsp* ']' -> ename_req      = (name:n wsp* extras?:e wsp* versionspec?:v wsp* quoted_marker?:m                 -> (n, e or [], v or [], m))url_req       = (name:n wsp* extras?:e wsp* urlspec:v (wsp+ | end) quoted_marker?:m                 -> (n, e or [], v, m))specification = wsp* ( url_req | name_req ):s wsp* -> s# The result is a tuple - name, list-of-extras,# list-of-version-constraints-or-a-url, marker-ast or NoneURI_reference = <URI | relative_ref>URI           = scheme ':' hier_part ('?' query )? ( '#' fragment)?hier_part     = ('//' authority path_abempty) | path_absolute | path_rootless | path_emptyabsolute_URI  = scheme ':' hier_part ( '?' query )?relative_ref  = relative_part ( '?' query )? ( '#' fragment )?relative_part = '//' authority path_abempty | path_absolute | path_noscheme | path_emptyscheme        = letter ( letter | digit | '+' | '-' | '.')*authority     = ( userinfo '@' )? host ( ':' port )?userinfo      = ( unreserved | pct_encoded | sub_delims | ':')*host          = IP_literal | IPv4address | reg_nameport          = digit*IP_literal    = '[' ( IPv6address | IPvFuture) ']'IPvFuture     = 'v' hexdig+ '.' ( unreserved | sub_delims | ':')+IPv6address   = (                  ( h16 ':'){6} ls32                  | '::' ( h16 ':'){5} ls32                  | ( h16 )?  '::' ( h16 ':'){4} ls32                  | ( ( h16 ':')? h16 )? '::' ( h16 ':'){3} ls32                  | ( ( h16 ':'){0,2} h16 )? '::' ( h16 ':'){2} ls32                  | ( ( h16 ':'){0,3} h16 )? '::' h16 ':' ls32                  | ( ( h16 ':'){0,4} h16 )? '::' ls32                  | ( ( h16 ':'){0,5} h16 )? '::' h16                  | ( ( h16 ':'){0,6} h16 )? '::' )h16           = hexdig{1,4}ls32          = ( h16 ':' h16) | IPv4addressIPv4address   = dec_octet '.' dec_octet '.' dec_octet '.' dec_octetnz            = ~'0' digitdec_octet     = (                  digit # 0-9                  | nz digit # 10-99                  | '1' digit{2} # 100-199                  | '2' ('0' | '1' | '2' | '3' | '4') digit # 200-249                  | '25' ('0' | '1' | '2' | '3' | '4' | '5') )# %250-255reg_name = ( unreserved | pct_encoded | sub_delims)*path = (        path_abempty # begins with '/' or is empty        | path_absolute # begins with '/' but not '//'        | path_noscheme # begins with a non-colon segment        | path_rootless # begins with a segment        | path_empty ) # zero characterspath_abempty  = ( '/' segment)*path_absolute = '/' ( segment_nz ( '/' segment)* )?path_noscheme = segment_nz_nc ( '/' segment)*path_rootless = segment_nz ( '/' segment)*path_empty    = pchar{0}segment       = pchar*segment_nz    = pchar+segment_nz_nc = ( unreserved | pct_encoded | sub_delims | '@')+                # non-zero-length segment without any colon ':'pchar         = unreserved | pct_encoded | sub_delims | ':' | '@'query         = ( pchar | '/' | '?')*fragment      = ( pchar | '/' | '?')*pct_encoded   = '%' hexdigunreserved    = letter | digit | '-' | '.' | '_' | '~'reserved      = gen_delims | sub_delimsgen_delims    = ':' | '/' | '?' | '#' | '(' | ')?' | '@'sub_delims    = '!' | '$' | '&' | '\\'' | '(' | ')' | '*' | '+' | ',' | ';' | '='hexdig        = digit | 'a' | 'A' | 'b' | 'B' | 'c' | 'C' | 'd' | 'D' | 'e' | 'E' | 'f' | 'F'

A test program - if the grammar is in a stringgrammar:

importosimportsysimportplatformfromparsleyimportmakeGrammargrammar="""    wsp ...    """tests=["A","A.B-C_D","aa","name","name<=1","name>=3","name>=3,","name>=3,<2","name@http://foo.com","name [fred,bar] @ http://foo.com ; python_version=='2.7'","name[quux, strange];python_version<'2.7' and platform_version=='2'","name; os_name=='a' or os_name=='b'",# Should parse as (a and b) or c"name; os_name=='a' and os_name=='b' or os_name=='c'",# Overriding precedence -> a and (b or c)"name; os_name=='a' and (os_name=='b' or os_name=='c')",# should parse as a or (b and c)"name; os_name=='a' or os_name=='b' and os_name=='c'",# Overriding precedence -> (a or b) and c"name; (os_name=='a' or os_name=='b') and os_name=='c'",]defformat_full_version(info):version='{0.major}.{0.minor}.{0.micro}'.format(info)kind=info.releaselevelifkind!='final':version+=kind[0]+str(info.serial)returnversionifhasattr(sys,'implementation'):implementation_version=format_full_version(sys.implementation.version)implementation_name=sys.implementation.nameelse:implementation_version='0'implementation_name=''bindings={'implementation_name':implementation_name,'implementation_version':implementation_version,'os_name':os.name,'platform_machine':platform.machine(),'platform_python_implementation':platform.python_implementation(),'platform_release':platform.release(),'platform_system':platform.system(),'platform_version':platform.version(),'python_full_version':platform.python_version(),'python_version':'.'.join(platform.python_version_tuple()[:2]),'sys_platform':sys.platform,}compiled=makeGrammar(grammar,{'lookup':bindings.__getitem__})fortestintests:parsed=compiled(test).specification()print("%s ->%s"%(test,parsed))

History

  • November 2015: This specification was approved throughPEP 508.

  • July 2019: The definition ofpython_version waschanged fromplatform.python_version()[:3] to'.'.join(platform.python_version_tuple()[:2]), to accommodate potentialfuture versions of Python with 2-digit major and minor versions(e.g. 3.10).[3]

  • June 2024: The definition ofversion_many was changed to allow trailingcommas, matching with the behavior of the Python implementation that has beenin use since late 2022.

  • April 2025: Addedextras anddependency_groups forpylock.toml Specification as approved throughPEP 751.

  • August 2025: The suggested name validation regex was fixed to match the fieldspecification (it previously finished with$ instead of\Z,incorrectly permitting trailing newlines)

  • December 2025: Ensure=== before== in grammar, to allow arbitraryequality comparisons to be parsed.

References

[1]

pip, the recommended installer for Python packages(http://pip.readthedocs.org/en/stable/)

[2]

The parsley PEG library.(https://pypi.python.org/pypi/parsley/)

[3]

Future Python versions might be problematic with thedefinition of Environment Marker Variablepython_version(https://github.com/python/peps/issues/560)

On this page

[8]ページ先頭

©2009-2025 Movatter.jp