from collections.abc import Iterator, Sequence from functools import partial from typing import NoReturn, TextIO
import build
from . import ProjectBuilder, _ctx from . import env as _env from ._exceptions import BuildBackendException, BuildException, FailedProcessError from ._types import ConfigSettings, Distribution, StrPath from .env import DefaultIsolatedEnv
def _format_dep_chain(dep_chain: Sequence[str]) -> str: return' -> '.join(dep.partition(';')[0].strip() for dep in dep_chain)
def _build_in_isolated_env(
srcdir: StrPath,
outdir: StrPath,
distribution: Distribution,
config_settings: ConfigSettings | None,
installer: _env.Installer,
) -> str: with DefaultIsolatedEnv(installer=installer) as env:
builder = ProjectBuilder.from_isolated_env(env, srcdir) # 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, config_settings or {})) return builder.build(distribution, outdir, config_settings or {})
ifnot skip_dependency_check:
missing = builder.check_dependencies(distribution, config_settings or {}) if missing:
dependencies = ''.join('\n\t' + dep for deps in missing for dep in (deps[0], _format_dep_chain(deps[1:])) if dep)
_cprint()
_error(f'Missing dependencies:{dependencies}')
return builder.build(distribution, outdir, config_settings or {})
:param srcdir: Source directory
:param outdir: Output directory
:param distribution: Distribution to build (sdist 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 """
built: list[str] = [] for distribution in distributions:
out = _build(isolation, srcdir, outdir, distribution, config_settings, skip_dependency_check, installer)
built.append(os.path.basename(out)) return built
def build_package_via_sdist(
srcdir: StrPath,
outdir: StrPath,
distributions: Sequence[Distribution],
config_settings: ConfigSettings | None = None,
isolation: bool = True,
skip_dependency_check: bool = False,
installer: _env.Installer = 'pip',
) -> Sequence[str]: """
Build a sdist and then the specified distributions from it.
:param srcdir: Source directory
:param outdir: Output directory
:param distribution: Distribution to build (only 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 """ from ._compat import tarfile
if'sdist'in distributions:
msg = 'Only binary distributions are allowed but sdist was specified' raise ValueError(msg)
sdist_name = os.path.basename(sdist)
sdist_out = tempfile.mkdtemp(prefix='build-via-sdist-')
built: list[str] = [] if distributions: # extract sdist with tarfile.TarFile.open(sdist) as t:
t.extractall(sdist_out) try:
_ctx.log(f'Building {_natural_language_list(distributions)} from sdist')
srcdir = os.path.join(sdist_out, sdist_name[: -len('.tar.gz')]) for distribution in distributions:
out = _build(isolation, srcdir, outdir, distribution, config_settings, skip_dependency_check, installer)
built.append(os.path.basename(out)) finally:
shutil.rmtree(sdist_out, ignore_errors=True) return [sdist_name, *built]
def main_parser() -> argparse.ArgumentParser: """
Construct the main parser. """
parser = argparse.ArgumentParser(
description=textwrap.indent(
textwrap.dedent( """
A simple, correct Python build frontend.
By default, a source distribution (sdist) is built from {srcdir} and a binary distribution (wheel) is built from the sdist.
This is recommended as it will ensure the sdist can be used
to build wheels.
Pass -s/--sdist and/or -w/--wheel to build a specific distribution. If you do this, the default behavior will be disabled, and all
artifacts will be built from {srcdir} (even if you combine
-w/--wheel with -s/--sdist, the wheel will be built from {srcdir}). """
).strip(), ' ',
), # Prevent argparse from taking up the entire width of the terminal window # which impedes readability.
formatter_class=partial(argparse.RawDescriptionHelpFormatter, width=min(_max_terminal_width, 127)),
)
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=f"build {build.__version__} ({','.join(build.__path__)})",
)
parser.add_argument( '--verbose', '-v',
dest='verbosity',
action='count',
default=0,
help='increase verbosity',
)
parser.add_argument( '--sdist', '-s',
dest='distributions',
action='append_const',
const='sdist',
help='build a source distribution (disables the default behavior)',
)
parser.add_argument( '--wheel', '-w',
dest='distributions',
action='append_const',
const='wheel',
help='build a wheel (disables the default behavior)',
)
parser.add_argument( '--outdir', '-o',
type=str,
help=f'output directory (defaults to {{srcdir}}{os.sep}dist)',
metavar='PATH',
)
parser.add_argument( '--skip-dependency-check', '-x',
action='store_true',
help='do not check that build dependencies are installed',
)
env_group = parser.add_mutually_exclusive_group()
env_group.add_argument( '--no-isolation', '-n',
action='store_true',
help='disable building the project in an isolated virtual environment. ' 'Build dependencies must be installed separately when this option is used',
)
env_group.add_argument( '--installer',
choices=_env.INSTALLERS,
help='Python package installer to use (defaults to pip)',
)
parser.add_argument( '--config-setting', '-C',
dest='config_settings',
action='append',
help='settings to pass to the backend. Multiple settings can be provided. ' 'Settings beginning with a hyphen will erroneously be interpreted as options to build if separated ' 'by a space character; use ``--config-setting=--my-setting -C--my-other-setting``',
metavar='KEY[=VALUE]',
) return parser
def main(cli_args: Sequence[str], prog: str | None = None) -> 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)
_setup_cli(verbosity=args.verbosity)
config_settings = {}
if args.config_settings: for arg in args.config_settings:
setting, _, value = arg.partition('=') if setting notin config_settings:
config_settings[setting] = value else: ifnot isinstance(config_settings[setting], list):
config_settings[setting] = [config_settings[setting]]
config_settings[setting].append(value)
# outdir is relative to srcdir only if omitted.
outdir = os.path.join(args.srcdir, 'dist') if args.outdir isNoneelse args.outdir
with _handle_build_error():
built = build_call(
args.srcdir,
outdir,
distributions,
config_settings, not args.no_isolation,
args.skip_dependency_check,
args.installer,
)
artifact_list = _natural_language_list(
['{underline}{}{reset}{bold}{green}'.format(artifact, **_styles.get()) for artifact in built]
)
_cprint('{bold}{green}Successfully built {}{reset}', artifact_list)
def entrypoint() -> None:
main(sys.argv[1:])
if __name__ == '__main__': # pragma: no cover
main(sys.argv[1:], 'python -m build')
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.