././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1622691171.2277687 build-0.4.0/0000777000175000017500000000000000000000000014515 5ustar00codespacecodespace00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1622691142.0 build-0.4.0/LICENSE0000666000175000017500000000213100000000000015517 0ustar00codespacecodespace00000000000000Copyright © 2019 Filipe Laíns 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 (including the next paragraph) 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. ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1622691205.8282268 build-0.4.0/PKG-INFO0000666000175000017500000000556700000000000015627 0ustar00codespacecodespace00000000000000Metadata-Version: 2.1 Name: build Version: 0.4.0 Summary: A simple, correct PEP517 package builder Home-page: UNKNOWN Author: Filipe Laíns Author-email: lains@riseup.net License: MIT Project-URL: homepage, https://github.com/pypa/build Project-URL: changelog, https://pypa-build.readthedocs.io/en/stable/changelog.html Description: # build [![CI check](https://github.com/pypa/build/workflows/check/badge.svg)](https://github.com/pypa/build/actions) [![CI test](https://github.com/pypa/build/actions/workflows/test.yml/badge.svg)](https://github.com/pypa/build/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/pypa/build/branch/main/graph/badge.svg)](https://codecov.io/gh/pypa/build) [![Documentation Status](https://readthedocs.org/projects/pypa-build/badge/?version=latest)](https://pypa-build.readthedocs.io/en/latest/?badge=latest) [![PyPI version](https://badge.fury.io/py/build.svg)](https://pypi.org/project/build/) [![Discord](https://img.shields.io/discord/803025117553754132?label=Discord%20chat%20%23build&style=flat-square)](https://discord.gg/pypa) A simple, correct PEP517 package builder. See the [documentation](https://pypa-build.readthedocs.io/en/latest/) for more information. ### Installation `build` can be installed via `pip` or an equivalent via: ```console $ pip install build ``` ### Usage ```console $ python -m build ``` This will build the package in an isolated environment, generating a source-distribution and wheel in the directory `dist/`. See the [documentation](https://pypa-build.readthedocs.io/en/latest/) for full information. ### Code of Conduct Everyone interacting in the build's codebase, issue trackers, chat rooms, and mailing lists is expected to follow the [PSF Code of Conduct]. [psf code of conduct]: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md Platform: UNKNOWN Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7 Description-Content-Type: text/markdown Provides-Extra: docs Provides-Extra: test Provides-Extra: typing Provides-Extra: virtualenv ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1622691142.0 build-0.4.0/README.md0000666000175000017500000000274000000000000015777 0ustar00codespacecodespace00000000000000# build [![CI check](https://github.com/pypa/build/workflows/check/badge.svg)](https://github.com/pypa/build/actions) [![CI test](https://github.com/pypa/build/actions/workflows/test.yml/badge.svg)](https://github.com/pypa/build/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/pypa/build/branch/main/graph/badge.svg)](https://codecov.io/gh/pypa/build) [![Documentation Status](https://readthedocs.org/projects/pypa-build/badge/?version=latest)](https://pypa-build.readthedocs.io/en/latest/?badge=latest) [![PyPI version](https://badge.fury.io/py/build.svg)](https://pypi.org/project/build/) [![Discord](https://img.shields.io/discord/803025117553754132?label=Discord%20chat%20%23build&style=flat-square)](https://discord.gg/pypa) A simple, correct PEP517 package builder. See the [documentation](https://pypa-build.readthedocs.io/en/latest/) for more information. ### Installation `build` can be installed via `pip` or an equivalent via: ```console $ pip install build ``` ### Usage ```console $ python -m build ``` This will build the package in an isolated environment, generating a source-distribution and wheel in the directory `dist/`. See the [documentation](https://pypa-build.readthedocs.io/en/latest/) for full information. ### Code of Conduct Everyone interacting in the build's codebase, issue trackers, chat rooms, and mailing lists is expected to follow the [PSF Code of Conduct]. [psf code of conduct]: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1622691142.0 build-0.4.0/pyproject.toml0000666000175000017500000000062700000000000017436 0ustar00codespacecodespace00000000000000[build-system] requires = ['setuptools >= 40.8.0', 'wheel'] build-backend = 'setuptools.build_meta' [tool.black] line-length = 127 skip-string-normalization = true target-version = ['py38', 'py37', 'py36', 'py35', 'py27'] [tool.isort] profile = "black" lines_between_types = 1 lines_after_imports = 2 line_length = 127 known_first_party = "build" skip = [] # "build" is included in the default skip list ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1622691205.8282268 build-0.4.0/setup.cfg0000666000175000017500000000453200000000000016342 0ustar00codespacecodespace00000000000000[metadata] name = build version = 0.4.0 description = A simple, correct PEP517 package builder long_description = file: README.md long_description_content_type = text/markdown author = Filipe Laíns author_email = lains@riseup.net license = MIT license_file = LICENSE classifiers = License :: OSI Approved :: MIT License Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: Implementation :: PyPy project_urls = homepage = https://github.com/pypa/build changelog = https://pypa-build.readthedocs.io/en/stable/changelog.html [options] packages = find: install_requires = packaging>=19.0 pep517>=0.9.1 toml>=0.10.0 colorama;os_name == "nt" # not actually a runtime dependency, only supplied as there is not "recomended dependency" support importlib-metadata>=0.22;python_version < "3.8" typing>=3.5.3.0;python_version < "3" virtualenv>=20.0.35;python_version < "3" python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* package_dir = =src [options.entry_points] console_scripts = pyproject-build = build.__main__:entrypoint pipx.run = build = build.__main__:entrypoint [options.extras_require] docs = furo>=2020.11.19b18 sphinx~=3.0 sphinx-argparse-cli>=1.5 sphinx-autodoc-typehints>=1.10 test = filelock>=3 pytest>=4 pytest-cov>=2 pytest-mock>=2 pytest-xdist>=1.34 typing = mypy==0.800 typing-extensions>=3.7.4.3 virtualenv = virtualenv>=20.0.35 [options.package_data] build = py.typed [options.packages.find] where = src [bdist_wheel] universal = 1 [tool:pytest] junit_family = xunit2 norecursedirs = tests/integration/* markers = isolated [flake8] max-line-length = 127 max-complexity = 10 extend-ignore = E203 [mypy] ignore_missing_imports = True strict = True [coverage:run] omit = setup.py *bin/pyproject-build *bin\pyproject-build.exe [coverage:report] exclude_lines = \#\s*pragma: no cover ^\s*raise NotImplementedError\b [coverage:paths] source = src */site-packages *\site-packages [coverage:html] show_contexts = true [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1622691142.0 build-0.4.0/setup.py0000666000175000017500000000004700000000000016230 0ustar00codespacecodespace00000000000000from setuptools import setup setup() ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1622691171.2277687 build-0.4.0/src/0000777000175000017500000000000000000000000015304 5ustar00codespacecodespace00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1622691171.2277687 build-0.4.0/src/build/0000777000175000017500000000000000000000000016403 5ustar00codespacecodespace00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1622691142.0 build-0.4.0/src/build/__init__.py0000666000175000017500000003432600000000000020524 0ustar00codespacecodespace00000000000000# SPDX-License-Identifier: MIT """ build - A simple, correct PEP 517 package builder """ __version__ = '0.4.0' import contextlib import difflib import io import os import subprocess import sys import types import warnings from collections import OrderedDict from typing import AbstractSet, Any, Callable, Dict, Iterator, Mapping, Optional, Sequence, Set, Text, Tuple, Type, Union import pep517.wrappers import toml import toml.decoder if sys.version_info < (3,): FileNotFoundError = IOError PermissionError = OSError RunnerType = Callable[[Sequence[str], Optional[Union[bytes, Text]], Optional[Dict[str, str]]], None] ConfigSettingsType = Mapping[str, Union[str, Sequence[str]]] _ExcInfoType = Union[ Tuple[Type[BaseException], BaseException, types.TracebackType], Tuple[None, None, None], ] _DEFAULT_BACKEND = { 'build-backend': 'setuptools.build_meta:__legacy__', 'requires': ['setuptools >= 40.8.0', 'wheel'], } class BuildException(Exception): """ Exception raised by ProjectBuilder """ class BuildBackendException(Exception): """ Exception raised when the backend fails """ def __init__(self, exception, description=None, exc_info=(None, None, None)): # type: (Exception, Optional[str], _ExcInfoType) -> None super(BuildBackendException, self).__init__() self.exception = exception # type: Exception self.exc_info = exc_info self._description = description def __str__(self): # type: () -> str if self._description: return self._description return 'Backend operation failed: {!r}'.format(self.exception) class TypoWarning(Warning): """ Warning raised when a potential typo is found """ def _validate_source_directory(srcdir): # type: (str) -> None if not os.path.isdir(srcdir): raise BuildException('Source {} is not a directory'.format(srcdir)) pyproject_toml = os.path.join(srcdir, 'pyproject.toml') setup_py = os.path.join(srcdir, 'setup.py') if not os.path.exists(pyproject_toml) and not os.path.exists(setup_py): raise BuildException('Source {} does not appear to be a Python project: no pyproject.toml or setup.py'.format(srcdir)) def check_dependency(req_string, ancestral_req_strings=(), parent_extras=frozenset()): # type: (str, Tuple[str, ...], AbstractSet[str]) -> Iterator[Tuple[str, ...]] """ Verify that a dependency and all of its dependencies are met. :param req_string: Requirement string :param parent_extras: Extras (eg. "test" in myproject[test]) :yields: Unmet dependencies """ import packaging.requirements if sys.version_info >= (3, 8): import importlib.metadata as importlib_metadata else: import importlib_metadata req = packaging.requirements.Requirement(req_string) if req.marker: extras = frozenset(('',)).union(parent_extras) # a requirement can have multiple extras but ``evaluate`` can # only check one at a time. if all(not req.marker.evaluate(environment={'extra': e}) for e in extras): # if the marker conditions are not met, we pretend that the # dependency is satisfied. return try: dist = importlib_metadata.distribution(req.name) except importlib_metadata.PackageNotFoundError: # dependency is not installed in the environment. yield ancestral_req_strings + (req_string,) else: if req.specifier and not req.specifier.contains(dist.version, prereleases=True): # the installed version is incompatible. yield ancestral_req_strings + (req_string,) elif dist.requires: for other_req_string in dist.requires: for unmet_req in check_dependency(other_req_string, ancestral_req_strings + (req_string,), req.extras): # a transitive dependency is not satisfied. yield unmet_req def _find_typo(dictionary, expected): # type: (Mapping[str, str], str) -> None if expected not in dictionary: for obj in dictionary: if difflib.SequenceMatcher(None, expected, obj).ratio() >= 0.8: warnings.warn( "Found '{}' in pyproject.toml, did you mean '{}'?".format(obj, expected), TypoWarning, ) @contextlib.contextmanager def _working_directory(path): # type: (str) -> Iterator[None] current = os.getcwd() os.chdir(path) try: yield finally: os.chdir(current) class ProjectBuilder(object): """ The PEP 517 consumer API. """ def __init__( self, srcdir, # type: str python_executable=sys.executable, # type: Union[bytes, Text] scripts_dir=None, # type: Optional[Union[bytes, Text]] runner=pep517.wrappers.default_subprocess_runner, # type: RunnerType ): # type: (...) -> None """ :param srcdir: The source directory :param scripts_dir: The location of the scripts dir (defaults to the folder where the python executable lives) :param python_executable: The python executable where the backend lives :param runner: An alternative runner for backend subprocesses The 'runner', if provided, must accept the following arguments: - 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 srcdir. - extra_environ: a dict mapping environment variable names to values which must be set for the subprocess execution. The default runner simply calls the backend hooks in a subprocess, writing backend output to stdout/stderr. """ self.srcdir = os.path.abspath(srcdir) # type: str _validate_source_directory(srcdir) spec_file = os.path.join(srcdir, 'pyproject.toml') try: with io.open(spec_file, encoding='UTF-8') as f: spec = toml.load(f) except FileNotFoundError: spec = {} except PermissionError as e: raise BuildException("{}: '{}' ".format(e.strerror, e.filename)) except toml.decoder.TomlDecodeError as e: raise BuildException('Failed to parse {}: {} '.format(spec_file, e)) build_system = spec.get('build-system') # if pyproject.toml is missing (per PEP 517) or [build-system] is missing (per PEP 518), # use default values. if build_system is None: _find_typo(spec, 'build-system') build_system = _DEFAULT_BACKEND # if [build-system] is present, it must have a ``requires`` field (per PEP 518). elif 'requires' not in build_system: _find_typo(build_system, 'requires') raise BuildException("Missing 'build-system.requires' in {}".format(spec_file)) # if ``build-backend`` is missing, inject the legacy setuptools backend # but leave ``requires`` alone to emulate pip. elif 'build-backend' not in build_system: _find_typo(build_system, 'build-backend') build_system['build-backend'] = _DEFAULT_BACKEND['build-backend'] self._build_system = build_system self._backend = self._build_system['build-backend'] self._scripts_dir = scripts_dir self._hook_runner = runner self._hook = pep517.wrappers.Pep517HookCaller( self.srcdir, self._backend, backend_path=self._build_system.get('backend-path'), python_executable=python_executable, runner=self._runner, ) def _runner(self, cmd, cwd=None, extra_environ=None): # type: (Sequence[str], Optional[Union[bytes, Text]], Optional[Dict[str, str]]) -> None # if script dir is specified must be inserted at the start of PATH (avoid duplicate path while doing so) if self.scripts_dir is not None: paths = OrderedDict() # type: Dict[str, None] paths[str(self.scripts_dir)] = None if 'PATH' in os.environ: paths.update((i, None) for i in os.environ['PATH'].split(os.pathsep)) extra_environ = {} if extra_environ is None else extra_environ extra_environ['PATH'] = os.pathsep.join(paths) self._hook_runner(cmd, cwd, extra_environ) @property def python_executable(self): # type: () -> Union[bytes, Text] """ The Python executable used to invoke the backend. """ # make mypy happy exe = self._hook.python_executable # type: Union[bytes, Text] return exe @python_executable.setter def python_executable(self, value): # type: (Union[bytes, Text]) -> None self._hook.python_executable = value @property def scripts_dir(self): # type: () -> Union[None, bytes, Text] """ The folder where the scripts are stored for the python executable. """ return self._scripts_dir @scripts_dir.setter def scripts_dir(self, value): # type: (Union[None, bytes, Text]) -> None self._scripts_dir = value @property def build_system_requires(self): # type: () -> Set[str] """ The dependencies defined in the ``pyproject.toml``'s ``build-system.requires`` field or the default build dependencies if ``pyproject.toml`` is missing or ``build-system`` is undefined. """ return set(self._build_system['requires']) def get_requires_for_build(self, distribution, config_settings=None): # type: (str, Optional[ConfigSettingsType]) -> Set[str] """ Return the dependencies defined by the backend in addition to :attr:`build_system_requires` for a given distribution. :param distribution: Distribution to get the dependencies of (``sdist`` or ``wheel``) :param config_settings: Config settings for the build backend """ hook_name = 'get_requires_for_build_{}'.format(distribution) get_requires = getattr(self._hook, hook_name) with self._handle_backend(hook_name): return set(get_requires(config_settings)) def check_dependencies(self, distribution, config_settings=None): # type: (str, Optional[ConfigSettingsType]) -> Set[Tuple[str, ...]] """ Return the dependencies which are not satisfied from the combined set of :attr:`build_system_requires` and :meth:`get_requires_for_build` for a given distribution. :param distribution: Distribution to check (``sdist`` or ``wheel``) :param config_settings: Config settings for the build backend :returns: Set of variable-length unmet dependency tuples """ dependencies = self.get_requires_for_build(distribution, config_settings).union(self.build_system_requires) return {u for d in dependencies for u in check_dependency(d)} def prepare(self, distribution, output_directory, config_settings=None): # type: (str, str, Optional[ConfigSettingsType]) -> Optional[str] """ Prepare metadata for a distribution. :param distribution: Distribution to build (must be ``wheel``) :param output_directory: Directory to put the prepared metadata in :param config_settings: Config settings for the build backend :returns: The full path to the prepared metadata directory """ prepare = getattr(self._hook, 'prepare_metadata_for_build_{}'.format(distribution)) try: return self._call_backend(prepare, output_directory, config_settings, _allow_fallback=False) except BuildBackendException as exception: if isinstance(exception.exception, pep517.wrappers.HookMissing): return None raise def build(self, distribution, output_directory, config_settings=None, metadata_directory=None): # type: (str, str, Optional[ConfigSettingsType], Optional[str]) -> str """ Build a distribution. :param distribution: Distribution to build (``sdist`` or ``wheel``) :param output_directory: Directory to put the built distribution in :param config_settings: Config settings for the build backend :param metadata_directory: If provided, should be the return value of a previous ``prepare`` call on the same ``distribution`` kind :returns: The full path to the built distribution """ build = getattr(self._hook, 'build_{}'.format(distribution)) kwargs = {} if metadata_directory is None else {'metadata_directory': metadata_directory} return self._call_backend(build, output_directory, config_settings, **kwargs) def _call_backend(self, callback, outdir, config_settings=None, **kwargs): # type: (Callable[...,str], str, Optional[ConfigSettingsType], Any) -> str outdir = os.path.abspath(outdir) if os.path.exists(outdir): if not os.path.isdir(outdir): raise BuildException("Build path '{}' exists and is not a directory".format(outdir)) else: os.mkdir(outdir) with self._handle_backend(callable.__name__): basename = callback(outdir, config_settings, **kwargs) # type: str return os.path.join(outdir, basename) @contextlib.contextmanager def _handle_backend(self, hook): # type: (str) -> Iterator[None] with _working_directory(self.srcdir): try: yield except pep517.wrappers.BackendUnavailable as exception: raise BuildBackendException( exception, "Backend '{}' is not available.".format(self._backend), sys.exc_info(), ) except subprocess.CalledProcessError as exception: raise BuildBackendException(exception, 'Backend subproccess exited when trying to invoke {}'.format(hook)) except Exception as exception: raise BuildBackendException(exception, exc_info=sys.exc_info()) __all__ = ( '__version__', 'ConfigSettingsType', 'RunnerType', 'BuildException', 'BuildBackendException', 'TypoWarning', 'check_dependency', 'ProjectBuilder', ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1622691142.0 build-0.4.0/src/build/__main__.py0000666000175000017500000001664300000000000020507 0ustar00codespacecodespace00000000000000# SPDX-License-Identifier: MIT from __future__ import print_function import argparse import os import subprocess import sys import traceback import warnings from typing import Iterable, List, Optional, Sequence, TextIO, Type, Union import build from build import BuildBackendException, BuildException, ConfigSettingsType, ProjectBuilder from build.env import IsolatedEnvBuilder try: import colorama except ImportError: pass else: colorama.init() # fix colors on windows __all__ = ['build', 'main', 'main_parser'] def _showwarning(message, category, filename, lineno, file=None, line=None): # pragma: no cover # type: (Union[Warning, str], Type[Warning], str, int, Optional[TextIO], Optional[str]) -> None prefix = 'WARNING' if sys.stdout.isatty(): prefix = '\33[93m' + prefix + '\33[0m' print('{} {}'.format(prefix, str(message))) warnings.showwarning = _showwarning def _error(msg, code=1): # type: (str, int) -> None # pragma: no cover """ Print an error message and exit. Will color the output when writing to a TTY. :param msg: Error message :param code: Error code """ prefix = 'ERROR' if sys.stdout.isatty(): prefix = '\33[91m' + prefix + '\33[0m' print('{} {}'.format(prefix, msg)) exit(code) def _format_dep_chain(dep_chain): # type: (Sequence[str]) -> str return ' -> '.join(dep.partition(';')[0].strip() for dep in dep_chain) def _build_in_isolated_env(builder, outdir, distributions, config_settings): # type: (ProjectBuilder, str, List[str], ConfigSettingsType) -> None for distribution in distributions: with IsolatedEnvBuilder() as env: builder.python_executable = env.executable builder.scripts_dir = env.scripts_dir # first install the build dependencies env.install(builder.build_system_requires) # then get the extra required dependencies from the backend (which was installed in the call above :P) env.install(builder.get_requires_for_build(distribution)) builder.build(distribution, outdir, config_settings) def _build_in_current_env(builder, outdir, distributions, config_settings, skip_dependency_check=False): # type: (ProjectBuilder, str, List[str], ConfigSettingsType, bool) -> None for dist in distributions: if not skip_dependency_check: missing = builder.check_dependencies(dist) if missing: _error( 'Missing dependencies:' + ''.join('\n\t' + dep for deps in missing for dep in (deps[0], _format_dep_chain(deps[1:])) if dep) ) builder.build(dist, outdir, config_settings) def build_package(srcdir, outdir, distributions, config_settings=None, isolation=True, skip_dependency_check=False): # type: (str, str, List[str], Optional[ConfigSettingsType], bool, bool) -> None """ Run the build process. :param srcdir: Source directory :param outdir: Output directory :param distributions: Distributions to build (sdist and/or wheel) :param config_settings: Configuration settings to be passed to the backend :param isolation: Isolate the build in a separate environment :param skip_dependency_check: Do not perform the dependency check """ if not config_settings: config_settings = {} try: builder = ProjectBuilder(srcdir) if isolation: _build_in_isolated_env(builder, outdir, distributions, config_settings) else: _build_in_current_env(builder, outdir, distributions, config_settings, skip_dependency_check) except BuildException as e: _error(str(e)) except BuildBackendException as e: if isinstance(e.exception, subprocess.CalledProcessError): print() else: if e.exc_info: traceback.print_exception( e.exc_info[0], e.exc_info[1], e.exc_info[2], limit=-1, ) else: if sys.version_info >= (3, 5): print(traceback.format_exc(-1)) else: print(traceback.format_tb()) _error(str(e)) def main_parser(): # type: () -> argparse.ArgumentParser """ Construct the main parser. """ # mypy does not recognize module.__path__ # https://github.com/python/mypy/issues/1422 paths = build.__path__ # type: Iterable[Optional[str]] # type: ignore parser = argparse.ArgumentParser() parser.add_argument( 'srcdir', type=str, nargs='?', default=os.getcwd(), help='source directory (defaults to current directory)', ) parser.add_argument( '--version', '-V', action='version', version='build {} ({})'.format(build.__version__, ', '.join(path for path in paths if path)), ) parser.add_argument( '--sdist', '-s', action='store_true', help='build a source distribution (enabled by default if no target is specified)', ) parser.add_argument( '--wheel', '-w', action='store_true', help='build a wheel (enabled by default if no target is specified)', ) parser.add_argument( '--outdir', '-o', type=str, help='output directory (defaults to {{srcdir}}{sep}dist)'.format(sep=os.sep), ) parser.add_argument( '--skip-dependency-check', '-x', action='store_true', help='do not check that build dependencies are installed', ) parser.add_argument( '--no-isolation', '-n', action='store_true', help='do not isolate the build in a virtual environment', ) parser.add_argument( '--config-setting', '-C', action='append', help='pass options to the backend. options which begin with a hyphen must be in the form of ' '"--config-setting=--opt(=value)" or "-C--opt(=value)"', ) return parser def main(cli_args, prog=None): # type: (List[str], Optional[str]) -> None """ Parse the CLI arguments and invoke the build process. :param cli_args: CLI arguments :param prog: Program name to show in help text """ parser = main_parser() if prog: parser.prog = prog args = parser.parse_args(cli_args) distributions = [] config_settings = {} if args.config_setting: for arg in args.config_setting: setting, _, value = arg.partition('=') if setting not in config_settings: config_settings[setting] = value else: if not isinstance(config_settings[setting], list): config_settings[setting] = [config_settings[setting]] config_settings[setting].append(value) if args.sdist: distributions.append('sdist') if args.wheel: distributions.append('wheel') # default targets if not distributions: distributions = ['sdist', 'wheel'] # outdir is relative to srcdir only if omitted. outdir = os.path.join(args.srcdir, 'dist') if args.outdir is None else args.outdir build_package(args.srcdir, outdir, distributions, config_settings, not args.no_isolation, args.skip_dependency_check) def entrypoint(): # type: () -> None main(sys.argv[1:]) if __name__ == '__main__': # pragma: no cover main(sys.argv[1:], 'python -m build') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1622691142.0 build-0.4.0/src/build/_compat.py0000666000175000017500000000237700000000000020410 0ustar00codespacecodespace00000000000000import abc import sys from typing import TYPE_CHECKING, Callable, TypeVar _T = TypeVar('_T') def add_metaclass(metaclass): # type: (type) -> Callable[[_T], _T] """Class decorator for creating a class with a metaclass (borrowed from six code).""" def wrapper(cls): # type: (_T) -> _T orig_vars = cls.__dict__.copy() slots = orig_vars.get('__slots__') if slots is not None: if isinstance(slots, str): # pragma: no cover slots = [slots] # pragma: no cover for slots_var in slots: # pragma: no cover orig_vars.pop(slots_var) # pragma: no cover orig_vars.pop('__dict__', None) orig_vars.pop('__weakref__', None) if hasattr(cls, '__qualname__'): orig_vars['__qualname__'] = cls.__qualname__ # type: ignore return metaclass(cls.__name__, cls.__bases__, orig_vars) # type: ignore return wrapper if sys.version_info[0] == 2: abstractproperty = abc.abstractproperty else: if TYPE_CHECKING: # pragma: no cover abstractproperty = property # pragma: no cover else: def abstractproperty(func): return property(abc.abstractmethod(func)) __all__ = ( 'abstractproperty', 'add_metaclass', ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1622691142.0 build-0.4.0/src/build/env.py0000666000175000017500000002364100000000000017553 0ustar00codespacecodespace00000000000000""" Creates and manages isolated build environments. """ import abc import functools import os import platform import shutil import subprocess import sys import sysconfig import tempfile from types import TracebackType from typing import Iterable, Optional, Tuple, Type import packaging.requirements import packaging.version import build from ._compat import abstractproperty, add_metaclass if sys.version_info < (3, 8): import importlib_metadata as metadata else: from importlib import metadata try: import virtualenv except ImportError: virtualenv = None @add_metaclass(abc.ABCMeta) class IsolatedEnv(object): """Abstract base of isolated build environments, as required by the build project.""" @abstractproperty def executable(self): # type: () -> str """The executable of the isolated build environment.""" raise NotImplementedError @abstractproperty def scripts_dir(self): # type: () -> str """The scripts directory of the isolated build environment.""" raise NotImplementedError @abc.abstractmethod def install(self, requirements): # type: (Iterable[str]) -> None """ Install packages from PEP 508 requirements in the isolated build environment. :param requirements: PEP 508 requirements """ raise NotImplementedError class IsolatedEnvBuilder(object): """Builder object for isolated environments.""" _has_virtualenv = None # type: Optional[bool] def __init__(self): # type: () -> None self._path = None # type: Optional[str] def _should_use_virtualenv(self): # type: () -> Optional[bool] # virtualenv might be incompatible if it was installed separately # from build. This verifies that virtualenv and all of its # dependencies are installed as specified by build. if self._has_virtualenv is None: self.__class__._has_virtualenv = virtualenv is not None and not any( packaging.requirements.Requirement(d[1]).name == 'virtualenv' for d in build.check_dependency('build[virtualenv]') if len(d) > 1 ) return self._has_virtualenv def __enter__(self): # type: () -> IsolatedEnv """ Create an isolated build environment. :return: The isolated build environment """ self._path = tempfile.mkdtemp(prefix='build-env-') try: # use virtualenv on Python 2 or when valid virtualenv is available (as it's faster than venv) if sys.version_info < (3,) or self._should_use_virtualenv(): executable, scripts_dir = _create_isolated_env_virtualenv(self._path) else: executable, scripts_dir = _create_isolated_env_venv(self._path) return _IsolatedEnvVenvPip(path=self._path, python_executable=executable, scripts_dir=scripts_dir) except Exception: # cleanup folder if creation fails self.__exit__(*sys.exc_info()) raise def __exit__(self, exc_type, exc_val, exc_tb): # type: (Optional[Type[BaseException]], Optional[BaseException], Optional[TracebackType]) -> None """ Delete the created isolated build environment. :param exc_type: The type of exception raised (if any) :param exc_val: The value of exception raised (if any) :param exc_tb: The traceback of exception raised (if any) """ if self._path is not None and os.path.exists(self._path): # in case the user already deleted skip remove shutil.rmtree(self._path) class _IsolatedEnvVenvPip(IsolatedEnv): """ Isolated build environment context manager Non-standard paths injected directly to sys.path will still be passed to the environment. """ def __init__(self, path, python_executable, scripts_dir): # type: (str, str, str) -> None """ :param path: The path where the environment exists :param python_executable: The python executable within the environment """ self._path = path self._python_executable = python_executable self._scripts_dir = scripts_dir @property def path(self): # type: () -> str """The location of the isolated build environment.""" return self._path @property def executable(self): # type: () -> str """The python executable of the isolated build environment.""" return self._python_executable @property def scripts_dir(self): # type: () -> str return self._scripts_dir def install(self, requirements): # type: (Iterable[str]) -> None """ Install packages from PEP 508 requirements in the isolated build environment. :param requirements: PEP 508 requirement specification to install :note: Passing non-PEP 508 strings will result in undefined behavior, you *should not* rely on it. It is merely an implementation detail, it may change any time without warning. """ if not requirements: return # pip does not honour environment markers in command line arguments # but it does for requirements from a file with tempfile.NamedTemporaryFile('w+', prefix='build-reqs-', suffix='.txt', delete=False) as req_file: req_file.write(os.linesep.join(requirements)) try: cmd = [ self.executable, '-{}m'.format('E' if sys.version_info[0] == 2 else 'I'), 'pip', 'install', '--use-pep517', '--no-warn-script-location', '-r', os.path.abspath(req_file.name), ] subprocess.check_call(cmd) finally: os.unlink(req_file.name) def _create_isolated_env_virtualenv(path): # type: (str) -> Tuple[str, str] """ On Python 2 we use the virtualenv package to provision a virtual environment. :param path: The path where to create the isolated build environment :return: The Python executable and script folder """ cmd = [str(path), '--no-setuptools', '--no-wheel', '--activators', ''] result = virtualenv.cli_run(cmd, setup_logging=False) executable = str(result.creator.exe) script_dir = str(result.creator.script_dir) return executable, script_dir # venv only exists on Python 3+ if sys.version_info >= (3,): # noqa: C901 @functools.lru_cache(maxsize=None) def _fs_supports_symlink(): # type: () -> bool """Return True if symlinks are supported""" # Using definition used by venv.main() if os.name != 'nt': return True # Windows may support symlinks (setting in Windows 10) with tempfile.NamedTemporaryFile(prefix='build-symlink-') as tmp_file: dest = '{}-b'.format(tmp_file) try: os.symlink(tmp_file.name, dest) os.unlink(dest) return True except (OSError, NotImplementedError, AttributeError): return False def _create_isolated_env_venv(path): # type: (str) -> Tuple[str, str] """ On Python 3 we use the venv package from the standard library. :param path: The path where to create the isolated build environment :return: The Python executable and script folder """ import venv venv.EnvBuilder(with_pip=True, symlinks=_fs_supports_symlink()).create(path) executable, script_dir, purelib = _find_executable_and_scripts(path) # Get the version of pip in the environment pip_distribution = next(iter(metadata.distributions(name='pip', path=[purelib]))) current_pip_version = packaging.version.Version(pip_distribution.version) if platform.system() == 'Darwin' and int(platform.mac_ver()[0].split('.')[0]) >= 11: # macOS 11+ name scheme change requires 20.3. Intel macOS 11.0 can be told to report 10.16 for backwards # compatibility; but that also fixes earlier versions of pip so this is only needed for 11+. is_apple_silicon_python = sys.version_info >= (3, 6) and platform.machine() != 'x86_64' minimum_pip_version = '21.0.1' if is_apple_silicon_python else '20.3.0' else: # PEP-517 and manylinux1 was first implemented in 19.1 minimum_pip_version = '19.1.0' if current_pip_version < packaging.version.Version(minimum_pip_version): subprocess.check_call([executable, '-m', 'pip', 'install', 'pip>={}'.format(minimum_pip_version)]) # Avoid the setuptools from ensurepip to break the isolation subprocess.check_call([executable, '-m', 'pip', 'uninstall', 'setuptools', '-y']) return executable, script_dir def _find_executable_and_scripts(path): # type: (str) -> Tuple[str, str, str] """ Detect the Python executable and script folder of a virtual environment. :param path: The location of the virtual environment :return: The Python executable, script folder, and purelib folder """ config_vars = sysconfig.get_config_vars().copy() # globally cached, copy before altering it config_vars['base'] = path env_scripts = sysconfig.get_path('scripts', vars=config_vars) if not env_scripts: raise RuntimeError("Couldn't get environment scripts path") exe = 'pypy3' if platform.python_implementation() == 'PyPy' else 'python' if os.name == 'nt': exe = '{}.exe'.format(exe) executable = os.path.join(env_scripts, exe) if not os.path.exists(executable): raise RuntimeError('Virtual environment creation failed, executable {} missing'.format(executable)) purelib = sysconfig.get_path('purelib', vars=config_vars) if not purelib: raise RuntimeError("Couldn't get environment purelib folder") return executable, env_scripts, purelib __all__ = ( 'IsolatedEnvBuilder', 'IsolatedEnv', ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1622691142.0 build-0.4.0/src/build/py.typed0000666000175000017500000000000000000000000020070 0ustar00codespacecodespace00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1622691205.8282268 build-0.4.0/src/build.egg-info/0000777000175000017500000000000000000000000020075 5ustar00codespacecodespace00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1622691205.0 build-0.4.0/src/build.egg-info/PKG-INFO0000666000175000017500000000556700000000000021207 0ustar00codespacecodespace00000000000000Metadata-Version: 2.1 Name: build Version: 0.4.0 Summary: A simple, correct PEP517 package builder Home-page: UNKNOWN Author: Filipe Laíns Author-email: lains@riseup.net License: MIT Project-URL: homepage, https://github.com/pypa/build Project-URL: changelog, https://pypa-build.readthedocs.io/en/stable/changelog.html Description: # build [![CI check](https://github.com/pypa/build/workflows/check/badge.svg)](https://github.com/pypa/build/actions) [![CI test](https://github.com/pypa/build/actions/workflows/test.yml/badge.svg)](https://github.com/pypa/build/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/pypa/build/branch/main/graph/badge.svg)](https://codecov.io/gh/pypa/build) [![Documentation Status](https://readthedocs.org/projects/pypa-build/badge/?version=latest)](https://pypa-build.readthedocs.io/en/latest/?badge=latest) [![PyPI version](https://badge.fury.io/py/build.svg)](https://pypi.org/project/build/) [![Discord](https://img.shields.io/discord/803025117553754132?label=Discord%20chat%20%23build&style=flat-square)](https://discord.gg/pypa) A simple, correct PEP517 package builder. See the [documentation](https://pypa-build.readthedocs.io/en/latest/) for more information. ### Installation `build` can be installed via `pip` or an equivalent via: ```console $ pip install build ``` ### Usage ```console $ python -m build ``` This will build the package in an isolated environment, generating a source-distribution and wheel in the directory `dist/`. See the [documentation](https://pypa-build.readthedocs.io/en/latest/) for full information. ### Code of Conduct Everyone interacting in the build's codebase, issue trackers, chat rooms, and mailing lists is expected to follow the [PSF Code of Conduct]. [psf code of conduct]: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md Platform: UNKNOWN Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7 Description-Content-Type: text/markdown Provides-Extra: docs Provides-Extra: test Provides-Extra: typing Provides-Extra: virtualenv ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1622691205.0 build-0.4.0/src/build.egg-info/SOURCES.txt0000666000175000017500000000054000000000000021760 0ustar00codespacecodespace00000000000000LICENSE README.md pyproject.toml setup.cfg setup.py src/build/__init__.py src/build/__main__.py src/build/_compat.py src/build/env.py src/build/py.typed src/build.egg-info/PKG-INFO src/build.egg-info/SOURCES.txt src/build.egg-info/dependency_links.txt src/build.egg-info/entry_points.txt src/build.egg-info/requires.txt src/build.egg-info/top_level.txt././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1622691205.0 build-0.4.0/src/build.egg-info/dependency_links.txt0000666000175000017500000000000100000000000024143 0ustar00codespacecodespace00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1622691205.0 build-0.4.0/src/build.egg-info/entry_points.txt0000666000175000017500000000015500000000000023374 0ustar00codespacecodespace00000000000000[console_scripts] pyproject-build = build.__main__:entrypoint [pipx.run] build = build.__main__:entrypoint ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1622691205.0 build-0.4.0/src/build.egg-info/requires.txt0000666000175000017500000000067200000000000022502 0ustar00codespacecodespace00000000000000packaging>=19.0 pep517>=0.9.1 toml>=0.10.0 [:os_name == "nt"] colorama [:python_version < "3"] typing>=3.5.3.0 virtualenv>=20.0.35 [:python_version < "3.8"] importlib-metadata>=0.22 [docs] furo>=2020.11.19b18 sphinx~=3.0 sphinx-argparse-cli>=1.5 sphinx-autodoc-typehints>=1.10 [test] filelock>=3 pytest>=4 pytest-cov>=2 pytest-mock>=2 pytest-xdist>=1.34 [typing] mypy==0.800 typing-extensions>=3.7.4.3 [virtualenv] virtualenv>=20.0.35 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1622691205.0 build-0.4.0/src/build.egg-info/top_level.txt0000666000175000017500000000000600000000000022623 0ustar00codespacecodespace00000000000000build