././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/.bumpversion.cfg0000644000000000000000000000014700000000000012614 0ustar00[bumpversion] current_version = 0.12.0 commit = True tag = True [bumpversion:file:pep517/__init__.py] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/.github/workflows/tests.yml0000644000000000000000000000210400000000000014761 0ustar00name: Tests on: [push, pull_request] jobs: tests: runs-on: ubuntu-latest strategy: matrix: python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9] steps: - name: Checkout uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install Tox run: | pip install tox tox-venv - name: Run tests run: tox -e python publish: runs-on: ubuntu-latest needs: tests if: ${{ startsWith(github.ref, 'refs/tags/') }} steps: - name: Checkout uses: actions/checkout@v2 - name: Set up Python 3.9 uses: actions/setup-python@v2 with: python-version: 3.9 - name: Install Tox run: | pip install tox tox-venv - name: Publish to PyPI run: tox -e release env: FLIT_USERNAME: __token__ FLIT_PASSWORD: ${{ secrets.PYPI_TOKEN }} TOX_TESTENV_PASSENV: "FLIT_USERNAME FLIT_PASSWORD" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/.gitignore0000644000000000000000000000013000000000000011464 0ustar00__pycache__/ *.pyc /dist/ .tox .pytest_cache doc/_build/ *.egg-info/ .coverage htmlcov/ ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/LICENSE0000644000000000000000000000207100000000000010507 0ustar00The MIT License (MIT) Copyright (c) 2017 Thomas Kluyver Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/README.rst0000644000000000000000000000622200000000000011173 0ustar00API to call PEP 517 hooks ========================= `PEP 517 `_ specifies a standard API for systems which build Python packages. `PEP 660 `_ extends it with a build mode that leads to editable installs. This package contains wrappers around the hooks specified by PEP 517 and PEP 660. It provides: - A mechanism to call the hooks in a subprocess, so they are isolated from the current process. - Fallbacks for the optional hooks, so that frontends can call the hooks without checking which are defined. Run the tests with ``pytest`` or `tox `_. Usage—you are responsible for ensuring build requirements are available: .. code-block:: python import os import tomli from pep517.wrappers import Pep517HookCaller src = 'path/to/source' # Folder containing 'pyproject.toml' with open(os.path.join(src, 'pyproject.toml')) as f: build_sys = tomli.load(f)['build-system'] print(build_sys['requires']) # List of static requirements # The caller is responsible for installing these and running the hooks in # an environment where they are available. hooks = Pep517HookCaller( src, build_backend=build_sys['build-backend'], backend_path=build_sys.get('backend-path'), ) config_options = {} # Optional parameters for backend # List of dynamic requirements: print(hooks.get_requires_for_build_wheel(config_options)) # Again, the caller is responsible for installing these build requirements destination = 'also/a/folder' whl_filename = hooks.build_wheel(destination, config_options) assert os.path.isfile(os.path.join(destination, whl_filename)) Deprecated high-level --------------------- For now, ``pep517`` also contains higher-level functions which install the build dependencies into a temporary environment and build a wheel/sdist using them. This is a rough implementation, e.g. it does not do proper build isolation. The `PyPA build project `_ is recommended as an alternative, although it's still quite young in October 2020. This layer of functionality in ``pep517`` is now deprecated, but won't be removed for some time, as there is code relying on it. High level usage, with build requirements handled: .. code-block:: python import os from pep517.envbuild import build_wheel, build_sdist src = 'path/to/source' # Folder containing 'pyproject.toml' destination = 'also/a/folder' whl_filename = build_wheel(src, destination) assert os.path.isfile(os.path.join(destination, whl_filename)) targz_filename = build_sdist(src, destination) assert os.path.isfile(os.path.join(destination, targz_filename)) To test the build backend for a project, run in a system shell: .. code-block:: shell python3 -m pep517.check path/to/source # source dir containing pyproject.toml To build a backend into source and/or binary distributions, run in a shell: .. code-block:: shell python -m pep517.build path/to/source # source dir containing pyproject.toml All of this high-level functionality is deprecated. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/RELEASE.rst0000644000000000000000000000042500000000000011315 0ustar00To cut a release, use `bump2version `_, the `preferred version `_ of bumpversion. Something like: bump2version minor Then push the commit and tags to GitHub. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/appveyor.yml0000644000000000000000000000100600000000000012067 0ustar00environment: matrix: - PYTHON: "C:\\Python27" - PYTHON: "C:\\Python27-x64" - PYTHON: "C:\\Python34" - PYTHON: "C:\\Python34-x64" - PYTHON: "C:\\Python35" - PYTHON: "C:\\Python35-x64" - PYTHON: "C:\\Python36" - PYTHON: "C:\\Python36-x64" - PYTHON: "C:\\Python37" - PYTHON: "C:\\Python37-x64" install: - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" - "python --version" - "pip install tox" build: off cache: - '%LOCALAPPDATA%\pip\Cache' test_script: - "tox -e py" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/dev-requirements.txt0000644000000000000000000000047100000000000013544 0ustar00pytest pytest-flake8 flake8 < 4 # https://github.com/tholo/pytest-flake8/issues/81 pytest-forward-compatibility; python_version<'3' mock ; python_version<'3.6' testpath toml ; python_version<'3.6' tomli ; python_version>='3.6' setuptools>=30 importlib_metadata ; python_version<'3.8' zipp ; python_version<'3.8' ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/doc/Makefile0000644000000000000000000000117200000000000011710 0ustar00# Minimal makefile for Sphinx documentation # # You can set these variables from the command line, and also # from the environment for the first two. SPHINXOPTS ?= SPHINXBUILD ?= sphinx-build SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/doc/callhooks.rst0000644000000000000000000000166500000000000012770 0ustar00API reference ============= .. module:: pep517 Calling the build system ------------------------ .. autoclass:: Pep517HookCaller .. automethod:: get_requires_for_build_sdist .. automethod:: get_requires_for_build_wheel .. automethod:: get_requires_for_build_editable .. automethod:: prepare_metadata_for_build_wheel .. automethod:: prepare_metadata_for_build_editable .. automethod:: build_sdist .. automethod:: build_wheel .. automethod:: build_editable .. automethod:: subprocess_runner Subprocess runners ------------------ These functions may be provided when creating :class:`Pep517HookCaller`, or to :meth:`Pep517HookCaller.subprocess_runner`. .. autofunction:: default_subprocess_runner .. autofunction:: quiet_subprocess_runner Exceptions ---------- .. autoexception:: BackendUnavailable .. autoexception:: BackendInvalid .. autoexception:: HookMissing .. autoexception:: UnsupportedOperation ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/doc/changelog.rst0000644000000000000000000000272000000000000012731 0ustar00Changelog ========= 0.11.1 ------ - Fix DeprecationWarning in tomli. 0.11 ---- - Support editable hooks (`PEP 660 `_). - Use the TOML 1.0 compliant ``tomli`` parser module on Python 3.6 and above. - Ensure TOML files are always read as UTF-8. - Switch CI to Github actions. 0.10 ---- - Avoid shadowing imports such as ``colorlog`` in the backend, by moving the ``_in_process.py`` script into a separate subpackage. - Issue warnings when using the deprecated ``pep517.build`` and ``pep517.check`` modules at the command line. See the `PyPA build project `_ for a replacement. - Allow building with flit_core 3.x. - Prefer the standard library ``unittest.mock`` to ``mock`` for tests on Python 3.6 and above. 0.9.1 ----- - Silence some static analysis warnings. 0.9 --- - Deprecated the higher level API which handles creating an environment and installing build dependencies. This was not very complete, and the `PyPA build project `_ is designed for this use case. - New ``python_executable`` parameter for :class:`.Pep517HookCaller` to run hooks with a different Python interpreter. - Fix for locating the script to run in the subprocess in some scenarios. - Fix example in README to get ``build-backend`` correctly. - Created `documentation on Read the Docs `__ - Various minor improvements to testing. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/doc/conf.py0000644000000000000000000000377000000000000011555 0ustar00# Configuration file for the Sphinx documentation builder. # # This file only contains a selection of the most common options. For a full # list see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html # -- Path setup -------------------------------------------------------------- # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys sys.path.insert(0, os.path.abspath('..')) # -- Project information ----------------------------------------------------- project = 'pep517' copyright = '2020, Thomas Kluyver' author = 'Thomas Kluyver' # -- General configuration --------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx_rtd_theme', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # Tell RTD to use index.rst, not contents.rst # https://github.com/readthedocs/readthedocs.org/issues/2569 master_doc = 'index' # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'sphinx_rtd_theme' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/doc/index.rst0000644000000000000000000000040300000000000012105 0ustar00pep517 ====== This package provides an API to call the hooks defined in :pep:`517`. .. toctree:: :maxdepth: 2 :caption: Contents: callhooks changelog Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/doc/make.bat0000644000000000000000000000143300000000000011655 0ustar00@ECHO OFF pushd %~dp0 REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set SOURCEDIR=. set BUILDDIR=_build if "%1" == "" goto help %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% goto end :help %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% :end popd ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/doc/requirements.txt0000644000000000000000000000007200000000000013532 0ustar00toml ; python_version<'3.6' tomli ; python_version>='3.6' ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/issue_template.md0000644000000000000000000000030500000000000013045 0ustar00 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/pep517/__init__.py0000644000000000000000000000020200000000000012626 0ustar00"""Wrappers to build Python packages using PEP 517 hooks """ __version__ = '0.12.0' from .wrappers import * # noqa: F401, F403 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/pep517/build.py0000644000000000000000000000660100000000000012177 0ustar00"""Build a project using PEP 517 hooks. """ import argparse import io import logging import os import shutil from .envbuild import BuildEnvironment from .wrappers import Pep517HookCaller from .dirtools import tempdir, mkdir_p from .compat import FileNotFoundError, toml_load log = logging.getLogger(__name__) def validate_system(system): """ Ensure build system has the requisite fields. """ required = {'requires', 'build-backend'} if not (required <= set(system)): message = "Missing required fields: {missing}".format( missing=required-set(system), ) raise ValueError(message) def load_system(source_dir): """ Load the build system from a source dir (pyproject.toml). """ pyproject = os.path.join(source_dir, 'pyproject.toml') with io.open(pyproject, 'rb') as f: pyproject_data = toml_load(f) return pyproject_data['build-system'] def compat_system(source_dir): """ Given a source dir, attempt to get a build system backend and requirements from pyproject.toml. Fallback to setuptools but only if the file was not found or a build system was not indicated. """ try: system = load_system(source_dir) except (FileNotFoundError, KeyError): system = {} system.setdefault( 'build-backend', 'setuptools.build_meta:__legacy__', ) system.setdefault('requires', ['setuptools', 'wheel']) return system def _do_build(hooks, env, dist, dest): get_requires_name = 'get_requires_for_build_{dist}'.format(**locals()) get_requires = getattr(hooks, get_requires_name) reqs = get_requires({}) log.info('Got build requires: %s', reqs) env.pip_install(reqs) log.info('Installed dynamic build dependencies') with tempdir() as td: log.info('Trying to build %s in %s', dist, td) build_name = 'build_{dist}'.format(**locals()) build = getattr(hooks, build_name) filename = build(td, {}) source = os.path.join(td, filename) shutil.move(source, os.path.join(dest, os.path.basename(filename))) def build(source_dir, dist, dest=None, system=None): system = system or load_system(source_dir) dest = os.path.join(source_dir, dest or 'dist') mkdir_p(dest) validate_system(system) hooks = Pep517HookCaller( source_dir, system['build-backend'], system.get('backend-path') ) with BuildEnvironment() as env: env.pip_install(system['requires']) _do_build(hooks, env, dist, dest) parser = argparse.ArgumentParser() parser.add_argument( 'source_dir', help="A directory containing pyproject.toml", ) parser.add_argument( '--binary', '-b', action='store_true', default=False, ) parser.add_argument( '--source', '-s', action='store_true', default=False, ) parser.add_argument( '--out-dir', '-o', help="Destination in which to save the builds relative to source dir", ) def main(args): log.warning('pep517.build is deprecated. ' 'Consider switching to https://pypi.org/project/build/') # determine which dists to build dists = list(filter(None, ( 'sdist' if args.source or not args.binary else None, 'wheel' if args.binary or not args.source else None, ))) for dist in dists: build(args.source_dir, dist, args.out_dir) if __name__ == '__main__': main(parser.parse_args()) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/pep517/check.py0000644000000000000000000001370400000000000012157 0ustar00"""Check a project and backend by attempting to build using PEP 517 hooks. """ import argparse import io import logging import os from os.path import isfile, join as pjoin import shutil from subprocess import CalledProcessError import sys import tarfile from tempfile import mkdtemp import zipfile from .colorlog import enable_colourful_output from .compat import TOMLDecodeError, toml_load from .envbuild import BuildEnvironment from .wrappers import Pep517HookCaller log = logging.getLogger(__name__) def check_build_sdist(hooks, build_sys_requires): with BuildEnvironment() as env: try: env.pip_install(build_sys_requires) log.info('Installed static build dependencies') except CalledProcessError: log.error('Failed to install static build dependencies') return False try: reqs = hooks.get_requires_for_build_sdist({}) log.info('Got build requires: %s', reqs) except Exception: log.error('Failure in get_requires_for_build_sdist', exc_info=True) return False try: env.pip_install(reqs) log.info('Installed dynamic build dependencies') except CalledProcessError: log.error('Failed to install dynamic build dependencies') return False td = mkdtemp() log.info('Trying to build sdist in %s', td) try: try: filename = hooks.build_sdist(td, {}) log.info('build_sdist returned %r', filename) except Exception: log.info('Failure in build_sdist', exc_info=True) return False if not filename.endswith('.tar.gz'): log.error( "Filename %s doesn't have .tar.gz extension", filename) return False path = pjoin(td, filename) if isfile(path): log.info("Output file %s exists", path) else: log.error("Output file %s does not exist", path) return False if tarfile.is_tarfile(path): log.info("Output file is a tar file") else: log.error("Output file is not a tar file") return False finally: shutil.rmtree(td) return True def check_build_wheel(hooks, build_sys_requires): with BuildEnvironment() as env: try: env.pip_install(build_sys_requires) log.info('Installed static build dependencies') except CalledProcessError: log.error('Failed to install static build dependencies') return False try: reqs = hooks.get_requires_for_build_wheel({}) log.info('Got build requires: %s', reqs) except Exception: log.error('Failure in get_requires_for_build_sdist', exc_info=True) return False try: env.pip_install(reqs) log.info('Installed dynamic build dependencies') except CalledProcessError: log.error('Failed to install dynamic build dependencies') return False td = mkdtemp() log.info('Trying to build wheel in %s', td) try: try: filename = hooks.build_wheel(td, {}) log.info('build_wheel returned %r', filename) except Exception: log.info('Failure in build_wheel', exc_info=True) return False if not filename.endswith('.whl'): log.error("Filename %s doesn't have .whl extension", filename) return False path = pjoin(td, filename) if isfile(path): log.info("Output file %s exists", path) else: log.error("Output file %s does not exist", path) return False if zipfile.is_zipfile(path): log.info("Output file is a zip file") else: log.error("Output file is not a zip file") return False finally: shutil.rmtree(td) return True def check(source_dir): pyproject = pjoin(source_dir, 'pyproject.toml') if isfile(pyproject): log.info('Found pyproject.toml') else: log.error('Missing pyproject.toml') return False try: with io.open(pyproject, 'rb') as f: pyproject_data = toml_load(f) # Ensure the mandatory data can be loaded buildsys = pyproject_data['build-system'] requires = buildsys['requires'] backend = buildsys['build-backend'] backend_path = buildsys.get('backend-path') log.info('Loaded pyproject.toml') except (TOMLDecodeError, KeyError): log.error("Invalid pyproject.toml", exc_info=True) return False hooks = Pep517HookCaller(source_dir, backend, backend_path) sdist_ok = check_build_sdist(hooks, requires) wheel_ok = check_build_wheel(hooks, requires) if not sdist_ok: log.warning('Sdist checks failed; scroll up to see') if not wheel_ok: log.warning('Wheel checks failed') return sdist_ok def main(argv=None): log.warning('pep517.check is deprecated. ' 'Consider switching to https://pypi.org/project/build/') ap = argparse.ArgumentParser() ap.add_argument( 'source_dir', help="A directory containing pyproject.toml") args = ap.parse_args(argv) enable_colourful_output() ok = check(args.source_dir) if ok: print(ansi('Checks passed', 'green')) else: print(ansi('Checks failed', 'red')) sys.exit(1) ansi_codes = { 'reset': '\x1b[0m', 'bold': '\x1b[1m', 'red': '\x1b[31m', 'green': '\x1b[32m', } def ansi(s, attr): if os.name != 'nt' and sys.stdout.isatty(): return ansi_codes[attr] + str(s) + ansi_codes['reset'] else: return str(s) if __name__ == '__main__': main() ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/pep517/colorlog.py0000644000000000000000000001000200000000000012706 0ustar00"""Nicer log formatting with colours. Code copied from Tornado, Apache licensed. """ # Copyright 2012 Facebook # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging import sys try: import curses except ImportError: curses = None def _stderr_supports_color(): color = False if curses and hasattr(sys.stderr, 'isatty') and sys.stderr.isatty(): try: curses.setupterm() if curses.tigetnum("colors") > 0: color = True except Exception: pass return color class LogFormatter(logging.Formatter): """Log formatter with colour support """ DEFAULT_COLORS = { logging.INFO: 2, # Green logging.WARNING: 3, # Yellow logging.ERROR: 1, # Red logging.CRITICAL: 1, } def __init__(self, color=True, datefmt=None): r""" :arg bool color: Enables color support. :arg string fmt: Log message format. It will be applied to the attributes dict of log records. The text between ``%(color)s`` and ``%(end_color)s`` will be colored depending on the level if color support is on. :arg dict colors: color mappings from logging level to terminal color code :arg string datefmt: Datetime format. Used for formatting ``(asctime)`` placeholder in ``prefix_fmt``. .. versionchanged:: 3.2 Added ``fmt`` and ``datefmt`` arguments. """ logging.Formatter.__init__(self, datefmt=datefmt) self._colors = {} if color and _stderr_supports_color(): # The curses module has some str/bytes confusion in # python3. Until version 3.2.3, most methods return # bytes, but only accept strings. In addition, we want to # output these strings with the logging module, which # works with unicode strings. The explicit calls to # unicode() below are harmless in python2 but will do the # right conversion in python 3. fg_color = (curses.tigetstr("setaf") or curses.tigetstr("setf") or "") if (3, 0) < sys.version_info < (3, 2, 3): fg_color = str(fg_color, "ascii") for levelno, code in self.DEFAULT_COLORS.items(): self._colors[levelno] = str( curses.tparm(fg_color, code), "ascii") self._normal = str(curses.tigetstr("sgr0"), "ascii") scr = curses.initscr() self.termwidth = scr.getmaxyx()[1] curses.endwin() else: self._normal = '' # Default width is usually 80, but too wide is # worse than too narrow self.termwidth = 70 def formatMessage(self, record): mlen = len(record.message) right_text = '{initial}-{name}'.format(initial=record.levelname[0], name=record.name) if mlen + len(right_text) < self.termwidth: space = ' ' * (self.termwidth - (mlen + len(right_text))) else: space = ' ' if record.levelno in self._colors: start_color = self._colors[record.levelno] end_color = self._normal else: start_color = end_color = '' return record.message + space + start_color + right_text + end_color def enable_colourful_output(level=logging.INFO): handler = logging.StreamHandler() handler.setFormatter(LogFormatter()) logging.root.addHandler(handler) logging.root.setLevel(level) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/pep517/compat.py0000644000000000000000000000231500000000000012361 0ustar00"""Python 2/3 compatibility""" import io import json import sys # Handle reading and writing JSON in UTF-8, on Python 3 and 2. if sys.version_info[0] >= 3: # Python 3 def write_json(obj, path, **kwargs): with open(path, 'w', encoding='utf-8') as f: json.dump(obj, f, **kwargs) def read_json(path): with open(path, 'r', encoding='utf-8') as f: return json.load(f) else: # Python 2 def write_json(obj, path, **kwargs): with open(path, 'wb') as f: json.dump(obj, f, encoding='utf-8', **kwargs) def read_json(path): with open(path, 'rb') as f: return json.load(f) # FileNotFoundError try: FileNotFoundError = FileNotFoundError except NameError: FileNotFoundError = IOError if sys.version_info < (3, 6): from toml import load as _toml_load # noqa: F401 def toml_load(f): w = io.TextIOWrapper(f, encoding="utf8", newline="") try: return _toml_load(w) finally: w.detach() from toml import TomlDecodeError as TOMLDecodeError # noqa: F401 else: from tomli import load as toml_load # noqa: F401 from tomli import TOMLDecodeError # noqa: F401 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/pep517/dirtools.py0000644000000000000000000000215100000000000012733 0ustar00import os import io import contextlib import tempfile import shutil import errno import zipfile @contextlib.contextmanager def tempdir(): """Create a temporary directory in a context manager.""" td = tempfile.mkdtemp() try: yield td finally: shutil.rmtree(td) def mkdir_p(*args, **kwargs): """Like `mkdir`, but does not raise an exception if the directory already exists. """ try: return os.mkdir(*args, **kwargs) except OSError as exc: if exc.errno != errno.EEXIST: raise def dir_to_zipfile(root): """Construct an in-memory zip file for a directory.""" buffer = io.BytesIO() zip_file = zipfile.ZipFile(buffer, 'w') for root, dirs, files in os.walk(root): for path in dirs: fs_path = os.path.join(root, path) rel_path = os.path.relpath(fs_path, root) zip_file.writestr(rel_path + '/', '') for path in files: fs_path = os.path.join(root, path) rel_path = os.path.relpath(fs_path, root) zip_file.write(fs_path, rel_path) return zip_file ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/pep517/envbuild.py0000644000000000000000000001372400000000000012714 0ustar00"""Build wheels/sdists by installing build deps to a temporary environment. """ import io import os import logging import shutil from subprocess import check_call import sys from sysconfig import get_paths from tempfile import mkdtemp from .compat import toml_load from .wrappers import Pep517HookCaller, LoggerWrapper log = logging.getLogger(__name__) def _load_pyproject(source_dir): with io.open( os.path.join(source_dir, 'pyproject.toml'), 'rb', ) as f: pyproject_data = toml_load(f) buildsys = pyproject_data['build-system'] return ( buildsys['requires'], buildsys['build-backend'], buildsys.get('backend-path'), ) class BuildEnvironment(object): """Context manager to install build deps in a simple temporary environment Based on code I wrote for pip, which is MIT licensed. """ # Copyright (c) 2008-2016 The pip developers (see AUTHORS.txt file) # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. path = None def __init__(self, cleanup=True): self._cleanup = cleanup def __enter__(self): self.path = mkdtemp(prefix='pep517-build-env-') log.info('Temporary build environment: %s', self.path) self.save_path = os.environ.get('PATH', None) self.save_pythonpath = os.environ.get('PYTHONPATH', None) install_scheme = 'nt' if (os.name == 'nt') else 'posix_prefix' install_dirs = get_paths(install_scheme, vars={ 'base': self.path, 'platbase': self.path, }) scripts = install_dirs['scripts'] if self.save_path: os.environ['PATH'] = scripts + os.pathsep + self.save_path else: os.environ['PATH'] = scripts + os.pathsep + os.defpath if install_dirs['purelib'] == install_dirs['platlib']: lib_dirs = install_dirs['purelib'] else: lib_dirs = install_dirs['purelib'] + os.pathsep + \ install_dirs['platlib'] if self.save_pythonpath: os.environ['PYTHONPATH'] = lib_dirs + os.pathsep + \ self.save_pythonpath else: os.environ['PYTHONPATH'] = lib_dirs return self def pip_install(self, reqs): """Install dependencies into this env by calling pip in a subprocess""" if not reqs: return log.info('Calling pip to install %s', reqs) cmd = [ sys.executable, '-m', 'pip', 'install', '--ignore-installed', '--prefix', self.path] + list(reqs) check_call( cmd, stdout=LoggerWrapper(log, logging.INFO), stderr=LoggerWrapper(log, logging.ERROR), ) def __exit__(self, exc_type, exc_val, exc_tb): needs_cleanup = ( self._cleanup and self.path is not None and os.path.isdir(self.path) ) if needs_cleanup: shutil.rmtree(self.path) if self.save_path is None: os.environ.pop('PATH', None) else: os.environ['PATH'] = self.save_path if self.save_pythonpath is None: os.environ.pop('PYTHONPATH', None) else: os.environ['PYTHONPATH'] = self.save_pythonpath def build_wheel(source_dir, wheel_dir, config_settings=None): """Build a wheel from a source directory using PEP 517 hooks. :param str source_dir: Source directory containing pyproject.toml :param str wheel_dir: Target directory to create wheel in :param dict config_settings: Options to pass to build backend This is a blocking function which will run pip in a subprocess to install build requirements. """ if config_settings is None: config_settings = {} requires, backend, backend_path = _load_pyproject(source_dir) hooks = Pep517HookCaller(source_dir, backend, backend_path) with BuildEnvironment() as env: env.pip_install(requires) reqs = hooks.get_requires_for_build_wheel(config_settings) env.pip_install(reqs) return hooks.build_wheel(wheel_dir, config_settings) def build_sdist(source_dir, sdist_dir, config_settings=None): """Build an sdist from a source directory using PEP 517 hooks. :param str source_dir: Source directory containing pyproject.toml :param str sdist_dir: Target directory to place sdist in :param dict config_settings: Options to pass to build backend This is a blocking function which will run pip in a subprocess to install build requirements. """ if config_settings is None: config_settings = {} requires, backend, backend_path = _load_pyproject(source_dir) hooks = Pep517HookCaller(source_dir, backend, backend_path) with BuildEnvironment() as env: env.pip_install(requires) reqs = hooks.get_requires_for_build_sdist(config_settings) env.pip_install(reqs) return hooks.build_sdist(sdist_dir, config_settings) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/pep517/in_process/__init__.py0000644000000000000000000000106300000000000015000 0ustar00"""This is a subpackage because the directory is on sys.path for _in_process.py The subpackage should stay as empty as possible to avoid shadowing modules that the backend might import. """ from os.path import dirname, abspath, join as pjoin from contextlib import contextmanager try: import importlib.resources as resources def _in_proc_script_path(): return resources.path(__package__, '_in_process.py') except ImportError: @contextmanager def _in_proc_script_path(): yield pjoin(dirname(abspath(__file__)), '_in_process.py') ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/pep517/in_process/_in_process.py0000644000000000000000000002570100000000000015551 0ustar00"""This is invoked in a subprocess to call the build backend hooks. It expects: - Command line args: hook_name, control_dir - Environment variables: PEP517_BUILD_BACKEND=entry.point:spec PEP517_BACKEND_PATH=paths (separated with os.pathsep) - control_dir/input.json: - {"kwargs": {...}} Results: - control_dir/output.json - {"return_val": ...} """ from glob import glob from importlib import import_module import json import os import os.path from os.path import join as pjoin import re import shutil import sys import traceback # This file is run as a script, and `import compat` is not zip-safe, so we # include write_json() and read_json() from compat.py. # # Handle reading and writing JSON in UTF-8, on Python 3 and 2. if sys.version_info[0] >= 3: # Python 3 def write_json(obj, path, **kwargs): with open(path, 'w', encoding='utf-8') as f: json.dump(obj, f, **kwargs) def read_json(path): with open(path, 'r', encoding='utf-8') as f: return json.load(f) else: # Python 2 def write_json(obj, path, **kwargs): with open(path, 'wb') as f: json.dump(obj, f, encoding='utf-8', **kwargs) def read_json(path): with open(path, 'rb') as f: return json.load(f) class BackendUnavailable(Exception): """Raised if we cannot import the backend""" def __init__(self, traceback): self.traceback = traceback class BackendInvalid(Exception): """Raised if the backend is invalid""" def __init__(self, message): self.message = message class HookMissing(Exception): """Raised if a hook is missing and we are not executing the fallback""" def __init__(self, hook_name=None): super(HookMissing, self).__init__(hook_name) self.hook_name = hook_name def contained_in(filename, directory): """Test if a file is located within the given directory.""" filename = os.path.normcase(os.path.abspath(filename)) directory = os.path.normcase(os.path.abspath(directory)) return os.path.commonprefix([filename, directory]) == directory def _build_backend(): """Find and load the build backend""" # Add in-tree backend directories to the front of sys.path. backend_path = os.environ.get('PEP517_BACKEND_PATH') if backend_path: extra_pathitems = backend_path.split(os.pathsep) sys.path[:0] = extra_pathitems ep = os.environ['PEP517_BUILD_BACKEND'] mod_path, _, obj_path = ep.partition(':') try: obj = import_module(mod_path) except ImportError: raise BackendUnavailable(traceback.format_exc()) if backend_path: if not any( contained_in(obj.__file__, path) for path in extra_pathitems ): raise BackendInvalid("Backend was not loaded from backend-path") if obj_path: for path_part in obj_path.split('.'): obj = getattr(obj, path_part) return obj def _supported_features(): """Return the list of options features supported by the backend. Returns a list of strings. The only possible value is 'build_editable'. """ backend = _build_backend() features = [] if hasattr(backend, "build_editable"): features.append("build_editable") return features def get_requires_for_build_wheel(config_settings): """Invoke the optional get_requires_for_build_wheel hook Returns [] if the hook is not defined. """ backend = _build_backend() try: hook = backend.get_requires_for_build_wheel except AttributeError: return [] else: return hook(config_settings) def get_requires_for_build_editable(config_settings): """Invoke the optional get_requires_for_build_editable hook Returns [] if the hook is not defined. """ backend = _build_backend() try: hook = backend.get_requires_for_build_editable except AttributeError: return [] else: return hook(config_settings) def prepare_metadata_for_build_wheel( metadata_directory, config_settings, _allow_fallback): """Invoke optional prepare_metadata_for_build_wheel Implements a fallback by building a wheel if the hook isn't defined, unless _allow_fallback is False in which case HookMissing is raised. """ backend = _build_backend() try: hook = backend.prepare_metadata_for_build_wheel except AttributeError: if not _allow_fallback: raise HookMissing() whl_basename = backend.build_wheel(metadata_directory, config_settings) return _get_wheel_metadata_from_wheel(whl_basename, metadata_directory, config_settings) else: return hook(metadata_directory, config_settings) def prepare_metadata_for_build_editable( metadata_directory, config_settings, _allow_fallback): """Invoke optional prepare_metadata_for_build_editable Implements a fallback by building an editable wheel if the hook isn't defined, unless _allow_fallback is False in which case HookMissing is raised. """ backend = _build_backend() try: hook = backend.prepare_metadata_for_build_editable except AttributeError: if not _allow_fallback: raise HookMissing() try: build_hook = backend.build_editable except AttributeError: raise HookMissing(hook_name='build_editable') else: whl_basename = build_hook(metadata_directory, config_settings) return _get_wheel_metadata_from_wheel(whl_basename, metadata_directory, config_settings) else: return hook(metadata_directory, config_settings) WHEEL_BUILT_MARKER = 'PEP517_ALREADY_BUILT_WHEEL' def _dist_info_files(whl_zip): """Identify the .dist-info folder inside a wheel ZipFile.""" res = [] for path in whl_zip.namelist(): m = re.match(r'[^/\\]+-[^/\\]+\.dist-info/', path) if m: res.append(path) if res: return res raise Exception("No .dist-info folder found in wheel") def _get_wheel_metadata_from_wheel( whl_basename, metadata_directory, config_settings): """Extract the metadata from a wheel. Fallback for when the build backend does not define the 'get_wheel_metadata' hook. """ from zipfile import ZipFile with open(os.path.join(metadata_directory, WHEEL_BUILT_MARKER), 'wb'): pass # Touch marker file whl_file = os.path.join(metadata_directory, whl_basename) with ZipFile(whl_file) as zipf: dist_info = _dist_info_files(zipf) zipf.extractall(path=metadata_directory, members=dist_info) return dist_info[0].split('/')[0] def _find_already_built_wheel(metadata_directory): """Check for a wheel already built during the get_wheel_metadata hook. """ if not metadata_directory: return None metadata_parent = os.path.dirname(metadata_directory) if not os.path.isfile(pjoin(metadata_parent, WHEEL_BUILT_MARKER)): return None whl_files = glob(os.path.join(metadata_parent, '*.whl')) if not whl_files: print('Found wheel built marker, but no .whl files') return None if len(whl_files) > 1: print('Found multiple .whl files; unspecified behaviour. ' 'Will call build_wheel.') return None # Exactly one .whl file return whl_files[0] def build_wheel(wheel_directory, config_settings, metadata_directory=None): """Invoke the mandatory build_wheel hook. If a wheel was already built in the prepare_metadata_for_build_wheel fallback, this will copy it rather than rebuilding the wheel. """ prebuilt_whl = _find_already_built_wheel(metadata_directory) if prebuilt_whl: shutil.copy2(prebuilt_whl, wheel_directory) return os.path.basename(prebuilt_whl) return _build_backend().build_wheel(wheel_directory, config_settings, metadata_directory) def build_editable(wheel_directory, config_settings, metadata_directory=None): """Invoke the optional build_editable hook. If a wheel was already built in the prepare_metadata_for_build_editable fallback, this will copy it rather than rebuilding the wheel. """ backend = _build_backend() try: hook = backend.build_editable except AttributeError: raise HookMissing() else: prebuilt_whl = _find_already_built_wheel(metadata_directory) if prebuilt_whl: shutil.copy2(prebuilt_whl, wheel_directory) return os.path.basename(prebuilt_whl) return hook(wheel_directory, config_settings, metadata_directory) def get_requires_for_build_sdist(config_settings): """Invoke the optional get_requires_for_build_wheel hook Returns [] if the hook is not defined. """ backend = _build_backend() try: hook = backend.get_requires_for_build_sdist except AttributeError: return [] else: return hook(config_settings) class _DummyException(Exception): """Nothing should ever raise this exception""" class GotUnsupportedOperation(Exception): """For internal use when backend raises UnsupportedOperation""" def __init__(self, traceback): self.traceback = traceback def build_sdist(sdist_directory, config_settings): """Invoke the mandatory build_sdist hook.""" backend = _build_backend() try: return backend.build_sdist(sdist_directory, config_settings) except getattr(backend, 'UnsupportedOperation', _DummyException): raise GotUnsupportedOperation(traceback.format_exc()) HOOK_NAMES = { 'get_requires_for_build_wheel', 'prepare_metadata_for_build_wheel', 'build_wheel', 'get_requires_for_build_editable', 'prepare_metadata_for_build_editable', 'build_editable', 'get_requires_for_build_sdist', 'build_sdist', '_supported_features', } def main(): if len(sys.argv) < 3: sys.exit("Needs args: hook_name, control_dir") hook_name = sys.argv[1] control_dir = sys.argv[2] if hook_name not in HOOK_NAMES: sys.exit("Unknown hook: %s" % hook_name) hook = globals()[hook_name] hook_input = read_json(pjoin(control_dir, 'input.json')) json_out = {'unsupported': False, 'return_val': None} try: json_out['return_val'] = hook(**hook_input['kwargs']) except BackendUnavailable as e: json_out['no_backend'] = True json_out['traceback'] = e.traceback except BackendInvalid as e: json_out['backend_invalid'] = True json_out['backend_error'] = e.message except GotUnsupportedOperation as e: json_out['unsupported'] = True json_out['traceback'] = e.traceback except HookMissing as e: json_out['hook_missing'] = True json_out['missing_hook_name'] = e.hook_name or hook_name write_json(json_out, pjoin(control_dir, 'output.json'), indent=2) if __name__ == '__main__': main() ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/pep517/meta.py0000644000000000000000000000463700000000000012035 0ustar00"""Build metadata for a project using PEP 517 hooks. """ import argparse import logging import os import shutil import functools try: import importlib.metadata as imp_meta except ImportError: import importlib_metadata as imp_meta try: from zipfile import Path except ImportError: from zipp import Path from .envbuild import BuildEnvironment from .wrappers import Pep517HookCaller, quiet_subprocess_runner from .dirtools import tempdir, mkdir_p, dir_to_zipfile from .build import validate_system, load_system, compat_system log = logging.getLogger(__name__) def _prep_meta(hooks, env, dest): reqs = hooks.get_requires_for_build_wheel({}) log.info('Got build requires: %s', reqs) env.pip_install(reqs) log.info('Installed dynamic build dependencies') with tempdir() as td: log.info('Trying to build metadata in %s', td) filename = hooks.prepare_metadata_for_build_wheel(td, {}) source = os.path.join(td, filename) shutil.move(source, os.path.join(dest, os.path.basename(filename))) def build(source_dir='.', dest=None, system=None): system = system or load_system(source_dir) dest = os.path.join(source_dir, dest or 'dist') mkdir_p(dest) validate_system(system) hooks = Pep517HookCaller( source_dir, system['build-backend'], system.get('backend-path') ) with hooks.subprocess_runner(quiet_subprocess_runner): with BuildEnvironment() as env: env.pip_install(system['requires']) _prep_meta(hooks, env, dest) def build_as_zip(builder=build): with tempdir() as out_dir: builder(dest=out_dir) return dir_to_zipfile(out_dir) def load(root): """ Given a source directory (root) of a package, return an importlib.metadata.Distribution object with metadata build from that package. """ root = os.path.expanduser(root) system = compat_system(root) builder = functools.partial(build, source_dir=root, system=system) path = Path(build_as_zip(builder)) return imp_meta.PathDistribution(path) parser = argparse.ArgumentParser() parser.add_argument( 'source_dir', help="A directory containing pyproject.toml", ) parser.add_argument( '--out-dir', '-o', help="Destination in which to save the builds relative to source dir", ) def main(): args = parser.parse_args() build(args.source_dir, args.out_dir) if __name__ == '__main__': main() ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/pep517/wrappers.py0000644000000000000000000003216500000000000012747 0ustar00import threading from contextlib import contextmanager import os from os.path import abspath, join as pjoin import shutil from subprocess import check_call, check_output, STDOUT import sys from tempfile import mkdtemp from . import compat from .in_process import _in_proc_script_path __all__ = [ 'BackendUnavailable', 'BackendInvalid', 'HookMissing', 'UnsupportedOperation', 'default_subprocess_runner', 'quiet_subprocess_runner', 'Pep517HookCaller', ] @contextmanager def tempdir(): td = mkdtemp() try: yield td finally: shutil.rmtree(td) class BackendUnavailable(Exception): """Will be raised if the backend cannot be imported in the hook process.""" def __init__(self, traceback): self.traceback = traceback class BackendInvalid(Exception): """Will be raised if the backend is invalid.""" def __init__(self, backend_name, backend_path, message): self.backend_name = backend_name self.backend_path = backend_path self.message = message class HookMissing(Exception): """Will be raised on missing hooks.""" def __init__(self, hook_name): super(HookMissing, self).__init__(hook_name) self.hook_name = hook_name class UnsupportedOperation(Exception): """May be raised by build_sdist if the backend indicates that it can't.""" def __init__(self, traceback): self.traceback = traceback def default_subprocess_runner(cmd, cwd=None, extra_environ=None): """The default method of calling the wrapper subprocess.""" env = os.environ.copy() if extra_environ: env.update(extra_environ) check_call(cmd, cwd=cwd, env=env) def quiet_subprocess_runner(cmd, cwd=None, extra_environ=None): """A method of calling the wrapper subprocess while suppressing output.""" env = os.environ.copy() if extra_environ: env.update(extra_environ) check_output(cmd, cwd=cwd, env=env, stderr=STDOUT) def norm_and_check(source_tree, requested): """Normalise and check a backend path. Ensure that the requested backend path is specified as a relative path, and resolves to a location under the given source tree. Return an absolute version of the requested path. """ if os.path.isabs(requested): raise ValueError("paths must be relative") abs_source = os.path.abspath(source_tree) abs_requested = os.path.normpath(os.path.join(abs_source, requested)) # We have to use commonprefix for Python 2.7 compatibility. So we # normalise case to avoid problems because commonprefix is a character # based comparison :-( norm_source = os.path.normcase(abs_source) norm_requested = os.path.normcase(abs_requested) if os.path.commonprefix([norm_source, norm_requested]) != norm_source: raise ValueError("paths must be inside source tree") return abs_requested class Pep517HookCaller(object): """A wrapper around a source directory to be built with a PEP 517 backend. :param source_dir: The path to the source directory, containing pyproject.toml. :param build_backend: The build backend spec, as per PEP 517, from pyproject.toml. :param backend_path: The backend path, as per PEP 517, from pyproject.toml. :param runner: A callable that invokes the wrapper subprocess. :param python_executable: The Python executable used to invoke the backend The 'runner', if provided, must expect the following: - cmd: a list of strings representing the command and arguments to execute, as would be passed to e.g. 'subprocess.check_call'. - cwd: a string representing the working directory that must be used for the subprocess. Corresponds to the provided source_dir. - extra_environ: a dict mapping environment variable names to values which must be set for the subprocess execution. """ def __init__( self, source_dir, build_backend, backend_path=None, runner=None, python_executable=None, ): if runner is None: runner = default_subprocess_runner self.source_dir = abspath(source_dir) self.build_backend = build_backend if backend_path: backend_path = [ norm_and_check(self.source_dir, p) for p in backend_path ] self.backend_path = backend_path self._subprocess_runner = runner if not python_executable: python_executable = sys.executable self.python_executable = python_executable @contextmanager def subprocess_runner(self, runner): """A context manager for temporarily overriding the default subprocess runner. """ prev = self._subprocess_runner self._subprocess_runner = runner try: yield finally: self._subprocess_runner = prev def _supported_features(self): """Return the list of optional features supported by the backend.""" return self._call_hook('_supported_features', {}) def get_requires_for_build_wheel(self, config_settings=None): """Identify packages required for building a wheel Returns a list of dependency specifications, e.g.:: ["wheel >= 0.25", "setuptools"] This does not include requirements specified in pyproject.toml. It returns the result of calling the equivalently named hook in a subprocess. """ return self._call_hook('get_requires_for_build_wheel', { 'config_settings': config_settings }) def prepare_metadata_for_build_wheel( self, metadata_directory, config_settings=None, _allow_fallback=True): """Prepare a ``*.dist-info`` folder with metadata for this project. Returns the name of the newly created folder. If the build backend defines a hook with this name, it will be called in a subprocess. If not, the backend will be asked to build a wheel, and the dist-info extracted from that (unless _allow_fallback is False). """ return self._call_hook('prepare_metadata_for_build_wheel', { 'metadata_directory': abspath(metadata_directory), 'config_settings': config_settings, '_allow_fallback': _allow_fallback, }) def build_wheel( self, wheel_directory, config_settings=None, metadata_directory=None): """Build a wheel from this project. Returns the name of the newly created file. In general, this will call the 'build_wheel' hook in the backend. However, if that was previously called by 'prepare_metadata_for_build_wheel', and the same metadata_directory is used, the previously built wheel will be copied to wheel_directory. """ if metadata_directory is not None: metadata_directory = abspath(metadata_directory) return self._call_hook('build_wheel', { 'wheel_directory': abspath(wheel_directory), 'config_settings': config_settings, 'metadata_directory': metadata_directory, }) def get_requires_for_build_editable(self, config_settings=None): """Identify packages required for building an editable wheel Returns a list of dependency specifications, e.g.:: ["wheel >= 0.25", "setuptools"] This does not include requirements specified in pyproject.toml. It returns the result of calling the equivalently named hook in a subprocess. """ return self._call_hook('get_requires_for_build_editable', { 'config_settings': config_settings }) def prepare_metadata_for_build_editable( self, metadata_directory, config_settings=None, _allow_fallback=True): """Prepare a ``*.dist-info`` folder with metadata for this project. Returns the name of the newly created folder. If the build backend defines a hook with this name, it will be called in a subprocess. If not, the backend will be asked to build an editable wheel, and the dist-info extracted from that (unless _allow_fallback is False). """ return self._call_hook('prepare_metadata_for_build_editable', { 'metadata_directory': abspath(metadata_directory), 'config_settings': config_settings, '_allow_fallback': _allow_fallback, }) def build_editable( self, wheel_directory, config_settings=None, metadata_directory=None): """Build an editable wheel from this project. Returns the name of the newly created file. In general, this will call the 'build_editable' hook in the backend. However, if that was previously called by 'prepare_metadata_for_build_editable', and the same metadata_directory is used, the previously built wheel will be copied to wheel_directory. """ if metadata_directory is not None: metadata_directory = abspath(metadata_directory) return self._call_hook('build_editable', { 'wheel_directory': abspath(wheel_directory), 'config_settings': config_settings, 'metadata_directory': metadata_directory, }) def get_requires_for_build_sdist(self, config_settings=None): """Identify packages required for building a wheel Returns a list of dependency specifications, e.g.:: ["setuptools >= 26"] This does not include requirements specified in pyproject.toml. It returns the result of calling the equivalently named hook in a subprocess. """ return self._call_hook('get_requires_for_build_sdist', { 'config_settings': config_settings }) def build_sdist(self, sdist_directory, config_settings=None): """Build an sdist from this project. Returns the name of the newly created file. This calls the 'build_sdist' backend hook in a subprocess. """ return self._call_hook('build_sdist', { 'sdist_directory': abspath(sdist_directory), 'config_settings': config_settings, }) def _call_hook(self, hook_name, kwargs): # On Python 2, pytoml returns Unicode values (which is correct) but the # environment passed to check_call needs to contain string values. We # convert here by encoding using ASCII (the backend can only contain # letters, digits and _, . and : characters, and will be used as a # Python identifier, so non-ASCII content is wrong on Python 2 in # any case). # For backend_path, we use sys.getfilesystemencoding. if sys.version_info[0] == 2: build_backend = self.build_backend.encode('ASCII') else: build_backend = self.build_backend extra_environ = {'PEP517_BUILD_BACKEND': build_backend} if self.backend_path: backend_path = os.pathsep.join(self.backend_path) if sys.version_info[0] == 2: backend_path = backend_path.encode(sys.getfilesystemencoding()) extra_environ['PEP517_BACKEND_PATH'] = backend_path with tempdir() as td: hook_input = {'kwargs': kwargs} compat.write_json(hook_input, pjoin(td, 'input.json'), indent=2) # Run the hook in a subprocess with _in_proc_script_path() as script: python = self.python_executable self._subprocess_runner( [python, abspath(str(script)), hook_name, td], cwd=self.source_dir, extra_environ=extra_environ ) data = compat.read_json(pjoin(td, 'output.json')) if data.get('unsupported'): raise UnsupportedOperation(data.get('traceback', '')) if data.get('no_backend'): raise BackendUnavailable(data.get('traceback', '')) if data.get('backend_invalid'): raise BackendInvalid( backend_name=self.build_backend, backend_path=self.backend_path, message=data.get('backend_error', '') ) if data.get('hook_missing'): raise HookMissing(data.get('missing_hook_name') or hook_name) return data['return_val'] class LoggerWrapper(threading.Thread): """ Read messages from a pipe and redirect them to a logger (see python's logging module). """ def __init__(self, logger, level): threading.Thread.__init__(self) self.daemon = True self.logger = logger self.level = level # create the pipe and reader self.fd_read, self.fd_write = os.pipe() self.reader = os.fdopen(self.fd_read) self.start() def fileno(self): return self.fd_write @staticmethod def remove_newline(msg): return msg[:-1] if msg.endswith(os.linesep) else msg def run(self): for line in self.reader: self._write(self.remove_newline(line)) def _write(self, message): self.logger.log(self.level, message) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/pyproject.toml0000644000000000000000000000111400000000000012413 0ustar00[build-system] requires = ["flit_core >=2,<4"] build-backend = "flit_core.buildapi" [tool.flit.metadata] module = "pep517" author = "Thomas Kluyver" author-email = "thomas@kluyver.me.uk" home-page = "https://github.com/pypa/pep517" description-file = "README.rst" requires = [ "toml;python_version<'3.6'", "tomli >=1.1.0;python_version>='3.6'", "importlib_metadata;python_version<'3.8'", "zipp;python_version<'3.8'", ] classifiers = [ "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", ] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/pytest.ini0000644000000000000000000000037400000000000011537 0ustar00[pytest] addopts = --strict-config --strict-markers --flake8 xfail_strict = True junit_family = xunit2 filterwarnings = error # Suppress deprecation warning in flake8 ignore:SelectableGroups dict interface is deprecated::flake8 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/tests/__init__.py0000644000000000000000000000000000000000000012743 0ustar00././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/tests/samples/buildsys_pkgs/buildsys.py0000644000000000000000000000363200000000000017406 0ustar00"""This is a very stupid backend for testing purposes. Don't use this for any real code. """ from glob import glob from os.path import join as pjoin import shutil import tarfile from zipfile import ZipFile def get_requires_for_build_wheel(config_settings): return ['wheelwright'] def get_requires_for_build_editable(config_settings): return ['wheelwright', 'editables'] def prepare_metadata_for_build_wheel(metadata_directory, config_settings): for distinfo in glob('*.dist-info'): shutil.copytree(distinfo, pjoin(metadata_directory, distinfo)) prepare_metadata_for_build_editable = prepare_metadata_for_build_wheel def prepare_build_wheel_files(build_directory, config_settings): shutil.copy('pyproject.toml', build_directory) for pyfile in glob('*.py'): shutil.copy(pyfile, build_directory) for distinfo in glob('*.dist-info'): shutil.copytree(distinfo, pjoin(build_directory, distinfo)) def build_wheel(wheel_directory, config_settings, metadata_directory=None): whl_file = 'pkg1-0.5-py2.py3-none-any.whl' with ZipFile(pjoin(wheel_directory, whl_file), 'w') as zf: for pyfile in glob('*.py'): zf.write(pyfile) for metadata in glob('*.dist-info/*'): zf.write(metadata) return whl_file build_editable = build_wheel def get_requires_for_build_sdist(config_settings): return ['frog'] class UnsupportedOperation(Exception): pass def build_sdist(sdist_directory, config_settings): if config_settings.get('test_unsupported', False): raise UnsupportedOperation target = 'pkg1-0.5.tar.gz' with tarfile.open(pjoin(sdist_directory, target), 'w:gz', format=tarfile.PAX_FORMAT) as tf: def _add(relpath): tf.add(relpath, arcname='pkg1-0.5/' + relpath) _add('pyproject.toml') for pyfile in glob('*.py'): _add(pyfile) return target ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/tests/samples/buildsys_pkgs/buildsys_minimal.py0000644000000000000000000000176400000000000021120 0ustar00"""Test backend defining only the mandatory hooks. Don't use this for any real code. """ from glob import glob from os.path import join as pjoin import tarfile from zipfile import ZipFile def build_wheel(wheel_directory, config_settings, metadata_directory=None): whl_file = 'pkg2-0.5-py2.py3-none-any.whl' with ZipFile(pjoin(wheel_directory, whl_file), 'w') as zf: for pyfile in glob('*.py'): zf.write(pyfile) for metadata in glob('*.dist-info/*'): zf.write(metadata) return whl_file def build_sdist(sdist_directory, config_settings): target = 'pkg2-0.5.tar.gz' with tarfile.open(pjoin(sdist_directory, target), 'w:gz', format=tarfile.PAX_FORMAT) as tf: def _add(relpath): tf.add(relpath, arcname='pkg2-0.5/' + relpath) _add('pyproject.toml') for pyfile in glob('*.py'): _add(pyfile) for distinfo in glob('*.dist-info'): _add(distinfo) return target ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/tests/samples/buildsys_pkgs/buildsys_minimal_editable.py0000644000000000000000000000013500000000000022740 0ustar00from buildsys_minimal import build_sdist, build_wheel # noqa build_editable = build_wheel ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/tests/samples/pkg1/pkg1-0.5.dist-info/LICENSE0000644000000000000000000000207100000000000017214 0ustar00The MIT License (MIT) Copyright (c) 2017 Thomas Kluyver Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/tests/samples/pkg1/pkg1-0.5.dist-info/METADATA0000644000000000000000000000037500000000000017317 0ustar00Metadata-Version: 1.2 Name: pkg1 Version: 0.5 Summary: Sample package for tests Home-page: https://github.com/takluyver/pep517 License: UNKNOWN Author: Thomas Kluyver Author-email: thomas@kluyver.me.uk Classifier: License :: OSI Approved :: MIT License ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/tests/samples/pkg1/pkg1-0.5.dist-info/RECORD0000644000000000000000000000052000000000000017105 0ustar00pkg1.py,sha256=ZawKBtrxtdGEheOCWvwzGZsO8Q1OSzEzecGNsRz-ekc,52 pkg1-0.5.dist-info/LICENSE,sha256=GyKwSbUmfW38I6Z79KhNjsBLn9-xpR02DkK0NCyLQVQ,1081 pkg1-0.5.dist-info/WHEEL,sha256=jxKvNaDKHDacpaLi69-vnLKkBSynwBzmMS82pipt1T0,100 pkg1-0.5.dist-info/METADATA,sha256=GDliGDwDPM11hoO79KhjyJuFgcm-TOj30gewsPNjkHw,251 pkg1-0.5.dist-info/RECORD,, ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/tests/samples/pkg1/pkg1-0.5.dist-info/WHEEL0000644000000000000000000000014500000000000016776 0ustar00Wheel-Version: 1.0 Generator: buildsys 0.1 Root-Is-Purelib: true Tag: py2-none-any Tag: py3-none-any ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/tests/samples/pkg1/pkg1.py0000644000000000000000000000006400000000000014366 0ustar00"""Sample package for tests""" __version__ = '0.5' ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2533517 pep517-0.12.0/tests/samples/pkg1/pyproject.toml0000644000000000000000000000025300000000000016066 0ustar00[build-system] requires = ["eg_buildsys"] build-backend = "buildsys" [project] description = "Factory ⸻ A code generator 🏭" maintainers = [{name = "Łukasz Langa"}] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/samples/pkg2/pkg2-0.5.dist-info/LICENSE0000644000000000000000000000207100000000000017216 0ustar00The MIT License (MIT) Copyright (c) 2017 Thomas Kluyver Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/samples/pkg2/pkg2-0.5.dist-info/METADATA0000644000000000000000000000037300000000000017317 0ustar00Metadata-Version: 1.2 Name: pkg2 Version: 0.5 Summary: Sample package for tests Home-page: https://github.com/takluyver/pkg2 License: UNKNOWN Author: Thomas Kluyver Author-email: thomas@kluyver.me.uk Classifier: License :: OSI Approved :: MIT License ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/samples/pkg2/pkg2-0.5.dist-info/RECORD0000644000000000000000000000052000000000000017107 0ustar00pkg2.py,sha256=ZawKBtrxtdGEheOCWvwzGZsO8Q1OSzEzecGNsRz-ekc,52 pkg2-0.5.dist-info/LICENSE,sha256=GyKwSbUmfW38I6Z79KhNjsBLn9-xpR02DkK0NCyLQVQ,1081 pkg2-0.5.dist-info/WHEEL,sha256=jxKvNaDKHDacpaLi69-vnLKkBSynwBzmMS82pipt1T0,100 pkg2-0.5.dist-info/METADATA,sha256=4zQxJqc4Rvnlf5Y-seXnRx8g-1FK-sjTuS0A1KP0ajk,251 pkg2-0.5.dist-info/RECORD,, ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/samples/pkg2/pkg2-0.5.dist-info/WHEEL0000644000000000000000000000014400000000000016777 0ustar00Wheel-Version: 1.0 Generator: flit 0.11.3 Root-Is-Purelib: true Tag: py2-none-any Tag: py3-none-any ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/samples/pkg2/pkg2.py0000644000000000000000000000006400000000000014370 0ustar00"""Sample package for tests""" __version__ = '0.5' ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/samples/pkg2/pyproject.toml0000644000000000000000000000010000000000000016056 0ustar00[build-system] requires = [] build-backend = "buildsys_minimal" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/samples/pkg3/pkg3-0.5.dist-info/LICENSE0000644000000000000000000000207100000000000017220 0ustar00The MIT License (MIT) Copyright (c) 2017 Thomas Kluyver Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/samples/pkg3/pkg3-0.5.dist-info/METADATA0000644000000000000000000000037300000000000017321 0ustar00Metadata-Version: 1.2 Name: pkg3 Version: 0.5 Summary: Sample package for tests Home-page: https://github.com/takluyver/pkg3 License: UNKNOWN Author: Thomas Kluyver Author-email: thomas@kluyver.me.uk Classifier: License :: OSI Approved :: MIT License ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/samples/pkg3/pkg3-0.5.dist-info/RECORD0000644000000000000000000000052000000000000017111 0ustar00pkg3.py,sha256=ZawKBtrxtdGEheOCWvwzGZsO8Q1OSzEzecGNsRz-ekc,52 pkg3-0.5.dist-info/LICENSE,sha256=GyKwSbUmfW38I6Z79KhNjsBLn9-xpR02DkK0NCyLQVQ,1081 pkg3-0.5.dist-info/WHEEL,sha256=jxKvNaDKHDacpaLi69-vnLKkBSynwBzmMS82pipt1T0,100 pkg3-0.5.dist-info/METADATA,sha256=4zQxJqc4Rvnlf5Y-seXnRx8g-1FK-sjTuS0A1KP0ajk,251 pkg3-0.5.dist-info/RECORD,, ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/samples/pkg3/pkg3-0.5.dist-info/WHEEL0000644000000000000000000000014400000000000017001 0ustar00Wheel-Version: 1.0 Generator: flit 0.11.3 Root-Is-Purelib: true Tag: py2-none-any Tag: py3-none-any ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/samples/pkg3/pkg3.py0000644000000000000000000000006400000000000014372 0ustar00"""Sample package for tests""" __version__ = '0.5' ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/samples/pkg3/pyproject.toml0000644000000000000000000000011100000000000016061 0ustar00[build-system] requires = [] build-backend = "buildsys_minimal_editable" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/samples/pkg_intree/backend/intree_backend.py0000644000000000000000000000013000000000000021327 0ustar00def get_requires_for_build_sdist(config_settings): return ["intree_backend_called"] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/samples/pkg_intree/pyproject.toml0000644000000000000000000000011200000000000017345 0ustar00[build-system] build-backend = 'intree_backend' backend-path = ['backend']././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/samples/setup-py/pyproject.toml0000644000000000000000000000013200000000000017006 0ustar00[build-system] requires = ["setuptools", "wheel"] build-backend = "setuptools.build_meta" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/samples/setup-py/setup.py0000644000000000000000000000014200000000000015605 0ustar00from setuptools import setup setup( name='pep517-test-setup-py-support', version='1.0' ) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/samples/test-for-issue-104/pyproject.toml0000644000000000000000000000013200000000000020413 0ustar00[build-system] requires = ["setuptools", "wheel"] build-backend = "setuptools.build_meta" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/samples/test-for-issue-104/setup.py0000644000000000000000000000037000000000000017215 0ustar00import sys from setuptools import setup from os import path, environ, listdir import json children = listdir(sys.path[0]) out = path.join(environ['PEP517_ISSUE104_OUTDIR'], 'out.json') with open(out, 'w') as f: json.dump(children, f) setup() ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/test_build.py0000644000000000000000000000150500000000000013355 0ustar00import pytest from pep517 import build def system(*args): return dict.fromkeys(args) class TestValidateSystem: def test_missing(self): with pytest.raises(ValueError): build.validate_system(system()) with pytest.raises(ValueError): build.validate_system(system('requires')) with pytest.raises(ValueError): build.validate_system(system('build-backend')) with pytest.raises(ValueError): build.validate_system(system('other')) def test_missing_and_extra(self): with pytest.raises(ValueError): build.validate_system(system('build-backend', 'other')) def test_satisfied(self): build.validate_system(system('build-backend', 'requires')) build.validate_system(system('build-backend', 'requires', 'other')) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/test_call_hooks.py0000644000000000000000000001556300000000000014405 0ustar00import io import os from os.path import dirname, abspath, join as pjoin import tarfile from testpath import modified_env, assert_isfile from testpath.tempdir import TemporaryDirectory, TemporaryWorkingDirectory import pytest import zipfile import sys import json try: from mock import Mock # Prefer the backport below python 3.6 except ImportError: from unittest.mock import Mock from pep517.compat import toml_load from pep517.wrappers import Pep517HookCaller, default_subprocess_runner from pep517.wrappers import UnsupportedOperation, BackendUnavailable if sys.version_info[0] == 2: FileNotFoundError = IOError SAMPLES_DIR = pjoin(dirname(abspath(__file__)), 'samples') BUILDSYS_PKGS = pjoin(SAMPLES_DIR, 'buildsys_pkgs') def get_hooks(pkg, **kwargs): source_dir = pjoin(SAMPLES_DIR, pkg) with io.open(pjoin(source_dir, 'pyproject.toml'), 'rb') as f: data = toml_load(f) return Pep517HookCaller( source_dir, data['build-system']['build-backend'], **kwargs ) def test_missing_backend_gives_exception(): hooks = get_hooks('pkg1') with modified_env({'PYTHONPATH': ''}): with pytest.raises(BackendUnavailable): hooks.get_requires_for_build_wheel({}) def test_get_requires_for_build_wheel(): hooks = get_hooks('pkg1') with modified_env({'PYTHONPATH': BUILDSYS_PKGS}): res = hooks.get_requires_for_build_wheel({}) assert res == ['wheelwright'] def test_get_requires_for_build_editable(): hooks = get_hooks('pkg1') with modified_env({'PYTHONPATH': BUILDSYS_PKGS}): res = hooks.get_requires_for_build_editable({}) assert res == ['wheelwright', 'editables'] def test_get_requires_for_build_sdist(): hooks = get_hooks('pkg1') with modified_env({'PYTHONPATH': BUILDSYS_PKGS}): res = hooks.get_requires_for_build_sdist({}) assert res == ['frog'] def test_prepare_metadata_for_build_wheel(): hooks = get_hooks('pkg1') with TemporaryDirectory() as metadatadir: with modified_env({'PYTHONPATH': BUILDSYS_PKGS}): hooks.prepare_metadata_for_build_wheel(metadatadir, {}) assert_isfile(pjoin(metadatadir, 'pkg1-0.5.dist-info', 'METADATA')) def test_prepare_metadata_for_build_editable(): hooks = get_hooks('pkg1') with TemporaryDirectory() as metadatadir: with modified_env({'PYTHONPATH': BUILDSYS_PKGS}): hooks.prepare_metadata_for_build_editable(metadatadir, {}) assert_isfile(pjoin(metadatadir, 'pkg1-0.5.dist-info', 'METADATA')) def test_build_wheel(): hooks = get_hooks('pkg1') with TemporaryDirectory() as builddir: with modified_env({'PYTHONPATH': BUILDSYS_PKGS}): whl_file = hooks.build_wheel(builddir, {}) assert whl_file.endswith('.whl') assert os.sep not in whl_file whl_file = pjoin(builddir, whl_file) assert_isfile(whl_file) assert zipfile.is_zipfile(whl_file) def test_build_editable(): hooks = get_hooks('pkg1') with TemporaryDirectory() as builddir: with modified_env({'PYTHONPATH': BUILDSYS_PKGS}): whl_file = hooks.build_editable(builddir, {}) assert whl_file.endswith('.whl') assert os.sep not in whl_file whl_file = pjoin(builddir, whl_file) assert_isfile(whl_file) assert zipfile.is_zipfile(whl_file) def test_build_wheel_relpath(): hooks = get_hooks('pkg1') with TemporaryWorkingDirectory() as builddir: with modified_env({'PYTHONPATH': BUILDSYS_PKGS}): whl_file = hooks.build_wheel('.', {}) assert whl_file.endswith('.whl') assert os.sep not in whl_file whl_file = pjoin(builddir, whl_file) assert_isfile(whl_file) assert zipfile.is_zipfile(whl_file) def test_build_sdist(): hooks = get_hooks('pkg1') with TemporaryDirectory() as sdistdir: with modified_env({'PYTHONPATH': BUILDSYS_PKGS}): sdist = hooks.build_sdist(sdistdir, {}) assert sdist.endswith('.tar.gz') assert os.sep not in sdist sdist = pjoin(sdistdir, sdist) assert_isfile(sdist) assert tarfile.is_tarfile(sdist) with tarfile.open(sdist) as tf: contents = tf.getnames() assert 'pkg1-0.5/pyproject.toml' in contents def test_build_sdist_unsupported(): hooks = get_hooks('pkg1') with TemporaryDirectory() as sdistdir: with modified_env({'PYTHONPATH': BUILDSYS_PKGS}): with pytest.raises(UnsupportedOperation): hooks.build_sdist(sdistdir, {'test_unsupported': True}) def test_runner_replaced_on_exception(monkeypatch): monkeypatch.setenv('PYTHONPATH', BUILDSYS_PKGS) runner = Mock(wraps=default_subprocess_runner) hooks = get_hooks('pkg1', runner=runner) hooks.get_requires_for_build_wheel() runner.assert_called_once() runner.reset_mock() runner2 = Mock(wraps=default_subprocess_runner) try: with hooks.subprocess_runner(runner2): hooks.get_requires_for_build_wheel() runner2.assert_called_once() runner2.reset_mock() raise RuntimeError() except RuntimeError: pass hooks.get_requires_for_build_wheel() runner.assert_called_once() def test_custom_python_executable(monkeypatch, tmpdir): monkeypatch.setenv('PYTHONPATH', BUILDSYS_PKGS) runner = Mock(autospec=default_subprocess_runner) hooks = get_hooks('pkg1', runner=runner, python_executable='some-python') with hooks.subprocess_runner(runner): with pytest.raises(FileNotFoundError): # output.json is missing because we didn't actually run the hook hooks.get_requires_for_build_wheel() runner.assert_called_once() assert runner.call_args[0][0][0] == 'some-python' def test_issue_104(): hooks = get_hooks('test-for-issue-104') with TemporaryDirectory() as outdir: with modified_env({ 'PYTHONPATH': BUILDSYS_PKGS, 'PEP517_ISSUE104_OUTDIR': outdir, }): hooks.get_requires_for_build_wheel({}) with open(pjoin(outdir, 'out.json')) as f: children = json.load(f) assert set(children) <= { '__init__.py', '__init__.pyc', '_in_process.py', '_in_process.pyc', '__pycache__', } def test_setup_py(): hooks = get_hooks('setup-py') with modified_env({'PYTHONPATH': BUILDSYS_PKGS}): res = hooks.get_requires_for_build_wheel({}) # Some versions of setuptools list setuptools itself here res = [x for x in res if x != 'setuptools'] assert res == ['wheel'] @pytest.mark.parametrize( ("pkg", "expected"), [ ("pkg1", ["build_editable"]), ("pkg2", []), ("pkg3", ["build_editable"]), ], ) def test__supported_features(pkg, expected): hooks = get_hooks(pkg) with modified_env({"PYTHONPATH": BUILDSYS_PKGS}): res = hooks._supported_features() assert res == expected ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/test_envbuild.py0000644000000000000000000000304700000000000014071 0ustar00from os.path import dirname, abspath, join as pjoin import tarfile from testpath import modified_env, assert_isfile from testpath.tempdir import TemporaryDirectory try: from unittest.mock import patch, call except ImportError: from mock import patch, call # Python 2 fallback import zipfile from pep517.envbuild import build_sdist, build_wheel, BuildEnvironment SAMPLES_DIR = pjoin(dirname(abspath(__file__)), 'samples') BUILDSYS_PKGS = pjoin(SAMPLES_DIR, 'buildsys_pkgs') @patch.object(BuildEnvironment, 'pip_install') def test_build_wheel(mock_pip_install): with modified_env({'PYTHONPATH': BUILDSYS_PKGS}), \ TemporaryDirectory() as outdir: filename = build_wheel(pjoin(SAMPLES_DIR, 'pkg1'), outdir) assert_isfile(pjoin(outdir, filename)) assert zipfile.is_zipfile(pjoin(outdir, filename)) assert mock_pip_install.call_count == 2 assert mock_pip_install.call_args_list[0] == call(['eg_buildsys']) assert mock_pip_install.call_args_list[1] == call(['wheelwright']) @patch.object(BuildEnvironment, 'pip_install') def test_build_sdist(mock_pip_install): with modified_env({'PYTHONPATH': BUILDSYS_PKGS}), \ TemporaryDirectory() as outdir: filename = build_sdist(pjoin(SAMPLES_DIR, 'pkg1'), outdir) assert_isfile(pjoin(outdir, filename)) assert tarfile.is_tarfile(pjoin(outdir, filename)) assert mock_pip_install.call_count == 2 assert mock_pip_install.call_args_list[0] == call(['eg_buildsys']) assert mock_pip_install.call_args_list[1] == call(['frog']) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/test_hook_fallbacks.py0000644000000000000000000000670400000000000015226 0ustar00import io from os.path import dirname, abspath, join as pjoin import pytest from testpath import modified_env, assert_isfile from testpath.tempdir import TemporaryDirectory from pep517.compat import toml_load from pep517.wrappers import HookMissing, Pep517HookCaller SAMPLES_DIR = pjoin(dirname(abspath(__file__)), 'samples') BUILDSYS_PKGS = pjoin(SAMPLES_DIR, 'buildsys_pkgs') def get_hooks(pkg): source_dir = pjoin(SAMPLES_DIR, pkg) with io.open(pjoin(source_dir, 'pyproject.toml'), 'rb') as f: data = toml_load(f) return Pep517HookCaller(source_dir, data['build-system']['build-backend']) def test_get_requires_for_build_wheel(): hooks = get_hooks('pkg2') with modified_env({'PYTHONPATH': BUILDSYS_PKGS}): res = hooks.get_requires_for_build_wheel({}) assert res == [] def test_get_requires_for_build_editable(): hooks = get_hooks('pkg2') with modified_env({'PYTHONPATH': BUILDSYS_PKGS}): res = hooks.get_requires_for_build_editable({}) assert res == [] def test_get_requires_for_build_sdist(): hooks = get_hooks('pkg2') with modified_env({'PYTHONPATH': BUILDSYS_PKGS}): res = hooks.get_requires_for_build_sdist({}) assert res == [] def test_prepare_metadata_for_build_wheel(): hooks = get_hooks('pkg2') with TemporaryDirectory() as metadatadir: with modified_env({'PYTHONPATH': BUILDSYS_PKGS}): hooks.prepare_metadata_for_build_wheel(metadatadir, {}) assert_isfile(pjoin(metadatadir, 'pkg2-0.5.dist-info', 'METADATA')) def test_prepare_metadata_for_build_editable(): hooks = get_hooks('pkg3') with TemporaryDirectory() as metadatadir: with modified_env({'PYTHONPATH': BUILDSYS_PKGS}): hooks.prepare_metadata_for_build_editable(metadatadir, {}) assert_isfile(pjoin(metadatadir, 'pkg3-0.5.dist-info', 'METADATA')) def test_prepare_metadata_for_build_editable_missing_build_editable(): hooks = get_hooks('pkg2') with TemporaryDirectory() as metadatadir: with modified_env({'PYTHONPATH': BUILDSYS_PKGS}): # pkg2's build system does not have build_editable with pytest.raises(HookMissing) as exc_info: hooks.prepare_metadata_for_build_editable(metadatadir, {}) e = exc_info.value assert 'build_editable' == e.hook_name assert 'build_editable' == str(e) def test_prepare_metadata_for_build_wheel_no_fallback(): hooks = get_hooks('pkg2') with TemporaryDirectory() as metadatadir: with modified_env({'PYTHONPATH': BUILDSYS_PKGS}): with pytest.raises(HookMissing) as exc_info: hooks.prepare_metadata_for_build_wheel( metadatadir, {}, _allow_fallback=False ) e = exc_info.value assert 'prepare_metadata_for_build_wheel' == e.hook_name assert 'prepare_metadata_for_build_wheel' in str(e) def test_prepare_metadata_for_build_editable_no_fallback(): hooks = get_hooks('pkg2') with TemporaryDirectory() as metadatadir: with modified_env({'PYTHONPATH': BUILDSYS_PKGS}): with pytest.raises(HookMissing) as exc_info: hooks.prepare_metadata_for_build_editable( metadatadir, {}, _allow_fallback=False ) e = exc_info.value assert 'prepare_metadata_for_build_editable' == e.hook_name assert 'prepare_metadata_for_build_editable' in str(e) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/test_inplace_hooks.py0000644000000000000000000000332400000000000015075 0ustar00import io from os.path import dirname, abspath, join as pjoin from testpath import modified_env import pytest from pep517.compat import toml_load from pep517.wrappers import Pep517HookCaller, BackendInvalid SAMPLES_DIR = pjoin(dirname(abspath(__file__)), 'samples') BUILDSYS_PKGS = pjoin(SAMPLES_DIR, 'buildsys_pkgs') SOURCE_DIR = pjoin(SAMPLES_DIR, 'pkg1') def get_hooks(pkg, backend=None, path=None): source_dir = pjoin(SAMPLES_DIR, pkg) with io.open(pjoin(source_dir, 'pyproject.toml'), 'rb') as f: data = toml_load(f) if backend is None: backend = data['build-system']['build-backend'] if path is None: path = data['build-system']['backend-path'] return Pep517HookCaller(source_dir, backend, path) @pytest.mark.parametrize('backend_path', [ ['.', 'subdir'], ['../pkg1', 'subdir/..'], ]) def test_backend_path_within_tree(backend_path): Pep517HookCaller(SOURCE_DIR, 'dummy', backend_path) @pytest.mark.parametrize('backend_path', [ [SOURCE_DIR], ['.', '..'], ['subdir/../..'], ['/'], ]) def test_backend_out_of_tree(backend_path): # TODO: Do we want to insist on ValueError, or invent another exception? with pytest.raises(Exception): Pep517HookCaller(SOURCE_DIR, 'dummy', backend_path) def test_intree_backend(): hooks = get_hooks('pkg_intree') with modified_env({'PYTHONPATH': BUILDSYS_PKGS}): res = hooks.get_requires_for_build_sdist({}) assert res == ["intree_backend_called"] def test_intree_backend_not_in_path(): hooks = get_hooks('pkg_intree', backend='buildsys') with modified_env({'PYTHONPATH': BUILDSYS_PKGS}): with pytest.raises(BackendInvalid): hooks.get_requires_for_build_sdist({}) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tests/test_meta.py0000644000000000000000000000153500000000000013207 0ustar00from __future__ import unicode_literals, absolute_import, division import re import pytest from pep517 import meta pep517_needs_python_3 = pytest.mark.xfail( 'sys.version_info < (3,)', reason="pep517 cannot be built on Python 2", ) def test_meta_for_this_package(): dist = meta.load('.') assert re.match(r'[\d.]+', dist.version) assert dist.metadata['Name'] == 'pep517' def test_classic_package(tmpdir): (tmpdir / 'setup.py').write_text( 'from distutils.core import setup; setup(name="foo", version="1.0")', encoding='utf-8', ) dist = meta.load(str(tmpdir)) assert dist.version == '1.0' assert dist.metadata['Name'] == 'foo' def test_meta_output(capfd): """load shouldn't emit any output""" meta.load('.') captured = capfd.readouterr() assert captured.out == captured.err == '' ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1634548450.2573519 pep517-0.12.0/tox.ini0000644000000000000000000000035400000000000011017 0ustar00[tox] envlist = py27, py34, py35, py36, py37, py38, py39, pypy, pypy3 skipsdist = true [testenv] deps = -rdev-requirements.txt commands = pytest [] [testenv:release] skip_install = True deps = flit commands = python -m flit publish pep517-0.12.0/setup.py0000644000000000000000000000127300000000000011217 0ustar00#!/usr/bin/env python # setup.py generated by flit for tools that don't yet use PEP 517 from distutils.core import setup packages = \ ['pep517', 'pep517.in_process'] package_data = \ {'': ['*']} extras_require = \ {":python_version<'3.6'": ['toml'], ":python_version<'3.8'": ['importlib_metadata', 'zipp'], ":python_version>='3.6'": ['tomli >=1.1.0']} setup(name='pep517', version='0.12.0', description='Wrappers to build Python packages using PEP 517 hooks', author='Thomas Kluyver', author_email='thomas@kluyver.me.uk', url='https://github.com/pypa/pep517', packages=packages, package_data=package_data, extras_require=extras_require, ) pep517-0.12.0/PKG-INFO0000644000000000000000000000734000000000000010603 0ustar00Metadata-Version: 2.1 Name: pep517 Version: 0.12.0 Summary: Wrappers to build Python packages using PEP 517 hooks Home-page: https://github.com/pypa/pep517 Author: Thomas Kluyver Author-email: thomas@kluyver.me.uk Description-Content-Type: text/x-rst Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Requires-Dist: toml;python_version<'3.6' Requires-Dist: tomli >=1.1.0;python_version>='3.6' Requires-Dist: importlib_metadata;python_version<'3.8' Requires-Dist: zipp;python_version<'3.8' API to call PEP 517 hooks ========================= `PEP 517 `_ specifies a standard API for systems which build Python packages. `PEP 660 `_ extends it with a build mode that leads to editable installs. This package contains wrappers around the hooks specified by PEP 517 and PEP 660. It provides: - A mechanism to call the hooks in a subprocess, so they are isolated from the current process. - Fallbacks for the optional hooks, so that frontends can call the hooks without checking which are defined. Run the tests with ``pytest`` or `tox `_. Usage—you are responsible for ensuring build requirements are available: .. code-block:: python import os import tomli from pep517.wrappers import Pep517HookCaller src = 'path/to/source' # Folder containing 'pyproject.toml' with open(os.path.join(src, 'pyproject.toml')) as f: build_sys = tomli.load(f)['build-system'] print(build_sys['requires']) # List of static requirements # The caller is responsible for installing these and running the hooks in # an environment where they are available. hooks = Pep517HookCaller( src, build_backend=build_sys['build-backend'], backend_path=build_sys.get('backend-path'), ) config_options = {} # Optional parameters for backend # List of dynamic requirements: print(hooks.get_requires_for_build_wheel(config_options)) # Again, the caller is responsible for installing these build requirements destination = 'also/a/folder' whl_filename = hooks.build_wheel(destination, config_options) assert os.path.isfile(os.path.join(destination, whl_filename)) Deprecated high-level --------------------- For now, ``pep517`` also contains higher-level functions which install the build dependencies into a temporary environment and build a wheel/sdist using them. This is a rough implementation, e.g. it does not do proper build isolation. The `PyPA build project `_ is recommended as an alternative, although it's still quite young in October 2020. This layer of functionality in ``pep517`` is now deprecated, but won't be removed for some time, as there is code relying on it. High level usage, with build requirements handled: .. code-block:: python import os from pep517.envbuild import build_wheel, build_sdist src = 'path/to/source' # Folder containing 'pyproject.toml' destination = 'also/a/folder' whl_filename = build_wheel(src, destination) assert os.path.isfile(os.path.join(destination, whl_filename)) targz_filename = build_sdist(src, destination) assert os.path.isfile(os.path.join(destination, targz_filename)) To test the build backend for a project, run in a system shell: .. code-block:: shell python3 -m pep517.check path/to/source # source dir containing pyproject.toml To build a backend into source and/or binary distributions, run in a shell: .. code-block:: shell python -m pep517.build path/to/source # source dir containing pyproject.toml All of this high-level functionality is deprecated.