{"action":"created","comment":{"url":"https://api.github.com/repos/pypa/interoperability-peps/pulls/comments/53245930","id":53245930,"diff_hunk":"@@ -0,0 +1,708 @@\n+PEP: ??\n+Title: A build-system independent format for source trees\n+Version: $Revision$\n+Last-Modified: $Date$\n+Author: Nathaniel J. Smith <njs@pobox.com>\n+Status: Draft\n+Type: Standards-Track\n+Content-Type: text/x-rst\n+Created: 30-Sep-2015\n+Post-History: 1 Oct 2015, 25 Oct 2015\n+Discussions-To: <distutils-sig@python.org>\n+\n+==========\n+ Abstract\n+==========\n+\n+While ``distutils`` / ``setuptools`` have taken us a long way, they\n+suffer from three serious problems: (a) they're missing important\n+features like usable build-time dependency declaration,\n+autoconfiguration, and even basic ergonomic niceties like `DRY\n+<https://en.wikipedia.org/wiki/Don%27t_repeat_yourself>`_-compliant\n+version number management, and (b) extending them is difficult, so\n+while there do exist various solutions to the above problems, they're\n+often quirky, fragile, and expensive to maintain, and yet (c) it's\n+very difficult to use anything else, because distutils/setuptools\n+provide the standard interface for installing packages expected by\n+both users and installation tools like ``pip``.\n+\n+Previous efforts (e.g. distutils2 or setuptools itself) have attempted\n+to solve problems (a) and/or (b). This proposal aims to solve (c).\n+\n+The goal of this PEP is get distutils-sig out of the business of being\n+a gatekeeper for Python build systems. If you want to use distutils,\n+great; if you want to use something else, then that should be easy to\n+do using standardized methods. The difficulty of interfacing with\n+distutils means that there aren't many such systems right now, but to\n+give a sense of what we're thinking about see `flit\n+<https://github.com/takluyver/flit>`_ or `bento\n+<https://cournape.github.io/Bento/>`_. Fortunately, wheels have now\n+solved many of the hard problems here -- e.g. it's no longer necessary\n+that a build system also know about every possible installation\n+configuration -- so pretty much all we really need from a build system\n+is that it have some way to spit out standard-compliant wheels and\n+sdists.\n+\n+We therefore propose a new, relatively minimal interface for\n+installation tools like ``pip`` to interact with package source trees\n+and source distributions.\n+\n+\n+=======================\n+ Terminology and goals\n+=======================\n+\n+A *source tree* is something like a VCS checkout. We need a standard\n+interface for installing from this format, to support usages like\n+``pip install some-directory/``.\n+\n+A *source distribution* is a static snapshot representing a particular\n+release of some source code, like ``lxml-3.4.4.zip``. Source\n+distributions serve many purposes: they form an archival record of\n+releases, they provide a stupid-simple de facto standard for tools\n+that want to ingest and process large corpora of code, possibly\n+written in many languages (e.g. code search), they act as the input to\n+downstream packaging systems like Debian/Fedora/Conda/..., and so\n+forth. In the Python ecosystem they additionally have a particularly\n+important role to play, because packaging tools like ``pip`` are able\n+to use source distributions to fulfill binary dependencies, e.g. if\n+there is a distribution ``foo.whl`` which declares a dependency on\n+``bar``, then we need to support the case where ``pip install bar`` or\n+``pip install foo`` automatically locates the sdist for ``bar``,\n+downloads it, builds it, and installs the resulting package.\n+\n+Source distributions are also known as *sdists* for short.\n+\n+A *build frontend* is a tool that users might run that takes arbitrary\n+source trees or source distributions and builds wheels from them. The\n+actual building is done by each source tree's *build backend*. In a\n+command like ``pip wheel some-directory/``, pip is acting as a build\n+frontend.\n+\n+An *integration frontend* is a tool that users might run that takes a\n+set of package requirements (e.g. a requirements.txt file) and\n+attempts to update a working environment to satisfy those\n+requirements. This may require locating, building, and installing a\n+combination of wheels and sdists. In a command like ``pip install\n+lxml==2.4.0``, pip is acting as an integration frontend.\n+\n+\n+==============\n+ Source trees\n+==============\n+\n+We retroactively declare the legacy source tree format involving\n+``setup.py`` to be \"version 0\". We don't try to specify it further;\n+its de facto specification is encoded in the source code and\n+documentation of ``distutils``, ``setuptools``, ``pip``, and other\n+tools.\n+\n+A \"version 1\" (or greater) source tree is any directory which contains\n+a file named ``pypackage.json``. (If a tree contains both\n+``pypackage.json`` and ``setup.py`` then it is a version 1+ source\n+tree and the ``setup.py`` is ignored; this allows packages to include\n+a ``setup.py`` for compatibility with old build frontends, while using\n+the new system with new build frontends.)\n+\n+This file has the following schema. Extra keys are ignored.\n+\n+schema\n+    The version of the schema. This PEP defines version \"1\".  Defaults to \"1\"\n+    when absent. All tools reading the file MUST error on an unrecognised\n+    schema version.\n+\n+bootstrap_requires\n+    Optional list of PEP 508 dependency specifications that the\n+    build frontend must ensure are available before invoking the build\n+    backend. For instance, if using flit, then the requirements might\n+    be::\n+\n+        \"bootstrap_requires\": [\"flit\"]\n+\n+build_backend\n+    A mandatory string naming a Python object that will be used to\n+    perform the build (see below for details). This is formatted\n+    following the same ``module:object`` syntax as a ``setuptools``\n+    entry point. For instance, if using flit, then the build system\n+    might be specified as::\n+\n+        \"build_system\": \"flit.api:main\"\n+\n+    and this object would be looked up by executing the equivalent of::\n+\n+        import flit.api\n+        backend = flit.api.main\n+\n+    It's also legal to leave out the ``:object`` part, e.g. ::\n+\n+        \"build_system\": \"flit.api\"\n+\n+    which acts like::\n+\n+        import flit.api\n+        backend = flit.api\n+\n+    Formally, the string should satisfy this grammar::\n+\n+        identifier = (letter | '_') (letter | '_' | digit)*\n+        module_path = identifier ('.' identifier)*\n+        object_path = identifier ('.' identifier)*\n+        entry_point = module_path (':' object_path)?\n+\n+    And we import ``module_path`` and then lookup\n+    ``module_path.object_path`` (or just ``module_path`` if\n+    ``object_path`` is missing).\n+\n+\n+=========================\n+ Build backend interface\n+=========================\n+\n+The build backend object is expected to have attributes which provide\n+some or all of the following hooks. The common ``config_settings``\n+argument is described after the individual hooks::\n+\n+  def get_build_requires(config_settings):\n+      ...\n+\n+This hook MUST return an additional list of strings containing PEP 508\n+dependency specifications, above and beyond those specified in the\n+``pypackage.json`` file. Example::\n+\n+  def get_build_requires(config_settings):\n+      return [\"wheel >= 0.25\", \"setuptools\"]\n+\n+Optional. If not defined, the default implementation is equivalent to\n+``return []``.\n+\n+::\n+\n+  def get_wheel_metadata(metadata_directory, config_settings):\n+      ...\n+\n+Must create a ``.dist-info`` directory containing wheel metadata\n+inside the specified ``metadata_directory`` (i.e., creates a directory\n+like ``{metadata_directory}/{package}-{version}.dist-info/``. This\n+directory MUST be a valid ``.dist-info`` directory as defined in the\n+wheel specification, except that it need not contain ``RECORD`` or\n+signatures. The hook MAY also create other files inside this\n+directory, and a build frontend MUST ignore such files; the intention\n+here is that in cases where the metadata depends on build-time\n+decisions, the build backend may need to record these decisions in\n+some convenient format for re-use by the actual wheel-building step.\n+\n+Return value is ignored.\n+\n+Optional. If a build frontend needs this information and the method is\n+not defined, it should call ``build_wheel`` and look at the resulting\n+metadata directly.\n+\n+::\n+\n+  def build_wheel(wheel_directory, config_settings, metadata_directory=None):\n+      ...\n+\n+Must build a ``.whl`` file, and place it in the specified\n+``wheel_directory``.\n+\n+If the build frontend has previously called ``get_wheel_metadata`` and\n+depends on the wheel resulting from this call to have metadata\n+matching this earlier call, then it should provide the path to the\n+previous ``metadata_directory`` as an argument. If this argument is\n+provided, then ``build_wheel`` MUST produce a wheel with identical\n+metadata. The directory passed in by the build frontend MUST be\n+identical to the directory created by ``get_wheel_metadata``,\n+including any unrecognized files it created.\n+\n+Mandatory.\n+\n+::\n+\n+  def install_editable(prefix, config_settings, metadata_directory=None):\n+      ...\n+\n+Must perform whatever actions are necessary to install the current\n+project into the Python installation at ``install_prefix`` in an\n+\"editable\" fashion. This is intentionally underspecified, because it's\n+included as a stopgap to avoid regressing compared to the current\n+equally underspecified setuptools ``develop`` command; hopefully a\n+future PEP will replace this hook with something that works better and\n+is better specified. (Unfortunately, cleaning up editable installs to\n+actually work well and be well-specified turns out to be a large and\n+difficult job, so we prefer not to do a half-way job here.)\n+\n+For the meaning and requirements of the ``metadata_directory``\n+argument, see ``build_wheel`` above.\n+\n+Optional. If not defined, then this build backend does not support\n+editable builds.\n+\n+::\n+\n+  config_settings\n+\n+This argument, which is passed to all hooks, is an arbitrary\n+dictionary provided as an \"escape hatch\" for users to pass ad-hoc\n+configuration into individual package builds. Build backends MAY\n+assign any semantics they like to this dictionary. Build frontends\n+SHOULD provide some mechanism for users to specify arbitrary\n+string-key/string-value pairs to be placed in this dictionary. For\n+example, they might support some syntax like ``--package-config\n+CC=gcc``. Build frontends MAY also provide arbitrary other mechanisms\n+for users to place entries in this dictionary. For example, ``pip``\n+might choose to map a mix of modern and legacy command line arguments\n+like::\n+\n+  pip install                                           \\\n+    --package-config CC=gcc                             \\\n+    --global-option=\"--some-global-option\"              \\\n+    --build-option=\"--build-option1\"                    \\\n+    --build-option=\"--build-option2\"\n+\n+into a ``config_settings`` dictionary like::\n+\n+  {\n+   \"CC\": \"gcc\",\n+   \"--global-option\": [\"--some-global-option\"],\n+   \"--build-option\": [\"--build-option1\", \"--build-option2\"],\n+  }\n+\n+Of course, it's up to users to make sure that they pass options which\n+make sense for the particular build backend and package that they are\n+building.\n+\n+All hooks are run with working directory set to the root of the source\n+tree, and MAY print arbitrary informational text on stdout and\n+stderr. They MUST NOT read from stdin, and the build frontend MAY\n+close stdin before invoking the hooks.\n+\n+If a hook raises an exception, or causes the process to terminate,\n+then this indicates an error.\n+\n+\n+Build environment\n+=================\n+\n+One of the responsibilities of a build frontend is to set up the\n+Python environment in which the build backend will run.\n+\n+We do not require that any particular \"virtual environment\" mechanism\n+be used; a build frontend might use virtualenv, or venv, or no special\n+mechanism at all. But whatever mechanism is used MUST meet the\n+following criteria:\n+\n+- All requirements specified by the project's build-requirements must\n+  be available for import from Python. In particular:\n+\n+  - The ``get_build_requires`` hook is executed in an environment\n+    which contains the bootstrap requirements specified in the\n+    ``pypackage.json`` file.\n+\n+  - All other hooks are executed in an environment which contains both\n+    the bootstrap requirements specified in the ``pypackage.json`` hook\n+    and those specified by the ``get_build_requires`` hook.\n+\n+- This must remain true even for new Python subprocesses spawned by\n+  the build environment, e.g. code like::\n+\n+    import sys, subprocess\n+    subprocess.check_call([sys.executable, ...])\n+\n+  must spawn a Python process which has access to all the project's\n+  build-requirements. This is necessary e.g. for build backends that\n+  want to run legacy ``setup.py`` scripts in a subprocess.\n+\n+- All command-line scripts provided by the build-required packages\n+  must be present in the build environment's PATH. For example, if a\n+  project declares a build-requirement on `flit\n+  <https://flit.readthedocs.org/en/latest/>`_, then the following must\n+  work as a mechanism for running the flit command-line tool::\n+\n+    import subprocess\n+    subprocess.check_call([\"flit\", ...])\n+\n+A build backend MUST be prepared to function in any environment which\n+meets the above criteria. In particular, it MUST NOT assume that it\n+has access to any packages except those that are present in the\n+stdlib, or that are explicitly declared as build-requirements.\n+\n+\n+Recommendations for build frontends (non-normative)\n+---------------------------------------------------\n+\n+A build frontend MAY use any mechanism for setting up a build\n+environment that meets the above criteria. For example, simply\n+installing all build-requirements into the global environment would be\n+sufficient to build any compliant package -- but this would be\n+sub-optimal for a number of reasons. This section contains\n+non-normative advice to frontend implementors.\n+\n+A build frontend SHOULD, by default, create an isolated environment\n+for each build, containing only the standard library and any\n+explicitly requested build-dependencies. This has two benefits:\n+\n+- It allows for a single installation run to build multiple packages\n+  that have contradictory build-requirements. E.g. if package1\n+  build-requires pbr==1.8.1, and package2 build-requires pbr==1.7.2,\n+  then these cannot both be installed simultaneously into the global\n+  environment -- which is a problem when the user requests ``pip\n+  install package1 package2``. Or if the user already has pbr==1.8.1\n+  installed in their global environment, and a package build-requires\n+  pbr==1.7.2, then downgrading the user's version would be rather\n+  rude.\n+\n+- It acts as a kind of public health measure to maximize the number of\n+  packages that actually do declare accurate build-dependencies. We\n+  can write all the strongly worded admonitions to package authors we\n+  want, but if build frontends don't enforce isolation by default,\n+  then we'll inevitably end up with lots of packages on PyPI that\n+  build fine on the original author's machine and nowhere else, which\n+  is a headache that no-one needs.\n+\n+However, there will also be situations where build-requirements are\n+problematic in various ways. For example, a package author might\n+accidentally leave off some crucial requirement despite our best\n+efforts; or, a package might declare a build-requirement on `foo >=\n+1.0` which worked great when 1.0 was the latest version, but now 1.1\n+is out and it has a showstopper bug; or, the user might decide to\n+build a package against numpy==1.7 -- overriding the package's\n+preferred numpy==1.8 -- to guarantee that the resulting build will be\n+compatible at the C ABI level with an older version of numpy (even if\n+this means the resulting build is unsupported upstream). Therefore,\n+build frontends SHOULD provide some mechanism for users to override\n+the above defaults. For example, a build frontend could have a\n+``--build-with-system-site-packages`` option that causes the\n+``--system-site-packages`` option to be passed to\n+virtualenv-or-equivalent when creating build environments, or a\n+``--build-requirements-override=my-requirements.txt`` option that\n+overrides the project's normal build-requirements.\n+\n+The general principle here is that we want to enforce hygiene on\n+package *authors*, while still allowing *end-users* to open up the\n+hood and apply duct tape when necessary.\n+\n+\n+======================\n+ Source distributions\n+======================\n+\n+For now, we continue with the legacy sdist format which is mostly\n+undefined, but basically comes down to: a file named\n+``{NAME}-{VERSION}.{EXT}``, which unpacks into a buildable source tree\n+called ``{NAME}-{VERSION}/``. Traditionally these have always\n+contained \"version 0\" source trees; we now allow them to also contain\n+version 1+ source trees.\n+\n+Integration frontends require that an sdist named\n+``{NAME}-{VERSION}.{EXT}`` will generate a wheel named\n+``{NAME}-{VERSION}-{COMPAT-INFO}.whl``.\n+\n+\n+===================================\n+ Comparison to competing proposals\n+===================================\n+\n+The primary difference between this and competing proposals (`in\n+particular\n+<https://github.com/pypa/interoperability-peps/pull/54/files>`_) is\n+that our build backend is defined via a Python hook-based interface\n+rather than a command-line based interface.\n+\n+We do *not* expect that this will, by itself, intrinsically reduce the\n+complexity calling into the backend, because build frontends will\n+in any case want to run hooks inside a child -- this is important to\n+isolate the build frontend itself from the backend code and to better\n+control the build backends execution environment. So under both\n+proposals, there will need to be some code in ``pip`` to spawn a\n+subprocess and talk to some kind of command-line/IPC interface, and\n+there will need to be some code in the subprocess that knows how to\n+parse these command line arguments and call the actual build backend\n+implementation. So this diagram applies to all proposals equally::\n+\n+  +-----------+          +---------------+           +----------------+\n+  | frontend  | -spawn-> | child cmdline | -Python-> |    backend     |\n+  |   (pip)   |          |   interface   |           | implementation |\n+  +-----------+          +---------------+           +----------------+\n+\n+\n+\n+The key difference between the two approaches is how these interface\n+boundaries map onto project structure::\n+\n+  .-= This PEP =-.\n+\n+  +-----------+          +---------------+    |      +----------------+\n+  | frontend  | -spawn-> | child cmdline | -Python-> |    backend     |\n+  |   (pip)   |          |   interface   |    |      | implementation |\n+  +-----------+          +---------------+    |      +----------------+\n+                                              |\n+  |______________________________________|    |\n+     Owned by pip, updated in lockstep        |\n+                                              |\n+                                              |\n+                                   PEP-defined interface boundary\n+                                 Changes here require distutils-sig\n+\n+\n+  .-= Alternative =-.\n+\n+  +-----------+    |     +---------------+           +----------------+\n+  | frontend  | -spawn-> | child cmdline | -Python-> |    backend     |\n+  |   (pip)   |    |     |   interface   |           | implementation |\n+  +-----------+    |     +---------------+           +----------------+\n+                   |\n+                   |     |____________________________________________|\n+                   |      Owned by build backend, updated in lockstep\n+                   |\n+      PEP-defined interface boundary\n+    Changes here require distutils-sig\n+\n+\n+By moving the PEP-defined interface boundary into Python code, we gain\n+three key advantages.\n+\n+**First**, because there will likely be only a small number of build\n+frontends (``pip``, and... maybe a few others?), while there will\n+likely be a long tail of custom build backends (since these are chosen\n+separately by each package to match their particular build\n+requirements), the actual diagrams probably look more like::\n+\n+  .-= This PEP =-.\n+\n+  +-----------+          +---------------+           +----------------+\n+  | frontend  | -spawn-> | child cmdline | -Python+> |    backend     |\n+  |   (pip)   |          |   interface   |        |  | implementation |\n+  +-----------+          +---------------+        |  +----------------+\n+                                                  |\n+                                                  |  +----------------+\n+                                                  +> |    backend     |\n+                                                  |  | implementation |\n+                                                  |  +----------------+\n+                                                  :\n+                                                  :\n+\n+  .-= Alternative =-.\n+\n+  +-----------+          +---------------+           +----------------+\n+  | frontend  | -spawn+> | child cmdline | -Python-> |    backend     |\n+  |   (pip)   |       |  |   interface   |           | implementation |\n+  +-----------+       |  +---------------+           +----------------+\n+                      |\n+                      |  +---------------+           +----------------+\n+                      +> | child cmdline | -Python-> |    backend     |\n+                      |  |   interface   |           | implementation |\n+                      |  +---------------+           +----------------+\n+                      :\n+                      :\n+\n+That is, this PEP leads to less total code in the overall\n+ecosystem. And in particular, it reduces the barrier to entry of\n+making a new build system. For example, this is a complete, working\n+build backend::\n+\n+    # mypackage_custom_build_backend.py\n+    import os.path\n+\n+    def get_build_requires(config_settings, config_directory):\n+        return [\"wheel\"]\n+\n+    def build_wheel(wheel_directory, config_settings, config_directory=None):\n+        from wheel.archive import archive_wheelfile\n+        path = os.path.join(wheel_directory,\n+                            \"mypackage-0.1-py2.py3-none-any\")\n+        archive_wheelfile(path, \"src/\")\n+\n+Of course, this is a *terrible* build backend: it requires the user to\n+have manually set up the wheel metadata in\n+``src/mypackage-0.1.dist-info/``; when the version number changes it\n+must be manually updated in multiple places; it doesn't implement the\n+metadata or develop hooks, ... but it works, and these features can be\n+added incrementally. Much experience suggests that large successful\n+projects often originate as quick hacks (e.g., Linux -- \"just a hobby,\n+won't be big and professional\"; `IPython/Jupyter\n+<https://en.wikipedia.org/wiki/IPython#Grants_and_awards>`_ -- `a grad\n+student's ``$PYTHONSTARTUP`` file\n+<http://blog.fperez.org/2012/01/ipython-notebook-historical.html>`_),\n+so if our goal is to encourage the growth of a vibrant ecosystem of\n+good build tools, it's important to minimize the barrier to entry.\n+\n+\n+**Second**, because Python provides a simpler yet richer structure for\n+describing interfaces, we remove unnecessary complexity from the\n+specification -- and specifications are the worst place for\n+complexity, because changing specifications requires painful\n+consensus-building across many stakeholders. In the command-line\n+interface approach, we have to come up with ad hoc ways to map\n+multiple different kinds of inputs into a single linear command line\n+(e.g. how do we avoid collisions between user-specified configuration\n+arguments and PEP-defined arguments? how do we specify optional\n+arguments? when working with a Python interface these questions have\n+simple, obvious answers). When spawning and managing subprocesses,\n+there are many fiddly details that must be gotten right, subtle\n+cross-platform differences, and some of the most obvious approaches --\n+e.g., using stdout to return data for the ``build_requires`` operation\n+-- can create unexpected pitfalls (e.g., what happens when computing\n+the build requirements requires spawning some child processes, and\n+these children occasionally print an error message to stdout?\n+obviously a careful build backend author can avoid this problem, but\n+the most obvious way of defining a Python interface removes this\n+possibility entirely, because the hook return value is clearly\n+demarcated).\n+\n+In general, the need to isolate build backends into their own process\n+means that we can't remove IPC complexity entirely -- but by placing\n+both sides of the IPC channel under the control of a single project,\n+we make it much much cheaper to fix bugs in the IPC interface than if\n+fixing bugs requires coordinated agreement and coordinated changes\n+across the ecosystem.\n+\n+**Third**, and most crucially, the Python hook approach gives us much\n+more powerful options for evolving this specification in the future.\n+\n+For concreteness, imagine that next year we add a new\n+``install_editable2`` hook, which replaces the current\n+``install_editable`` hook with something better specified. In order to\n+manage the transition, we want it to be possible for build frontends\n+to transparently use ``install_editable2`` when available and fall\n+back onto ``install_editable`` otherwise; and we want it to be\n+possible for build backends to define both methods, for compatibility\n+with both old and new build frontends.\n+\n+Furthermore, our mechanism should also fulfill two more goals: (a) If\n+new versions of e.g. ``pip`` and ``flit`` are both updated to support\n+the new interface, then this should be sufficient for it to be used;\n+in particular, it should *not* be necessary for every project that\n+*uses* ``flit`` to update its individual ``pypackage.json`` file. (b)\n+We do not want to have to spawn extra processes just to perform this\n+negotation, because process spawns can easily become a bottleneck when","path":"unpublished/build-system-abstraction-njs.rst","position":577,"original_position":577,"commit_id":"cf4d34b605b98c3098781e451232bc5351f74269","original_commit_id":"cf4d34b605b98c3098781e451232bc5351f74269","user":{"login":"njsmith","id":609896,"avatar_url":"https://avatars.githubusercontent.com/u/609896?v=3","gravatar_id":"","url":"https://api.github.com/users/njsmith","html_url":"https://github.com/njsmith","followers_url":"https://api.github.com/users/njsmith/followers","following_url":"https://api.github.com/users/njsmith/following{/other_user}","gists_url":"https://api.github.com/users/njsmith/gists{/gist_id}","starred_url":"https://api.github.com/users/njsmith/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/njsmith/subscriptions","organizations_url":"https://api.github.com/users/njsmith/orgs","repos_url":"https://api.github.com/users/njsmith/repos","events_url":"https://api.github.com/users/njsmith/events{/privacy}","received_events_url":"https://api.github.com/users/njsmith/received_events","type":"User","site_admin":false},"body":"thanks, fixed","created_at":"2016-02-17T22:56:55Z","updated_at":"2016-02-17T22:56:55Z","html_url":"https://github.com/pypa/interoperability-peps/pull/63#discussion_r53245930","pull_request_url":"https://api.github.com/repos/pypa/interoperability-peps/pulls/63","_links":{"self":{"href":"https://api.github.com/repos/pypa/interoperability-peps/pulls/comments/53245930"},"html":{"href":"https://github.com/pypa/interoperability-peps/pull/63#discussion_r53245930"},"pull_request":{"href":"https://api.github.com/repos/pypa/interoperability-peps/pulls/63"}}},"pull_request":{"url":"https://api.github.com/repos/pypa/interoperability-peps/pulls/63","id":59583376,"html_url":"https://github.com/pypa/interoperability-peps/pull/63","diff_url":"https://github.com/pypa/interoperability-peps/pull/63.diff","patch_url":"https://github.com/pypa/interoperability-peps/pull/63.patch","issue_url":"https://api.github.com/repos/pypa/interoperability-peps/issues/63","number":63,"state":"open","locked":false,"title":"Another build system abstraction PEP","user":{"login":"njsmith","id":609896,"avatar_url":"https://avatars.githubusercontent.com/u/609896?v=3","gravatar_id":"","url":"https://api.github.com/users/njsmith","html_url":"https://github.com/njsmith","followers_url":"https://api.github.com/users/njsmith/followers","following_url":"https://api.github.com/users/njsmith/following{/other_user}","gists_url":"https://api.github.com/users/njsmith/gists{/gist_id}","starred_url":"https://api.github.com/users/njsmith/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/njsmith/subscriptions","organizations_url":"https://api.github.com/users/njsmith/orgs","repos_url":"https://api.github.com/users/njsmith/repos","events_url":"https://api.github.com/users/njsmith/events{/privacy}","received_events_url":"https://api.github.com/users/njsmith/received_events","type":"User","site_admin":false},"body":"\n\n<!-- Reviewable:start -->\n[<img src=\"https://reviewable.io/review_button.svg\" height=\"40\" alt=\"Review on Reviewable\"/>](https://reviewable.io/reviews/pypa/interoperability-peps/63)\n<!-- Reviewable:end -->\n","created_at":"2016-02-17T01:28:44Z","updated_at":"2016-02-17T22:56:55Z","closed_at":null,"merged_at":null,"merge_commit_sha":"be83ac4e1a48e213dda19a0d926e816c656c8bcc","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/pypa/interoperability-peps/pulls/63/commits","review_comments_url":"https://api.github.com/repos/pypa/interoperability-peps/pulls/63/comments","review_comment_url":"https://api.github.com/repos/pypa/interoperability-peps/pulls/comments{/number}","comments_url":"https://api.github.com/repos/pypa/interoperability-peps/issues/63/comments","statuses_url":"https://api.github.com/repos/pypa/interoperability-peps/statuses/cf4d34b605b98c3098781e451232bc5351f74269","head":{"label":"njsmith:build-system-abstraction-njs","ref":"build-system-abstraction-njs","sha":"cf4d34b605b98c3098781e451232bc5351f74269","user":{"login":"njsmith","id":609896,"avatar_url":"https://avatars.githubusercontent.com/u/609896?v=3","gravatar_id":"","url":"https://api.github.com/users/njsmith","html_url":"https://github.com/njsmith","followers_url":"https://api.github.com/users/njsmith/followers","following_url":"https://api.github.com/users/njsmith/following{/other_user}","gists_url":"https://api.github.com/users/njsmith/gists{/gist_id}","starred_url":"https://api.github.com/users/njsmith/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/njsmith/subscriptions","organizations_url":"https://api.github.com/users/njsmith/orgs","repos_url":"https://api.github.com/users/njsmith/repos","events_url":"https://api.github.com/users/njsmith/events{/privacy}","received_events_url":"https://api.github.com/users/njsmith/received_events","type":"User","site_admin":false},"repo":{"id":45770582,"name":"interoperability-peps","full_name":"njsmith/interoperability-peps","owner":{"login":"njsmith","id":609896,"avatar_url":"https://avatars.githubusercontent.com/u/609896?v=3","gravatar_id":"","url":"https://api.github.com/users/njsmith","html_url":"https://github.com/njsmith","followers_url":"https://api.github.com/users/njsmith/followers","following_url":"https://api.github.com/users/njsmith/following{/other_user}","gists_url":"https://api.github.com/users/njsmith/gists{/gist_id}","starred_url":"https://api.github.com/users/njsmith/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/njsmith/subscriptions","organizations_url":"https://api.github.com/users/njsmith/orgs","repos_url":"https://api.github.com/users/njsmith/repos","events_url":"https://api.github.com/users/njsmith/events{/privacy}","received_events_url":"https://api.github.com/users/njsmith/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/njsmith/interoperability-peps","description":"Development repo for evolution of PyPA interoperability standards (released versions are published as PEPs on python.org)","fork":true,"url":"https://api.github.com/repos/njsmith/interoperability-peps","forks_url":"https://api.github.com/repos/njsmith/interoperability-peps/forks","keys_url":"https://api.github.com/repos/njsmith/interoperability-peps/keys{/key_id}","collaborators_url":"https://api.github.com/repos/njsmith/interoperability-peps/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/njsmith/interoperability-peps/teams","hooks_url":"https://api.github.com/repos/njsmith/interoperability-peps/hooks","issue_events_url":"https://api.github.com/repos/njsmith/interoperability-peps/issues/events{/number}","events_url":"https://api.github.com/repos/njsmith/interoperability-peps/events","assignees_url":"https://api.github.com/repos/njsmith/interoperability-peps/assignees{/user}","branches_url":"https://api.github.com/repos/njsmith/interoperability-peps/branches{/branch}","tags_url":"https://api.github.com/repos/njsmith/interoperability-peps/tags","blobs_url":"https://api.github.com/repos/njsmith/interoperability-peps/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/njsmith/interoperability-peps/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/njsmith/interoperability-peps/git/refs{/sha}","trees_url":"https://api.github.com/repos/njsmith/interoperability-peps/git/trees{/sha}","statuses_url":"https://api.github.com/repos/njsmith/interoperability-peps/statuses/{sha}","languages_url":"https://api.github.com/repos/njsmith/interoperability-peps/languages","stargazers_url":"https://api.github.com/repos/njsmith/interoperability-peps/stargazers","contributors_url":"https://api.github.com/repos/njsmith/interoperability-peps/contributors","subscribers_url":"https://api.github.com/repos/njsmith/interoperability-peps/subscribers","subscription_url":"https://api.github.com/repos/njsmith/interoperability-peps/subscription","commits_url":"https://api.github.com/repos/njsmith/interoperability-peps/commits{/sha}","git_commits_url":"https://api.github.com/repos/njsmith/interoperability-peps/git/commits{/sha}","comments_url":"https://api.github.com/repos/njsmith/interoperability-peps/comments{/number}","issue_comment_url":"https://api.github.com/repos/njsmith/interoperability-peps/issues/comments{/number}","contents_url":"https://api.github.com/repos/njsmith/interoperability-peps/contents/{+path}","compare_url":"https://api.github.com/repos/njsmith/interoperability-peps/compare/{base}...{head}","merges_url":"https://api.github.com/repos/njsmith/interoperability-peps/merges","archive_url":"https://api.github.com/repos/njsmith/interoperability-peps/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/njsmith/interoperability-peps/downloads","issues_url":"https://api.github.com/repos/njsmith/interoperability-peps/issues{/number}","pulls_url":"https://api.github.com/repos/njsmith/interoperability-peps/pulls{/number}","milestones_url":"https://api.github.com/repos/njsmith/interoperability-peps/milestones{/number}","notifications_url":"https://api.github.com/repos/njsmith/interoperability-peps/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/njsmith/interoperability-peps/labels{/name}","releases_url":"https://api.github.com/repos/njsmith/interoperability-peps/releases{/id}","deployments_url":"https://api.github.com/repos/njsmith/interoperability-peps/deployments","created_at":"2015-11-08T07:03:51Z","updated_at":"2015-11-08T07:03:52Z","pushed_at":"2016-02-17T01:27:35Z","git_url":"git://github.com/njsmith/interoperability-peps.git","ssh_url":"git@github.com:njsmith/interoperability-peps.git","clone_url":"https://github.com/njsmith/interoperability-peps.git","svn_url":"https://github.com/njsmith/interoperability-peps","homepage":null,"size":24521,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"pypa:master","ref":"master","sha":"a7e2b1059009da1f2b005ca0913644d8b2d2976a","user":{"login":"pypa","id":647025,"avatar_url":"https://avatars.githubusercontent.com/u/647025?v=3","gravatar_id":"","url":"https://api.github.com/users/pypa","html_url":"https://github.com/pypa","followers_url":"https://api.github.com/users/pypa/followers","following_url":"https://api.github.com/users/pypa/following{/other_user}","gists_url":"https://api.github.com/users/pypa/gists{/gist_id}","starred_url":"https://api.github.com/users/pypa/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pypa/subscriptions","organizations_url":"https://api.github.com/users/pypa/orgs","repos_url":"https://api.github.com/users/pypa/repos","events_url":"https://api.github.com/users/pypa/events{/privacy}","received_events_url":"https://api.github.com/users/pypa/received_events","type":"Organization","site_admin":false},"repo":{"id":28580365,"name":"interoperability-peps","full_name":"pypa/interoperability-peps","owner":{"login":"pypa","id":647025,"avatar_url":"https://avatars.githubusercontent.com/u/647025?v=3","gravatar_id":"","url":"https://api.github.com/users/pypa","html_url":"https://github.com/pypa","followers_url":"https://api.github.com/users/pypa/followers","following_url":"https://api.github.com/users/pypa/following{/other_user}","gists_url":"https://api.github.com/users/pypa/gists{/gist_id}","starred_url":"https://api.github.com/users/pypa/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pypa/subscriptions","organizations_url":"https://api.github.com/users/pypa/orgs","repos_url":"https://api.github.com/users/pypa/repos","events_url":"https://api.github.com/users/pypa/events{/privacy}","received_events_url":"https://api.github.com/users/pypa/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/pypa/interoperability-peps","description":"Development repo for evolution of PyPA interoperability standards (released versions are published as PEPs on python.org)","fork":false,"url":"https://api.github.com/repos/pypa/interoperability-peps","forks_url":"https://api.github.com/repos/pypa/interoperability-peps/forks","keys_url":"https://api.github.com/repos/pypa/interoperability-peps/keys{/key_id}","collaborators_url":"https://api.github.com/repos/pypa/interoperability-peps/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/pypa/interoperability-peps/teams","hooks_url":"https://api.github.com/repos/pypa/interoperability-peps/hooks","issue_events_url":"https://api.github.com/repos/pypa/interoperability-peps/issues/events{/number}","events_url":"https://api.github.com/repos/pypa/interoperability-peps/events","assignees_url":"https://api.github.com/repos/pypa/interoperability-peps/assignees{/user}","branches_url":"https://api.github.com/repos/pypa/interoperability-peps/branches{/branch}","tags_url":"https://api.github.com/repos/pypa/interoperability-peps/tags","blobs_url":"https://api.github.com/repos/pypa/interoperability-peps/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/pypa/interoperability-peps/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/pypa/interoperability-peps/git/refs{/sha}","trees_url":"https://api.github.com/repos/pypa/interoperability-peps/git/trees{/sha}","statuses_url":"https://api.github.com/repos/pypa/interoperability-peps/statuses/{sha}","languages_url":"https://api.github.com/repos/pypa/interoperability-peps/languages","stargazers_url":"https://api.github.com/repos/pypa/interoperability-peps/stargazers","contributors_url":"https://api.github.com/repos/pypa/interoperability-peps/contributors","subscribers_url":"https://api.github.com/repos/pypa/interoperability-peps/subscribers","subscription_url":"https://api.github.com/repos/pypa/interoperability-peps/subscription","commits_url":"https://api.github.com/repos/pypa/interoperability-peps/commits{/sha}","git_commits_url":"https://api.github.com/repos/pypa/interoperability-peps/git/commits{/sha}","comments_url":"https://api.github.com/repos/pypa/interoperability-peps/comments{/number}","issue_comment_url":"https://api.github.com/repos/pypa/interoperability-peps/issues/comments{/number}","contents_url":"https://api.github.com/repos/pypa/interoperability-peps/contents/{+path}","compare_url":"https://api.github.com/repos/pypa/interoperability-peps/compare/{base}...{head}","merges_url":"https://api.github.com/repos/pypa/interoperability-peps/merges","archive_url":"https://api.github.com/repos/pypa/interoperability-peps/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/pypa/interoperability-peps/downloads","issues_url":"https://api.github.com/repos/pypa/interoperability-peps/issues{/number}","pulls_url":"https://api.github.com/repos/pypa/interoperability-peps/pulls{/number}","milestones_url":"https://api.github.com/repos/pypa/interoperability-peps/milestones{/number}","notifications_url":"https://api.github.com/repos/pypa/interoperability-peps/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/pypa/interoperability-peps/labels{/name}","releases_url":"https://api.github.com/repos/pypa/interoperability-peps/releases{/id}","deployments_url":"https://api.github.com/repos/pypa/interoperability-peps/deployments","created_at":"2014-12-29T04:35:09Z","updated_at":"2016-01-21T06:12:32Z","pushed_at":"2016-02-17T01:28:49Z","git_url":"git://github.com/pypa/interoperability-peps.git","ssh_url":"git@github.com:pypa/interoperability-peps.git","clone_url":"https://github.com/pypa/interoperability-peps.git","svn_url":"https://github.com/pypa/interoperability-peps","homepage":null,"size":24582,"stargazers_count":8,"watchers_count":8,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":11,"mirror_url":null,"open_issues_count":34,"forks":11,"open_issues":34,"watchers":8,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/pypa/interoperability-peps/pulls/63"},"html":{"href":"https://github.com/pypa/interoperability-peps/pull/63"},"issue":{"href":"https://api.github.com/repos/pypa/interoperability-peps/issues/63"},"comments":{"href":"https://api.github.com/repos/pypa/interoperability-peps/issues/63/comments"},"review_comments":{"href":"https://api.github.com/repos/pypa/interoperability-peps/pulls/63/comments"},"review_comment":{"href":"https://api.github.com/repos/pypa/interoperability-peps/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/pypa/interoperability-peps/pulls/63/commits"},"statuses":{"href":"https://api.github.com/repos/pypa/interoperability-peps/statuses/cf4d34b605b98c3098781e451232bc5351f74269"}}}}