本文档介绍最初指定的依赖项说明符格式 在 PEP 508 中。
依赖项的工作是使 pip [1] 等工具能够找到正确的 要安装的包。有时这是非常松散的 - 只是指定一个名称,并且 有时非常具体 - 指要安装的特定文件。有时 依赖项仅在一个平台中相关,或者只有某些版本相关 可以接受,因此语言允许描述所有这些情况。
定义的语言是基于行的紧凑格式,该格式已经在 广泛用于 pip 需求文件,尽管我们没有指定命令 这些文件允许的行选项处理。有一点需要注意 - 在版本控制说明符规范中指定的 URL 引用表单实际上并未在 pip 中实现,但我们使用该格式而不是 比 pip 当前的原生格式。
规范
例子
通过基于名称的查找显示的语言的所有功能:
requests [security,tests] >= 2.8.1, == 2.8.* ; Python_version < "2.7"
基于 URL 的最小查找:
pip @ https://Github.com/pypa/pip/archive/1.3.1.zip#sha1=da9234ee9982d4bbb3c72346a6de940a148ea686
概念
依赖项规范始终指定分布名称。它可能 include extras,将命名分发的依赖项扩展到 启用可选功能。安装的版本可以使用以下方式进行控制 版本限制,或提供要安装的特定项目的 URL。最后 可以使用环境标记使依赖关系成为条件。
语法
我们首先简要介绍语法,然后深入研究每个语法的语义 部分稍后。
分发规范是用 ASCII 文本编写的。我们使用欧芹 [2] 语法来提供精确的语法。预计 规范将被嵌入到一个更大的系统中,该系统提供这样的框架 作为注释,通过延续或其他此类功能支持多行。
完整的语法,包括用于构建有用解析树的注释是 包含在本文档末尾。
可以根据版本说明符规范的规则指定版本。(注: URI 在 std-66 中定义):
version_cmp = wsp* '<' | '<=' | '!=' | '==' | '>=' | '>' | '~=' | '===' version = wsp* ( letterOrDigit | '-' | '_' | '.' | '*' | '+' | '!' )+ version_one = version_cmp version wsp* version_many = version_one (wsp* ',' version_one)* versionspec = ( '(' version_many ')' ) | version_many urlspec = '@' 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)* 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' # 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_expr marker_or = marker_and wsp* 'or' marker_and | marker_and marker = marker_or quoted_marker = ';' wsp* marker
可以使用附加组件指定发行版的可选组件 田:
identifier_end = letterOrDigit | (('-' | '_' | '.' )* letterOrDigit) identifier = letterOrDigit identifier_end* name = identifier extras_list = identifier (wsp* ',' wsp* identifier)* extras = '[' wsp* extras_list? wsp* ']'
PEP 685 中定义了对临时演员姓名的限制。
为我们提供基于名称的要求的规则:
name_req = name wsp* extras? wsp* versionspec? wsp* quoted_marker?
以及直接参考规范的规则:
url_req = name wsp* extras? wsp* urlspec wsp+ quoted_marker?
导致可以指定依赖关系的统一规则。
specification = wsp* ( url_req | name_req ) wsp*
空白
非换行空格大多是可选的,没有语义意义。这 唯一的例外是检测 URL 要求的末尾。
名字
Python 分布名称目前在 PEP 345 中定义。名字 充当分配的主要标识符。它们存在于所有 依赖规范,并且足以成为其 有。但是,PyPI 对名称有严格的限制 - 它们必须与 不区分大小写的正则表达式,否则它们将不被接受。因此,在这个 文档,我们将标识符的可接受值限制为该正则表达式。一个完整的 名称的重新定义可能会在未来的元数据 PEP 中进行。正则表达式 (运行 与 re.IGNORECASE)是:
^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$
额外
额外部分是分配的可选部分。分布可以指定为 许多额外的东西,如他们所愿,每一个额外的结果都会导致声明 当 extra 在 依赖项规范。例如:
requests[security,tests]
附加项在它们定义的依赖项中与 它们所依附的分布。上面的示例将导致请求 正在安装,并请求自己的依赖项,以及任何依赖项 列在请求的“安全”附加项中。
如果列出了多个附加项,则所有依赖项将合并在一起。
版本
请参阅版本说明符规范 有关版本号和版本比较的更多详细信息。版本 规范限制了可以 使用。它们仅适用于按名称查找的发行版,而不适用于 通过 URL。版本比较也用于标记功能。这 为了与 PEP 345 兼容,版本周围存在可选括号,但不应生成,只能接受。
环境标记
环境标记允许依赖项规范提供以下规则: 描述何时应使用依赖项。例如,考虑一个包 这需要 argparse。在 Python 2.7 中,argparse 始终存在。在较旧的 Python 上 版本 它必须作为依赖项安装。这可以表示为:
argparse;python_version<"2.7"
标记表达式的计算结果为 True 或 False。当它计算为 False,则应忽略依赖项规范。
标记语言的灵感来自 Python 本身,选择它是为了能够 安全地评估它,而无需运行可能成为安全性的任意代码 脆弱性。标记物首先在 PEP 345 中标准化。本文档 修复了 PEP 426 中描述的设计中观察到的一些问题。
标记表达式中的比较由比较运算符类型化。这 <marker_op>不在<version_cmp>的操作员的性能与它们相同 在 Python 中对字符串执行操作。<version_cmp>运算符使用版本比较 版本说明符规范的规则(即当双方都具有有效的 version 说明符)。如果此规范没有定义的行为 并且运算符存在于 Python 中,然后运算符回退到 Python 行为。否则,应引发错误。例如,以下内容 将导致错误:
"dog" ~= "fred"
python_version ~= "surprise"
用户提供的常量始终编码为带引号或引号的字符串。请注意,反斜杠转义不是定义的,而是存在的 实现确实支持它们。它们不包括在内 规范,因为它们增加了复杂性,并且没有可观察到的需要 他们今天。同样,我们也没有定义非 ASCII 字符支持:所有 我们引用的运行时变量应仅是 ASCII。'"
标记语法中的变量(如“os_name”)解析为查看的值 在 Python 运行时中。除“extra”外,所有值均已定义 在今天的所有 Python 版本上 - 这是标记实现中的错误 如果未定义值。
未知变量必须引起错误,而不是导致比较 计算结果为 True 或 False。
无法在给定 Python 实现上计算其值的变量 对于版本,应计算为 to,对于所有其他版本,应为空字符串 变量。0
“extra”变量是特殊的。它被轮子用来发出信号 规范适用于车轮文件中的给定额外内容,但 由于该文件基于 PEP 426 的草稿版本,因此有 目前没有这方面的规范。无论如何,在上下文之外 正在进行特殊处理,“额外”变量应导致 错误与所有其他未知变量一样。METADATAMETADATA
标记 | Python 等价物 | 示例值 |
sys_platform | sys.平台 | linux、 、 、 (注意 “linux” 来自 Python3 和来自 Python2 的“linux2”)linux2darwinjava1.8.0_51 |
platform_machine | 平台.machine() | x86_64 |
platform_python_implementation platform_release platform_system | platform.python_implementation() 平台.release() 平台.系统() | CPython,Jython 3.14.1-x86_64-linode39, ,14.5.01.8.0_51 Linux, ,WindowsJava |
platform_version | platform.version() (平台版本) | #1 SMP Fri Apr 25 13:07:35 EDT 2014 Java HotSpot(TM) 64-Bit Server VM, 25.51-b03, Oracle Corporation Darwin Kernel Version 14.5.0: Wed Jul 29 02:18:53 PDT 2015; root:xnu-2782.40.9~2/RELEASE_X86_64 |
python_version | '.'.join(platform.python_version_tuple()[:2]) | 3.4,2.7 |
python_full_version | platform.python_version() | 3.4.0,3.5.0b1 |
implementation_name | sys.implementation.name | cpython |
implementation_version | 请参阅下面的定义 | 3.4.0,3.5.0b1 |
extra | 错误,除非由解释 规范。 | test |
marker 变量派生自:implementation_version
def format_full_version(info): version = '{0.major}.{0.minor}.{0.micro}'.format(info) kind = info.releaselevel if kind != 'final': version += kind[0] + str(info.serial) return versionif hasattr(sys, 'implementation'): implementation_version = format_full_version(sys.implementation.version)else: implementation_version = "0"
此环境标记部分最初通过 PEP 508 定义,取代了环境标记 PEP 345 中的部分。
完整语法
完整的欧芹语法:
wsp = ' ' | '\t' version_cmp = wsp* <'<=' | '<' | '!=' | '==' | '>=' | '>' | '~=' | '==='> version = wsp* <( letterOrDigit | '-' | '_' | '.' | '*' | '+' | '!' )+> version_one = version_cmp:op version:v wsp* -> (op, v) version_many = version_one:v1 (wsp* ',' version_one)*:v2 -> [v1] + v2 versionspec = ('(' version_many:v ')' ->v) | version_many urlspec = '@' 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) -> s 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' # 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* ')' -> m marker_and = marker_expr:l wsp* 'and' marker_expr:r -> ('and', l, r) | marker_expr:m -> m marker_or = marker_and:l wsp* 'or' marker_and:r -> ('or', l, r) | marker_and:m -> m marker = marker_or quoted_marker = ';' wsp* marker identifier_end = letterOrDigit | (('-' | '_' | '.' )* letterOrDigit) identifier = < letterOrDigit identifier_end* > name = identifier extras_list = identifier:i (wsp* ',' wsp* identifier)*:ids -> [i] + ids extras = '[' wsp* extras_list?:e wsp* ']' -> e name_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 None URI_reference = <URI | relative_ref> URI = scheme ':' hier_part ('?' query )? ( '#' fragment)? hier_part = ('//' authority path_abempty) | path_absolute | path_rootless | path_empty absolute_URI = scheme ':' hier_part ( '?' query )? relative_ref = relative_part ( '?' query )? ( '#' fragment )? relative_part = '//' authority path_abempty | path_absolute | path_noscheme | path_empty scheme = letter ( letter | digit | '+' | '-' | '.')* authority = ( userinfo '@' )? host ( ':' port )? userinfo = ( unreserved | pct_encoded | sub_delims | ':')* host = IP_literal | IPv4address | reg_name port = 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) | IPv4address IPv4address = dec_octet '.' dec_octet '.' dec_octet '.' dec_octet nz = ~'0' digit dec_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-255 reg_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 characters path_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 = '%' hexdig unreserved = letter | digit | '-' | '.' | '_' | '~' reserved = gen_delims | sub_delims gen_delims = ':' | '/' | '?' | '#' | '(' | ')?' | '@' sub_delims = '!' | '$' | '&' | '\\'' | '(' | ')' | '*' | '+' | ',' | ';' | '=' hexdig = digit | 'a' | 'A' | 'b' | 'B' | 'c' | 'C' | 'd' | 'D' | 'e' | 'E' | 'f' | 'F'
测试程序 - 如果语法在字符串中:grammar
import osimport sysimport platformfrom parsley import makeGrammargrammar = """ wsp ... """tests = [ "A", "A.B-C_D", "aa", "name", "name<=1", "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'", ]def format_full_version(info): version = '{0.major}.{0.minor}.{0.micro}'.format(info) kind = info.releaselevel if kind != 'final': version += kind[0] + str(info.serial) return versionif hasattr(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__})for test in tests: parsed = compiled(test).specification() print("%s -> %s" % (test, parsed))
历史
2015 年 11 月:该规范通过 PEP 508 获得批准。
2019 年 7 月:将 的定义从 更改为 ,以适应潜在的 具有 2 位主要版本和次要版本的 Python 的未来版本 (例如 3.10)。[3]python_versionplatform.python_version()[:3]'.'.join(platform.python_version_tuple()[:2])
文章声明:以上内容(如有图片或视频在内)除非注明,否则均为腾龙猫勺儿原创文章,转载或复制请以超链接形式并注明出处。
本文作者:猫勺本文链接:https://www.jo6.cn/post/57.html