mirror of
https://github.com/pypa/pip
synced 2023-12-13 21:30:23 +01:00
updated distlib to 0.2.2
This fixes an issue with tox/virtualenv/pip where the pip and wheel
shebang lines were wrapped with a quote " that prevents the command
from executing.
66e4805271/distlib/scripts.py
?at=default&fileviewer=file-view-default#scripts.py-66
I confirmed that with the updated distlib I was able to create a
virtualenv, and then install the updated `pip` into it.
```
virtualenv --no-wheel --no-pip --no-setuptools --python=jython jython.env
jython -c 'import sys, pip; sys.exit(pip.main(["install", "--ignore-installed"] + sys.argv[1:]))' pip
```
```
$ pip list
decorator (4.0.9)
ipython (4.1.1)
ipython-genutils (0.1.0)
path.py (8.1.2)
pexpect (4.0.1)
pickleshare (0.6)
pip (8.0.2)
ptyprocess (0.5.1)
py (1.4.31)
pytest (2.8.7)
requests (2.9.1)
setuptools (20.0)
simplegeneric (0.8.1)
simplejson (3.8.1)
six (1.10.0)
traitlets (4.1.0)
wheel (0.29.0)
```
Aside: The process for tracking and updating _vendor libs should be streamlined.
The Makefile blows away modified code, making it a manual process to update
vendored libs.
This commit is contained in:
parent
61f75c40ad
commit
d7ee5a639f
|
@ -1,12 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2012-2014 Vinay Sajip.
|
||||
# Copyright (C) 2012-2016 Vinay Sajip.
|
||||
# Licensed to the Python Software Foundation under a contributor agreement.
|
||||
# See LICENSE.txt and CONTRIBUTORS.txt.
|
||||
#
|
||||
import logging
|
||||
|
||||
__version__ = '0.2.1'
|
||||
__version__ = '0.2.2'
|
||||
|
||||
class DistlibException(Exception):
|
||||
pass
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2013-2014 Vinay Sajip.
|
||||
# Copyright (C) 2013-2016 Vinay Sajip.
|
||||
# Licensed to the Python Software Foundation under a contributor agreement.
|
||||
# See LICENSE.txt and CONTRIBUTORS.txt.
|
||||
#
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2012-2014 The Python Software Foundation.
|
||||
# Copyright (C) 2012-2016 The Python Software Foundation.
|
||||
# See LICENSE.txt and CONTRIBUTORS.txt.
|
||||
#
|
||||
"""PEP 376 implementation."""
|
||||
|
@ -20,7 +20,7 @@ import zipimport
|
|||
from . import DistlibException, resources
|
||||
from .compat import StringIO
|
||||
from .version import get_scheme, UnsupportedVersionError
|
||||
from .metadata import Metadata, METADATA_FILENAME
|
||||
from .metadata import Metadata, METADATA_FILENAME, WHEEL_METADATA_FILENAME
|
||||
from .util import (parse_requirement, cached_property, parse_name_and_version,
|
||||
read_exports, write_exports, CSVReader, CSVWriter)
|
||||
|
||||
|
@ -132,13 +132,17 @@ class DistributionPath(object):
|
|||
if not r or r.path in seen:
|
||||
continue
|
||||
if self._include_dist and entry.endswith(DISTINFO_EXT):
|
||||
metadata_path = posixpath.join(entry, METADATA_FILENAME)
|
||||
pydist = finder.find(metadata_path)
|
||||
if not pydist:
|
||||
possible_filenames = [METADATA_FILENAME, WHEEL_METADATA_FILENAME]
|
||||
for metadata_filename in possible_filenames:
|
||||
metadata_path = posixpath.join(entry, metadata_filename)
|
||||
pydist = finder.find(metadata_path)
|
||||
if pydist:
|
||||
break
|
||||
else:
|
||||
continue
|
||||
|
||||
metadata = Metadata(fileobj=pydist.as_stream(),
|
||||
scheme='legacy')
|
||||
with contextlib.closing(pydist.as_stream()) as stream:
|
||||
metadata = Metadata(fileobj=stream, scheme='legacy')
|
||||
logger.debug('Found %s', r.path)
|
||||
seen.add(r.path)
|
||||
yield new_dist_class(r.path, metadata=metadata,
|
||||
|
@ -532,6 +536,9 @@ class InstalledDistribution(BaseInstalledDistribution):
|
|||
metadata = env._cache.path[path].metadata
|
||||
elif metadata is None:
|
||||
r = finder.find(METADATA_FILENAME)
|
||||
# Temporary - for Wheel 0.23 support
|
||||
if r is None:
|
||||
r = finder.find(WHEEL_METADATA_FILENAME)
|
||||
# Temporary - for legacy support
|
||||
if r is None:
|
||||
r = finder.find('METADATA')
|
||||
|
|
|
@ -14,7 +14,7 @@ import posixpath
|
|||
import re
|
||||
try:
|
||||
import threading
|
||||
except ImportError:
|
||||
except ImportError: # pragma: no cover
|
||||
import dummy_threading as threading
|
||||
import zlib
|
||||
|
||||
|
@ -36,7 +36,7 @@ logger = logging.getLogger(__name__)
|
|||
HASHER_HASH = re.compile('^(\w+)=([a-f0-9]+)')
|
||||
CHARSET = re.compile(r';\s*charset\s*=\s*(.*)\s*$', re.I)
|
||||
HTML_CONTENT_TYPE = re.compile('text/html|application/x(ht)?ml')
|
||||
DEFAULT_INDEX = 'http://python.org/pypi'
|
||||
DEFAULT_INDEX = 'https://pypi.python.org/pypi'
|
||||
|
||||
def get_all_distribution_names(url=None):
|
||||
"""
|
||||
|
@ -354,7 +354,7 @@ class Locator(object):
|
|||
else:
|
||||
logger.debug('skipping pre-release '
|
||||
'version %s of %s', k, matcher.name)
|
||||
except Exception:
|
||||
except Exception: # pragma: no cover
|
||||
logger.warning('error matching %s with %r', matcher, k)
|
||||
pass # slist.append(k)
|
||||
if len(slist) > 1:
|
||||
|
@ -763,18 +763,18 @@ class SimpleScrapingLocator(Locator):
|
|||
encoding = m.group(1)
|
||||
try:
|
||||
data = data.decode(encoding)
|
||||
except UnicodeError:
|
||||
except UnicodeError: # pragma: no cover
|
||||
data = data.decode('latin-1') # fallback
|
||||
result = Page(data, final_url)
|
||||
self._page_cache[final_url] = result
|
||||
except HTTPError as e:
|
||||
if e.code != 404:
|
||||
logger.exception('Fetch failed: %s: %s', url, e)
|
||||
except URLError as e:
|
||||
except URLError as e: # pragma: no cover
|
||||
logger.exception('Fetch failed: %s: %s', url, e)
|
||||
with self._lock:
|
||||
self._bad_hosts.add(host)
|
||||
except Exception as e:
|
||||
except Exception as e: # pragma: no cover
|
||||
logger.exception('Fetch failed: %s: %s', url, e)
|
||||
finally:
|
||||
self._page_cache[url] = result # even if None (failure)
|
||||
|
@ -812,7 +812,7 @@ class DirectoryLocator(Locator):
|
|||
self.recursive = kwargs.pop('recursive', True)
|
||||
super(DirectoryLocator, self).__init__(**kwargs)
|
||||
path = os.path.abspath(path)
|
||||
if not os.path.isdir(path):
|
||||
if not os.path.isdir(path): # pragma: no cover
|
||||
raise DistlibException('Not a directory: %r' % path)
|
||||
self.base_dir = path
|
||||
|
||||
|
@ -1083,7 +1083,7 @@ class DependencyFinder(object):
|
|||
"""
|
||||
try:
|
||||
matcher = self.scheme.matcher(reqt)
|
||||
except UnsupportedVersionError:
|
||||
except UnsupportedVersionError: # pragma: no cover
|
||||
# XXX compat-mode if cannot read the version
|
||||
name = reqt.split()[0]
|
||||
matcher = self.scheme.matcher(name)
|
||||
|
|
|
@ -50,7 +50,8 @@ PKG_INFO_ENCODING = 'utf-8'
|
|||
# to 1.2 once PEP 345 is supported everywhere
|
||||
PKG_INFO_PREFERRED_VERSION = '1.1'
|
||||
|
||||
_LINE_PREFIX = re.compile('\n \|')
|
||||
_LINE_PREFIX_1_2 = re.compile('\n \|')
|
||||
_LINE_PREFIX_PRE_1_2 = re.compile('\n ')
|
||||
_241_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform',
|
||||
'Summary', 'Description',
|
||||
'Keywords', 'Home-page', 'Author', 'Author-email',
|
||||
|
@ -295,7 +296,10 @@ class LegacyMetadata(object):
|
|||
return 'UNKNOWN'
|
||||
|
||||
def _remove_line_prefix(self, value):
|
||||
return _LINE_PREFIX.sub('\n', value)
|
||||
if self.metadata_version in ('1.0', '1.1'):
|
||||
return _LINE_PREFIX_PRE_1_2.sub('\n', value)
|
||||
else:
|
||||
return _LINE_PREFIX_1_2.sub('\n', value)
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name in _ATTR2FIELD:
|
||||
|
@ -374,7 +378,10 @@ class LegacyMetadata(object):
|
|||
continue
|
||||
if field not in _LISTFIELDS:
|
||||
if field == 'Description':
|
||||
values = values.replace('\n', '\n |')
|
||||
if self.metadata_version in ('1.0', '1.1'):
|
||||
values = values.replace('\n', '\n ')
|
||||
else:
|
||||
values = values.replace('\n', '\n |')
|
||||
values = [values]
|
||||
|
||||
if field in _LISTTUPLEFIELDS:
|
||||
|
@ -548,7 +555,7 @@ class LegacyMetadata(object):
|
|||
('description', 'Description'),
|
||||
('keywords', 'Keywords'),
|
||||
('platform', 'Platform'),
|
||||
('classifier', 'Classifier'),
|
||||
('classifiers', 'Classifier'),
|
||||
('download_url', 'Download-URL'),
|
||||
)
|
||||
|
||||
|
@ -617,6 +624,7 @@ class LegacyMetadata(object):
|
|||
|
||||
|
||||
METADATA_FILENAME = 'pydist.json'
|
||||
WHEEL_METADATA_FILENAME = 'metadata.json'
|
||||
|
||||
|
||||
class Metadata(object):
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2013 Vinay Sajip.
|
||||
# Copyright (C) 2013-2016 Vinay Sajip.
|
||||
# Licensed to the Python Software Foundation under a contributor agreement.
|
||||
# See LICENSE.txt and CONTRIBUTORS.txt.
|
||||
#
|
||||
|
|
|
@ -63,6 +63,22 @@ if __name__ == '__main__':
|
|||
'''
|
||||
|
||||
|
||||
def _enquote_executable(executable):
|
||||
if ' ' in executable:
|
||||
# make sure we quote only the executable in case of env
|
||||
# for example /usr/bin/env "/dir with spaces/bin/jython"
|
||||
# instead of "/usr/bin/env /dir with spaces/bin/jython"
|
||||
# otherwise whole
|
||||
if executable.startswith('/usr/bin/env '):
|
||||
env, _executable = executable.split(' ', 1)
|
||||
if ' ' in _executable and not _executable.startswith('"'):
|
||||
executable = '%s "%s"' % (env, _executable)
|
||||
else:
|
||||
if not executable.startswith('"'):
|
||||
executable = '"%s"' % executable
|
||||
return executable
|
||||
|
||||
|
||||
class ScriptMaker(object):
|
||||
"""
|
||||
A class to copy or create scripts from source scripts or callable
|
||||
|
@ -85,8 +101,11 @@ class ScriptMaker(object):
|
|||
self.variants = set(('', 'X.Y'))
|
||||
self._fileop = fileop or FileOperator(dry_run)
|
||||
|
||||
self._is_nt = os.name == 'nt' or (
|
||||
os.name == 'java' and os._name == 'nt')
|
||||
|
||||
def _get_alternate_executable(self, executable, options):
|
||||
if options.get('gui', False) and os.name == 'nt':
|
||||
if options.get('gui', False) and self._is_nt: # pragma: no cover
|
||||
dn, fn = os.path.split(executable)
|
||||
fn = fn.replace('python', 'pythonw')
|
||||
executable = os.path.join(dn, fn)
|
||||
|
@ -124,10 +143,10 @@ class ScriptMaker(object):
|
|||
enquote = False # assume this will be taken care of
|
||||
elif not sysconfig.is_python_build():
|
||||
executable = get_executable()
|
||||
elif in_venv():
|
||||
elif in_venv(): # pragma: no cover
|
||||
executable = os.path.join(sysconfig.get_path('scripts'),
|
||||
'python%s' % sysconfig.get_config_var('EXE'))
|
||||
else:
|
||||
else: # pragma: no cover
|
||||
executable = os.path.join(
|
||||
sysconfig.get_config_var('BINDIR'),
|
||||
'python%s%s' % (sysconfig.get_config_var('VERSION'),
|
||||
|
@ -141,14 +160,14 @@ class ScriptMaker(object):
|
|||
executable = os.path.normcase(executable)
|
||||
# If the user didn't specify an executable, it may be necessary to
|
||||
# cater for executable paths with spaces (not uncommon on Windows)
|
||||
if enquote and ' ' in executable:
|
||||
executable = '"%s"' % executable
|
||||
if enquote:
|
||||
executable = _enquote_executable(executable)
|
||||
# Issue #51: don't use fsencode, since we later try to
|
||||
# check that the shebang is decodable using utf-8.
|
||||
executable = executable.encode('utf-8')
|
||||
# in case of IronPython, play safe and enable frames support
|
||||
if (sys.platform == 'cli' and '-X:Frames' not in post_interp
|
||||
and '-X:FullFrames' not in post_interp):
|
||||
and '-X:FullFrames' not in post_interp): # pragma: no cover
|
||||
post_interp += b' -X:Frames'
|
||||
shebang = b'#!' + executable + post_interp + b'\n'
|
||||
# Python parser starts to read a script using UTF-8 until
|
||||
|
@ -158,7 +177,7 @@ class ScriptMaker(object):
|
|||
# UTF-8.
|
||||
try:
|
||||
shebang.decode('utf-8')
|
||||
except UnicodeDecodeError:
|
||||
except UnicodeDecodeError: # pragma: no cover
|
||||
raise ValueError(
|
||||
'The shebang (%r) is not decodable from utf-8' % shebang)
|
||||
# If the script is encoded to a custom encoding (use a
|
||||
|
@ -167,7 +186,7 @@ class ScriptMaker(object):
|
|||
if encoding != 'utf-8':
|
||||
try:
|
||||
shebang.decode(encoding)
|
||||
except UnicodeDecodeError:
|
||||
except UnicodeDecodeError: # pragma: no cover
|
||||
raise ValueError(
|
||||
'The shebang (%r) is not decodable '
|
||||
'from the script encoding (%r)' % (shebang, encoding))
|
||||
|
@ -184,11 +203,11 @@ class ScriptMaker(object):
|
|||
return self.manifest % base
|
||||
|
||||
def _write_script(self, names, shebang, script_bytes, filenames, ext):
|
||||
use_launcher = self.add_launchers and os.name == 'nt'
|
||||
use_launcher = self.add_launchers and self._is_nt
|
||||
linesep = os.linesep.encode('utf-8')
|
||||
if not use_launcher:
|
||||
script_bytes = shebang + linesep + script_bytes
|
||||
else:
|
||||
else: # pragma: no cover
|
||||
if ext == 'py':
|
||||
launcher = self._get_launcher('t')
|
||||
else:
|
||||
|
@ -200,7 +219,7 @@ class ScriptMaker(object):
|
|||
script_bytes = launcher + shebang + linesep + zip_data
|
||||
for name in names:
|
||||
outname = os.path.join(self.target_dir, name)
|
||||
if use_launcher:
|
||||
if use_launcher: # pragma: no cover
|
||||
n, e = os.path.splitext(outname)
|
||||
if e.startswith('.py'):
|
||||
outname = n
|
||||
|
@ -223,7 +242,7 @@ class ScriptMaker(object):
|
|||
except Exception:
|
||||
pass # still in use - ignore error
|
||||
else:
|
||||
if os.name == 'nt' and not outname.endswith('.' + ext):
|
||||
if self._is_nt and not outname.endswith('.' + ext): # pragma: no cover
|
||||
outname = '%s.%s' % (outname, ext)
|
||||
if os.path.exists(outname) and not self.clobber:
|
||||
logger.warning('Skipping existing file %s', outname)
|
||||
|
@ -269,15 +288,13 @@ class ScriptMaker(object):
|
|||
# script.
|
||||
try:
|
||||
f = open(script, 'rb')
|
||||
except IOError:
|
||||
except IOError: # pragma: no cover
|
||||
if not self.dry_run:
|
||||
raise
|
||||
f = None
|
||||
else:
|
||||
encoding, lines = detect_encoding(f.readline)
|
||||
f.seek(0)
|
||||
first_line = f.readline()
|
||||
if not first_line:
|
||||
if not first_line: # pragma: no cover
|
||||
logger.warning('%s: %s is an empty file (skipping)',
|
||||
self.get_command_name(), script)
|
||||
return
|
||||
|
@ -298,8 +315,10 @@ class ScriptMaker(object):
|
|||
logger.info('copying and adjusting %s -> %s', script,
|
||||
self.target_dir)
|
||||
if not self._fileop.dry_run:
|
||||
encoding, lines = detect_encoding(f.readline)
|
||||
f.seek(0)
|
||||
shebang = self._get_shebang(encoding, post_interp)
|
||||
if b'pythonw' in first_line:
|
||||
if b'pythonw' in first_line: # pragma: no cover
|
||||
ext = 'pyw'
|
||||
else:
|
||||
ext = 'py'
|
||||
|
@ -316,7 +335,7 @@ class ScriptMaker(object):
|
|||
def dry_run(self, value):
|
||||
self._fileop.dry_run = value
|
||||
|
||||
if os.name == 'nt':
|
||||
if os.name == 'nt' or (os.name == 'java' and os._name == 'nt'): # pragma: no cover
|
||||
# Executable launcher support.
|
||||
# Launchers are from https://bitbucket.org/vinay.sajip/simple_launcher/
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# Copyright (C) 2012-2014 The Python Software Foundation.
|
||||
# Copyright (C) 2012-2016 The Python Software Foundation.
|
||||
# See LICENSE.txt and CONTRIBUTORS.txt.
|
||||
#
|
||||
import codecs
|
||||
|
@ -20,6 +20,8 @@ import subprocess
|
|||
import sys
|
||||
import tarfile
|
||||
import tempfile
|
||||
import textwrap
|
||||
|
||||
try:
|
||||
import threading
|
||||
except ImportError:
|
||||
|
@ -28,8 +30,8 @@ import time
|
|||
|
||||
from . import DistlibException
|
||||
from .compat import (string_types, text_type, shutil, raw_input, StringIO,
|
||||
cache_from_source, urlopen, httplib, xmlrpclib, splittype,
|
||||
HTTPHandler, HTTPSHandler as BaseHTTPSHandler,
|
||||
cache_from_source, urlopen, urljoin, httplib, xmlrpclib,
|
||||
splittype, HTTPHandler, HTTPSHandler as BaseHTTPSHandler,
|
||||
BaseConfigurator, valid_ident, Container, configparser,
|
||||
URLError, match_hostname, CertificateError, ZipFile)
|
||||
|
||||
|
@ -199,8 +201,8 @@ def read_exports(stream):
|
|||
data = stream.read()
|
||||
stream = StringIO(data)
|
||||
try:
|
||||
data = json.load(stream)
|
||||
result = data['extensions']['python.exports']['exports']
|
||||
jdata = json.load(stream)
|
||||
result = jdata['extensions']['python.exports']['exports']
|
||||
for group, entries in result.items():
|
||||
for k, v in entries.items():
|
||||
s = '%s = %s' % (k, v)
|
||||
|
@ -210,11 +212,22 @@ def read_exports(stream):
|
|||
return result
|
||||
except Exception:
|
||||
stream.seek(0, 0)
|
||||
|
||||
def read_stream(cp, stream):
|
||||
if hasattr(cp, 'read_file'):
|
||||
cp.read_file(stream)
|
||||
else:
|
||||
cp.readfp(stream)
|
||||
|
||||
cp = configparser.ConfigParser()
|
||||
if hasattr(cp, 'read_file'):
|
||||
cp.read_file(stream)
|
||||
else:
|
||||
cp.readfp(stream)
|
||||
try:
|
||||
read_stream(cp, stream)
|
||||
except configparser.MissingSectionHeaderError:
|
||||
stream.close()
|
||||
data = textwrap.dedent(data)
|
||||
stream = StringIO(data)
|
||||
read_stream(cp, stream)
|
||||
|
||||
result = {}
|
||||
for key in cp.sections():
|
||||
result[key] = entries = {}
|
||||
|
@ -758,16 +771,17 @@ def _get_external_data(url):
|
|||
logger.exception('Failed to get external data for %s: %s', url, e)
|
||||
return result
|
||||
|
||||
_external_data_base_url = 'https://www.red-dove.com/pypi/projects/'
|
||||
|
||||
def get_project_data(name):
|
||||
url = ('https://www.red-dove.com/pypi/projects/'
|
||||
'%s/%s/project.json' % (name[0].upper(), name))
|
||||
url = '%s/%s/project.json' % (name[0].upper(), name)
|
||||
url = urljoin(_external_data_base_url, url)
|
||||
result = _get_external_data(url)
|
||||
return result
|
||||
|
||||
def get_package_data(name, version):
|
||||
url = ('https://www.red-dove.com/pypi/projects/'
|
||||
'%s/%s/package-%s.json' % (name[0].upper(), name, version))
|
||||
url = '%s/%s/package-%s.json' % (name[0].upper(), name, version)
|
||||
url = urljoin(_external_data_base_url, url)
|
||||
return _get_external_data(url)
|
||||
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2012-2014 The Python Software Foundation.
|
||||
# Copyright (C) 2012-2016 The Python Software Foundation.
|
||||
# See LICENSE.txt and CONTRIBUTORS.txt.
|
||||
#
|
||||
"""
|
||||
Implementation of a flexible versioning scheme providing support for PEP-386,
|
||||
distribute-compatible and semantic versioning.
|
||||
Implementation of a flexible versioning scheme providing support for PEP-440,
|
||||
setuptools-compatible and semantic versioning.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2013-2014 Vinay Sajip.
|
||||
# Copyright (C) 2013-2016 Vinay Sajip.
|
||||
# Licensed to the Python Software Foundation under a contributor agreement.
|
||||
# See LICENSE.txt and CONTRIBUTORS.txt.
|
||||
#
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
distlib==0.2.1
|
||||
distlib==0.2.2
|
||||
html5lib==1.0b8
|
||||
six==1.10.0
|
||||
colorama==0.3.6
|
||||
|
|
Loading…
Reference in a new issue