CoolFace
Apppublic

Aluode/PerceptionLabPortable

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
install_lib.py239 linesDownload Raw Back to command
1"""distutils.command.install_lib2 3Implements the Distutils 'install_lib' command4(install all Python modules)."""5 6from __future__ import annotations7 8import importlib.util9import os10import sys11from typing import Any, ClassVar12 13from ..core import Command14from ..errors import DistutilsOptionError15 16# Extension for Python source files.17PYTHON_SOURCE_EXTENSION = ".py"18 19 20class install_lib(Command):21    description = "install all Python modules (extensions and pure Python)"22 23    # The byte-compilation options are a tad confusing.  Here are the24    # possible scenarios:25    #   1) no compilation at all (--no-compile --no-optimize)26    #   2) compile .pyc only (--compile --no-optimize; default)27    #   3) compile .pyc and "opt-1" .pyc (--compile --optimize)28    #   4) compile "opt-1" .pyc only (--no-compile --optimize)29    #   5) compile .pyc and "opt-2" .pyc (--compile --optimize-more)30    #   6) compile "opt-2" .pyc only (--no-compile --optimize-more)31    #32    # The UI for this is two options, 'compile' and 'optimize'.33    # 'compile' is strictly boolean, and only decides whether to34    # generate .pyc files.  'optimize' is three-way (0, 1, or 2), and35    # decides both whether to generate .pyc files and what level of36    # optimization to use.37 38    user_options = [39        ('install-dir=', 'd', "directory to install to"),40        ('build-dir=', 'b', "build directory (where to install from)"),41        ('force', 'f', "force installation (overwrite existing files)"),42        ('compile', 'c', "compile .py to .pyc [default]"),43        ('no-compile', None, "don't compile .py files"),44        (45            'optimize=',46            'O',47            "also compile with optimization: -O1 for \"python -O\", "48            "-O2 for \"python -OO\", and -O0 to disable [default: -O0]",49        ),50        ('skip-build', None, "skip the build steps"),51    ]52 53    boolean_options: ClassVar[list[str]] = ['force', 'compile', 'skip-build']54    negative_opt: ClassVar[dict[str, str]] = {'no-compile': 'compile'}55 56    def initialize_options(self):57        # let the 'install' command dictate our installation directory58        self.install_dir = None59        self.build_dir = None60        self.force = False61        self.compile = None62        self.optimize = None63        self.skip_build = None64 65    def finalize_options(self) -> None:66        # Get all the information we need to install pure Python modules67        # from the umbrella 'install' command -- build (source) directory,68        # install (target) directory, and whether to compile .py files.69        self.set_undefined_options(70            'install',71            ('build_lib', 'build_dir'),72            ('install_lib', 'install_dir'),73            ('force', 'force'),74            ('compile', 'compile'),75            ('optimize', 'optimize'),76            ('skip_build', 'skip_build'),77        )78 79        if self.compile is None:80            self.compile = True81        if self.optimize is None:82            self.optimize = False83 84        if not isinstance(self.optimize, int):85            try:86                self.optimize = int(self.optimize)87            except ValueError:88                pass89            if self.optimize not in (0, 1, 2):90                raise DistutilsOptionError("optimize must be 0, 1, or 2")91 92    def run(self) -> None:93        # Make sure we have built everything we need first94        self.build()95 96        # Install everything: simply dump the entire contents of the build97        # directory to the installation directory (that's the beauty of98        # having a build directory!)99        outfiles = self.install()100 101        # (Optionally) compile .py to .pyc102        if outfiles is not None and self.distribution.has_pure_modules():103            self.byte_compile(outfiles)104 105    # -- Top-level worker functions ------------------------------------106    # (called from 'run()')107 108    def build(self) -> None:109        if not self.skip_build:110            if self.distribution.has_pure_modules():111                self.run_command('build_py')112            if self.distribution.has_ext_modules():113                self.run_command('build_ext')114 115    # Any: https://typing.readthedocs.io/en/latest/guides/writing_stubs.html#the-any-trick116    def install(self) -> list[str] | Any:117        if os.path.isdir(self.build_dir):118            outfiles = self.copy_tree(self.build_dir, self.install_dir)119        else:120            self.warn(121                f"'{self.build_dir}' does not exist -- no Python modules to install"122            )123            return124        return outfiles125 126    def byte_compile(self, files) -> None:127        if sys.dont_write_bytecode:128            self.warn('byte-compiling is disabled, skipping.')129            return130 131        from ..util import byte_compile132 133        # Get the "--root" directory supplied to the "install" command,134        # and use it as a prefix to strip off the purported filename135        # encoded in bytecode files.  This is far from complete, but it136        # should at least generate usable bytecode in RPM distributions.137        install_root = self.get_finalized_command('install').root138 139        if self.compile:140            byte_compile(141                files,142                optimize=0,143                force=self.force,144                prefix=install_root,145                dry_run=self.dry_run,146            )147        if self.optimize > 0:148            byte_compile(149                files,150                optimize=self.optimize,151                force=self.force,152                prefix=install_root,153                verbose=self.verbose,154                dry_run=self.dry_run,155            )156 157    # -- Utility methods -----------------------------------------------158 159    def _mutate_outputs(self, has_any, build_cmd, cmd_option, output_dir):160        if not has_any:161            return []162 163        build_cmd = self.get_finalized_command(build_cmd)164        build_files = build_cmd.get_outputs()165        build_dir = getattr(build_cmd, cmd_option)166 167        prefix_len = len(build_dir) + len(os.sep)168        outputs = [os.path.join(output_dir, file[prefix_len:]) for file in build_files]169 170        return outputs171 172    def _bytecode_filenames(self, py_filenames):173        bytecode_files = []174        for py_file in py_filenames:175            # Since build_py handles package data installation, the176            # list of outputs can contain more than just .py files.177            # Make sure we only report bytecode for the .py files.178            ext = os.path.splitext(os.path.normcase(py_file))[1]179            if ext != PYTHON_SOURCE_EXTENSION:180                continue181            if self.compile:182                bytecode_files.append(183                    importlib.util.cache_from_source(py_file, optimization='')184                )185            if self.optimize > 0:186                bytecode_files.append(187                    importlib.util.cache_from_source(188                        py_file, optimization=self.optimize189                    )190                )191 192        return bytecode_files193 194    # -- External interface --------------------------------------------195    # (called by outsiders)196 197    def get_outputs(self):198        """Return the list of files that would be installed if this command199        were actually run.  Not affected by the "dry-run" flag or whether200        modules have actually been built yet.201        """202        pure_outputs = self._mutate_outputs(203            self.distribution.has_pure_modules(),204            'build_py',205            'build_lib',206            self.install_dir,207        )208        if self.compile:209            bytecode_outputs = self._bytecode_filenames(pure_outputs)210        else:211            bytecode_outputs = []212 213        ext_outputs = self._mutate_outputs(214            self.distribution.has_ext_modules(),215            'build_ext',216            'build_lib',217            self.install_dir,218        )219 220        return pure_outputs + bytecode_outputs + ext_outputs221 222    def get_inputs(self):223        """Get the list of files that are input to this command, ie. the224        files that get installed as they are named in the build tree.225        The files in this list correspond one-to-one to the output226        filenames returned by 'get_outputs()'.227        """228        inputs = []229 230        if self.distribution.has_pure_modules():231            build_py = self.get_finalized_command('build_py')232            inputs.extend(build_py.get_outputs())233 234        if self.distribution.has_ext_modules():235            build_ext = self.get_finalized_command('build_ext')236            inputs.extend(build_ext.get_outputs())237 238        return inputs239