5 A tool for doing automatic download/extract/build of distutils-based Python
6 packages. For detailed documentation, see the accompanying EasyInstall.txt
7 file, or visit the `EasyInstall home page`__.
9 __ https://setuptools.pypa.io/en/latest/deprecated/easy_install.html
14 from distutils
.util
import get_platform
15 from distutils
.util
import convert_path
, subst_vars
16 from distutils
.errors
import (
17 DistutilsArgError
, DistutilsOptionError
,
18 DistutilsError
, DistutilsPlatformError
,
20 from distutils
import log
, dir_util
21 from distutils
.command
.build_scripts
import first_line_re
22 from distutils
.spawn
import find_executable
23 from distutils
.command
import install
45 from sysconfig
import get_path
47 from setuptools
import SetuptoolsDeprecationWarning
49 from setuptools
import Command
50 from setuptools
.sandbox
import run_setup
51 from setuptools
.command
import setopt
52 from setuptools
.archive_util
import unpack_archive
53 from setuptools
.package_index
import (
54 PackageIndex
, parse_requirement_arg
, URL_SCHEME
,
56 from setuptools
.command
import bdist_egg
, egg_info
57 from setuptools
.wheel
import Wheel
58 from pkg_resources
import (
59 normalize_path
, resource_string
,
60 get_distribution
, find_distributions
, Environment
, Requirement
,
61 Distribution
, PathMetadata
, EggMetadata
, WorkingSet
, DistributionNotFound
,
62 VersionConflict
, DEVELOP_DIST
,
65 from .._path
import ensure_directory
66 from ..extern
.jaraco
.text
import yield_lines
69 # Turn on PEP440Warnings
70 warnings
.filterwarnings("default", category
=pkg_resources
.PEP440Warning
)
73 'easy_install', 'PthDistributions', 'extract_wininst_cfg',
79 return struct
.calcsize("P") == 8
83 return s
.encode('utf8')
95 return textwrap
.dedent(text
).strip().replace('\n', '; ')
98 class easy_install(Command
):
99 """Manage a download/build/install process"""
100 description
= "Find/get/install Python packages"
101 command_consumes_arguments
= True
104 ('prefix=', None, "installation prefix"),
105 ("zip-ok", "z", "install package as a zipfile"),
106 ("multi-version", "m", "make apps have to require() a version"),
107 ("upgrade", "U", "force upgrade (searches PyPI for latest versions)"),
108 ("install-dir=", "d", "install package to DIR"),
109 ("script-dir=", "s", "install scripts to DIR"),
110 ("exclude-scripts", "x", "Don't install scripts"),
111 ("always-copy", "a", "Copy all needed packages to install dir"),
112 ("index-url=", "i", "base URL of Python Package Index"),
113 ("find-links=", "f", "additional URL(s) to search for packages"),
114 ("build-directory=", "b",
115 "download/extract/build in DIR; keep the results"),
117 "also compile with optimization: -O1 for \"python -O\", "
118 "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),
120 "filename in which to record list of installed files"),
121 ('always-unzip', 'Z', "don't install as a zipfile, no matter what"),
122 ('site-dirs=', 'S', "list of directories where .pth files work"),
123 ('editable', 'e', "Install specified packages in editable form"),
124 ('no-deps', 'N', "don't install dependencies"),
125 ('allow-hosts=', 'H', "pattern(s) that hostnames must match"),
126 ('local-snapshots-ok', 'l',
127 "allow building eggs from local checkouts"),
128 ('version', None, "print version information and exit"),
129 ('install-layout=', None, "installation layout to choose (known values: deb)"),
130 ('force-installation-into-system-dir', '0', "force installation into /usr"),
131 ('no-find-links', None,
132 "Don't load find-links defined in packages being installed"),
133 ('user', None, "install in user site-package '%s'" % site
.USER_SITE
)
136 'zip-ok', 'multi-version', 'exclude-scripts', 'upgrade', 'always-copy',
138 'no-deps', 'local-snapshots-ok', 'version', 'force-installation-into-system-dir'
142 negative_opt
= {'always-unzip': 'zip-ok'}
143 create_index
= PackageIndex
145 def initialize_options(self
):
147 "easy_install command is deprecated. "
148 "Use build and pip and other standards-based tools.",
149 EasyInstallDeprecationWarning
,
152 # the --user option seems to be an opt-in one,
153 # so the default should be False.
155 self
.zip_ok
= self
.local_snapshots_ok
= None
156 self
.install_dir
= self
.script_dir
= self
.exclude_scripts
= None
157 self
.index_url
= None
158 self
.find_links
= None
159 self
.build_directory
= None
161 self
.optimize
= self
.record
= None
162 self
.upgrade
= self
.always_copy
= self
.multi_version
= None
163 self
.editable
= self
.no_deps
= self
.allow_hosts
= None
164 self
.root
= self
.prefix
= self
.no_report
= None
166 self
.install_purelib
= None # for pure module distributions
167 self
.install_platlib
= None # non-pure (dists w/ extensions)
168 self
.install_headers
= None # for C/C++ headers
169 self
.install_lib
= None # set to either purelib or platlib
170 self
.install_scripts
= None
171 self
.install_data
= None
172 self
.install_base
= None
173 self
.install_platbase
= None
174 self
.install_userbase
= site
.USER_BASE
175 self
.install_usersite
= site
.USER_SITE
176 self
.no_find_links
= None
178 # Options not specifiable via command line
179 self
.package_index
= None
180 self
.pth_file
= self
.always_copy_from
= None
181 self
.site_dirs
= None
182 self
.installed_projects
= {}
183 # enable custom installation, known values: deb
184 self
.install_layout
= None
185 self
.force_installation_into_system_dir
= None
186 self
.multiarch
= None
188 # Always read easy_install options, even if we are subclassed, or have
189 # an independent instance created. This ensures that defaults will
190 # always come from the standard configuration file(s)' "easy_install"
191 # section, even if this is a "develop" or "install" command, or some
194 self
.verbose
= self
.distribution
.verbose
195 self
.distribution
._set
_command
_options
(
196 self
, self
.distribution
.get_option_dict('easy_install')
199 def delete_blockers(self
, blockers
):
201 filename
for filename
in blockers
202 if os
.path
.exists(filename
) or os
.path
.islink(filename
)
204 list(map(self
._delete
_path
, extant_blockers
))
206 def _delete_path(self
, path
):
207 log
.info("Deleting %s", path
)
211 is_tree
= os
.path
.isdir(path
) and not os
.path
.islink(path
)
212 remover
= rmtree
if is_tree
else os
.unlink
216 def _render_version():
218 Render the Setuptools version and installation details, then exit.
220 ver
= '{}.{}'.format(*sys
.version_info
)
221 dist
= get_distribution('setuptools')
222 tmpl
= 'setuptools {dist.version} from {dist.location} (Python {ver})'
223 print(tmpl
.format(**locals()))
226 def finalize_options(self
): # noqa: C901 # is too complex (25) # FIXME
227 self
.version
and self
._render
_version
()
229 py_version
= sys
.version
.split()[0]
231 self
.config_vars
= dict(sysconfig
.get_config_vars())
233 self
.config_vars
.update({
234 'dist_name': self
.distribution
.get_name(),
235 'dist_version': self
.distribution
.get_version(),
236 'dist_fullname': self
.distribution
.get_fullname(),
237 'py_version': py_version
,
238 'py_version_short': f
'{sys.version_info.major}.{sys.version_info.minor}',
239 'py_version_nodot': f
'{sys.version_info.major}{sys.version_info.minor}',
240 'sys_prefix': self
.config_vars
['prefix'],
241 'sys_exec_prefix': self
.config_vars
['exec_prefix'],
242 # Only python 3.2+ has abiflags
243 'abiflags': getattr(sys
, 'abiflags', ''),
244 'platlibdir': getattr(sys
, 'platlibdir', 'lib'),
246 with contextlib
.suppress(AttributeError):
247 # only for distutils outside stdlib
248 self
.config_vars
.update({
249 'implementation_lower': install
._get
_implementation
().lower(),
250 'implementation': install
._get
_implementation
(),
253 # pypa/distutils#113 Python 3.9 compat
254 self
.config_vars
.setdefault(
255 'py_version_nodot_plat',
256 getattr(sys
, 'windir', '').replace('.', ''),
259 self
.config_vars
['userbase'] = self
.install_userbase
260 self
.config_vars
['usersite'] = self
.install_usersite
261 if self
.user
and not site
.ENABLE_USER_SITE
:
262 log
.warn("WARNING: The user site-packages directory is disabled.")
264 self
._fix
_install
_dir
_for
_user
_site
()
266 self
.expand_basedirs()
269 if self
.install_layout
:
270 if not self
.install_layout
.lower() in ['deb']:
271 raise DistutilsOptionError("unknown value for --install-layout")
272 self
.install_layout
= self
.install_layout
.lower()
274 if sys
.version_info
[:2] >= (3, 3):
275 self
.multiarch
= sysconfig
.get_config_var('MULTIARCH')
278 'install_dir', 'script_dir', 'build_directory',
281 # If a non-default installation directory was specified, default the
282 # script directory to match it.
283 if self
.script_dir
is None:
284 self
.script_dir
= self
.install_dir
286 if self
.no_find_links
is None:
287 self
.no_find_links
= False
289 # Let install_dir get set by install_lib command, which in turn
290 # gets its info from the install command, and takes into account
291 # --prefix and --home and all that other crud.
292 self
.set_undefined_options(
293 'install_lib', ('install_dir', 'install_dir')
295 # Likewise, set default script_dir from 'install_scripts.install_dir'
296 self
.set_undefined_options(
297 'install_scripts', ('install_dir', 'script_dir')
300 if self
.user
and self
.install_purelib
:
301 self
.install_dir
= self
.install_purelib
302 self
.script_dir
= self
.install_scripts
304 if self
.prefix
== '/usr' and not self
.force_installation_into_system_dir
:
305 raise DistutilsOptionError("""installation into /usr
307 Trying to install into the system managed parts of the file system. Please
308 consider to install to another location, or use the option
309 --force-installation-into-system-dir to overwrite this warning.
312 # default --record from the install command
313 self
.set_undefined_options('install', ('record', 'record'))
314 self
.all_site_dirs
= get_site_dirs()
315 self
.all_site_dirs
.extend(self
._process
_site
_dirs
(self
.site_dirs
))
317 if not self
.editable
:
318 self
.check_site_dir()
319 default_index
= os
.getenv("__EASYINSTALL_INDEX", "https://pypi.org/simple/")
320 # ^ Private API for testing purposes only
321 self
.index_url
= self
.index_url
or default_index
322 self
.shadow_path
= self
.all_site_dirs
[:]
323 for path_item
in self
.install_dir
, normalize_path(self
.script_dir
):
324 if path_item
not in self
.shadow_path
:
325 self
.shadow_path
.insert(0, path_item
)
327 if self
.allow_hosts
is not None:
328 hosts
= [s
.strip() for s
in self
.allow_hosts
.split(',')]
331 if self
.package_index
is None:
332 self
.package_index
= self
.create_index(
333 self
.index_url
, search_path
=self
.shadow_path
, hosts
=hosts
,
335 self
.local_index
= Environment(self
.shadow_path
+ sys
.path
)
337 if self
.find_links
is not None:
338 if isinstance(self
.find_links
, str):
339 self
.find_links
= self
.find_links
.split()
342 if self
.local_snapshots_ok
:
343 self
.package_index
.scan_egg_links(self
.shadow_path
+ sys
.path
)
344 if not self
.no_find_links
:
345 self
.package_index
.add_find_links(self
.find_links
)
346 self
.set_undefined_options('install_lib', ('optimize', 'optimize'))
347 self
.optimize
= self
._validate
_optimize
(self
.optimize
)
349 if self
.editable
and not self
.build_directory
:
350 raise DistutilsArgError(
351 "Must specify a build directory (-b) when using --editable"
354 raise DistutilsArgError(
355 "No urls, filenames, or requirements specified (see --help)")
360 def _process_site_dirs(site_dirs
):
361 if site_dirs
is None:
364 normpath
= map(normalize_path
, sys
.path
)
366 os
.path
.expanduser(s
.strip()) for s
in
370 if not os
.path
.isdir(d
):
371 log
.warn("%s (in --site-dirs) does not exist", d
)
372 elif normalize_path(d
) not in normpath
:
373 raise DistutilsOptionError(
374 d
+ " (in --site-dirs) is not on sys.path"
377 yield normalize_path(d
)
380 def _validate_optimize(value
):
383 if value
not in range(3):
385 except ValueError as e
:
386 raise DistutilsOptionError(
387 "--optimize must be 0, 1, or 2"
392 def _fix_install_dir_for_user_site(self
):
394 Fix the install_dir if "--user" was used.
399 self
.create_home_path()
400 if self
.install_userbase
is None:
401 msg
= "User base directory is not specified"
402 raise DistutilsPlatformError(msg
)
403 self
.install_base
= self
.install_platbase
= self
.install_userbase
404 scheme_name
= f
'{os.name}_user'
405 self
.select_scheme(scheme_name
)
407 def _expand_attrs(self
, attrs
):
409 val
= getattr(self
, attr
)
411 if os
.name
== 'posix' or os
.name
== 'nt':
412 val
= os
.path
.expanduser(val
)
413 val
= subst_vars(val
, self
.config_vars
)
414 setattr(self
, attr
, val
)
416 def expand_basedirs(self
):
417 """Calls `os.path.expanduser` on install_base, install_platbase and
419 self
._expand
_attrs
(['install_base', 'install_platbase', 'root'])
421 def expand_dirs(self
):
422 """Calls `os.path.expanduser` on install dirs."""
431 self
._expand
_attrs
(dirs
)
433 def run(self
, show_deprecation
=True):
436 "WARNING: The easy_install command is deprecated "
437 "and will be removed in a future version.",
440 if self
.verbose
!= self
.distribution
.verbose
:
441 log
.set_verbosity(self
.verbose
)
443 for spec
in self
.args
:
444 self
.easy_install(spec
, not self
.no_deps
)
446 outputs
= list(sorted(self
.outputs
))
447 if self
.root
: # strip any package prefix
448 root_len
= len(self
.root
)
449 for counter
in range(len(outputs
)):
450 outputs
[counter
] = outputs
[counter
][root_len
:]
451 from distutils
import file_util
454 file_util
.write_file
, (self
.record
, outputs
),
455 "writing list of installed files to '%s'" %
458 self
.warn_deprecated_options()
460 log
.set_verbosity(self
.distribution
.verbose
)
462 def pseudo_tempname(self
):
463 """Return a pseudo-tempname base in the install directory.
464 This code is intentionally naive; if a malicious party can write to
465 the target directory you're already in deep doodoo.
470 pid
= random
.randint(0, sys
.maxsize
)
471 return os
.path
.join(self
.install_dir
, "test-easy-install-%s" % pid
)
473 def warn_deprecated_options(self
):
476 def check_site_dir(self
): # noqa: C901 # is too complex (12) # FIXME
477 """Verify that self.install_dir is .pth-capable dir, if needed"""
479 instdir
= normalize_path(self
.install_dir
)
480 pth_file
= os
.path
.join(instdir
, 'easy-install.pth')
482 if not os
.path
.exists(instdir
):
485 except (OSError, IOError):
486 self
.cant_write_to_target()
488 # Is it a configured, PYTHONPATH, implicit, or explicit site dir?
489 is_site_dir
= instdir
in self
.all_site_dirs
491 if not is_site_dir
and not self
.multi_version
:
492 # No? Then directly test whether it does .pth file processing
493 is_site_dir
= self
.check_pth_processing()
495 # make sure we can write to target dir
496 testfile
= self
.pseudo_tempname() + '.write-test'
497 test_exists
= os
.path
.exists(testfile
)
501 open(testfile
, 'w').close()
503 except (OSError, IOError):
504 self
.cant_write_to_target()
506 if not is_site_dir
and not self
.multi_version
:
507 # Can't install non-multi to non-site dir with easy_install
508 pythonpath
= os
.environ
.get('PYTHONPATH', '')
509 log
.warn(self
.__no
_default
_msg
, self
.install_dir
, pythonpath
)
512 if self
.pth_file
is None:
513 self
.pth_file
= PthDistributions(pth_file
, self
.all_site_dirs
)
517 if self
.multi_version
and not os
.path
.exists(pth_file
):
518 self
.pth_file
= None # don't create a .pth file
519 self
.install_dir
= instdir
521 __cant_write_msg
= textwrap
.dedent("""
522 can't create or remove files in install directory
524 The following error occurred while trying to add or remove files in the
525 installation directory:
529 The installation directory you specified (via --install-dir, --prefix, or
530 the distutils default setting) was:
535 __not_exists_id
= textwrap
.dedent("""
536 This directory does not currently exist. Please create it and try again, or
537 choose a different installation directory (using the -d or --install-dir
541 __access_msg
= textwrap
.dedent("""
542 Perhaps your account does not have write access to this directory? If the
543 installation directory is a system-owned directory, you may need to sign in
544 as the administrator or "root" account. If you do not have administrative
545 access to this machine, you may wish to choose a different installation
546 directory, preferably one that is listed in your PYTHONPATH environment
549 For information on other options, you may wish to consult the
552 https://setuptools.pypa.io/en/latest/deprecated/easy_install.html
554 Please make the appropriate changes for your system and try again.
557 def cant_write_to_target(self
):
558 msg
= self
.__cant
_write
_msg
% (sys
.exc_info()[1], self
.install_dir
,)
560 if not os
.path
.exists(self
.install_dir
):
561 msg
+= '\n' + self
.__not
_exists
_id
563 msg
+= '\n' + self
.__access
_msg
564 raise DistutilsError(msg
)
566 def check_pth_processing(self
):
567 """Empirically verify whether .pth files are supported in inst. dir"""
568 instdir
= self
.install_dir
569 log
.info("Checking .pth file support in %s", instdir
)
570 pth_file
= self
.pseudo_tempname() + ".pth"
571 ok_file
= pth_file
+ '.ok'
572 ok_exists
= os
.path
.exists(ok_file
)
573 tmpl
= _one_liner("""
575 f = open({ok_file!r}, 'w')
582 dirname
= os
.path
.dirname(ok_file
)
583 os
.makedirs(dirname
, exist_ok
=True)
584 f
= open(pth_file
, 'w')
585 except (OSError, IOError):
586 self
.cant_write_to_target()
589 f
.write(tmpl
.format(**locals()))
592 executable
= sys
.executable
594 dirname
, basename
= os
.path
.split(executable
)
595 alt
= os
.path
.join(dirname
, 'pythonw.exe')
597 basename
.lower() == 'python.exe' and
601 # use pythonw.exe to avoid opening a console window
604 from distutils
.spawn
import spawn
606 spawn([executable
, '-E', '-c', 'pass'], 0)
608 if os
.path
.exists(ok_file
):
610 "TEST PASSED: %s appears to support .pth files",
617 if os
.path
.exists(ok_file
):
619 if os
.path
.exists(pth_file
):
621 if not self
.multi_version
:
622 log
.warn("TEST FAILED: %s does NOT support .pth files", instdir
)
625 def install_egg_scripts(self
, dist
):
626 """Write all the scripts for `dist`, unless scripts are excluded"""
627 if not self
.exclude_scripts
and dist
.metadata_isdir('scripts'):
628 for script_name
in dist
.metadata_listdir('scripts'):
629 if dist
.metadata_isdir('scripts/' + script_name
):
630 # The "script" is a directory, likely a Python 3
631 # __pycache__ directory, so skip it.
635 dist
.get_metadata('scripts/' + script_name
)
637 self
.install_wrapper_scripts(dist
)
639 def add_output(self
, path
):
640 if os
.path
.isdir(path
):
641 for base
, dirs
, files
in os
.walk(path
):
642 for filename
in files
:
643 self
.outputs
.append(os
.path
.join(base
, filename
))
645 self
.outputs
.append(path
)
647 def not_editable(self
, spec
):
649 raise DistutilsArgError(
650 "Invalid argument %r: you can't use filenames or URLs "
651 "with --editable (except via the --find-links option)."
655 def check_editable(self
, spec
):
656 if not self
.editable
:
659 if os
.path
.exists(os
.path
.join(self
.build_directory
, spec
.key
)):
660 raise DistutilsArgError(
661 "%r already exists in %s; can't do a checkout there" %
662 (spec
.key
, self
.build_directory
)
665 @contextlib.contextmanager
667 tmpdir
= tempfile
.mkdtemp(prefix
=u
"easy_install-")
669 # cast to str as workaround for #709 and #710 and #712
672 os
.path
.exists(tmpdir
) and rmtree(tmpdir
)
674 def easy_install(self
, spec
, deps
=False):
675 with self
._tmpdir
() as tmpdir
:
676 if not isinstance(spec
, Requirement
):
678 # It's a url, download it to tmpdir and process
679 self
.not_editable(spec
)
680 dl
= self
.package_index
.download(spec
, tmpdir
)
681 return self
.install_item(None, dl
, tmpdir
, deps
, True)
683 elif os
.path
.exists(spec
):
684 # Existing file or directory, just process it directly
685 self
.not_editable(spec
)
686 return self
.install_item(None, spec
, tmpdir
, deps
, True)
688 spec
= parse_requirement_arg(spec
)
690 self
.check_editable(spec
)
691 dist
= self
.package_index
.fetch_distribution(
692 spec
, tmpdir
, self
.upgrade
, self
.editable
,
693 not self
.always_copy
, self
.local_index
696 msg
= "Could not find suitable distribution for %r" % spec
698 msg
+= " (--always-copy skips system and development eggs)"
699 raise DistutilsError(msg
)
700 elif dist
.precedence
== DEVELOP_DIST
:
701 # .egg-info dists don't need installing, just process deps
702 self
.process_distribution(spec
, dist
, deps
, "Using")
705 return self
.install_item(spec
, dist
.location
, tmpdir
, deps
)
707 def install_item(self
, spec
, download
, tmpdir
, deps
, install_needed
=False):
709 # Installation is also needed if file in tmpdir or is not an egg
710 install_needed
= install_needed
or self
.always_copy
711 install_needed
= install_needed
or os
.path
.dirname(download
) == tmpdir
712 install_needed
= install_needed
or not download
.endswith('.egg')
713 install_needed
= install_needed
or (
714 self
.always_copy_from
is not None and
715 os
.path
.dirname(normalize_path(download
)) ==
716 normalize_path(self
.always_copy_from
)
719 if spec
and not install_needed
:
720 # at this point, we know it's a local .egg, we just don't know if
721 # it's already installed.
722 for dist
in self
.local_index
[spec
.project_name
]:
723 if dist
.location
== download
:
726 install_needed
= True # it's not in the local index
728 log
.info("Processing %s", os
.path
.basename(download
))
731 dists
= self
.install_eggs(spec
, download
, tmpdir
)
733 self
.process_distribution(spec
, dist
, deps
)
735 dists
= [self
.egg_distribution(download
)]
736 self
.process_distribution(spec
, dists
[0], deps
, "Using")
743 def select_scheme(self
, name
):
745 install
._select
_scheme
(self
, name
)
746 except AttributeError:
748 install
.install
.select_scheme(self
, name
.replace('posix', 'unix'))
750 # FIXME: 'easy_install.process_distribution' is too complex (12)
751 def process_distribution( # noqa: C901
752 self
, requirement
, dist
, deps
=True, *info
,
754 self
.update_pth(dist
)
755 self
.package_index
.add(dist
)
756 if dist
in self
.local_index
[dist
.key
]:
757 self
.local_index
.remove(dist
)
758 self
.local_index
.add(dist
)
759 self
.install_egg_scripts(dist
)
760 self
.installed_projects
[dist
.key
] = dist
761 log
.info(self
.installation_report(requirement
, dist
, *info
))
762 if (dist
.has_metadata('dependency_links.txt') and
763 not self
.no_find_links
):
764 self
.package_index
.add_find_links(
765 dist
.get_metadata_lines('dependency_links.txt')
767 if not deps
and not self
.always_copy
:
769 elif requirement
is not None and dist
.key
!= requirement
.key
:
770 log
.warn("Skipping dependencies for %s", dist
)
771 return # XXX this is not the distribution we were looking for
772 elif requirement
is None or dist
not in requirement
:
773 # if we wound up with a different version, resolve what we've got
774 distreq
= dist
.as_requirement()
775 requirement
= Requirement(str(distreq
))
776 log
.info("Processing dependencies for %s", requirement
)
778 distros
= WorkingSet([]).resolve(
779 [requirement
], self
.local_index
, self
.easy_install
781 except DistributionNotFound
as e
:
782 raise DistutilsError(str(e
)) from e
783 except VersionConflict
as e
:
784 raise DistutilsError(e
.report()) from e
785 if self
.always_copy
or self
.always_copy_from
:
786 # Force all the relevant distros to be copied or activated
788 if dist
.key
not in self
.installed_projects
:
789 self
.easy_install(dist
.as_requirement())
790 log
.info("Finished processing dependencies for %s", requirement
)
792 def should_unzip(self
, dist
):
793 if self
.zip_ok
is not None:
794 return not self
.zip_ok
795 if dist
.has_metadata('not-zip-safe'):
797 if not dist
.has_metadata('zip-safe'):
801 def maybe_move(self
, spec
, dist_filename
, setup_base
):
802 dst
= os
.path
.join(self
.build_directory
, spec
.key
)
803 if os
.path
.exists(dst
):
805 "%r already exists in %s; build directory %s will not be kept"
807 log
.warn(msg
, spec
.key
, self
.build_directory
, setup_base
)
809 if os
.path
.isdir(dist_filename
):
810 setup_base
= dist_filename
812 if os
.path
.dirname(dist_filename
) == setup_base
:
813 os
.unlink(dist_filename
) # get it out of the tmp dir
814 contents
= os
.listdir(setup_base
)
815 if len(contents
) == 1:
816 dist_filename
= os
.path
.join(setup_base
, contents
[0])
817 if os
.path
.isdir(dist_filename
):
818 # if the only thing there is a directory, move it instead
819 setup_base
= dist_filename
820 ensure_directory(dst
)
821 shutil
.move(setup_base
, dst
)
824 def install_wrapper_scripts(self
, dist
):
825 if self
.exclude_scripts
:
827 for args
in ScriptWriter
.best().get_args(dist
):
828 self
.write_script(*args
)
830 def install_script(self
, dist
, script_name
, script_text
, dev_path
=None):
831 """Generate a legacy script wrapper and install it"""
832 spec
= str(dist
.as_requirement())
833 is_script
= is_python_script(script_text
, script_name
)
836 body
= self
._load
_template
(dev_path
) % locals()
837 script_text
= ScriptWriter
.get_header(script_text
) + body
838 self
.write_script(script_name
, _to_bytes(script_text
), 'b')
841 def _load_template(dev_path
):
843 There are a couple of template scripts in the package. This
844 function loads one of them and prepares it for use.
846 # See https://github.com/pypa/setuptools/issues/134 for info
847 # on script file naming and downstream issues with SVR4
850 name
= name
.replace('.tmpl', ' (dev).tmpl')
852 raw_bytes
= resource_string('setuptools', name
)
853 return raw_bytes
.decode('utf-8')
855 def write_script(self
, script_name
, contents
, mode
="t", blockers
=()):
856 """Write an executable file to the scripts directory"""
857 self
.delete_blockers( # clean up old .py/.pyw w/o a script
858 [os
.path
.join(self
.script_dir
, x
) for x
in blockers
]
860 log
.info("Installing %s script to %s", script_name
, self
.script_dir
)
861 target
= os
.path
.join(self
.script_dir
, script_name
)
862 self
.add_output(target
)
867 mask
= current_umask()
868 ensure_directory(target
)
869 if os
.path
.exists(target
):
871 with
open(target
, "w" + mode
) as f
:
873 chmod(target
, 0o777 - mask
)
875 def install_eggs(self
, spec
, dist_filename
, tmpdir
):
876 # .egg dirs or files are already built, so just return them
878 '.egg': self
.install_egg
,
879 '.exe': self
.install_exe
,
880 '.whl': self
.install_wheel
,
883 install_dist
= installer_map
[
884 dist_filename
.lower()[-4:]
889 return [install_dist(dist_filename
, tmpdir
)]
891 # Anything else, try to extract and build
893 if os
.path
.isfile(dist_filename
) and not dist_filename
.endswith('.py'):
894 unpack_archive(dist_filename
, tmpdir
, self
.unpack_progress
)
895 elif os
.path
.isdir(dist_filename
):
896 setup_base
= os
.path
.abspath(dist_filename
)
898 if (setup_base
.startswith(tmpdir
) # something we downloaded
899 and self
.build_directory
and spec
is not None):
900 setup_base
= self
.maybe_move(spec
, dist_filename
, setup_base
)
902 # Find the setup.py file
903 setup_script
= os
.path
.join(setup_base
, 'setup.py')
905 if not os
.path
.exists(setup_script
):
906 setups
= glob(os
.path
.join(setup_base
, '*', 'setup.py'))
908 raise DistutilsError(
909 "Couldn't find a setup script in %s" %
910 os
.path
.abspath(dist_filename
)
913 raise DistutilsError(
914 "Multiple setup scripts in %s" %
915 os
.path
.abspath(dist_filename
)
917 setup_script
= setups
[0]
919 # Now run it, and return the result
921 log
.info(self
.report_editable(spec
, setup_script
))
924 return self
.build_and_install(setup_script
, setup_base
)
926 def egg_distribution(self
, egg_path
):
927 if os
.path
.isdir(egg_path
):
928 metadata
= PathMetadata(egg_path
, os
.path
.join(egg_path
,
931 metadata
= EggMetadata(zipimport
.zipimporter(egg_path
))
932 return Distribution
.from_filename(egg_path
, metadata
=metadata
)
934 # FIXME: 'easy_install.install_egg' is too complex (11)
935 def install_egg(self
, egg_path
, tmpdir
): # noqa: C901
936 destination
= os
.path
.join(
938 os
.path
.basename(egg_path
),
940 destination
= os
.path
.abspath(destination
)
942 ensure_directory(destination
)
944 dist
= self
.egg_distribution(egg_path
)
946 os
.path
.exists(destination
) and os
.path
.samefile(egg_path
, destination
)
948 if os
.path
.isdir(destination
) and not os
.path
.islink(destination
):
949 dir_util
.remove_tree(destination
, dry_run
=self
.dry_run
)
950 elif os
.path
.exists(destination
):
954 "Removing " + destination
,
957 new_dist_is_zipped
= False
958 if os
.path
.isdir(egg_path
):
959 if egg_path
.startswith(tmpdir
):
960 f
, m
= shutil
.move
, "Moving"
962 f
, m
= shutil
.copytree
, "Copying"
963 elif self
.should_unzip(dist
):
964 self
.mkpath(destination
)
965 f
, m
= self
.unpack_and_compile
, "Extracting"
967 new_dist_is_zipped
= True
968 if egg_path
.startswith(tmpdir
):
969 f
, m
= shutil
.move
, "Moving"
971 f
, m
= shutil
.copy2
, "Copying"
974 (egg_path
, destination
),
975 (m
+ " %s to %s") % (
976 os
.path
.basename(egg_path
),
977 os
.path
.dirname(destination
)
982 fix_zipimporter_caches
=new_dist_is_zipped
,
985 update_dist_caches(destination
, fix_zipimporter_caches
=False)
988 self
.add_output(destination
)
989 return self
.egg_distribution(destination
)
991 def install_exe(self
, dist_filename
, tmpdir
):
992 # See if it's valid, get data
993 cfg
= extract_wininst_cfg(dist_filename
)
995 raise DistutilsError(
996 "%s is not a valid distutils Windows .exe" % dist_filename
998 # Create a dummy distribution object until we build the real distro
1001 project_name
=cfg
.get('metadata', 'name'),
1002 version
=cfg
.get('metadata', 'version'), platform
=get_platform(),
1005 # Convert the .exe to an unpacked egg
1006 egg_path
= os
.path
.join(tmpdir
, dist
.egg_name() + '.egg')
1007 dist
.location
= egg_path
1008 egg_tmp
= egg_path
+ '.tmp'
1009 _egg_info
= os
.path
.join(egg_tmp
, 'EGG-INFO')
1010 pkg_inf
= os
.path
.join(_egg_info
, 'PKG-INFO')
1011 ensure_directory(pkg_inf
) # make sure EGG-INFO dir exists
1012 dist
._provider
= PathMetadata(egg_tmp
, _egg_info
) # XXX
1013 self
.exe_to_egg(dist_filename
, egg_tmp
)
1015 # Write EGG-INFO/PKG-INFO
1016 if not os
.path
.exists(pkg_inf
):
1017 f
= open(pkg_inf
, 'w')
1018 f
.write('Metadata-Version: 1.0\n')
1019 for k
, v
in cfg
.items('metadata'):
1020 if k
!= 'target_version':
1021 f
.write('%s: %s\n' % (k
.replace('_', '-').title(), v
))
1023 script_dir
= os
.path
.join(_egg_info
, 'scripts')
1024 # delete entry-point scripts to avoid duping
1025 self
.delete_blockers([
1026 os
.path
.join(script_dir
, args
[0])
1027 for args
in ScriptWriter
.get_args(dist
)
1029 # Build .egg file from tmpdir
1030 bdist_egg
.make_zipfile(
1031 egg_path
, egg_tmp
, verbose
=self
.verbose
, dry_run
=self
.dry_run
,
1034 return self
.install_egg(egg_path
, tmpdir
)
1036 # FIXME: 'easy_install.exe_to_egg' is too complex (12)
1037 def exe_to_egg(self
, dist_filename
, egg_tmp
): # noqa: C901
1038 """Extract a bdist_wininst to the directories an egg would use"""
1039 # Check for .pth file and set up prefix translations
1040 prefixes
= get_exe_prefixes(dist_filename
)
1045 def process(src
, dst
):
1047 for old
, new
in prefixes
:
1048 if s
.startswith(old
):
1049 src
= new
+ src
[len(old
):]
1050 parts
= src
.split('/')
1051 dst
= os
.path
.join(egg_tmp
, *parts
)
1053 if dl
.endswith('.pyd') or dl
.endswith('.dll'):
1054 parts
[-1] = bdist_egg
.strip_module(parts
[-1])
1055 top_level
[os
.path
.splitext(parts
[0])[0]] = 1
1056 native_libs
.append(src
)
1057 elif dl
.endswith('.py') and old
!= 'SCRIPTS/':
1058 top_level
[os
.path
.splitext(parts
[0])[0]] = 1
1059 to_compile
.append(dst
)
1061 if not src
.endswith('.pth'):
1062 log
.warn("WARNING: can't process %s", src
)
1065 # extract, tracking .pyd/.dll->native_libs and .py -> to_compile
1066 unpack_archive(dist_filename
, egg_tmp
, process
)
1068 for res
in native_libs
:
1069 if res
.lower().endswith('.pyd'): # create stubs for .pyd's
1070 parts
= res
.split('/')
1071 resource
= parts
[-1]
1072 parts
[-1] = bdist_egg
.strip_module(parts
[-1]) + '.py'
1073 pyfile
= os
.path
.join(egg_tmp
, *parts
)
1074 to_compile
.append(pyfile
)
1075 stubs
.append(pyfile
)
1076 bdist_egg
.write_stub(resource
, pyfile
)
1077 self
.byte_compile(to_compile
) # compile .py's
1078 bdist_egg
.write_safety_flag(
1079 os
.path
.join(egg_tmp
, 'EGG-INFO'),
1080 bdist_egg
.analyze_egg(egg_tmp
, stubs
)) # write zip-safety flag
1082 for name
in 'top_level', 'native_libs':
1084 txt
= os
.path
.join(egg_tmp
, 'EGG-INFO', name
+ '.txt')
1085 if not os
.path
.exists(txt
):
1087 f
.write('\n'.join(locals()[name
]) + '\n')
1090 def install_wheel(self
, wheel_path
, tmpdir
):
1091 wheel
= Wheel(wheel_path
)
1092 assert wheel
.is_compatible()
1093 destination
= os
.path
.join(self
.install_dir
, wheel
.egg_name())
1094 destination
= os
.path
.abspath(destination
)
1095 if not self
.dry_run
:
1096 ensure_directory(destination
)
1097 if os
.path
.isdir(destination
) and not os
.path
.islink(destination
):
1098 dir_util
.remove_tree(destination
, dry_run
=self
.dry_run
)
1099 elif os
.path
.exists(destination
):
1103 "Removing " + destination
,
1107 wheel
.install_as_egg
,
1109 ("Installing %s to %s") % (
1110 os
.path
.basename(wheel_path
),
1111 os
.path
.dirname(destination
)
1115 update_dist_caches(destination
, fix_zipimporter_caches
=False)
1116 self
.add_output(destination
)
1117 return self
.egg_distribution(destination
)
1119 __mv_warning
= textwrap
.dedent("""
1120 Because this distribution was installed --multi-version, before you can
1121 import modules from this package in an application, you will need to
1122 'import pkg_resources' and then use a 'require()' call similar to one of
1123 these examples, in order to select the desired version:
1125 pkg_resources.require("%(name)s") # latest installed version
1126 pkg_resources.require("%(name)s==%(version)s") # this exact version
1127 pkg_resources.require("%(name)s>=%(version)s") # this version or higher
1128 """).lstrip() # noqa
1130 __id_warning
= textwrap
.dedent("""
1131 Note also that the installation directory must be on sys.path at runtime for
1132 this to work. (e.g. by being the application's script directory, by being on
1133 PYTHONPATH, or by being added to sys.path by your code.)
1136 def installation_report(self
, req
, dist
, what
="Installed"):
1137 """Helpful installation message for display to package users"""
1138 msg
= "\n%(what)s %(eggloc)s%(extras)s"
1139 if self
.multi_version
and not self
.no_report
:
1140 msg
+= '\n' + self
.__mv
_warning
1141 if self
.install_dir
not in map(normalize_path
, sys
.path
):
1142 msg
+= '\n' + self
.__id
_warning
1144 eggloc
= dist
.location
1145 name
= dist
.project_name
1146 version
= dist
.version
1147 extras
= '' # TODO: self.report_extras(req, dist)
1148 return msg
% locals()
1150 __editable_msg
= textwrap
.dedent("""
1151 Extracted editable version of %(spec)s to %(dirname)s
1153 If it uses setuptools in its setup script, you can activate it in
1154 "development" mode by going to that directory and running::
1156 %(python)s setup.py develop
1158 See the setuptools documentation for the "develop" command for more info.
1159 """).lstrip() # noqa
1161 def report_editable(self
, spec
, setup_script
):
1162 dirname
= os
.path
.dirname(setup_script
)
1163 python
= sys
.executable
1164 return '\n' + self
.__editable
_msg
% locals()
1166 def run_setup(self
, setup_script
, setup_base
, args
):
1167 sys
.modules
.setdefault('distutils.command.bdist_egg', bdist_egg
)
1168 sys
.modules
.setdefault('distutils.command.egg_info', egg_info
)
1171 if self
.verbose
> 2:
1172 v
= 'v' * (self
.verbose
- 1)
1173 args
.insert(0, '-' + v
)
1174 elif self
.verbose
< 2:
1175 args
.insert(0, '-q')
1177 args
.insert(0, '-n')
1179 "Running %s %s", setup_script
[len(setup_base
) + 1:], ' '.join(args
)
1182 run_setup(setup_script
, args
)
1183 except SystemExit as v
:
1184 raise DistutilsError(
1185 "Setup script exited with %s" % (v
.args
[0],)
1188 def build_and_install(self
, setup_script
, setup_base
):
1189 args
= ['bdist_egg', '--dist-dir']
1191 dist_dir
= tempfile
.mkdtemp(
1192 prefix
='egg-dist-tmp-', dir=os
.path
.dirname(setup_script
)
1195 self
._set
_fetcher
_options
(os
.path
.dirname(setup_script
))
1196 args
.append(dist_dir
)
1198 self
.run_setup(setup_script
, setup_base
, args
)
1199 all_eggs
= Environment([dist_dir
])
1201 for key
in all_eggs
:
1202 for dist
in all_eggs
[key
]:
1203 eggs
.append(self
.install_egg(dist
.location
, setup_base
))
1204 if not eggs
and not self
.dry_run
:
1205 log
.warn("No eggs found in %s (setup script problem?)",
1210 log
.set_verbosity(self
.verbose
) # restore our log verbosity
1212 def _set_fetcher_options(self
, base
):
1214 When easy_install is about to run bdist_egg on a source dist, that
1215 source dist might have 'setup_requires' directives, requiring
1216 additional fetching. Ensure the fetcher options given to easy_install
1217 are available to that command as well.
1219 # find the fetch options from easy_install and write them out
1220 # to the setup.cfg file.
1221 ei_opts
= self
.distribution
.get_option_dict('easy_install').copy()
1222 fetch_directives
= (
1223 'find_links', 'site_dirs', 'index_url', 'optimize', 'allow_hosts',
1226 for key
, val
in ei_opts
.items():
1227 if key
not in fetch_directives
:
1229 fetch_options
[key
] = val
[1]
1230 # create a settings dictionary suitable for `edit_config`
1231 settings
= dict(easy_install
=fetch_options
)
1232 cfg_filename
= os
.path
.join(base
, 'setup.cfg')
1233 setopt
.edit_config(cfg_filename
, settings
)
1235 def update_pth(self
, dist
): # noqa: C901 # is too complex (11) # FIXME
1236 if self
.pth_file
is None:
1239 for d
in self
.pth_file
[dist
.key
]: # drop old entries
1240 if not self
.multi_version
and d
.location
== dist
.location
:
1243 log
.info("Removing %s from easy-install.pth file", d
)
1244 self
.pth_file
.remove(d
)
1245 if d
.location
in self
.shadow_path
:
1246 self
.shadow_path
.remove(d
.location
)
1248 if not self
.multi_version
:
1249 if dist
.location
in self
.pth_file
.paths
:
1251 "%s is already the active version in easy-install.pth",
1255 log
.info("Adding %s to easy-install.pth file", dist
)
1256 self
.pth_file
.add(dist
) # add new entry
1257 if dist
.location
not in self
.shadow_path
:
1258 self
.shadow_path
.append(dist
.location
)
1263 self
.pth_file
.save()
1265 if dist
.key
!= 'setuptools':
1268 # Ensure that setuptools itself never becomes unavailable!
1269 # XXX should this check for latest version?
1270 filename
= os
.path
.join(self
.install_dir
, 'setuptools.pth')
1271 if os
.path
.islink(filename
):
1273 with
open(filename
, 'wt') as f
:
1274 f
.write(self
.pth_file
.make_relative(dist
.location
) + '\n')
1276 def unpack_progress(self
, src
, dst
):
1277 # Progress filter for unpacking
1278 log
.debug("Unpacking %s to %s", src
, dst
)
1279 return dst
# only unpack-and-compile skips files for dry run
1281 def unpack_and_compile(self
, egg_path
, destination
):
1286 if dst
.endswith('.py') and not src
.startswith('EGG-INFO/'):
1287 to_compile
.append(dst
)
1288 elif dst
.endswith('.dll') or dst
.endswith('.so'):
1289 to_chmod
.append(dst
)
1290 self
.unpack_progress(src
, dst
)
1291 return not self
.dry_run
and dst
or None
1293 unpack_archive(egg_path
, destination
, pf
)
1294 self
.byte_compile(to_compile
)
1295 if not self
.dry_run
:
1297 mode
= ((os
.stat(f
)[stat
.ST_MODE
]) |
0o555) & 0o7755
1300 def byte_compile(self
, to_compile
):
1301 if sys
.dont_write_bytecode
:
1304 from distutils
.util
import byte_compile
1307 # try to make the byte compile messages quieter
1308 log
.set_verbosity(self
.verbose
- 1)
1310 byte_compile(to_compile
, optimize
=0, force
=1, dry_run
=self
.dry_run
)
1313 to_compile
, optimize
=self
.optimize
, force
=1,
1314 dry_run
=self
.dry_run
,
1317 log
.set_verbosity(self
.verbose
) # restore original verbosity
1319 __no_default_msg
= textwrap
.dedent("""
1320 bad install directory or PYTHONPATH
1322 You are attempting to install a package to a directory that is not
1323 on PYTHONPATH and which Python does not read ".pth" files from. The
1324 installation directory you specified (via --install-dir, --prefix, or
1325 the distutils default setting) was:
1329 and your PYTHONPATH environment variable currently contains:
1333 Here are some of your options for correcting the problem:
1335 * You can choose a different installation directory, i.e., one that is
1336 on PYTHONPATH or supports .pth files
1338 * You can add the installation directory to the PYTHONPATH environment
1339 variable. (It must then also be on PYTHONPATH whenever you run
1340 Python and want to use the package(s) you are installing.)
1342 * You can set up the installation directory to support ".pth" files by
1343 using one of the approaches described here:
1345 https://setuptools.pypa.io/en/latest/deprecated/easy_install.html#custom-installation-locations
1348 Please make the appropriate changes for your system and try again.
1351 def create_home_path(self
):
1352 """Create directories under ~."""
1355 home
= convert_path(os
.path
.expanduser("~"))
1356 for path
in only_strs(self
.config_vars
.values()):
1357 if path
.startswith(home
) and not os
.path
.isdir(path
):
1358 self
.debug_print("os.makedirs('%s', 0o700)" % path
)
1359 os
.makedirs(path
, 0o700)
1361 if sys
.version
[:3] in ('2.3', '2.4', '2.5') or 'real_prefix' in sys
.__dict
__:
1362 sitedir_name
= 'site-packages'
1364 sitedir_name
= 'dist-packages'
1366 INSTALL_SCHEMES
= dict(
1368 install_dir
='$base/lib/python$py_version_short/site-packages',
1369 script_dir
='$base/bin',
1372 install_dir
= '$base/local/lib/python$py_version_short/%s' % sitedir_name
,
1373 script_dir
= '$base/local/bin',
1376 install_dir
= '$base/local/lib/python$py_version_short/%s' % sitedir_name
,
1377 script_dir
= '$base/local/bin',
1380 install_dir
= '$base/lib/python3/%s' % sitedir_name
,
1381 script_dir
= '$base/bin',
1385 DEFAULT_SCHEME
= dict(
1386 install_dir
='$base/Lib/site-packages',
1387 script_dir
='$base/Scripts',
1390 def _expand(self
, *attrs
):
1391 config_vars
= self
.get_finalized_command('install').config_vars
1393 if self
.prefix
or self
.install_layout
:
1394 if self
.install_layout
and self
.install_layout
in ['deb']:
1395 scheme_name
= "deb_system"
1396 self
.prefix
= '/usr'
1397 elif self
.prefix
or 'real_prefix' in sys
.__dict
__:
1398 scheme_name
= os
.name
1400 scheme_name
= "posix_local"
1401 # Set default install_dir/scripts from --prefix
1402 config_vars
= dict(config_vars
)
1403 config_vars
['base'] = self
.prefix
1404 scheme
= self
.INSTALL_SCHEMES
.get(scheme_name
,self
.DEFAULT_SCHEME
)
1405 for attr
, val
in scheme
.items():
1406 if getattr(self
, attr
, None) is None:
1407 setattr(self
, attr
, val
)
1409 from distutils
.util
import subst_vars
1412 val
= getattr(self
, attr
)
1414 val
= subst_vars(val
, config_vars
)
1415 if os
.name
== 'posix':
1416 val
= os
.path
.expanduser(val
)
1417 setattr(self
, attr
, val
)
1421 items
= os
.environ
.get('PYTHONPATH', '').split(os
.pathsep
)
1422 return filter(None, items
)
1425 def get_site_dirs():
1427 Return a list of 'site' dirs
1432 # start with PYTHONPATH
1433 sitedirs
.extend(_pythonpath())
1435 prefixes
= [sys
.prefix
]
1436 if sys
.exec_prefix
!= sys
.prefix
:
1437 prefixes
.append(sys
.exec_prefix
)
1438 for prefix
in prefixes
:
1442 if sys
.platform
in ('os2emx', 'riscos'):
1443 sitedirs
.append(os
.path
.join(prefix
, "Lib", "site-packages"))
1449 "python" + sys
.version
[:3],
1455 "python{}.{}".format(*sys
.version_info
),
1458 os
.path
.join(prefix
, "lib", "site-python"),
1463 os
.path
.join(prefix
, "lib", "site-packages"),
1465 if sys
.platform
!= 'darwin':
1468 # for framework builds *only* we add the standard Apple
1469 # locations. Currently only per-user, but /Library and
1470 # /Network/Library could be added too
1471 if 'Python.framework' not in prefix
:
1474 home
= os
.environ
.get('HOME')
1478 home_sp
= os
.path
.join(
1482 '{}.{}'.format(*sys
.version_info
),
1485 sitedirs
.append(home_sp
)
1486 lib_paths
= get_path('purelib'), get_path('platlib')
1488 sitedirs
.extend(s
for s
in lib_paths
if s
not in sitedirs
)
1490 if site
.ENABLE_USER_SITE
:
1491 sitedirs
.append(site
.USER_SITE
)
1493 with contextlib
.suppress(AttributeError):
1494 sitedirs
.extend(site
.getsitepackages())
1496 sitedirs
= list(map(normalize_path
, sitedirs
))
1501 def expand_paths(inputs
): # noqa: C901 # is too complex (11) # FIXME
1502 """Yield sys.path directories that might contain "old-style" packages"""
1506 for dirname
in inputs
:
1507 dirname
= normalize_path(dirname
)
1512 if not os
.path
.isdir(dirname
):
1515 files
= os
.listdir(dirname
)
1516 yield dirname
, files
1519 if not name
.endswith('.pth'):
1520 # We only care about the .pth files
1522 if name
in ('easy-install.pth', 'setuptools.pth'):
1523 # Ignore .pth files that we control
1526 # Read the .pth file
1527 f
= open(os
.path
.join(dirname
, name
))
1528 lines
= list(yield_lines(f
))
1531 # Yield existing non-dupe, non-import directory lines from it
1533 if line
.startswith("import"):
1536 line
= normalize_path(line
.rstrip())
1541 if not os
.path
.isdir(line
):
1544 yield line
, os
.listdir(line
)
1547 def extract_wininst_cfg(dist_filename
):
1548 """Extract configuration data from a bdist_wininst .exe
1550 Returns a configparser.RawConfigParser, or None
1552 f
= open(dist_filename
, 'rb')
1554 endrec
= zipfile
._EndRecData
(f
)
1558 prepended
= (endrec
[9] - endrec
[5]) - endrec
[6]
1559 if prepended
< 12: # no wininst data here
1561 f
.seek(prepended
- 12)
1563 tag
, cfglen
, bmlen
= struct
.unpack("<iii", f
.read(12))
1564 if tag
not in (0x1234567A, 0x1234567B):
1565 return None # not a valid tag
1567 f
.seek(prepended
- (12 + cfglen
))
1568 init
= {'version': '', 'target_version': ''}
1569 cfg
= configparser
.RawConfigParser(init
)
1571 part
= f
.read(cfglen
)
1572 # Read up to the first null byte.
1573 config
= part
.split(b
'\0', 1)[0]
1574 # Now the config is in bytes, but for RawConfigParser, it should
1575 # be text, so decode it.
1576 config
= config
.decode(sys
.getfilesystemencoding())
1577 cfg
.read_file(io
.StringIO(config
))
1578 except configparser
.Error
:
1580 if not cfg
.has_section('metadata') or not cfg
.has_section('Setup'):
1588 def get_exe_prefixes(exe_filename
):
1589 """Get exe->egg path translations for a given .exe file"""
1593 ('PLATLIB/pywin32_system32', ''),
1595 ('SCRIPTS/', 'EGG-INFO/scripts/'),
1596 ('DATA/lib/site-packages', ''),
1598 z
= zipfile
.ZipFile(exe_filename
)
1600 for info
in z
.infolist():
1601 name
= info
.filename
1602 parts
= name
.split('/')
1603 if len(parts
) == 3 and parts
[2] == 'PKG-INFO':
1604 if parts
[1].endswith('.egg-info'):
1605 prefixes
.insert(0, ('/'.join(parts
[:2]), 'EGG-INFO/'))
1607 if len(parts
) != 2 or not name
.endswith('.pth'):
1609 if name
.endswith('-nspkg.pth'):
1611 if parts
[0].upper() in ('PURELIB', 'PLATLIB'):
1612 contents
= z
.read(name
).decode()
1613 for pth
in yield_lines(contents
):
1614 pth
= pth
.strip().replace('\\', '/')
1615 if not pth
.startswith('import'):
1616 prefixes
.append((('%s/%s/' % (parts
[0], pth
)), ''))
1619 prefixes
= [(x
.lower(), y
) for x
, y
in prefixes
]
1625 class PthDistributions(Environment
):
1626 """A .pth file with Distribution paths in it"""
1630 def __init__(self
, filename
, sitedirs
=()):
1631 self
.filename
= filename
1632 self
.sitedirs
= list(map(normalize_path
, sitedirs
))
1633 self
.basedir
= normalize_path(os
.path
.dirname(self
.filename
))
1635 super().__init
__([], None, None)
1636 for path
in yield_lines(self
.paths
):
1637 list(map(self
.add
, find_distributions(path
, True)))
1642 seen
= dict.fromkeys(self
.sitedirs
)
1643 if os
.path
.isfile(self
.filename
):
1644 f
= open(self
.filename
, 'rt')
1646 if line
.startswith('import'):
1649 path
= line
.rstrip()
1650 self
.paths
.append(path
)
1651 if not path
.strip() or path
.strip().startswith('#'):
1653 # skip non-existent paths, in case somebody deleted a package
1654 # manually, and duplicate paths as well
1655 path
= self
.paths
[-1] = normalize_path(
1656 os
.path
.join(self
.basedir
, path
)
1658 if not os
.path
.exists(path
) or path
in seen
:
1659 self
.paths
.pop() # skip it
1660 self
.dirty
= True # we cleaned up, so we're dirty now :)
1665 if self
.paths
and not saw_import
:
1666 self
.dirty
= True # ensure anything we touch has import wrappers
1667 while self
.paths
and not self
.paths
[-1].strip():
1671 """Write changed .pth file back to disk"""
1675 rel_paths
= list(map(self
.make_relative
, self
.paths
))
1677 log
.debug("Saving %s", self
.filename
)
1678 lines
= self
._wrap
_lines
(rel_paths
)
1679 data
= '\n'.join(lines
) + '\n'
1681 if os
.path
.islink(self
.filename
):
1682 os
.unlink(self
.filename
)
1683 with
open(self
.filename
, 'wt') as f
:
1686 elif os
.path
.exists(self
.filename
):
1687 log
.debug("Deleting empty %s", self
.filename
)
1688 os
.unlink(self
.filename
)
1693 def _wrap_lines(lines
):
1696 def add(self
, dist
):
1697 """Add `dist` to the distribution map"""
1699 dist
.location
not in self
.paths
and (
1700 dist
.location
not in self
.sitedirs
or
1701 # account for '.' being in PYTHONPATH
1702 dist
.location
== os
.getcwd()
1706 self
.paths
.append(dist
.location
)
1710 def remove(self
, dist
):
1711 """Remove `dist` from the distribution map"""
1712 while dist
.location
in self
.paths
:
1713 self
.paths
.remove(dist
.location
)
1715 super().remove(dist
)
1717 def make_relative(self
, path
):
1718 npath
, last
= os
.path
.split(normalize_path(path
))
1719 baselen
= len(self
.basedir
)
1721 sep
= os
.altsep
== '/' and '/' or os
.sep
1722 while len(npath
) >= baselen
:
1723 if npath
== self
.basedir
:
1724 parts
.append(os
.curdir
)
1726 return sep
.join(parts
)
1727 npath
, last
= os
.path
.split(npath
)
1733 class RewritePthDistributions(PthDistributions
):
1735 def _wrap_lines(cls
, lines
):
1741 prelude
= _one_liner("""
1743 sys.__plen = len(sys.path)
1745 postlude
= _one_liner("""
1747 new = sys.path[sys.__plen:]
1748 del sys.path[sys.__plen:]
1749 p = getattr(sys, '__egginsert', 0)
1751 sys.__egginsert = p + len(new)
1755 if os
.environ
.get('SETUPTOOLS_SYS_PATH_TECHNIQUE', 'raw') == 'rewrite':
1756 PthDistributions
= RewritePthDistributions
1759 def _first_line_re():
1761 Return a regular expression based on first_line_re suitable for matching
1764 if isinstance(first_line_re
.pattern
, str):
1765 return first_line_re
1767 # first_line_re in Python >=3.1.4 and >=3.2.1 is a bytes pattern.
1768 return re
.compile(first_line_re
.pattern
.decode())
1771 def auto_chmod(func
, arg
, exc
):
1772 if func
in [os
.unlink
, os
.remove
] and os
.name
== 'nt':
1773 chmod(arg
, stat
.S_IWRITE
)
1775 et
, ev
, _
= sys
.exc_info()
1776 # TODO: This code doesn't make sense. What is it trying to do?
1777 raise (ev
[0], ev
[1] + (" %s %s" % (func
, arg
)))
1780 def update_dist_caches(dist_path
, fix_zipimporter_caches
):
1782 Fix any globally cached `dist_path` related data
1784 `dist_path` should be a path of a newly installed egg distribution (zipped
1787 sys.path_importer_cache contains finder objects that have been cached when
1788 importing data from the original distribution. Any such finders need to be
1789 cleared since the replacement distribution might be packaged differently,
1790 e.g. a zipped egg distribution might get replaced with an unzipped egg
1791 folder or vice versa. Having the old finders cached may then cause Python
1792 to attempt loading modules from the replacement distribution using an
1795 zipimport.zipimporter objects are Python loaders charged with importing
1796 data packaged inside zip archives. If stale loaders referencing the
1797 original distribution, are left behind, they can fail to load modules from
1798 the replacement distribution. E.g. if an old zipimport.zipimporter instance
1799 is used to load data from a new zipped egg archive, it may cause the
1800 operation to attempt to locate the requested data in the wrong location -
1801 one indicated by the original distribution's zip archive directory
1802 information. Such an operation may then fail outright, e.g. report having
1803 read a 'bad local file header', or even worse, it may fail silently &
1804 return invalid data.
1806 zipimport._zip_directory_cache contains cached zip archive directory
1807 information for all existing zipimport.zipimporter instances and all such
1808 instances connected to the same archive share the same cached directory
1811 If asked, and the underlying Python implementation allows it, we can fix
1812 all existing zipimport.zipimporter instances instead of having to track
1813 them down and remove them one by one, by updating their shared cached zip
1814 archive directory information. This, of course, assumes that the
1815 replacement distribution is packaged as a zipped egg.
1817 If not asked to fix existing zipimport.zipimporter instances, we still do
1818 our best to clear any remaining zipimport.zipimporter related cached data
1819 that might somehow later get used when attempting to load data from the new
1820 distribution and thus cause such load operations to fail. Note that when
1821 tracking down such remaining stale data, we can not catch every conceivable
1822 usage from here, and we clear only those that we know of and have found to
1823 cause problems if left alive. Any remaining caches should be updated by
1824 whomever is in charge of maintaining them, i.e. they should be ready to
1825 handle us replacing their zip archives with new distributions at runtime.
1828 # There are several other known sources of stale zipimport.zipimporter
1829 # instances that we do not clear here, but might if ever given a reason to
1831 # * Global setuptools pkg_resources.working_set (a.k.a. 'master working
1832 # set') may contain distributions which may in turn contain their
1833 # zipimport.zipimporter loaders.
1834 # * Several zipimport.zipimporter loaders held by local variables further
1835 # up the function call stack when running the setuptools installation.
1836 # * Already loaded modules may have their __loader__ attribute set to the
1837 # exact loader instance used when importing them. Python 3.4 docs state
1838 # that this information is intended mostly for introspection and so is
1839 # not expected to cause us problems.
1840 normalized_path
= normalize_path(dist_path
)
1841 _uncache(normalized_path
, sys
.path_importer_cache
)
1842 if fix_zipimporter_caches
:
1843 _replace_zip_directory_cache_data(normalized_path
)
1845 # Here, even though we do not want to fix existing and now stale
1846 # zipimporter cache information, we still want to remove it. Related to
1847 # Python's zip archive directory information cache, we clear each of
1848 # its stale entries in two phases:
1849 # 1. Clear the entry so attempting to access zip archive information
1850 # via any existing stale zipimport.zipimporter instances fails.
1851 # 2. Remove the entry from the cache so any newly constructed
1852 # zipimport.zipimporter instances do not end up using old stale
1853 # zip archive directory information.
1854 # This whole stale data removal step does not seem strictly necessary,
1855 # but has been left in because it was done before we started replacing
1856 # the zip archive directory information cache content if possible, and
1857 # there are no relevant unit tests that we can depend on to tell us if
1858 # this is really needed.
1859 _remove_and_clear_zip_directory_cache_data(normalized_path
)
1862 def _collect_zipimporter_cache_entries(normalized_path
, cache
):
1864 Return zipimporter cache entry keys related to a given normalized path.
1866 Alternative path spellings (e.g. those using different character case or
1867 those using alternative path separators) related to the same path are
1868 included. Any sub-path entries are included as well, i.e. those
1869 corresponding to zip archives embedded in other zip archives.
1873 prefix_len
= len(normalized_path
)
1875 np
= normalize_path(p
)
1876 if (np
.startswith(normalized_path
) and
1877 np
[prefix_len
:prefix_len
+ 1] in (os
.sep
, '')):
1882 def _update_zipimporter_cache(normalized_path
, cache
, updater
=None):
1884 Update zipimporter cache data for a given normalized path.
1886 Any sub-path entries are processed as well, i.e. those corresponding to zip
1887 archives embedded in other zip archives.
1889 Given updater is a callable taking a cache entry key and the original entry
1890 (after already removing the entry from the cache), and expected to update
1891 the entry and possibly return a new one to be inserted in its place.
1892 Returning None indicates that the entry should not be replaced with a new
1893 one. If no updater is given, the cache entries are simply removed without
1894 any additional processing, the same as if the updater simply returned None.
1897 for p
in _collect_zipimporter_cache_entries(normalized_path
, cache
):
1898 # N.B. pypy's custom zipimport._zip_directory_cache implementation does
1899 # not support the complete dict interface:
1900 # * Does not support item assignment, thus not allowing this function
1901 # to be used only for removing existing cache entries.
1902 # * Does not support the dict.pop() method, forcing us to use the
1903 # get/del patterns instead. For more detailed information see the
1905 # https://github.com/pypa/setuptools/issues/202#issuecomment-202913420
1906 # http://bit.ly/2h9itJX
1907 old_entry
= cache
[p
]
1909 new_entry
= updater
and updater(p
, old_entry
)
1910 if new_entry
is not None:
1911 cache
[p
] = new_entry
1914 def _uncache(normalized_path
, cache
):
1915 _update_zipimporter_cache(normalized_path
, cache
)
1918 def _remove_and_clear_zip_directory_cache_data(normalized_path
):
1919 def clear_and_remove_cached_zip_archive_directory_data(path
, old_entry
):
1922 _update_zipimporter_cache(
1923 normalized_path
, zipimport
._zip
_directory
_cache
,
1924 updater
=clear_and_remove_cached_zip_archive_directory_data
)
1927 # PyPy Python implementation does not allow directly writing to the
1928 # zipimport._zip_directory_cache and so prevents us from attempting to correct
1929 # its content. The best we can do there is clear the problematic cache content
1930 # and have PyPy repopulate it as needed. The downside is that if there are any
1931 # stale zipimport.zipimporter instances laying around, attempting to use them
1932 # will fail due to not having its zip archive directory information available
1933 # instead of being automatically corrected to use the new correct zip archive
1934 # directory information.
1935 if '__pypy__' in sys
.builtin_module_names
:
1936 _replace_zip_directory_cache_data
= \
1937 _remove_and_clear_zip_directory_cache_data
1940 def _replace_zip_directory_cache_data(normalized_path
):
1941 def replace_cached_zip_archive_directory_data(path
, old_entry
):
1942 # N.B. In theory, we could load the zip directory information just
1943 # once for all updated path spellings, and then copy it locally and
1944 # update its contained path strings to contain the correct
1945 # spelling, but that seems like a way too invasive move (this cache
1946 # structure is not officially documented anywhere and could in
1947 # theory change with new Python releases) for no significant
1950 zipimport
.zipimporter(path
)
1951 old_entry
.update(zipimport
._zip
_directory
_cache
[path
])
1954 _update_zipimporter_cache(
1955 normalized_path
, zipimport
._zip
_directory
_cache
,
1956 updater
=replace_cached_zip_archive_directory_data
)
1959 def is_python(text
, filename
='<string>'):
1960 "Is this string a valid Python script?"
1962 compile(text
, filename
, 'exec')
1963 except (SyntaxError, TypeError):
1969 def is_sh(executable
):
1970 """Determine if the specified executable is a .sh (contains a #! line)"""
1972 with io
.open(executable
, encoding
='latin-1') as fp
:
1974 except (OSError, IOError):
1976 return magic
== '#!'
1979 def nt_quote_arg(arg
):
1980 """Quote a command line argument according to Windows parsing rules"""
1981 return subprocess
.list2cmdline([arg
])
1984 def is_python_script(script_text
, filename
):
1985 """Is this text, as a whole, a Python script? (as opposed to shell/bat/etc.
1987 if filename
.endswith('.py') or filename
.endswith('.pyw'):
1988 return True # extension says it's Python
1989 if is_python(script_text
, filename
):
1990 return True # it's syntactically valid Python
1991 if script_text
.startswith('#!'):
1992 # It begins with a '#!' line, so check if 'python' is in it somewhere
1993 return 'python' in script_text
.splitlines()[0].lower()
1995 return False # Not any Python I can recognize
1999 from os
import chmod
as _chmod
2001 # Jython compatibility
2006 def chmod(path
, mode
):
2007 log
.debug("changing mode of %s to %o", path
, mode
)
2010 except os
.error
as e
:
2011 log
.debug("chmod failed: %s", e
)
2014 class CommandSpec(list):
2016 A command spec for a #! header, specified as a list of arguments akin to
2017 those passed to Popen.
2026 Choose the best CommandSpec class based on environmental conditions.
2031 def _sys_executable(cls
):
2032 _default
= os
.path
.normpath(sys
.executable
)
2033 return os
.environ
.get('__PYVENV_LAUNCHER__', _default
)
2036 def from_param(cls
, param
):
2038 Construct a CommandSpec from a parameter to build_scripts, which may
2041 if isinstance(param
, cls
):
2043 if isinstance(param
, list):
2046 return cls
.from_environment()
2047 # otherwise, assume it's a string.
2048 return cls
.from_string(param
)
2051 def from_environment(cls
):
2052 return cls([cls
._sys
_executable
()])
2055 def from_string(cls
, string
):
2057 Construct a command spec from a simple string representing a command
2058 line parseable by shlex.split.
2060 items
= shlex
.split(string
, **cls
.split_args
)
2063 def install_options(self
, script_text
):
2064 self
.options
= shlex
.split(self
._extract
_options
(script_text
))
2065 cmdline
= subprocess
.list2cmdline(self
)
2066 if not isascii(cmdline
):
2067 self
.options
[:0] = ['-x']
2070 def _extract_options(orig_script
):
2072 Extract any options from the first line of the script.
2074 first
= (orig_script
+ '\n').splitlines()[0]
2075 match
= _first_line_re().match(first
)
2076 options
= match
.group(1) or '' if match
else ''
2077 return options
.strip()
2079 def as_header(self
):
2080 return self
._render
(self
+ list(self
.options
))
2083 def _strip_quotes(item
):
2086 if item
.startswith(q
) and item
.endswith(q
):
2092 cmdline
= subprocess
.list2cmdline(
2093 CommandSpec
._strip
_quotes
(item
.strip()) for item
in items
)
2094 return '#!' + cmdline
+ '\n'
2097 # For pbr compat; will be removed in a future version.
2098 sys_executable
= CommandSpec
._sys
_executable
()
2101 class WindowsCommandSpec(CommandSpec
):
2102 split_args
= dict(posix
=False)
2107 Encapsulates behavior around writing entry point scripts for console and
2111 template
= textwrap
.dedent(r
"""
2112 # EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r
2116 # for compatibility with easy_install; see #2198
2117 __requires__ = %(spec)r
2120 from importlib.metadata import distribution
2123 from importlib_metadata import distribution
2125 from pkg_resources import load_entry_point
2128 def importlib_load_entry_point(spec, group, name):
2129 dist_name, _, _ = spec.partition('==')
2132 for entry_point in distribution(dist_name).entry_points
2133 if entry_point.group == group and entry_point.name == name
2135 return next(matches).load()
2138 globals().setdefault('load_entry_point', importlib_load_entry_point)
2141 if __name__ == '__main__':
2142 sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
2143 sys.exit(load_entry_point(%(spec)r, %(group)r, %(name)r)())
2146 command_spec_class
= CommandSpec
2149 def get_script_args(cls
, dist
, executable
=None, wininst
=False):
2150 # for backward compatibility
2151 warnings
.warn("Use get_args", EasyInstallDeprecationWarning
)
2152 writer
= (WindowsScriptWriter
if wininst
else ScriptWriter
).best()
2153 header
= cls
.get_script_header("", executable
, wininst
)
2154 return writer
.get_args(dist
, header
)
2157 def get_script_header(cls
, script_text
, executable
=None, wininst
=False):
2158 # for backward compatibility
2160 "Use get_header", EasyInstallDeprecationWarning
, stacklevel
=2)
2162 executable
= "python.exe"
2163 return cls
.get_header(script_text
, executable
)
2166 def get_args(cls
, dist
, header
=None):
2168 Yield write_script() argument tuples for a distribution's
2169 console_scripts and gui_scripts entry points.
2172 header
= cls
.get_header()
2173 spec
= str(dist
.as_requirement())
2174 for type_
in 'console', 'gui':
2175 group
= type_
+ '_scripts'
2176 for name
, ep
in dist
.get_entry_map(group
).items():
2177 cls
._ensure
_safe
_name
(name
)
2178 script_text
= cls
.template
% locals()
2179 args
= cls
._get
_script
_args
(type_
, name
, header
, script_text
)
2184 def _ensure_safe_name(name
):
2186 Prevent paths in *_scripts entry point names.
2188 has_path_sep
= re
.search(r
'[\\/]', name
)
2190 raise ValueError("Path separators not allowed in script names")
2193 def get_writer(cls
, force_windows
):
2194 # for backward compatibility
2195 warnings
.warn("Use best", EasyInstallDeprecationWarning
)
2196 return WindowsScriptWriter
.best() if force_windows
else cls
.best()
2201 Select the best ScriptWriter for this environment.
2203 if sys
.platform
== 'win32' or (os
.name
== 'java' and os
._name
== 'nt'):
2204 return WindowsScriptWriter
.best()
2209 def _get_script_args(cls
, type_
, name
, header
, script_text
):
2210 # Simply write the stub with no extension.
2211 yield (name
, header
+ script_text
)
2214 def get_header(cls
, script_text
="", executable
=None):
2215 """Create a #! line, getting options (if any) from script_text"""
2216 cmd
= cls
.command_spec_class
.best().from_param(executable
)
2217 cmd
.install_options(script_text
)
2218 return cmd
.as_header()
2221 class WindowsScriptWriter(ScriptWriter
):
2222 command_spec_class
= WindowsCommandSpec
2225 def get_writer(cls
):
2226 # for backward compatibility
2227 warnings
.warn("Use best", EasyInstallDeprecationWarning
)
2233 Select the best ScriptWriter suitable for Windows
2235 writer_lookup
= dict(
2236 executable
=WindowsExecutableLauncherWriter
,
2239 # for compatibility, use the executable launcher by default
2240 launcher
= os
.environ
.get('SETUPTOOLS_LAUNCHER', 'executable')
2241 return writer_lookup
[launcher
]
2244 def _get_script_args(cls
, type_
, name
, header
, script_text
):
2245 "For Windows, add a .py extension"
2246 ext
= dict(console
='.pya', gui
='.pyw')[type_
]
2247 if ext
not in os
.environ
['PATHEXT'].lower().split(';'):
2249 "{ext} not listed in PATHEXT; scripts will not be "
2250 "recognized as executables."
2251 ).format(**locals())
2252 warnings
.warn(msg
, UserWarning)
2253 old
= ['.pya', '.py', '-script.py', '.pyc', '.pyo', '.pyw', '.exe']
2255 header
= cls
._adjust
_header
(type_
, header
)
2256 blockers
= [name
+ x
for x
in old
]
2257 yield name
+ ext
, header
+ script_text
, 't', blockers
2260 def _adjust_header(cls
, type_
, orig_header
):
2262 Make sure 'pythonw' is used for gui and 'python' is used for
2263 console (regardless of what sys.executable is).
2265 pattern
= 'pythonw.exe'
2268 pattern
, repl
= repl
, pattern
2269 pattern_ob
= re
.compile(re
.escape(pattern
), re
.IGNORECASE
)
2270 new_header
= pattern_ob
.sub(string
=orig_header
, repl
=repl
)
2271 return new_header
if cls
._use
_header
(new_header
) else orig_header
2274 def _use_header(new_header
):
2276 Should _adjust_header use the replaced header?
2278 On non-windows systems, always use. On
2279 Windows systems, only use the replaced header if it resolves
2280 to an executable on the system.
2282 clean_header
= new_header
[2:-1].strip('"')
2283 return sys
.platform
!= 'win32' or find_executable(clean_header
)
2286 class WindowsExecutableLauncherWriter(WindowsScriptWriter
):
2288 def _get_script_args(cls
, type_
, name
, header
, script_text
):
2290 For Windows, add a .py extension and an .exe launcher
2293 launcher_type
= 'gui'
2297 launcher_type
= 'cli'
2299 old
= ['.py', '.pyc', '.pyo']
2300 hdr
= cls
._adjust
_header
(type_
, header
)
2301 blockers
= [name
+ x
for x
in old
]
2302 yield (name
+ ext
, hdr
+ script_text
, 't', blockers
)
2304 name
+ '.exe', get_win_launcher(launcher_type
),
2305 'b' # write in binary mode
2308 # install a manifest for the launcher to prevent Windows
2309 # from detecting it as an installer (which it will for
2310 # launchers like easy_install.exe). Consider only
2311 # adding a manifest for launchers detected as installers.
2312 # See Distribute #143 for details.
2313 m_name
= name
+ '.exe.manifest'
2314 yield (m_name
, load_launcher_manifest(name
), 't')
2317 # for backward-compatibility
2318 get_script_args
= ScriptWriter
.get_script_args
2319 get_script_header
= ScriptWriter
.get_script_header
2322 def get_win_launcher(type):
2324 Load the Windows launcher (executable) suitable for launching a script.
2326 `type` should be either 'cli' or 'gui'
2328 Returns the executable as a byte string.
2330 launcher_fn
= '%s.exe' % type
2332 if get_platform() == "win-arm64":
2333 launcher_fn
= launcher_fn
.replace(".", "-arm64.")
2335 launcher_fn
= launcher_fn
.replace(".", "-64.")
2337 launcher_fn
= launcher_fn
.replace(".", "-32.")
2338 return resource_string('setuptools', launcher_fn
)
2341 def load_launcher_manifest(name
):
2342 manifest
= pkg_resources
.resource_string(__name__
, 'launcher manifest.xml')
2343 return manifest
.decode('utf-8') % vars()
2346 def rmtree(path
, ignore_errors
=False, onerror
=auto_chmod
):
2347 return shutil
.rmtree(path
, ignore_errors
, onerror
)
2350 def current_umask():
2351 tmp
= os
.umask(0o022)
2356 def only_strs(values
):
2358 Exclude non-str values. Ref #3063.
2360 return filter(lambda val
: isinstance(val
, str), values
)
2363 class EasyInstallDeprecationWarning(SetuptoolsDeprecationWarning
):
2365 Warning for EasyInstall deprecations, bypassing suppression.