added setuptools and new url taggit-template2

This commit is contained in:
Esther Kleinhenz
2018-08-25 12:14:54 +02:00
parent 3d9e40f840
commit 8da96e27a5
44 changed files with 565 additions and 698 deletions
@@ -1,12 +1,14 @@
"""Extensions to the 'distutils' for large or complex distributions"""
import os
import sys
import functools
import distutils.core
import distutils.filelist
from distutils.util import convert_path
from fnmatch import fnmatchcase
from setuptools.extern.six import PY3
from setuptools.extern.six.moves import filter, map
import setuptools.version
@@ -15,11 +17,17 @@ from setuptools.dist import Distribution, Feature
from setuptools.depends import Require
from . import monkey
__metaclass__ = type
__all__ = [
'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require',
'find_packages',
'find_packages'
]
if PY3:
__all__.append('find_namespace_packages')
__version__ = setuptools.version.__version__
bootstrap_install_from = None
@@ -31,7 +39,7 @@ run_2to3_on_doctests = True
lib2to3_fixer_packages = ['lib2to3.fixes']
class PackageFinder(object):
class PackageFinder:
"""
Generate a list of all Python packages found within a directory
"""
@@ -109,6 +117,9 @@ class PEP420PackageFinder(PackageFinder):
find_packages = PackageFinder.find
if PY3:
find_namespace_packages = PEP420PackageFinder.find
def _install_setup_requires(attrs):
# Note: do not use `setuptools.Distribution` directly, as
@@ -60,8 +60,8 @@ The pyparsing module handles some of the problems that are typically vexing when
- embedded comments
"""
__version__ = "2.1.10"
__versionTime__ = "07 Oct 2016 01:31 UTC"
__version__ = "2.2.0"
__versionTime__ = "06 Mar 2017 02:06 UTC"
__author__ = "Paul McGuire <ptmcg@users.sourceforge.net>"
import string
@@ -144,7 +144,7 @@ else:
except UnicodeEncodeError:
# Else encode it
ret = unicode(obj).encode(sys.getdefaultencoding(), 'xmlcharrefreplace')
xmlcharref = Regex('&#\d+;')
xmlcharref = Regex(r'&#\d+;')
xmlcharref.setParseAction(lambda t: '\\u' + hex(int(t[0][2:-1]))[2:])
return xmlcharref.transformString(ret)
@@ -809,7 +809,7 @@ class ParseResults(object):
return None
def getName(self):
"""
r"""
Returns the results name for this token expression. Useful when several
different expressions might match at a particular location.
@@ -1226,7 +1226,7 @@ class ParserElement(object):
def setParseAction( self, *fns, **kwargs ):
"""
Define action to perform when successfully matching parse element definition.
Define one or more actions to perform when successfully matching parse element definition.
Parse action fn is a callable method with 0-3 arguments, called as C{fn(s,loc,toks)},
C{fn(loc,toks)}, C{fn(toks)}, or just C{fn()}, where:
- s = the original string being parsed (see note below)
@@ -1264,7 +1264,7 @@ class ParserElement(object):
def addParseAction( self, *fns, **kwargs ):
"""
Add parse action to expression's list of parse actions. See L{I{setParseAction}<setParseAction>}.
Add one or more parse actions to expression's list of parse actions. See L{I{setParseAction}<setParseAction>}.
See examples in L{I{copy}<copy>}.
"""
@@ -1443,10 +1443,14 @@ class ParserElement(object):
def clear(self):
cache.clear()
def cache_len(self):
return len(cache)
self.get = types.MethodType(get, self)
self.set = types.MethodType(set, self)
self.clear = types.MethodType(clear, self)
self.__len__ = types.MethodType(cache_len, self)
if _OrderedDict is not None:
class _FifoCache(object):
@@ -1460,15 +1464,22 @@ class ParserElement(object):
def set(self, key, value):
cache[key] = value
if len(cache) > size:
cache.popitem(False)
while len(cache) > size:
try:
cache.popitem(False)
except KeyError:
pass
def clear(self):
cache.clear()
def cache_len(self):
return len(cache)
self.get = types.MethodType(get, self)
self.set = types.MethodType(set, self)
self.clear = types.MethodType(clear, self)
self.__len__ = types.MethodType(cache_len, self)
else:
class _FifoCache(object):
@@ -1483,7 +1494,7 @@ class ParserElement(object):
def set(self, key, value):
cache[key] = value
if len(cache) > size:
while len(key_fifo) > size:
cache.pop(key_fifo.popleft(), None)
key_fifo.append(key)
@@ -1491,9 +1502,13 @@ class ParserElement(object):
cache.clear()
key_fifo.clear()
def cache_len(self):
return len(cache)
self.get = types.MethodType(get, self)
self.set = types.MethodType(set, self)
self.clear = types.MethodType(clear, self)
self.__len__ = types.MethodType(cache_len, self)
# argument cache for optimizing repeated calls when backtracking through recursive expressions
packrat_cache = {} # this is set later by enabledPackrat(); this is here so that resetCache() doesn't fail
@@ -1743,8 +1758,12 @@ class ParserElement(object):
cap_word = Word(alphas.upper(), alphas.lower())
print(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity"))
# the sum() builtin can be used to merge results into a single ParseResults object
print(sum(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity")))
prints::
['More', 'Iron', 'Lead', 'Gold', 'I']
[['More'], ['Iron'], ['Lead'], ['Gold'], ['I'], ['Electricity']]
['More', 'Iron', 'Lead', 'Gold', 'I', 'Electricity']
"""
try:
return ParseResults([ t for t,s,e in self.scanString( instring, maxMatches ) ])
@@ -1819,7 +1838,7 @@ class ParserElement(object):
warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
SyntaxWarning, stacklevel=2)
return None
return And( [ self, And._ErrorStop(), other ] )
return self + And._ErrorStop() + other
def __rsub__(self, other ):
"""
@@ -2722,7 +2741,7 @@ class Word(Token):
class Regex(Token):
"""
r"""
Token for matching strings that match a given regular expression.
Defined with string specifying the regular expression in a form recognized by the inbuilt Python re module.
If the given regex contains named groups (defined using C{(?P<name>...)}), these will be preserved as
@@ -2911,7 +2930,7 @@ class QuotedString(Token):
# replace escaped characters
if self.escChar:
ret = re.sub(self.escCharReplacePattern,"\g<1>",ret)
ret = re.sub(self.escCharReplacePattern, r"\g<1>", ret)
# replace escaped quotes
if self.escQuote:
@@ -5020,7 +5039,9 @@ def infixNotation( baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')') ):
constants C{opAssoc.RIGHT} and C{opAssoc.LEFT}.
- parseAction is the parse action to be associated with
expressions matching this operator expression (the
parse action tuple member may be omitted)
parse action tuple member may be omitted); if the parse action
is passed a tuple or list of functions, this is equivalent to
calling C{setParseAction(*fn)} (L{ParserElement.setParseAction})
- lpar - expression for matching left-parentheses (default=C{Suppress('(')})
- rpar - expression for matching right-parentheses (default=C{Suppress(')')})
@@ -5093,7 +5114,10 @@ def infixNotation( baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')') ):
else:
raise ValueError("operator must indicate right or left associativity")
if pa:
matchExpr.setParseAction( pa )
if isinstance(pa, (tuple, list)):
matchExpr.setParseAction(*pa)
else:
matchExpr.setParseAction(pa)
thisExpr <<= ( matchExpr.setName(termName) | lastExpr )
lastExpr = thisExpr
ret <<= lastExpr
@@ -61,6 +61,19 @@ class Distribution(setuptools.dist.Distribution):
distutils.core.Distribution = orig
def _to_str(s):
"""
Convert a filename to a string (on Python 2, explicitly
a byte string, not Unicode) as distutils checks for the
exact type str.
"""
if sys.version_info[0] == 2 and not isinstance(s, str):
# Assume it's Unicode, as that's what the PEP says
# should be provided.
return s.encode(sys.getfilesystemencoding())
return s
def _run_setup(setup_script='setup.py'):
# Note that we can reuse our build directory between calls
# Correctness comes first, then optimization later
@@ -109,7 +122,7 @@ def get_requires_for_build_sdist(config_settings=None):
def prepare_metadata_for_build_wheel(metadata_directory, config_settings=None):
sys.argv = sys.argv[:1] + ['dist_info', '--egg-base', metadata_directory]
sys.argv = sys.argv[:1] + ['dist_info', '--egg-base', _to_str(metadata_directory)]
_run_setup()
dist_info_directory = metadata_directory
@@ -411,7 +411,7 @@ 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
elif sys.version_info < (3, 7):
skip = 12 # skip magic & date & file size
@@ -112,7 +112,7 @@ class build_ext(_build_ext):
and get_abi3_suffix()
)
if use_abi3:
so_ext = _get_config_var_837('EXT_SUFFIX')
so_ext = get_config_var('EXT_SUFFIX')
filename = filename[:-len(so_ext)]
filename = filename + get_abi3_suffix()
if isinstance(ext, Library):
@@ -319,13 +319,3 @@ else:
self.create_static_lib(
objects, basename, output_dir, debug, target_lang
)
def _get_config_var_837(name):
"""
In https://github.com/pypa/setuptools/pull/837, we discovered
Python 3.3.0 exposes the extension suffix under the name 'SO'.
"""
if sys.version_info < (3, 3, 1):
name = 'SO'
return get_config_var(name)
@@ -12,6 +12,8 @@ from setuptools.command.easy_install import easy_install
from setuptools import namespaces
import setuptools
__metaclass__ = type
class develop(namespaces.DevelopInstaller, easy_install):
"""Set up package for development"""
@@ -192,7 +194,7 @@ class develop(namespaces.DevelopInstaller, 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
@@ -40,12 +40,13 @@ 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
@@ -62,6 +63,8 @@ from pkg_resources import (
)
import pkg_resources.py31compat
__metaclass__ = type
# Turn on PEP440Warnings
warnings.filterwarnings("default", category=pkg_resources.PEP440Warning)
@@ -93,7 +96,7 @@ def samefile(p1, p2):
if six.PY2:
def _to_ascii(s):
def _to_bytes(s):
return s
def isascii(s):
@@ -104,8 +107,8 @@ if six.PY2:
return False
else:
def _to_ascii(s):
return s.encode('ascii')
def _to_bytes(s):
return s.encode('utf8')
def isascii(s):
try:
@@ -319,7 +322,7 @@ class easy_install(Command):
self.all_site_dirs.append(normalize_path(d))
if not self.editable:
self.check_site_dir()
self.index_url = self.index_url or "https://pypi.python.org/simple"
self.index_url = self.index_url or "https://pypi.org/simple/"
self.shadow_path = self.all_site_dirs[:]
for path_item in self.install_dir, normalize_path(self.script_dir):
if path_item not in self.shadow_path:
@@ -802,7 +805,7 @@ class easy_install(Command):
if is_script:
body = self._load_template(dev_path) % locals()
script_text = ScriptWriter.get_header(script_text) + body
self.write_script(script_name, _to_ascii(script_text), 'b')
self.write_script(script_name, _to_bytes(script_text), 'b')
@staticmethod
def _load_template(dev_path):
@@ -2049,7 +2052,7 @@ 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.
@@ -116,7 +116,33 @@ def translate_pattern(glob):
return re.compile(pat, flags=re.MULTILINE|re.DOTALL)
class egg_info(Command):
class InfoCommon:
tag_build = None
tag_date = None
@property
def name(self):
return safe_name(self.distribution.get_name())
def tagged_version(self):
version = self.distribution.get_version()
# egg_info may be called more than once for a distribution,
# in which case the version string already contains all tags.
if self.vtags and version.endswith(self.vtags):
return safe_version(version)
return safe_version(version + self.vtags)
def tags(self):
version = ''
if self.tag_build:
version += self.tag_build
if self.tag_date:
version += time.strftime("-%Y%m%d")
return version
vtags = property(tags)
class egg_info(InfoCommon, Command):
description = "create a distribution's .egg-info directory"
user_options = [
@@ -133,14 +159,11 @@ class egg_info(Command):
}
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_date = 0
self.egg_version = None
self.broken_egg_info = False
self.vtags = None
####################################
# allow the 'tag_svn_revision' to be detected and
@@ -168,10 +191,12 @@ class egg_info(Command):
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:
@@ -254,14 +279,6 @@ 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)
installer = self.distribution.fetch_build_egg
@@ -277,14 +294,6 @@ class egg_info(Command):
self.find_sources()
def tags(self):
version = ''
if self.tag_build:
version += self.tag_build
if self.tag_date:
version += time.strftime("-%Y%m%d")
return version
def find_sources(self):
"""Generate SOURCES.txt manifest file"""
manifest_filename = os.path.join(self.egg_info, "SOURCES.txt")
@@ -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
)
@@ -16,6 +16,8 @@ from pkg_resources import (resource_listdir, resource_exists, normalize_path,
add_activation_listener, require, EntryPoint)
from setuptools import Command
__metaclass__ = type
class ScanningLoader(TestLoader):
@@ -58,7 +60,7 @@ class ScanningLoader(TestLoader):
# adapted from jaraco.classes.properties:NonDataProperty
class NonDataProperty(object):
class NonDataProperty:
def __init__(self, fget):
self.fget = fget
@@ -1,4 +1,5 @@
import getpass
from distutils import log
from distutils.command import upload as orig
@@ -8,6 +9,16 @@ class upload(orig.upload):
in a variety of different ways.
"""
def run(self):
try:
orig.upload.run(self)
finally:
self.announce(
"WARNING: Uploading via this command is deprecated, use twine "
"to upload instead (https://pypi.org/p/twine/)",
log.WARN
)
def finalize_options(self):
orig.upload.finalize_options(self)
self.username = (
@@ -7,7 +7,11 @@ from functools import partial
from importlib import import_module
from distutils.errors import DistutilsOptionError, DistutilsFileError
from setuptools.extern.six import string_types
from setuptools.extern.packaging.version import LegacyVersion, parse
from setuptools.extern.six import string_types, PY3
__metaclass__ = type
def read_configuration(
@@ -101,18 +105,18 @@ def parse_configuration(
If False exceptions are propagated as expected.
:rtype: list
"""
meta = ConfigMetadataHandler(
distribution.metadata, command_options, ignore_option_errors)
meta.parse()
options = ConfigOptionsHandler(
distribution, command_options, ignore_option_errors)
options.parse()
meta = ConfigMetadataHandler(
distribution.metadata, command_options, ignore_option_errors, distribution.package_dir)
meta.parse()
return meta, options
class ConfigHandler(object):
class ConfigHandler:
"""Handles metadata supplied in configuration files."""
section_prefix = None
@@ -280,7 +284,7 @@ class ConfigHandler(object):
return f.read()
@classmethod
def _parse_attr(cls, value):
def _parse_attr(cls, value, package_dir=None):
"""Represents value as a module attribute.
Examples:
@@ -300,7 +304,21 @@ class ConfigHandler(object):
module_name = '.'.join(attrs_path)
module_name = module_name or '__init__'
sys.path.insert(0, os.getcwd())
parent_path = os.getcwd()
if package_dir:
if attrs_path[0] in package_dir:
# A custom path was specified for the module we want to import
custom_path = package_dir[attrs_path[0]]
parts = custom_path.rsplit('/', 1)
if len(parts) > 1:
parent_path = os.path.join(os.getcwd(), parts[0])
module_name = parts[1]
else:
module_name = custom_path
elif '' in package_dir:
# A custom parent directory was specified for all root modules
parent_path = os.path.join(os.getcwd(), package_dir[''])
sys.path.insert(0, parent_path)
try:
module = import_module(module_name)
value = getattr(module, attr_name)
@@ -399,6 +417,12 @@ class ConfigMetadataHandler(ConfigHandler):
"""
def __init__(self, target_obj, options, ignore_option_errors=False,
package_dir=None):
super(ConfigMetadataHandler, self).__init__(target_obj, options,
ignore_option_errors)
self.package_dir = package_dir
@property
def parsers(self):
"""Metadata item name to parser function mapping."""
@@ -427,7 +451,19 @@ class ConfigMetadataHandler(ConfigHandler):
:rtype: str
"""
version = self._parse_attr(value)
version = self._parse_file(value)
if version != value:
version = version.strip()
# Be strict about versions loaded from file because it's easy to
# accidentally include newlines and other unintended content
if isinstance(parse(version), LegacyVersion):
raise DistutilsOptionError('Version loaded from %s does not comply with PEP 440: %s' % (
value, version
))
return version
version = self._parse_attr(value, self.package_dir)
if callable(version):
version = version()
@@ -479,16 +515,24 @@ class ConfigOptionsHandler(ConfigHandler):
:param value:
:rtype: list
"""
find_directive = 'find:'
find_directives = ['find:', 'find_namespace:']
trimmed_value = value.strip()
if not value.startswith(find_directive):
if not trimmed_value in find_directives:
return self._parse_list(value)
findns = trimmed_value == find_directives[1]
if findns and not PY3:
raise DistutilsOptionError('find_namespace: directive is unsupported on Python < 3.3')
# Read function arguments from a dedicated section.
find_kwargs = self.parse_section_packages__find(
self.sections.get('packages.find', {}))
from setuptools import find_packages
if findns:
from setuptools import find_namespace_packages as find_packages
else:
from setuptools import find_packages
return find_packages(**find_kwargs)
@@ -123,15 +123,6 @@ def write_pkg_file(self, file):
file.write('Provides-Extra: %s\n' % extra)
# from Python 3.4
def write_pkg_info(self, base_dir):
"""Write the PKG-INFO file into the release tree.
"""
with open(os.path.join(base_dir, 'PKG-INFO'), 'w',
encoding='UTF-8') as pkg_info:
self.write_pkg_file(pkg_info)
sequence = tuple, list
@@ -337,6 +328,12 @@ class Distribution(Distribution_parse_config_files, _Distribution):
distribution for the included and excluded features.
"""
_DISTUTILS_UNSUPPORTED_METADATA = {
'long_description_content_type': None,
'project_urls': dict,
'provides_extras': set,
}
_patched_dist = None
def patch_missing_pkg_info(self, attrs):
@@ -362,25 +359,29 @@ class Distribution(Distribution_parse_config_files, _Distribution):
self.require_features = []
self.features = {}
self.dist_files = []
# Filter-out setuptools' specific options.
self.src_root = attrs.pop("src_root", None)
self.patch_missing_pkg_info(attrs)
self.project_urls = attrs.get('project_urls', {})
self.dependency_links = attrs.pop('dependency_links', [])
self.setup_requires = attrs.pop('setup_requires', [])
for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'):
vars(self).setdefault(ep.name, None)
_Distribution.__init__(self, attrs)
_Distribution.__init__(self, {
k: v for k, v in attrs.items()
if k not in self._DISTUTILS_UNSUPPORTED_METADATA
})
# The project_urls attribute may not be supported in distutils, so
# prime it here from our value if not automatically set
self.metadata.project_urls = getattr(
self.metadata, 'project_urls', self.project_urls)
self.metadata.long_description_content_type = attrs.get(
'long_description_content_type'
)
self.metadata.provides_extras = getattr(
self.metadata, 'provides_extras', set()
)
# Fill-in missing metadata fields not supported by distutils.
# Note some fields may have been set by other tools (e.g. pbr)
# above; they are taken preferrentially to setup() arguments
for option, default in self._DISTUTILS_UNSUPPORTED_METADATA.items():
for source in self.metadata.__dict__, attrs:
if option in source:
value = source[option]
break
else:
value = default() if default else None
setattr(self.metadata, option, value)
if isinstance(self.metadata.version, numbers.Number):
# Some people apparently take "version number" too literally :)
@@ -48,7 +48,7 @@ class VendorImporter:
# on later Python versions to cause relative imports
# in the vendor package to resolve the same modules
# as those going through this importer.
if sys.version_info > (3, 3):
if sys.version_info >= (3, ):
del sys.modules[extant]
return mod
except ImportError:
@@ -75,8 +75,6 @@ def patch_all():
needs_warehouse = (
sys.version_info < (2, 7, 13)
or
(3, 0) < sys.version_info < (3, 3, 7)
or
(3, 4) < sys.version_info < (3, 4, 6)
or
(3, 5) < sys.version_info <= (3, 5, 3)
@@ -87,7 +85,6 @@ def patch_all():
distutils.config.PyPIRCCommand.DEFAULT_REPOSITORY = warehouse
_patch_distribution_metadata_write_pkg_file()
_patch_distribution_metadata_write_pkg_info()
# Install Distribution throughout the distutils
for module in distutils.dist, distutils.core, distutils.cmd:
@@ -111,21 +108,6 @@ def _patch_distribution_metadata_write_pkg_file():
)
def _patch_distribution_metadata_write_pkg_info():
"""
Workaround issue #197 - Python 3 prior to 3.2.2 uses an environment-local
encoding to save the pkg_info. Monkey-patch its write_pkg_info method to
correct this undesirable behavior.
"""
environment_local = (3,) <= sys.version_info[:3] < (3, 2, 2)
if not environment_local:
return
distutils.dist.DistributionMetadata.write_pkg_info = (
setuptools.dist.write_pkg_info
)
def patch_func(replacement, target_mod, func_name):
"""
Patch func_name in target_mod with replacement
@@ -232,8 +232,7 @@ def _augment_exception(exc, version, arch=''):
elif version >= 14.0:
# For VC++ 14.0 Redirect user to Visual C++ Build Tools
message += (' Get it with "Microsoft Visual C++ Build Tools": '
r'http://landinghub.visualstudio.com/'
'visual-cpp-build-tools')
r'https://visualstudio.microsoft.com/downloads/')
exc.args = (message, )
@@ -26,12 +26,13 @@ from setuptools.py27compat import get_all_headers
from setuptools.py33compat import unescape
from setuptools.wheel import Wheel
__metaclass__ = type
EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.+!]+)$')
HREF = re.compile("""href\\s*=\\s*['"]?([^'"> ]+)""", re.I)
# this is here to fix emacs' cruddy broken syntax highlighting
HREF = re.compile(r"""href\s*=\s*['"]?([^'"> ]+)""", re.I)
PYPI_MD5 = re.compile(
'<a href="([^"#]+)">([^<]+)</a>\n\\s+\\(<a (?:title="MD5 hash"\n\\s+)'
'href="[^?]+\\?:action=show_md5&amp;digest=([0-9a-f]{32})">md5</a>\\)'
r'<a href="([^"#]+)">([^<]+)</a>\n\s+\(<a (?:title="MD5 hash"\n\s+)'
r'href="[^?]+\?:action=show_md5&amp;digest=([0-9a-f]{32})">md5</a>\)'
)
URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):', re.I).match
EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz".split()
@@ -235,7 +236,7 @@ def find_external_links(url, page):
yield urllib.parse.urljoin(url, htmldecode(match.group(1)))
class ContentChecker(object):
class ContentChecker:
"""
A null content checker that defines the interface for checking content
"""
@@ -297,7 +298,7 @@ class PackageIndex(Environment):
"""A distribution index that scans web pages for download URLs"""
def __init__(
self, index_url="https://pypi.python.org/simple", hosts=('*',),
self, index_url="https://pypi.org/simple/", hosts=('*',),
ca_bundle=None, verify_ssl=True, *args, **kw
):
Environment.__init__(self, *args, **kw)
@@ -933,12 +934,19 @@ entity_sub = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?').sub
def decode_entity(match):
what = match.group(1)
what = match.group(0)
return unescape(what)
def htmldecode(text):
"""Decode HTML entities in the given text."""
"""
Decode HTML entities in the given text.
>>> htmldecode(
... 'https://../package_name-0.1.2.tar.gz'
... '?tokena=A&amp;tokenb=B">package_name-0.1.2.tar.gz')
'https://../package_name-0.1.2.tar.gz?tokena=A&tokenb=B">package_name-0.1.2.tar.gz'
"""
return entity_sub(decode_entity, text)
@@ -980,7 +988,7 @@ def _encode_auth(auth):
return encoded.replace('\n', '')
class Credential(object):
class Credential:
"""
A username/password pair. Use like a namedtuple.
"""
@@ -4,6 +4,7 @@
from __future__ import absolute_import
import distutils.util
from distutils import log
import platform
import re
import sys
@@ -11,6 +12,8 @@ import sysconfig
import warnings
from collections import OrderedDict
from .extern import six
from . import glibc
_osx_arch_pat = re.compile(r'(.+)_(\d+)_(\d+)_(.+)')
@@ -69,8 +72,8 @@ def get_flag(var, fallback, expected=True, warn=True):
val = get_config_var(var)
if val is None:
if warn:
warnings.warn("Config variable '{0}' is unset, Python ABI tag may "
"be incorrect".format(var), RuntimeWarning, 2)
log.debug("Config variable '%s' is unset, Python ABI tag may "
"be incorrect", var)
return fallback()
return val == expected
@@ -96,8 +99,8 @@ def get_abi_tag():
lambda: sys.maxunicode == 0x10ffff,
expected=4,
warn=(impl == 'cp' and
sys.version_info < (3, 3))) \
and sys.version_info < (3, 3):
six.PY2)) \
and six.PY2:
u = 'u'
abi = '%s%s%s%s%s' % (impl, get_impl_ver(), d, m, u)
elif soabi and soabi.startswith('cpython-'):
@@ -1,15 +1,6 @@
__all__ = ['get_config_vars', 'get_path']
__all__ = []
try:
# Python 2.7 or >=3.2
from sysconfig import get_config_vars, get_path
except ImportError:
from distutils.sysconfig import get_config_vars, get_python_lib
def get_path(name):
if name not in ('platlib', 'purelib'):
raise ValueError("Name must be purelib or platlib")
return get_python_lib(name == 'platlib')
__metaclass__ = type
try:
@@ -19,7 +10,7 @@ except ImportError:
import shutil
import tempfile
class TemporaryDirectory(object):
class TemporaryDirectory:
"""
Very simple temporary directory context manager.
Will try to delete afterward, but will also ignore OS and similar
@@ -10,11 +10,12 @@ except ImportError:
from setuptools.extern import six
from setuptools.extern.six.moves import html_parser
__metaclass__ = type
OpArg = collections.namedtuple('OpArg', 'opcode arg')
class Bytecode_compat(object):
class Bytecode_compat:
def __init__(self, code):
self.code = code
@@ -2,4 +2,5 @@
__requires__ = %(spec)r
__import__('pkg_resources').require(%(spec)r)
__file__ = %(dev_path)r
exec(compile(open(__file__).read(), __file__, 'exec'))
with open(__file__) as f:
exec(compile(f.read(), __file__, 'exec'))
@@ -23,7 +23,7 @@ def __boot():
break
else:
try:
import imp # Avoid import loop in Python >= 3.3
import imp # Avoid import loop in Python 3
stream, path, descr = imp.find_module('site', [item])
except ImportError:
continue
@@ -1,24 +1,29 @@
'''Wheels support.'''
"""Wheels support."""
from distutils.util import get_platform
import email
import itertools
import os
import posixpath
import re
import zipfile
from pkg_resources import Distribution, PathMetadata, parse_version
from setuptools.extern.packaging.utils import canonicalize_name
from setuptools.extern.six import PY3
from setuptools import Distribution as SetuptoolsDistribution
from setuptools import pep425tags
from setuptools.command.egg_info import write_requirements
__metaclass__ = type
WHEEL_NAME = re.compile(
r"""^(?P<project_name>.+?)-(?P<version>\d.*?)
((-(?P<build>\d.*?))?-(?P<py_version>.+?)-(?P<abi>.+?)-(?P<platform>.+?)
)\.whl$""",
re.VERBOSE).match
re.VERBOSE).match
NAMESPACE_PACKAGE_INIT = '''\
try:
@@ -50,7 +55,7 @@ def unpack(src_dir, dst_dir):
os.rmdir(dirpath)
class Wheel(object):
class Wheel:
def __init__(self, filename):
match = WHEEL_NAME(os.path.basename(filename))
@@ -62,9 +67,11 @@ class Wheel(object):
def tags(self):
'''List tags (py_version, abi, platform) supported by this wheel.'''
return itertools.product(self.py_version.split('.'),
self.abi.split('.'),
self.platform.split('.'))
return itertools.product(
self.py_version.split('.'),
self.abi.split('.'),
self.platform.split('.'),
)
def is_compatible(self):
'''Is the wheel is compatible with the current platform?'''
@@ -77,87 +84,127 @@ class Wheel(object):
platform=(None if self.platform == 'any' else get_platform()),
).egg_name() + '.egg'
def get_dist_info(self, zf):
# find the correct name of the .dist-info dir in the wheel file
for member in zf.namelist():
dirname = posixpath.dirname(member)
if (dirname.endswith('.dist-info') and
canonicalize_name(dirname).startswith(
canonicalize_name(self.project_name))):
return dirname
raise ValueError("unsupported wheel format. .dist-info not found")
def install_as_egg(self, destination_eggdir):
'''Install wheel as an egg directory.'''
with zipfile.ZipFile(self.filename) as zf:
dist_basename = '%s-%s' % (self.project_name, self.version)
dist_info = '%s.dist-info' % dist_basename
dist_data = '%s.data' % dist_basename
def get_metadata(name):
with zf.open('%s/%s' % (dist_info, name)) as fp:
value = fp.read().decode('utf-8') if PY3 else fp.read()
return email.parser.Parser().parsestr(value)
wheel_metadata = get_metadata('WHEEL')
dist_metadata = get_metadata('METADATA')
# Check wheel format version is supported.
wheel_version = parse_version(wheel_metadata.get('Wheel-Version'))
if not parse_version('1.0') <= wheel_version < parse_version('2.0dev0'):
raise ValueError('unsupported wheel format version: %s' % wheel_version)
# Extract to target directory.
os.mkdir(destination_eggdir)
zf.extractall(destination_eggdir)
# Convert metadata.
dist_info = os.path.join(destination_eggdir, dist_info)
dist = Distribution.from_location(
destination_eggdir, dist_info,
metadata=PathMetadata(destination_eggdir, dist_info)
self._install_as_egg(destination_eggdir, zf)
def _install_as_egg(self, destination_eggdir, zf):
dist_basename = '%s-%s' % (self.project_name, self.version)
dist_info = self.get_dist_info(zf)
dist_data = '%s.data' % dist_basename
egg_info = os.path.join(destination_eggdir, 'EGG-INFO')
self._convert_metadata(zf, destination_eggdir, dist_info, egg_info)
self._move_data_entries(destination_eggdir, dist_data)
self._fix_namespace_packages(egg_info, destination_eggdir)
@staticmethod
def _convert_metadata(zf, destination_eggdir, dist_info, egg_info):
def get_metadata(name):
with zf.open(posixpath.join(dist_info, name)) as fp:
value = fp.read().decode('utf-8') if PY3 else fp.read()
return email.parser.Parser().parsestr(value)
wheel_metadata = get_metadata('WHEEL')
# Check wheel format version is supported.
wheel_version = parse_version(wheel_metadata.get('Wheel-Version'))
wheel_v1 = (
parse_version('1.0') <= wheel_version < parse_version('2.0dev0')
)
if not wheel_v1:
raise ValueError(
'unsupported wheel format version: %s' % wheel_version)
# Extract to target directory.
os.mkdir(destination_eggdir)
zf.extractall(destination_eggdir)
# Convert metadata.
dist_info = os.path.join(destination_eggdir, dist_info)
dist = Distribution.from_location(
destination_eggdir, dist_info,
metadata=PathMetadata(destination_eggdir, dist_info),
)
# Note: Evaluate and strip markers now,
# as it's difficult to convert back from the syntax:
# foobar; "linux" in sys_platform and extra == 'test'
def raw_req(req):
req.marker = None
return str(req)
install_requires = list(sorted(map(raw_req, dist.requires())))
extras_require = {
extra: sorted(
req
for req in map(raw_req, dist.requires((extra,)))
if req not in install_requires
)
# Note: we need to evaluate and strip markers now,
# as we can't easily convert back from the syntax:
# foobar; "linux" in sys_platform and extra == 'test'
def raw_req(req):
req.marker = None
return str(req)
install_requires = list(sorted(map(raw_req, dist.requires())))
extras_require = {
extra: list(sorted(
req
for req in map(raw_req, dist.requires((extra,)))
if req not in install_requires
))
for extra in dist.extras
}
egg_info = os.path.join(destination_eggdir, 'EGG-INFO')
os.rename(dist_info, egg_info)
os.rename(os.path.join(egg_info, 'METADATA'),
os.path.join(egg_info, 'PKG-INFO'))
setup_dist = SetuptoolsDistribution(attrs=dict(
for extra in dist.extras
}
os.rename(dist_info, egg_info)
os.rename(
os.path.join(egg_info, 'METADATA'),
os.path.join(egg_info, 'PKG-INFO'),
)
setup_dist = SetuptoolsDistribution(
attrs=dict(
install_requires=install_requires,
extras_require=extras_require,
))
write_requirements(setup_dist.get_command_obj('egg_info'),
None, os.path.join(egg_info, 'requires.txt'))
# Move data entries to their correct location.
dist_data = os.path.join(destination_eggdir, dist_data)
dist_data_scripts = os.path.join(dist_data, 'scripts')
if os.path.exists(dist_data_scripts):
egg_info_scripts = os.path.join(destination_eggdir,
'EGG-INFO', 'scripts')
os.mkdir(egg_info_scripts)
for entry in os.listdir(dist_data_scripts):
# Remove bytecode, as it's not properly handled
# during easy_install scripts install phase.
if entry.endswith('.pyc'):
os.unlink(os.path.join(dist_data_scripts, entry))
else:
os.rename(os.path.join(dist_data_scripts, entry),
os.path.join(egg_info_scripts, entry))
os.rmdir(dist_data_scripts)
for subdir in filter(os.path.exists, (
os.path.join(dist_data, d)
for d in ('data', 'headers', 'purelib', 'platlib')
)):
unpack(subdir, destination_eggdir)
if os.path.exists(dist_data):
os.rmdir(dist_data)
# Fix namespace packages.
namespace_packages = os.path.join(egg_info, 'namespace_packages.txt')
if os.path.exists(namespace_packages):
with open(namespace_packages) as fp:
namespace_packages = fp.read().split()
for mod in namespace_packages:
mod_dir = os.path.join(destination_eggdir, *mod.split('.'))
mod_init = os.path.join(mod_dir, '__init__.py')
if os.path.exists(mod_dir) and not os.path.exists(mod_init):
with open(mod_init, 'w') as fp:
fp.write(NAMESPACE_PACKAGE_INIT)
),
)
write_requirements(
setup_dist.get_command_obj('egg_info'),
None,
os.path.join(egg_info, 'requires.txt'),
)
@staticmethod
def _move_data_entries(destination_eggdir, dist_data):
"""Move data entries to their correct location."""
dist_data = os.path.join(destination_eggdir, dist_data)
dist_data_scripts = os.path.join(dist_data, 'scripts')
if os.path.exists(dist_data_scripts):
egg_info_scripts = os.path.join(
destination_eggdir, 'EGG-INFO', 'scripts')
os.mkdir(egg_info_scripts)
for entry in os.listdir(dist_data_scripts):
# Remove bytecode, as it's not properly handled
# during easy_install scripts install phase.
if entry.endswith('.pyc'):
os.unlink(os.path.join(dist_data_scripts, entry))
else:
os.rename(
os.path.join(dist_data_scripts, entry),
os.path.join(egg_info_scripts, entry),
)
os.rmdir(dist_data_scripts)
for subdir in filter(os.path.exists, (
os.path.join(dist_data, d)
for d in ('data', 'headers', 'purelib', 'platlib')
)):
unpack(subdir, destination_eggdir)
if os.path.exists(dist_data):
os.rmdir(dist_data)
@staticmethod
def _fix_namespace_packages(egg_info, destination_eggdir):
namespace_packages = os.path.join(
egg_info, 'namespace_packages.txt')
if os.path.exists(namespace_packages):
with open(namespace_packages) as fp:
namespace_packages = fp.read().split()
for mod in namespace_packages:
mod_dir = os.path.join(destination_eggdir, *mod.split('.'))
mod_init = os.path.join(mod_dir, '__init__.py')
if os.path.exists(mod_dir) and not os.path.exists(mod_init):
with open(mod_init, 'w') as fp:
fp.write(NAMESPACE_PACKAGE_INIT)