codekingpro/portable-devtools
115k
1"""distutils.command.install2 3Implements the Distutils 'install' command."""4 5from __future__ import annotations6 7import collections8import contextlib9import itertools10import os11import sys12import sysconfig13from distutils._log import log14from site import USER_BASE, USER_SITE15from typing import ClassVar16 17from ..core import Command18from ..debug import DEBUG19from ..errors import DistutilsOptionError, DistutilsPlatformError20from ..file_util import write_file21from ..sysconfig import get_config_vars22from ..util import change_root, convert_path, get_platform, subst_vars23from . import _framework_compat as fw24 25HAS_USER_SITE = True26 27WINDOWS_SCHEME = {28 'purelib': '{base}/Lib/site-packages',29 'platlib': '{base}/Lib/site-packages',30 'headers': '{base}/Include/{dist_name}',31 'scripts': '{base}/Scripts',32 'data': '{base}',33}34 35INSTALL_SCHEMES = {36 'posix_prefix': {37 'purelib': '{base}/lib/{implementation_lower}{py_version_short}/site-packages',38 'platlib': '{platbase}/{platlibdir}/{implementation_lower}'39 '{py_version_short}/site-packages',40 'headers': '{base}/include/{implementation_lower}'41 '{py_version_short}{abiflags}/{dist_name}',42 'scripts': '{base}/bin',43 'data': '{base}',44 },45 'posix_home': {46 'purelib': '{base}/lib/{implementation_lower}',47 'platlib': '{base}/{platlibdir}/{implementation_lower}',48 'headers': '{base}/include/{implementation_lower}/{dist_name}',49 'scripts': '{base}/bin',50 'data': '{base}',51 },52 'nt': WINDOWS_SCHEME,53 'pypy': {54 'purelib': '{base}/site-packages',55 'platlib': '{base}/site-packages',56 'headers': '{base}/include/{dist_name}',57 'scripts': '{base}/bin',58 'data': '{base}',59 },60 'pypy_nt': {61 'purelib': '{base}/site-packages',62 'platlib': '{base}/site-packages',63 'headers': '{base}/include/{dist_name}',64 'scripts': '{base}/Scripts',65 'data': '{base}',66 },67}68 69# user site schemes70if HAS_USER_SITE:71 INSTALL_SCHEMES['nt_user'] = {72 'purelib': '{usersite}',73 'platlib': '{usersite}',74 'headers': '{userbase}/{implementation}{py_version_nodot_plat}'75 '/Include/{dist_name}',76 'scripts': '{userbase}/{implementation}{py_version_nodot_plat}/Scripts',77 'data': '{userbase}',78 }79 80 INSTALL_SCHEMES['posix_user'] = {81 'purelib': '{usersite}',82 'platlib': '{usersite}',83 'headers': '{userbase}/include/{implementation_lower}'84 '{py_version_short}{abiflags}/{dist_name}',85 'scripts': '{userbase}/bin',86 'data': '{userbase}',87 }88 89 90INSTALL_SCHEMES.update(fw.schemes)91 92 93# The keys to an installation scheme; if any new types of files are to be94# installed, be sure to add an entry to every installation scheme above,95# and to SCHEME_KEYS here.96SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data')97 98 99def _load_sysconfig_schemes():100 with contextlib.suppress(AttributeError):101 return {102 scheme: sysconfig.get_paths(scheme, expand=False)103 for scheme in sysconfig.get_scheme_names()104 }105 106 107def _load_schemes():108 """109 Extend default schemes with schemes from sysconfig.110 """111 112 sysconfig_schemes = _load_sysconfig_schemes() or {}113 114 return {115 scheme: {116 **INSTALL_SCHEMES.get(scheme, {}),117 **sysconfig_schemes.get(scheme, {}),118 }119 for scheme in set(itertools.chain(INSTALL_SCHEMES, sysconfig_schemes))120 }121 122 123def _get_implementation():124 if hasattr(sys, 'pypy_version_info'):125 return 'PyPy'126 else:127 return 'Python'128 129 130def _select_scheme(ob, name):131 scheme = _inject_headers(name, _load_scheme(_resolve_scheme(name)))132 vars(ob).update(_remove_set(ob, _scheme_attrs(scheme)))133 134 135def _remove_set(ob, attrs):136 """137 Include only attrs that are None in ob.138 """139 return {key: value for key, value in attrs.items() if getattr(ob, key) is None}140 141 142def _resolve_scheme(name):143 os_name, sep, key = name.partition('_')144 try:145 resolved = sysconfig.get_preferred_scheme(key)146 except Exception:147 resolved = fw.scheme(name)148 return resolved149 150 151def _load_scheme(name):152 return _load_schemes()[name]153 154 155def _inject_headers(name, scheme):156 """157 Given a scheme name and the resolved scheme,158 if the scheme does not include headers, resolve159 the fallback scheme for the name and use headers160 from it. pypa/distutils#88161 """162 # Bypass the preferred scheme, which may not163 # have defined headers.164 fallback = _load_scheme(name)165 scheme.setdefault('headers', fallback['headers'])166 return scheme167 168 169def _scheme_attrs(scheme):170 """Resolve install directories by applying the install schemes."""171 return {f'install_{key}': scheme[key] for key in SCHEME_KEYS}172 173 174class install(Command):175 description = "install everything from build directory"176 177 user_options = [178 # Select installation scheme and set base director(y|ies)179 ('prefix=', None, "installation prefix"),180 ('exec-prefix=', None, "(Unix only) prefix for platform-specific files"),181 ('home=', None, "(Unix only) home directory to install under"),182 # Or, just set the base director(y|ies)183 (184 'install-base=',185 None,186 "base installation directory (instead of --prefix or --home)",187 ),188 (189 'install-platbase=',190 None,191 "base installation directory for platform-specific files (instead of --exec-prefix or --home)",192 ),193 ('root=', None, "install everything relative to this alternate root directory"),194 # Or, explicitly set the installation scheme195 (196 'install-purelib=',197 None,198 "installation directory for pure Python module distributions",199 ),200 (201 'install-platlib=',202 None,203 "installation directory for non-pure module distributions",204 ),205 (206 'install-lib=',207 None,208 "installation directory for all module distributions (overrides --install-purelib and --install-platlib)",209 ),210 ('install-headers=', None, "installation directory for C/C++ headers"),211 ('install-scripts=', None, "installation directory for Python scripts"),212 ('install-data=', None, "installation directory for data files"),213 # Byte-compilation options -- see install_lib.py for details, as214 # these are duplicated from there (but only install_lib does215 # anything with them).216 ('compile', 'c', "compile .py to .pyc [default]"),217 ('no-compile', None, "don't compile .py files"),218 (219 'optimize=',220 'O',221 "also compile with optimization: -O1 for \"python -O\", "222 "-O2 for \"python -OO\", and -O0 to disable [default: -O0]",223 ),224 # Miscellaneous control options225 ('force', 'f', "force installation (overwrite any existing files)"),226 ('skip-build', None, "skip rebuilding everything (for testing/debugging)"),227 # Where to install documentation (eventually!)228 # ('doc-format=', None, "format of documentation to generate"),229 # ('install-man=', None, "directory for Unix man pages"),230 # ('install-html=', None, "directory for HTML documentation"),231 # ('install-info=', None, "directory for GNU info files"),232 ('record=', None, "filename in which to record list of installed files"),233 ]234 235 boolean_options: ClassVar[list[str]] = ['compile', 'force', 'skip-build']236 237 if HAS_USER_SITE:238 user_options.append((239 'user',240 None,241 f"install in user site-package '{USER_SITE}'",242 ))243 boolean_options.append('user')244 245 negative_opt: ClassVar[dict[str, str]] = {'no-compile': 'compile'}246 247 def initialize_options(self) -> None:248 """Initializes options."""249 # High-level options: these select both an installation base250 # and scheme.251 self.prefix: str | None = None252 self.exec_prefix: str | None = None253 self.home: str | None = None254 self.user = False255 256 # These select only the installation base; it's up to the user to257 # specify the installation scheme (currently, that means supplying258 # the --install-{platlib,purelib,scripts,data} options).259 self.install_base = None260 self.install_platbase = None261 self.root: str | None = None262 263 # These options are the actual installation directories; if not264 # supplied by the user, they are filled in using the installation265 # scheme implied by prefix/exec-prefix/home and the contents of266 # that installation scheme.267 self.install_purelib = None # for pure module distributions268 self.install_platlib = None # non-pure (dists w/ extensions)269 self.install_headers = None # for C/C++ headers270 self.install_lib: str | None = None # set to either purelib or platlib271 self.install_scripts = None272 self.install_data = None273 self.install_userbase = USER_BASE274 self.install_usersite = USER_SITE275 276 self.compile = None277 self.optimize = None278 279 # Deprecated280 # These two are for putting non-packagized distributions into their281 # own directory and creating a .pth file if it makes sense.282 # 'extra_path' comes from the setup file; 'install_path_file' can283 # be turned off if it makes no sense to install a .pth file. (But284 # better to install it uselessly than to guess wrong and not285 # install it when it's necessary and would be used!) Currently,286 # 'install_path_file' is always true unless some outsider meddles287 # with it.288 self.extra_path = None289 self.install_path_file = True290 291 # 'force' forces installation, even if target files are not292 # out-of-date. 'skip_build' skips running the "build" command,293 # handy if you know it's not necessary. 'warn_dir' (which is *not*294 # a user option, it's just there so the bdist_* commands can turn295 # it off) determines whether we warn about installing to a296 # directory not in sys.path.297 self.force = False298 self.skip_build = False299 self.warn_dir = True300 301 # These are only here as a conduit from the 'build' command to the302 # 'install_*' commands that do the real work. ('build_base' isn't303 # actually used anywhere, but it might be useful in future.) They304 # are not user options, because if the user told the install305 # command where the build directory is, that wouldn't affect the306 # build command.307 self.build_base = None308 self.build_lib = None309 310 # Not defined yet because we don't know anything about311 # documentation yet.312 # self.install_man = None313 # self.install_html = None314 # self.install_info = None315 316 self.record = None317 318 # -- Option finalizing methods -------------------------------------319 # (This is rather more involved than for most commands,320 # because this is where the policy for installing third-321 # party Python modules on various platforms given a wide322 # array of user input is decided. Yes, it's quite complex!)323 324 def finalize_options(self) -> None: # noqa: C901325 """Finalizes options."""326 # This method (and its helpers, like 'finalize_unix()',327 # 'finalize_other()', and 'select_scheme()') is where the default328 # installation directories for modules, extension modules, and329 # anything else we care to install from a Python module330 # distribution. Thus, this code makes a pretty important policy331 # statement about how third-party stuff is added to a Python332 # installation! Note that the actual work of installation is done333 # by the relatively simple 'install_*' commands; they just take334 # their orders from the installation directory options determined335 # here.336 337 # Check for errors/inconsistencies in the options; first, stuff338 # that's wrong on any platform.339 340 if (self.prefix or self.exec_prefix or self.home) and (341 self.install_base or self.install_platbase342 ):343 raise DistutilsOptionError(344 "must supply either prefix/exec-prefix/home or install-base/install-platbase -- not both"345 )346 347 if self.home and (self.prefix or self.exec_prefix):348 raise DistutilsOptionError(349 "must supply either home or prefix/exec-prefix -- not both"350 )351 352 if self.user and (353 self.prefix354 or self.exec_prefix355 or self.home356 or self.install_base357 or self.install_platbase358 ):359 raise DistutilsOptionError(360 "can't combine user with prefix, "361 "exec_prefix/home, or install_(plat)base"362 )363 364 # Next, stuff that's wrong (or dubious) only on certain platforms.365 if os.name != "posix":366 if self.exec_prefix:367 self.warn("exec-prefix option ignored on this platform")368 self.exec_prefix = None369 370 # Now the interesting logic -- so interesting that we farm it out371 # to other methods. The goal of these methods is to set the final372 # values for the install_{lib,scripts,data,...} options, using as373 # input a heady brew of prefix, exec_prefix, home, install_base,374 # install_platbase, user-supplied versions of375 # install_{purelib,platlib,lib,scripts,data,...}, and the376 # install schemes. Phew!377 378 self.dump_dirs("pre-finalize_{unix,other}")379 380 if os.name == 'posix':381 self.finalize_unix()382 else:383 self.finalize_other()384 385 self.dump_dirs("post-finalize_{unix,other}()")386 387 # Expand configuration variables, tilde, etc. in self.install_base388 # and self.install_platbase -- that way, we can use $base or389 # $platbase in the other installation directories and not worry390 # about needing recursive variable expansion (shudder).391 392 py_version = sys.version.split()[0]393 (prefix, exec_prefix) = get_config_vars('prefix', 'exec_prefix')394 try:395 abiflags = sys.abiflags396 except AttributeError:397 # sys.abiflags may not be defined on all platforms.398 abiflags = ''399 local_vars = {400 'dist_name': self.distribution.get_name(),401 'dist_version': self.distribution.get_version(),402 'dist_fullname': self.distribution.get_fullname(),403 'py_version': py_version,404 'py_version_short': f'{sys.version_info.major}.{sys.version_info.minor}',405 'py_version_nodot': f'{sys.version_info.major}{sys.version_info.minor}',406 'sys_prefix': prefix,407 'prefix': prefix,408 'sys_exec_prefix': exec_prefix,409 'exec_prefix': exec_prefix,410 'abiflags': abiflags,411 'platlibdir': getattr(sys, 'platlibdir', 'lib'),412 'implementation_lower': _get_implementation().lower(),413 'implementation': _get_implementation(),414 }415 416 # vars for compatibility on older Pythons417 compat_vars = dict(418 # Python 3.9 and earlier419 py_version_nodot_plat=getattr(sys, 'winver', '').replace('.', ''),420 )421 422 if HAS_USER_SITE:423 local_vars['userbase'] = self.install_userbase424 local_vars['usersite'] = self.install_usersite425 426 self.config_vars = collections.ChainMap(427 local_vars,428 sysconfig.get_config_vars(),429 compat_vars,430 fw.vars(),431 )432 433 self.expand_basedirs()434 435 self.dump_dirs("post-expand_basedirs()")436 437 # Now define config vars for the base directories so we can expand438 # everything else.439 local_vars['base'] = self.install_base440 local_vars['platbase'] = self.install_platbase441 442 if DEBUG:443 from pprint import pprint444 445 print("config vars:")446 pprint(dict(self.config_vars))447 448 # Expand "~" and configuration variables in the installation449 # directories.450 self.expand_dirs()451 452 self.dump_dirs("post-expand_dirs()")453 454 # Create directories in the home dir:455 if self.user:456 self.create_home_path()457 458 # Pick the actual directory to install all modules to: either459 # install_purelib or install_platlib, depending on whether this460 # module distribution is pure or not. Of course, if the user461 # already specified install_lib, use their selection.462 if self.install_lib is None:463 if self.distribution.has_ext_modules(): # has extensions: non-pure464 self.install_lib = self.install_platlib465 else:466 self.install_lib = self.install_purelib467 468 # Convert directories from Unix /-separated syntax to the local469 # convention.470 self.convert_paths(471 'lib',472 'purelib',473 'platlib',474 'scripts',475 'data',476 'headers',477 'userbase',478 'usersite',479 )480 481 # Deprecated482 # Well, we're not actually fully completely finalized yet: we still483 # have to deal with 'extra_path', which is the hack for allowing484 # non-packagized module distributions (hello, Numerical Python!) to485 # get their own directories.486 self.handle_extra_path()487 self.install_libbase = self.install_lib # needed for .pth file488 self.install_lib = os.path.join(self.install_lib, self.extra_dirs)489 490 # If a new root directory was supplied, make all the installation491 # dirs relative to it.492 if self.root is not None:493 self.change_roots(494 'libbase', 'lib', 'purelib', 'platlib', 'scripts', 'data', 'headers'495 )496 497 self.dump_dirs("after prepending root")498 499 # Find out the build directories, ie. where to install from.500 self.set_undefined_options(501 'build', ('build_base', 'build_base'), ('build_lib', 'build_lib')502 )503 504 # Punt on doc directories for now -- after all, we're punting on505 # documentation completely!506 507 def dump_dirs(self, msg) -> None:508 """Dumps the list of user options."""509 if not DEBUG:510 return511 from ..fancy_getopt import longopt_xlate512 513 log.debug(msg + ":")514 for opt in self.user_options:515 opt_name = opt[0]516 if opt_name[-1] == "=":517 opt_name = opt_name[0:-1]518 if opt_name in self.negative_opt:519 opt_name = self.negative_opt[opt_name]520 opt_name = opt_name.translate(longopt_xlate)521 val = not getattr(self, opt_name)522 else:523 opt_name = opt_name.translate(longopt_xlate)524 val = getattr(self, opt_name)525 log.debug(" %s: %s", opt_name, val)526 527 def finalize_unix(self) -> None:528 """Finalizes options for posix platforms."""529 if self.install_base is not None or self.install_platbase is not None:530 incomplete_scheme = (531 (532 self.install_lib is None533 and self.install_purelib is None534 and self.install_platlib is None535 )536 or self.install_headers is None537 or self.install_scripts is None538 or self.install_data is None539 )540 if incomplete_scheme:541 raise DistutilsOptionError(542 "install-base or install-platbase supplied, but "543 "installation scheme is incomplete"544 )545 return546 547 if self.user:548 if self.install_userbase is None:549 raise DistutilsPlatformError("User base directory is not specified")550 self.install_base = self.install_platbase = self.install_userbase551 self.select_scheme("posix_user")552 elif self.home is not None:553 self.install_base = self.install_platbase = self.home554 self.select_scheme("posix_home")555 else:556 if self.prefix is None:557 if self.exec_prefix is not None:558 raise DistutilsOptionError(559 "must not supply exec-prefix without prefix"560 )561 562 # Allow Fedora to add components to the prefix563 _prefix_addition = getattr(sysconfig, '_prefix_addition', "")564 565 self.prefix = os.path.normpath(sys.prefix) + _prefix_addition566 self.exec_prefix = os.path.normpath(sys.exec_prefix) + _prefix_addition567 568 else:569 if self.exec_prefix is None:570 self.exec_prefix = self.prefix571 572 self.install_base = self.prefix573 self.install_platbase = self.exec_prefix574 self.select_scheme("posix_prefix")575 576 def finalize_other(self) -> None:577 """Finalizes options for non-posix platforms"""578 if self.user:579 if self.install_userbase is None:580 raise DistutilsPlatformError("User base directory is not specified")581 self.install_base = self.install_platbase = self.install_userbase582 self.select_scheme(os.name + "_user")583 elif self.home is not None:584 self.install_base = self.install_platbase = self.home585 self.select_scheme("posix_home")586 else:587 if self.prefix is None:588 self.prefix = os.path.normpath(sys.prefix)589 590 self.install_base = self.install_platbase = self.prefix591 try:592 self.select_scheme(os.name)593 except KeyError:594 raise DistutilsPlatformError(595 f"I don't know how to install stuff on '{os.name}'"596 )597 598 def select_scheme(self, name) -> None:599 _select_scheme(self, name)600 601 def _expand_attrs(self, attrs):602 for attr in attrs:603 val = getattr(self, attr)604 if val is not None:605 if os.name in ('posix', 'nt'):606 val = os.path.expanduser(val)607 val = subst_vars(val, self.config_vars)608 setattr(self, attr, val)609 610 def expand_basedirs(self) -> None:611 """Calls `os.path.expanduser` on install_base, install_platbase and612 root."""613 self._expand_attrs(['install_base', 'install_platbase', 'root'])614 615 def expand_dirs(self) -> None:616 """Calls `os.path.expanduser` on install dirs."""617 self._expand_attrs([618 'install_purelib',619 'install_platlib',620 'install_lib',621 'install_headers',622 'install_scripts',623 'install_data',624 ])625 626 def convert_paths(self, *names) -> None:627 """Call `convert_path` over `names`."""628 for name in names:629 attr = "install_" + name630 setattr(self, attr, convert_path(getattr(self, attr)))631 632 def handle_extra_path(self) -> None:633 """Set `path_file` and `extra_dirs` using `extra_path`."""634 if self.extra_path is None:635 self.extra_path = self.distribution.extra_path636 637 if self.extra_path is not None:638 log.warning(639 "Distribution option extra_path is deprecated. "640 "See issue27919 for details."641 )642 if isinstance(self.extra_path, str):643 self.extra_path = self.extra_path.split(',')644 645 if len(self.extra_path) == 1:646 path_file = extra_dirs = self.extra_path[0]647 elif len(self.extra_path) == 2:648 path_file, extra_dirs = self.extra_path649 else:650 raise DistutilsOptionError(651 "'extra_path' option must be a list, tuple, or "652 "comma-separated string with 1 or 2 elements"653 )654 655 # convert to local form in case Unix notation used (as it656 # should be in setup scripts)657 extra_dirs = convert_path(extra_dirs)658 else:659 path_file = None660 extra_dirs = ''661 662 # XXX should we warn if path_file and not extra_dirs? (in which663 # case the path file would be harmless but pointless)664 self.path_file = path_file665 self.extra_dirs = extra_dirs666 667 def change_roots(self, *names) -> None:668 """Change the install directories pointed by name using root."""669 for name in names:670 attr = "install_" + name671 setattr(self, attr, change_root(self.root, getattr(self, attr)))672 673 def create_home_path(self) -> None:674 """Create directories under ~."""675 if not self.user:676 return677 home = convert_path(os.path.expanduser("~"))678 for path in self.config_vars.values():679 if str(path).startswith(home) and not os.path.isdir(path):680 self.debug_print(f"os.makedirs('{path}', 0o700)")681 os.makedirs(path, 0o700)682 683 # -- Command execution methods -------------------------------------684 685 def run(self):686 """Runs the command."""687 # Obviously have to build before we can install688 if not self.skip_build:689 self.run_command('build')690 # If we built for any other platform, we can't install.691 build_plat = self.distribution.get_command_obj('build').plat_name692 # check warn_dir - it is a clue that the 'install' is happening693 # internally, and not to sys.path, so we don't check the platform694 # matches what we are running.695 if self.warn_dir and build_plat != get_platform():696 raise DistutilsPlatformError("Can't install when cross-compiling")697 698 # Run all sub-commands (at least those that need to be run)699 for cmd_name in self.get_sub_commands():700 self.run_command(cmd_name)701 702 if self.path_file:703 self.create_path_file()704 705 # write list of installed files, if requested.706 if self.record:707 outputs = self.get_outputs()708 if self.root: # strip any package prefix709 root_len = len(self.root)710 for counter in range(len(outputs)):711 outputs[counter] = outputs[counter][root_len:]712 self.execute(713 write_file,714 (self.record, outputs),715 f"writing list of installed files to '{self.record}'",716 )717 718 sys_path = map(os.path.normpath, sys.path)719 sys_path = map(os.path.normcase, sys_path)720 install_lib = os.path.normcase(os.path.normpath(self.install_lib))721 if (722 self.warn_dir723 and not (self.path_file and self.install_path_file)724 and install_lib not in sys_path725 ):726 log.debug(727 (728 "modules installed to '%s', which is not in "729 "Python's module search path (sys.path) -- "730 "you'll have to change the search path yourself"731 ),732 self.install_lib,733 )734 735 def create_path_file(self):736 """Creates the .pth file"""737 filename = os.path.join(self.install_libbase, self.path_file + ".pth")738 if self.install_path_file:739 self.execute(740 write_file, (filename, [self.extra_dirs]), f"creating {filename}"741 )742 else:743 self.warn(f"path file '{filename}' not created")744 745 # -- Reporting methods ---------------------------------------------746 747 def get_outputs(self):748 """Assembles the outputs of all the sub-commands."""749 outputs = []750 for cmd_name in self.get_sub_commands():751 cmd = self.get_finalized_command(cmd_name)752 # Add the contents of cmd.get_outputs(), ensuring753 # that outputs doesn't contain duplicate entries754 for filename in cmd.get_outputs():755 if filename not in outputs:756 outputs.append(filename)757 758 if self.path_file and self.install_path_file:759 outputs.append(os.path.join(self.install_libbase, self.path_file + ".pth"))760 761 return outputs762 763 def get_inputs(self):764 """Returns the inputs of all the sub-commands"""765 # XXX gee, this looks familiar ;-(766 inputs = []767 for cmd_name in self.get_sub_commands():768 cmd = self.get_finalized_command(cmd_name)769 inputs.extend(cmd.get_inputs())770 771 return inputs772 773 # -- Predicates for sub-command list -------------------------------774 775 def has_lib(self):776 """Returns true if the current distribution has any Python777 modules to install."""778 return (779 self.distribution.has_pure_modules() or self.distribution.has_ext_modules()780 )781 782 def has_headers(self):783 """Returns true if the current distribution has any headers to784 install."""785 return self.distribution.has_headers()786 787 def has_scripts(self):788 """Returns true if the current distribution has any scripts to.789 install."""790 return self.distribution.has_scripts()791 792 def has_data(self):793 """Returns true if the current distribution has any data to.794 install."""795 return self.distribution.has_data_files()796 797 # 'sub_commands': a list of commands this command might have to run to798 # get its work done. See cmd.py for more info.799 sub_commands = [800 ('install_lib', has_lib),801 ('install_headers', has_headers),802 ('install_scripts', has_scripts),803 ('install_data', has_data),804 ('install_egg_info', lambda self: True),805 ]806 