diff --git a/packaging/pep517_backend/_backend.py b/packaging/pep517_backend/_backend.py index 14bc911aaae..d4451ac14e0 100644 --- a/packaging/pep517_backend/_backend.py +++ b/packaging/pep517_backend/_backend.py @@ -5,8 +5,11 @@ from __future__ import annotations import os import subprocess import sys +import typing as t from configparser import ConfigParser +from contextlib import suppress from importlib.metadata import import_module +from io import StringIO from pathlib import Path from setuptools.build_meta import ( @@ -14,16 +17,25 @@ from setuptools.build_meta import ( get_requires_for_build_sdist as _setuptools_get_requires_for_build_sdist, ) +with suppress(ImportError): + # NOTE: Only available for sdist builds that bundle manpages. Declared by + # NOTE: `get_requires_for_build_sdist()` when `--build-manpages` is passed. + from docutils.core import publish_file + from docutils.writers import manpage + __all__ = ( # noqa: WPS317, WPS410 'build_sdist', 'get_requires_for_build_sdist', ) +BUILD_MANPAGES_CONFIG_SETTING = '--build-manpages' +"""Config setting name toggle that is used to request manpage in sdists.""" + + def _make_in_tree_ansible_importable() -> None: """Add the library directory to module lookup paths.""" lib_path = str(Path.cwd() / 'lib/') - os.environ['PYTHONPATH'] = lib_path # NOTE: for subprocesses sys.path.insert(0, lib_path) # NOTE: for the current runtime session @@ -35,10 +47,11 @@ def _get_package_distribution_version() -> str: cfg_version = setup_cfg.get('metadata', 'version') importable_version_str = cfg_version.removeprefix('attr: ') version_mod_str, version_var_str = importable_version_str.rsplit('.', 1) + _make_in_tree_ansible_importable() return getattr(import_module(version_mod_str), version_var_str) -def _generate_rst_in_templates() -> Path: +def _generate_rst_in_templates() -> t.Iterable[Path]: """Create ``*.1.rst.in`` files out of CLI Python modules.""" generate_man_cmd = ( sys.executable, @@ -53,41 +66,43 @@ def _generate_rst_in_templates() -> Path: return Path('docs/man/man1/').glob('*.1.rst.in') -def _convert_rst_in_template_to_manpage(rst_in, version_number) -> None: +def _convert_rst_in_template_to_manpage( + rst_doc_template: str, + destination_path: os.PathLike, + version_number: str, +) -> None: """Render pre-made ``*.1.rst.in`` templates into manpages. This includes pasting the hardcoded version into the resulting files. The resulting ``in``-files are wiped in the process. """ - templated_rst_doc = rst_in.with_suffix('') - templated_rst_doc.write_text( - rst_in.read_text().replace('%VERSION%', version_number)) + templated_rst_doc = rst_doc_template.replace('%VERSION%', version_number) - rst_in.unlink() - - rst2man_cmd = ( - sys.executable, - Path(sys.executable).parent / 'rst2man.py', - templated_rst_doc, - templated_rst_doc.with_suffix(''), - ) - subprocess.check_call(tuple(map(str, rst2man_cmd))) - templated_rst_doc.unlink() + with StringIO(templated_rst_doc) as in_mem_rst_doc: + publish_file( + source=in_mem_rst_doc, + destination_path=destination_path, + writer=manpage.Writer(), + ) def build_sdist( # noqa: WPS210, WPS430 sdist_directory: os.PathLike, config_settings: dict[str, str] | None = None, ) -> str: - build_manpages_requested = '--build-manpages' in ( + build_manpages_requested = BUILD_MANPAGES_CONFIG_SETTING in ( config_settings or {} ) if build_manpages_requested: Path('docs/man/man1/').mkdir(exist_ok=True, parents=True) - _make_in_tree_ansible_importable() version_number = _get_package_distribution_version() for rst_in in _generate_rst_in_templates(): - _convert_rst_in_template_to_manpage(rst_in, version_number) + _convert_rst_in_template_to_manpage( + rst_doc_template=rst_in.read_text(), + destination_path=rst_in.with_suffix('').with_suffix(''), + version_number=version_number, + ) + rst_in.unlink() return _setuptools_build_sdist( sdist_directory=sdist_directory, @@ -98,10 +113,17 @@ def build_sdist( # noqa: WPS210, WPS430 def get_requires_for_build_sdist( config_settings: dict[str, str] | None = None, ) -> list[str]: - return _setuptools_get_requires_for_build_sdist( - config_settings=config_settings, - ) + [ + build_manpages_requested = BUILD_MANPAGES_CONFIG_SETTING in ( + config_settings or {} + ) + build_manpages_requested = True # FIXME: Once pypa/build#559 is addressed. + + manpage_build_deps = [ 'docutils', # provides `rst2man` 'jinja2', # used in `hacking/build-ansible.py generate-man` 'pyyaml', # needed for importing in-tree `ansible-core` from `lib/` - ] + ] if build_manpages_requested else [] + + return _setuptools_get_requires_for_build_sdist( + config_settings=config_settings, + ) + manpage_build_deps