update windows build to Python 3.7
This commit is contained in:
parent
73105fa71e
commit
ddc59ab92d
5761 changed files with 750298 additions and 213405 deletions
|
|
@ -2,7 +2,8 @@ __all__ = [
|
|||
'alias', 'bdist_egg', 'bdist_rpm', 'build_ext', 'build_py', 'develop',
|
||||
'easy_install', 'egg_info', 'install', 'install_lib', 'rotate', 'saveopts',
|
||||
'sdist', 'setopt', 'test', 'install_egg_info', 'install_scripts',
|
||||
'register', 'bdist_wininst', 'upload_docs',
|
||||
'register', 'bdist_wininst', 'upload_docs', 'upload', 'build_clib',
|
||||
'dist_info',
|
||||
]
|
||||
|
||||
from distutils.command.bdist import bdist
|
||||
|
|
@ -10,7 +11,6 @@ import sys
|
|||
|
||||
from setuptools.command import install_scripts
|
||||
|
||||
|
||||
if 'egg' not in bdist.format_commands:
|
||||
bdist.format_command['egg'] = ('bdist_egg', "Python .egg file")
|
||||
bdist.format_commands.append('egg')
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,5 +1,7 @@
|
|||
from distutils.errors import DistutilsOptionError
|
||||
|
||||
from setuptools.extern.six.moves import map
|
||||
|
||||
from setuptools.command.setopt import edit_config, option_base, config_file
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@ from distutils import log
|
|||
from types import CodeType
|
||||
import sys
|
||||
import os
|
||||
import marshal
|
||||
import re
|
||||
import textwrap
|
||||
import marshal
|
||||
|
||||
from setuptools.extern import six
|
||||
|
||||
|
|
@ -39,6 +40,16 @@ def strip_module(filename):
|
|||
return filename
|
||||
|
||||
|
||||
def sorted_walk(dir):
|
||||
"""Do os.walk in a reproducible way,
|
||||
independent of indeterministic filesystem readdir order
|
||||
"""
|
||||
for base, dirs, files in os.walk(dir):
|
||||
dirs.sort()
|
||||
files.sort()
|
||||
yield base, dirs, files
|
||||
|
||||
|
||||
def write_stub(resource, pyfile):
|
||||
_stub_template = textwrap.dedent("""
|
||||
def __bootstrap__():
|
||||
|
|
@ -129,7 +140,7 @@ class bdist_egg(Command):
|
|||
self.distribution.data_files.append(item)
|
||||
|
||||
try:
|
||||
log.info("installing package data to %s" % self.bdist_dir)
|
||||
log.info("installing package data to %s", self.bdist_dir)
|
||||
self.call_command('install_data', force=0, root=None)
|
||||
finally:
|
||||
self.distribution.data_files = old
|
||||
|
|
@ -152,7 +163,7 @@ class bdist_egg(Command):
|
|||
self.run_command("egg_info")
|
||||
# We run install_lib before install_data, because some data hacks
|
||||
# pull their data path from the install_lib command.
|
||||
log.info("installing library code to %s" % self.bdist_dir)
|
||||
log.info("installing library code to %s", self.bdist_dir)
|
||||
instcmd = self.get_finalized_command('install')
|
||||
old_root = instcmd.root
|
||||
instcmd.root = None
|
||||
|
|
@ -169,7 +180,7 @@ class bdist_egg(Command):
|
|||
pyfile = os.path.join(self.bdist_dir, strip_module(filename) +
|
||||
'.py')
|
||||
self.stubs.append(pyfile)
|
||||
log.info("creating stub loader for %s" % ext_name)
|
||||
log.info("creating stub loader for %s", ext_name)
|
||||
if not self.dry_run:
|
||||
write_stub(os.path.basename(ext_name), pyfile)
|
||||
to_compile.append(pyfile)
|
||||
|
|
@ -186,14 +197,14 @@ class bdist_egg(Command):
|
|||
self.mkpath(egg_info)
|
||||
if self.distribution.scripts:
|
||||
script_dir = os.path.join(egg_info, 'scripts')
|
||||
log.info("installing scripts to %s" % script_dir)
|
||||
log.info("installing scripts to %s", script_dir)
|
||||
self.call_command('install_scripts', install_dir=script_dir,
|
||||
no_ep=1)
|
||||
|
||||
self.copy_metadata_to(egg_info)
|
||||
native_libs = os.path.join(egg_info, "native_libs.txt")
|
||||
if all_outputs:
|
||||
log.info("writing %s" % native_libs)
|
||||
log.info("writing %s", native_libs)
|
||||
if not self.dry_run:
|
||||
ensure_directory(native_libs)
|
||||
libs_file = open(native_libs, 'wt')
|
||||
|
|
@ -201,7 +212,7 @@ class bdist_egg(Command):
|
|||
libs_file.write('\n')
|
||||
libs_file.close()
|
||||
elif os.path.isfile(native_libs):
|
||||
log.info("removing %s" % native_libs)
|
||||
log.info("removing %s", native_libs)
|
||||
if not self.dry_run:
|
||||
os.unlink(native_libs)
|
||||
|
||||
|
|
@ -232,11 +243,28 @@ class bdist_egg(Command):
|
|||
log.info("Removing .py files from temporary directory")
|
||||
for base, dirs, files in walk_egg(self.bdist_dir):
|
||||
for name in files:
|
||||
path = os.path.join(base, name)
|
||||
|
||||
if name.endswith('.py'):
|
||||
path = os.path.join(base, name)
|
||||
log.debug("Deleting %s", path)
|
||||
os.unlink(path)
|
||||
|
||||
if base.endswith('__pycache__'):
|
||||
path_old = path
|
||||
|
||||
pattern = r'(?P<name>.+)\.(?P<magic>[^.]+)\.pyc'
|
||||
m = re.match(pattern, name)
|
||||
path_new = os.path.join(
|
||||
base, os.pardir, m.group('name') + '.pyc')
|
||||
log.info(
|
||||
"Renaming file from [%s] to [%s]"
|
||||
% (path_old, path_new))
|
||||
try:
|
||||
os.remove(path_new)
|
||||
except OSError:
|
||||
pass
|
||||
os.rename(path_old, path_new)
|
||||
|
||||
def zip_safe(self):
|
||||
safe = getattr(self.distribution, 'zip_safe', None)
|
||||
if safe is not None:
|
||||
|
|
@ -302,7 +330,7 @@ class bdist_egg(Command):
|
|||
ext_outputs = []
|
||||
|
||||
paths = {self.bdist_dir: ''}
|
||||
for base, dirs, files in os.walk(self.bdist_dir):
|
||||
for base, dirs, files in sorted_walk(self.bdist_dir):
|
||||
for filename in files:
|
||||
if os.path.splitext(filename)[1].lower() in NATIVE_EXTENSIONS:
|
||||
all_outputs.append(paths[base] + filename)
|
||||
|
|
@ -329,7 +357,7 @@ NATIVE_EXTENSIONS = dict.fromkeys('.dll .so .dylib .pyd'.split())
|
|||
|
||||
def walk_egg(egg_dir):
|
||||
"""Walk an unpacked egg's contents, skipping the metadata directory"""
|
||||
walker = os.walk(egg_dir)
|
||||
walker = sorted_walk(egg_dir)
|
||||
base, dirs, files = next(walker)
|
||||
if 'EGG-INFO' in dirs:
|
||||
dirs.remove('EGG-INFO')
|
||||
|
|
@ -383,10 +411,12 @@ def scan_module(egg_dir, base, name, stubs):
|
|||
return True # Extension module
|
||||
pkg = base[len(egg_dir) + 1:].replace(os.sep, '.')
|
||||
module = pkg + (pkg and '.' or '') + os.path.splitext(name)[0]
|
||||
if sys.version_info < (3, 3):
|
||||
if six.PY2:
|
||||
skip = 8 # skip magic & date
|
||||
else:
|
||||
elif sys.version_info < (3, 7):
|
||||
skip = 12 # skip magic & date & file size
|
||||
else:
|
||||
skip = 16 # skip magic & reserved? & date & file size
|
||||
f = open(filename, 'rb')
|
||||
f.read(skip)
|
||||
code = marshal.load(f)
|
||||
|
|
@ -429,6 +459,7 @@ def can_scan():
|
|||
log.warn("Please ask the author to include a 'zip_safe'"
|
||||
" setting (either True or False) in the package's setup.py")
|
||||
|
||||
|
||||
# Attribute names of options for commands that might need to be convinced to
|
||||
# install to the egg build directory
|
||||
|
||||
|
|
@ -457,15 +488,15 @@ def make_zipfile(zip_filename, base_dir, verbose=0, dry_run=0, compress=True,
|
|||
p = path[len(base_dir) + 1:]
|
||||
if not dry_run:
|
||||
z.write(path, p)
|
||||
log.debug("adding '%s'" % p)
|
||||
log.debug("adding '%s'", p)
|
||||
|
||||
compression = zipfile.ZIP_DEFLATED if compress else zipfile.ZIP_STORED
|
||||
if not dry_run:
|
||||
z = zipfile.ZipFile(zip_filename, mode, compression=compression)
|
||||
for dirname, dirs, files in os.walk(base_dir):
|
||||
for dirname, dirs, files in sorted_walk(base_dir):
|
||||
visit(z, dirname, files)
|
||||
z.close()
|
||||
else:
|
||||
for dirname, dirs, files in os.walk(base_dir):
|
||||
for dirname, dirs, files in sorted_walk(base_dir):
|
||||
visit(None, dirname, files)
|
||||
return zip_filename
|
||||
|
|
|
|||
98
Lib/site-packages/setuptools/command/build_clib.py
Normal file
98
Lib/site-packages/setuptools/command/build_clib.py
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
import distutils.command.build_clib as orig
|
||||
from distutils.errors import DistutilsSetupError
|
||||
from distutils import log
|
||||
from setuptools.dep_util import newer_pairwise_group
|
||||
|
||||
|
||||
class build_clib(orig.build_clib):
|
||||
"""
|
||||
Override the default build_clib behaviour to do the following:
|
||||
|
||||
1. Implement a rudimentary timestamp-based dependency system
|
||||
so 'compile()' doesn't run every time.
|
||||
2. Add more keys to the 'build_info' dictionary:
|
||||
* obj_deps - specify dependencies for each object compiled.
|
||||
this should be a dictionary mapping a key
|
||||
with the source filename to a list of
|
||||
dependencies. Use an empty string for global
|
||||
dependencies.
|
||||
* cflags - specify a list of additional flags to pass to
|
||||
the compiler.
|
||||
"""
|
||||
|
||||
def build_libraries(self, libraries):
|
||||
for (lib_name, build_info) in libraries:
|
||||
sources = build_info.get('sources')
|
||||
if sources is None or not isinstance(sources, (list, tuple)):
|
||||
raise DistutilsSetupError(
|
||||
"in 'libraries' option (library '%s'), "
|
||||
"'sources' must be present and must be "
|
||||
"a list of source filenames" % lib_name)
|
||||
sources = list(sources)
|
||||
|
||||
log.info("building '%s' library", lib_name)
|
||||
|
||||
# Make sure everything is the correct type.
|
||||
# obj_deps should be a dictionary of keys as sources
|
||||
# and a list/tuple of files that are its dependencies.
|
||||
obj_deps = build_info.get('obj_deps', dict())
|
||||
if not isinstance(obj_deps, dict):
|
||||
raise DistutilsSetupError(
|
||||
"in 'libraries' option (library '%s'), "
|
||||
"'obj_deps' must be a dictionary of "
|
||||
"type 'source: list'" % lib_name)
|
||||
dependencies = []
|
||||
|
||||
# Get the global dependencies that are specified by the '' key.
|
||||
# These will go into every source's dependency list.
|
||||
global_deps = obj_deps.get('', list())
|
||||
if not isinstance(global_deps, (list, tuple)):
|
||||
raise DistutilsSetupError(
|
||||
"in 'libraries' option (library '%s'), "
|
||||
"'obj_deps' must be a dictionary of "
|
||||
"type 'source: list'" % lib_name)
|
||||
|
||||
# Build the list to be used by newer_pairwise_group
|
||||
# each source will be auto-added to its dependencies.
|
||||
for source in sources:
|
||||
src_deps = [source]
|
||||
src_deps.extend(global_deps)
|
||||
extra_deps = obj_deps.get(source, list())
|
||||
if not isinstance(extra_deps, (list, tuple)):
|
||||
raise DistutilsSetupError(
|
||||
"in 'libraries' option (library '%s'), "
|
||||
"'obj_deps' must be a dictionary of "
|
||||
"type 'source: list'" % lib_name)
|
||||
src_deps.extend(extra_deps)
|
||||
dependencies.append(src_deps)
|
||||
|
||||
expected_objects = self.compiler.object_filenames(
|
||||
sources,
|
||||
output_dir=self.build_temp
|
||||
)
|
||||
|
||||
if newer_pairwise_group(dependencies, expected_objects) != ([], []):
|
||||
# First, compile the source code to object files in the library
|
||||
# directory. (This should probably change to putting object
|
||||
# files in a temporary build directory.)
|
||||
macros = build_info.get('macros')
|
||||
include_dirs = build_info.get('include_dirs')
|
||||
cflags = build_info.get('cflags')
|
||||
objects = self.compiler.compile(
|
||||
sources,
|
||||
output_dir=self.build_temp,
|
||||
macros=macros,
|
||||
include_dirs=include_dirs,
|
||||
extra_postargs=cflags,
|
||||
debug=self.debug
|
||||
)
|
||||
|
||||
# Now "link" the object files together into a static library.
|
||||
# (On Unix at least, this isn't really linking -- it just
|
||||
# builds an archive. Whatever.)
|
||||
self.compiler.create_static_lib(
|
||||
expected_objects,
|
||||
lib_name,
|
||||
output_dir=self.build_clib,
|
||||
debug=self.debug
|
||||
)
|
||||
|
|
@ -1,30 +1,50 @@
|
|||
from distutils.command.build_ext import build_ext as _du_build_ext
|
||||
from distutils.file_util import copy_file
|
||||
from distutils.ccompiler import new_compiler
|
||||
from distutils.sysconfig import customize_compiler
|
||||
from distutils.errors import DistutilsError
|
||||
from distutils import log
|
||||
import os
|
||||
import sys
|
||||
import itertools
|
||||
import imp
|
||||
from distutils.command.build_ext import build_ext as _du_build_ext
|
||||
from distutils.file_util import copy_file
|
||||
from distutils.ccompiler import new_compiler
|
||||
from distutils.sysconfig import customize_compiler, get_config_var
|
||||
from distutils.errors import DistutilsError
|
||||
from distutils import log
|
||||
|
||||
from setuptools.extension import Library
|
||||
from setuptools.extern import six
|
||||
|
||||
try:
|
||||
# Attempt to use Cython for building extensions, if available
|
||||
from Cython.Distutils.build_ext import build_ext as _build_ext
|
||||
# Additionally, assert that the compiler module will load
|
||||
# also. Ref #1229.
|
||||
__import__('Cython.Compiler.Main')
|
||||
except ImportError:
|
||||
_build_ext = _du_build_ext
|
||||
|
||||
try:
|
||||
# Python 2.7 or >=3.2
|
||||
from sysconfig import _CONFIG_VARS
|
||||
except ImportError:
|
||||
from distutils.sysconfig import get_config_var
|
||||
# make sure _config_vars is initialized
|
||||
get_config_var("LDSHARED")
|
||||
from distutils.sysconfig import _config_vars as _CONFIG_VARS
|
||||
|
||||
|
||||
def _customize_compiler_for_shlib(compiler):
|
||||
if sys.platform == "darwin":
|
||||
# building .dylib requires additional compiler flags on OSX; here we
|
||||
# temporarily substitute the pyconfig.h variables so that distutils'
|
||||
# 'customize_compiler' uses them before we build the shared libraries.
|
||||
tmp = _CONFIG_VARS.copy()
|
||||
try:
|
||||
# XXX Help! I don't have any idea whether these are right...
|
||||
_CONFIG_VARS['LDSHARED'] = (
|
||||
"gcc -Wl,-x -dynamiclib -undefined dynamic_lookup")
|
||||
_CONFIG_VARS['CCSHARED'] = " -dynamiclib"
|
||||
_CONFIG_VARS['SO'] = ".dylib"
|
||||
customize_compiler(compiler)
|
||||
finally:
|
||||
_CONFIG_VARS.clear()
|
||||
_CONFIG_VARS.update(tmp)
|
||||
else:
|
||||
customize_compiler(compiler)
|
||||
|
||||
get_config_var("LDSHARED") # make sure _config_vars is initialized
|
||||
del get_config_var
|
||||
from distutils.sysconfig import _config_vars as _CONFIG_VARS
|
||||
|
||||
have_rtld = False
|
||||
use_stubs = False
|
||||
|
|
@ -39,9 +59,18 @@ elif os.name != 'nt':
|
|||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
if_dl = lambda s: s if have_rtld else ''
|
||||
|
||||
|
||||
def get_abi3_suffix():
|
||||
"""Return the file extension for an abi3-compliant Extension()"""
|
||||
for suffix, _, _ in (s for s in imp.get_suffixes() if s[2] == imp.C_EXTENSION):
|
||||
if '.abi3' in suffix: # Unix
|
||||
return suffix
|
||||
elif suffix == '.pyd': # Windows
|
||||
return suffix
|
||||
|
||||
|
||||
class build_ext(_build_ext):
|
||||
def run(self):
|
||||
"""Build extensions in build directory, then copy if --inplace"""
|
||||
|
|
@ -77,6 +106,15 @@ class build_ext(_build_ext):
|
|||
filename = _build_ext.get_ext_filename(self, fullname)
|
||||
if fullname in self.ext_map:
|
||||
ext = self.ext_map[fullname]
|
||||
use_abi3 = (
|
||||
six.PY3
|
||||
and getattr(ext, 'py_limited_api')
|
||||
and get_abi3_suffix()
|
||||
)
|
||||
if use_abi3:
|
||||
so_ext = get_config_var('EXT_SUFFIX')
|
||||
filename = filename[:-len(so_ext)]
|
||||
filename = filename + get_abi3_suffix()
|
||||
if isinstance(ext, Library):
|
||||
fn, ext = os.path.splitext(filename)
|
||||
return self.shlib_compiler.library_filename(fn, libtype)
|
||||
|
|
@ -124,20 +162,7 @@ class build_ext(_build_ext):
|
|||
compiler = self.shlib_compiler = new_compiler(
|
||||
compiler=self.compiler, dry_run=self.dry_run, force=self.force
|
||||
)
|
||||
if sys.platform == "darwin":
|
||||
tmp = _CONFIG_VARS.copy()
|
||||
try:
|
||||
# XXX Help! I don't have any idea whether these are right...
|
||||
_CONFIG_VARS['LDSHARED'] = (
|
||||
"gcc -Wl,-x -dynamiclib -undefined dynamic_lookup")
|
||||
_CONFIG_VARS['CCSHARED'] = " -dynamiclib"
|
||||
_CONFIG_VARS['SO'] = ".dylib"
|
||||
customize_compiler(compiler)
|
||||
finally:
|
||||
_CONFIG_VARS.clear()
|
||||
_CONFIG_VARS.update(tmp)
|
||||
else:
|
||||
customize_compiler(compiler)
|
||||
_customize_compiler_for_shlib(compiler)
|
||||
|
||||
if self.include_dirs is not None:
|
||||
compiler.set_include_dirs(self.include_dirs)
|
||||
|
|
|
|||
|
|
@ -6,13 +6,15 @@ import fnmatch
|
|||
import textwrap
|
||||
import io
|
||||
import distutils.errors
|
||||
import collections
|
||||
import itertools
|
||||
|
||||
from setuptools.extern import six
|
||||
from setuptools.extern.six.moves import map, filter, filterfalse
|
||||
|
||||
try:
|
||||
from setuptools.lib2to3_ex import Mixin2to3
|
||||
except ImportError:
|
||||
|
||||
class Mixin2to3:
|
||||
def run_2to3(self, files, doctests=True):
|
||||
"do nothing"
|
||||
|
|
@ -59,12 +61,16 @@ class build_py(orig.build_py, Mixin2to3):
|
|||
self.byte_compile(orig.build_py.get_outputs(self, include_bytecode=0))
|
||||
|
||||
def __getattr__(self, attr):
|
||||
if attr == 'data_files': # lazily compute data files
|
||||
self.data_files = files = self._get_data_files()
|
||||
return files
|
||||
"lazily compute data files"
|
||||
if attr == 'data_files':
|
||||
self.data_files = self._get_data_files()
|
||||
return self.data_files
|
||||
return orig.build_py.__getattr__(self, attr)
|
||||
|
||||
def build_module(self, module, module_file, package):
|
||||
if six.PY2 and isinstance(package, six.string_types):
|
||||
# avoid errors on Python 2 when unicode is passed (#190)
|
||||
package = package.split('.')
|
||||
outfile, copied = orig.build_py.build_module(self, module, module_file,
|
||||
package)
|
||||
if copied:
|
||||
|
|
@ -74,32 +80,37 @@ class build_py(orig.build_py, Mixin2to3):
|
|||
def _get_data_files(self):
|
||||
"""Generate list of '(package,src_dir,build_dir,filenames)' tuples"""
|
||||
self.analyze_manifest()
|
||||
data = []
|
||||
for package in self.packages or ():
|
||||
# Locate package source directory
|
||||
src_dir = self.get_package_dir(package)
|
||||
return list(map(self._get_pkg_data_files, self.packages or ()))
|
||||
|
||||
# Compute package build directory
|
||||
build_dir = os.path.join(*([self.build_lib] + package.split('.')))
|
||||
def _get_pkg_data_files(self, package):
|
||||
# Locate package source directory
|
||||
src_dir = self.get_package_dir(package)
|
||||
|
||||
# Length of path to strip from found files
|
||||
plen = len(src_dir) + 1
|
||||
# Compute package build directory
|
||||
build_dir = os.path.join(*([self.build_lib] + package.split('.')))
|
||||
|
||||
# Strip directory from globbed filenames
|
||||
filenames = [
|
||||
file[plen:] for file in self.find_data_files(package, src_dir)
|
||||
]
|
||||
data.append((package, src_dir, build_dir, filenames))
|
||||
return data
|
||||
# Strip directory from globbed filenames
|
||||
filenames = [
|
||||
os.path.relpath(file, src_dir)
|
||||
for file in self.find_data_files(package, src_dir)
|
||||
]
|
||||
return package, src_dir, build_dir, filenames
|
||||
|
||||
def find_data_files(self, package, src_dir):
|
||||
"""Return filenames for package's data files in 'src_dir'"""
|
||||
globs = (self.package_data.get('', [])
|
||||
+ self.package_data.get(package, []))
|
||||
files = self.manifest_files.get(package, [])[:]
|
||||
for pattern in globs:
|
||||
# Each pattern has to be converted to a platform-specific path
|
||||
files.extend(glob(os.path.join(src_dir, convert_path(pattern))))
|
||||
patterns = self._get_platform_patterns(
|
||||
self.package_data,
|
||||
package,
|
||||
src_dir,
|
||||
)
|
||||
globs_expanded = map(glob, patterns)
|
||||
# flatten the expanded globs into an iterable of matches
|
||||
globs_matches = itertools.chain.from_iterable(globs_expanded)
|
||||
glob_files = filter(os.path.isfile, globs_matches)
|
||||
files = itertools.chain(
|
||||
self.manifest_files.get(package, []),
|
||||
glob_files,
|
||||
)
|
||||
return self.exclude_data_files(package, src_dir, files)
|
||||
|
||||
def build_package_data(self):
|
||||
|
|
@ -184,26 +195,63 @@ class build_py(orig.build_py, Mixin2to3):
|
|||
|
||||
def exclude_data_files(self, package, src_dir, files):
|
||||
"""Filter filenames for package's data files in 'src_dir'"""
|
||||
globs = (
|
||||
self.exclude_package_data.get('', [])
|
||||
+ self.exclude_package_data.get(package, [])
|
||||
files = list(files)
|
||||
patterns = self._get_platform_patterns(
|
||||
self.exclude_package_data,
|
||||
package,
|
||||
src_dir,
|
||||
)
|
||||
bad = set(
|
||||
item
|
||||
for pattern in globs
|
||||
for item in fnmatch.filter(
|
||||
files,
|
||||
os.path.join(src_dir, convert_path(pattern)),
|
||||
)
|
||||
match_groups = (
|
||||
fnmatch.filter(files, pattern)
|
||||
for pattern in patterns
|
||||
)
|
||||
seen = collections.defaultdict(itertools.count)
|
||||
return [
|
||||
# flatten the groups of matches into an iterable of matches
|
||||
matches = itertools.chain.from_iterable(match_groups)
|
||||
bad = set(matches)
|
||||
keepers = (
|
||||
fn
|
||||
for fn in files
|
||||
if fn not in bad
|
||||
# ditch dupes
|
||||
and not next(seen[fn])
|
||||
]
|
||||
)
|
||||
# ditch dupes
|
||||
return list(_unique_everseen(keepers))
|
||||
|
||||
@staticmethod
|
||||
def _get_platform_patterns(spec, package, src_dir):
|
||||
"""
|
||||
yield platform-specific path patterns (suitable for glob
|
||||
or fn_match) from a glob-based spec (such as
|
||||
self.package_data or self.exclude_package_data)
|
||||
matching package in src_dir.
|
||||
"""
|
||||
raw_patterns = itertools.chain(
|
||||
spec.get('', []),
|
||||
spec.get(package, []),
|
||||
)
|
||||
return (
|
||||
# Each pattern has to be converted to a platform-specific path
|
||||
os.path.join(src_dir, convert_path(pattern))
|
||||
for pattern in raw_patterns
|
||||
)
|
||||
|
||||
|
||||
# from Python docs
|
||||
def _unique_everseen(iterable, key=None):
|
||||
"List unique elements, preserving order. Remember all elements ever seen."
|
||||
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
|
||||
# unique_everseen('ABBCcAD', str.lower) --> A B C D
|
||||
seen = set()
|
||||
seen_add = seen.add
|
||||
if key is None:
|
||||
for element in filterfalse(seen.__contains__, iterable):
|
||||
seen_add(element)
|
||||
yield element
|
||||
else:
|
||||
for element in iterable:
|
||||
k = key(element)
|
||||
if k not in seen:
|
||||
seen_add(k)
|
||||
yield element
|
||||
|
||||
|
||||
def assert_relative(path):
|
||||
|
|
|
|||
|
|
@ -9,10 +9,13 @@ from setuptools.extern import six
|
|||
|
||||
from pkg_resources import Distribution, PathMetadata, normalize_path
|
||||
from setuptools.command.easy_install import easy_install
|
||||
from setuptools import namespaces
|
||||
import setuptools
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
class develop(easy_install):
|
||||
|
||||
class develop(namespaces.DevelopInstaller, easy_install):
|
||||
"""Set up package for development"""
|
||||
|
||||
description = "install package in 'development mode'"
|
||||
|
|
@ -30,6 +33,7 @@ class develop(easy_install):
|
|||
if self.uninstall:
|
||||
self.multi_version = True
|
||||
self.uninstall_link()
|
||||
self.uninstall_namespaces()
|
||||
else:
|
||||
self.install_for_development()
|
||||
self.warn_deprecated_options()
|
||||
|
|
@ -77,15 +81,30 @@ class develop(easy_install):
|
|||
project_name=ei.egg_name
|
||||
)
|
||||
|
||||
p = self.egg_base.replace(os.sep, '/')
|
||||
if p != os.curdir:
|
||||
p = '../' * (p.count('/') + 1)
|
||||
self.setup_path = p
|
||||
p = normalize_path(os.path.join(self.install_dir, self.egg_path, p))
|
||||
if p != normalize_path(os.curdir):
|
||||
self.setup_path = self._resolve_setup_path(
|
||||
self.egg_base,
|
||||
self.install_dir,
|
||||
self.egg_path,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _resolve_setup_path(egg_base, install_dir, egg_path):
|
||||
"""
|
||||
Generate a path from egg_base back to '.' where the
|
||||
setup script resides and ensure that path points to the
|
||||
setup path from $install_dir/$egg_path.
|
||||
"""
|
||||
path_to_setup = egg_base.replace(os.sep, '/').rstrip('/')
|
||||
if path_to_setup != os.curdir:
|
||||
path_to_setup = '../' * (path_to_setup.count('/') + 1)
|
||||
resolved = normalize_path(
|
||||
os.path.join(install_dir, egg_path, path_to_setup)
|
||||
)
|
||||
if resolved != normalize_path(os.curdir):
|
||||
raise DistutilsOptionError(
|
||||
"Can't get a consistent path to setup script from"
|
||||
" installation directory", p, normalize_path(os.curdir))
|
||||
" installation directory", resolved, normalize_path(os.curdir))
|
||||
return path_to_setup
|
||||
|
||||
def install_for_development(self):
|
||||
if six.PY3 and getattr(self.distribution, 'use_2to3', False):
|
||||
|
|
@ -123,6 +142,8 @@ class develop(easy_install):
|
|||
self.easy_install(setuptools.bootstrap_install_from)
|
||||
setuptools.bootstrap_install_from = None
|
||||
|
||||
self.install_namespaces()
|
||||
|
||||
# create an .egg-link in the installation dir, pointing to our egg
|
||||
log.info("Creating %s (link to %s)", self.egg_link, self.egg_base)
|
||||
if not self.dry_run:
|
||||
|
|
@ -173,7 +194,7 @@ class develop(easy_install):
|
|||
return easy_install.install_wrapper_scripts(self, dist)
|
||||
|
||||
|
||||
class VersionlessRequirement(object):
|
||||
class VersionlessRequirement:
|
||||
"""
|
||||
Adapt a pkg_resources.Distribution to simply return the project
|
||||
name as the 'requirement' so that scripts will work across
|
||||
|
|
@ -186,6 +207,7 @@ class VersionlessRequirement(object):
|
|||
>>> str(adapted_dist.as_requirement())
|
||||
'foo'
|
||||
"""
|
||||
|
||||
def __init__(self, dist):
|
||||
self.__dist = dist
|
||||
|
||||
|
|
|
|||
36
Lib/site-packages/setuptools/command/dist_info.py
Normal file
36
Lib/site-packages/setuptools/command/dist_info.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
"""
|
||||
Create a dist_info directory
|
||||
As defined in the wheel specification
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from distutils.core import Command
|
||||
from distutils import log
|
||||
|
||||
|
||||
class dist_info(Command):
|
||||
|
||||
description = 'create a .dist-info directory'
|
||||
|
||||
user_options = [
|
||||
('egg-base=', 'e', "directory containing .egg-info directories"
|
||||
" (default: top of the source tree)"),
|
||||
]
|
||||
|
||||
def initialize_options(self):
|
||||
self.egg_base = None
|
||||
|
||||
def finalize_options(self):
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
egg_info = self.get_finalized_command('egg_info')
|
||||
egg_info.egg_base = self.egg_base
|
||||
egg_info.finalize_options()
|
||||
egg_info.run()
|
||||
dist_info_dir = egg_info.egg_info[:-len('.egg-info')] + '.dist-info'
|
||||
log.info("creating '{}'".format(os.path.abspath(dist_info_dir)))
|
||||
|
||||
bdist_wheel = self.get_finalized_command('bdist_wheel')
|
||||
bdist_wheel.egg2dist(egg_info.egg_info, dist_info_dir)
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Easy Install
|
||||
------------
|
||||
|
|
@ -8,15 +7,17 @@ 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://pythonhosted.org/setuptools/easy_install.html
|
||||
__ 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.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
|
||||
|
|
@ -30,7 +31,6 @@ import zipfile
|
|||
import re
|
||||
import stat
|
||||
import random
|
||||
import platform
|
||||
import textwrap
|
||||
import warnings
|
||||
import site
|
||||
|
|
@ -40,29 +40,37 @@ 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
|
||||
from setuptools.extern.six.moves import configparser, map
|
||||
|
||||
from setuptools import Command
|
||||
from setuptools.sandbox import run_setup
|
||||
from setuptools.py31compat import get_path, get_config_vars
|
||||
from setuptools.py27compat import rmtree_safe
|
||||
from setuptools.command import setopt
|
||||
from setuptools.archive_util import unpack_archive
|
||||
from setuptools.package_index import PackageIndex
|
||||
from setuptools.package_index import URL_SCHEME
|
||||
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
|
||||
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',
|
||||
|
|
@ -74,6 +82,12 @@ def is_64bit():
|
|||
|
||||
|
||||
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:
|
||||
|
|
@ -84,7 +98,8 @@ def samefile(p1, p2):
|
|||
|
||||
|
||||
if six.PY2:
|
||||
def _to_ascii(s):
|
||||
|
||||
def _to_bytes(s):
|
||||
return s
|
||||
|
||||
def isascii(s):
|
||||
|
|
@ -94,8 +109,9 @@ if six.PY2:
|
|||
except UnicodeError:
|
||||
return False
|
||||
else:
|
||||
def _to_ascii(s):
|
||||
return s.encode('ascii')
|
||||
|
||||
def _to_bytes(s):
|
||||
return s.encode('utf8')
|
||||
|
||||
def isascii(s):
|
||||
try:
|
||||
|
|
@ -105,6 +121,9 @@ else:
|
|||
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"
|
||||
|
|
@ -258,8 +277,10 @@ class easy_install(Command):
|
|||
self.expand_basedirs()
|
||||
self.expand_dirs()
|
||||
|
||||
self._expand('install_dir', 'script_dir', 'build_directory',
|
||||
'site_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:
|
||||
|
|
@ -304,7 +325,7 @@ class easy_install(Command):
|
|||
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.python.org/simple"
|
||||
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:
|
||||
|
|
@ -379,9 +400,15 @@ class easy_install(Command):
|
|||
|
||||
def expand_dirs(self):
|
||||
"""Calls `os.path.expanduser` on install dirs."""
|
||||
self._expand_attrs(['install_purelib', 'install_platlib',
|
||||
'install_lib', 'install_headers',
|
||||
'install_scripts', 'install_data', ])
|
||||
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:
|
||||
|
|
@ -413,7 +440,7 @@ class easy_install(Command):
|
|||
"""
|
||||
try:
|
||||
pid = os.getpid()
|
||||
except:
|
||||
except Exception:
|
||||
pid = random.randint(0, sys.maxsize)
|
||||
return os.path.join(self.install_dir, "test-easy-install-%s" % pid)
|
||||
|
||||
|
|
@ -454,8 +481,7 @@ class easy_install(Command):
|
|||
else:
|
||||
self.pth_file = None
|
||||
|
||||
PYTHONPATH = os.environ.get('PYTHONPATH', '').split(os.pathsep)
|
||||
if instdir not in map(normalize_path, filter(None, PYTHONPATH)):
|
||||
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):
|
||||
|
|
@ -494,7 +520,7 @@ class easy_install(Command):
|
|||
For information on other options, you may wish to consult the
|
||||
documentation at:
|
||||
|
||||
https://pythonhosted.org/setuptools/easy_install.html
|
||||
https://setuptools.readthedocs.io/en/latest/easy_install.html
|
||||
|
||||
Please make the appropriate changes for your system and try again.
|
||||
""").lstrip()
|
||||
|
|
@ -515,27 +541,34 @@ class easy_install(Command):
|
|||
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)
|
||||
if not os.path.exists(dirname):
|
||||
os.makedirs(dirname)
|
||||
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("import os; f = open(%r, 'w'); f.write('OK'); "
|
||||
"f.close()\n" % (ok_file,))
|
||||
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')
|
||||
if (basename.lower() == 'python.exe' and
|
||||
os.path.exists(alt)):
|
||||
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
|
||||
|
||||
|
|
@ -600,20 +633,26 @@ class easy_install(Command):
|
|||
(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):
|
||||
tmpdir = tempfile.mkdtemp(prefix="easy_install-")
|
||||
download = None
|
||||
if not self.editable:
|
||||
self.install_site_py()
|
||||
|
||||
try:
|
||||
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)
|
||||
download = self.package_index.download(spec, tmpdir)
|
||||
return self.install_item(None, download, tmpdir, deps,
|
||||
True)
|
||||
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
|
||||
|
|
@ -639,10 +678,6 @@ class easy_install(Command):
|
|||
else:
|
||||
return self.install_item(spec, dist.location, tmpdir, deps)
|
||||
|
||||
finally:
|
||||
if os.path.exists(tmpdir):
|
||||
rmtree(tmpdir)
|
||||
|
||||
def install_item(self, spec, download, tmpdir, deps, install_needed=False):
|
||||
|
||||
# Installation is also needed if file in tmpdir or is not an egg
|
||||
|
|
@ -710,10 +745,7 @@ class easy_install(Command):
|
|||
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 or distreq
|
||||
requirement = Requirement(
|
||||
distreq.project_name, distreq.specs, requirement.extras
|
||||
)
|
||||
requirement = Requirement(str(distreq))
|
||||
log.info("Processing dependencies for %s", requirement)
|
||||
try:
|
||||
distros = WorkingSet([]).resolve(
|
||||
|
|
@ -742,8 +774,9 @@ class easy_install(Command):
|
|||
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")
|
||||
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):
|
||||
|
|
@ -775,7 +808,7 @@ class easy_install(Command):
|
|||
if is_script:
|
||||
body = self._load_template(dev_path) % locals()
|
||||
script_text = ScriptWriter.get_header(script_text) + body
|
||||
self.write_script(script_name, _to_ascii(script_text), 'b')
|
||||
self.write_script(script_name, _to_bytes(script_text), 'b')
|
||||
|
||||
@staticmethod
|
||||
def _load_template(dev_path):
|
||||
|
|
@ -783,7 +816,7 @@ class easy_install(Command):
|
|||
There are a couple of template scripts in the package. This
|
||||
function loads one of them and prepares it for use.
|
||||
"""
|
||||
# See https://bitbucket.org/pypa/setuptools/issue/134 for info
|
||||
# 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:
|
||||
|
|
@ -801,14 +834,16 @@ class easy_install(Command):
|
|||
target = os.path.join(self.script_dir, script_name)
|
||||
self.add_output(target)
|
||||
|
||||
if self.dry_run:
|
||||
return
|
||||
|
||||
mask = current_umask()
|
||||
if not self.dry_run:
|
||||
ensure_directory(target)
|
||||
if os.path.exists(target):
|
||||
os.unlink(target)
|
||||
with open(target, "w" + mode) as f:
|
||||
f.write(contents)
|
||||
chmod(target, 0o777 - mask)
|
||||
ensure_directory(target)
|
||||
if os.path.exists(target):
|
||||
os.unlink(target)
|
||||
with open(target, "w" + mode) as f:
|
||||
f.write(contents)
|
||||
chmod(target, 0o777 - mask)
|
||||
|
||||
def install_eggs(self, spec, dist_filename, tmpdir):
|
||||
# .egg dirs or files are already built, so just return them
|
||||
|
|
@ -816,6 +851,8 @@ class easy_install(Command):
|
|||
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
|
||||
|
|
@ -861,8 +898,10 @@ class easy_install(Command):
|
|||
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.join(
|
||||
self.install_dir,
|
||||
os.path.basename(egg_path),
|
||||
)
|
||||
destination = os.path.abspath(destination)
|
||||
if not self.dry_run:
|
||||
ensure_directory(destination)
|
||||
|
|
@ -872,8 +911,11 @@ class easy_install(Command):
|
|||
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)
|
||||
self.execute(
|
||||
os.unlink,
|
||||
(destination,),
|
||||
"Removing " + destination,
|
||||
)
|
||||
try:
|
||||
new_dist_is_zipped = False
|
||||
if os.path.isdir(egg_path):
|
||||
|
|
@ -890,13 +932,19 @@ class easy_install(Command):
|
|||
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:
|
||||
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
|
||||
|
||||
|
|
@ -918,8 +966,8 @@ class easy_install(Command):
|
|||
)
|
||||
|
||||
# Convert the .exe to an unpacked egg
|
||||
egg_path = dist.location = os.path.join(tmpdir, dist.egg_name() +
|
||||
'.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')
|
||||
|
|
@ -937,13 +985,13 @@ class easy_install(Command):
|
|||
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)]
|
||||
)
|
||||
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
|
||||
egg_path, egg_tmp, verbose=self.verbose, dry_run=self.dry_run,
|
||||
)
|
||||
# install the .egg
|
||||
return self.install_egg(egg_path, tmpdir)
|
||||
|
|
@ -1001,6 +1049,35 @@ class easy_install(Command):
|
|||
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
|
||||
|
|
@ -1131,7 +1208,7 @@ class easy_install(Command):
|
|||
if dist.location in self.pth_file.paths:
|
||||
log.info(
|
||||
"%s is already the active version in easy-install.pth",
|
||||
dist
|
||||
dist,
|
||||
)
|
||||
else:
|
||||
log.info("Adding %s to easy-install.pth file", dist)
|
||||
|
|
@ -1179,7 +1256,6 @@ class easy_install(Command):
|
|||
|
||||
def byte_compile(self, to_compile):
|
||||
if sys.dont_write_bytecode:
|
||||
self.warn('byte-compiling is disabled, skipping.')
|
||||
return
|
||||
|
||||
from distutils.util import byte_compile
|
||||
|
|
@ -1192,7 +1268,7 @@ class easy_install(Command):
|
|||
if self.optimize:
|
||||
byte_compile(
|
||||
to_compile, optimize=self.optimize, force=1,
|
||||
dry_run=self.dry_run
|
||||
dry_run=self.dry_run,
|
||||
)
|
||||
finally:
|
||||
log.set_verbosity(self.verbose) # restore original verbosity
|
||||
|
|
@ -1223,7 +1299,8 @@ class easy_install(Command):
|
|||
* You can set up the installation directory to support ".pth" files by
|
||||
using one of the approaches described here:
|
||||
|
||||
https://pythonhosted.org/setuptools/easy_install.html#custom-installation-locations
|
||||
https://setuptools.readthedocs.io/en/latest/easy_install.html#custom-installation-locations
|
||||
|
||||
|
||||
Please make the appropriate changes for your system and try again.""").lstrip()
|
||||
|
||||
|
|
@ -1239,17 +1316,14 @@ class easy_install(Command):
|
|||
|
||||
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)
|
||||
f = open(sitepy, 'rb')
|
||||
current = f.read()
|
||||
# we want str, not bytes
|
||||
if six.PY3:
|
||||
current = current.decode()
|
||||
with io.open(sitepy) as strm:
|
||||
current = strm.read()
|
||||
|
||||
f.close()
|
||||
if not current.startswith('def __boot():'):
|
||||
raise DistutilsError(
|
||||
"%s is not a setuptools-generated site.py; please"
|
||||
|
|
@ -1260,9 +1334,8 @@ class easy_install(Command):
|
|||
log.info("Creating %s", sitepy)
|
||||
if not self.dry_run:
|
||||
ensure_directory(sitepy)
|
||||
f = open(sitepy, 'wb')
|
||||
f.write(source)
|
||||
f.close()
|
||||
with io.open(sitepy, 'w', encoding='utf-8') as strm:
|
||||
strm.write(source)
|
||||
self.byte_compile([sitepy])
|
||||
|
||||
self.sitepy_installed = True
|
||||
|
|
@ -1312,10 +1385,21 @@ class easy_install(Command):
|
|||
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 = [_f for _f in os.environ.get('PYTHONPATH',
|
||||
'').split(os.pathsep) if _f]
|
||||
"""
|
||||
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)
|
||||
|
|
@ -1324,15 +1408,20 @@ def get_site_dirs():
|
|||
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")])
|
||||
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")]
|
||||
)
|
||||
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
|
||||
|
|
@ -1340,12 +1429,14 @@ def get_site_dirs():
|
|||
if 'Python.framework' in prefix:
|
||||
home = os.environ.get('HOME')
|
||||
if home:
|
||||
sitedirs.append(
|
||||
os.path.join(home,
|
||||
'Library',
|
||||
'Python',
|
||||
sys.version[:3],
|
||||
'site-packages'))
|
||||
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:
|
||||
|
|
@ -1354,6 +1445,11 @@ def get_site_dirs():
|
|||
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
|
||||
|
|
@ -1421,8 +1517,8 @@ def extract_wininst_cfg(dist_filename):
|
|||
return None # not a valid tag
|
||||
|
||||
f.seek(prepended - (12 + cfglen))
|
||||
cfg = configparser.RawConfigParser(
|
||||
{'version': '', 'target_version': ''})
|
||||
init = {'version': '', 'target_version': ''}
|
||||
cfg = configparser.RawConfigParser(init)
|
||||
try:
|
||||
part = f.read(cfglen)
|
||||
# Read up to the first null byte.
|
||||
|
|
@ -1445,7 +1541,8 @@ def get_exe_prefixes(exe_filename):
|
|||
"""Get exe->egg path translations for a given .exe file"""
|
||||
|
||||
prefixes = [
|
||||
('PURELIB/', ''), ('PLATLIB/pywin32_system32', ''),
|
||||
('PURELIB/', ''),
|
||||
('PLATLIB/pywin32_system32', ''),
|
||||
('PLATLIB/', ''),
|
||||
('SCRIPTS/', 'EGG-INFO/scripts/'),
|
||||
('DATA/lib/site-packages', ''),
|
||||
|
|
@ -1479,15 +1576,6 @@ def get_exe_prefixes(exe_filename):
|
|||
return prefixes
|
||||
|
||||
|
||||
def parse_requirement_arg(spec):
|
||||
try:
|
||||
return Requirement.parse(spec)
|
||||
except ValueError:
|
||||
raise DistutilsError(
|
||||
"Not a URL, existing file, or requirement spec: %r" % (spec,)
|
||||
)
|
||||
|
||||
|
||||
class PthDistributions(Environment):
|
||||
"""A .pth file with Distribution paths in it"""
|
||||
|
||||
|
|
@ -1597,7 +1685,6 @@ class PthDistributions(Environment):
|
|||
|
||||
|
||||
class RewritePthDistributions(PthDistributions):
|
||||
|
||||
@classmethod
|
||||
def _wrap_lines(cls, lines):
|
||||
yield cls.prelude
|
||||
|
|
@ -1605,12 +1692,11 @@ class RewritePthDistributions(PthDistributions):
|
|||
yield line
|
||||
yield cls.postlude
|
||||
|
||||
_inline = lambda text: textwrap.dedent(text).strip().replace('\n', '; ')
|
||||
prelude = _inline("""
|
||||
prelude = _one_liner("""
|
||||
import sys
|
||||
sys.__plen = len(sys.path)
|
||||
""")
|
||||
postlude = _inline("""
|
||||
postlude = _one_liner("""
|
||||
import sys
|
||||
new = sys.path[sys.__plen:]
|
||||
del sys.path[sys.__plen:]
|
||||
|
|
@ -1620,7 +1706,7 @@ class RewritePthDistributions(PthDistributions):
|
|||
""")
|
||||
|
||||
|
||||
if os.environ.get('SETUPTOOLS_SYS_PATH_TECHNIQUE', 'rewrite') == 'rewrite':
|
||||
if os.environ.get('SETUPTOOLS_SYS_PATH_TECHNIQUE', 'raw') == 'rewrite':
|
||||
PthDistributions = RewritePthDistributions
|
||||
|
||||
|
||||
|
|
@ -1637,7 +1723,7 @@ def _first_line_re():
|
|||
|
||||
|
||||
def auto_chmod(func, arg, exc):
|
||||
if func is os.remove and os.name == 'nt':
|
||||
if func in [os.unlink, os.remove] and os.name == 'nt':
|
||||
chmod(arg, stat.S_IWRITE)
|
||||
return func(arg)
|
||||
et, ev, _ = sys.exc_info()
|
||||
|
|
@ -1769,8 +1855,8 @@ def _update_zipimporter_cache(normalized_path, cache, updater=None):
|
|||
# * 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://bitbucket.org/pypa/setuptools/issue/202/more-robust-zipimporter-cache-invalidation#comment-10495960
|
||||
# https://bitbucket.org/pypy/pypy/src/dd07756a34a41f674c0cacfbc8ae1d4cc9ea2ae4/pypy/module/zipimport/interp_zipimport.py#cl-99
|
||||
# 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)
|
||||
|
|
@ -1790,6 +1876,7 @@ def _remove_and_clear_zip_directory_cache_data(normalized_path):
|
|||
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
|
||||
|
|
@ -1802,6 +1889,7 @@ 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
|
||||
|
|
@ -1876,17 +1964,6 @@ def chmod(path, mode):
|
|||
log.debug("chmod failed: %s", e)
|
||||
|
||||
|
||||
def fix_jython_executable(executable, options):
|
||||
warnings.warn("Use JythonCommandSpec", DeprecationWarning, stacklevel=2)
|
||||
|
||||
if not JythonCommandSpec.relevant():
|
||||
return executable
|
||||
|
||||
cmd = CommandSpec.best().from_param(executable)
|
||||
cmd.install_options(options)
|
||||
return cmd.as_header().lstrip('#!').rstrip('\n')
|
||||
|
||||
|
||||
class CommandSpec(list):
|
||||
"""
|
||||
A command spec for a #! header, specified as a list of arguments akin to
|
||||
|
|
@ -1901,7 +1978,7 @@ class CommandSpec(list):
|
|||
"""
|
||||
Choose the best CommandSpec class based on environmental conditions.
|
||||
"""
|
||||
return cls if not JythonCommandSpec.relevant() else JythonCommandSpec
|
||||
return cls
|
||||
|
||||
@classmethod
|
||||
def _sys_executable(cls):
|
||||
|
|
@ -1955,11 +2032,21 @@ class CommandSpec(list):
|
|||
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(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()
|
||||
|
||||
|
|
@ -1968,49 +2055,21 @@ class WindowsCommandSpec(CommandSpec):
|
|||
split_args = dict(posix=False)
|
||||
|
||||
|
||||
class JythonCommandSpec(CommandSpec):
|
||||
@classmethod
|
||||
def relevant(cls):
|
||||
return (
|
||||
sys.platform.startswith('java')
|
||||
and
|
||||
__import__('java').lang.System.getProperty('os.name') != 'Linux'
|
||||
)
|
||||
|
||||
def as_header(self):
|
||||
"""
|
||||
Workaround Jython's sys.executable being a .sh (an invalid
|
||||
shebang line interpreter)
|
||||
"""
|
||||
if not is_sh(self[0]):
|
||||
return super(JythonCommandSpec, self).as_header()
|
||||
|
||||
if self.options:
|
||||
# Can't apply the workaround, leave it broken
|
||||
log.warn(
|
||||
"WARNING: Unable to adapt shebang line for Jython,"
|
||||
" the following script is NOT executable\n"
|
||||
" see http://bugs.jython.org/issue1112 for"
|
||||
" more information.")
|
||||
return super(JythonCommandSpec, self).as_header()
|
||||
|
||||
items = ['/usr/bin/env'] + self + list(self.options)
|
||||
return self._render(items)
|
||||
|
||||
|
||||
class ScriptWriter(object):
|
||||
class ScriptWriter:
|
||||
"""
|
||||
Encapsulates behavior around writing entry point scripts for console and
|
||||
gui apps.
|
||||
"""
|
||||
|
||||
template = textwrap.dedent("""
|
||||
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)()
|
||||
)
|
||||
|
|
@ -2021,7 +2080,7 @@ class ScriptWriter(object):
|
|||
@classmethod
|
||||
def get_script_args(cls, dist, executable=None, wininst=False):
|
||||
# for backward compatibility
|
||||
warnings.warn("Use get_args", DeprecationWarning)
|
||||
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)
|
||||
|
|
@ -2029,12 +2088,10 @@ class ScriptWriter(object):
|
|||
@classmethod
|
||||
def get_script_header(cls, script_text, executable=None, wininst=False):
|
||||
# for backward compatibility
|
||||
warnings.warn("Use get_header", DeprecationWarning)
|
||||
warnings.warn("Use get_header", EasyInstallDeprecationWarning, stacklevel=2)
|
||||
if wininst:
|
||||
executable = "python.exe"
|
||||
cmd = cls.command_spec_class.best().from_param(executable)
|
||||
cmd.install_options(script_text)
|
||||
return cmd.as_header()
|
||||
return cls.get_header(script_text, executable)
|
||||
|
||||
@classmethod
|
||||
def get_args(cls, dist, header=None):
|
||||
|
|
@ -2066,7 +2123,7 @@ class ScriptWriter(object):
|
|||
@classmethod
|
||||
def get_writer(cls, force_windows):
|
||||
# for backward compatibility
|
||||
warnings.warn("Use best", DeprecationWarning)
|
||||
warnings.warn("Use best", EasyInstallDeprecationWarning)
|
||||
return WindowsScriptWriter.best() if force_windows else cls.best()
|
||||
|
||||
@classmethod
|
||||
|
|
@ -2074,7 +2131,10 @@ class ScriptWriter(object):
|
|||
"""
|
||||
Select the best ScriptWriter for this environment.
|
||||
"""
|
||||
return WindowsScriptWriter.best() if sys.platform == 'win32' else cls
|
||||
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):
|
||||
|
|
@ -2095,7 +2155,7 @@ class WindowsScriptWriter(ScriptWriter):
|
|||
@classmethod
|
||||
def get_writer(cls):
|
||||
# for backward compatibility
|
||||
warnings.warn("Use best", DeprecationWarning)
|
||||
warnings.warn("Use best", EasyInstallDeprecationWarning)
|
||||
return cls.best()
|
||||
|
||||
@classmethod
|
||||
|
|
@ -2116,8 +2176,11 @@ class WindowsScriptWriter(ScriptWriter):
|
|||
"For Windows, add a .py extension"
|
||||
ext = dict(console='.pya', gui='.pyw')[type_]
|
||||
if ext not in os.environ['PATHEXT'].lower().split(';'):
|
||||
warnings.warn("%s not listed in PATHEXT; scripts will not be "
|
||||
"recognized as executables." % ext, UserWarning)
|
||||
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)
|
||||
|
|
@ -2196,8 +2259,6 @@ def get_win_launcher(type):
|
|||
Returns the executable as a byte string.
|
||||
"""
|
||||
launcher_fn = '%s.exe' % type
|
||||
if platform.machine().lower() == 'arm':
|
||||
launcher_fn = launcher_fn.replace(".", "-arm.")
|
||||
if is_64bit():
|
||||
launcher_fn = launcher_fn.replace(".", "-64.")
|
||||
else:
|
||||
|
|
@ -2214,39 +2275,7 @@ def load_launcher_manifest(name):
|
|||
|
||||
|
||||
def rmtree(path, ignore_errors=False, onerror=auto_chmod):
|
||||
"""Recursively delete a directory tree.
|
||||
|
||||
This code is taken from the Python 2.4 version of 'shutil', because
|
||||
the 2.3 version doesn't really work right.
|
||||
"""
|
||||
if ignore_errors:
|
||||
def onerror(*args):
|
||||
pass
|
||||
elif onerror is None:
|
||||
def onerror(*args):
|
||||
raise
|
||||
names = []
|
||||
try:
|
||||
names = os.listdir(path)
|
||||
except os.error:
|
||||
onerror(os.listdir, path, sys.exc_info())
|
||||
for name in names:
|
||||
fullname = os.path.join(path, name)
|
||||
try:
|
||||
mode = os.lstat(fullname).st_mode
|
||||
except os.error:
|
||||
mode = 0
|
||||
if stat.S_ISDIR(mode):
|
||||
rmtree(fullname, ignore_errors, onerror)
|
||||
else:
|
||||
try:
|
||||
os.remove(fullname)
|
||||
except os.error:
|
||||
onerror(os.remove, fullname, sys.exc_info())
|
||||
try:
|
||||
os.rmdir(path)
|
||||
except os.error:
|
||||
onerror(os.rmdir, path, sys.exc_info())
|
||||
return shutil.rmtree(path, ignore_errors, onerror)
|
||||
|
||||
|
||||
def current_umask():
|
||||
|
|
@ -2283,7 +2312,8 @@ def main(argv=None, **kw):
|
|||
setup(
|
||||
script_args=['-q', 'easy_install', '-v'] + argv,
|
||||
script_name=sys.argv[0] or 'easy_install',
|
||||
distclass=DistributionWithoutHelpCommands, **kw
|
||||
distclass=DistributionWithoutHelpCommands,
|
||||
**kw
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -2306,3 +2336,7 @@ def _patch_usage():
|
|||
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."""
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
Create a distribution's .egg-info directory and contents"""
|
||||
|
||||
from distutils.filelist import FileList as _FileList
|
||||
from distutils.errors import DistutilsInternalError
|
||||
from distutils.util import convert_path
|
||||
from distutils import log
|
||||
import distutils.errors
|
||||
|
|
@ -13,8 +14,10 @@ import sys
|
|||
import io
|
||||
import warnings
|
||||
import time
|
||||
import collections
|
||||
|
||||
from setuptools.extern import six
|
||||
from setuptools.extern.six.moves import map
|
||||
|
||||
from setuptools import Command
|
||||
from setuptools.command.sdist import sdist
|
||||
|
|
@ -25,60 +28,175 @@ from pkg_resources import (
|
|||
parse_requirements, safe_name, parse_version,
|
||||
safe_version, yield_lines, EntryPoint, iter_entry_points, to_filename)
|
||||
import setuptools.unicode_utils as unicode_utils
|
||||
from setuptools.glob import glob
|
||||
|
||||
from pkg_resources.extern import packaging
|
||||
from setuptools.extern import packaging
|
||||
from setuptools import SetuptoolsDeprecationWarning
|
||||
|
||||
try:
|
||||
from setuptools_svn import svn_utils
|
||||
except ImportError:
|
||||
pass
|
||||
def translate_pattern(glob):
|
||||
"""
|
||||
Translate a file path glob like '*.txt' in to a regular expression.
|
||||
This differs from fnmatch.translate which allows wildcards to match
|
||||
directory separators. It also knows about '**/' which matches any number of
|
||||
directories.
|
||||
"""
|
||||
pat = ''
|
||||
|
||||
# This will split on '/' within [character classes]. This is deliberate.
|
||||
chunks = glob.split(os.path.sep)
|
||||
|
||||
sep = re.escape(os.sep)
|
||||
valid_char = '[^%s]' % (sep,)
|
||||
|
||||
for c, chunk in enumerate(chunks):
|
||||
last_chunk = c == len(chunks) - 1
|
||||
|
||||
# Chunks that are a literal ** are globstars. They match anything.
|
||||
if chunk == '**':
|
||||
if last_chunk:
|
||||
# Match anything if this is the last component
|
||||
pat += '.*'
|
||||
else:
|
||||
# Match '(name/)*'
|
||||
pat += '(?:%s+%s)*' % (valid_char, sep)
|
||||
continue # Break here as the whole path component has been handled
|
||||
|
||||
# Find any special characters in the remainder
|
||||
i = 0
|
||||
chunk_len = len(chunk)
|
||||
while i < chunk_len:
|
||||
char = chunk[i]
|
||||
if char == '*':
|
||||
# Match any number of name characters
|
||||
pat += valid_char + '*'
|
||||
elif char == '?':
|
||||
# Match a name character
|
||||
pat += valid_char
|
||||
elif char == '[':
|
||||
# Character class
|
||||
inner_i = i + 1
|
||||
# Skip initial !/] chars
|
||||
if inner_i < chunk_len and chunk[inner_i] == '!':
|
||||
inner_i = inner_i + 1
|
||||
if inner_i < chunk_len and chunk[inner_i] == ']':
|
||||
inner_i = inner_i + 1
|
||||
|
||||
# Loop till the closing ] is found
|
||||
while inner_i < chunk_len and chunk[inner_i] != ']':
|
||||
inner_i = inner_i + 1
|
||||
|
||||
if inner_i >= chunk_len:
|
||||
# Got to the end of the string without finding a closing ]
|
||||
# Do not treat this as a matching group, but as a literal [
|
||||
pat += re.escape(char)
|
||||
else:
|
||||
# Grab the insides of the [brackets]
|
||||
inner = chunk[i + 1:inner_i]
|
||||
char_class = ''
|
||||
|
||||
# Class negation
|
||||
if inner[0] == '!':
|
||||
char_class = '^'
|
||||
inner = inner[1:]
|
||||
|
||||
char_class += re.escape(inner)
|
||||
pat += '[%s]' % (char_class,)
|
||||
|
||||
# Skip to the end ]
|
||||
i = inner_i
|
||||
else:
|
||||
pat += re.escape(char)
|
||||
i += 1
|
||||
|
||||
# Join each chunk with the dir separator
|
||||
if not last_chunk:
|
||||
pat += sep
|
||||
|
||||
pat += r'\Z'
|
||||
return re.compile(pat, flags=re.MULTILINE|re.DOTALL)
|
||||
|
||||
|
||||
class egg_info(Command):
|
||||
class InfoCommon:
|
||||
tag_build = None
|
||||
tag_date = None
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return safe_name(self.distribution.get_name())
|
||||
|
||||
def tagged_version(self):
|
||||
version = self.distribution.get_version()
|
||||
# egg_info may be called more than once for a distribution,
|
||||
# in which case the version string already contains all tags.
|
||||
if self.vtags and version.endswith(self.vtags):
|
||||
return safe_version(version)
|
||||
return safe_version(version + self.vtags)
|
||||
|
||||
def tags(self):
|
||||
version = ''
|
||||
if self.tag_build:
|
||||
version += self.tag_build
|
||||
if self.tag_date:
|
||||
version += time.strftime("-%Y%m%d")
|
||||
return version
|
||||
vtags = property(tags)
|
||||
|
||||
|
||||
class egg_info(InfoCommon, Command):
|
||||
description = "create a distribution's .egg-info directory"
|
||||
|
||||
user_options = [
|
||||
('egg-base=', 'e', "directory containing .egg-info directories"
|
||||
" (default: top of the source tree)"),
|
||||
('tag-svn-revision', 'r',
|
||||
"Add subversion revision ID to version number"),
|
||||
('tag-date', 'd', "Add date stamp (e.g. 20050528) to version number"),
|
||||
('tag-build=', 'b', "Specify explicit tag to add to version number"),
|
||||
('no-svn-revision', 'R',
|
||||
"Don't add subversion revision ID [default]"),
|
||||
('no-date', 'D', "Don't include date stamp [default]"),
|
||||
]
|
||||
|
||||
boolean_options = ['tag-date', 'tag-svn-revision']
|
||||
negative_opt = {'no-svn-revision': 'tag-svn-revision',
|
||||
'no-date': 'tag-date'}
|
||||
boolean_options = ['tag-date']
|
||||
negative_opt = {
|
||||
'no-date': 'tag-date',
|
||||
}
|
||||
|
||||
def initialize_options(self):
|
||||
self.egg_name = None
|
||||
self.egg_version = None
|
||||
self.egg_base = None
|
||||
self.egg_name = None
|
||||
self.egg_info = None
|
||||
self.tag_build = None
|
||||
self.tag_svn_revision = 0
|
||||
self.tag_date = 0
|
||||
self.egg_version = None
|
||||
self.broken_egg_info = False
|
||||
self.vtags = None
|
||||
|
||||
####################################
|
||||
# allow the 'tag_svn_revision' to be detected and
|
||||
# set, supporting sdists built on older Setuptools.
|
||||
@property
|
||||
def tag_svn_revision(self):
|
||||
pass
|
||||
|
||||
@tag_svn_revision.setter
|
||||
def tag_svn_revision(self, value):
|
||||
pass
|
||||
####################################
|
||||
|
||||
def save_version_info(self, filename):
|
||||
values = dict(
|
||||
egg_info=dict(
|
||||
tag_svn_revision=0,
|
||||
tag_date=0,
|
||||
tag_build=self.tags(),
|
||||
)
|
||||
)
|
||||
edit_config(filename, values)
|
||||
"""
|
||||
Materialize the value of date into the
|
||||
build tag. Install build keys in a deterministic order
|
||||
to avoid arbitrary reordering on subsequent builds.
|
||||
"""
|
||||
egg_info = collections.OrderedDict()
|
||||
# follow the order these keys would have been added
|
||||
# when PYTHONHASHSEED=0
|
||||
egg_info['tag_build'] = self.tags()
|
||||
egg_info['tag_date'] = 0
|
||||
edit_config(filename, dict(egg_info=egg_info))
|
||||
|
||||
def finalize_options(self):
|
||||
self.egg_name = safe_name(self.distribution.get_name())
|
||||
self.vtags = self.tags()
|
||||
# Note: we need to capture the current value returned
|
||||
# by `self.tagged_version()`, so we can later update
|
||||
# `self.distribution.metadata.version` without
|
||||
# repercussions.
|
||||
self.egg_name = self.name
|
||||
self.egg_version = self.tagged_version()
|
||||
|
||||
parsed_version = parse_version(self.egg_version)
|
||||
|
||||
try:
|
||||
|
|
@ -161,16 +279,9 @@ class egg_info(Command):
|
|||
if not self.dry_run:
|
||||
os.unlink(filename)
|
||||
|
||||
def tagged_version(self):
|
||||
version = self.distribution.get_version()
|
||||
# egg_info may be called more than once for a distribution,
|
||||
# in which case the version string already contains all tags.
|
||||
if self.vtags and version.endswith(self.vtags):
|
||||
return safe_version(version)
|
||||
return safe_version(version + self.vtags)
|
||||
|
||||
def run(self):
|
||||
self.mkpath(self.egg_info)
|
||||
os.utime(self.egg_info, None)
|
||||
installer = self.distribution.fetch_build_egg
|
||||
for ep in iter_entry_points('egg_info.writers'):
|
||||
ep.require(installer=installer)
|
||||
|
|
@ -184,22 +295,6 @@ class egg_info(Command):
|
|||
|
||||
self.find_sources()
|
||||
|
||||
def tags(self):
|
||||
version = ''
|
||||
if self.tag_build:
|
||||
version += self.tag_build
|
||||
if self.tag_svn_revision:
|
||||
version += '-r%s' % self.get_svn_revision()
|
||||
if self.tag_date:
|
||||
version += time.strftime("-%Y%m%d")
|
||||
return version
|
||||
|
||||
@staticmethod
|
||||
def get_svn_revision():
|
||||
if 'svn_utils' not in globals():
|
||||
return "0"
|
||||
return str(svn_utils.SvnInfo.load(os.curdir).get_revision())
|
||||
|
||||
def find_sources(self):
|
||||
"""Generate SOURCES.txt manifest file"""
|
||||
manifest_filename = os.path.join(self.egg_info, "SOURCES.txt")
|
||||
|
|
@ -225,7 +320,155 @@ class egg_info(Command):
|
|||
|
||||
|
||||
class FileList(_FileList):
|
||||
"""File list that accepts only existing, platform-independent paths"""
|
||||
# Implementations of the various MANIFEST.in commands
|
||||
|
||||
def process_template_line(self, line):
|
||||
# Parse the line: split it up, make sure the right number of words
|
||||
# is there, and return the relevant words. 'action' is always
|
||||
# defined: it's the first word of the line. Which of the other
|
||||
# three are defined depends on the action; it'll be either
|
||||
# patterns, (dir and patterns), or (dir_pattern).
|
||||
(action, patterns, dir, dir_pattern) = self._parse_template_line(line)
|
||||
|
||||
# OK, now we know that the action is valid and we have the
|
||||
# right number of words on the line for that action -- so we
|
||||
# can proceed with minimal error-checking.
|
||||
if action == 'include':
|
||||
self.debug_print("include " + ' '.join(patterns))
|
||||
for pattern in patterns:
|
||||
if not self.include(pattern):
|
||||
log.warn("warning: no files found matching '%s'", pattern)
|
||||
|
||||
elif action == 'exclude':
|
||||
self.debug_print("exclude " + ' '.join(patterns))
|
||||
for pattern in patterns:
|
||||
if not self.exclude(pattern):
|
||||
log.warn(("warning: no previously-included files "
|
||||
"found matching '%s'"), pattern)
|
||||
|
||||
elif action == 'global-include':
|
||||
self.debug_print("global-include " + ' '.join(patterns))
|
||||
for pattern in patterns:
|
||||
if not self.global_include(pattern):
|
||||
log.warn(("warning: no files found matching '%s' "
|
||||
"anywhere in distribution"), pattern)
|
||||
|
||||
elif action == 'global-exclude':
|
||||
self.debug_print("global-exclude " + ' '.join(patterns))
|
||||
for pattern in patterns:
|
||||
if not self.global_exclude(pattern):
|
||||
log.warn(("warning: no previously-included files matching "
|
||||
"'%s' found anywhere in distribution"),
|
||||
pattern)
|
||||
|
||||
elif action == 'recursive-include':
|
||||
self.debug_print("recursive-include %s %s" %
|
||||
(dir, ' '.join(patterns)))
|
||||
for pattern in patterns:
|
||||
if not self.recursive_include(dir, pattern):
|
||||
log.warn(("warning: no files found matching '%s' "
|
||||
"under directory '%s'"),
|
||||
pattern, dir)
|
||||
|
||||
elif action == 'recursive-exclude':
|
||||
self.debug_print("recursive-exclude %s %s" %
|
||||
(dir, ' '.join(patterns)))
|
||||
for pattern in patterns:
|
||||
if not self.recursive_exclude(dir, pattern):
|
||||
log.warn(("warning: no previously-included files matching "
|
||||
"'%s' found under directory '%s'"),
|
||||
pattern, dir)
|
||||
|
||||
elif action == 'graft':
|
||||
self.debug_print("graft " + dir_pattern)
|
||||
if not self.graft(dir_pattern):
|
||||
log.warn("warning: no directories found matching '%s'",
|
||||
dir_pattern)
|
||||
|
||||
elif action == 'prune':
|
||||
self.debug_print("prune " + dir_pattern)
|
||||
if not self.prune(dir_pattern):
|
||||
log.warn(("no previously-included directories found "
|
||||
"matching '%s'"), dir_pattern)
|
||||
|
||||
else:
|
||||
raise DistutilsInternalError(
|
||||
"this cannot happen: invalid action '%s'" % action)
|
||||
|
||||
def _remove_files(self, predicate):
|
||||
"""
|
||||
Remove all files from the file list that match the predicate.
|
||||
Return True if any matching files were removed
|
||||
"""
|
||||
found = False
|
||||
for i in range(len(self.files) - 1, -1, -1):
|
||||
if predicate(self.files[i]):
|
||||
self.debug_print(" removing " + self.files[i])
|
||||
del self.files[i]
|
||||
found = True
|
||||
return found
|
||||
|
||||
def include(self, pattern):
|
||||
"""Include files that match 'pattern'."""
|
||||
found = [f for f in glob(pattern) if not os.path.isdir(f)]
|
||||
self.extend(found)
|
||||
return bool(found)
|
||||
|
||||
def exclude(self, pattern):
|
||||
"""Exclude files that match 'pattern'."""
|
||||
match = translate_pattern(pattern)
|
||||
return self._remove_files(match.match)
|
||||
|
||||
def recursive_include(self, dir, pattern):
|
||||
"""
|
||||
Include all files anywhere in 'dir/' that match the pattern.
|
||||
"""
|
||||
full_pattern = os.path.join(dir, '**', pattern)
|
||||
found = [f for f in glob(full_pattern, recursive=True)
|
||||
if not os.path.isdir(f)]
|
||||
self.extend(found)
|
||||
return bool(found)
|
||||
|
||||
def recursive_exclude(self, dir, pattern):
|
||||
"""
|
||||
Exclude any file anywhere in 'dir/' that match the pattern.
|
||||
"""
|
||||
match = translate_pattern(os.path.join(dir, '**', pattern))
|
||||
return self._remove_files(match.match)
|
||||
|
||||
def graft(self, dir):
|
||||
"""Include all files from 'dir/'."""
|
||||
found = [
|
||||
item
|
||||
for match_dir in glob(dir)
|
||||
for item in distutils.filelist.findall(match_dir)
|
||||
]
|
||||
self.extend(found)
|
||||
return bool(found)
|
||||
|
||||
def prune(self, dir):
|
||||
"""Filter out files from 'dir/'."""
|
||||
match = translate_pattern(os.path.join(dir, '**'))
|
||||
return self._remove_files(match.match)
|
||||
|
||||
def global_include(self, pattern):
|
||||
"""
|
||||
Include all files anywhere in the current directory that match the
|
||||
pattern. This is very inefficient on large file trees.
|
||||
"""
|
||||
if self.allfiles is None:
|
||||
self.findall()
|
||||
match = translate_pattern(os.path.join('**', pattern))
|
||||
found = [f for f in self.allfiles if match.match(f)]
|
||||
self.extend(found)
|
||||
return bool(found)
|
||||
|
||||
def global_exclude(self, pattern):
|
||||
"""
|
||||
Exclude all files anywhere that match the pattern.
|
||||
"""
|
||||
match = translate_pattern(os.path.join('**', pattern))
|
||||
return self._remove_files(match.match)
|
||||
|
||||
def append(self, item):
|
||||
if item.endswith('\r'): # Fix older sdists built on Windows
|
||||
|
|
@ -288,7 +531,6 @@ class manifest_maker(sdist):
|
|||
self.filelist = FileList()
|
||||
if not os.path.exists(self.manifest):
|
||||
self.write_manifest() # it must exist so it'll get in the list
|
||||
self.filelist.findall()
|
||||
self.add_defaults()
|
||||
if os.path.exists(self.template):
|
||||
self.read_template()
|
||||
|
|
@ -313,10 +555,17 @@ class manifest_maker(sdist):
|
|||
msg = "writing manifest file '%s'" % self.manifest
|
||||
self.execute(write_file, (self.manifest, files), msg)
|
||||
|
||||
def warn(self, msg): # suppress missing-file warnings from sdist
|
||||
if not msg.startswith("standard file not found:"):
|
||||
def warn(self, msg):
|
||||
if not self._should_suppress_warning(msg):
|
||||
sdist.warn(self, msg)
|
||||
|
||||
@staticmethod
|
||||
def _should_suppress_warning(msg):
|
||||
"""
|
||||
suppress missing-file warnings from sdist
|
||||
"""
|
||||
return re.match(r"standard file .*not found", msg)
|
||||
|
||||
def add_defaults(self):
|
||||
sdist.add_defaults(self)
|
||||
self.filelist.append(self.template)
|
||||
|
|
@ -326,39 +575,20 @@ class manifest_maker(sdist):
|
|||
self.filelist.extend(rcfiles)
|
||||
elif os.path.exists(self.manifest):
|
||||
self.read_manifest()
|
||||
|
||||
if os.path.exists("setup.py"):
|
||||
# setup.py should be included by default, even if it's not
|
||||
# the script called to create the sdist
|
||||
self.filelist.append("setup.py")
|
||||
|
||||
ei_cmd = self.get_finalized_command('egg_info')
|
||||
self._add_egg_info(cmd=ei_cmd)
|
||||
self.filelist.include_pattern("*", prefix=ei_cmd.egg_info)
|
||||
|
||||
def _add_egg_info(self, cmd):
|
||||
"""
|
||||
Add paths for egg-info files for an external egg-base.
|
||||
|
||||
The egg-info files are written to egg-base. If egg-base is
|
||||
outside the current working directory, this method
|
||||
searchs the egg-base directory for files to include
|
||||
in the manifest. Uses distutils.filelist.findall (which is
|
||||
really the version monkeypatched in by setuptools/__init__.py)
|
||||
to perform the search.
|
||||
|
||||
Since findall records relative paths, prefix the returned
|
||||
paths with cmd.egg_base, so add_default's include_pattern call
|
||||
(which is looking for the absolute cmd.egg_info) will match
|
||||
them.
|
||||
"""
|
||||
if cmd.egg_base == os.curdir:
|
||||
# egg-info files were already added by something else
|
||||
return
|
||||
|
||||
discovered = distutils.filelist.findall(cmd.egg_base)
|
||||
resolved = (os.path.join(cmd.egg_base, path) for path in discovered)
|
||||
self.filelist.allfiles.extend(resolved)
|
||||
self.filelist.graft(ei_cmd.egg_info)
|
||||
|
||||
def prune_file_list(self):
|
||||
build = self.get_finalized_command('build')
|
||||
base_dir = self.distribution.get_fullname()
|
||||
self.filelist.exclude_pattern(None, prefix=build.build_base)
|
||||
self.filelist.exclude_pattern(None, prefix=base_dir)
|
||||
self.filelist.prune(build.build_base)
|
||||
self.filelist.prune(base_dir)
|
||||
sep = re.escape(os.sep)
|
||||
self.filelist.exclude_pattern(r'(^|' + sep + r')(RCS|CVS|\.svn)' + sep,
|
||||
is_regex=1)
|
||||
|
|
@ -383,6 +613,7 @@ def write_pkg_info(cmd, basename, filename):
|
|||
metadata = cmd.distribution.metadata
|
||||
metadata.version, oldver = cmd.egg_version, metadata.version
|
||||
metadata.name, oldname = cmd.egg_name, metadata.name
|
||||
|
||||
try:
|
||||
# write unescaped data to PKG-INFO, so older pkg_resources
|
||||
# can still parse it
|
||||
|
|
@ -422,7 +653,7 @@ def write_requirements(cmd, basename, filename):
|
|||
|
||||
|
||||
def write_setup_requirements(cmd, basename, filename):
|
||||
data = StringIO()
|
||||
data = io.StringIO()
|
||||
_write_requirements(data, cmd.distribution.setup_requires)
|
||||
cmd.write_or_delete_file("setup-requirements", filename, data.getvalue())
|
||||
|
||||
|
|
@ -471,7 +702,7 @@ def get_pkg_info_revision():
|
|||
Get a -r### off of PKG-INFO Version in case this is an sdist of
|
||||
a subversion revision.
|
||||
"""
|
||||
warnings.warn("get_pkg_info_revision is deprecated.", DeprecationWarning)
|
||||
warnings.warn("get_pkg_info_revision is deprecated.", EggInfoDeprecationWarning)
|
||||
if os.path.exists('PKG-INFO'):
|
||||
with io.open('PKG-INFO') as f:
|
||||
for line in f:
|
||||
|
|
@ -479,3 +710,7 @@ def get_pkg_info_revision():
|
|||
if match:
|
||||
return int(match.group(1))
|
||||
return 0
|
||||
|
||||
|
||||
class EggInfoDeprecationWarning(SetuptoolsDeprecationWarning):
|
||||
"""Class for warning about deprecations in eggInfo in setupTools. Not ignored by default, unlike DeprecationWarning."""
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import distutils.command.install as orig
|
|||
import setuptools
|
||||
|
||||
# Prior to numpy 1.9, NumPy relies on the '_install' name, so provide it for
|
||||
# now. See https://bitbucket.org/pypa/setuptools/issue/199/
|
||||
# now. See https://github.com/pypa/setuptools/issues/199/
|
||||
_install = orig.install
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@ from distutils import log, dir_util
|
|||
import os
|
||||
|
||||
from setuptools import Command
|
||||
from setuptools import namespaces
|
||||
from setuptools.archive_util import unpack_archive
|
||||
import pkg_resources
|
||||
|
||||
|
||||
class install_egg_info(Command):
|
||||
class install_egg_info(namespaces.Installer, Command):
|
||||
"""Install an .egg-info directory for the package"""
|
||||
|
||||
description = "Install an .egg-info directory for the package"
|
||||
|
|
@ -27,7 +28,7 @@ class install_egg_info(Command):
|
|||
).egg_name() + '.egg-info'
|
||||
self.source = ei_cmd.egg_info
|
||||
self.target = os.path.join(self.install_dir, basename)
|
||||
self.outputs = [self.target]
|
||||
self.outputs = []
|
||||
|
||||
def run(self):
|
||||
self.run_command('egg_info')
|
||||
|
|
@ -59,58 +60,3 @@ class install_egg_info(Command):
|
|||
return dst
|
||||
|
||||
unpack_archive(self.source, self.target, skimmer)
|
||||
|
||||
def install_namespaces(self):
|
||||
nsp = self._get_all_ns_packages()
|
||||
if not nsp:
|
||||
return
|
||||
filename, ext = os.path.splitext(self.target)
|
||||
filename += '-nspkg.pth'
|
||||
self.outputs.append(filename)
|
||||
log.info("Installing %s", filename)
|
||||
lines = map(self._gen_nspkg_line, nsp)
|
||||
|
||||
if self.dry_run:
|
||||
# always generate the lines, even in dry run
|
||||
list(lines)
|
||||
return
|
||||
|
||||
with open(filename, 'wt') as f:
|
||||
f.writelines(lines)
|
||||
|
||||
_nspkg_tmpl = (
|
||||
"import sys, types, os",
|
||||
"p = os.path.join(sys._getframe(1).f_locals['sitedir'], *%(pth)r)",
|
||||
"ie = os.path.exists(os.path.join(p,'__init__.py'))",
|
||||
"m = not ie and "
|
||||
"sys.modules.setdefault(%(pkg)r, types.ModuleType(%(pkg)r))",
|
||||
"mp = (m or []) and m.__dict__.setdefault('__path__',[])",
|
||||
"(p not in mp) and mp.append(p)",
|
||||
)
|
||||
"lines for the namespace installer"
|
||||
|
||||
_nspkg_tmpl_multi = (
|
||||
'm and setattr(sys.modules[%(parent)r], %(child)r, m)',
|
||||
)
|
||||
"additional line(s) when a parent package is indicated"
|
||||
|
||||
@classmethod
|
||||
def _gen_nspkg_line(cls, pkg):
|
||||
# ensure pkg is not a unicode string under Python 2.7
|
||||
pkg = str(pkg)
|
||||
pth = tuple(pkg.split('.'))
|
||||
tmpl_lines = cls._nspkg_tmpl
|
||||
parent, sep, child = pkg.rpartition('.')
|
||||
if parent:
|
||||
tmpl_lines += cls._nspkg_tmpl_multi
|
||||
return ';'.join(tmpl_lines) % locals() + '\n'
|
||||
|
||||
def _get_all_ns_packages(self):
|
||||
"""Return sorted list of all package namespaces"""
|
||||
nsp = set()
|
||||
for pkg in self.distribution.namespace_packages or []:
|
||||
pkg = pkg.split('.')
|
||||
while pkg:
|
||||
nsp.add('.'.join(pkg))
|
||||
pkg.pop()
|
||||
return sorted(nsp)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import imp
|
|||
from itertools import product, starmap
|
||||
import distutils.command.install_lib as orig
|
||||
|
||||
|
||||
class install_lib(orig.install_lib):
|
||||
"""Don't add compiled flags to filenames of non-Python files"""
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from distutils import log
|
||||
import distutils.command.install_scripts as orig
|
||||
import os
|
||||
import sys
|
||||
|
||||
from pkg_resources import Distribution, PathMetadata, ensure_directory
|
||||
|
||||
|
|
@ -37,6 +38,10 @@ class install_scripts(orig.install_scripts):
|
|||
if is_wininst:
|
||||
exec_param = "python.exe"
|
||||
writer = ei.WindowsScriptWriter
|
||||
if exec_param == sys.executable:
|
||||
# In case the path to the Python executable contains a space, wrap
|
||||
# it so it's not split up.
|
||||
exec_param = [exec_param]
|
||||
# resolve the writer to the environment
|
||||
writer = writer.best()
|
||||
cmd = writer.command_spec_class.best().from_param(exec_param)
|
||||
|
|
|
|||
136
Lib/site-packages/setuptools/command/py36compat.py
Normal file
136
Lib/site-packages/setuptools/command/py36compat.py
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
import os
|
||||
from glob import glob
|
||||
from distutils.util import convert_path
|
||||
from distutils.command import sdist
|
||||
|
||||
from setuptools.extern.six.moves import filter
|
||||
|
||||
|
||||
class sdist_add_defaults:
|
||||
"""
|
||||
Mix-in providing forward-compatibility for functionality as found in
|
||||
distutils on Python 3.7.
|
||||
|
||||
Do not edit the code in this class except to update functionality
|
||||
as implemented in distutils. Instead, override in the subclass.
|
||||
"""
|
||||
|
||||
def add_defaults(self):
|
||||
"""Add all the default files to self.filelist:
|
||||
- README or README.txt
|
||||
- setup.py
|
||||
- test/test*.py
|
||||
- all pure Python modules mentioned in setup script
|
||||
- all files pointed by package_data (build_py)
|
||||
- all files defined in data_files.
|
||||
- all files defined as scripts.
|
||||
- all C sources listed as part of extensions or C libraries
|
||||
in the setup script (doesn't catch C headers!)
|
||||
Warns if (README or README.txt) or setup.py are missing; everything
|
||||
else is optional.
|
||||
"""
|
||||
self._add_defaults_standards()
|
||||
self._add_defaults_optional()
|
||||
self._add_defaults_python()
|
||||
self._add_defaults_data_files()
|
||||
self._add_defaults_ext()
|
||||
self._add_defaults_c_libs()
|
||||
self._add_defaults_scripts()
|
||||
|
||||
@staticmethod
|
||||
def _cs_path_exists(fspath):
|
||||
"""
|
||||
Case-sensitive path existence check
|
||||
|
||||
>>> sdist_add_defaults._cs_path_exists(__file__)
|
||||
True
|
||||
>>> sdist_add_defaults._cs_path_exists(__file__.upper())
|
||||
False
|
||||
"""
|
||||
if not os.path.exists(fspath):
|
||||
return False
|
||||
# make absolute so we always have a directory
|
||||
abspath = os.path.abspath(fspath)
|
||||
directory, filename = os.path.split(abspath)
|
||||
return filename in os.listdir(directory)
|
||||
|
||||
def _add_defaults_standards(self):
|
||||
standards = [self.READMES, self.distribution.script_name]
|
||||
for fn in standards:
|
||||
if isinstance(fn, tuple):
|
||||
alts = fn
|
||||
got_it = False
|
||||
for fn in alts:
|
||||
if self._cs_path_exists(fn):
|
||||
got_it = True
|
||||
self.filelist.append(fn)
|
||||
break
|
||||
|
||||
if not got_it:
|
||||
self.warn("standard file not found: should have one of " +
|
||||
', '.join(alts))
|
||||
else:
|
||||
if self._cs_path_exists(fn):
|
||||
self.filelist.append(fn)
|
||||
else:
|
||||
self.warn("standard file '%s' not found" % fn)
|
||||
|
||||
def _add_defaults_optional(self):
|
||||
optional = ['test/test*.py', 'setup.cfg']
|
||||
for pattern in optional:
|
||||
files = filter(os.path.isfile, glob(pattern))
|
||||
self.filelist.extend(files)
|
||||
|
||||
def _add_defaults_python(self):
|
||||
# build_py is used to get:
|
||||
# - python modules
|
||||
# - files defined in package_data
|
||||
build_py = self.get_finalized_command('build_py')
|
||||
|
||||
# getting python files
|
||||
if self.distribution.has_pure_modules():
|
||||
self.filelist.extend(build_py.get_source_files())
|
||||
|
||||
# getting package_data files
|
||||
# (computed in build_py.data_files by build_py.finalize_options)
|
||||
for pkg, src_dir, build_dir, filenames in build_py.data_files:
|
||||
for filename in filenames:
|
||||
self.filelist.append(os.path.join(src_dir, filename))
|
||||
|
||||
def _add_defaults_data_files(self):
|
||||
# getting distribution.data_files
|
||||
if self.distribution.has_data_files():
|
||||
for item in self.distribution.data_files:
|
||||
if isinstance(item, str):
|
||||
# plain file
|
||||
item = convert_path(item)
|
||||
if os.path.isfile(item):
|
||||
self.filelist.append(item)
|
||||
else:
|
||||
# a (dirname, filenames) tuple
|
||||
dirname, filenames = item
|
||||
for f in filenames:
|
||||
f = convert_path(f)
|
||||
if os.path.isfile(f):
|
||||
self.filelist.append(f)
|
||||
|
||||
def _add_defaults_ext(self):
|
||||
if self.distribution.has_ext_modules():
|
||||
build_ext = self.get_finalized_command('build_ext')
|
||||
self.filelist.extend(build_ext.get_source_files())
|
||||
|
||||
def _add_defaults_c_libs(self):
|
||||
if self.distribution.has_c_libraries():
|
||||
build_clib = self.get_finalized_command('build_clib')
|
||||
self.filelist.extend(build_clib.get_source_files())
|
||||
|
||||
def _add_defaults_scripts(self):
|
||||
if self.distribution.has_scripts():
|
||||
build_scripts = self.get_finalized_command('build_scripts')
|
||||
self.filelist.extend(build_scripts.get_source_files())
|
||||
|
||||
|
||||
if hasattr(sdist.sdist, '_add_defaults_standards'):
|
||||
# disable the functionality already available upstream
|
||||
class sdist_add_defaults:
|
||||
pass
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
from distutils import log
|
||||
import distutils.command.register as orig
|
||||
|
||||
|
||||
|
|
@ -5,6 +6,13 @@ class register(orig.register):
|
|||
__doc__ = orig.register.__doc__
|
||||
|
||||
def run(self):
|
||||
# Make sure that we are using valid current name/version info
|
||||
self.run_command('egg_info')
|
||||
orig.register.run(self)
|
||||
try:
|
||||
# Make sure that we are using valid current name/version info
|
||||
self.run_command('egg_info')
|
||||
orig.register.run(self)
|
||||
finally:
|
||||
self.announce(
|
||||
"WARNING: Registering is deprecated, use twine to "
|
||||
"upload instead (https://pypi.org/p/twine/)",
|
||||
log.WARN
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ from distutils.util import convert_path
|
|||
from distutils import log
|
||||
from distutils.errors import DistutilsOptionError
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from setuptools.extern import six
|
||||
|
||||
|
|
@ -59,4 +60,7 @@ class rotate(Command):
|
|||
for (t, f) in files:
|
||||
log.info("Deleting %s", f)
|
||||
if not self.dry_run:
|
||||
os.unlink(f)
|
||||
if os.path.isdir(f):
|
||||
shutil.rmtree(f)
|
||||
else:
|
||||
os.unlink(f)
|
||||
|
|
|
|||
|
|
@ -1,20 +1,19 @@
|
|||
from glob import glob
|
||||
from distutils import log
|
||||
import distutils.command.sdist as orig
|
||||
import os
|
||||
import sys
|
||||
import io
|
||||
import contextlib
|
||||
|
||||
from setuptools.extern import six
|
||||
|
||||
from setuptools.utils import cs_path_exists
|
||||
from .py36compat import sdist_add_defaults
|
||||
|
||||
import pkg_resources
|
||||
|
||||
READMES = 'README', 'README.rst', 'README.txt'
|
||||
|
||||
_default_revctrl = list
|
||||
|
||||
|
||||
def walk_revctrl(dirname=''):
|
||||
"""Find all files under revision control"""
|
||||
for ep in pkg_resources.iter_entry_points('setuptools.file_finders'):
|
||||
|
|
@ -22,7 +21,7 @@ def walk_revctrl(dirname=''):
|
|||
yield item
|
||||
|
||||
|
||||
class sdist(orig.sdist):
|
||||
class sdist(sdist_add_defaults, orig.sdist):
|
||||
"""Smart sdist that finds anything supported by revision control"""
|
||||
|
||||
user_options = [
|
||||
|
|
@ -38,6 +37,9 @@ class sdist(orig.sdist):
|
|||
|
||||
negative_opt = {}
|
||||
|
||||
README_EXTENSIONS = ['', '.rst', '.txt', '.md']
|
||||
READMES = tuple('README{0}'.format(ext) for ext in README_EXTENSIONS)
|
||||
|
||||
def run(self):
|
||||
self.run_command('egg_info')
|
||||
ei_cmd = self.get_finalized_command('egg_info')
|
||||
|
|
@ -49,13 +51,6 @@ class sdist(orig.sdist):
|
|||
for cmd_name in self.get_sub_commands():
|
||||
self.run_command(cmd_name)
|
||||
|
||||
# Call check_metadata only if no 'check' command
|
||||
# (distutils <= 2.6)
|
||||
import distutils.command
|
||||
|
||||
if 'check' not in distutils.command.__all__:
|
||||
self.check_metadata()
|
||||
|
||||
self.make_distribution()
|
||||
|
||||
dist_files = getattr(self.distribution, 'dist_files', [])
|
||||
|
|
@ -64,6 +59,45 @@ class sdist(orig.sdist):
|
|||
if data not in dist_files:
|
||||
dist_files.append(data)
|
||||
|
||||
def initialize_options(self):
|
||||
orig.sdist.initialize_options(self)
|
||||
|
||||
self._default_to_gztar()
|
||||
|
||||
def _default_to_gztar(self):
|
||||
# only needed on Python prior to 3.6.
|
||||
if sys.version_info >= (3, 6, 0, 'beta', 1):
|
||||
return
|
||||
self.formats = ['gztar']
|
||||
|
||||
def make_distribution(self):
|
||||
"""
|
||||
Workaround for #516
|
||||
"""
|
||||
with self._remove_os_link():
|
||||
orig.sdist.make_distribution(self)
|
||||
|
||||
@staticmethod
|
||||
@contextlib.contextmanager
|
||||
def _remove_os_link():
|
||||
"""
|
||||
In a context, remove and restore os.link if it exists
|
||||
"""
|
||||
|
||||
class NoValue:
|
||||
pass
|
||||
|
||||
orig_val = getattr(os, 'link', NoValue)
|
||||
try:
|
||||
del os.link
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
if orig_val is not NoValue:
|
||||
setattr(os, 'link', orig_val)
|
||||
|
||||
def __read_template_hack(self):
|
||||
# This grody hack closes the template file (MANIFEST.in) if an
|
||||
# exception occurs during read_template.
|
||||
|
|
@ -71,7 +105,7 @@ class sdist(orig.sdist):
|
|||
# file.
|
||||
try:
|
||||
orig.sdist.read_template(self)
|
||||
except:
|
||||
except Exception:
|
||||
_, _, tb = sys.exc_info()
|
||||
tb.tb_next.tb_frame.f_locals['template'].close()
|
||||
raise
|
||||
|
|
@ -87,35 +121,8 @@ class sdist(orig.sdist):
|
|||
if has_leaky_handle:
|
||||
read_template = __read_template_hack
|
||||
|
||||
def add_defaults(self):
|
||||
standards = [READMES,
|
||||
self.distribution.script_name]
|
||||
for fn in standards:
|
||||
if isinstance(fn, tuple):
|
||||
alts = fn
|
||||
got_it = 0
|
||||
for fn in alts:
|
||||
if cs_path_exists(fn):
|
||||
got_it = 1
|
||||
self.filelist.append(fn)
|
||||
break
|
||||
|
||||
if not got_it:
|
||||
self.warn("standard file not found: should have one of " +
|
||||
', '.join(alts))
|
||||
else:
|
||||
if cs_path_exists(fn):
|
||||
self.filelist.append(fn)
|
||||
else:
|
||||
self.warn("standard file '%s' not found" % fn)
|
||||
|
||||
optional = ['test/test*.py', 'setup.cfg']
|
||||
for pattern in optional:
|
||||
files = list(filter(cs_path_exists, glob(pattern)))
|
||||
if files:
|
||||
self.filelist.extend(files)
|
||||
|
||||
# getting python files
|
||||
def _add_defaults_python(self):
|
||||
"""getting python files"""
|
||||
if self.distribution.has_pure_modules():
|
||||
build_py = self.get_finalized_command('build_py')
|
||||
self.filelist.extend(build_py.get_source_files())
|
||||
|
|
@ -128,26 +135,23 @@ class sdist(orig.sdist):
|
|||
self.filelist.extend([os.path.join(src_dir, filename)
|
||||
for filename in filenames])
|
||||
|
||||
if self.distribution.has_ext_modules():
|
||||
build_ext = self.get_finalized_command('build_ext')
|
||||
self.filelist.extend(build_ext.get_source_files())
|
||||
|
||||
if self.distribution.has_c_libraries():
|
||||
build_clib = self.get_finalized_command('build_clib')
|
||||
self.filelist.extend(build_clib.get_source_files())
|
||||
|
||||
if self.distribution.has_scripts():
|
||||
build_scripts = self.get_finalized_command('build_scripts')
|
||||
self.filelist.extend(build_scripts.get_source_files())
|
||||
def _add_defaults_data_files(self):
|
||||
try:
|
||||
if six.PY2:
|
||||
sdist_add_defaults._add_defaults_data_files(self)
|
||||
else:
|
||||
super()._add_defaults_data_files()
|
||||
except TypeError:
|
||||
log.warn("data_files contains unexpected objects")
|
||||
|
||||
def check_readme(self):
|
||||
for f in READMES:
|
||||
for f in self.READMES:
|
||||
if os.path.exists(f):
|
||||
return
|
||||
else:
|
||||
self.warn(
|
||||
"standard file not found: should have one of " +
|
||||
', '.join(READMES)
|
||||
', '.join(self.READMES)
|
||||
)
|
||||
|
||||
def make_release_tree(self, base_dir, files):
|
||||
|
|
@ -179,7 +183,7 @@ class sdist(orig.sdist):
|
|||
distribution.
|
||||
"""
|
||||
log.info("reading manifest file '%s'", self.manifest)
|
||||
manifest = open(self.manifest, 'rbU')
|
||||
manifest = open(self.manifest, 'rb')
|
||||
for line in manifest:
|
||||
# The manifest must contain UTF-8. See #303.
|
||||
if six.PY3:
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ from setuptools.extern.six.moves import configparser
|
|||
|
||||
from setuptools import Command
|
||||
|
||||
|
||||
__all__ = ['config_file', 'edit_config', 'option_base', 'setopt']
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,30 @@
|
|||
from distutils.errors import DistutilsOptionError
|
||||
from unittest import TestLoader
|
||||
import os
|
||||
import operator
|
||||
import sys
|
||||
import contextlib
|
||||
import itertools
|
||||
import unittest
|
||||
from distutils.errors import DistutilsError, DistutilsOptionError
|
||||
from distutils import log
|
||||
from unittest import TestLoader
|
||||
|
||||
from setuptools.extern import six
|
||||
from setuptools.extern.six.moves import map, filter
|
||||
|
||||
from pkg_resources import (resource_listdir, resource_exists, normalize_path,
|
||||
working_set, _namespace_packages,
|
||||
working_set, _namespace_packages, evaluate_marker,
|
||||
add_activation_listener, require, EntryPoint)
|
||||
from setuptools import Command
|
||||
from setuptools.py31compat import unittest_main
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
class ScanningLoader(TestLoader):
|
||||
|
||||
def __init__(self):
|
||||
TestLoader.__init__(self)
|
||||
self._visited = set()
|
||||
|
||||
def loadTestsFromModule(self, module, pattern=None):
|
||||
"""Return a suite of all tests cases contained in the given module
|
||||
|
||||
|
|
@ -19,6 +32,10 @@ class ScanningLoader(TestLoader):
|
|||
If the module has an ``additional_tests`` function, call it and add
|
||||
the return value to the tests.
|
||||
"""
|
||||
if module in self._visited:
|
||||
return None
|
||||
self._visited.add(module)
|
||||
|
||||
tests = []
|
||||
tests.append(TestLoader.loadTestsFromModule(self, module))
|
||||
|
||||
|
|
@ -43,7 +60,7 @@ class ScanningLoader(TestLoader):
|
|||
|
||||
|
||||
# adapted from jaraco.classes.properties:NonDataProperty
|
||||
class NonDataProperty(object):
|
||||
class NonDataProperty:
|
||||
def __init__(self, fget):
|
||||
self.fget = fget
|
||||
|
||||
|
|
@ -61,7 +78,7 @@ class test(Command):
|
|||
user_options = [
|
||||
('test-module=', 'm', "Run 'test_suite' in specified module"),
|
||||
('test-suite=', 's',
|
||||
"Test suite to run (e.g. 'some_module.test_suite')"),
|
||||
"Run single test, case or suite (e.g. 'module.test_suite')"),
|
||||
('test-runner=', 'r', "Test runner to use"),
|
||||
]
|
||||
|
||||
|
|
@ -95,12 +112,22 @@ class test(Command):
|
|||
return list(self._test_args())
|
||||
|
||||
def _test_args(self):
|
||||
if not self.test_suite and sys.version_info >= (2, 7):
|
||||
yield 'discover'
|
||||
if self.verbose:
|
||||
yield '--verbose'
|
||||
if self.test_suite:
|
||||
yield self.test_suite
|
||||
|
||||
def with_project_on_sys_path(self, func):
|
||||
"""
|
||||
Backward compatibility for project_on_sys_path context.
|
||||
"""
|
||||
with self.project_on_sys_path():
|
||||
func()
|
||||
|
||||
@contextlib.contextmanager
|
||||
def project_on_sys_path(self, include_dists=[]):
|
||||
with_2to3 = six.PY3 and getattr(self.distribution, 'use_2to3', False)
|
||||
|
||||
if with_2to3:
|
||||
|
|
@ -132,30 +159,73 @@ class test(Command):
|
|||
old_modules = sys.modules.copy()
|
||||
|
||||
try:
|
||||
sys.path.insert(0, normalize_path(ei_cmd.egg_base))
|
||||
project_path = normalize_path(ei_cmd.egg_base)
|
||||
sys.path.insert(0, project_path)
|
||||
working_set.__init__()
|
||||
add_activation_listener(lambda dist: dist.activate())
|
||||
require('%s==%s' % (ei_cmd.egg_name, ei_cmd.egg_version))
|
||||
func()
|
||||
with self.paths_on_pythonpath([project_path]):
|
||||
yield
|
||||
finally:
|
||||
sys.path[:] = old_path
|
||||
sys.modules.clear()
|
||||
sys.modules.update(old_modules)
|
||||
working_set.__init__()
|
||||
|
||||
@staticmethod
|
||||
@contextlib.contextmanager
|
||||
def paths_on_pythonpath(paths):
|
||||
"""
|
||||
Add the indicated paths to the head of the PYTHONPATH environment
|
||||
variable so that subprocesses will also see the packages at
|
||||
these paths.
|
||||
|
||||
Do this in a context that restores the value on exit.
|
||||
"""
|
||||
nothing = object()
|
||||
orig_pythonpath = os.environ.get('PYTHONPATH', nothing)
|
||||
current_pythonpath = os.environ.get('PYTHONPATH', '')
|
||||
try:
|
||||
prefix = os.pathsep.join(paths)
|
||||
to_join = filter(None, [prefix, current_pythonpath])
|
||||
new_path = os.pathsep.join(to_join)
|
||||
if new_path:
|
||||
os.environ['PYTHONPATH'] = new_path
|
||||
yield
|
||||
finally:
|
||||
if orig_pythonpath is nothing:
|
||||
os.environ.pop('PYTHONPATH', None)
|
||||
else:
|
||||
os.environ['PYTHONPATH'] = orig_pythonpath
|
||||
|
||||
@staticmethod
|
||||
def install_dists(dist):
|
||||
"""
|
||||
Install the requirements indicated by self.distribution and
|
||||
return an iterable of the dists that were built.
|
||||
"""
|
||||
ir_d = dist.fetch_build_eggs(dist.install_requires)
|
||||
tr_d = dist.fetch_build_eggs(dist.tests_require or [])
|
||||
er_d = dist.fetch_build_eggs(
|
||||
v for k, v in dist.extras_require.items()
|
||||
if k.startswith(':') and evaluate_marker(k[1:])
|
||||
)
|
||||
return itertools.chain(ir_d, tr_d, er_d)
|
||||
|
||||
def run(self):
|
||||
if self.distribution.install_requires:
|
||||
self.distribution.fetch_build_eggs(
|
||||
self.distribution.install_requires)
|
||||
if self.distribution.tests_require:
|
||||
self.distribution.fetch_build_eggs(self.distribution.tests_require)
|
||||
installed_dists = self.install_dists(self.distribution)
|
||||
|
||||
cmd = ' '.join(self._argv)
|
||||
if self.dry_run:
|
||||
self.announce('skipping "%s" (dry run)' % cmd)
|
||||
else:
|
||||
self.announce('running "%s"' % cmd)
|
||||
self.with_project_on_sys_path(self.run_tests)
|
||||
return
|
||||
|
||||
self.announce('running "%s"' % cmd)
|
||||
|
||||
paths = map(operator.attrgetter('location'), installed_dists)
|
||||
with self.paths_on_pythonpath(paths):
|
||||
with self.project_on_sys_path():
|
||||
self.run_tests()
|
||||
|
||||
def run_tests(self):
|
||||
# Purge modules under test from sys.modules. The test loader will
|
||||
|
|
@ -173,11 +243,16 @@ class test(Command):
|
|||
del_modules.append(name)
|
||||
list(map(sys.modules.__delitem__, del_modules))
|
||||
|
||||
unittest_main(
|
||||
test = unittest.main(
|
||||
None, None, self._argv,
|
||||
testLoader=self._resolve_as_ep(self.test_loader),
|
||||
testRunner=self._resolve_as_ep(self.test_runner),
|
||||
exit=False,
|
||||
)
|
||||
if not test.result.wasSuccessful():
|
||||
msg = 'Test failed: %s' % test.result
|
||||
self.announce(msg, log.ERROR)
|
||||
raise DistutilsError(msg)
|
||||
|
||||
@property
|
||||
def _argv(self):
|
||||
|
|
|
|||
196
Lib/site-packages/setuptools/command/upload.py
Normal file
196
Lib/site-packages/setuptools/command/upload.py
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
import io
|
||||
import os
|
||||
import hashlib
|
||||
import getpass
|
||||
import platform
|
||||
|
||||
from base64 import standard_b64encode
|
||||
|
||||
from distutils import log
|
||||
from distutils.command import upload as orig
|
||||
from distutils.spawn import spawn
|
||||
|
||||
from distutils.errors import DistutilsError
|
||||
|
||||
from setuptools.extern.six.moves.urllib.request import urlopen, Request
|
||||
from setuptools.extern.six.moves.urllib.error import HTTPError
|
||||
from setuptools.extern.six.moves.urllib.parse import urlparse
|
||||
|
||||
class upload(orig.upload):
|
||||
"""
|
||||
Override default upload behavior to obtain password
|
||||
in a variety of different ways.
|
||||
"""
|
||||
def run(self):
|
||||
try:
|
||||
orig.upload.run(self)
|
||||
finally:
|
||||
self.announce(
|
||||
"WARNING: Uploading via this command is deprecated, use twine "
|
||||
"to upload instead (https://pypi.org/p/twine/)",
|
||||
log.WARN
|
||||
)
|
||||
|
||||
def finalize_options(self):
|
||||
orig.upload.finalize_options(self)
|
||||
self.username = (
|
||||
self.username or
|
||||
getpass.getuser()
|
||||
)
|
||||
# Attempt to obtain password. Short circuit evaluation at the first
|
||||
# sign of success.
|
||||
self.password = (
|
||||
self.password or
|
||||
self._load_password_from_keyring() or
|
||||
self._prompt_for_password()
|
||||
)
|
||||
|
||||
def upload_file(self, command, pyversion, filename):
|
||||
# Makes sure the repository URL is compliant
|
||||
schema, netloc, url, params, query, fragments = \
|
||||
urlparse(self.repository)
|
||||
if params or query or fragments:
|
||||
raise AssertionError("Incompatible url %s" % self.repository)
|
||||
|
||||
if schema not in ('http', 'https'):
|
||||
raise AssertionError("unsupported schema " + schema)
|
||||
|
||||
# Sign if requested
|
||||
if self.sign:
|
||||
gpg_args = ["gpg", "--detach-sign", "-a", filename]
|
||||
if self.identity:
|
||||
gpg_args[2:2] = ["--local-user", self.identity]
|
||||
spawn(gpg_args,
|
||||
dry_run=self.dry_run)
|
||||
|
||||
# Fill in the data - send all the meta-data in case we need to
|
||||
# register a new release
|
||||
with open(filename, 'rb') as f:
|
||||
content = f.read()
|
||||
|
||||
meta = self.distribution.metadata
|
||||
|
||||
data = {
|
||||
# action
|
||||
':action': 'file_upload',
|
||||
'protocol_version': '1',
|
||||
|
||||
# identify release
|
||||
'name': meta.get_name(),
|
||||
'version': meta.get_version(),
|
||||
|
||||
# file content
|
||||
'content': (os.path.basename(filename),content),
|
||||
'filetype': command,
|
||||
'pyversion': pyversion,
|
||||
'md5_digest': hashlib.md5(content).hexdigest(),
|
||||
|
||||
# additional meta-data
|
||||
'metadata_version': str(meta.get_metadata_version()),
|
||||
'summary': meta.get_description(),
|
||||
'home_page': meta.get_url(),
|
||||
'author': meta.get_contact(),
|
||||
'author_email': meta.get_contact_email(),
|
||||
'license': meta.get_licence(),
|
||||
'description': meta.get_long_description(),
|
||||
'keywords': meta.get_keywords(),
|
||||
'platform': meta.get_platforms(),
|
||||
'classifiers': meta.get_classifiers(),
|
||||
'download_url': meta.get_download_url(),
|
||||
# PEP 314
|
||||
'provides': meta.get_provides(),
|
||||
'requires': meta.get_requires(),
|
||||
'obsoletes': meta.get_obsoletes(),
|
||||
}
|
||||
|
||||
data['comment'] = ''
|
||||
|
||||
if self.sign:
|
||||
data['gpg_signature'] = (os.path.basename(filename) + ".asc",
|
||||
open(filename+".asc", "rb").read())
|
||||
|
||||
# set up the authentication
|
||||
user_pass = (self.username + ":" + self.password).encode('ascii')
|
||||
# The exact encoding of the authentication string is debated.
|
||||
# Anyway PyPI only accepts ascii for both username or password.
|
||||
auth = "Basic " + standard_b64encode(user_pass).decode('ascii')
|
||||
|
||||
# Build up the MIME payload for the POST data
|
||||
boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
|
||||
sep_boundary = b'\r\n--' + boundary.encode('ascii')
|
||||
end_boundary = sep_boundary + b'--\r\n'
|
||||
body = io.BytesIO()
|
||||
for key, value in data.items():
|
||||
title = '\r\nContent-Disposition: form-data; name="%s"' % key
|
||||
# handle multiple entries for the same name
|
||||
if not isinstance(value, list):
|
||||
value = [value]
|
||||
for value in value:
|
||||
if type(value) is tuple:
|
||||
title += '; filename="%s"' % value[0]
|
||||
value = value[1]
|
||||
else:
|
||||
value = str(value).encode('utf-8')
|
||||
body.write(sep_boundary)
|
||||
body.write(title.encode('utf-8'))
|
||||
body.write(b"\r\n\r\n")
|
||||
body.write(value)
|
||||
body.write(end_boundary)
|
||||
body = body.getvalue()
|
||||
|
||||
msg = "Submitting %s to %s" % (filename, self.repository)
|
||||
self.announce(msg, log.INFO)
|
||||
|
||||
# build the Request
|
||||
headers = {
|
||||
'Content-type': 'multipart/form-data; boundary=%s' % boundary,
|
||||
'Content-length': str(len(body)),
|
||||
'Authorization': auth,
|
||||
}
|
||||
|
||||
request = Request(self.repository, data=body,
|
||||
headers=headers)
|
||||
# send the data
|
||||
try:
|
||||
result = urlopen(request)
|
||||
status = result.getcode()
|
||||
reason = result.msg
|
||||
except HTTPError as e:
|
||||
status = e.code
|
||||
reason = e.msg
|
||||
except OSError as e:
|
||||
self.announce(str(e), log.ERROR)
|
||||
raise
|
||||
|
||||
if status == 200:
|
||||
self.announce('Server response (%s): %s' % (status, reason),
|
||||
log.INFO)
|
||||
if self.show_response:
|
||||
text = getattr(self, '_read_pypi_response',
|
||||
lambda x: None)(result)
|
||||
if text is not None:
|
||||
msg = '\n'.join(('-' * 75, text, '-' * 75))
|
||||
self.announce(msg, log.INFO)
|
||||
else:
|
||||
msg = 'Upload failed (%s): %s' % (status, reason)
|
||||
self.announce(msg, log.ERROR)
|
||||
raise DistutilsError(msg)
|
||||
|
||||
def _load_password_from_keyring(self):
|
||||
"""
|
||||
Attempt to load password from keyring. Suppress Exceptions.
|
||||
"""
|
||||
try:
|
||||
keyring = __import__('keyring')
|
||||
return keyring.get_password(self.repository, self.username)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _prompt_for_password(self):
|
||||
"""
|
||||
Prompt for a password on the tty. Suppress Exceptions.
|
||||
"""
|
||||
try:
|
||||
return getpass.getpass()
|
||||
except (Exception, KeyboardInterrupt):
|
||||
pass
|
||||
|
|
@ -8,32 +8,31 @@ PyPI's pythonhosted.org).
|
|||
from base64 import standard_b64encode
|
||||
from distutils import log
|
||||
from distutils.errors import DistutilsOptionError
|
||||
from distutils.command.upload import upload
|
||||
import os
|
||||
import socket
|
||||
import zipfile
|
||||
import tempfile
|
||||
import sys
|
||||
import shutil
|
||||
import itertools
|
||||
import functools
|
||||
|
||||
from setuptools.extern import six
|
||||
from setuptools.extern.six.moves import http_client, urllib
|
||||
|
||||
from pkg_resources import iter_entry_points
|
||||
from .upload import upload
|
||||
|
||||
|
||||
errors = 'surrogateescape' if six.PY3 else 'strict'
|
||||
|
||||
|
||||
# This is not just a replacement for byte literals
|
||||
# but works as a general purpose encoder
|
||||
def b(s, encoding='utf-8'):
|
||||
if isinstance(s, six.text_type):
|
||||
return s.encode(encoding, errors)
|
||||
return s
|
||||
def _encode(s):
|
||||
errors = 'surrogateescape' if six.PY3 else 'strict'
|
||||
return s.encode('utf-8', errors)
|
||||
|
||||
|
||||
class upload_docs(upload):
|
||||
# override the default repository as upload_docs isn't
|
||||
# supported by Warehouse (and won't be).
|
||||
DEFAULT_REPOSITORY = 'https://pypi.python.org/pypi/'
|
||||
|
||||
description = 'Upload documentation to PyPI'
|
||||
|
||||
user_options = [
|
||||
|
|
@ -69,6 +68,8 @@ class upload_docs(upload):
|
|||
else:
|
||||
self.ensure_dirname('upload_dir')
|
||||
self.target_dir = self.upload_dir
|
||||
if 'pypi.python.org' in self.repository:
|
||||
log.warn("Upload_docs command is deprecated. Use RTD instead.")
|
||||
self.announce('Using upload directory %s' % self.target_dir)
|
||||
|
||||
def create_zipfile(self, filename):
|
||||
|
|
@ -77,9 +78,8 @@ class upload_docs(upload):
|
|||
self.mkpath(self.target_dir) # just in case
|
||||
for root, dirs, files in os.walk(self.target_dir):
|
||||
if root == self.target_dir and not files:
|
||||
raise DistutilsOptionError(
|
||||
"no files found in upload directory '%s'"
|
||||
% self.target_dir)
|
||||
tmpl = "no files found in upload directory '%s'"
|
||||
raise DistutilsOptionError(tmpl % self.target_dir)
|
||||
for name in files:
|
||||
full = os.path.join(root, name)
|
||||
relative = root[len(self.target_dir):].lstrip(os.path.sep)
|
||||
|
|
@ -102,10 +102,48 @@ class upload_docs(upload):
|
|||
finally:
|
||||
shutil.rmtree(tmp_dir)
|
||||
|
||||
@staticmethod
|
||||
def _build_part(item, sep_boundary):
|
||||
key, values = item
|
||||
title = '\nContent-Disposition: form-data; name="%s"' % key
|
||||
# handle multiple entries for the same name
|
||||
if not isinstance(values, list):
|
||||
values = [values]
|
||||
for value in values:
|
||||
if isinstance(value, tuple):
|
||||
title += '; filename="%s"' % value[0]
|
||||
value = value[1]
|
||||
else:
|
||||
value = _encode(value)
|
||||
yield sep_boundary
|
||||
yield _encode(title)
|
||||
yield b"\n\n"
|
||||
yield value
|
||||
if value and value[-1:] == b'\r':
|
||||
yield b'\n' # write an extra newline (lurve Macs)
|
||||
|
||||
@classmethod
|
||||
def _build_multipart(cls, data):
|
||||
"""
|
||||
Build up the MIME payload for the POST data
|
||||
"""
|
||||
boundary = b'--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
|
||||
sep_boundary = b'\n--' + boundary
|
||||
end_boundary = sep_boundary + b'--'
|
||||
end_items = end_boundary, b"\n",
|
||||
builder = functools.partial(
|
||||
cls._build_part,
|
||||
sep_boundary=sep_boundary,
|
||||
)
|
||||
part_groups = map(builder, data.items())
|
||||
parts = itertools.chain.from_iterable(part_groups)
|
||||
body_items = itertools.chain(parts, end_items)
|
||||
content_type = 'multipart/form-data; boundary=%s' % boundary.decode('ascii')
|
||||
return b''.join(body_items), content_type
|
||||
|
||||
def upload_file(self, filename):
|
||||
f = open(filename, 'rb')
|
||||
content = f.read()
|
||||
f.close()
|
||||
with open(filename, 'rb') as f:
|
||||
content = f.read()
|
||||
meta = self.distribution.metadata
|
||||
data = {
|
||||
':action': 'doc_upload',
|
||||
|
|
@ -113,40 +151,16 @@ class upload_docs(upload):
|
|||
'content': (os.path.basename(filename), content),
|
||||
}
|
||||
# set up the authentication
|
||||
credentials = b(self.username + ':' + self.password)
|
||||
credentials = _encode(self.username + ':' + self.password)
|
||||
credentials = standard_b64encode(credentials)
|
||||
if six.PY3:
|
||||
credentials = credentials.decode('ascii')
|
||||
auth = "Basic " + credentials
|
||||
|
||||
# Build up the MIME payload for the POST data
|
||||
boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
|
||||
sep_boundary = b('\n--') + b(boundary)
|
||||
end_boundary = sep_boundary + b('--')
|
||||
body = []
|
||||
for key, values in six.iteritems(data):
|
||||
title = '\nContent-Disposition: form-data; name="%s"' % key
|
||||
# handle multiple entries for the same name
|
||||
if not isinstance(values, list):
|
||||
values = [values]
|
||||
for value in values:
|
||||
if type(value) is tuple:
|
||||
title += '; filename="%s"' % value[0]
|
||||
value = value[1]
|
||||
else:
|
||||
value = b(value)
|
||||
body.append(sep_boundary)
|
||||
body.append(b(title))
|
||||
body.append(b("\n\n"))
|
||||
body.append(value)
|
||||
if value and value[-1:] == b('\r'):
|
||||
body.append(b('\n')) # write an extra newline (lurve Macs)
|
||||
body.append(end_boundary)
|
||||
body.append(b("\n"))
|
||||
body = b('').join(body)
|
||||
body, ct = self._build_multipart(data)
|
||||
|
||||
self.announce("Submitting documentation to %s" % (self.repository),
|
||||
log.INFO)
|
||||
msg = "Submitting documentation to %s" % (self.repository)
|
||||
self.announce(msg, log.INFO)
|
||||
|
||||
# build the Request
|
||||
# We can't use urllib2 since we need to send the Basic
|
||||
|
|
@ -165,7 +179,7 @@ class upload_docs(upload):
|
|||
try:
|
||||
conn.connect()
|
||||
conn.putrequest("POST", url)
|
||||
content_type = 'multipart/form-data; boundary=%s' % boundary
|
||||
content_type = ct
|
||||
conn.putheader('Content-type', content_type)
|
||||
conn.putheader('Content-length', str(len(body)))
|
||||
conn.putheader('Authorization', auth)
|
||||
|
|
@ -177,16 +191,16 @@ class upload_docs(upload):
|
|||
|
||||
r = conn.getresponse()
|
||||
if r.status == 200:
|
||||
self.announce('Server response (%s): %s' % (r.status, r.reason),
|
||||
log.INFO)
|
||||
msg = 'Server response (%s): %s' % (r.status, r.reason)
|
||||
self.announce(msg, log.INFO)
|
||||
elif r.status == 301:
|
||||
location = r.getheader('Location')
|
||||
if location is None:
|
||||
location = 'https://pythonhosted.org/%s/' % meta.get_name()
|
||||
self.announce('Upload successful. Visit %s' % location,
|
||||
log.INFO)
|
||||
msg = 'Upload successful. Visit %s' % location
|
||||
self.announce(msg, log.INFO)
|
||||
else:
|
||||
self.announce('Upload failed (%s): %s' % (r.status, r.reason),
|
||||
log.ERROR)
|
||||
msg = 'Upload failed (%s): %s' % (r.status, r.reason)
|
||||
self.announce(msg, log.ERROR)
|
||||
if self.show_response:
|
||||
print('-' * 75, r.read(), '-' * 75)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue