Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
news
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Sartika Aritonang
news
Commits
f5fa93f6
Commit
f5fa93f6
authored
May 29, 2020
by
Sartika Aritonang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Upload New File
parent
bb7c8b6b
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
2342 additions
and
0 deletions
+2342
-0
easy_install.py
stbi/Lib/site-packages/setuptools/command/easy_install.py
+2342
-0
No files found.
stbi/Lib/site-packages/setuptools/command/easy_install.py
0 → 100644
View file @
f5fa93f6
#!/usr/bin/env python
"""
Easy Install
------------
A tool for doing automatic download/extract/build of distutils-based Python
packages. For detailed documentation, see the accompanying EasyInstall.txt
file, or visit the `EasyInstall home page`__.
__ https://setuptools.readthedocs.io/en/latest/easy_install.html
"""
from
glob
import
glob
from
distutils.util
import
get_platform
from
distutils.util
import
convert_path
,
subst_vars
from
distutils.errors
import
(
DistutilsArgError
,
DistutilsOptionError
,
DistutilsError
,
DistutilsPlatformError
,
)
from
distutils.command.install
import
INSTALL_SCHEMES
,
SCHEME_KEYS
from
distutils
import
log
,
dir_util
from
distutils.command.build_scripts
import
first_line_re
from
distutils.spawn
import
find_executable
import
sys
import
os
import
zipimport
import
shutil
import
tempfile
import
zipfile
import
re
import
stat
import
random
import
textwrap
import
warnings
import
site
import
struct
import
contextlib
import
subprocess
import
shlex
import
io
from
sysconfig
import
get_config_vars
,
get_path
from
setuptools
import
SetuptoolsDeprecationWarning
from
setuptools.extern
import
six
from
setuptools.extern.six.moves
import
configparser
,
map
from
setuptools
import
Command
from
setuptools.sandbox
import
run_setup
from
setuptools.py27compat
import
rmtree_safe
from
setuptools.command
import
setopt
from
setuptools.archive_util
import
unpack_archive
from
setuptools.package_index
import
(
PackageIndex
,
parse_requirement_arg
,
URL_SCHEME
,
)
from
setuptools.command
import
bdist_egg
,
egg_info
from
setuptools.wheel
import
Wheel
from
pkg_resources
import
(
yield_lines
,
normalize_path
,
resource_string
,
ensure_directory
,
get_distribution
,
find_distributions
,
Environment
,
Requirement
,
Distribution
,
PathMetadata
,
EggMetadata
,
WorkingSet
,
DistributionNotFound
,
VersionConflict
,
DEVELOP_DIST
,
)
import
pkg_resources.py31compat
__metaclass__
=
type
# Turn on PEP440Warnings
warnings
.
filterwarnings
(
"default"
,
category
=
pkg_resources
.
PEP440Warning
)
__all__
=
[
'samefile'
,
'easy_install'
,
'PthDistributions'
,
'extract_wininst_cfg'
,
'main'
,
'get_exe_prefixes'
,
]
def
is_64bit
():
return
struct
.
calcsize
(
"P"
)
==
8
def
samefile
(
p1
,
p2
):
"""
Determine if two paths reference the same file.
Augments os.path.samefile to work on Windows and
suppresses errors if the path doesn't exist.
"""
both_exist
=
os
.
path
.
exists
(
p1
)
and
os
.
path
.
exists
(
p2
)
use_samefile
=
hasattr
(
os
.
path
,
'samefile'
)
and
both_exist
if
use_samefile
:
return
os
.
path
.
samefile
(
p1
,
p2
)
norm_p1
=
os
.
path
.
normpath
(
os
.
path
.
normcase
(
p1
))
norm_p2
=
os
.
path
.
normpath
(
os
.
path
.
normcase
(
p2
))
return
norm_p1
==
norm_p2
if
six
.
PY2
:
def
_to_bytes
(
s
):
return
s
def
isascii
(
s
):
try
:
six
.
text_type
(
s
,
'ascii'
)
return
True
except
UnicodeError
:
return
False
else
:
def
_to_bytes
(
s
):
return
s
.
encode
(
'utf8'
)
def
isascii
(
s
):
try
:
s
.
encode
(
'ascii'
)
return
True
except
UnicodeError
:
return
False
_one_liner
=
lambda
text
:
textwrap
.
dedent
(
text
)
.
strip
()
.
replace
(
'
\n
'
,
'; '
)
class
easy_install
(
Command
):
"""Manage a download/build/install process"""
description
=
"Find/get/install Python packages"
command_consumes_arguments
=
True
user_options
=
[
(
'prefix='
,
None
,
"installation prefix"
),
(
"zip-ok"
,
"z"
,
"install package as a zipfile"
),
(
"multi-version"
,
"m"
,
"make apps have to require() a version"
),
(
"upgrade"
,
"U"
,
"force upgrade (searches PyPI for latest versions)"
),
(
"install-dir="
,
"d"
,
"install package to DIR"
),
(
"script-dir="
,
"s"
,
"install scripts to DIR"
),
(
"exclude-scripts"
,
"x"
,
"Don't install scripts"
),
(
"always-copy"
,
"a"
,
"Copy all needed packages to install dir"
),
(
"index-url="
,
"i"
,
"base URL of Python Package Index"
),
(
"find-links="
,
"f"
,
"additional URL(s) to search for packages"
),
(
"build-directory="
,
"b"
,
"download/extract/build in DIR; keep the results"
),
(
'optimize='
,
'O'
,
"also compile with optimization: -O1 for
\"
python -O
\"
, "
"-O2 for
\"
python -OO
\"
, and -O0 to disable [default: -O0]"
),
(
'record='
,
None
,
"filename in which to record list of installed files"
),
(
'always-unzip'
,
'Z'
,
"don't install as a zipfile, no matter what"
),
(
'site-dirs='
,
'S'
,
"list of directories where .pth files work"
),
(
'editable'
,
'e'
,
"Install specified packages in editable form"
),
(
'no-deps'
,
'N'
,
"don't install dependencies"
),
(
'allow-hosts='
,
'H'
,
"pattern(s) that hostnames must match"
),
(
'local-snapshots-ok'
,
'l'
,
"allow building eggs from local checkouts"
),
(
'version'
,
None
,
"print version information and exit"
),
(
'no-find-links'
,
None
,
"Don't load find-links defined in packages being installed"
)
]
boolean_options
=
[
'zip-ok'
,
'multi-version'
,
'exclude-scripts'
,
'upgrade'
,
'always-copy'
,
'editable'
,
'no-deps'
,
'local-snapshots-ok'
,
'version'
]
if
site
.
ENABLE_USER_SITE
:
help_msg
=
"install in user site-package '
%
s'"
%
site
.
USER_SITE
user_options
.
append
((
'user'
,
None
,
help_msg
))
boolean_options
.
append
(
'user'
)
negative_opt
=
{
'always-unzip'
:
'zip-ok'
}
create_index
=
PackageIndex
def
initialize_options
(
self
):
# the --user option seems to be an opt-in one,
# so the default should be False.
self
.
user
=
0
self
.
zip_ok
=
self
.
local_snapshots_ok
=
None
self
.
install_dir
=
self
.
script_dir
=
self
.
exclude_scripts
=
None
self
.
index_url
=
None
self
.
find_links
=
None
self
.
build_directory
=
None
self
.
args
=
None
self
.
optimize
=
self
.
record
=
None
self
.
upgrade
=
self
.
always_copy
=
self
.
multi_version
=
None
self
.
editable
=
self
.
no_deps
=
self
.
allow_hosts
=
None
self
.
root
=
self
.
prefix
=
self
.
no_report
=
None
self
.
version
=
None
self
.
install_purelib
=
None
# for pure module distributions
self
.
install_platlib
=
None
# non-pure (dists w/ extensions)
self
.
install_headers
=
None
# for C/C++ headers
self
.
install_lib
=
None
# set to either purelib or platlib
self
.
install_scripts
=
None
self
.
install_data
=
None
self
.
install_base
=
None
self
.
install_platbase
=
None
if
site
.
ENABLE_USER_SITE
:
self
.
install_userbase
=
site
.
USER_BASE
self
.
install_usersite
=
site
.
USER_SITE
else
:
self
.
install_userbase
=
None
self
.
install_usersite
=
None
self
.
no_find_links
=
None
# Options not specifiable via command line
self
.
package_index
=
None
self
.
pth_file
=
self
.
always_copy_from
=
None
self
.
site_dirs
=
None
self
.
installed_projects
=
{}
self
.
sitepy_installed
=
False
# Always read easy_install options, even if we are subclassed, or have
# an independent instance created. This ensures that defaults will
# always come from the standard configuration file(s)' "easy_install"
# section, even if this is a "develop" or "install" command, or some
# other embedding.
self
.
_dry_run
=
None
self
.
verbose
=
self
.
distribution
.
verbose
self
.
distribution
.
_set_command_options
(
self
,
self
.
distribution
.
get_option_dict
(
'easy_install'
)
)
def
delete_blockers
(
self
,
blockers
):
extant_blockers
=
(
filename
for
filename
in
blockers
if
os
.
path
.
exists
(
filename
)
or
os
.
path
.
islink
(
filename
)
)
list
(
map
(
self
.
_delete_path
,
extant_blockers
))
def
_delete_path
(
self
,
path
):
log
.
info
(
"Deleting
%
s"
,
path
)
if
self
.
dry_run
:
return
is_tree
=
os
.
path
.
isdir
(
path
)
and
not
os
.
path
.
islink
(
path
)
remover
=
rmtree
if
is_tree
else
os
.
unlink
remover
(
path
)
@staticmethod
def
_render_version
():
"""
Render the Setuptools version and installation details, then exit.
"""
ver
=
sys
.
version
[:
3
]
dist
=
get_distribution
(
'setuptools'
)
tmpl
=
'setuptools {dist.version} from {dist.location} (Python {ver})'
print
(
tmpl
.
format
(
**
locals
()))
raise
SystemExit
()
def
finalize_options
(
self
):
self
.
version
and
self
.
_render_version
()
py_version
=
sys
.
version
.
split
()[
0
]
prefix
,
exec_prefix
=
get_config_vars
(
'prefix'
,
'exec_prefix'
)
self
.
config_vars
=
{
'dist_name'
:
self
.
distribution
.
get_name
(),
'dist_version'
:
self
.
distribution
.
get_version
(),
'dist_fullname'
:
self
.
distribution
.
get_fullname
(),
'py_version'
:
py_version
,
'py_version_short'
:
py_version
[
0
:
3
],
'py_version_nodot'
:
py_version
[
0
]
+
py_version
[
2
],
'sys_prefix'
:
prefix
,
'prefix'
:
prefix
,
'sys_exec_prefix'
:
exec_prefix
,
'exec_prefix'
:
exec_prefix
,
# Only python 3.2+ has abiflags
'abiflags'
:
getattr
(
sys
,
'abiflags'
,
''
),
}
if
site
.
ENABLE_USER_SITE
:
self
.
config_vars
[
'userbase'
]
=
self
.
install_userbase
self
.
config_vars
[
'usersite'
]
=
self
.
install_usersite
self
.
_fix_install_dir_for_user_site
()
self
.
expand_basedirs
()
self
.
expand_dirs
()
self
.
_expand
(
'install_dir'
,
'script_dir'
,
'build_directory'
,
'site_dirs'
,
)
# If a non-default installation directory was specified, default the
# script directory to match it.
if
self
.
script_dir
is
None
:
self
.
script_dir
=
self
.
install_dir
if
self
.
no_find_links
is
None
:
self
.
no_find_links
=
False
# Let install_dir get set by install_lib command, which in turn
# gets its info from the install command, and takes into account
# --prefix and --home and all that other crud.
self
.
set_undefined_options
(
'install_lib'
,
(
'install_dir'
,
'install_dir'
)
)
# Likewise, set default script_dir from 'install_scripts.install_dir'
self
.
set_undefined_options
(
'install_scripts'
,
(
'install_dir'
,
'script_dir'
)
)
if
self
.
user
and
self
.
install_purelib
:
self
.
install_dir
=
self
.
install_purelib
self
.
script_dir
=
self
.
install_scripts
# default --record from the install command
self
.
set_undefined_options
(
'install'
,
(
'record'
,
'record'
))
# Should this be moved to the if statement below? It's not used
# elsewhere
normpath
=
map
(
normalize_path
,
sys
.
path
)
self
.
all_site_dirs
=
get_site_dirs
()
if
self
.
site_dirs
is
not
None
:
site_dirs
=
[
os
.
path
.
expanduser
(
s
.
strip
())
for
s
in
self
.
site_dirs
.
split
(
','
)
]
for
d
in
site_dirs
:
if
not
os
.
path
.
isdir
(
d
):
log
.
warn
(
"
%
s (in --site-dirs) does not exist"
,
d
)
elif
normalize_path
(
d
)
not
in
normpath
:
raise
DistutilsOptionError
(
d
+
" (in --site-dirs) is not on sys.path"
)
else
:
self
.
all_site_dirs
.
append
(
normalize_path
(
d
))
if
not
self
.
editable
:
self
.
check_site_dir
()
self
.
index_url
=
self
.
index_url
or
"https://pypi.org/simple/"
self
.
shadow_path
=
self
.
all_site_dirs
[:]
for
path_item
in
self
.
install_dir
,
normalize_path
(
self
.
script_dir
):
if
path_item
not
in
self
.
shadow_path
:
self
.
shadow_path
.
insert
(
0
,
path_item
)
if
self
.
allow_hosts
is
not
None
:
hosts
=
[
s
.
strip
()
for
s
in
self
.
allow_hosts
.
split
(
','
)]
else
:
hosts
=
[
'*'
]
if
self
.
package_index
is
None
:
self
.
package_index
=
self
.
create_index
(
self
.
index_url
,
search_path
=
self
.
shadow_path
,
hosts
=
hosts
,
)
self
.
local_index
=
Environment
(
self
.
shadow_path
+
sys
.
path
)
if
self
.
find_links
is
not
None
:
if
isinstance
(
self
.
find_links
,
six
.
string_types
):
self
.
find_links
=
self
.
find_links
.
split
()
else
:
self
.
find_links
=
[]
if
self
.
local_snapshots_ok
:
self
.
package_index
.
scan_egg_links
(
self
.
shadow_path
+
sys
.
path
)
if
not
self
.
no_find_links
:
self
.
package_index
.
add_find_links
(
self
.
find_links
)
self
.
set_undefined_options
(
'install_lib'
,
(
'optimize'
,
'optimize'
))
if
not
isinstance
(
self
.
optimize
,
int
):
try
:
self
.
optimize
=
int
(
self
.
optimize
)
if
not
(
0
<=
self
.
optimize
<=
2
):
raise
ValueError
except
ValueError
:
raise
DistutilsOptionError
(
"--optimize must be 0, 1, or 2"
)
if
self
.
editable
and
not
self
.
build_directory
:
raise
DistutilsArgError
(
"Must specify a build directory (-b) when using --editable"
)
if
not
self
.
args
:
raise
DistutilsArgError
(
"No urls, filenames, or requirements specified (see --help)"
)
self
.
outputs
=
[]
def
_fix_install_dir_for_user_site
(
self
):
"""
Fix the install_dir if "--user" was used.
"""
if
not
self
.
user
or
not
site
.
ENABLE_USER_SITE
:
return
self
.
create_home_path
()
if
self
.
install_userbase
is
None
:
msg
=
"User base directory is not specified"
raise
DistutilsPlatformError
(
msg
)
self
.
install_base
=
self
.
install_platbase
=
self
.
install_userbase
scheme_name
=
os
.
name
.
replace
(
'posix'
,
'unix'
)
+
'_user'
self
.
select_scheme
(
scheme_name
)
def
_expand_attrs
(
self
,
attrs
):
for
attr
in
attrs
:
val
=
getattr
(
self
,
attr
)
if
val
is
not
None
:
if
os
.
name
==
'posix'
or
os
.
name
==
'nt'
:
val
=
os
.
path
.
expanduser
(
val
)
val
=
subst_vars
(
val
,
self
.
config_vars
)
setattr
(
self
,
attr
,
val
)
def
expand_basedirs
(
self
):
"""Calls `os.path.expanduser` on install_base, install_platbase and
root."""
self
.
_expand_attrs
([
'install_base'
,
'install_platbase'
,
'root'
])
def
expand_dirs
(
self
):
"""Calls `os.path.expanduser` on install dirs."""
dirs
=
[
'install_purelib'
,
'install_platlib'
,
'install_lib'
,
'install_headers'
,
'install_scripts'
,
'install_data'
,
]
self
.
_expand_attrs
(
dirs
)
def
run
(
self
):
if
self
.
verbose
!=
self
.
distribution
.
verbose
:
log
.
set_verbosity
(
self
.
verbose
)
try
:
for
spec
in
self
.
args
:
self
.
easy_install
(
spec
,
not
self
.
no_deps
)
if
self
.
record
:
outputs
=
self
.
outputs
if
self
.
root
:
# strip any package prefix
root_len
=
len
(
self
.
root
)
for
counter
in
range
(
len
(
outputs
)):
outputs
[
counter
]
=
outputs
[
counter
][
root_len
:]
from
distutils
import
file_util
self
.
execute
(
file_util
.
write_file
,
(
self
.
record
,
outputs
),
"writing list of installed files to '
%
s'"
%
self
.
record
)
self
.
warn_deprecated_options
()
finally
:
log
.
set_verbosity
(
self
.
distribution
.
verbose
)
def
pseudo_tempname
(
self
):
"""Return a pseudo-tempname base in the install directory.
This code is intentionally naive; if a malicious party can write to
the target directory you're already in deep doodoo.
"""
try
:
pid
=
os
.
getpid
()
except
Exception
:
pid
=
random
.
randint
(
0
,
sys
.
maxsize
)
return
os
.
path
.
join
(
self
.
install_dir
,
"test-easy-install-
%
s"
%
pid
)
def
warn_deprecated_options
(
self
):
pass
def
check_site_dir
(
self
):
"""Verify that self.install_dir is .pth-capable dir, if needed"""
instdir
=
normalize_path
(
self
.
install_dir
)
pth_file
=
os
.
path
.
join
(
instdir
,
'easy-install.pth'
)
# Is it a configured, PYTHONPATH, implicit, or explicit site dir?
is_site_dir
=
instdir
in
self
.
all_site_dirs
if
not
is_site_dir
and
not
self
.
multi_version
:
# No? Then directly test whether it does .pth file processing
is_site_dir
=
self
.
check_pth_processing
()
else
:
# make sure we can write to target dir
testfile
=
self
.
pseudo_tempname
()
+
'.write-test'
test_exists
=
os
.
path
.
exists
(
testfile
)
try
:
if
test_exists
:
os
.
unlink
(
testfile
)
open
(
testfile
,
'w'
)
.
close
()
os
.
unlink
(
testfile
)
except
(
OSError
,
IOError
):
self
.
cant_write_to_target
()
if
not
is_site_dir
and
not
self
.
multi_version
:
# Can't install non-multi to non-site dir
raise
DistutilsError
(
self
.
no_default_version_msg
())
if
is_site_dir
:
if
self
.
pth_file
is
None
:
self
.
pth_file
=
PthDistributions
(
pth_file
,
self
.
all_site_dirs
)
else
:
self
.
pth_file
=
None
if
instdir
not
in
map
(
normalize_path
,
_pythonpath
()):
# only PYTHONPATH dirs need a site.py, so pretend it's there
self
.
sitepy_installed
=
True
elif
self
.
multi_version
and
not
os
.
path
.
exists
(
pth_file
):
self
.
sitepy_installed
=
True
# don't need site.py in this case
self
.
pth_file
=
None
# and don't create a .pth file
self
.
install_dir
=
instdir
__cant_write_msg
=
textwrap
.
dedent
(
"""
can't create or remove files in install directory
The following error occurred while trying to add or remove files in the
installation directory:
%
s
The installation directory you specified (via --install-dir, --prefix, or
the distutils default setting) was:
%
s
"""
)
.
lstrip
()
__not_exists_id
=
textwrap
.
dedent
(
"""
This directory does not currently exist. Please create it and try again, or
choose a different installation directory (using the -d or --install-dir
option).
"""
)
.
lstrip
()
__access_msg
=
textwrap
.
dedent
(
"""
Perhaps your account does not have write access to this directory? If the
installation directory is a system-owned directory, you may need to sign in
as the administrator or "root" account. If you do not have administrative
access to this machine, you may wish to choose a different installation
directory, preferably one that is listed in your PYTHONPATH environment
variable.
For information on other options, you may wish to consult the
documentation at:
https://setuptools.readthedocs.io/en/latest/easy_install.html
Please make the appropriate changes for your system and try again.
"""
)
.
lstrip
()
def
cant_write_to_target
(
self
):
msg
=
self
.
__cant_write_msg
%
(
sys
.
exc_info
()[
1
],
self
.
install_dir
,)
if
not
os
.
path
.
exists
(
self
.
install_dir
):
msg
+=
'
\n
'
+
self
.
__not_exists_id
else
:
msg
+=
'
\n
'
+
self
.
__access_msg
raise
DistutilsError
(
msg
)
def
check_pth_processing
(
self
):
"""Empirically verify whether .pth files are supported in inst. dir"""
instdir
=
self
.
install_dir
log
.
info
(
"Checking .pth file support in
%
s"
,
instdir
)
pth_file
=
self
.
pseudo_tempname
()
+
".pth"
ok_file
=
pth_file
+
'.ok'
ok_exists
=
os
.
path
.
exists
(
ok_file
)
tmpl
=
_one_liner
(
"""
import os
f = open({ok_file!r}, 'w')
f.write('OK')
f.close()
"""
)
+
'
\n
'
try
:
if
ok_exists
:
os
.
unlink
(
ok_file
)
dirname
=
os
.
path
.
dirname
(
ok_file
)
pkg_resources
.
py31compat
.
makedirs
(
dirname
,
exist_ok
=
True
)
f
=
open
(
pth_file
,
'w'
)
except
(
OSError
,
IOError
):
self
.
cant_write_to_target
()
else
:
try
:
f
.
write
(
tmpl
.
format
(
**
locals
()))
f
.
close
()
f
=
None
executable
=
sys
.
executable
if
os
.
name
==
'nt'
:
dirname
,
basename
=
os
.
path
.
split
(
executable
)
alt
=
os
.
path
.
join
(
dirname
,
'pythonw.exe'
)
use_alt
=
(
basename
.
lower
()
==
'python.exe'
and
os
.
path
.
exists
(
alt
)
)
if
use_alt
:
# use pythonw.exe to avoid opening a console window
executable
=
alt
from
distutils.spawn
import
spawn
spawn
([
executable
,
'-E'
,
'-c'
,
'pass'
],
0
)
if
os
.
path
.
exists
(
ok_file
):
log
.
info
(
"TEST PASSED:
%
s appears to support .pth files"
,
instdir
)
return
True
finally
:
if
f
:
f
.
close
()
if
os
.
path
.
exists
(
ok_file
):
os
.
unlink
(
ok_file
)
if
os
.
path
.
exists
(
pth_file
):
os
.
unlink
(
pth_file
)
if
not
self
.
multi_version
:
log
.
warn
(
"TEST FAILED:
%
s does NOT support .pth files"
,
instdir
)
return
False
def
install_egg_scripts
(
self
,
dist
):
"""Write all the scripts for `dist`, unless scripts are excluded"""
if
not
self
.
exclude_scripts
and
dist
.
metadata_isdir
(
'scripts'
):
for
script_name
in
dist
.
metadata_listdir
(
'scripts'
):
if
dist
.
metadata_isdir
(
'scripts/'
+
script_name
):
# The "script" is a directory, likely a Python 3
# __pycache__ directory, so skip it.
continue
self
.
install_script
(
dist
,
script_name
,
dist
.
get_metadata
(
'scripts/'
+
script_name
)
)
self
.
install_wrapper_scripts
(
dist
)
def
add_output
(
self
,
path
):
if
os
.
path
.
isdir
(
path
):
for
base
,
dirs
,
files
in
os
.
walk
(
path
):
for
filename
in
files
:
self
.
outputs
.
append
(
os
.
path
.
join
(
base
,
filename
))
else
:
self
.
outputs
.
append
(
path
)
def
not_editable
(
self
,
spec
):
if
self
.
editable
:
raise
DistutilsArgError
(
"Invalid argument
%
r: you can't use filenames or URLs "
"with --editable (except via the --find-links option)."
%
(
spec
,)
)
def
check_editable
(
self
,
spec
):
if
not
self
.
editable
:
return
if
os
.
path
.
exists
(
os
.
path
.
join
(
self
.
build_directory
,
spec
.
key
)):
raise
DistutilsArgError
(
"
%
r already exists in
%
s; can't do a checkout there"
%
(
spec
.
key
,
self
.
build_directory
)
)
@contextlib.contextmanager
def
_tmpdir
(
self
):
tmpdir
=
tempfile
.
mkdtemp
(
prefix
=
u"easy_install-"
)
try
:
# cast to str as workaround for #709 and #710 and #712
yield
str
(
tmpdir
)
finally
:
os
.
path
.
exists
(
tmpdir
)
and
rmtree
(
rmtree_safe
(
tmpdir
))
def
easy_install
(
self
,
spec
,
deps
=
False
):
if
not
self
.
editable
:
self
.
install_site_py
()
with
self
.
_tmpdir
()
as
tmpdir
:
if
not
isinstance
(
spec
,
Requirement
):
if
URL_SCHEME
(
spec
):
# It's a url, download it to tmpdir and process
self
.
not_editable
(
spec
)
dl
=
self
.
package_index
.
download
(
spec
,
tmpdir
)
return
self
.
install_item
(
None
,
dl
,
tmpdir
,
deps
,
True
)
elif
os
.
path
.
exists
(
spec
):
# Existing file or directory, just process it directly
self
.
not_editable
(
spec
)
return
self
.
install_item
(
None
,
spec
,
tmpdir
,
deps
,
True
)
else
:
spec
=
parse_requirement_arg
(
spec
)
self
.
check_editable
(
spec
)
dist
=
self
.
package_index
.
fetch_distribution
(
spec
,
tmpdir
,
self
.
upgrade
,
self
.
editable
,
not
self
.
always_copy
,
self
.
local_index
)
if
dist
is
None
:
msg
=
"Could not find suitable distribution for
%
r"
%
spec
if
self
.
always_copy
:
msg
+=
" (--always-copy skips system and development eggs)"
raise
DistutilsError
(
msg
)
elif
dist
.
precedence
==
DEVELOP_DIST
:
# .egg-info dists don't need installing, just process deps
self
.
process_distribution
(
spec
,
dist
,
deps
,
"Using"
)
return
dist
else
:
return
self
.
install_item
(
spec
,
dist
.
location
,
tmpdir
,
deps
)
def
install_item
(
self
,
spec
,
download
,
tmpdir
,
deps
,
install_needed
=
False
):
# Installation is also needed if file in tmpdir or is not an egg
install_needed
=
install_needed
or
self
.
always_copy
install_needed
=
install_needed
or
os
.
path
.
dirname
(
download
)
==
tmpdir
install_needed
=
install_needed
or
not
download
.
endswith
(
'.egg'
)
install_needed
=
install_needed
or
(
self
.
always_copy_from
is
not
None
and
os
.
path
.
dirname
(
normalize_path
(
download
))
==
normalize_path
(
self
.
always_copy_from
)
)
if
spec
and
not
install_needed
:
# at this point, we know it's a local .egg, we just don't know if
# it's already installed.
for
dist
in
self
.
local_index
[
spec
.
project_name
]:
if
dist
.
location
==
download
:
break
else
:
install_needed
=
True
# it's not in the local index
log
.
info
(
"Processing
%
s"
,
os
.
path
.
basename
(
download
))
if
install_needed
:
dists
=
self
.
install_eggs
(
spec
,
download
,
tmpdir
)
for
dist
in
dists
:
self
.
process_distribution
(
spec
,
dist
,
deps
)
else
:
dists
=
[
self
.
egg_distribution
(
download
)]
self
.
process_distribution
(
spec
,
dists
[
0
],
deps
,
"Using"
)
if
spec
is
not
None
:
for
dist
in
dists
:
if
dist
in
spec
:
return
dist
def
select_scheme
(
self
,
name
):
"""Sets the install directories by applying the install schemes."""
# it's the caller's problem if they supply a bad name!
scheme
=
INSTALL_SCHEMES
[
name
]
for
key
in
SCHEME_KEYS
:
attrname
=
'install_'
+
key
if
getattr
(
self
,
attrname
)
is
None
:
setattr
(
self
,
attrname
,
scheme
[
key
])
def
process_distribution
(
self
,
requirement
,
dist
,
deps
=
True
,
*
info
):
self
.
update_pth
(
dist
)
self
.
package_index
.
add
(
dist
)
if
dist
in
self
.
local_index
[
dist
.
key
]:
self
.
local_index
.
remove
(
dist
)
self
.
local_index
.
add
(
dist
)
self
.
install_egg_scripts
(
dist
)
self
.
installed_projects
[
dist
.
key
]
=
dist
log
.
info
(
self
.
installation_report
(
requirement
,
dist
,
*
info
))
if
(
dist
.
has_metadata
(
'dependency_links.txt'
)
and
not
self
.
no_find_links
):
self
.
package_index
.
add_find_links
(
dist
.
get_metadata_lines
(
'dependency_links.txt'
)
)
if
not
deps
and
not
self
.
always_copy
:
return
elif
requirement
is
not
None
and
dist
.
key
!=
requirement
.
key
:
log
.
warn
(
"Skipping dependencies for
%
s"
,
dist
)
return
# XXX this is not the distribution we were looking for
elif
requirement
is
None
or
dist
not
in
requirement
:
# if we wound up with a different version, resolve what we've got
distreq
=
dist
.
as_requirement
()
requirement
=
Requirement
(
str
(
distreq
))
log
.
info
(
"Processing dependencies for
%
s"
,
requirement
)
try
:
distros
=
WorkingSet
([])
.
resolve
(
[
requirement
],
self
.
local_index
,
self
.
easy_install
)
except
DistributionNotFound
as
e
:
raise
DistutilsError
(
str
(
e
))
except
VersionConflict
as
e
:
raise
DistutilsError
(
e
.
report
())
if
self
.
always_copy
or
self
.
always_copy_from
:
# Force all the relevant distros to be copied or activated
for
dist
in
distros
:
if
dist
.
key
not
in
self
.
installed_projects
:
self
.
easy_install
(
dist
.
as_requirement
())
log
.
info
(
"Finished processing dependencies for
%
s"
,
requirement
)
def
should_unzip
(
self
,
dist
):
if
self
.
zip_ok
is
not
None
:
return
not
self
.
zip_ok
if
dist
.
has_metadata
(
'not-zip-safe'
):
return
True
if
not
dist
.
has_metadata
(
'zip-safe'
):
return
True
return
False
def
maybe_move
(
self
,
spec
,
dist_filename
,
setup_base
):
dst
=
os
.
path
.
join
(
self
.
build_directory
,
spec
.
key
)
if
os
.
path
.
exists
(
dst
):
msg
=
(
"
%
r already exists in
%
s; build directory
%
s will not be kept"
)
log
.
warn
(
msg
,
spec
.
key
,
self
.
build_directory
,
setup_base
)
return
setup_base
if
os
.
path
.
isdir
(
dist_filename
):
setup_base
=
dist_filename
else
:
if
os
.
path
.
dirname
(
dist_filename
)
==
setup_base
:
os
.
unlink
(
dist_filename
)
# get it out of the tmp dir
contents
=
os
.
listdir
(
setup_base
)
if
len
(
contents
)
==
1
:
dist_filename
=
os
.
path
.
join
(
setup_base
,
contents
[
0
])
if
os
.
path
.
isdir
(
dist_filename
):
# if the only thing there is a directory, move it instead
setup_base
=
dist_filename
ensure_directory
(
dst
)
shutil
.
move
(
setup_base
,
dst
)
return
dst
def
install_wrapper_scripts
(
self
,
dist
):
if
self
.
exclude_scripts
:
return
for
args
in
ScriptWriter
.
best
()
.
get_args
(
dist
):
self
.
write_script
(
*
args
)
def
install_script
(
self
,
dist
,
script_name
,
script_text
,
dev_path
=
None
):
"""Generate a legacy script wrapper and install it"""
spec
=
str
(
dist
.
as_requirement
())
is_script
=
is_python_script
(
script_text
,
script_name
)
if
is_script
:
body
=
self
.
_load_template
(
dev_path
)
%
locals
()
script_text
=
ScriptWriter
.
get_header
(
script_text
)
+
body
self
.
write_script
(
script_name
,
_to_bytes
(
script_text
),
'b'
)
@staticmethod
def
_load_template
(
dev_path
):
"""
There are a couple of template scripts in the package. This
function loads one of them and prepares it for use.
"""
# See https://github.com/pypa/setuptools/issues/134 for info
# on script file naming and downstream issues with SVR4
name
=
'script.tmpl'
if
dev_path
:
name
=
name
.
replace
(
'.tmpl'
,
' (dev).tmpl'
)
raw_bytes
=
resource_string
(
'setuptools'
,
name
)
return
raw_bytes
.
decode
(
'utf-8'
)
def
write_script
(
self
,
script_name
,
contents
,
mode
=
"t"
,
blockers
=
()):
"""Write an executable file to the scripts directory"""
self
.
delete_blockers
(
# clean up old .py/.pyw w/o a script
[
os
.
path
.
join
(
self
.
script_dir
,
x
)
for
x
in
blockers
]
)
log
.
info
(
"Installing
%
s script to
%
s"
,
script_name
,
self
.
script_dir
)
target
=
os
.
path
.
join
(
self
.
script_dir
,
script_name
)
self
.
add_output
(
target
)
if
self
.
dry_run
:
return
mask
=
current_umask
()
ensure_directory
(
target
)
if
os
.
path
.
exists
(
target
):
os
.
unlink
(
target
)
with
open
(
target
,
"w"
+
mode
)
as
f
:
f
.
write
(
contents
)
chmod
(
target
,
0
o777
-
mask
)
def
install_eggs
(
self
,
spec
,
dist_filename
,
tmpdir
):
# .egg dirs or files are already built, so just return them
if
dist_filename
.
lower
()
.
endswith
(
'.egg'
):
return
[
self
.
install_egg
(
dist_filename
,
tmpdir
)]
elif
dist_filename
.
lower
()
.
endswith
(
'.exe'
):
return
[
self
.
install_exe
(
dist_filename
,
tmpdir
)]
elif
dist_filename
.
lower
()
.
endswith
(
'.whl'
):
return
[
self
.
install_wheel
(
dist_filename
,
tmpdir
)]
# Anything else, try to extract and build
setup_base
=
tmpdir
if
os
.
path
.
isfile
(
dist_filename
)
and
not
dist_filename
.
endswith
(
'.py'
):
unpack_archive
(
dist_filename
,
tmpdir
,
self
.
unpack_progress
)
elif
os
.
path
.
isdir
(
dist_filename
):
setup_base
=
os
.
path
.
abspath
(
dist_filename
)
if
(
setup_base
.
startswith
(
tmpdir
)
# something we downloaded
and
self
.
build_directory
and
spec
is
not
None
):
setup_base
=
self
.
maybe_move
(
spec
,
dist_filename
,
setup_base
)
# Find the setup.py file
setup_script
=
os
.
path
.
join
(
setup_base
,
'setup.py'
)
if
not
os
.
path
.
exists
(
setup_script
):
setups
=
glob
(
os
.
path
.
join
(
setup_base
,
'*'
,
'setup.py'
))
if
not
setups
:
raise
DistutilsError
(
"Couldn't find a setup script in
%
s"
%
os
.
path
.
abspath
(
dist_filename
)
)
if
len
(
setups
)
>
1
:
raise
DistutilsError
(
"Multiple setup scripts in
%
s"
%
os
.
path
.
abspath
(
dist_filename
)
)
setup_script
=
setups
[
0
]
# Now run it, and return the result
if
self
.
editable
:
log
.
info
(
self
.
report_editable
(
spec
,
setup_script
))
return
[]
else
:
return
self
.
build_and_install
(
setup_script
,
setup_base
)
def
egg_distribution
(
self
,
egg_path
):
if
os
.
path
.
isdir
(
egg_path
):
metadata
=
PathMetadata
(
egg_path
,
os
.
path
.
join
(
egg_path
,
'EGG-INFO'
))
else
:
metadata
=
EggMetadata
(
zipimport
.
zipimporter
(
egg_path
))
return
Distribution
.
from_filename
(
egg_path
,
metadata
=
metadata
)
def
install_egg
(
self
,
egg_path
,
tmpdir
):
destination
=
os
.
path
.
join
(
self
.
install_dir
,
os
.
path
.
basename
(
egg_path
),
)
destination
=
os
.
path
.
abspath
(
destination
)
if
not
self
.
dry_run
:
ensure_directory
(
destination
)
dist
=
self
.
egg_distribution
(
egg_path
)
if
not
samefile
(
egg_path
,
destination
):
if
os
.
path
.
isdir
(
destination
)
and
not
os
.
path
.
islink
(
destination
):
dir_util
.
remove_tree
(
destination
,
dry_run
=
self
.
dry_run
)
elif
os
.
path
.
exists
(
destination
):
self
.
execute
(
os
.
unlink
,
(
destination
,),
"Removing "
+
destination
,
)
try
:
new_dist_is_zipped
=
False
if
os
.
path
.
isdir
(
egg_path
):
if
egg_path
.
startswith
(
tmpdir
):
f
,
m
=
shutil
.
move
,
"Moving"
else
:
f
,
m
=
shutil
.
copytree
,
"Copying"
elif
self
.
should_unzip
(
dist
):
self
.
mkpath
(
destination
)
f
,
m
=
self
.
unpack_and_compile
,
"Extracting"
else
:
new_dist_is_zipped
=
True
if
egg_path
.
startswith
(
tmpdir
):
f
,
m
=
shutil
.
move
,
"Moving"
else
:
f
,
m
=
shutil
.
copy2
,
"Copying"
self
.
execute
(
f
,
(
egg_path
,
destination
),
(
m
+
"
%
s to
%
s"
)
%
(
os
.
path
.
basename
(
egg_path
),
os
.
path
.
dirname
(
destination
)
),
)
update_dist_caches
(
destination
,
fix_zipimporter_caches
=
new_dist_is_zipped
,
)
except
Exception
:
update_dist_caches
(
destination
,
fix_zipimporter_caches
=
False
)
raise
self
.
add_output
(
destination
)
return
self
.
egg_distribution
(
destination
)
def
install_exe
(
self
,
dist_filename
,
tmpdir
):
# See if it's valid, get data
cfg
=
extract_wininst_cfg
(
dist_filename
)
if
cfg
is
None
:
raise
DistutilsError
(
"
%
s is not a valid distutils Windows .exe"
%
dist_filename
)
# Create a dummy distribution object until we build the real distro
dist
=
Distribution
(
None
,
project_name
=
cfg
.
get
(
'metadata'
,
'name'
),
version
=
cfg
.
get
(
'metadata'
,
'version'
),
platform
=
get_platform
(),
)
# Convert the .exe to an unpacked egg
egg_path
=
os
.
path
.
join
(
tmpdir
,
dist
.
egg_name
()
+
'.egg'
)
dist
.
location
=
egg_path
egg_tmp
=
egg_path
+
'.tmp'
_egg_info
=
os
.
path
.
join
(
egg_tmp
,
'EGG-INFO'
)
pkg_inf
=
os
.
path
.
join
(
_egg_info
,
'PKG-INFO'
)
ensure_directory
(
pkg_inf
)
# make sure EGG-INFO dir exists
dist
.
_provider
=
PathMetadata
(
egg_tmp
,
_egg_info
)
# XXX
self
.
exe_to_egg
(
dist_filename
,
egg_tmp
)
# Write EGG-INFO/PKG-INFO
if
not
os
.
path
.
exists
(
pkg_inf
):
f
=
open
(
pkg_inf
,
'w'
)
f
.
write
(
'Metadata-Version: 1.0
\n
'
)
for
k
,
v
in
cfg
.
items
(
'metadata'
):
if
k
!=
'target_version'
:
f
.
write
(
'
%
s:
%
s
\n
'
%
(
k
.
replace
(
'_'
,
'-'
)
.
title
(),
v
))
f
.
close
()
script_dir
=
os
.
path
.
join
(
_egg_info
,
'scripts'
)
# delete entry-point scripts to avoid duping
self
.
delete_blockers
([
os
.
path
.
join
(
script_dir
,
args
[
0
])
for
args
in
ScriptWriter
.
get_args
(
dist
)
])
# Build .egg file from tmpdir
bdist_egg
.
make_zipfile
(
egg_path
,
egg_tmp
,
verbose
=
self
.
verbose
,
dry_run
=
self
.
dry_run
,
)
# install the .egg
return
self
.
install_egg
(
egg_path
,
tmpdir
)
def
exe_to_egg
(
self
,
dist_filename
,
egg_tmp
):
"""Extract a bdist_wininst to the directories an egg would use"""
# Check for .pth file and set up prefix translations
prefixes
=
get_exe_prefixes
(
dist_filename
)
to_compile
=
[]
native_libs
=
[]
top_level
=
{}
def
process
(
src
,
dst
):
s
=
src
.
lower
()
for
old
,
new
in
prefixes
:
if
s
.
startswith
(
old
):
src
=
new
+
src
[
len
(
old
):]
parts
=
src
.
split
(
'/'
)
dst
=
os
.
path
.
join
(
egg_tmp
,
*
parts
)
dl
=
dst
.
lower
()
if
dl
.
endswith
(
'.pyd'
)
or
dl
.
endswith
(
'.dll'
):
parts
[
-
1
]
=
bdist_egg
.
strip_module
(
parts
[
-
1
])
top_level
[
os
.
path
.
splitext
(
parts
[
0
])[
0
]]
=
1
native_libs
.
append
(
src
)
elif
dl
.
endswith
(
'.py'
)
and
old
!=
'SCRIPTS/'
:
top_level
[
os
.
path
.
splitext
(
parts
[
0
])[
0
]]
=
1
to_compile
.
append
(
dst
)
return
dst
if
not
src
.
endswith
(
'.pth'
):
log
.
warn
(
"WARNING: can't process
%
s"
,
src
)
return
None
# extract, tracking .pyd/.dll->native_libs and .py -> to_compile
unpack_archive
(
dist_filename
,
egg_tmp
,
process
)
stubs
=
[]
for
res
in
native_libs
:
if
res
.
lower
()
.
endswith
(
'.pyd'
):
# create stubs for .pyd's
parts
=
res
.
split
(
'/'
)
resource
=
parts
[
-
1
]
parts
[
-
1
]
=
bdist_egg
.
strip_module
(
parts
[
-
1
])
+
'.py'
pyfile
=
os
.
path
.
join
(
egg_tmp
,
*
parts
)
to_compile
.
append
(
pyfile
)
stubs
.
append
(
pyfile
)
bdist_egg
.
write_stub
(
resource
,
pyfile
)
self
.
byte_compile
(
to_compile
)
# compile .py's
bdist_egg
.
write_safety_flag
(
os
.
path
.
join
(
egg_tmp
,
'EGG-INFO'
),
bdist_egg
.
analyze_egg
(
egg_tmp
,
stubs
))
# write zip-safety flag
for
name
in
'top_level'
,
'native_libs'
:
if
locals
()[
name
]:
txt
=
os
.
path
.
join
(
egg_tmp
,
'EGG-INFO'
,
name
+
'.txt'
)
if
not
os
.
path
.
exists
(
txt
):
f
=
open
(
txt
,
'w'
)
f
.
write
(
'
\n
'
.
join
(
locals
()[
name
])
+
'
\n
'
)
f
.
close
()
def
install_wheel
(
self
,
wheel_path
,
tmpdir
):
wheel
=
Wheel
(
wheel_path
)
assert
wheel
.
is_compatible
()
destination
=
os
.
path
.
join
(
self
.
install_dir
,
wheel
.
egg_name
())
destination
=
os
.
path
.
abspath
(
destination
)
if
not
self
.
dry_run
:
ensure_directory
(
destination
)
if
os
.
path
.
isdir
(
destination
)
and
not
os
.
path
.
islink
(
destination
):
dir_util
.
remove_tree
(
destination
,
dry_run
=
self
.
dry_run
)
elif
os
.
path
.
exists
(
destination
):
self
.
execute
(
os
.
unlink
,
(
destination
,),
"Removing "
+
destination
,
)
try
:
self
.
execute
(
wheel
.
install_as_egg
,
(
destination
,),
(
"Installing
%
s to
%
s"
)
%
(
os
.
path
.
basename
(
wheel_path
),
os
.
path
.
dirname
(
destination
)
),
)
finally
:
update_dist_caches
(
destination
,
fix_zipimporter_caches
=
False
)
self
.
add_output
(
destination
)
return
self
.
egg_distribution
(
destination
)
__mv_warning
=
textwrap
.
dedent
(
"""
Because this distribution was installed --multi-version, before you can
import modules from this package in an application, you will need to
'import pkg_resources' and then use a 'require()' call similar to one of
these examples, in order to select the desired version:
pkg_resources.require("
%(name)
s") # latest installed version
pkg_resources.require("
%(name)
s==
%(version)
s") # this exact version
pkg_resources.require("
%(name)
s>=
%(version)
s") # this version or higher
"""
)
.
lstrip
()
__id_warning
=
textwrap
.
dedent
(
"""
Note also that the installation directory must be on sys.path at runtime for
this to work. (e.g. by being the application's script directory, by being on
PYTHONPATH, or by being added to sys.path by your code.)
"""
)
def
installation_report
(
self
,
req
,
dist
,
what
=
"Installed"
):
"""Helpful installation message for display to package users"""
msg
=
"
\n
%(what)
s
%(eggloc)
s
%(extras)
s"
if
self
.
multi_version
and
not
self
.
no_report
:
msg
+=
'
\n
'
+
self
.
__mv_warning
if
self
.
install_dir
not
in
map
(
normalize_path
,
sys
.
path
):
msg
+=
'
\n
'
+
self
.
__id_warning
eggloc
=
dist
.
location
name
=
dist
.
project_name
version
=
dist
.
version
extras
=
''
# TODO: self.report_extras(req, dist)
return
msg
%
locals
()
__editable_msg
=
textwrap
.
dedent
(
"""
Extracted editable version of
%(spec)
s to
%(dirname)
s
If it uses setuptools in its setup script, you can activate it in
"development" mode by going to that directory and running::
%(python)
s setup.py develop
See the setuptools documentation for the "develop" command for more info.
"""
)
.
lstrip
()
def
report_editable
(
self
,
spec
,
setup_script
):
dirname
=
os
.
path
.
dirname
(
setup_script
)
python
=
sys
.
executable
return
'
\n
'
+
self
.
__editable_msg
%
locals
()
def
run_setup
(
self
,
setup_script
,
setup_base
,
args
):
sys
.
modules
.
setdefault
(
'distutils.command.bdist_egg'
,
bdist_egg
)
sys
.
modules
.
setdefault
(
'distutils.command.egg_info'
,
egg_info
)
args
=
list
(
args
)
if
self
.
verbose
>
2
:
v
=
'v'
*
(
self
.
verbose
-
1
)
args
.
insert
(
0
,
'-'
+
v
)
elif
self
.
verbose
<
2
:
args
.
insert
(
0
,
'-q'
)
if
self
.
dry_run
:
args
.
insert
(
0
,
'-n'
)
log
.
info
(
"Running
%
s
%
s"
,
setup_script
[
len
(
setup_base
)
+
1
:],
' '
.
join
(
args
)
)
try
:
run_setup
(
setup_script
,
args
)
except
SystemExit
as
v
:
raise
DistutilsError
(
"Setup script exited with
%
s"
%
(
v
.
args
[
0
],))
def
build_and_install
(
self
,
setup_script
,
setup_base
):
args
=
[
'bdist_egg'
,
'--dist-dir'
]
dist_dir
=
tempfile
.
mkdtemp
(
prefix
=
'egg-dist-tmp-'
,
dir
=
os
.
path
.
dirname
(
setup_script
)
)
try
:
self
.
_set_fetcher_options
(
os
.
path
.
dirname
(
setup_script
))
args
.
append
(
dist_dir
)
self
.
run_setup
(
setup_script
,
setup_base
,
args
)
all_eggs
=
Environment
([
dist_dir
])
eggs
=
[]
for
key
in
all_eggs
:
for
dist
in
all_eggs
[
key
]:
eggs
.
append
(
self
.
install_egg
(
dist
.
location
,
setup_base
))
if
not
eggs
and
not
self
.
dry_run
:
log
.
warn
(
"No eggs found in
%
s (setup script problem?)"
,
dist_dir
)
return
eggs
finally
:
rmtree
(
dist_dir
)
log
.
set_verbosity
(
self
.
verbose
)
# restore our log verbosity
def
_set_fetcher_options
(
self
,
base
):
"""
When easy_install is about to run bdist_egg on a source dist, that
source dist might have 'setup_requires' directives, requiring
additional fetching. Ensure the fetcher options given to easy_install
are available to that command as well.
"""
# find the fetch options from easy_install and write them out
# to the setup.cfg file.
ei_opts
=
self
.
distribution
.
get_option_dict
(
'easy_install'
)
.
copy
()
fetch_directives
=
(
'find_links'
,
'site_dirs'
,
'index_url'
,
'optimize'
,
'site_dirs'
,
'allow_hosts'
,
)
fetch_options
=
{}
for
key
,
val
in
ei_opts
.
items
():
if
key
not
in
fetch_directives
:
continue
fetch_options
[
key
.
replace
(
'_'
,
'-'
)]
=
val
[
1
]
# create a settings dictionary suitable for `edit_config`
settings
=
dict
(
easy_install
=
fetch_options
)
cfg_filename
=
os
.
path
.
join
(
base
,
'setup.cfg'
)
setopt
.
edit_config
(
cfg_filename
,
settings
)
def
update_pth
(
self
,
dist
):
if
self
.
pth_file
is
None
:
return
for
d
in
self
.
pth_file
[
dist
.
key
]:
# drop old entries
if
self
.
multi_version
or
d
.
location
!=
dist
.
location
:
log
.
info
(
"Removing
%
s from easy-install.pth file"
,
d
)
self
.
pth_file
.
remove
(
d
)
if
d
.
location
in
self
.
shadow_path
:
self
.
shadow_path
.
remove
(
d
.
location
)
if
not
self
.
multi_version
:
if
dist
.
location
in
self
.
pth_file
.
paths
:
log
.
info
(
"
%
s is already the active version in easy-install.pth"
,
dist
,
)
else
:
log
.
info
(
"Adding
%
s to easy-install.pth file"
,
dist
)
self
.
pth_file
.
add
(
dist
)
# add new entry
if
dist
.
location
not
in
self
.
shadow_path
:
self
.
shadow_path
.
append
(
dist
.
location
)
if
not
self
.
dry_run
:
self
.
pth_file
.
save
()
if
dist
.
key
==
'setuptools'
:
# Ensure that setuptools itself never becomes unavailable!
# XXX should this check for latest version?
filename
=
os
.
path
.
join
(
self
.
install_dir
,
'setuptools.pth'
)
if
os
.
path
.
islink
(
filename
):
os
.
unlink
(
filename
)
f
=
open
(
filename
,
'wt'
)
f
.
write
(
self
.
pth_file
.
make_relative
(
dist
.
location
)
+
'
\n
'
)
f
.
close
()
def
unpack_progress
(
self
,
src
,
dst
):
# Progress filter for unpacking
log
.
debug
(
"Unpacking
%
s to
%
s"
,
src
,
dst
)
return
dst
# only unpack-and-compile skips files for dry run
def
unpack_and_compile
(
self
,
egg_path
,
destination
):
to_compile
=
[]
to_chmod
=
[]
def
pf
(
src
,
dst
):
if
dst
.
endswith
(
'.py'
)
and
not
src
.
startswith
(
'EGG-INFO/'
):
to_compile
.
append
(
dst
)
elif
dst
.
endswith
(
'.dll'
)
or
dst
.
endswith
(
'.so'
):
to_chmod
.
append
(
dst
)
self
.
unpack_progress
(
src
,
dst
)
return
not
self
.
dry_run
and
dst
or
None
unpack_archive
(
egg_path
,
destination
,
pf
)
self
.
byte_compile
(
to_compile
)
if
not
self
.
dry_run
:
for
f
in
to_chmod
:
mode
=
((
os
.
stat
(
f
)[
stat
.
ST_MODE
])
|
0
o555
)
&
0
o7755
chmod
(
f
,
mode
)
def
byte_compile
(
self
,
to_compile
):
if
sys
.
dont_write_bytecode
:
return
from
distutils.util
import
byte_compile
try
:
# try to make the byte compile messages quieter
log
.
set_verbosity
(
self
.
verbose
-
1
)
byte_compile
(
to_compile
,
optimize
=
0
,
force
=
1
,
dry_run
=
self
.
dry_run
)
if
self
.
optimize
:
byte_compile
(
to_compile
,
optimize
=
self
.
optimize
,
force
=
1
,
dry_run
=
self
.
dry_run
,
)
finally
:
log
.
set_verbosity
(
self
.
verbose
)
# restore original verbosity
__no_default_msg
=
textwrap
.
dedent
(
"""
bad install directory or PYTHONPATH
You are attempting to install a package to a directory that is not
on PYTHONPATH and which Python does not read ".pth" files from. The
installation directory you specified (via --install-dir, --prefix, or
the distutils default setting) was:
%
s
and your PYTHONPATH environment variable currently contains:
%
r
Here are some of your options for correcting the problem:
* You can choose a different installation directory, i.e., one that is
on PYTHONPATH or supports .pth files
* You can add the installation directory to the PYTHONPATH environment
variable. (It must then also be on PYTHONPATH whenever you run
Python and want to use the package(s) you are installing.)
* You can set up the installation directory to support ".pth" files by
using one of the approaches described here:
https://setuptools.readthedocs.io/en/latest/easy_install.html#custom-installation-locations
Please make the appropriate changes for your system and try again."""
)
.
lstrip
()
def
no_default_version_msg
(
self
):
template
=
self
.
__no_default_msg
return
template
%
(
self
.
install_dir
,
os
.
environ
.
get
(
'PYTHONPATH'
,
''
))
def
install_site_py
(
self
):
"""Make sure there's a site.py in the target dir, if needed"""
if
self
.
sitepy_installed
:
return
# already did it, or don't need to
sitepy
=
os
.
path
.
join
(
self
.
install_dir
,
"site.py"
)
source
=
resource_string
(
"setuptools"
,
"site-patch.py"
)
source
=
source
.
decode
(
'utf-8'
)
current
=
""
if
os
.
path
.
exists
(
sitepy
):
log
.
debug
(
"Checking existing site.py in
%
s"
,
self
.
install_dir
)
with
io
.
open
(
sitepy
)
as
strm
:
current
=
strm
.
read
()
if
not
current
.
startswith
(
'def __boot():'
):
raise
DistutilsError
(
"
%
s is not a setuptools-generated site.py; please"
" remove it."
%
sitepy
)
if
current
!=
source
:
log
.
info
(
"Creating
%
s"
,
sitepy
)
if
not
self
.
dry_run
:
ensure_directory
(
sitepy
)
with
io
.
open
(
sitepy
,
'w'
,
encoding
=
'utf-8'
)
as
strm
:
strm
.
write
(
source
)
self
.
byte_compile
([
sitepy
])
self
.
sitepy_installed
=
True
def
create_home_path
(
self
):
"""Create directories under ~."""
if
not
self
.
user
:
return
home
=
convert_path
(
os
.
path
.
expanduser
(
"~"
))
for
name
,
path
in
six
.
iteritems
(
self
.
config_vars
):
if
path
.
startswith
(
home
)
and
not
os
.
path
.
isdir
(
path
):
self
.
debug_print
(
"os.makedirs('
%
s', 0o700)"
%
path
)
os
.
makedirs
(
path
,
0
o700
)
INSTALL_SCHEMES
=
dict
(
posix
=
dict
(
install_dir
=
'$base/lib/python$py_version_short/site-packages'
,
script_dir
=
'$base/bin'
,
),
)
DEFAULT_SCHEME
=
dict
(
install_dir
=
'$base/Lib/site-packages'
,
script_dir
=
'$base/Scripts'
,
)
def
_expand
(
self
,
*
attrs
):
config_vars
=
self
.
get_finalized_command
(
'install'
)
.
config_vars
if
self
.
prefix
:
# Set default install_dir/scripts from --prefix
config_vars
=
config_vars
.
copy
()
config_vars
[
'base'
]
=
self
.
prefix
scheme
=
self
.
INSTALL_SCHEMES
.
get
(
os
.
name
,
self
.
DEFAULT_SCHEME
)
for
attr
,
val
in
scheme
.
items
():
if
getattr
(
self
,
attr
,
None
)
is
None
:
setattr
(
self
,
attr
,
val
)
from
distutils.util
import
subst_vars
for
attr
in
attrs
:
val
=
getattr
(
self
,
attr
)
if
val
is
not
None
:
val
=
subst_vars
(
val
,
config_vars
)
if
os
.
name
==
'posix'
:
val
=
os
.
path
.
expanduser
(
val
)
setattr
(
self
,
attr
,
val
)
def
_pythonpath
():
items
=
os
.
environ
.
get
(
'PYTHONPATH'
,
''
)
.
split
(
os
.
pathsep
)
return
filter
(
None
,
items
)
def
get_site_dirs
():
"""
Return a list of 'site' dirs
"""
sitedirs
=
[]
# start with PYTHONPATH
sitedirs
.
extend
(
_pythonpath
())
prefixes
=
[
sys
.
prefix
]
if
sys
.
exec_prefix
!=
sys
.
prefix
:
prefixes
.
append
(
sys
.
exec_prefix
)
for
prefix
in
prefixes
:
if
prefix
:
if
sys
.
platform
in
(
'os2emx'
,
'riscos'
):
sitedirs
.
append
(
os
.
path
.
join
(
prefix
,
"Lib"
,
"site-packages"
))
elif
os
.
sep
==
'/'
:
sitedirs
.
extend
([
os
.
path
.
join
(
prefix
,
"lib"
,
"python"
+
sys
.
version
[:
3
],
"site-packages"
,
),
os
.
path
.
join
(
prefix
,
"lib"
,
"site-python"
),
])
else
:
sitedirs
.
extend
([
prefix
,
os
.
path
.
join
(
prefix
,
"lib"
,
"site-packages"
),
])
if
sys
.
platform
==
'darwin'
:
# for framework builds *only* we add the standard Apple
# locations. Currently only per-user, but /Library and
# /Network/Library could be added too
if
'Python.framework'
in
prefix
:
home
=
os
.
environ
.
get
(
'HOME'
)
if
home
:
home_sp
=
os
.
path
.
join
(
home
,
'Library'
,
'Python'
,
sys
.
version
[:
3
],
'site-packages'
,
)
sitedirs
.
append
(
home_sp
)
lib_paths
=
get_path
(
'purelib'
),
get_path
(
'platlib'
)
for
site_lib
in
lib_paths
:
if
site_lib
not
in
sitedirs
:
sitedirs
.
append
(
site_lib
)
if
site
.
ENABLE_USER_SITE
:
sitedirs
.
append
(
site
.
USER_SITE
)
try
:
sitedirs
.
extend
(
site
.
getsitepackages
())
except
AttributeError
:
pass
sitedirs
=
list
(
map
(
normalize_path
,
sitedirs
))
return
sitedirs
def
expand_paths
(
inputs
):
"""Yield sys.path directories that might contain "old-style" packages"""
seen
=
{}
for
dirname
in
inputs
:
dirname
=
normalize_path
(
dirname
)
if
dirname
in
seen
:
continue
seen
[
dirname
]
=
1
if
not
os
.
path
.
isdir
(
dirname
):
continue
files
=
os
.
listdir
(
dirname
)
yield
dirname
,
files
for
name
in
files
:
if
not
name
.
endswith
(
'.pth'
):
# We only care about the .pth files
continue
if
name
in
(
'easy-install.pth'
,
'setuptools.pth'
):
# Ignore .pth files that we control
continue
# Read the .pth file
f
=
open
(
os
.
path
.
join
(
dirname
,
name
))
lines
=
list
(
yield_lines
(
f
))
f
.
close
()
# Yield existing non-dupe, non-import directory lines from it
for
line
in
lines
:
if
not
line
.
startswith
(
"import"
):
line
=
normalize_path
(
line
.
rstrip
())
if
line
not
in
seen
:
seen
[
line
]
=
1
if
not
os
.
path
.
isdir
(
line
):
continue
yield
line
,
os
.
listdir
(
line
)
def
extract_wininst_cfg
(
dist_filename
):
"""Extract configuration data from a bdist_wininst .exe
Returns a configparser.RawConfigParser, or None
"""
f
=
open
(
dist_filename
,
'rb'
)
try
:
endrec
=
zipfile
.
_EndRecData
(
f
)
if
endrec
is
None
:
return
None
prepended
=
(
endrec
[
9
]
-
endrec
[
5
])
-
endrec
[
6
]
if
prepended
<
12
:
# no wininst data here
return
None
f
.
seek
(
prepended
-
12
)
tag
,
cfglen
,
bmlen
=
struct
.
unpack
(
"<iii"
,
f
.
read
(
12
))
if
tag
not
in
(
0x1234567A
,
0x1234567B
):
return
None
# not a valid tag
f
.
seek
(
prepended
-
(
12
+
cfglen
))
init
=
{
'version'
:
''
,
'target_version'
:
''
}
cfg
=
configparser
.
RawConfigParser
(
init
)
try
:
part
=
f
.
read
(
cfglen
)
# Read up to the first null byte.
config
=
part
.
split
(
b
'
\0
'
,
1
)[
0
]
# Now the config is in bytes, but for RawConfigParser, it should
# be text, so decode it.
config
=
config
.
decode
(
sys
.
getfilesystemencoding
())
cfg
.
readfp
(
six
.
StringIO
(
config
))
except
configparser
.
Error
:
return
None
if
not
cfg
.
has_section
(
'metadata'
)
or
not
cfg
.
has_section
(
'Setup'
):
return
None
return
cfg
finally
:
f
.
close
()
def
get_exe_prefixes
(
exe_filename
):
"""Get exe->egg path translations for a given .exe file"""
prefixes
=
[
(
'PURELIB/'
,
''
),
(
'PLATLIB/pywin32_system32'
,
''
),
(
'PLATLIB/'
,
''
),
(
'SCRIPTS/'
,
'EGG-INFO/scripts/'
),
(
'DATA/lib/site-packages'
,
''
),
]
z
=
zipfile
.
ZipFile
(
exe_filename
)
try
:
for
info
in
z
.
infolist
():
name
=
info
.
filename
parts
=
name
.
split
(
'/'
)
if
len
(
parts
)
==
3
and
parts
[
2
]
==
'PKG-INFO'
:
if
parts
[
1
]
.
endswith
(
'.egg-info'
):
prefixes
.
insert
(
0
,
(
'/'
.
join
(
parts
[:
2
]),
'EGG-INFO/'
))
break
if
len
(
parts
)
!=
2
or
not
name
.
endswith
(
'.pth'
):
continue
if
name
.
endswith
(
'-nspkg.pth'
):
continue
if
parts
[
0
]
.
upper
()
in
(
'PURELIB'
,
'PLATLIB'
):
contents
=
z
.
read
(
name
)
if
six
.
PY3
:
contents
=
contents
.
decode
()
for
pth
in
yield_lines
(
contents
):
pth
=
pth
.
strip
()
.
replace
(
'
\\
'
,
'/'
)
if
not
pth
.
startswith
(
'import'
):
prefixes
.
append
(((
'
%
s/
%
s/'
%
(
parts
[
0
],
pth
)),
''
))
finally
:
z
.
close
()
prefixes
=
[(
x
.
lower
(),
y
)
for
x
,
y
in
prefixes
]
prefixes
.
sort
()
prefixes
.
reverse
()
return
prefixes
class
PthDistributions
(
Environment
):
"""A .pth file with Distribution paths in it"""
dirty
=
False
def
__init__
(
self
,
filename
,
sitedirs
=
()):
self
.
filename
=
filename
self
.
sitedirs
=
list
(
map
(
normalize_path
,
sitedirs
))
self
.
basedir
=
normalize_path
(
os
.
path
.
dirname
(
self
.
filename
))
self
.
_load
()
Environment
.
__init__
(
self
,
[],
None
,
None
)
for
path
in
yield_lines
(
self
.
paths
):
list
(
map
(
self
.
add
,
find_distributions
(
path
,
True
)))
def
_load
(
self
):
self
.
paths
=
[]
saw_import
=
False
seen
=
dict
.
fromkeys
(
self
.
sitedirs
)
if
os
.
path
.
isfile
(
self
.
filename
):
f
=
open
(
self
.
filename
,
'rt'
)
for
line
in
f
:
if
line
.
startswith
(
'import'
):
saw_import
=
True
continue
path
=
line
.
rstrip
()
self
.
paths
.
append
(
path
)
if
not
path
.
strip
()
or
path
.
strip
()
.
startswith
(
'#'
):
continue
# skip non-existent paths, in case somebody deleted a package
# manually, and duplicate paths as well
path
=
self
.
paths
[
-
1
]
=
normalize_path
(
os
.
path
.
join
(
self
.
basedir
,
path
)
)
if
not
os
.
path
.
exists
(
path
)
or
path
in
seen
:
self
.
paths
.
pop
()
# skip it
self
.
dirty
=
True
# we cleaned up, so we're dirty now :)
continue
seen
[
path
]
=
1
f
.
close
()
if
self
.
paths
and
not
saw_import
:
self
.
dirty
=
True
# ensure anything we touch has import wrappers
while
self
.
paths
and
not
self
.
paths
[
-
1
]
.
strip
():
self
.
paths
.
pop
()
def
save
(
self
):
"""Write changed .pth file back to disk"""
if
not
self
.
dirty
:
return
rel_paths
=
list
(
map
(
self
.
make_relative
,
self
.
paths
))
if
rel_paths
:
log
.
debug
(
"Saving
%
s"
,
self
.
filename
)
lines
=
self
.
_wrap_lines
(
rel_paths
)
data
=
'
\n
'
.
join
(
lines
)
+
'
\n
'
if
os
.
path
.
islink
(
self
.
filename
):
os
.
unlink
(
self
.
filename
)
with
open
(
self
.
filename
,
'wt'
)
as
f
:
f
.
write
(
data
)
elif
os
.
path
.
exists
(
self
.
filename
):
log
.
debug
(
"Deleting empty
%
s"
,
self
.
filename
)
os
.
unlink
(
self
.
filename
)
self
.
dirty
=
False
@staticmethod
def
_wrap_lines
(
lines
):
return
lines
def
add
(
self
,
dist
):
"""Add `dist` to the distribution map"""
new_path
=
(
dist
.
location
not
in
self
.
paths
and
(
dist
.
location
not
in
self
.
sitedirs
or
# account for '.' being in PYTHONPATH
dist
.
location
==
os
.
getcwd
()
)
)
if
new_path
:
self
.
paths
.
append
(
dist
.
location
)
self
.
dirty
=
True
Environment
.
add
(
self
,
dist
)
def
remove
(
self
,
dist
):
"""Remove `dist` from the distribution map"""
while
dist
.
location
in
self
.
paths
:
self
.
paths
.
remove
(
dist
.
location
)
self
.
dirty
=
True
Environment
.
remove
(
self
,
dist
)
def
make_relative
(
self
,
path
):
npath
,
last
=
os
.
path
.
split
(
normalize_path
(
path
))
baselen
=
len
(
self
.
basedir
)
parts
=
[
last
]
sep
=
os
.
altsep
==
'/'
and
'/'
or
os
.
sep
while
len
(
npath
)
>=
baselen
:
if
npath
==
self
.
basedir
:
parts
.
append
(
os
.
curdir
)
parts
.
reverse
()
return
sep
.
join
(
parts
)
npath
,
last
=
os
.
path
.
split
(
npath
)
parts
.
append
(
last
)
else
:
return
path
class
RewritePthDistributions
(
PthDistributions
):
@classmethod
def
_wrap_lines
(
cls
,
lines
):
yield
cls
.
prelude
for
line
in
lines
:
yield
line
yield
cls
.
postlude
prelude
=
_one_liner
(
"""
import sys
sys.__plen = len(sys.path)
"""
)
postlude
=
_one_liner
(
"""
import sys
new = sys.path[sys.__plen:]
del sys.path[sys.__plen:]
p = getattr(sys, '__egginsert', 0)
sys.path[p:p] = new
sys.__egginsert = p + len(new)
"""
)
if
os
.
environ
.
get
(
'SETUPTOOLS_SYS_PATH_TECHNIQUE'
,
'raw'
)
==
'rewrite'
:
PthDistributions
=
RewritePthDistributions
def
_first_line_re
():
"""
Return a regular expression based on first_line_re suitable for matching
strings.
"""
if
isinstance
(
first_line_re
.
pattern
,
str
):
return
first_line_re
# first_line_re in Python >=3.1.4 and >=3.2.1 is a bytes pattern.
return
re
.
compile
(
first_line_re
.
pattern
.
decode
())
def
auto_chmod
(
func
,
arg
,
exc
):
if
func
in
[
os
.
unlink
,
os
.
remove
]
and
os
.
name
==
'nt'
:
chmod
(
arg
,
stat
.
S_IWRITE
)
return
func
(
arg
)
et
,
ev
,
_
=
sys
.
exc_info
()
six
.
reraise
(
et
,
(
ev
[
0
],
ev
[
1
]
+
(
"
%
s
%
s"
%
(
func
,
arg
))))
def
update_dist_caches
(
dist_path
,
fix_zipimporter_caches
):
"""
Fix any globally cached `dist_path` related data
`dist_path` should be a path of a newly installed egg distribution (zipped
or unzipped).
sys.path_importer_cache contains finder objects that have been cached when
importing data from the original distribution. Any such finders need to be
cleared since the replacement distribution might be packaged differently,
e.g. a zipped egg distribution might get replaced with an unzipped egg
folder or vice versa. Having the old finders cached may then cause Python
to attempt loading modules from the replacement distribution using an
incorrect loader.
zipimport.zipimporter objects are Python loaders charged with importing
data packaged inside zip archives. If stale loaders referencing the
original distribution, are left behind, they can fail to load modules from
the replacement distribution. E.g. if an old zipimport.zipimporter instance
is used to load data from a new zipped egg archive, it may cause the
operation to attempt to locate the requested data in the wrong location -
one indicated by the original distribution's zip archive directory
information. Such an operation may then fail outright, e.g. report having
read a 'bad local file header', or even worse, it may fail silently &
return invalid data.
zipimport._zip_directory_cache contains cached zip archive directory
information for all existing zipimport.zipimporter instances and all such
instances connected to the same archive share the same cached directory
information.
If asked, and the underlying Python implementation allows it, we can fix
all existing zipimport.zipimporter instances instead of having to track
them down and remove them one by one, by updating their shared cached zip
archive directory information. This, of course, assumes that the
replacement distribution is packaged as a zipped egg.
If not asked to fix existing zipimport.zipimporter instances, we still do
our best to clear any remaining zipimport.zipimporter related cached data
that might somehow later get used when attempting to load data from the new
distribution and thus cause such load operations to fail. Note that when
tracking down such remaining stale data, we can not catch every conceivable
usage from here, and we clear only those that we know of and have found to
cause problems if left alive. Any remaining caches should be updated by
whomever is in charge of maintaining them, i.e. they should be ready to
handle us replacing their zip archives with new distributions at runtime.
"""
# There are several other known sources of stale zipimport.zipimporter
# instances that we do not clear here, but might if ever given a reason to
# do so:
# * Global setuptools pkg_resources.working_set (a.k.a. 'master working
# set') may contain distributions which may in turn contain their
# zipimport.zipimporter loaders.
# * Several zipimport.zipimporter loaders held by local variables further
# up the function call stack when running the setuptools installation.
# * Already loaded modules may have their __loader__ attribute set to the
# exact loader instance used when importing them. Python 3.4 docs state
# that this information is intended mostly for introspection and so is
# not expected to cause us problems.
normalized_path
=
normalize_path
(
dist_path
)
_uncache
(
normalized_path
,
sys
.
path_importer_cache
)
if
fix_zipimporter_caches
:
_replace_zip_directory_cache_data
(
normalized_path
)
else
:
# Here, even though we do not want to fix existing and now stale
# zipimporter cache information, we still want to remove it. Related to
# Python's zip archive directory information cache, we clear each of
# its stale entries in two phases:
# 1. Clear the entry so attempting to access zip archive information
# via any existing stale zipimport.zipimporter instances fails.
# 2. Remove the entry from the cache so any newly constructed
# zipimport.zipimporter instances do not end up using old stale
# zip archive directory information.
# This whole stale data removal step does not seem strictly necessary,
# but has been left in because it was done before we started replacing
# the zip archive directory information cache content if possible, and
# there are no relevant unit tests that we can depend on to tell us if
# this is really needed.
_remove_and_clear_zip_directory_cache_data
(
normalized_path
)
def
_collect_zipimporter_cache_entries
(
normalized_path
,
cache
):
"""
Return zipimporter cache entry keys related to a given normalized path.
Alternative path spellings (e.g. those using different character case or
those using alternative path separators) related to the same path are
included. Any sub-path entries are included as well, i.e. those
corresponding to zip archives embedded in other zip archives.
"""
result
=
[]
prefix_len
=
len
(
normalized_path
)
for
p
in
cache
:
np
=
normalize_path
(
p
)
if
(
np
.
startswith
(
normalized_path
)
and
np
[
prefix_len
:
prefix_len
+
1
]
in
(
os
.
sep
,
''
)):
result
.
append
(
p
)
return
result
def
_update_zipimporter_cache
(
normalized_path
,
cache
,
updater
=
None
):
"""
Update zipimporter cache data for a given normalized path.
Any sub-path entries are processed as well, i.e. those corresponding to zip
archives embedded in other zip archives.
Given updater is a callable taking a cache entry key and the original entry
(after already removing the entry from the cache), and expected to update
the entry and possibly return a new one to be inserted in its place.
Returning None indicates that the entry should not be replaced with a new
one. If no updater is given, the cache entries are simply removed without
any additional processing, the same as if the updater simply returned None.
"""
for
p
in
_collect_zipimporter_cache_entries
(
normalized_path
,
cache
):
# N.B. pypy's custom zipimport._zip_directory_cache implementation does
# not support the complete dict interface:
# * Does not support item assignment, thus not allowing this function
# to be used only for removing existing cache entries.
# * Does not support the dict.pop() method, forcing us to use the
# get/del patterns instead. For more detailed information see the
# following links:
# https://github.com/pypa/setuptools/issues/202#issuecomment-202913420
# http://bit.ly/2h9itJX
old_entry
=
cache
[
p
]
del
cache
[
p
]
new_entry
=
updater
and
updater
(
p
,
old_entry
)
if
new_entry
is
not
None
:
cache
[
p
]
=
new_entry
def
_uncache
(
normalized_path
,
cache
):
_update_zipimporter_cache
(
normalized_path
,
cache
)
def
_remove_and_clear_zip_directory_cache_data
(
normalized_path
):
def
clear_and_remove_cached_zip_archive_directory_data
(
path
,
old_entry
):
old_entry
.
clear
()
_update_zipimporter_cache
(
normalized_path
,
zipimport
.
_zip_directory_cache
,
updater
=
clear_and_remove_cached_zip_archive_directory_data
)
# PyPy Python implementation does not allow directly writing to the
# zipimport._zip_directory_cache and so prevents us from attempting to correct
# its content. The best we can do there is clear the problematic cache content
# and have PyPy repopulate it as needed. The downside is that if there are any
# stale zipimport.zipimporter instances laying around, attempting to use them
# will fail due to not having its zip archive directory information available
# instead of being automatically corrected to use the new correct zip archive
# directory information.
if
'__pypy__'
in
sys
.
builtin_module_names
:
_replace_zip_directory_cache_data
=
\
_remove_and_clear_zip_directory_cache_data
else
:
def
_replace_zip_directory_cache_data
(
normalized_path
):
def
replace_cached_zip_archive_directory_data
(
path
,
old_entry
):
# N.B. In theory, we could load the zip directory information just
# once for all updated path spellings, and then copy it locally and
# update its contained path strings to contain the correct
# spelling, but that seems like a way too invasive move (this cache
# structure is not officially documented anywhere and could in
# theory change with new Python releases) for no significant
# benefit.
old_entry
.
clear
()
zipimport
.
zipimporter
(
path
)
old_entry
.
update
(
zipimport
.
_zip_directory_cache
[
path
])
return
old_entry
_update_zipimporter_cache
(
normalized_path
,
zipimport
.
_zip_directory_cache
,
updater
=
replace_cached_zip_archive_directory_data
)
def
is_python
(
text
,
filename
=
'<string>'
):
"Is this string a valid Python script?"
try
:
compile
(
text
,
filename
,
'exec'
)
except
(
SyntaxError
,
TypeError
):
return
False
else
:
return
True
def
is_sh
(
executable
):
"""Determine if the specified executable is a .sh (contains a #! line)"""
try
:
with
io
.
open
(
executable
,
encoding
=
'latin-1'
)
as
fp
:
magic
=
fp
.
read
(
2
)
except
(
OSError
,
IOError
):
return
executable
return
magic
==
'#!'
def
nt_quote_arg
(
arg
):
"""Quote a command line argument according to Windows parsing rules"""
return
subprocess
.
list2cmdline
([
arg
])
def
is_python_script
(
script_text
,
filename
):
"""Is this text, as a whole, a Python script? (as opposed to shell/bat/etc.
"""
if
filename
.
endswith
(
'.py'
)
or
filename
.
endswith
(
'.pyw'
):
return
True
# extension says it's Python
if
is_python
(
script_text
,
filename
):
return
True
# it's syntactically valid Python
if
script_text
.
startswith
(
'#!'
):
# It begins with a '#!' line, so check if 'python' is in it somewhere
return
'python'
in
script_text
.
splitlines
()[
0
]
.
lower
()
return
False
# Not any Python I can recognize
try
:
from
os
import
chmod
as
_chmod
except
ImportError
:
# Jython compatibility
def
_chmod
(
*
args
):
pass
def
chmod
(
path
,
mode
):
log
.
debug
(
"changing mode of
%
s to
%
o"
,
path
,
mode
)
try
:
_chmod
(
path
,
mode
)
except
os
.
error
as
e
:
log
.
debug
(
"chmod failed:
%
s"
,
e
)
class
CommandSpec
(
list
):
"""
A command spec for a #! header, specified as a list of arguments akin to
those passed to Popen.
"""
options
=
[]
split_args
=
dict
()
@classmethod
def
best
(
cls
):
"""
Choose the best CommandSpec class based on environmental conditions.
"""
return
cls
@classmethod
def
_sys_executable
(
cls
):
_default
=
os
.
path
.
normpath
(
sys
.
executable
)
return
os
.
environ
.
get
(
'__PYVENV_LAUNCHER__'
,
_default
)
@classmethod
def
from_param
(
cls
,
param
):
"""
Construct a CommandSpec from a parameter to build_scripts, which may
be None.
"""
if
isinstance
(
param
,
cls
):
return
param
if
isinstance
(
param
,
list
):
return
cls
(
param
)
if
param
is
None
:
return
cls
.
from_environment
()
# otherwise, assume it's a string.
return
cls
.
from_string
(
param
)
@classmethod
def
from_environment
(
cls
):
return
cls
([
cls
.
_sys_executable
()])
@classmethod
def
from_string
(
cls
,
string
):
"""
Construct a command spec from a simple string representing a command
line parseable by shlex.split.
"""
items
=
shlex
.
split
(
string
,
**
cls
.
split_args
)
return
cls
(
items
)
def
install_options
(
self
,
script_text
):
self
.
options
=
shlex
.
split
(
self
.
_extract_options
(
script_text
))
cmdline
=
subprocess
.
list2cmdline
(
self
)
if
not
isascii
(
cmdline
):
self
.
options
[:
0
]
=
[
'-x'
]
@staticmethod
def
_extract_options
(
orig_script
):
"""
Extract any options from the first line of the script.
"""
first
=
(
orig_script
+
'
\n
'
)
.
splitlines
()[
0
]
match
=
_first_line_re
()
.
match
(
first
)
options
=
match
.
group
(
1
)
or
''
if
match
else
''
return
options
.
strip
()
def
as_header
(
self
):
return
self
.
_render
(
self
+
list
(
self
.
options
))
@staticmethod
def
_strip_quotes
(
item
):
_QUOTES
=
'"
\'
'
for
q
in
_QUOTES
:
if
item
.
startswith
(
q
)
and
item
.
endswith
(
q
):
return
item
[
1
:
-
1
]
return
item
@staticmethod
def
_render
(
items
):
cmdline
=
subprocess
.
list2cmdline
(
CommandSpec
.
_strip_quotes
(
item
.
strip
())
for
item
in
items
)
return
'#!'
+
cmdline
+
'
\n
'
# For pbr compat; will be removed in a future version.
sys_executable
=
CommandSpec
.
_sys_executable
()
class
WindowsCommandSpec
(
CommandSpec
):
split_args
=
dict
(
posix
=
False
)
class
ScriptWriter
:
"""
Encapsulates behavior around writing entry point scripts for console and
gui apps.
"""
template
=
textwrap
.
dedent
(
r"""
# EASY-INSTALL-ENTRY-SCRIPT:
%(spec)
r,
%(group)
r,
%(name)
r
__requires__ =
%(spec)
r
import re
import sys
from pkg_resources import load_entry_point
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
sys.exit(
load_entry_point(
%(spec)
r,
%(group)
r,
%(name)
r)()
)
"""
)
.
lstrip
()
command_spec_class
=
CommandSpec
@classmethod
def
get_script_args
(
cls
,
dist
,
executable
=
None
,
wininst
=
False
):
# for backward compatibility
warnings
.
warn
(
"Use get_args"
,
EasyInstallDeprecationWarning
)
writer
=
(
WindowsScriptWriter
if
wininst
else
ScriptWriter
)
.
best
()
header
=
cls
.
get_script_header
(
""
,
executable
,
wininst
)
return
writer
.
get_args
(
dist
,
header
)
@classmethod
def
get_script_header
(
cls
,
script_text
,
executable
=
None
,
wininst
=
False
):
# for backward compatibility
warnings
.
warn
(
"Use get_header"
,
EasyInstallDeprecationWarning
,
stacklevel
=
2
)
if
wininst
:
executable
=
"python.exe"
return
cls
.
get_header
(
script_text
,
executable
)
@classmethod
def
get_args
(
cls
,
dist
,
header
=
None
):
"""
Yield write_script() argument tuples for a distribution's
console_scripts and gui_scripts entry points.
"""
if
header
is
None
:
header
=
cls
.
get_header
()
spec
=
str
(
dist
.
as_requirement
())
for
type_
in
'console'
,
'gui'
:
group
=
type_
+
'_scripts'
for
name
,
ep
in
dist
.
get_entry_map
(
group
)
.
items
():
cls
.
_ensure_safe_name
(
name
)
script_text
=
cls
.
template
%
locals
()
args
=
cls
.
_get_script_args
(
type_
,
name
,
header
,
script_text
)
for
res
in
args
:
yield
res
@staticmethod
def
_ensure_safe_name
(
name
):
"""
Prevent paths in *_scripts entry point names.
"""
has_path_sep
=
re
.
search
(
r'[\\/]'
,
name
)
if
has_path_sep
:
raise
ValueError
(
"Path separators not allowed in script names"
)
@classmethod
def
get_writer
(
cls
,
force_windows
):
# for backward compatibility
warnings
.
warn
(
"Use best"
,
EasyInstallDeprecationWarning
)
return
WindowsScriptWriter
.
best
()
if
force_windows
else
cls
.
best
()
@classmethod
def
best
(
cls
):
"""
Select the best ScriptWriter for this environment.
"""
if
sys
.
platform
==
'win32'
or
(
os
.
name
==
'java'
and
os
.
_name
==
'nt'
):
return
WindowsScriptWriter
.
best
()
else
:
return
cls
@classmethod
def
_get_script_args
(
cls
,
type_
,
name
,
header
,
script_text
):
# Simply write the stub with no extension.
yield
(
name
,
header
+
script_text
)
@classmethod
def
get_header
(
cls
,
script_text
=
""
,
executable
=
None
):
"""Create a #! line, getting options (if any) from script_text"""
cmd
=
cls
.
command_spec_class
.
best
()
.
from_param
(
executable
)
cmd
.
install_options
(
script_text
)
return
cmd
.
as_header
()
class
WindowsScriptWriter
(
ScriptWriter
):
command_spec_class
=
WindowsCommandSpec
@classmethod
def
get_writer
(
cls
):
# for backward compatibility
warnings
.
warn
(
"Use best"
,
EasyInstallDeprecationWarning
)
return
cls
.
best
()
@classmethod
def
best
(
cls
):
"""
Select the best ScriptWriter suitable for Windows
"""
writer_lookup
=
dict
(
executable
=
WindowsExecutableLauncherWriter
,
natural
=
cls
,
)
# for compatibility, use the executable launcher by default
launcher
=
os
.
environ
.
get
(
'SETUPTOOLS_LAUNCHER'
,
'executable'
)
return
writer_lookup
[
launcher
]
@classmethod
def
_get_script_args
(
cls
,
type_
,
name
,
header
,
script_text
):
"For Windows, add a .py extension"
ext
=
dict
(
console
=
'.pya'
,
gui
=
'.pyw'
)[
type_
]
if
ext
not
in
os
.
environ
[
'PATHEXT'
]
.
lower
()
.
split
(
';'
):
msg
=
(
"{ext} not listed in PATHEXT; scripts will not be "
"recognized as executables."
)
.
format
(
**
locals
())
warnings
.
warn
(
msg
,
UserWarning
)
old
=
[
'.pya'
,
'.py'
,
'-script.py'
,
'.pyc'
,
'.pyo'
,
'.pyw'
,
'.exe'
]
old
.
remove
(
ext
)
header
=
cls
.
_adjust_header
(
type_
,
header
)
blockers
=
[
name
+
x
for
x
in
old
]
yield
name
+
ext
,
header
+
script_text
,
't'
,
blockers
@classmethod
def
_adjust_header
(
cls
,
type_
,
orig_header
):
"""
Make sure 'pythonw' is used for gui and and 'python' is used for
console (regardless of what sys.executable is).
"""
pattern
=
'pythonw.exe'
repl
=
'python.exe'
if
type_
==
'gui'
:
pattern
,
repl
=
repl
,
pattern
pattern_ob
=
re
.
compile
(
re
.
escape
(
pattern
),
re
.
IGNORECASE
)
new_header
=
pattern_ob
.
sub
(
string
=
orig_header
,
repl
=
repl
)
return
new_header
if
cls
.
_use_header
(
new_header
)
else
orig_header
@staticmethod
def
_use_header
(
new_header
):
"""
Should _adjust_header use the replaced header?
On non-windows systems, always use. On
Windows systems, only use the replaced header if it resolves
to an executable on the system.
"""
clean_header
=
new_header
[
2
:
-
1
]
.
strip
(
'"'
)
return
sys
.
platform
!=
'win32'
or
find_executable
(
clean_header
)
class
WindowsExecutableLauncherWriter
(
WindowsScriptWriter
):
@classmethod
def
_get_script_args
(
cls
,
type_
,
name
,
header
,
script_text
):
"""
For Windows, add a .py extension and an .exe launcher
"""
if
type_
==
'gui'
:
launcher_type
=
'gui'
ext
=
'-script.pyw'
old
=
[
'.pyw'
]
else
:
launcher_type
=
'cli'
ext
=
'-script.py'
old
=
[
'.py'
,
'.pyc'
,
'.pyo'
]
hdr
=
cls
.
_adjust_header
(
type_
,
header
)
blockers
=
[
name
+
x
for
x
in
old
]
yield
(
name
+
ext
,
hdr
+
script_text
,
't'
,
blockers
)
yield
(
name
+
'.exe'
,
get_win_launcher
(
launcher_type
),
'b'
# write in binary mode
)
if
not
is_64bit
():
# install a manifest for the launcher to prevent Windows
# from detecting it as an installer (which it will for
# launchers like easy_install.exe). Consider only
# adding a manifest for launchers detected as installers.
# See Distribute #143 for details.
m_name
=
name
+
'.exe.manifest'
yield
(
m_name
,
load_launcher_manifest
(
name
),
't'
)
# for backward-compatibility
get_script_args
=
ScriptWriter
.
get_script_args
get_script_header
=
ScriptWriter
.
get_script_header
def
get_win_launcher
(
type
):
"""
Load the Windows launcher (executable) suitable for launching a script.
`type` should be either 'cli' or 'gui'
Returns the executable as a byte string.
"""
launcher_fn
=
'
%
s.exe'
%
type
if
is_64bit
():
launcher_fn
=
launcher_fn
.
replace
(
"."
,
"-64."
)
else
:
launcher_fn
=
launcher_fn
.
replace
(
"."
,
"-32."
)
return
resource_string
(
'setuptools'
,
launcher_fn
)
def
load_launcher_manifest
(
name
):
manifest
=
pkg_resources
.
resource_string
(
__name__
,
'launcher manifest.xml'
)
if
six
.
PY2
:
return
manifest
%
vars
()
else
:
return
manifest
.
decode
(
'utf-8'
)
%
vars
()
def
rmtree
(
path
,
ignore_errors
=
False
,
onerror
=
auto_chmod
):
return
shutil
.
rmtree
(
path
,
ignore_errors
,
onerror
)
def
current_umask
():
tmp
=
os
.
umask
(
0
o022
)
os
.
umask
(
tmp
)
return
tmp
def
bootstrap
():
# This function is called when setuptools*.egg is run using /bin/sh
import
setuptools
argv0
=
os
.
path
.
dirname
(
setuptools
.
__path__
[
0
])
sys
.
argv
[
0
]
=
argv0
sys
.
argv
.
append
(
argv0
)
main
()
def
main
(
argv
=
None
,
**
kw
):
from
setuptools
import
setup
from
setuptools.dist
import
Distribution
class
DistributionWithoutHelpCommands
(
Distribution
):
common_usage
=
""
def
_show_help
(
self
,
*
args
,
**
kw
):
with
_patch_usage
():
Distribution
.
_show_help
(
self
,
*
args
,
**
kw
)
if
argv
is
None
:
argv
=
sys
.
argv
[
1
:]
with
_patch_usage
():
setup
(
script_args
=
[
'-q'
,
'easy_install'
,
'-v'
]
+
argv
,
script_name
=
sys
.
argv
[
0
]
or
'easy_install'
,
distclass
=
DistributionWithoutHelpCommands
,
**
kw
)
@contextlib.contextmanager
def
_patch_usage
():
import
distutils.core
USAGE
=
textwrap
.
dedent
(
"""
usage:
%(script)
s [options] requirement_or_url ...
or:
%(script)
s --help
"""
)
.
lstrip
()
def
gen_usage
(
script_name
):
return
USAGE
%
dict
(
script
=
os
.
path
.
basename
(
script_name
),
)
saved
=
distutils
.
core
.
gen_usage
distutils
.
core
.
gen_usage
=
gen_usage
try
:
yield
finally
:
distutils
.
core
.
gen_usage
=
saved
class
EasyInstallDeprecationWarning
(
SetuptoolsDeprecationWarning
):
"""Class for warning about deprecations in EasyInstall in SetupTools. Not ignored by default, unlike DeprecationWarning."""
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment