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

Python 개선 제안 한국어 번역

PEP 508 – Python 소프트웨어 패키지의 의존성 명세

Author:
Robert Collins <rbtcollins at hp.com>
BDFL-Delegate:
Donald Stufft <donald at stufft.io>
Discussions-To:
Distutils-SIG list
Status:
Final
Type:
Standards Track
Topic:
Packaging
Created:
11-Nov-2015
Post-History:
05-Nov-2015, 16-Nov-2015
Resolution:
Distutils-SIG message

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

Important

This PEP is a historical document. The up-to-date, canonical spec, Dependency specifiers, is maintained on the PyPA specs page.

×

See the PyPA specification update process for how to propose changes.

초록

이 PEP는 패키지의 의존성을 기술하는 데 사용하는 언어를 명세합니다. 이 PEP는 단일 의존성을 기술하는 데까지만 다룹니다. 의존성의 여러 종류와 각 의존성을 언제 설치해야 하는지는 더 상위 수준의 문제입니다. 이 PEP의 목적은 상위 계층 명세를 위한 기본 구성 요소를 제공하는 것입니다.

의존성의 역할은 pip [1] 같은 도구가 설치할 올바른 패키지를 찾을 수 있게 하는 것입니다. 의존성은 이름만 지정할 정도로 매우 느슨할 때도 있고, 설치할 특정 파일을 가리킬 정도로 매우 구체적일 때도 있습니다. 의존성이 한 플랫폼에서만 의미가 있거나 일부 버전만 허용되는 경우도 있으므로, 이 언어로는 이러한 경우를 모두 기술할 수 있습니다.

여기서 정의하는 언어는 pip 요구 사항 파일에서 이미 널리 쓰이는 간결한 줄 단위 형식입니다. 다만 그 파일에서 허용하는 명령줄 옵션 처리는 명세하지 않습니다. 한 가지 주의할 점이 있습니다. PEP 440에 명세된 URL 참조 형식은 실제로는 pip에 구현되어 있지 않습니다. 그러나 PEP 440이 승인되었으므로 pip의 현재 고유 형식 대신 그 형식을 사용합니다.

동기

Python 패키징 생태계에서 의존성 목록을 사용해야 하는 명세는 모두 이를 위해 승인된 PEP를 기반으로 해야 합니다. 그러나 PEP 426은 대체로 지향점을 제시하는 데 그치며, 대신 채택할 수 있는 의존성 명세 구현이 이미 존재합니다. 기존 구현은 실전에서 검증되었고 사용하기도 편리합니다. 따라서 지향점만 제시할 뿐 실제로 쓰이지 않는 형식을 승인하는 것보다 기존 구현을 채택하는 편이 훨씬 낫다고 할 수 있습니다.

명세

예시

이름 기반 조회로 보여 주는 언어의 모든 기능:

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

개념

의존성 명세는 항상 배포 이름을 지정합니다. 의존성 명세에는 extras를 포함할 수 있습니다. extras는 선택적 기능을 활성화하기 위해 지정한 배포의 의존성을 확장합니다. 설치할 버전은 버전 제한을 사용하거나 설치할 특정 아티팩트의 URL을 지정하여 제어할 수 있습니다. 마지막으로 환경 마커를 사용하여 의존성에 조건을 걸 수 있습니다.

문법

먼저 문법을 간단히 살펴본 다음, 뒤에서 각 부분의 의미를 자세히 다룹니다.

배포 명세는 ASCII 텍스트로 작성합니다. 정확한 문법을 제공하기 위해 parsley [2] 문법을 사용합니다. 이 명세는 주석, 줄 연속을 통한 여러 줄 지원 같은 기능으로 틀을 제공하는 더 큰 시스템에 포함되어 사용될 것으로 예상합니다.

유용한 구문 트리를 만들기 위한 어노테이션을 포함한 전체 문법은 이 PEP의 끝에 수록되어 있습니다.

버전은 PEP 440 규칙에 따라 지정할 수 있습니다. (참고: 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

배포의 선택적 구성 요소는 extras 필드를 사용하여 지정할 수 있습니다:

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

이로써 이름 기반 요구 사항에 대한 규칙이 만들어집니다:

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에서는 식별자로 허용되는 값을 그 정규 표현식과 일치하는 값으로 제한합니다. 향후 메타데이터 PEP에서 이름을 완전히 재정의할 수도 있습니다. 정규 표현식(re.IGNORECASE로 실행)은 다음과 같습니다.:

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

추가 기능

추가 기능(extra)은 배포 패키지의 선택적 부분입니다. 배포 패키지는 원하는 만큼 추가 기능을 지정할 수 있으며, 각 추가 기능은 의존성 명세에서 해당 추가 기능이 사용될 때 배포 패키지의 추가 의존성을 선언하게 합니다. 예를 들면 다음과 같습니다.:

requests[security]

추가 기능은 자신이 정의하는 의존성을 자신이 연결된 배포 패키지의 의존성과 합칩니다. 위의 예에서는 requests가 설치되고, requests 자체의 의존성과 requests의 “security” 추가 기능에 나열된 모든 의존성도 설치됩니다.

여러 추가 기능이 나열되면 모든 의존성이 하나로 합쳐집니다.

버전

버전 번호와 버전 비교에 관한 자세한 내용은 PEP 440을 참조하십시오. 버전 명세는 사용할 수 있는 배포 패키지의 버전을 제한합니다. 버전 명세는 URL을 통해서가 아니라 이름으로 조회하는 배포 패키지에만 적용됩니다. 버전 비교는 마커 기능에서도 사용됩니다. 버전을 둘러싼 선택적 괄호는 PEP 345와의 호환성을 위해 존재하지만, 생성해서는 안 되며 받아들이기만 해야 합니다.

환경 마커

환경 마커를 사용하면 의존성 명세에서 해당 의존성을 언제 사용해야 하는지 설명하는 규칙을 제공할 수 있습니다. 예를 들어 argparse가 필요한 패키지를 생각해 보십시오. Python 2.7에는 argparse가 항상 있습니다. 그보다 오래된 Python 버전에서는 argparse를 의존성으로 설치해야 합니다. 이는 다음과 같이 표현할 수 있습니다.:

argparse;python_version<"2.7"

마커 표현식은 True 또는 False 중 하나로 평가됩니다. False로 평가되면 해당 의존성 명세는 무시해야 합니다.

마커 언어는 Python 자체에서 영감을 받았으며, 보안 취약점이 될 수 있는 임의의 코드를 실행하지 않고도 안전하게 평가할 수 있다는 점 때문에 선택되었습니다. 마커는 PEP 345에서 처음 표준화되었습니다. 이 PEP는 PEP 426에 기술된 설계에서 발견된 몇 가지 문제를 수정합니다.

마커 표현식에서 비교의 타입은 비교 연산자에 따라 정해집니다. <version_cmp>에 속하지 않는 <marker_op> 연산자는 Python에서 문자열에 대해 동작하는 것과 똑같이 동작합니다. <version_cmp> 연산자는 PEP 440 버전 비교 규칙이 정의되어 있는 경우(즉, 양쪽 모두 유효한 버전 지정자인 경우) 그 규칙을 사용합니다. 정의된 PEP 440 동작이 없고 해당 연산자가 Python에 존재하면, 연산자는 Python의 동작으로 대체됩니다. 그 밖의 경우에는 오류를 발생시켜야 합니다. 예를 들어 다음은 오류가 됩니다.:

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

사용자가 제공하는 상수는 항상 ' 또는 " 따옴표로 감싼 문자열로 표기합니다. 백슬래시 이스케이프는 정의되어 있지 않지만, 기존 구현들은 이를 지원한다는 점에 유의하십시오. 백슬래시 이스케이프는 복잡성을 더하고 현재로서는 필요성이 확인되지 않으므로 이 명세에 포함하지 않습니다. 마찬가지로 비ASCII 문자 지원도 정의하지 않습니다. 여기서 참조하는 모든 런타임 변수는 ASCII 문자만 포함할 것으로 예상됩니다.

“os_name”과 같은 마커 문법의 변수는 Python 런타임에서 조회한 값으로 해석됩니다. “extra”를 예외로 하면 현재 모든 Python 버전에서 모든 값이 정의되어 있습니다. 값이 정의되어 있지 않다면 그것은 마커 구현의 오류입니다.

알 수 없는 변수는 True나 False로 평가되는 비교 결과를 내는 대신 오류를 발생시켜야 합니다.

특정 Python 구현에서 값을 계산할 수 없는 변수는 버전의 경우 0으로, 그 밖의 모든 변수의 경우 빈 문자열로 평가되어야 합니다.

“extra” 변수는 특별합니다. 이 변수는 휠 METADATA 파일에서 특정 extra에 어떤 명세가 적용되는지 알리기 위해 휠에서 사용되지만, METADATA 파일은 PEP 426의 초안 버전에 기반하므로 현재 이에 대한 명세는 없습니다. 어쨌든 이러한 특별한 처리가 이루어지는 맥락 밖에서는 “extra” 변수도 다른 모든 알 수 없는 변수와 마찬가지로 오류가 되어야 합니다.

마커 Python 대응 표현 예시 값
os_name os.name posix, java
sys_platform sys.platform linux, linux2, darwin, java1.8.0_51 (“linux”는 Python3의 값이고 “linux2”는 Python2의 값이라는 점에 유의하십시오)
platform_machine platform.machine() x86_64
platform_python_implementation platform.python_implementation() CPython, Jython
platform_release platform.release() 3.14.1-x86_64-linode39, 14.5.0, 1.8.0_51
platform_system platform.system() Linux, Windows, Java
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

implementation_version 마커 변수는 sys.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 version

if hasattr(sys, 'implementation'):
    implementation_version = format_full_version(sys.implementation.version)
else:
    implementation_version = "0"

하위 호환성

이 PEP의 대부분은 이미 널리 배포되어 있으므로 호환성 문제가 없습니다.

그러나 이 PEP가 기존에 배포된 구현과 다른 점이 몇 가지 있습니다.

첫째, PEP 440 직접 참조는 아직 실제 환경에 배포된 적이 없지만, 호환되는 방식으로 추가할 수 있도록 설계되었습니다. 또한 pip이나 배포판의 기존 의존성 메타데이터를 사용하는 다른 도구에 직접 참조를 추가하는 데 알려진 걸림돌은 없습니다. 특히 PyPI에 업로드되는 배포판에는 어차피 직접 참조가 허용되지 않을 것이기 때문입니다.

둘째, 특히 wheel과 pip에서 어느 정도 배포된 PEP 426 마커는 python_full_version “2.7.10”과의 버전 비교를 다르게 처리합니다. 구체적으로, 426에서는 “2.7.10”이 “2.7.9”보다 작습니다. 이러한 하위 호환성 단절은 의도된 것입니다. 또한 이전 마커 구현에는 없는 새로운 연산자 “~=”와 “===”, 그리고 새로운 변수 platform_release, platform_system, implementation_name, implementation_version도 정의합니다. 이 변수들은 해당 구현에서 오류를 일으킵니다. 두 기능의 사용자는 생태계 전반에 지원이 충분히 퍼져서 이 기능들을 사용해도 호환성 문제가 생기지 않는 시점이 언제인지 판단해야 합니다.

셋째, PEP 345는 버전 지정자를 괄호로 감싸도록 요구했습니다. PEP 345 의존성 명세를 받아들이기 위해 괄호를 허용하지만, 괄호를 생성해서는 안 됩니다.

근거

환경 마커에 의존하는 새로운 PEP를 진행하려면 환경 마커를 현대적인 형태로 포함하는 명세가 필요했습니다. 이 PEP는 현재 명세되지 않은 모든 구성 요소를 모아 명세된 형태로 정리합니다.

요구 사항 지정자는 setuptools pkg_resources 문서의 EBNF에서 가져왔습니다. PEP로 명세된 표준이 아닌 사실상의 표준에 의존하는 것을 피하고자 하기 때문입니다.

전체 문법

전체 parsley 문법:

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 os
import sys
import platform

from parsley import makeGrammar

grammar = """
    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 version

if hasattr(sys, 'implementation'):
    implementation_version = format_full_version(sys.implementation.version)
    implementation_name = sys.implementation.name
else:
    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))

PEP 508 변경 사항 요약

최초 구현 이후 받은 피드백을 바탕으로 이 PEP가 다음과 같이 변경되었습니다:

  • 주 버전과 부 버전이 두 자리인 향후 Python 버전(예: 3.10)을 수용할 수 있도록 python_version의 정의가 platform.python_version()[:3]에서 '.'.join(platform.python_version_tuple()[:2])로 변경되었습니다. [3]

참고 문헌