[Distutils] FINAL DRAFT: Dependency specifier PEP

Xavier Fernandez xav.fernandez at gmail.com
Fri Nov 27 08:28:58 EST 2015


https://www.python.org/dev/peps/pep-0508/

On Wed, Nov 25, 2015 at 9:05 PM, Marcus Smith <qwcode at gmail.com> wrote:

> PEP number yet?
>
> On Sun, Nov 22, 2015 at 4:45 PM, Donald Stufft <donald at stufft.io> wrote:
>
>> Okay. I’ve read over this, implemented enough of it, and I think it’s
>> gone through enough nit picking. I’m going to go ahead and accept this PEP.
>> It’s largely just standardizing what we are already doing so it’s pretty
>> low impact other than fixing up a few issues and giving implementations
>> something they can point at for what the standard behavior is.
>>
>> So congratulations to everyone working on this :)
>>
>> I’ll get this into the PEPs repo and get it pushed.
>>
>>
>> > On Nov 16, 2015, at 3:46 PM, Robert Collins <robertc at robertcollins.net>
>> wrote:
>> >
>> > :PEP: XX
>> > :Title: Dependency specification for Python Software Packages
>> > :Version: $Revision$
>> > :Last-Modified: $Date$
>> > :Author: Robert Collins <rbtcollins at hp.com>
>> > :BDFL-Delegate: Donald Stufft <donald at stufft.io>
>> > :Discussions-To: distutils-sig <distutils-sig at python.org>
>> > :Status: Draft
>> > :Type: Standards Track
>> > :Content-Type: text/x-rst
>> > :Created: 11-Nov-2015
>> > :Post-History: XX
>> >
>> >
>> > Abstract
>> > ========
>> >
>> > This PEP specifies the language used to describe dependencies for
>> packages.
>> > It draws a border at the edge of describing a single dependency - the
>> > different sorts of dependencies and when they should be installed is a
>> higher
>> > level problem. The intent is provide a building block for higher layer
>> > specifications.
>> >
>> > The job of a dependency is to enable tools like pip [#pip]_ to find the
>> right
>> > package to install. Sometimes this is very loose - just specifying a
>> name, and
>> > sometimes very specific - referring to a specific file to install.
>> Sometimes
>> > dependencies are only relevant in one platform, or only some versions
>> are
>> > acceptable, so the language permits describing all these cases.
>> >
>> > The language defined is a compact line based format which is already in
>> > widespread use in pip requirements files, though we do not specify the
>> command
>> > line option handling that those files permit. There is one caveat - the
>> > URL reference form, specified in PEP-440 [#pep440]_ is not actually
>> > implemented in pip, but since PEP-440 is accepted, we use that format
>> rather
>> > than pip's current native format.
>> >
>> > Motivation
>> > ==========
>> >
>> > Any specification in the Python packaging ecosystem that needs to
>> consume
>> > lists of dependencies needs to build on an approved PEP for such, but
>> > PEP-426 [#pep426]_ is mostly aspirational - and there are already
>> existing
>> > implementations of the dependency specification which we can instead
>> adopt.
>> > The existing implementations are battle proven and user friendly, so
>> adopting
>> > them is arguably much better than approving an aspirational,
>> unconsumed, 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.10"
>> >
>> > 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 may
>> > include extras, which expand the dependencies of the named distribution
>> to
>> > enable optional features. The version installed can be controlled using
>> > version limits, or giving the URL to a specific artifact to install.
>> Finally
>> > the dependency can be made conditional using environment markers.
>> >
>> > Grammar
>> > -------
>> >
>> > We first cover the grammar briefly and then drill into the semantics of
>> each
>> > section later.
>> >
>> > A distribution specification is written in ASCII text. We use a parsley
>> > [#parsley]_ grammar to provide a precise grammar. It is expected that
>> the
>> > specification will be embedded into a larger system which offers
>> framing such
>> > as comments, multiple line support via continuations, or other such
>> features.
>> >
>> > The full grammar including annotations to build a useful parse tree is
>> > included at the end of the PEP.
>> >
>> > Versions may be specified according to the PEP-440 [#pep440]_ rules.
>> (Note:
>> > URI is defined in std-66 [#std66]_::
>> >
>> >    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>
>> >
>> > Environment markers allow making a specification only take effect in
>> some
>> > environments::
>> >
>> >    marker_op     = version_cmp | 'in' | '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' | 'python_implementation' |
>> >                     'implementation_name' | 'implementation_version' |
>> >                     'extra' # ONLY when defined by a containing layer
>> >                     )
>> >    marker_var    = env_var | python_str
>> >    marker_expr   = ('(' wsp* marker wsp* ')'
>> >                     | (marker_var wsp* marker_op wsp* marker_var))
>> >    marker        = wsp* marker_expr ( wsp* ('and' | 'or') wsp*
>> marker_expr)*
>> >    quoted_marker = ';' wsp* marker
>> >
>> > Optional components of a distribution may be specified using the extras
>> > field::
>> >
>> >    identifier    = letterOrDigit (
>> >                    letterOrDigit |
>> >                    (( letterOrDigit | '-' | '_' | '.')* letterOrDigit )
>> )*
>> >    name          = identifier
>> >    extras_list   = identifier (wsp* ',' wsp* identifier)*
>> >    extras        = '[' wsp* extras_list? wsp* ']'
>> >
>> > 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. The
>> > sole exception is detecting the end of a URL requirement.
>> >
>> > Names
>> > -----
>> >
>> > Python distribution names are currently defined in PEP-345 [#pep345]_.
>> Names
>> > act as the primary identifier for distributions. They are present in all
>> > dependency specifications, and are sufficient to be a specification on
>> their
>> > own. However, PyPI places strict restrictions on names - they must
>> match a
>> > case insensitive regex or they won't be accepted. Accordingly in this
>> PEP we
>> > limit the acceptable values for identifiers to that regex. A full
>> redefinition
>> > of name may take place in a future metadata PEP::
>> >
>> >    ^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$
>> >
>> > Extras
>> > ------
>> >
>> > An extra is an optional part of a distribution. Distributions can
>> specify as
>> > many extras as they wish, and each extra results in the declaration of
>> > additional dependencies of the distribution **when** the extra is used
>> in a
>> > dependency specification. For instance::
>> >
>> >    requests[security]
>> >
>> > Extras union in the dependencies they define with the dependencies of
>> the
>> > distribution they are attached to. The example above would result in
>> requests
>> > being installed, and requests own dependencies, and also any
>> dependencies that
>> > are listed in the "security" extra of requests.
>> >
>> > If multiple extras are listed, all the dependencies are unioned
>> together.
>> >
>> > Versions
>> > --------
>> >
>> > See PEP-440 [#pep440]_ for more detail on both version numbers and
>> version
>> > comparisons. Version specifications limit the versions of a
>> distribution that
>> > can be used. They only apply to distributions looked up by name, rather
>> than
>> > via a URL. Version comparison are also used in the markers feature. The
>> > optional brackets around a version are present for compatibility with
>> PEP-345
>> > [#pep345]_ but should not be generated, only accepted.
>> >
>> > Environment Markers
>> > -------------------
>> >
>> > Environment markers allow a dependency specification to provide a rule
>> that
>> > describes when the dependency should be used. For instance, consider a
>> package
>> > that needs argparse. In Python 2.7 argparse is always present. On older
>> Python
>> > versions it has to be installed as a dependency. This can be expressed
>> as so::
>> >
>> >    argparse;python_version<"2.7"
>> >
>> > A marker expression evalutes to either True or False. When it evaluates
>> to
>> > False, the dependency specification should be ignored.
>> >
>> > The marker language is a subset of Python itself, chosen for the
>> ability to
>> > safely evaluate it without running arbitrary code that could become a
>> security
>> > vulnerability. Markers were first standardised in PEP-345 [#pep345]_.
>> This PEP
>> > fixes some issues that were observed in the design described in PEP-426
>> > [#pep426]_.
>> >
>> > Comparisons in marker expressions are typed by the comparison
>> operator.  The
>> > <marker_op> operators that are not in <version_cmp> perform the same as
>> they
>> > do for strings in Python. The <version_cmp> operators use the PEP-440
>> > [#pep440]_ version comparison rules when those are defined (that is
>> when both
>> > sides have a valid version specifier). If there is no defined PEP-440
>> > behaviour and the operator exists in Python, then the operator falls
>> back to
>> > the Python behaviour. 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
>> existing
>> > implementations do support them. They are not included in this
>> > specification because they add complexity and there is no observable
>> need for
>> > them today. Similarly we do not define non-ASCII character support: all
>> the
>> > runtime variables we are referencing are expected to be ASCII-only.
>> >
>> > The variables in the marker grammar such as "os_name" resolve to values
>> looked
>> > up in the Python runtime. With the exception of "extra" all values are
>> defined
>> > on all Python versions today - it is an error in the implementation of
>> markers
>> > if a value is not defined.
>> >
>> > Unknown variables must raise an error rather than resulting in a
>> comparison
>> > that evaluates to True or False.
>> >
>> > Variables whose value cannot be calculated on a given Python
>> implementation
>> > should evaluate to ``0`` for versions, and an empty string for all other
>> > variables.
>> >
>> > The "extra" variable is special. It is used by wheels to signal which
>> > specifications apply to a given extra in the wheel ``METADATA`` file,
>> but
>> > since the ``METADATA`` file is based on a draft version of PEP-426,
>> there is
>> > no current specification for this. Regardless, outside of a context
>> where this
>> > special handling is taking place, the "extra" variable should result in
>> an
>> > error like all other unknown variables.
>> >
>> > .. list-table::
>> >   :header-rows: 1
>> >
>> >   * - Marker
>> >     - Python equivalent
>> >     - Sample values
>> >   * - ``os_name``
>> >     - ``os.name``
>> >     - ``posix``, ``java``
>> >   * - ``sys_platform``
>> >     - ``sys.platform``
>> >     - ``linux``, ``linux2``, ``darwin``, ``java1.8.0_51`` (note that
>> "linux"
>> >       is from Python3 and "linux2" from Python2)
>> >   * - ``platform_machine``
>> >     - ``platform.machine()``
>> >     - ``x86_64``
>> >   * - ``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``
>> >     - ``platform.python_version()[:3]``
>> >     - ``3.4``, ``2.7``
>> >   * - ``python_full_version``
>> >     - ``platform.python_version()``
>> >     - ``3.4.0``, ``3.5.0b1``
>> >   * - ``implementation_name``
>> >     - ``sys.implementation.name``
>> >     - ``cpython``
>> >   * - ``implementation_version``
>> >     - see definition below
>> >     - ``3.4.0``, ``3.5.0b1``
>> >   * - ``extra``
>> >     - An error except when defined by the context interpreting the
>> >       specification.
>> >     - ``test``
>> >
>> > The ``implementation_version`` marker variable is derived from
>> > ``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"
>> >
>> > Backwards Compatibility
>> > =======================
>> >
>> > Most of this PEP is already widely deployed and thus offers no
>> compatibiltiy
>> > concerns.
>> >
>> > There are however a few points where the PEP differs from the deployed
>> base.
>> >
>> > Firstly, PEP-440 direct references haven't actually been deployed in
>> the wild,
>> > but they were designed to be compatibly added, and there are no known
>> > obstacles to adding them to pip or other tools that consume the existing
>> > dependency metadata in distributions - particularly since they won't be
>> > permitted to be present in PyPI uploaded distributions anyway.
>> >
>> > Secondly, PEP-426 markers which have had some reasonable deployment,
>> > particularly in wheels and pip, will handle version comparisons with
>> > ``python_version`` "2.7.10" differently. Specifically in 426 "2.7.10"
>> is less
>> > than "2.7.9". This backward incompatibility is deliberate. We are also
>> > defining new operators - "~=" and "===", and new variables -
>> > ``platform_release``, ``platform_system``, ``implementation_name``, and
>> > ``implementation_version`` which are not present in older marker
>> > implementations. The variables will error on those implementations.
>> Users of
>> > both features will need to make a judgement as to when support has
>> become
>> > sufficiently widespread in the ecosystem that using them will not cause
>> > compatibility issues.
>> >
>> > Thirdly, PEP-345 required brackets around version specifiers. In order
>> to
>> > accept PEP-345 dependency specifications, brackets are accepted, but
>> they
>> > should not be generated.
>> >
>> > Rationale
>> > =========
>> >
>> > In order to move forward with any new PEPs that depend on environment
>> markers,
>> > we needed a specification that included them in their modern form. This
>> PEP
>> > brings together all the currently unspecified components into a
>> specified
>> > form.
>> >
>> > The requirement specifier was adopted from the EBNF in the setuptools
>> > pkg_resources documentation, since we wish to avoid depending on a
>> defacto, vs
>> > PEP specified, standard.
>> >
>> > 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 (wsp* ',' version_one)*:v2 -> [v1] +
>> v2
>> >    versionspec   = ('(' version_many:v ')' ->v) | version_many
>> >    urlspec       = '@' wsp* <URI_reference>
>> >    marker_op     = version_cmp | 'in' | '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' | 'python_implementation' |
>> >                     'implementation_name' | 'implementation_version' |
>> >                     'extra' # ONLY when defined by a containing layer
>> >                     ):varname -> lookup(varname)
>> >    marker_var    = env_var | python_str
>> >    marker_expr   = (("(" wsp* marker:m wsp* ")" -> m)
>> >                         | ((marker_var:l wsp* marker_op:o wsp*
>> marker_var:r))
>> >                         -> (l, o, r))
>> >    marker        = (wsp* marker_expr:m ( wsp* ("and" | "or"):o wsp*
>> >                     marker_expr:r -> (o, r))*:ms -> (m, ms))
>> >    quoted_marker = ';' wsp* marker
>> >    identifier    = <letterOrDigit (
>> >                    letterOrDigit |
>> >                    (( letterOrDigit | '-' | '_' | '.')* letterOrDigit )
>> )*>
>> >    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+
>> 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'
>> >
>> > A test program - if the grammar is in a string ``grammar``::
>> >
>> >    import os
>> >    import sys
>> >    import platform
>> >
>> >    from parsley import makeGrammar
>> >
>> >    grammar = """
>> >        wsp ...
>> >        """
>> >    tests = [
>> >        "A",
>> >        "aa",
>> >        "name",
>> >        "name>=3",
>> >        "name>=3,<2",
>> >        "name [fred,bar] @ http://foo.com ; python_version=='2.7'",
>> >        "name[quux, strange];python_version<'2.7' and
>> platform_version=='2'",
>> >        "name; os_name=='dud' and (os_name=='odd' or os_name=='fred')",
>> >        "name; os_name=='dud' and os_name=='odd' or os_name=='fred'",
>> >        ]
>> >
>> >    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_release': platform.release(),
>> >        'platform_system': platform.system(),
>> >        'platform_version': platform.version(),
>> >        'python_full_version': platform.python_version(),
>> >        'python_implementation': platform.python_implementation(),
>> >        'python_version': platform.python_version()[:3],
>> >        'sys_platform': sys.platform,
>> >    }
>> >
>> >    compiled = makeGrammar(grammar, {'lookup': bindings.__getitem__})
>> >    for test in tests:
>> >      parsed = compiled(test).specification()
>> >      print(parsed)
>> >
>> > References
>> > ==========
>> >
>> > .. [#pip] pip, the recommended installer for Python packages
>> >   (http://pip.readthedocs.org/en/stable/)
>> >
>> > .. [#pep345] PEP-345, Python distribution metadata version 1.2.
>> >   (https://www.python.org/dev/peps/pep-0345/)
>> >
>> > .. [#pep426] PEP-426, Python distribution metadata.
>> >   (https://www.python.org/dev/peps/pep-0426/)
>> >
>> > .. [#pep440] PEP-440, Python distribution metadata.
>> >   (https://www.python.org/dev/peps/pep-0440/)
>> >
>> > .. [#std66] The URL specification.
>> >   (https://tools.ietf.org/html/rfc3986)
>> >
>> > .. [#parsley] The parsley PEG library.
>> >   (https://pypi.python.org/pypi/parsley/)
>> >
>> > Copyright
>> > =========
>> >
>> > This document has been placed in the public domain.
>> >
>> >
>> >
>> > ..
>> >   Local Variables:
>> >   mode: indented-text
>> >   indent-tabs-mode: nil
>> >   sentence-end-double-space: t
>> >   fill-column: 70
>> >   coding: utf-8
>> >   End:
>> >
>> >
>> > --
>> > Robert Collins <rbtcollins at hp.com>
>> > Distinguished Technologist
>> > HP Converged Cloud
>> > _______________________________________________
>> > Distutils-SIG maillist  -  Distutils-SIG at python.org
>> > https://mail.python.org/mailman/listinfo/distutils-sig
>>
>>
>> -----------------
>> Donald Stufft
>> PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372
>> DCFA
>>
>>
>> _______________________________________________
>> Distutils-SIG maillist  -  Distutils-SIG at python.org
>> https://mail.python.org/mailman/listinfo/distutils-sig
>>
>>
>
> _______________________________________________
> Distutils-SIG maillist  -  Distutils-SIG at python.org
> https://mail.python.org/mailman/listinfo/distutils-sig
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/distutils-sig/attachments/20151127/66a385cf/attachment-0001.html>


More information about the Distutils-SIG mailing list