run update
This commit is contained in:
parent
11af4540c5
commit
6806bebb7c
607 changed files with 52543 additions and 31832 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', 'upload',
|
||||
'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')
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,14 +6,15 @@ import fnmatch
|
|||
import textwrap
|
||||
import io
|
||||
import distutils.errors
|
||||
import collections
|
||||
import itertools
|
||||
|
||||
from setuptools.extern.six.moves import map
|
||||
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"
|
||||
|
|
@ -67,6 +68,9 @@ class build_py(orig.build_py, Mixin2to3):
|
|||
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:
|
||||
|
|
@ -94,12 +98,19 @@ class build_py(orig.build_py, Mixin2to3):
|
|||
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -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,34 @@ import subprocess
|
|||
import shlex
|
||||
import io
|
||||
|
||||
from sysconfig import get_config_vars, get_path
|
||||
|
||||
from setuptools.extern import six
|
||||
from setuptools.extern.six.moves import configparser, map
|
||||
|
||||
from setuptools import Command
|
||||
from setuptools.sandbox import run_setup
|
||||
from setuptools.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 +79,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 +95,8 @@ def samefile(p1, p2):
|
|||
|
||||
|
||||
if six.PY2:
|
||||
def _to_ascii(s):
|
||||
|
||||
def _to_bytes(s):
|
||||
return s
|
||||
|
||||
def isascii(s):
|
||||
|
|
@ -94,8 +106,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 +118,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"
|
||||
|
|
@ -269,11 +285,15 @@ class easy_install(Command):
|
|||
if not self.install_layout.lower() in ['deb']:
|
||||
raise DistutilsOptionError("unknown value for --install-layout")
|
||||
self.install_layout = self.install_layout.lower()
|
||||
|
||||
import sysconfig
|
||||
if sys.version_info[:2] >= (3, 3):
|
||||
self.multiarch = sysconfig.get_config_var('MULTIARCH')
|
||||
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:
|
||||
|
|
@ -327,7 +347,7 @@ consider to install to another location, or use the option
|
|||
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:
|
||||
|
|
@ -402,9 +422,15 @@ consider to install to another location, or use the option
|
|||
|
||||
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 +439,7 @@ consider to install to another location, or use the option
|
|||
for spec in self.args:
|
||||
self.easy_install(spec, not self.no_deps)
|
||||
if self.record:
|
||||
outputs = self.outputs
|
||||
outputs = list(sorted(self.outputs))
|
||||
if self.root: # strip any package prefix
|
||||
root_len = len(self.root)
|
||||
for counter in range(len(outputs)):
|
||||
|
|
@ -436,7 +462,7 @@ consider to install to another location, or use the option
|
|||
"""
|
||||
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)
|
||||
|
||||
|
|
@ -477,8 +503,7 @@ consider to install to another location, or use the option
|
|||
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):
|
||||
|
|
@ -517,7 +542,7 @@ consider to install to another location, or use the option
|
|||
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()
|
||||
|
|
@ -538,27 +563,34 @@ consider to install to another location, or use the option
|
|||
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
|
||||
|
||||
|
|
@ -623,20 +655,26 @@ consider to install to another location, or use the option
|
|||
(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
|
||||
|
|
@ -662,10 +700,6 @@ consider to install to another location, or use the option
|
|||
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
|
||||
|
|
@ -733,10 +767,7 @@ consider to install to another location, or use the option
|
|||
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(
|
||||
|
|
@ -765,8 +796,9 @@ consider to install to another location, or use the option
|
|||
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):
|
||||
|
|
@ -798,7 +830,7 @@ consider to install to another location, or use the option
|
|||
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):
|
||||
|
|
@ -806,7 +838,7 @@ consider to install to another location, or use the option
|
|||
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:
|
||||
|
|
@ -824,14 +856,16 @@ consider to install to another location, or use the option
|
|||
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
|
||||
|
|
@ -839,6 +873,8 @@ consider to install to another location, or use the option
|
|||
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
|
||||
|
|
@ -884,8 +920,10 @@ consider to install to another location, or use the option
|
|||
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)
|
||||
|
|
@ -895,8 +933,11 @@ consider to install to another location, or use the option
|
|||
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):
|
||||
|
|
@ -913,13 +954,19 @@ consider to install to another location, or use the option
|
|||
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
|
||||
|
||||
|
|
@ -941,8 +988,8 @@ consider to install to another location, or use the option
|
|||
)
|
||||
|
||||
# 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')
|
||||
|
|
@ -960,13 +1007,13 @@ consider to install to another location, or use the option
|
|||
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)
|
||||
|
|
@ -1024,6 +1071,35 @@ consider to install to another location, or use the option
|
|||
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
|
||||
|
|
@ -1154,7 +1230,7 @@ consider to install to another location, or use the option
|
|||
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)
|
||||
|
|
@ -1202,7 +1278,6 @@ consider to install to another location, or use the option
|
|||
|
||||
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
|
||||
|
|
@ -1215,7 +1290,7 @@ consider to install to another location, or use the option
|
|||
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
|
||||
|
|
@ -1246,7 +1321,8 @@ consider to install to another location, or use the option
|
|||
* 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()
|
||||
|
||||
|
|
@ -1262,17 +1338,14 @@ consider to install to another location, or use the option
|
|||
|
||||
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"
|
||||
|
|
@ -1283,9 +1356,8 @@ consider to install to another location, or use the option
|
|||
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
|
||||
|
|
@ -1359,10 +1431,21 @@ consider to install to another location, or use the option
|
|||
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)
|
||||
|
|
@ -1371,20 +1454,26 @@ 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,
|
||||
"local/lib",
|
||||
"python" + sys.version[:3],
|
||||
"dist-packages",
|
||||
),
|
||||
os.path.join(
|
||||
prefix,
|
||||
"lib",
|
||||
"python" + sys.version[:3],
|
||||
"dist-packages",
|
||||
),
|
||||
os.path.join(prefix, "lib", "site-python"),
|
||||
])
|
||||
else:
|
||||
if sys.version[:3] in ('2.3', '2.4', '2.5'):
|
||||
sdir = "site-packages"
|
||||
else:
|
||||
sdir = "dist-packages"
|
||||
sitedirs.extend(
|
||||
[os.path.join(prefix, "local/lib", "python" + sys.version[:3], sdir),
|
||||
os.path.join(prefix, "lib", "python" + sys.version[:3], sdir)]
|
||||
)
|
||||
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
|
||||
|
|
@ -1392,12 +1481,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:
|
||||
|
|
@ -1406,6 +1497,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
|
||||
|
|
@ -1473,8 +1569,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.
|
||||
|
|
@ -1497,7 +1593,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', ''),
|
||||
|
|
@ -1531,15 +1628,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"""
|
||||
|
||||
|
|
@ -1649,7 +1737,6 @@ class PthDistributions(Environment):
|
|||
|
||||
|
||||
class RewritePthDistributions(PthDistributions):
|
||||
|
||||
@classmethod
|
||||
def _wrap_lines(cls, lines):
|
||||
yield cls.prelude
|
||||
|
|
@ -1657,12 +1744,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:]
|
||||
|
|
@ -1672,7 +1758,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
|
||||
|
||||
|
||||
|
|
@ -1689,7 +1775,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()
|
||||
|
|
@ -1821,8 +1907,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)
|
||||
|
|
@ -1842,6 +1928,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
|
||||
|
|
@ -1854,6 +1941,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
|
||||
|
|
@ -1996,11 +2084,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()
|
||||
|
||||
|
|
@ -2009,19 +2107,21 @@ class WindowsCommandSpec(CommandSpec):
|
|||
split_args = dict(posix=False)
|
||||
|
||||
|
||||
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)()
|
||||
)
|
||||
|
|
@ -2040,12 +2140,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", DeprecationWarning, 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):
|
||||
|
|
@ -2130,8 +2228,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)
|
||||
|
|
@ -2210,8 +2311,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:
|
||||
|
|
@ -2228,39 +2327,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():
|
||||
|
|
@ -2297,7 +2364,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
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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,6 +14,7 @@ import sys
|
|||
import io
|
||||
import warnings
|
||||
import time
|
||||
import collections
|
||||
|
||||
from setuptools.extern import six
|
||||
from setuptools.extern.six.moves import map
|
||||
|
|
@ -26,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
|
||||
|
||||
try:
|
||||
from setuptools_svn import svn_utils
|
||||
except ImportError:
|
||||
pass
|
||||
from setuptools.extern import packaging
|
||||
|
||||
|
||||
class egg_info(Command):
|
||||
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 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:
|
||||
|
|
@ -162,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)
|
||||
|
|
@ -185,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")
|
||||
|
|
@ -226,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
|
||||
|
|
@ -289,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()
|
||||
|
|
@ -314,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)
|
||||
|
|
@ -328,38 +576,13 @@ class manifest_maker(sdist):
|
|||
elif os.path.exists(self.manifest):
|
||||
self.read_manifest()
|
||||
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)
|
||||
|
|
@ -384,6 +607,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
|
||||
|
|
@ -407,7 +631,7 @@ def warn_depends_obsolete(cmd, basename, filename):
|
|||
def _write_requirements(stream, reqs):
|
||||
lines = yield_lines(reqs or ())
|
||||
append_cr = lambda line: line + '\n'
|
||||
lines = map(append_cr, lines)
|
||||
lines = map(append_cr, sorted(lines))
|
||||
stream.writelines(lines)
|
||||
|
||||
|
||||
|
|
@ -423,7 +647,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())
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
from distutils import log, dir_util
|
||||
import os, sys
|
||||
|
||||
from setuptools.extern.six.moves import map
|
||||
|
||||
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"
|
||||
|
|
@ -81,58 +80,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)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,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)
|
||||
|
|
|
|||
|
|
@ -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,18 +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
|
||||
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
|
||||
|
||||
|
|
@ -20,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))
|
||||
|
||||
|
|
@ -44,7 +60,7 @@ class ScanningLoader(TestLoader):
|
|||
|
||||
|
||||
# adapted from jaraco.classes.properties:NonDataProperty
|
||||
class NonDataProperty(object):
|
||||
class NonDataProperty:
|
||||
def __init__(self, fget):
|
||||
self.fget = fget
|
||||
|
||||
|
|
@ -62,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"),
|
||||
]
|
||||
|
||||
|
|
@ -96,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:
|
||||
|
|
@ -133,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
|
||||
|
|
@ -174,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):
|
||||
|
|
|
|||
|
|
@ -1,15 +1,37 @@
|
|||
import getpass
|
||||
from distutils import log
|
||||
from distutils.command import upload as orig
|
||||
|
||||
|
||||
class upload(orig.upload):
|
||||
"""
|
||||
Override default upload behavior to look up password
|
||||
in the keyring if available.
|
||||
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.password or self._load_password_from_keyring()
|
||||
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 _load_password_from_keyring(self):
|
||||
"""
|
||||
|
|
@ -17,7 +39,15 @@ class upload(orig.upload):
|
|||
"""
|
||||
try:
|
||||
keyring = __import__('keyring')
|
||||
self.password = keyring.get_password(self.repository,
|
||||
self.username)
|
||||
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
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import socket
|
|||
import zipfile
|
||||
import tempfile
|
||||
import shutil
|
||||
import itertools
|
||||
import functools
|
||||
|
||||
from setuptools.extern import six
|
||||
from setuptools.extern.six.moves import http_client, urllib
|
||||
|
|
@ -21,18 +23,16 @@ 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 = [
|
||||
|
|
@ -68,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):
|
||||
|
|
@ -76,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)
|
||||
|
|
@ -101,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',
|
||||
|
|
@ -112,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
|
||||
|
|
@ -164,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)
|
||||
|
|
@ -176,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