summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDonald Stufft <donald@stufft.io>2014-05-03 01:13:28 -0400
committerDonald Stufft <donald@stufft.io>2014-05-03 01:13:28 -0400
commit0e72f65bf4e5ea6b8fd17d02fe4cae0ba7da5113 (patch)
tree5c04afcabe3c7087e38b8114b19de437f2cf4ab3
parentbdef6cebfb4e92a388cf84ed55cbd4ead8ef269c (diff)
parent93098d0c6781dd7019dfa43d9030f14b28f1bf2d (diff)
downloadpip-0e72f65bf4e5ea6b8fd17d02fe4cae0ba7da5113.tar.gz
Merge branch '1.5.X'1.5.5
Conflicts: .travis/py34.sh CHANGES.txt PROJECT.txt
-rw-r--r--.travis.yml6
-rwxr-xr-x.travis/py34.sh4
-rw-r--r--CHANGES.txt14
-rw-r--r--PROJECT.txt6
-rw-r--r--README.rst4
-rw-r--r--docs/installing.rst2
-rwxr-xr-xpip/__init__.py2
-rw-r--r--pip/_vendor/README.rst2
-rw-r--r--pip/_vendor/colorama/__init__.py2
-rw-r--r--pip/_vendor/colorama/ansi.py100
-rw-r--r--pip/_vendor/colorama/ansitowin32.py379
-rw-r--r--pip/_vendor/colorama/initialise.py112
-rw-r--r--pip/_vendor/colorama/win32.py7
-rw-r--r--pip/_vendor/colorama/winterm.py240
-rw-r--r--pip/_vendor/distlib/__init__.py5
-rw-r--r--pip/_vendor/distlib/_backport/sysconfig.py2
-rw-r--r--pip/_vendor/distlib/index.py16
-rw-r--r--pip/_vendor/distlib/locators.py33
-rw-r--r--pip/_vendor/distlib/metadata.py13
-rw-r--r--pip/_vendor/distlib/resources.py50
-rw-r--r--pip/_vendor/distlib/scripts.py6
-rw-r--r--pip/_vendor/distlib/util.py51
-rw-r--r--pip/_vendor/distlib/version.py35
-rw-r--r--pip/_vendor/distlib/wheel.py325
-rw-r--r--pip/_vendor/html5lib/LICENSE20
-rw-r--r--pip/_vendor/html5lib/__init__.py2
-rw-r--r--pip/_vendor/html5lib/constants.py18
-rw-r--r--pip/_vendor/html5lib/filters/lint.py30
-rw-r--r--pip/_vendor/html5lib/html5parser.py128
-rw-r--r--pip/_vendor/html5lib/inputstream.py10
-rw-r--r--pip/_vendor/html5lib/serializer/htmlserializer.py21
-rw-r--r--pip/_vendor/html5lib/treeadapters/__init__.py0
-rw-r--r--pip/_vendor/html5lib/treeadapters/sax.py44
-rw-r--r--pip/_vendor/html5lib/treebuilders/dom.py65
-rw-r--r--pip/_vendor/html5lib/treewalkers/_base.py122
-rw-r--r--pip/_vendor/html5lib/treewalkers/etree.py9
-rw-r--r--pip/_vendor/html5lib/utils.py4
-rw-r--r--pip/_vendor/pkg_resources.py113
-rw-r--r--pip/_vendor/six.py36
-rw-r--r--pip/_vendor/vendor.txt8
-rw-r--r--pip/wheel.py18
-rw-r--r--setup.py2
-rw-r--r--tests/data/packages/sample-1.2.0-py2.py3-none-any.whlbin0 -> 3581 bytes
-rw-r--r--tests/data/src/sample/MANIFEST.in8
-rw-r--r--tests/data/src/sample/data/data_file1
-rw-r--r--tests/data/src/sample/sample/__init__.py5
-rw-r--r--tests/data/src/sample/sample/package_data.dat1
-rw-r--r--tests/data/src/sample/setup.cfg5
-rw-r--r--tests/data/src/sample/setup.py103
-rw-r--r--tests/functional/test_uninstall.py11
-rw-r--r--tests/functional/test_wheel.py12
-rw-r--r--tests/unit/test_locations.py2
-rw-r--r--tests/unit/test_wheel.py63
-rw-r--r--tox.ini2
54 files changed, 1415 insertions, 864 deletions
diff --git a/.travis.yml b/.travis.yml
index f874c48b1..cbcdbbea3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,12 +13,6 @@ before_install:
- "[ -d ~/.distlib ] && sudo chown -R travis:travis ~/.distlib || true"
install:
- - "if [[ $TOXENV == 'pypy' ]]; then sudo add-apt-repository -y ppa:pypy/ppa; fi"
- - "if [[ $TOXENV == 'pypy' ]]; then sudo apt-get -y update && sudo apt-get -y install pypy; fi"
- # This is required because we need to get rid of the Travis installed PyPy
- # or it'll take precedence over the PPA installed one.
- - "if [[ $TOXENV == 'pypy' ]]; then sudo rm -rf /usr/local/pypy/bin; fi"
- - "if [[ $TOXENV == 'py34' ]]; then .travis/py34.sh; fi"
- sudo apt-get install subversion bzr mercurial
- echo -e "[web]\ncacerts = /etc/ssl/certs/ca-certificates.crt" >> ~/.hgrc
- git config --global user.email "python-virtualenv@googlegroups.com"
diff --git a/.travis/py34.sh b/.travis/py34.sh
deleted file mode 100755
index a5590087a..000000000
--- a/.travis/py34.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-sudo add-apt-repository -y ppa:fkrull/deadsnakes
-sudo apt-get update
-sudo apt-get install python3.4
diff --git a/CHANGES.txt b/CHANGES.txt
index cfcd82308..a902b7739 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,17 @@
+**1.5.5 (2014-05-03)**
+
+* Fixes #1632. Uninstall issues on debianized pypy, specifically issues with
+ setuptools upgrades. (PR #1743)
+
+* Update documentation to point at https://bootstrap.pypa.io/get-pip.py for
+ bootstrapping pip.
+
+* Update docs to point to https://pip.pypa.io/
+
+* Upgrade the bundled projects (distlib==0.1.8, html5lib==1.0b3, six==1.6.1,
+ colorama==0.3.1, setuptools==3.4.4).
+
+
**1.5.4 (2014-02-21)**
diff --git a/PROJECT.txt b/PROJECT.txt
index aecfa331b..60f6c1ede 100644
--- a/PROJECT.txt
+++ b/PROJECT.txt
@@ -2,10 +2,10 @@ Project Info
============
* Project Page: https://github.com/pypa/pip
-* Install howto: http://www.pip-installer.org/en/latest/installing.html
-* Changelog: http://www.pip-installer.org/en/latest/news.html
+* Install howto: https://pip.pypa.io/en/latest/installing.html
+* Changelog: https://pip.pypa.io/en/latest/news.html
* Bug Tracking: https://github.com/pypa/pip/issues
* Mailing list: http://groups.google.com/group/python-virtualenv
-* Docs: http://www.pip-installer.org/
+* Docs: https://pip.pypa.io/
* User IRC: #pypa on Freenode.
* Dev IRC: #pypa-dev on Freenode.
diff --git a/README.rst b/README.rst
index 1c6146af5..5707824d6 100644
--- a/README.rst
+++ b/README.rst
@@ -2,9 +2,9 @@ pip
===
.. image:: https://pypip.in/v/pip/badge.png
- :target: https://crate.io/packages/pip
+ :target: https://pypi.python.org/pypi/pip
.. image:: https://secure.travis-ci.org/pypa/pip.png?branch=develop
:target: http://travis-ci.org/pypa/pip
-For documentation, see http://www.pip-installer.org
+For documentation, see https://pip.pypa.io/
diff --git a/docs/installing.rst b/docs/installing.rst
index 16b925761..ab0083503 100644
--- a/docs/installing.rst
+++ b/docs/installing.rst
@@ -21,7 +21,7 @@ Install pip
-----------
To install or upgrade pip, securely download `get-pip.py
-<https://raw.github.com/pypa/pip/master/contrib/get-pip.py>`_. [1]_
+<https://bootstrap.pypa.io/get-pip.py>`_. [1]_
Then run the following (which may require administrator access)::
diff --git a/pip/__init__.py b/pip/__init__.py
index 9b4e9069d..7a8d69463 100755
--- a/pip/__init__.py
+++ b/pip/__init__.py
@@ -19,7 +19,7 @@ import pip.cmdoptions
cmdoptions = pip.cmdoptions
# The version as used in the setup.py and the docs conf.py
-__version__ = "1.5.4"
+__version__ = "1.5.5"
def autocomplete():
diff --git a/pip/_vendor/README.rst b/pip/_vendor/README.rst
index 0fc983d92..f2c9ee9b4 100644
--- a/pip/_vendor/README.rst
+++ b/pip/_vendor/README.rst
@@ -16,7 +16,7 @@ Modifications
Markerlib and pkg_resources
===========================
-Markerlib and pkg_resources has been pulled in from setuptools 2.1
+Markerlib and pkg_resources has been pulled in from setuptools 3.4.4
Note to Downstream Distributors
diff --git a/pip/_vendor/colorama/__init__.py b/pip/_vendor/colorama/__init__.py
index 2d127fa8e..0856986fa 100644
--- a/pip/_vendor/colorama/__init__.py
+++ b/pip/_vendor/colorama/__init__.py
@@ -3,5 +3,5 @@ from .initialise import init, deinit, reinit
from .ansi import Fore, Back, Style
from .ansitowin32 import AnsiToWin32
-VERSION = '0.2.7'
+__version__ = '0.3.1'
diff --git a/pip/_vendor/colorama/ansi.py b/pip/_vendor/colorama/ansi.py
index fef40571d..5dfe374ce 100644
--- a/pip/_vendor/colorama/ansi.py
+++ b/pip/_vendor/colorama/ansi.py
@@ -1,50 +1,50 @@
-# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
-'''
-This module generates ANSI character codes to printing colors to terminals.
-See: http://en.wikipedia.org/wiki/ANSI_escape_code
-'''
-
-CSI = '\033['
-
-def code_to_chars(code):
- return CSI + str(code) + 'm'
-
-class AnsiCodes(object):
- def __init__(self, codes):
- for name in dir(codes):
- if not name.startswith('_'):
- value = getattr(codes, name)
- setattr(self, name, code_to_chars(value))
-
-class AnsiFore:
- BLACK = 30
- RED = 31
- GREEN = 32
- YELLOW = 33
- BLUE = 34
- MAGENTA = 35
- CYAN = 36
- WHITE = 37
- RESET = 39
-
-class AnsiBack:
- BLACK = 40
- RED = 41
- GREEN = 42
- YELLOW = 43
- BLUE = 44
- MAGENTA = 45
- CYAN = 46
- WHITE = 47
- RESET = 49
-
-class AnsiStyle:
- BRIGHT = 1
- DIM = 2
- NORMAL = 22
- RESET_ALL = 0
-
-Fore = AnsiCodes( AnsiFore )
-Back = AnsiCodes( AnsiBack )
-Style = AnsiCodes( AnsiStyle )
-
+# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
+'''
+This module generates ANSI character codes to printing colors to terminals.
+See: http://en.wikipedia.org/wiki/ANSI_escape_code
+'''
+
+CSI = '\033['
+
+def code_to_chars(code):
+ return CSI + str(code) + 'm'
+
+class AnsiCodes(object):
+ def __init__(self, codes):
+ for name in dir(codes):
+ if not name.startswith('_'):
+ value = getattr(codes, name)
+ setattr(self, name, code_to_chars(value))
+
+class AnsiFore:
+ BLACK = 30
+ RED = 31
+ GREEN = 32
+ YELLOW = 33
+ BLUE = 34
+ MAGENTA = 35
+ CYAN = 36
+ WHITE = 37
+ RESET = 39
+
+class AnsiBack:
+ BLACK = 40
+ RED = 41
+ GREEN = 42
+ YELLOW = 43
+ BLUE = 44
+ MAGENTA = 45
+ CYAN = 46
+ WHITE = 47
+ RESET = 49
+
+class AnsiStyle:
+ BRIGHT = 1
+ DIM = 2
+ NORMAL = 22
+ RESET_ALL = 0
+
+Fore = AnsiCodes( AnsiFore )
+Back = AnsiCodes( AnsiBack )
+Style = AnsiCodes( AnsiStyle )
+
diff --git a/pip/_vendor/colorama/ansitowin32.py b/pip/_vendor/colorama/ansitowin32.py
index d29060187..0053a7758 100644
--- a/pip/_vendor/colorama/ansitowin32.py
+++ b/pip/_vendor/colorama/ansitowin32.py
@@ -1,189 +1,190 @@
-# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
-import re
-import sys
-
-from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style
-from .winterm import WinTerm, WinColor, WinStyle
-from .win32 import windll
-
-
-if windll is not None:
- winterm = WinTerm()
-
-
-def is_a_tty(stream):
- return hasattr(stream, 'isatty') and stream.isatty()
-
-
-class StreamWrapper(object):
- '''
- Wraps a stream (such as stdout), acting as a transparent proxy for all
- attribute access apart from method 'write()', which is delegated to our
- Converter instance.
- '''
- def __init__(self, wrapped, converter):
- # double-underscore everything to prevent clashes with names of
- # attributes on the wrapped stream object.
- self.__wrapped = wrapped
- self.__convertor = converter
-
- def __getattr__(self, name):
- return getattr(self.__wrapped, name)
-
- def write(self, text):
- self.__convertor.write(text)
-
-
-class AnsiToWin32(object):
- '''
- Implements a 'write()' method which, on Windows, will strip ANSI character
- sequences from the text, and if outputting to a tty, will convert them into
- win32 function calls.
- '''
- ANSI_RE = re.compile('\033\[((?:\d|;)*)([a-zA-Z])')
-
- def __init__(self, wrapped, convert=None, strip=None, autoreset=False):
- # The wrapped stream (normally sys.stdout or sys.stderr)
- self.wrapped = wrapped
-
- # should we reset colors to defaults after every .write()
- self.autoreset = autoreset
-
- # create the proxy wrapping our output stream
- self.stream = StreamWrapper(wrapped, self)
-
- on_windows = sys.platform.startswith('win')
-
- # should we strip ANSI sequences from our output?
- if strip is None:
- strip = on_windows
- self.strip = strip
-
- # should we should convert ANSI sequences into win32 calls?
- if convert is None:
- convert = on_windows and is_a_tty(wrapped)
- self.convert = convert
-
- # dict of ansi codes to win32 functions and parameters
- self.win32_calls = self.get_win32_calls()
-
- # are we wrapping stderr?
- self.on_stderr = self.wrapped is sys.stderr
-
-
- def should_wrap(self):
- '''
- True if this class is actually needed. If false, then the output
- stream will not be affected, nor will win32 calls be issued, so
- wrapping stdout is not actually required. This will generally be
- False on non-Windows platforms, unless optional functionality like
- autoreset has been requested using kwargs to init()
- '''
- return self.convert or self.strip or self.autoreset
-
-
- def get_win32_calls(self):
- if self.convert and winterm:
- return {
- AnsiStyle.RESET_ALL: (winterm.reset_all, ),
- AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT),
- AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL),
- AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL),
- AnsiFore.BLACK: (winterm.fore, WinColor.BLACK),
- AnsiFore.RED: (winterm.fore, WinColor.RED),
- AnsiFore.GREEN: (winterm.fore, WinColor.GREEN),
- AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW),
- AnsiFore.BLUE: (winterm.fore, WinColor.BLUE),
- AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA),
- AnsiFore.CYAN: (winterm.fore, WinColor.CYAN),
- AnsiFore.WHITE: (winterm.fore, WinColor.GREY),
- AnsiFore.RESET: (winterm.fore, ),
- AnsiBack.BLACK: (winterm.back, WinColor.BLACK),
- AnsiBack.RED: (winterm.back, WinColor.RED),
- AnsiBack.GREEN: (winterm.back, WinColor.GREEN),
- AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW),
- AnsiBack.BLUE: (winterm.back, WinColor.BLUE),
- AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA),
- AnsiBack.CYAN: (winterm.back, WinColor.CYAN),
- AnsiBack.WHITE: (winterm.back, WinColor.GREY),
- AnsiBack.RESET: (winterm.back, ),
- }
-
-
- def write(self, text):
- if self.strip or self.convert:
- self.write_and_convert(text)
- else:
- self.wrapped.write(text)
- self.wrapped.flush()
- if self.autoreset:
- self.reset_all()
-
-
- def reset_all(self):
- if self.convert:
- self.call_win32('m', (0,))
- elif is_a_tty(self.wrapped):
- self.wrapped.write(Style.RESET_ALL)
-
-
- def write_and_convert(self, text):
- '''
- Write the given text to our wrapped stream, stripping any ANSI
- sequences from the text, and optionally converting them into win32
- calls.
- '''
- cursor = 0
- for match in self.ANSI_RE.finditer(text):
- start, end = match.span()
- self.write_plain_text(text, cursor, start)
- self.convert_ansi(*match.groups())
- cursor = end
- self.write_plain_text(text, cursor, len(text))
-
-
- def write_plain_text(self, text, start, end):
- if start < end:
- self.wrapped.write(text[start:end])
- self.wrapped.flush()
-
-
- def convert_ansi(self, paramstring, command):
- if self.convert:
- params = self.extract_params(paramstring)
- self.call_win32(command, params)
-
-
- def extract_params(self, paramstring):
- def split(paramstring):
- for p in paramstring.split(';'):
- if p != '':
- yield int(p)
- return tuple(split(paramstring))
-
-
- def call_win32(self, command, params):
- if params == []:
- params = [0]
- if command == 'm':
- for param in params:
- if param in self.win32_calls:
- func_args = self.win32_calls[param]
- func = func_args[0]
- args = func_args[1:]
- kwargs = dict(on_stderr=self.on_stderr)
- func(*args, **kwargs)
- elif command in ('H', 'f'): # set cursor position
- func = winterm.set_cursor_position
- func(params, on_stderr=self.on_stderr)
- elif command in ('J'):
- func = winterm.erase_data
- func(params, on_stderr=self.on_stderr)
- elif command == 'A':
- if params == () or params == None:
- num_rows = 1
- else:
- num_rows = params[0]
- func = winterm.cursor_up
- func(num_rows, on_stderr=self.on_stderr)
-
+# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
+import re
+import sys
+
+from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style
+from .winterm import WinTerm, WinColor, WinStyle
+from .win32 import windll
+
+
+winterm = None
+if windll is not None:
+ winterm = WinTerm()
+
+
+def is_a_tty(stream):
+ return hasattr(stream, 'isatty') and stream.isatty()
+
+
+class StreamWrapper(object):
+ '''
+ Wraps a stream (such as stdout), acting as a transparent proxy for all
+ attribute access apart from method 'write()', which is delegated to our
+ Converter instance.
+ '''
+ def __init__(self, wrapped, converter):
+ # double-underscore everything to prevent clashes with names of
+ # attributes on the wrapped stream object.
+ self.__wrapped = wrapped
+ self.__convertor = converter
+
+ def __getattr__(self, name):
+ return getattr(self.__wrapped, name)
+
+ def write(self, text):
+ self.__convertor.write(text)
+
+
+class AnsiToWin32(object):
+ '''
+ Implements a 'write()' method which, on Windows, will strip ANSI character
+ sequences from the text, and if outputting to a tty, will convert them into
+ win32 function calls.
+ '''
+ ANSI_RE = re.compile('\033\[((?:\d|;)*)([a-zA-Z])')
+
+ def __init__(self, wrapped, convert=None, strip=None, autoreset=False):
+ # The wrapped stream (normally sys.stdout or sys.stderr)
+ self.wrapped = wrapped
+
+ # should we reset colors to defaults after every .write()
+ self.autoreset = autoreset
+
+ # create the proxy wrapping our output stream
+ self.stream = StreamWrapper(wrapped, self)
+
+ on_windows = sys.platform.startswith('win')
+
+ # should we strip ANSI sequences from our output?
+ if strip is None:
+ strip = on_windows
+ self.strip = strip
+
+ # should we should convert ANSI sequences into win32 calls?
+ if convert is None:
+ convert = on_windows and is_a_tty(wrapped)
+ self.convert = convert
+
+ # dict of ansi codes to win32 functions and parameters
+ self.win32_calls = self.get_win32_calls()
+
+ # are we wrapping stderr?
+ self.on_stderr = self.wrapped is sys.stderr
+
+
+ def should_wrap(self):
+ '''
+ True if this class is actually needed. If false, then the output
+ stream will not be affected, nor will win32 calls be issued, so
+ wrapping stdout is not actually required. This will generally be
+ False on non-Windows platforms, unless optional functionality like
+ autoreset has been requested using kwargs to init()
+ '''
+ return self.convert or self.strip or self.autoreset
+
+
+ def get_win32_calls(self):
+ if self.convert and winterm:
+ return {
+ AnsiStyle.RESET_ALL: (winterm.reset_all, ),
+ AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT),
+ AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL),
+ AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL),
+ AnsiFore.BLACK: (winterm.fore, WinColor.BLACK),
+ AnsiFore.RED: (winterm.fore, WinColor.RED),
+ AnsiFore.GREEN: (winterm.fore, WinColor.GREEN),
+ AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW),
+ AnsiFore.BLUE: (winterm.fore, WinColor.BLUE),
+ AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA),
+ AnsiFore.CYAN: (winterm.fore, WinColor.CYAN),
+ AnsiFore.WHITE: (winterm.fore, WinColor.GREY),
+ AnsiFore.RESET: (winterm.fore, ),
+ AnsiBack.BLACK: (winterm.back, WinColor.BLACK),
+ AnsiBack.RED: (winterm.back, WinColor.RED),
+ AnsiBack.GREEN: (winterm.back, WinColor.GREEN),
+ AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW),
+ AnsiBack.BLUE: (winterm.back, WinColor.BLUE),
+ AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA),
+ AnsiBack.CYAN: (winterm.back, WinColor.CYAN),
+ AnsiBack.WHITE: (winterm.back, WinColor.GREY),
+ AnsiBack.RESET: (winterm.back, ),
+ }
+
+
+ def write(self, text):
+ if self.strip or self.convert:
+ self.write_and_convert(text)
+ else:
+ self.wrapped.write(text)
+ self.wrapped.flush()
+ if self.autoreset:
+ self.reset_all()
+
+
+ def reset_all(self):
+ if self.convert:
+ self.call_win32('m', (0,))
+ elif not self.wrapped.closed and is_a_tty(self.wrapped):
+ self.wrapped.write(Style.RESET_ALL)
+
+
+ def write_and_convert(self, text):
+ '''
+ Write the given text to our wrapped stream, stripping any ANSI
+ sequences from the text, and optionally converting them into win32
+ calls.
+ '''
+ cursor = 0
+ for match in self.ANSI_RE.finditer(text):
+ start, end = match.span()
+ self.write_plain_text(text, cursor, start)
+ self.convert_ansi(*match.groups())
+ cursor = end
+ self.write_plain_text(text, cursor, len(text))
+
+
+ def write_plain_text(self, text, start, end):
+ if start < end:
+ self.wrapped.write(text[start:end])
+ self.wrapped.flush()
+
+
+ def convert_ansi(self, paramstring, command):
+ if self.convert:
+ params = self.extract_params(paramstring)
+ self.call_win32(command, params)
+
+
+ def extract_params(self, paramstring):
+ def split(paramstring):
+ for p in paramstring.split(';'):
+ if p != '':
+ yield int(p)
+ return tuple(split(paramstring))
+
+
+ def call_win32(self, command, params):
+ if params == []:
+ params = [0]
+ if command == 'm':
+ for param in params:
+ if param in self.win32_calls:
+ func_args = self.win32_calls[param]
+ func = func_args[0]
+ args = func_args[1:]
+ kwargs = dict(on_stderr=self.on_stderr)
+ func(*args, **kwargs)
+ elif command in ('H', 'f'): # set cursor position
+ func = winterm.set_cursor_position
+ func(params, on_stderr=self.on_stderr)
+ elif command in ('J'):
+ func = winterm.erase_data
+ func(params, on_stderr=self.on_stderr)
+ elif command == 'A':
+ if params == () or params == None:
+ num_rows = 1
+ else:
+ num_rows = params[0]
+ func = winterm.cursor_up
+ func(num_rows, on_stderr=self.on_stderr)
+
diff --git a/pip/_vendor/colorama/initialise.py b/pip/_vendor/colorama/initialise.py
index 7c8e77611..cba3676dd 100644
--- a/pip/_vendor/colorama/initialise.py
+++ b/pip/_vendor/colorama/initialise.py
@@ -1,56 +1,56 @@
-# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
-import atexit
-import sys
-
-from .ansitowin32 import AnsiToWin32
-
-
-orig_stdout = sys.stdout
-orig_stderr = sys.stderr
-
-wrapped_stdout = sys.stdout
-wrapped_stderr = sys.stderr
-
-atexit_done = False
-
-
-def reset_all():
- AnsiToWin32(orig_stdout).reset_all()
-
-
-def init(autoreset=False, convert=None, strip=None, wrap=True):
-
- if not wrap and any([autoreset, convert, strip]):
- raise ValueError('wrap=False conflicts with any other arg=True')
-
- global wrapped_stdout, wrapped_stderr
- sys.stdout = wrapped_stdout = \
- wrap_stream(orig_stdout, convert, strip, autoreset, wrap)
- sys.stderr = wrapped_stderr = \
- wrap_stream(orig_stderr, convert, strip, autoreset, wrap)
-
- global atexit_done
- if not atexit_done:
- atexit.register(reset_all)
- atexit_done = True
-
-
-def deinit():
- sys.stdout = orig_stdout
- sys.stderr = orig_stderr
-
-
-def reinit():
- sys.stdout = wrapped_stdout
- sys.stderr = wrapped_stdout
-
-
-def wrap_stream(stream, convert, strip, autoreset, wrap):
- if wrap:
- wrapper = AnsiToWin32(stream,
- convert=convert, strip=strip, autoreset=autoreset)
- if wrapper.should_wrap():
- stream = wrapper.stream
- return stream
-
-
+# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
+import atexit
+import sys
+
+from .ansitowin32 import AnsiToWin32
+
+
+orig_stdout = sys.stdout
+orig_stderr = sys.stderr
+
+wrapped_stdout = sys.stdout
+wrapped_stderr = sys.stderr
+
+atexit_done = False
+
+
+def reset_all():
+ AnsiToWin32(orig_stdout).reset_all()
+
+
+def init(autoreset=False, convert=None, strip=None, wrap=True):
+
+ if not wrap and any([autoreset, convert, strip]):
+ raise ValueError('wrap=False conflicts with any other arg=True')
+
+ global wrapped_stdout, wrapped_stderr
+ sys.stdout = wrapped_stdout = \
+ wrap_stream(orig_stdout, convert, strip, autoreset, wrap)
+ sys.stderr = wrapped_stderr = \
+ wrap_stream(orig_stderr, convert, strip, autoreset, wrap)
+
+ global atexit_done
+ if not atexit_done:
+ atexit.register(reset_all)
+ atexit_done = True
+
+
+def deinit():
+ sys.stdout = orig_stdout
+ sys.stderr = orig_stderr
+
+
+def reinit():
+ sys.stdout = wrapped_stdout
+ sys.stderr = wrapped_stdout
+
+
+def wrap_stream(stream, convert, strip, autoreset, wrap):
+ if wrap:
+ wrapper = AnsiToWin32(stream,
+ convert=convert, strip=strip, autoreset=autoreset)
+ if wrapper.should_wrap():
+ stream = wrapper.stream
+ return stream
+
+
diff --git a/pip/_vendor/colorama/win32.py b/pip/_vendor/colorama/win32.py
index f4024f95e..5203e791d 100644
--- a/pip/_vendor/colorama/win32.py
+++ b/pip/_vendor/colorama/win32.py
@@ -4,10 +4,13 @@
STDOUT = -11
STDERR = -12
+import ctypes
+from ctypes import LibraryLoader
+
try:
- from ctypes import windll
+ windll = LibraryLoader(ctypes.WinDLL)
from ctypes import wintypes
-except ImportError:
+except (AttributeError, ImportError):
windll = None
SetConsoleTextAttribute = lambda *_: None
else:
diff --git a/pip/_vendor/colorama/winterm.py b/pip/_vendor/colorama/winterm.py
index 4a711fde9..270881154 100644
--- a/pip/_vendor/colorama/winterm.py
+++ b/pip/_vendor/colorama/winterm.py
@@ -1,120 +1,120 @@
-# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
-from . import win32
-
-
-# from wincon.h
-class WinColor(object):
- BLACK = 0
- BLUE = 1
- GREEN = 2
- CYAN = 3
- RED = 4
- MAGENTA = 5
- YELLOW = 6
- GREY = 7
-
-# from wincon.h
-class WinStyle(object):
- NORMAL = 0x00 # dim text, dim background
- BRIGHT = 0x08 # bright text, dim background
-
-
-class WinTerm(object):
-
- def __init__(self):
- self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes
- self.set_attrs(self._default)
- self._default_fore = self._fore
- self._default_back = self._back
- self._default_style = self._style
-
- def get_attrs(self):
- return self._fore + self._back * 16 + self._style
-
- def set_attrs(self, value):
- self._fore = value & 7
- self._back = (value >> 4) & 7
- self._style = value & WinStyle.BRIGHT
-
- def reset_all(self, on_stderr=None):
- self.set_attrs(self._default)
- self.set_console(attrs=self._default)
-
- def fore(self, fore=None, on_stderr=False):
- if fore is None:
- fore = self._default_fore
- self._fore = fore
- self.set_console(on_stderr=on_stderr)
-
- def back(self, back=None, on_stderr=False):
- if back is None:
- back = self._default_back
- self._back = back
- self.set_console(on_stderr=on_stderr)
-
- def style(self, style=None, on_stderr=False):
- if style is None:
- style = self._default_style
- self._style = style
- self.set_console(on_stderr=on_stderr)
-
- def set_console(self, attrs=None, on_stderr=False):
- if attrs is None:
- attrs = self.get_attrs()
- handle = win32.STDOUT
- if on_stderr:
- handle = win32.STDERR
- win32.SetConsoleTextAttribute(handle, attrs)
-
- def get_position(self, handle):
- position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition
- # Because Windows coordinates are 0-based,
- # and win32.SetConsoleCursorPosition expects 1-based.
- position.X += 1
- position.Y += 1
- return position
-
- def set_cursor_position(self, position=None, on_stderr=False):
- if position is None:
- #I'm not currently tracking the position, so there is no default.
- #position = self.get_position()
- return
- handle = win32.STDOUT
- if on_stderr:
- handle = win32.STDERR
- win32.SetConsoleCursorPosition(handle, position)
-
- def cursor_up(self, num_rows=0, on_stderr=False):
- if num_rows == 0:
- return
- handle = win32.STDOUT
- if on_stderr:
- handle = win32.STDERR
- position = self.get_position(handle)
- adjusted_position = (position.Y - num_rows, position.X)
- self.set_cursor_position(adjusted_position, on_stderr)
-
- def erase_data(self, mode=0, on_stderr=False):
- # 0 (or None) should clear from the cursor to the end of the screen.
- # 1 should clear from the cursor to the beginning of the screen.
- # 2 should clear the entire screen. (And maybe move cursor to (1,1)?)
- #
- # At the moment, I only support mode 2. From looking at the API, it
- # should be possible to calculate a different number of bytes to clear,
- # and to do so relative to the cursor position.
- if mode[0] not in (2,):
- return
- handle = win32.STDOUT
- if on_stderr:
- handle = win32.STDERR
- # here's where we'll home the cursor
- coord_screen = win32.COORD(0,0)
- csbi = win32.GetConsoleScreenBufferInfo(handle)
- # get the number of character cells in the current buffer
- dw_con_size = csbi.dwSize.X * csbi.dwSize.Y
- # fill the entire screen with blanks
- win32.FillConsoleOutputCharacter(handle, ' ', dw_con_size, coord_screen)
- # now set the buffer's attributes accordingly
- win32.FillConsoleOutputAttribute(handle, self.get_attrs(), dw_con_size, coord_screen );
- # put the cursor at (0, 0)
- win32.SetConsoleCursorPosition(handle, (coord_screen.X, coord_screen.Y))
+# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
+from . import win32
+
+
+# from wincon.h
+class WinColor(object):
+ BLACK = 0
+ BLUE = 1
+ GREEN = 2
+ CYAN = 3
+ RED = 4
+ MAGENTA = 5
+ YELLOW = 6
+ GREY = 7
+
+# from wincon.h
+class WinStyle(object):
+ NORMAL = 0x00 # dim text, dim background
+ BRIGHT = 0x08 # bright text, dim background
+
+
+class WinTerm(object):
+
+ def __init__(self):
+ self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes
+ self.set_attrs(self._default)
+ self._default_fore = self._fore
+ self._default_back = self._back
+ self._default_style = self._style
+
+ def get_attrs(self):
+ return self._fore + self._back * 16 + self._style
+
+ def set_attrs(self, value):
+ self._fore = value & 7
+ self._back = (value >> 4) & 7
+ self._style = value & WinStyle.BRIGHT
+
+ def reset_all(self, on_stderr=None):
+ self.set_attrs(self._default)
+ self.set_console(attrs=self._default)
+
+ def fore(self, fore=None, on_stderr=False):
+ if fore is None:
+ fore = self._default_fore
+ self._fore = fore
+ self.set_console(on_stderr=on_stderr)
+
+ def back(self, back=None, on_stderr=False):
+ if back is None:
+ back = self._default_back
+ self._back = back
+ self.set_console(on_stderr=on_stderr)
+
+ def style(self, style=None, on_stderr=False):
+ if style is None:
+ style = self._default_style
+ self._style = style
+ self.set_console(on_stderr=on_stderr)
+
+ def set_console(self, attrs=None, on_stderr=False):
+ if attrs is None:
+ attrs = self.get_attrs()
+ handle = win32.STDOUT
+ if on_stderr:
+ handle = win32.STDERR
+ win32.SetConsoleTextAttribute(handle, attrs)
+
+ def get_position(self, handle):
+ position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition
+ # Because Windows coordinates are 0-based,
+ # and win32.SetConsoleCursorPosition expects 1-based.
+ position.X += 1
+ position.Y += 1
+ return position
+
+ def set_cursor_position(self, position=None, on_stderr=False):
+ if position is None:
+ #I'm not currently tracking the position, so there is no default.
+ #position = self.get_position()
+ return
+ handle = win32.STDOUT
+ if on_stderr:
+ handle = win32.STDERR
+ win32.SetConsoleCursorPosition(handle, position)
+
+ def cursor_up(self, num_rows=0, on_stderr=False):
+ if num_rows == 0:
+ return
+ handle = win32.STDOUT
+ if on_stderr:
+ handle = win32.STDERR
+ position = self.get_position(handle)
+ adjusted_position = (position.Y - num_rows, position.X)
+ self.set_cursor_position(adjusted_position, on_stderr)
+
+ def erase_data(self, mode=0, on_stderr=False):
+ # 0 (or None) should clear from the cursor to the end of the screen.
+ # 1 should clear from the cursor to the beginning of the screen.
+ # 2 should clear the entire screen. (And maybe move cursor to (1,1)?)
+ #
+ # At the moment, I only support mode 2. From looking at the API, it
+ # should be possible to calculate a different number of bytes to clear,
+ # and to do so relative to the cursor position.
+ if mode[0] not in (2,):
+ return
+ handle = win32.STDOUT
+ if on_stderr:
+ handle = win32.STDERR
+ # here's where we'll home the cursor
+ coord_screen = win32.COORD(0,0)
+ csbi = win32.GetConsoleScreenBufferInfo(handle)
+ # get the number of character cells in the current buffer
+ dw_con_size = csbi.dwSize.X * csbi.dwSize.Y
+ # fill the entire screen with blanks
+ win32.FillConsoleOutputCharacter(handle, ' ', dw_con_size, coord_screen)
+ # now set the buffer's attributes accordingly
+ win32.FillConsoleOutputAttribute(handle, self.get_attrs(), dw_con_size, coord_screen );
+ # put the cursor at (0, 0)
+ win32.SetConsoleCursorPosition(handle, (coord_screen.X, coord_screen.Y))
diff --git a/pip/_vendor/distlib/__init__.py b/pip/_vendor/distlib/__init__.py
index 56a56ba68..f9081bb84 100644
--- a/pip/_vendor/distlib/__init__.py
+++ b/pip/_vendor/distlib/__init__.py
@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*-
#
-# Copyright (C) 2012-2013 Vinay Sajip.
+# Copyright (C) 2012-2014 Vinay Sajip.
# Licensed to the Python Software Foundation under a contributor agreement.
# See LICENSE.txt and CONTRIBUTORS.txt.
#
import logging
-__version__ = '0.1.7'
+__version__ = '0.1.8'
class DistlibException(Exception):
pass
@@ -17,6 +17,7 @@ except ImportError: # pragma: no cover
class NullHandler(logging.Handler):
def handle(self, record): pass
def emit(self, record): pass
+ def createLock(self): self.lock = None
logger = logging.getLogger(__name__)
logger.addHandler(NullHandler())
diff --git a/pip/_vendor/distlib/_backport/sysconfig.py b/pip/_vendor/distlib/_backport/sysconfig.py
index a5b55feb4..1d3132679 100644
--- a/pip/_vendor/distlib/_backport/sysconfig.py
+++ b/pip/_vendor/distlib/_backport/sysconfig.py
@@ -68,7 +68,7 @@ _cfg_read = False
def _ensure_cfg_read():
global _cfg_read
if not _cfg_read:
- from distlib.resources import finder
+ from ..resources import finder
backport_package = __name__.rsplit('.', 1)[0]
_finder = finder(backport_package)
_cfgfile = _finder.find('sysconfig.cfg')
diff --git a/pip/_vendor/distlib/index.py b/pip/_vendor/distlib/index.py
index 30b2aa5a5..83004b13f 100644
--- a/pip/_vendor/distlib/index.py
+++ b/pip/_vendor/distlib/index.py
@@ -15,10 +15,10 @@ try:
except ImportError:
from dummy_threading import Thread
-from distlib import DistlibException
-from distlib.compat import (HTTPBasicAuthHandler, Request, HTTPPasswordMgr,
- urlparse, build_opener)
-from distlib.util import cached_property, zip_dir
+from . import DistlibException
+from .compat import (HTTPBasicAuthHandler, Request, HTTPPasswordMgr,
+ urlparse, build_opener, string_types)
+from .util import cached_property, zip_dir, ServerProxy
logger = logging.getLogger(__name__)
@@ -49,6 +49,7 @@ class PackageIndex(object):
self.ssl_verifier = None
self.gpg = None
self.gpg_home = None
+ self.rpc_proxy = None
with open(os.devnull, 'w') as sink:
for s in ('gpg2', 'gpg'):
try:
@@ -478,3 +479,10 @@ class PackageIndex(object):
'Content-length': str(len(body))
}
return Request(self.url, body, headers)
+
+ def search(self, terms, operator=None):
+ if isinstance(terms, string_types):
+ terms = {'name': terms}
+ if self.rpc_proxy is None:
+ self.rpc_proxy = ServerProxy(self.url, timeout=3.0)
+ return self.rpc_proxy.search(terms, operator or 'and')
diff --git a/pip/_vendor/distlib/locators.py b/pip/_vendor/distlib/locators.py
index ffe99310f..07bc1fd43 100644
--- a/pip/_vendor/distlib/locators.py
+++ b/pip/_vendor/distlib/locators.py
@@ -534,6 +534,11 @@ class SimpleScrapingLocator(Locator):
self.skip_externals = False
self.num_workers = num_workers
self._lock = threading.RLock()
+ # See issue #45: we need to be resilient when the locator is used
+ # in a thread, e.g. with concurrent.futures. We can't use self._lock
+ # as it is for coordinating our internal threads - the ones created
+ # in _prepare_threads.
+ self._gplock = threading.RLock()
def _prepare_threads(self):
"""
@@ -562,19 +567,21 @@ class SimpleScrapingLocator(Locator):
self._threads = []
def _get_project(self, name):
- self.result = result = {}
- self.project_name = name
- url = urljoin(self.base_url, '%s/' % quote(name))
- self._seen.clear()
- self._page_cache.clear()
- self._prepare_threads()
- try:
- logger.debug('Queueing %s', url)
- self._to_fetch.put(url)
- self._to_fetch.join()
- finally:
- self._wait_threads()
- del self.result
+ result = {}
+ with self._gplock:
+ self.result = result
+ self.project_name = name
+ url = urljoin(self.base_url, '%s/' % quote(name))
+ self._seen.clear()
+ self._page_cache.clear()
+ self._prepare_threads()
+ try:
+ logger.debug('Queueing %s', url)
+ self._to_fetch.put(url)
+ self._to_fetch.join()
+ finally:
+ self._wait_threads()
+ del self.result
return result
platform_dependent = re.compile(r'\b(linux-(i\d86|x86_64|arm\w+)|'
diff --git a/pip/_vendor/distlib/metadata.py b/pip/_vendor/distlib/metadata.py
index ef3b1d6b3..8441d8fef 100644
--- a/pip/_vendor/distlib/metadata.py
+++ b/pip/_vendor/distlib/metadata.py
@@ -1006,7 +1006,18 @@ class Metadata(object):
if self._legacy:
self._legacy.add_requirements(requirements)
else:
- self._data.setdefault('run_requires', []).extend(requirements)
+ run_requires = self._data.setdefault('run_requires', [])
+ always = None
+ for entry in run_requires:
+ if 'environment' not in entry and 'extra' not in entry:
+ always = entry
+ break
+ if always is None:
+ always = { 'requires': requirements }
+ run_requires.insert(0, always)
+ else:
+ rset = set(always['requires']) | set(requirements)
+ always['requires'] = sorted(rset)
def __repr__(self):
name = self.name or '(no name)'
diff --git a/pip/_vendor/distlib/resources.py b/pip/_vendor/distlib/resources.py
index 7c2673720..567840e7b 100644
--- a/pip/_vendor/distlib/resources.py
+++ b/pip/_vendor/distlib/resources.py
@@ -17,39 +17,20 @@ import types
import zipimport
from . import DistlibException
-from .util import cached_property, get_cache_base, path_to_cache_dir
+from .util import cached_property, get_cache_base, path_to_cache_dir, Cache
logger = logging.getLogger(__name__)
-class Cache(object):
- """
- A class implementing a cache for resources that need to live in the file system
- e.g. shared libraries.
- """
+cache = None # created when needed
- def __init__(self, base=None):
- """
- Initialise an instance.
- :param base: The base directory where the cache should be located. If
- not specified, this will be the ``resource-cache``
- directory under whatever :func:`get_cache_base` returns.
- """
+class ResourceCache(Cache):
+ def __init__(self, base=None):
if base is None:
# Use native string to avoid issues on 2.x: see Python #20140.
base = os.path.join(get_cache_base(), str('resource-cache'))
- # we use 'isdir' instead of 'exists', because we want to
- # fail if there's a file with that name
- if not os.path.isdir(base):
- os.makedirs(base)
- self.base = os.path.abspath(os.path.normpath(base))
-
- def prefix_to_dir(self, prefix):
- """
- Converts a resource prefix to a directory name in the cache.
- """
- return path_to_cache_dir(prefix)
+ super(ResourceCache, self).__init__(base)
def is_stale(self, resource, path):
"""
@@ -87,24 +68,6 @@ class Cache(object):
f.write(resource.bytes)
return result
- def clear(self):
- """
- Clear the cache.
- """
- not_removed = []
- for fn in os.listdir(self.base):
- fn = os.path.join(self.base, fn)
- try:
- if os.path.islink(fn) or os.path.isfile(fn):
- os.remove(fn)
- elif os.path.isdir(fn):
- shutil.rmtree(fn)
- except Exception:
- not_removed.append(fn)
- return not_removed
-
-cache = Cache()
-
class ResourceBase(object):
def __init__(self, finder, name):
@@ -131,6 +94,9 @@ class Resource(ResourceBase):
@cached_property
def file_path(self):
+ global cache
+ if cache is None:
+ cache = ResourceCache()
return cache.get(self)
@cached_property
diff --git a/pip/_vendor/distlib/scripts.py b/pip/_vendor/distlib/scripts.py
index ba0e5202b..36850b2a3 100644
--- a/pip/_vendor/distlib/scripts.py
+++ b/pip/_vendor/distlib/scripts.py
@@ -92,8 +92,10 @@ class ScriptMaker(object):
return executable
def _get_shebang(self, encoding, post_interp=b'', options=None):
+ enquote = True
if self.executable:
executable = self.executable
+ enquote = False # assume this will be taken care of
elif not sysconfig.is_python_build():
executable = get_executable()
elif in_venv():
@@ -107,6 +109,10 @@ class ScriptMaker(object):
if options:
executable = self._get_alternate_executable(executable, options)
+ # 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
executable = fsencode(executable)
shebang = b'#!' + executable + post_interp + b'\n'
# Python parser starts to read a script using UTF-8 until
diff --git a/pip/_vendor/distlib/util.py b/pip/_vendor/distlib/util.py
index e64d078b6..29ec519ab 100644
--- a/pip/_vendor/distlib/util.py
+++ b/pip/_vendor/distlib/util.py
@@ -154,9 +154,9 @@ def in_venv():
def get_executable():
- if sys.platform == 'darwin' and ('__VENV_LAUNCHER__'
+ if sys.platform == 'darwin' and ('__PYVENV_LAUNCHER__'
in os.environ):
- result = os.environ['__VENV_LAUNCHER__']
+ result = os.environ['__PYVENV_LAUNCHER__']
else:
result = sys.executable
return result
@@ -595,7 +595,6 @@ def get_cache_base(suffix=None):
else:
# Assume posix, or old Windows
result = os.path.expanduser('~')
- result = os.path.join(result, suffix)
# we use 'isdir' instead of 'exists', because we want to
# fail if there's a file with that name
if os.path.isdir(result):
@@ -612,7 +611,7 @@ def get_cache_base(suffix=None):
if not usable:
result = tempfile.mkdtemp()
logger.warning('Default location unusable, using %s', result)
- return result
+ return os.path.join(result, suffix)
def path_to_cache_dir(path):
@@ -768,6 +767,50 @@ def get_package_data(name, version):
return _get_external_data(url)
+class Cache(object):
+ """
+ A class implementing a cache for resources that need to live in the file system
+ e.g. shared libraries. This class was moved from resources to here because it
+ could be used by other modules, e.g. the wheel module.
+ """
+
+ def __init__(self, base):
+ """
+ Initialise an instance.
+
+ :param base: The base directory where the cache should be located.
+ """
+ # we use 'isdir' instead of 'exists', because we want to
+ # fail if there's a file with that name
+ if not os.path.isdir(base):
+ os.makedirs(base)
+ if (os.stat(base).st_mode & 0o77) != 0:
+ logger.warning('Directory \'%s\' is not private', base)
+ self.base = os.path.abspath(os.path.normpath(base))
+
+ def prefix_to_dir(self, prefix):
+ """
+ Converts a resource prefix to a directory name in the cache.
+ """
+ return path_to_cache_dir(prefix)
+
+ def clear(self):
+ """
+ Clear the cache.
+ """
+ not_removed = []
+ for fn in os.listdir(self.base):
+ fn = os.path.join(self.base, fn)
+ try:
+ if os.path.islink(fn) or os.path.isfile(fn):
+ os.remove(fn)
+ elif os.path.isdir(fn):
+ shutil.rmtree(fn)
+ except Exception:
+ not_removed.append(fn)
+ return not_removed
+
+
class EventMixin(object):
"""
A very simple publish/subscribe system.
diff --git a/pip/_vendor/distlib/version.py b/pip/_vendor/distlib/version.py
index a2192274c..f0e62c4ee 100644
--- a/pip/_vendor/distlib/version.py
+++ b/pip/_vendor/distlib/version.py
@@ -181,7 +181,7 @@ class Matcher(object):
return self._string
-PEP426_VERSION_RE = re.compile(r'^(\d+\.\d+(\.\d+)*)((a|b|c|rc)(\d+))?'
+PEP426_VERSION_RE = re.compile(r'^(\d+(\.\d+)*)((a|b|c|rc)(\d+))?'
r'(\.(post)(\d+))?(\.(dev)(\d+))?'
r'(-(\d+(\.\d+)?))?$')
@@ -297,7 +297,22 @@ class NormalizedMatcher(Matcher):
'!=': '_match_ne',
}
+ def _adjust_local(self, version, constraint, prefix):
+ if prefix:
+ strip_local = '-' not in constraint and version._parts[-1]
+ else:
+ # both constraint and version are
+ # NormalizedVersion instances.
+ # If constraint does not have a local component,
+ # ensure the version doesn't, either.
+ strip_local = not constraint._parts[-1] and version._parts[-1]
+ if strip_local:
+ s = version._string.split('-', 1)[0]
+ version = self.version_class(s)
+ return version, constraint
+
def _match_lt(self, version, constraint, prefix):
+ version, constraint = self._adjust_local(version, constraint, prefix)
if version >= constraint:
return False
release_clause = constraint._release_clause
@@ -305,6 +320,7 @@ class NormalizedMatcher(Matcher):
return not _match_prefix(version, pfx)
def _match_gt(self, version, constraint, prefix):
+ version, constraint = self._adjust_local(version, constraint, prefix)
if version <= constraint:
return False
release_clause = constraint._release_clause
@@ -312,12 +328,15 @@ class NormalizedMatcher(Matcher):
return not _match_prefix(version, pfx)
def _match_le(self, version, constraint, prefix):
+ version, constraint = self._adjust_local(version, constraint, prefix)
return version <= constraint
def _match_ge(self, version, constraint, prefix):
+ version, constraint = self._adjust_local(version, constraint, prefix)
return version >= constraint
def _match_eq(self, version, constraint, prefix):
+ version, constraint = self._adjust_local(version, constraint, prefix)
if not prefix:
result = (version == constraint)
else:
@@ -325,6 +344,7 @@ class NormalizedMatcher(Matcher):
return result
def _match_ne(self, version, constraint, prefix):
+ version, constraint = self._adjust_local(version, constraint, prefix)
if not prefix:
result = (version != constraint)
else:
@@ -332,6 +352,7 @@ class NormalizedMatcher(Matcher):
return result
def _match_compatible(self, version, constraint, prefix):
+ version, constraint = self._adjust_local(version, constraint, prefix)
if version == constraint:
return True
if version < constraint:
@@ -569,13 +590,15 @@ class LegacyVersion(Version):
def parse(self, s):
return _legacy_key(s)
- PREREL_TAGS = set(
- ['*a', '*alpha', '*b', '*beta', '*c', '*rc', '*r', '*@', '*pre']
- )
-
@property
def is_prerelease(self):
- return any(x in self.PREREL_TAGS for x in self._parts)
+ result = False
+ for x in self._parts:
+ if (isinstance(x, string_types) and x.startswith('*') and
+ x < '*final'):
+ result = True
+ break
+ return result
class LegacyMatcher(Matcher):
diff --git a/pip/_vendor/distlib/wheel.py b/pip/_vendor/distlib/wheel.py
index 6dbca81f1..d67d4bc5d 100644
--- a/pip/_vendor/distlib/wheel.py
+++ b/pip/_vendor/distlib/wheel.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# Copyright (C) 2013 Vinay Sajip.
+# Copyright (C) 2013-2014 Vinay Sajip.
# Licensed to the Python Software Foundation under a contributor agreement.
# See LICENSE.txt and CONTRIBUTORS.txt.
#
@@ -27,12 +27,13 @@ from . import __version__, DistlibException
from .compat import sysconfig, ZipFile, fsdecode, text_type, filter
from .database import InstalledDistribution
from .metadata import Metadata, METADATA_FILENAME
-from .util import (FileOperator, convert_path, CSVReader, CSVWriter,
- cached_property, get_cache_base, read_exports)
-
+from .util import (FileOperator, convert_path, CSVReader, CSVWriter, Cache,
+ cached_property, get_cache_base, read_exports, tempdir)
+from .version import NormalizedVersion, UnsupportedVersionError
logger = logging.getLogger(__name__)
+cache = None # created when needed
if hasattr(sys, 'pypy_version_info'):
IMP_PREFIX = 'pp'
@@ -55,7 +56,17 @@ ABI = sysconfig.get_config_var('SOABI')
if ABI and ABI.startswith('cpython-'):
ABI = ABI.replace('cpython-', 'cp')
else:
- ABI = 'none'
+ def _derive_abi():
+ parts = ['cp', VER_SUFFIX]
+ if sysconfig.get_config_var('Py_DEBUG'):
+ parts.append('d')
+ if sysconfig.get_config_var('WITH_PYMALLOC'):
+ parts.append('m')
+ if sysconfig.get_config_var('Py_UNICODE_SIZE') == 4:
+ parts.append('u')
+ return ''.join(parts)
+ ABI = _derive_abi()
+ del _derive_abi
FILENAME_RE = re.compile(r'''
(?P<nm>[^-]+)
@@ -132,7 +143,7 @@ class Wheel(object):
Initialise an instance using a (valid) filename.
"""
self.sign = sign
- self.verify = verify
+ self.should_verify = verify
self.buildver = ''
self.pyver = [PYVER]
self.abi = ['none']
@@ -147,7 +158,8 @@ class Wheel(object):
if m:
info = m.groupdict('')
self.name = info['nm']
- self.version = info['vn']
+ # Reinstate the local version separator
+ self.version = info['vn'].replace('_', '-')
self.buildver = info['bn']
self._filename = self.filename
else:
@@ -179,10 +191,17 @@ class Wheel(object):
pyver = '.'.join(self.pyver)
abi = '.'.join(self.abi)
arch = '.'.join(self.arch)
- return '%s-%s%s-%s-%s-%s.whl' % (self.name, self.version, buildver,
+ # replace - with _ as a local version separator
+ version = self.version.replace('-', '_')
+ return '%s-%s%s-%s-%s-%s.whl' % (self.name, version, buildver,
pyver, abi, arch)
@property
+ def exists(self):
+ path = os.path.join(self.dirname, self.filename)
+ return os.path.isfile(path)
+
+ @property
def tags(self):
for pyver in self.pyver:
for abi in self.abi:
@@ -195,29 +214,38 @@ class Wheel(object):
name_ver = '%s-%s' % (self.name, self.version)
info_dir = '%s.dist-info' % name_ver
wrapper = codecs.getreader('utf-8')
- metadata_filename = posixpath.join(info_dir, METADATA_FILENAME)
with ZipFile(pathname, 'r') as zf:
+ wheel_metadata = self.get_wheel_metadata(zf)
+ wv = wheel_metadata['Wheel-Version'].split('.', 1)
+ file_version = tuple([int(i) for i in wv])
+ if file_version < (1, 1):
+ fn = 'METADATA'
+ else:
+ fn = METADATA_FILENAME
try:
+ metadata_filename = posixpath.join(info_dir, fn)
with zf.open(metadata_filename) as bf:
wf = wrapper(bf)
result = Metadata(fileobj=wf)
except KeyError:
raise ValueError('Invalid wheel, because %s is '
- 'missing' % METADATA_FILENAME)
+ 'missing' % fn)
return result
- @cached_property
- def info(self):
- pathname = os.path.join(self.dirname, self.filename)
+ def get_wheel_metadata(self, zf):
name_ver = '%s-%s' % (self.name, self.version)
info_dir = '%s.dist-info' % name_ver
metadata_filename = posixpath.join(info_dir, 'WHEEL')
- wrapper = codecs.getreader('utf-8')
+ with zf.open(metadata_filename) as bf:
+ wf = codecs.getreader('utf-8')(bf)
+ message = message_from_file(wf)
+ return dict(message)
+
+ @cached_property
+ def info(self):
+ pathname = os.path.join(self.dirname, self.filename)
with ZipFile(pathname, 'r') as zf:
- with zf.open(metadata_filename) as bf:
- wf = wrapper(bf)
- message = message_from_file(wf)
- result = dict(message)
+ result = self.get_wheel_metadata(zf)
return result
def process_shebang(self, data):
@@ -255,6 +283,28 @@ class Wheel(object):
p = to_posix(os.path.relpath(record_path, base))
writer.writerow((p, '', ''))
+ def write_records(self, info, libdir, archive_paths):
+ records = []
+ distinfo, info_dir = info
+ hasher = getattr(hashlib, self.hash_kind)
+ for ap, p in archive_paths:
+ with open(p, 'rb') as f:
+ data = f.read()
+ digest = '%s=%s' % self.get_hash(data)
+ size = os.path.getsize(p)
+ records.append((ap, digest, size))
+
+ p = os.path.join(distinfo, 'RECORD')
+ self.write_record(records, p, libdir)
+ ap = to_posix(os.path.join(info_dir, 'RECORD'))
+ archive_paths.append((ap, p))
+
+ def build_zip(self, pathname, archive_paths):
+ with ZipFile(pathname, 'w', zipfile.ZIP_DEFLATED) as zf:
+ for ap, p in archive_paths:
+ logger.debug('Wrote %s to %s in wheel', p, ap)
+ zf.write(p, ap)
+
def build(self, paths, tags=None, wheel_version=None):
"""
Build a wheel from files in specified paths, and use any specified tags
@@ -353,25 +403,10 @@ class Wheel(object):
# Now, at last, RECORD.
# Paths in here are archive paths - nothing else makes sense.
- records = []
- hasher = getattr(hashlib, self.hash_kind)
- for ap, p in archive_paths:
- with open(p, 'rb') as f:
- data = f.read()
- digest = '%s=%s' % self.get_hash(data)
- size = os.path.getsize(p)
- records.append((ap, digest, size))
-
- p = os.path.join(distinfo, 'RECORD')
- self.write_record(records, p, libdir)
- ap = to_posix(os.path.join(info_dir, 'RECORD'))
- archive_paths.append((ap, p))
+ self.write_records((distinfo, info_dir), libdir, archive_paths)
# Now, ready to build the zip file
pathname = os.path.join(self.dirname, self.filename)
- with ZipFile(pathname, 'w', zipfile.ZIP_DEFLATED) as zf:
- for ap, p in archive_paths:
- logger.debug('Wrote %s to %s in wheel', p, ap)
- zf.write(p, ap)
+ self.build_zip(pathname, archive_paths)
return pathname
def install(self, paths, maker, **kwargs):
@@ -601,11 +636,13 @@ class Wheel(object):
shutil.rmtree(workdir)
def _get_dylib_cache(self):
- # Use native string to avoid issues on 2.x: see Python #20140.
- result = os.path.join(get_cache_base(), str('dylib-cache'), sys.version[:3])
- if not os.path.isdir(result):
- os.makedirs(result)
- return result
+ global cache
+ if cache is None:
+ # Use native string to avoid issues on 2.x: see Python #20140.
+ base = os.path.join(get_cache_base(), str('dylib-cache'),
+ sys.version[:3])
+ cache = Cache(base)
+ return cache
def _get_extensions(self):
pathname = os.path.join(self.dirname, self.filename)
@@ -619,7 +656,11 @@ class Wheel(object):
with zf.open(arcname) as bf:
wf = wrapper(bf)
extensions = json.load(wf)
- cache_base = self._get_dylib_cache()
+ cache = self._get_dylib_cache()
+ prefix = cache.prefix_to_dir(pathname)
+ cache_base = os.path.join(cache.base, prefix)
+ if not os.path.isdir(cache_base):
+ os.makedirs(cache_base)
for name, relpath in extensions.items():
dest = os.path.join(cache_base, convert_path(relpath))
if not os.path.exists(dest):
@@ -637,10 +678,25 @@ class Wheel(object):
pass
return result
+ def is_compatible(self):
+ """
+ Determine if a wheel is compatible with the running system.
+ """
+ return is_compatible(self)
+
+ def is_mountable(self):
+ """
+ Determine if a wheel is asserted as mountable by its metadata.
+ """
+ return True # for now - metadata details TBD
+
def mount(self, append=False):
pathname = os.path.abspath(os.path.join(self.dirname, self.filename))
- if not is_compatible(self):
- msg = 'Wheel %s not mountable in this Python.' % pathname
+ if not self.is_compatible():
+ msg = 'Wheel %s not compatible with this Python.' % pathname
+ raise DistlibException(msg)
+ if not self.is_mountable():
+ msg = 'Wheel %s is marked as not mountable.' % pathname
raise DistlibException(msg)
if pathname in sys.path:
logger.debug('%s already in path', pathname)
@@ -667,6 +723,160 @@ class Wheel(object):
if _hook in sys.meta_path:
sys.meta_path.remove(_hook)
+ def verify(self):
+ pathname = os.path.join(self.dirname, self.filename)
+ name_ver = '%s-%s' % (self.name, self.version)
+ data_dir = '%s.data' % name_ver
+ info_dir = '%s.dist-info' % name_ver
+
+ metadata_name = posixpath.join(info_dir, METADATA_FILENAME)
+ wheel_metadata_name = posixpath.join(info_dir, 'WHEEL')
+ record_name = posixpath.join(info_dir, 'RECORD')
+
+ wrapper = codecs.getreader('utf-8')
+
+ with ZipFile(pathname, 'r') as zf:
+ with zf.open(wheel_metadata_name) as bwf:
+ wf = wrapper(bwf)
+ message = message_from_file(wf)
+ wv = message['Wheel-Version'].split('.', 1)
+ file_version = tuple([int(i) for i in wv])
+ # TODO version verification
+
+ records = {}
+ with zf.open(record_name) as bf:
+ with CSVReader(stream=bf) as reader:
+ for row in reader:
+ p = row[0]
+ records[p] = row
+
+ for zinfo in zf.infolist():
+ arcname = zinfo.filename
+ if isinstance(arcname, text_type):
+ u_arcname = arcname
+ else:
+ u_arcname = arcname.decode('utf-8')
+ if '..' in u_arcname:
+ raise DistlibException('invalid entry in '
+ 'wheel: %r' % u_arcname)
+
+ # The signature file won't be in RECORD,
+ # and we don't currently don't do anything with it
+ if u_arcname.endswith('/RECORD.jws'):
+ continue
+ row = records[u_arcname]
+ if row[2] and str(zinfo.file_size) != row[2]:
+ raise DistlibException('size mismatch for '
+ '%s' % u_arcname)
+ if row[1]:
+ kind, value = row[1].split('=', 1)
+ with zf.open(arcname) as bf:
+ data = bf.read()
+ _, digest = self.get_hash(data, kind)
+ if digest != value:
+ raise DistlibException('digest mismatch for '
+ '%s' % arcname)
+
+ def update(self, modifier, dest_dir=None, **kwargs):
+ """
+ Update the contents of a wheel in a generic way. The modifier should
+ be a callable which expects a dictionary argument: its keys are
+ archive-entry paths, and its values are absolute filesystem paths
+ where the contents the corresponding archive entries can be found. The
+ modifier is free to change the contents of the files pointed to, add
+ new entries and remove entries, before returning. This method will
+ extract the entire contents of the wheel to a temporary location, call
+ the modifier, and then use the passed (and possibly updated)
+ dictionary to write a new wheel. If ``dest_dir`` is specified, the new
+ wheel is written there -- otherwise, the original wheel is overwritten.
+
+ The modifier should return True if it updated the wheel, else False.
+ This method returns the same value the modifier returns.
+ """
+
+ def get_version(path_map, info_dir):
+ version = path = None
+ key = '%s/%s' % (info_dir, METADATA_FILENAME)
+ if key not in path_map:
+ key = '%s/PKG-INFO' % info_dir
+ if key in path_map:
+ path = path_map[key]
+ version = Metadata(path=path).version
+ return version, path
+
+ def update_version(version, path):
+ updated = None
+ try:
+ v = NormalizedVersion(version)
+ i = version.find('-')
+ if i < 0:
+ updated = '%s-1' % version
+ else:
+ parts = [int(s) for s in version[i + 1:].split('.')]
+ parts[-1] += 1
+ updated = '%s-%s' % (version[:i],
+ '.'.join(str(i) for i in parts))
+ except UnsupportedVersionError:
+ logger.debug('Cannot update non-compliant (PEP-440) '
+ 'version %r', version)
+ if updated:
+ md = Metadata(path=path)
+ md.version = updated
+ legacy = not path.endswith(METADATA_FILENAME)
+ md.write(path=path, legacy=legacy)
+ logger.debug('Version updated from %r to %r', version,
+ updated)
+
+ pathname = os.path.join(self.dirname, self.filename)
+ name_ver = '%s-%s' % (self.name, self.version)
+ info_dir = '%s.dist-info' % name_ver
+ record_name = posixpath.join(info_dir, 'RECORD')
+ with tempdir() as workdir:
+ with ZipFile(pathname, 'r') as zf:
+ path_map = {}
+ for zinfo in zf.infolist():
+ arcname = zinfo.filename
+ if isinstance(arcname, text_type):
+ u_arcname = arcname
+ else:
+ u_arcname = arcname.decode('utf-8')
+ if u_arcname == record_name:
+ continue
+ if '..' in u_arcname:
+ raise DistlibException('invalid entry in '
+ 'wheel: %r' % u_arcname)
+ zf.extract(zinfo, workdir)
+ path = os.path.join(workdir, convert_path(u_arcname))
+ path_map[u_arcname] = path
+
+ # Remember the version.
+ original_version, _ = get_version(path_map, info_dir)
+ # Files extracted. Call the modifier.
+ modified = modifier(path_map, **kwargs)
+ if modified:
+ # Something changed - need to build a new wheel.
+ current_version, path = get_version(path_map, info_dir)
+ if current_version and (current_version == original_version):
+ # Add or update local version to signify changes.
+ update_version(current_version, path)
+ # Decide where the new wheel goes.
+ if dest_dir is None:
+ fd, newpath = tempfile.mkstemp(suffix='.whl',
+ prefix='wheel-update-',
+ dir=workdir)
+ os.close(fd)
+ else:
+ if not os.path.isdir(dest_dir):
+ raise DistlibException('Not a directory: %r' % dest_dir)
+ newpath = os.path.join(dest_dir, self.filename)
+ archive_paths = list(path_map.items())
+ distinfo = os.path.join(workdir, info_dir)
+ info = distinfo, info_dir
+ self.write_records(info, workdir, archive_paths)
+ self.build_zip(newpath, archive_paths)
+ if dest_dir is None:
+ shutil.copyfile(newpath, pathname)
+ return modified
def compatible_tags():
"""
@@ -687,9 +897,34 @@ def compatible_tags():
abis.append('none')
result = []
+ arches = [ARCH]
+ if sys.platform == 'darwin':
+ m = re.match('(\w+)_(\d+)_(\d+)_(\w+)$', ARCH)
+ if m:
+ name, major, minor, arch = m.groups()
+ minor = int(minor)
+ matches = [arch]
+ if arch in ('i386', 'ppc'):
+ matches.append('fat')
+ if arch in ('i386', 'ppc', 'x86_64'):
+ matches.append('fat3')
+ if arch in ('ppc64', 'x86_64'):
+ matches.append('fat64')
+ if arch in ('i386', 'x86_64'):
+ matches.append('intel')
+ if arch in ('i386', 'x86_64', 'intel', 'ppc', 'ppc64'):
+ matches.append('universal')
+ while minor >= 0:
+ for match in matches:
+ s = '%s_%s_%s_%s' % (name, major, minor, match)
+ if s != ARCH: # already there
+ arches.append(s)
+ minor -= 1
+
# Most specific - our Python version, ABI and arch
for abi in abis:
- result.append((''.join((IMP_PREFIX, versions[0])), abi, ARCH))
+ for arch in arches:
+ result.append((''.join((IMP_PREFIX, versions[0])), abi, arch))
# where no ABI / arch dependency, but IMP_PREFIX dependency
for i, version in enumerate(versions):
@@ -702,7 +937,7 @@ def compatible_tags():
result.append((''.join(('py', version)), 'none', 'any'))
if i == 0:
result.append((''.join(('py', version[0])), 'none', 'any'))
- return result
+ return set(result)
COMPATIBLE_TAGS = compatible_tags()
diff --git a/pip/_vendor/html5lib/LICENSE b/pip/_vendor/html5lib/LICENSE
deleted file mode 100644
index c87fa7a00..000000000
--- a/pip/_vendor/html5lib/LICENSE
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright (c) 2006-2013 James Graham and other contributors
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/pip/_vendor/html5lib/__init__.py b/pip/_vendor/html5lib/__init__.py
index 10e2b74c2..ff5c77551 100644
--- a/pip/_vendor/html5lib/__init__.py
+++ b/pip/_vendor/html5lib/__init__.py
@@ -20,4 +20,4 @@ from .serializer import serialize
__all__ = ["HTMLParser", "parse", "parseFragment", "getTreeBuilder",
"getTreeWalker", "serialize"]
-__version__ = "1.0b1"
+__version__ = "1.0b3"
diff --git a/pip/_vendor/html5lib/constants.py b/pip/_vendor/html5lib/constants.py
index 1866dd78e..e7089846d 100644
--- a/pip/_vendor/html5lib/constants.py
+++ b/pip/_vendor/html5lib/constants.py
@@ -433,6 +433,24 @@ mathmlTextIntegrationPointElements = frozenset((
(namespaces["mathml"], "mtext")
))
+adjustForeignAttributes = {
+ "xlink:actuate": ("xlink", "actuate", namespaces["xlink"]),
+ "xlink:arcrole": ("xlink", "arcrole", namespaces["xlink"]),
+ "xlink:href": ("xlink", "href", namespaces["xlink"]),
+ "xlink:role": ("xlink", "role", namespaces["xlink"]),
+ "xlink:show": ("xlink", "show", namespaces["xlink"]),
+ "xlink:title": ("xlink", "title", namespaces["xlink"]),
+ "xlink:type": ("xlink", "type", namespaces["xlink"]),
+ "xml:base": ("xml", "base", namespaces["xml"]),
+ "xml:lang": ("xml", "lang", namespaces["xml"]),
+ "xml:space": ("xml", "space", namespaces["xml"]),
+ "xmlns": (None, "xmlns", namespaces["xmlns"]),
+ "xmlns:xlink": ("xmlns", "xlink", namespaces["xmlns"])
+}
+
+unadjustForeignAttributes = dict([((ns, local), qname) for qname, (prefix, local, ns) in
+ adjustForeignAttributes.items()])
+
spaceCharacters = frozenset((
"\t",
"\n",
diff --git a/pip/_vendor/html5lib/filters/lint.py b/pip/_vendor/html5lib/filters/lint.py
index 83ad63971..7cc99a4ba 100644
--- a/pip/_vendor/html5lib/filters/lint.py
+++ b/pip/_vendor/html5lib/filters/lint.py
@@ -23,24 +23,24 @@ class Filter(_base.Filter):
if type in ("StartTag", "EmptyTag"):
name = token["name"]
if contentModelFlag != "PCDATA":
- raise LintError(_("StartTag not in PCDATA content model flag: %s") % name)
+ raise LintError(_("StartTag not in PCDATA content model flag: %(tag)s") % {"tag": name})
if not isinstance(name, str):
- raise LintError(_("Tag name is not a string: %r") % name)
+ raise LintError(_("Tag name is not a string: %(tag)r") % {"tag": name})
if not name:
raise LintError(_("Empty tag name"))
if type == "StartTag" and name in voidElements:
- raise LintError(_("Void element reported as StartTag token: %s") % name)
+ raise LintError(_("Void element reported as StartTag token: %(tag)s") % {"tag": name})
elif type == "EmptyTag" and name not in voidElements:
- raise LintError(_("Non-void element reported as EmptyTag token: %s") % token["name"])
+ raise LintError(_("Non-void element reported as EmptyTag token: %(tag)s") % {"tag": token["name"]})
if type == "StartTag":
open_elements.append(name)
for name, value in token["data"]:
if not isinstance(name, str):
- raise LintError(_("Attribute name is not a string: %r") % name)
+ raise LintError(_("Attribute name is not a string: %(name)r") % {"name": name})
if not name:
raise LintError(_("Empty attribute name"))
if not isinstance(value, str):
- raise LintError(_("Attribute value is not a string: %r") % value)
+ raise LintError(_("Attribute value is not a string: %(value)r") % {"value": value})
if name in cdataElements:
contentModelFlag = "CDATA"
elif name in rcdataElements:
@@ -51,14 +51,14 @@ class Filter(_base.Filter):
elif type == "EndTag":
name = token["name"]
if not isinstance(name, str):
- raise LintError(_("Tag name is not a string: %r") % name)
+ raise LintError(_("Tag name is not a string: %(tag)r") % {"tag": name})
if not name:
raise LintError(_("Empty tag name"))
if name in voidElements:
- raise LintError(_("Void element reported as EndTag token: %s") % name)
+ raise LintError(_("Void element reported as EndTag token: %(tag)s") % {"tag": name})
start_name = open_elements.pop()
if start_name != name:
- raise LintError(_("EndTag (%s) does not match StartTag (%s)") % (name, start_name))
+ raise LintError(_("EndTag (%(end)s) does not match StartTag (%(start)s)") % {"end": name, "start": start_name})
contentModelFlag = "PCDATA"
elif type == "Comment":
@@ -68,26 +68,26 @@ class Filter(_base.Filter):
elif type in ("Characters", "SpaceCharacters"):
data = token["data"]
if not isinstance(data, str):
- raise LintError(_("Attribute name is not a string: %r") % data)
+ raise LintError(_("Attribute name is not a string: %(name)r") % {"name": data})
if not data:
- raise LintError(_("%s token with empty data") % type)
+ raise LintError(_("%(type)s token with empty data") % {"type": type})
if type == "SpaceCharacters":
data = data.strip(spaceCharacters)
if data:
- raise LintError(_("Non-space character(s) found in SpaceCharacters token: ") % data)
+ raise LintError(_("Non-space character(s) found in SpaceCharacters token: %(token)r") % {"token": data})
elif type == "Doctype":
name = token["name"]
if contentModelFlag != "PCDATA":
- raise LintError(_("Doctype not in PCDATA content model flag: %s") % name)
+ raise LintError(_("Doctype not in PCDATA content model flag: %(name)s") % {"name": name})
if not isinstance(name, str):
- raise LintError(_("Tag name is not a string: %r") % name)
+ raise LintError(_("Tag name is not a string: %(tag)r") % {"tag": name})
# XXX: what to do with token["data"] ?
elif type in ("ParseError", "SerializeError"):
pass
else:
- raise LintError(_("Unknown token type: %s") % type)
+ raise LintError(_("Unknown token type: %(type)s") % {"type": type})
yield token
diff --git a/pip/_vendor/html5lib/html5parser.py b/pip/_vendor/html5lib/html5parser.py
index 8a5acfeb0..b28f46f2a 100644
--- a/pip/_vendor/html5lib/html5parser.py
+++ b/pip/_vendor/html5lib/html5parser.py
@@ -17,6 +17,7 @@ from .constants import headingElements
from .constants import cdataElements, rcdataElements
from .constants import tokenTypes, ReparseException, namespaces
from .constants import htmlIntegrationPointElements, mathmlTextIntegrationPointElements
+from .constants import adjustForeignAttributes as adjustForeignAttributesMap
def parse(doc, treebuilder="etree", encoding=None,
@@ -168,7 +169,7 @@ class HTMLParser(object):
(self.isMathMLTextIntegrationPoint(currentNode) and
((type == StartTagToken and
token["name"] not in frozenset(["mglyph", "malignmark"])) or
- type in (CharactersToken, SpaceCharactersToken))) or
+ type in (CharactersToken, SpaceCharactersToken))) or
(currentNodeNamespace == namespaces["mathml"] and
currentNodeName == "annotation-xml" and
token["name"] == "svg") or
@@ -333,20 +334,7 @@ class HTMLParser(object):
del token["data"][originalName]
def adjustForeignAttributes(self, token):
- replacements = {
- "xlink:actuate": ("xlink", "actuate", namespaces["xlink"]),
- "xlink:arcrole": ("xlink", "arcrole", namespaces["xlink"]),
- "xlink:href": ("xlink", "href", namespaces["xlink"]),
- "xlink:role": ("xlink", "role", namespaces["xlink"]),
- "xlink:show": ("xlink", "show", namespaces["xlink"]),
- "xlink:title": ("xlink", "title", namespaces["xlink"]),
- "xlink:type": ("xlink", "type", namespaces["xlink"]),
- "xml:base": ("xml", "base", namespaces["xml"]),
- "xml:lang": ("xml", "lang", namespaces["xml"]),
- "xml:space": ("xml", "space", namespaces["xml"]),
- "xmlns": (None, "xmlns", namespaces["xmlns"]),
- "xmlns:xlink": ("xmlns", "xlink", namespaces["xmlns"])
- }
+ replacements = adjustForeignAttributesMap
for originalName in token["data"].keys():
if originalName in replacements:
@@ -519,61 +507,61 @@ def getPhases(debug):
if (not correct or token["name"] != "html"
or publicId.startswith(
- ("+//silmaril//dtd html pro v0r11 19970101//",
- "-//advasoft ltd//dtd html 3.0 aswedit + extensions//",
- "-//as//dtd html 3.0 aswedit + extensions//",
- "-//ietf//dtd html 2.0 level 1//",
- "-//ietf//dtd html 2.0 level 2//",
- "-//ietf//dtd html 2.0 strict level 1//",
- "-//ietf//dtd html 2.0 strict level 2//",
- "-//ietf//dtd html 2.0 strict//",
- "-//ietf//dtd html 2.0//",
- "-//ietf//dtd html 2.1e//",
- "-//ietf//dtd html 3.0//",
- "-//ietf//dtd html 3.2 final//",
- "-//ietf//dtd html 3.2//",
- "-//ietf//dtd html 3//",
- "-//ietf//dtd html level 0//",
- "-//ietf//dtd html level 1//",
- "-//ietf//dtd html level 2//",
- "-//ietf//dtd html level 3//",
- "-//ietf//dtd html strict level 0//",
- "-//ietf//dtd html strict level 1//",
- "-//ietf//dtd html strict level 2//",
- "-//ietf//dtd html strict level 3//",
- "-//ietf//dtd html strict//",
- "-//ietf//dtd html//",
- "-//metrius//dtd metrius presentational//",
- "-//microsoft//dtd internet explorer 2.0 html strict//",
- "-//microsoft//dtd internet explorer 2.0 html//",
- "-//microsoft//dtd internet explorer 2.0 tables//",
- "-//microsoft//dtd internet explorer 3.0 html strict//",
- "-//microsoft//dtd internet explorer 3.0 html//",
- "-//microsoft//dtd internet explorer 3.0 tables//",
- "-//netscape comm. corp.//dtd html//",
- "-//netscape comm. corp.//dtd strict html//",
- "-//o'reilly and associates//dtd html 2.0//",
- "-//o'reilly and associates//dtd html extended 1.0//",
- "-//o'reilly and associates//dtd html extended relaxed 1.0//",
- "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//",
- "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//",
- "-//spyglass//dtd html 2.0 extended//",
- "-//sq//dtd html 2.0 hotmetal + extensions//",
- "-//sun microsystems corp.//dtd hotjava html//",
- "-//sun microsystems corp.//dtd hotjava strict html//",
- "-//w3c//dtd html 3 1995-03-24//",
- "-//w3c//dtd html 3.2 draft//",
- "-//w3c//dtd html 3.2 final//",
- "-//w3c//dtd html 3.2//",
- "-//w3c//dtd html 3.2s draft//",
- "-//w3c//dtd html 4.0 frameset//",
- "-//w3c//dtd html 4.0 transitional//",
- "-//w3c//dtd html experimental 19960712//",
- "-//w3c//dtd html experimental 970421//",
- "-//w3c//dtd w3 html//",
- "-//w3o//dtd w3 html 3.0//",
- "-//webtechs//dtd mozilla html 2.0//",
- "-//webtechs//dtd mozilla html//"))
+ ("+//silmaril//dtd html pro v0r11 19970101//",
+ "-//advasoft ltd//dtd html 3.0 aswedit + extensions//",
+ "-//as//dtd html 3.0 aswedit + extensions//",
+ "-//ietf//dtd html 2.0 level 1//",
+ "-//ietf//dtd html 2.0 level 2//",
+ "-//ietf//dtd html 2.0 strict level 1//",
+ "-//ietf//dtd html 2.0 strict level 2//",
+ "-//ietf//dtd html 2.0 strict//",
+ "-//ietf//dtd html 2.0//",
+ "-//ietf//dtd html 2.1e//",
+ "-//ietf//dtd html 3.0//",
+ "-//ietf//dtd html 3.2 final//",
+ "-//ietf//dtd html 3.2//",
+ "-//ietf//dtd html 3//",
+ "-//ietf//dtd html level 0//",
+ "-//ietf//dtd html level 1//",
+ "-//ietf//dtd html level 2//",
+ "-//ietf//dtd html level 3//",
+ "-//ietf//dtd html strict level 0//",
+ "-//ietf//dtd html strict level 1//",
+ "-//ietf//dtd html strict level 2//",
+ "-//ietf//dtd html strict level 3//",
+ "-//ietf//dtd html strict//",
+ "-//ietf//dtd html//",
+ "-//metrius//dtd metrius presentational//",
+ "-//microsoft//dtd internet explorer 2.0 html strict//",
+ "-//microsoft//dtd internet explorer 2.0 html//",
+ "-//microsoft//dtd internet explorer 2.0 tables//",
+ "-//microsoft//dtd internet explorer 3.0 html strict//",
+ "-//microsoft//dtd internet explorer 3.0 html//",
+ "-//microsoft//dtd internet explorer 3.0 tables//",
+ "-//netscape comm. corp.//dtd html//",
+ "-//netscape comm. corp.//dtd strict html//",
+ "-//o'reilly and associates//dtd html 2.0//",
+ "-//o'reilly and associates//dtd html extended 1.0//",
+ "-//o'reilly and associates//dtd html extended relaxed 1.0//",
+ "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//",
+ "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//",
+ "-//spyglass//dtd html 2.0 extended//",
+ "-//sq//dtd html 2.0 hotmetal + extensions//",
+ "-//sun microsystems corp.//dtd hotjava html//",
+ "-//sun microsystems corp.//dtd hotjava strict html//",
+ "-//w3c//dtd html 3 1995-03-24//",
+ "-//w3c//dtd html 3.2 draft//",
+ "-//w3c//dtd html 3.2 final//",
+ "-//w3c//dtd html 3.2//",
+ "-//w3c//dtd html 3.2s draft//",
+ "-//w3c//dtd html 4.0 frameset//",
+ "-//w3c//dtd html 4.0 transitional//",
+ "-//w3c//dtd html experimental 19960712//",
+ "-//w3c//dtd html experimental 970421//",
+ "-//w3c//dtd w3 html//",
+ "-//w3o//dtd w3 html 3.0//",
+ "-//webtechs//dtd mozilla html 2.0//",
+ "-//webtechs//dtd mozilla html//"))
or publicId in
("-//w3o//dtd w3 html strict 3.0//en//",
"-/w3c/dtd html 4.0 transitional/en",
diff --git a/pip/_vendor/html5lib/inputstream.py b/pip/_vendor/html5lib/inputstream.py
index 0ac70bb3a..f3dfd7f3d 100644
--- a/pip/_vendor/html5lib/inputstream.py
+++ b/pip/_vendor/html5lib/inputstream.py
@@ -43,7 +43,7 @@ ascii_punctuation_re = re.compile("[\u0009-\u000D\u0020-\u002F\u003A-\u0040\u005
charsUntilRegEx = {}
-class BufferedStream:
+class BufferedStream(object):
"""Buffering for streams that do not have buffering of their own
The buffer is implemented as a list of chunks on the assumption that
@@ -63,11 +63,11 @@ class BufferedStream:
return pos
def seek(self, pos):
- assert pos < self._bufferedBytes()
+ assert pos <= self._bufferedBytes()
offset = pos
i = 0
while len(self.buffer[i]) < offset:
- offset -= pos
+ offset -= len(self.buffer[i])
i += 1
self.position = [i, offset]
@@ -114,7 +114,7 @@ class BufferedStream:
if remainingBytes:
rv.append(self._readStream(remainingBytes))
- return "".join(rv)
+ return b"".join(rv)
def HTMLInputStream(source, encoding=None, parseMeta=True, chardet=True):
@@ -132,7 +132,7 @@ def HTMLInputStream(source, encoding=None, parseMeta=True, chardet=True):
return HTMLBinaryInputStream(source, encoding, parseMeta, chardet)
-class HTMLUnicodeInputStream:
+class HTMLUnicodeInputStream(object):
"""Provides a unicode stream of characters to the HTMLTokenizer.
This class takes care of character encoding and removing or replacing
diff --git a/pip/_vendor/html5lib/serializer/htmlserializer.py b/pip/_vendor/html5lib/serializer/htmlserializer.py
index 08b60dfcc..157840a05 100644
--- a/pip/_vendor/html5lib/serializer/htmlserializer.py
+++ b/pip/_vendor/html5lib/serializer/htmlserializer.py
@@ -92,15 +92,17 @@ class HTMLSerializer(object):
resolve_entities = True
# miscellaneous options
+ alphabetical_attributes = False
inject_meta_charset = True
strip_whitespace = False
sanitize = False
options = ("quote_attr_values", "quote_char", "use_best_quote_char",
- "minimize_boolean_attributes", "use_trailing_solidus",
- "space_before_trailing_solidus", "omit_optional_tags",
- "strip_whitespace", "inject_meta_charset", "escape_lt_in_attrs",
- "escape_rcdata", "resolve_entities", "sanitize")
+ "omit_optional_tags", "minimize_boolean_attributes",
+ "use_trailing_solidus", "space_before_trailing_solidus",
+ "escape_lt_in_attrs", "escape_rcdata", "resolve_entities",
+ "alphabetical_attributes", "inject_meta_charset",
+ "strip_whitespace", "sanitize")
def __init__(self, **kwargs):
"""Initialize HTMLSerializer.
@@ -143,6 +145,8 @@ class HTMLSerializer(object):
See `html5lib user documentation`_
omit_optional_tags=True|False
Omit start/end tags that are optional.
+ alphabetical_attributes=False|True
+ Reorder attributes to be in alphabetical order.
.. _html5lib user documentation: http://code.google.com/p/html5lib/wiki/UserDocumentation
"""
@@ -171,10 +175,11 @@ class HTMLSerializer(object):
self.encoding = encoding
in_cdata = False
self.errors = []
+
if encoding and self.inject_meta_charset:
from ..filters.inject_meta_charset import Filter
treewalker = Filter(treewalker, encoding)
- # XXX: WhitespaceFilter should be used before OptionalTagFilter
+ # WhitespaceFilter should be used before OptionalTagFilter
# for maximum efficiently of this latter filter
if self.strip_whitespace:
from ..filters.whitespace import Filter
@@ -185,6 +190,12 @@ class HTMLSerializer(object):
if self.omit_optional_tags:
from ..filters.optionaltags import Filter
treewalker = Filter(treewalker)
+ # Alphabetical attributes must be last, as other filters
+ # could add attributes and alter the order
+ if self.alphabetical_attributes:
+ from ..filters.alphabeticalattributes import Filter
+ treewalker = Filter(treewalker)
+
for token in treewalker:
type = token["type"]
if type == "Doctype":
diff --git a/pip/_vendor/html5lib/treeadapters/__init__.py b/pip/_vendor/html5lib/treeadapters/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/pip/_vendor/html5lib/treeadapters/__init__.py
diff --git a/pip/_vendor/html5lib/treeadapters/sax.py b/pip/_vendor/html5lib/treeadapters/sax.py
new file mode 100644
index 000000000..ad47df956
--- /dev/null
+++ b/pip/_vendor/html5lib/treeadapters/sax.py
@@ -0,0 +1,44 @@
+from __future__ import absolute_import, division, unicode_literals
+
+from xml.sax.xmlreader import AttributesNSImpl
+
+from ..constants import adjustForeignAttributes, unadjustForeignAttributes
+
+prefix_mapping = {}
+for prefix, localName, namespace in adjustForeignAttributes.values():
+ if prefix is not None:
+ prefix_mapping[prefix] = namespace
+
+
+def to_sax(walker, handler):
+ """Call SAX-like content handler based on treewalker walker"""
+ handler.startDocument()
+ for prefix, namespace in prefix_mapping.items():
+ handler.startPrefixMapping(prefix, namespace)
+
+ for token in walker:
+ type = token["type"]
+ if type == "Doctype":
+ continue
+ elif type in ("StartTag", "EmptyTag"):
+ attrs = AttributesNSImpl(token["data"],
+ unadjustForeignAttributes)
+ handler.startElementNS((token["namespace"], token["name"]),
+ token["name"],
+ attrs)
+ if type == "EmptyTag":
+ handler.endElementNS((token["namespace"], token["name"]),
+ token["name"])
+ elif type == "EndTag":
+ handler.endElementNS((token["namespace"], token["name"]),
+ token["name"])
+ elif type in ("Characters", "SpaceCharacters"):
+ handler.characters(token["data"])
+ elif type == "Comment":
+ pass
+ else:
+ assert False, "Unknown token type"
+
+ for prefix, namespace in prefix_mapping.items():
+ handler.endPrefixMapping(prefix)
+ handler.endDocument()
diff --git a/pip/_vendor/html5lib/treebuilders/dom.py b/pip/_vendor/html5lib/treebuilders/dom.py
index f9e0d76e7..61e5ed79e 100644
--- a/pip/_vendor/html5lib/treebuilders/dom.py
+++ b/pip/_vendor/html5lib/treebuilders/dom.py
@@ -1,7 +1,7 @@
from __future__ import absolute_import, division, unicode_literals
-from xml.dom import minidom, Node, XML_NAMESPACE, XMLNS_NAMESPACE
+from xml.dom import minidom, Node
import weakref
from . import _base
@@ -220,69 +220,6 @@ def getDomBuilder(DomImplementation):
return "\n".join(rv)
- def dom2sax(node, handler, nsmap={'xml': XML_NAMESPACE}):
- if node.nodeType == Node.ELEMENT_NODE:
- if not nsmap:
- handler.startElement(node.nodeName, node.attributes)
- for child in node.childNodes:
- dom2sax(child, handler, nsmap)
- handler.endElement(node.nodeName)
- else:
- attributes = dict(node.attributes.itemsNS())
-
- # gather namespace declarations
- prefixes = []
- for attrname in list(node.attributes.keys()):
- attr = node.getAttributeNode(attrname)
- if (attr.namespaceURI == XMLNS_NAMESPACE or
- (attr.namespaceURI is None and attr.nodeName.startswith('xmlns'))):
- prefix = (attr.nodeName != 'xmlns' and attr.nodeName or None)
- handler.startPrefixMapping(prefix, attr.nodeValue)
- prefixes.append(prefix)
- nsmap = nsmap.copy()
- nsmap[prefix] = attr.nodeValue
- del attributes[(attr.namespaceURI, attr.nodeName)]
-
- # apply namespace declarations
- for attrname in list(node.attributes.keys()):
- attr = node.getAttributeNode(attrname)
- if attr.namespaceURI is None and ':' in attr.nodeName:
- prefix = attr.nodeName.split(':')[0]
- if prefix in nsmap:
- del attributes[(attr.namespaceURI, attr.nodeName)]
- attributes[(nsmap[prefix], attr.nodeName)] = attr.nodeValue
-
- # SAX events
- ns = node.namespaceURI or nsmap.get(None, None)
- handler.startElementNS((ns, node.nodeName), node.nodeName, attributes)
- for child in node.childNodes:
- dom2sax(child, handler, nsmap)
- handler.endElementNS((ns, node.nodeName), node.nodeName)
- for prefix in prefixes:
- handler.endPrefixMapping(prefix)
-
- elif node.nodeType in [Node.TEXT_NODE, Node.CDATA_SECTION_NODE]:
- handler.characters(node.nodeValue)
-
- elif node.nodeType == Node.DOCUMENT_NODE:
- handler.startDocument()
- for child in node.childNodes:
- dom2sax(child, handler, nsmap)
- handler.endDocument()
-
- elif node.nodeType == Node.DOCUMENT_FRAGMENT_NODE:
- for child in node.childNodes:
- dom2sax(child, handler, nsmap)
-
- else:
- # ATTRIBUTE_NODE
- # ENTITY_NODE
- # PROCESSING_INSTRUCTION_NODE
- # COMMENT_NODE
- # DOCUMENT_TYPE_NODE
- # NOTATION_NODE
- pass
-
return locals()
diff --git a/pip/_vendor/html5lib/treewalkers/_base.py b/pip/_vendor/html5lib/treewalkers/_base.py
index a20235961..dda3cd74e 100644
--- a/pip/_vendor/html5lib/treewalkers/_base.py
+++ b/pip/_vendor/html5lib/treewalkers/_base.py
@@ -1,13 +1,41 @@
from __future__ import absolute_import, division, unicode_literals
-from pip._vendor.six import text_type
+from pip._vendor.six import text_type, string_types
import gettext
_ = gettext.gettext
+from xml.dom import Node
+
+DOCUMENT = Node.DOCUMENT_NODE
+DOCTYPE = Node.DOCUMENT_TYPE_NODE
+TEXT = Node.TEXT_NODE
+ELEMENT = Node.ELEMENT_NODE
+COMMENT = Node.COMMENT_NODE
+ENTITY = Node.ENTITY_NODE
+UNKNOWN = "<#UNKNOWN#>"
+
from ..constants import voidElements, spaceCharacters
spaceCharacters = "".join(spaceCharacters)
+def to_text(s, blank_if_none=True):
+ """Wrapper around six.text_type to convert None to empty string"""
+ if s is None:
+ if blank_if_none:
+ return ""
+ else:
+ return None
+ elif isinstance(s, text_type):
+ return s
+ else:
+ return text_type(s)
+
+
+def is_text_or_none(string):
+ """Wrapper around isinstance(string_types) or is None"""
+ return string is None or isinstance(string, string_types)
+
+
class TreeWalker(object):
def __init__(self, tree):
self.tree = tree
@@ -19,45 +47,47 @@ class TreeWalker(object):
return {"type": "SerializeError", "data": msg}
def emptyTag(self, namespace, name, attrs, hasChildren=False):
- assert namespace is None or isinstance(namespace, text_type), type(namespace)
- assert isinstance(name, text_type), type(name)
- assert all((namespace is None or isinstance(namespace, text_type)) and
- isinstance(name, text_type) and
- isinstance(value, text_type)
+ assert namespace is None or isinstance(namespace, string_types), type(namespace)
+ assert isinstance(name, string_types), type(name)
+ assert all((namespace is None or isinstance(namespace, string_types)) and
+ isinstance(name, string_types) and
+ isinstance(value, string_types)
for (namespace, name), value in attrs.items())
- yield {"type": "EmptyTag", "name": name,
- "namespace": namespace,
+ yield {"type": "EmptyTag", "name": to_text(name, False),
+ "namespace": to_text(namespace),
"data": attrs}
if hasChildren:
yield self.error(_("Void element has children"))
def startTag(self, namespace, name, attrs):
- assert namespace is None or isinstance(namespace, text_type), type(namespace)
- assert isinstance(name, text_type), type(name)
- assert all((namespace is None or isinstance(namespace, text_type)) and
- isinstance(name, text_type) and
- isinstance(value, text_type)
+ assert namespace is None or isinstance(namespace, string_types), type(namespace)
+ assert isinstance(name, string_types), type(name)
+ assert all((namespace is None or isinstance(namespace, string_types)) and
+ isinstance(name, string_types) and
+ isinstance(value, string_types)
for (namespace, name), value in attrs.items())
return {"type": "StartTag",
- "name": name,
- "namespace": namespace,
- "data": attrs}
+ "name": text_type(name),
+ "namespace": to_text(namespace),
+ "data": dict(((to_text(namespace, False), to_text(name)),
+ to_text(value, False))
+ for (namespace, name), value in attrs.items())}
def endTag(self, namespace, name):
- assert namespace is None or isinstance(namespace, text_type), type(namespace)
- assert isinstance(name, text_type), type(namespace)
+ assert namespace is None or isinstance(namespace, string_types), type(namespace)
+ assert isinstance(name, string_types), type(namespace)
return {"type": "EndTag",
- "name": name,
- "namespace": namespace,
+ "name": to_text(name, False),
+ "namespace": to_text(namespace),
"data": {}}
def text(self, data):
- assert isinstance(data, text_type), type(data)
+ assert isinstance(data, string_types), type(data)
- data = data
+ data = to_text(data)
middle = data.lstrip(spaceCharacters)
left = data[:len(data) - len(middle)]
if left:
@@ -71,56 +101,30 @@ class TreeWalker(object):
yield {"type": "SpaceCharacters", "data": right}
def comment(self, data):
- assert isinstance(data, text_type), type(data)
+ assert isinstance(data, string_types), type(data)
- return {"type": "Comment", "data": data}
+ return {"type": "Comment", "data": text_type(data)}
def doctype(self, name, publicId=None, systemId=None, correct=True):
- assert name is None or isinstance(name, text_type), type(name)
- assert publicId is None or isinstance(publicId, text_type), type(publicId)
- assert systemId is None or isinstance(systemId, text_type), type(systemId)
+ assert is_text_or_none(name), type(name)
+ assert is_text_or_none(publicId), type(publicId)
+ assert is_text_or_none(systemId), type(systemId)
return {"type": "Doctype",
- "name": name if name is not None else "",
- "publicId": publicId,
- "systemId": systemId,
- "correct": correct}
+ "name": to_text(name),
+ "publicId": to_text(publicId),
+ "systemId": to_text(systemId),
+ "correct": to_text(correct)}
def entity(self, name):
- assert isinstance(name, text_type), type(name)
+ assert isinstance(name, string_types), type(name)
- return {"type": "Entity", "name": name}
+ return {"type": "Entity", "name": text_type(name)}
def unknown(self, nodeType):
return self.error(_("Unknown node type: ") + nodeType)
-class RecursiveTreeWalker(TreeWalker):
- def walkChildren(self, node):
- raise NotImplementedError
-
- def element(self, node, namespace, name, attrs, hasChildren):
- if name in voidElements:
- for token in self.emptyTag(namespace, name, attrs, hasChildren):
- yield token
- else:
- yield self.startTag(name, attrs)
- if hasChildren:
- for token in self.walkChildren(node):
- yield token
- yield self.endTag(name)
-
-from xml.dom import Node
-
-DOCUMENT = Node.DOCUMENT_NODE
-DOCTYPE = Node.DOCUMENT_TYPE_NODE
-TEXT = Node.TEXT_NODE
-ELEMENT = Node.ELEMENT_NODE
-COMMENT = Node.COMMENT_NODE
-ENTITY = Node.ENTITY_NODE
-UNKNOWN = "<#UNKNOWN#>"
-
-
class NonRecursiveTreeWalker(TreeWalker):
def getNodeDetails(self, node):
raise NotImplementedError
diff --git a/pip/_vendor/html5lib/treewalkers/etree.py b/pip/_vendor/html5lib/treewalkers/etree.py
index 88fb9811a..f5615f50a 100644
--- a/pip/_vendor/html5lib/treewalkers/etree.py
+++ b/pip/_vendor/html5lib/treewalkers/etree.py
@@ -1,5 +1,12 @@
from __future__ import absolute_import, division, unicode_literals
+try:
+ from collections import OrderedDict
+except ImportError:
+ try:
+ from ordereddict import OrderedDict
+ except ImportError:
+ OrderedDict = dict
import gettext
_ = gettext.gettext
@@ -61,7 +68,7 @@ def getETreeBuilder(ElementTreeImplementation):
else:
namespace = None
tag = node.tag
- attrs = {}
+ attrs = OrderedDict()
for name, value in list(node.attrib.items()):
match = tag_regexp.match(name)
if match:
diff --git a/pip/_vendor/html5lib/utils.py b/pip/_vendor/html5lib/utils.py
index 4e8559db6..2f41f4dfa 100644
--- a/pip/_vendor/html5lib/utils.py
+++ b/pip/_vendor/html5lib/utils.py
@@ -8,6 +8,10 @@ except ImportError:
import xml.etree.ElementTree as default_etree
+__all__ = ["default_etree", "MethodDispatcher", "isSurrogatePair",
+ "surrogatePairToCodepoint", "moduleFactoryFactory"]
+
+
class MethodDispatcher(dict):
"""Dict with 2 special properties:
diff --git a/pip/_vendor/pkg_resources.py b/pip/_vendor/pkg_resources.py
index b93cd24d3..ee8a4823b 100644
--- a/pip/_vendor/pkg_resources.py
+++ b/pip/_vendor/pkg_resources.py
@@ -90,10 +90,8 @@ def _bypass_ensure_directory(name, mode=0x1FF): # 0777
_state_vars = {}
def _declare_state(vartype, **kw):
- g = globals()
- for name, val in kw.items():
- g[name] = val
- _state_vars[name] = vartype
+ globals().update(kw)
+ _state_vars.update(dict.fromkeys(kw, vartype))
def __getstate__():
state = {}
@@ -429,6 +427,48 @@ class WorkingSet(object):
for entry in entries:
self.add_entry(entry)
+ @classmethod
+ def _build_master(cls):
+ """
+ Prepare the master working set.
+ """
+ ws = cls()
+ try:
+ from __main__ import __requires__
+ except ImportError:
+ # The main program does not list any requirements
+ return ws
+
+ # ensure the requirements are met
+ try:
+ ws.require(__requires__)
+ except VersionConflict:
+ return cls._build_from_requirements(__requires__)
+
+ return ws
+
+ @classmethod
+ def _build_from_requirements(cls, req_spec):
+ """
+ Build a working set from a requirement spec. Rewrites sys.path.
+ """
+ # try it without defaults already on sys.path
+ # by starting with an empty path
+ ws = cls([])
+ reqs = parse_requirements(req_spec)
+ dists = ws.resolve(reqs, Environment())
+ for dist in dists:
+ ws.add(dist)
+
+ # add any missing entries from sys.path
+ for entry in sys.path:
+ if entry not in ws.entries:
+ ws.add_entry(entry)
+
+ # then copy back to sys.path
+ sys.path[:] = ws.entries
+ return ws
+
def add_entry(self, entry):
"""Add a path item to ``.entries``, finding any distributions on it
@@ -504,7 +544,7 @@ class WorkingSet(object):
seen[key]=1
yield self.by_key[key]
- def add(self, dist, entry=None, insert=True):
+ def add(self, dist, entry=None, insert=True, replace=False):
"""Add `dist` to working set, associated with `entry`
If `entry` is unspecified, it defaults to the ``.location`` of `dist`.
@@ -512,8 +552,9 @@ class WorkingSet(object):
set's ``.entries`` (if it wasn't already present).
`dist` is only added to the working set if it's for a project that
- doesn't already have a distribution in the set. If it's added, any
- callbacks registered with the ``subscribe()`` method will be called.
+ doesn't already have a distribution in the set, unless `replace=True`.
+ If it's added, any callbacks registered with the ``subscribe()`` method
+ will be called.
"""
if insert:
dist.insert_on(self.entries, entry)
@@ -522,7 +563,7 @@ class WorkingSet(object):
entry = dist.location
keys = self.entry_keys.setdefault(entry,[])
keys2 = self.entry_keys.setdefault(dist.location,[])
- if dist.key in self.by_key:
+ if not replace and dist.key in self.by_key:
return # ignore hidden distros
self.by_key[dist.key] = dist
@@ -532,7 +573,8 @@ class WorkingSet(object):
keys2.append(dist.key)
self._added_new(dist)
- def resolve(self, requirements, env=None, installer=None):
+ def resolve(self, requirements, env=None, installer=None,
+ replace_conflicting=False):
"""List all distributions needed to (recursively) meet `requirements`
`requirements` must be a sequence of ``Requirement`` objects. `env`,
@@ -542,6 +584,12 @@ class WorkingSet(object):
will be invoked with each requirement that cannot be met by an
already-installed distribution; it should return a ``Distribution`` or
``None``.
+
+ Unless `replace_conflicting=True`, raises a VersionConflict exception if
+ any requirements are found on the path that have the correct name but
+ the wrong version. Otherwise, if an `installer` is supplied it will be
+ invoked to obtain the correct version of the requirement and activate
+ it.
"""
requirements = list(requirements)[::-1] # set up the stack
@@ -558,10 +606,18 @@ class WorkingSet(object):
if dist is None:
# Find the best distribution and add it to the map
dist = self.by_key.get(req.key)
- if dist is None:
+ if dist is None or (dist not in req and replace_conflicting):
+ ws = self
if env is None:
- env = Environment(self.entries)
- dist = best[req.key] = env.best_match(req, self, installer)
+ if dist is None:
+ env = Environment(self.entries)
+ else:
+ # Use an empty environment and workingset to avoid
+ # any further conflicts with the conflicting
+ # distribution
+ env = Environment([])
+ ws = WorkingSet([])
+ dist = best[req.key] = env.best_match(req, ws, installer)
if dist is None:
#msg = ("The '%s' distribution was not found on this "
# "system, and is required by this application.")
@@ -1811,6 +1867,7 @@ def register_namespace_handler(importer_type, namespace_handler):
def _handle_ns(packageName, path_item):
"""Ensure that named package includes a subpath of path_item (if needed)"""
+
importer = get_importer(path_item)
if importer is None:
return None
@@ -1825,12 +1882,14 @@ def _handle_ns(packageName, path_item):
elif not hasattr(module,'__path__'):
raise TypeError("Not a package:", packageName)
handler = _find_adapter(_namespace_handlers, importer)
- subpath = handler(importer,path_item,packageName,module)
+ subpath = handler(importer, path_item, packageName, module)
if subpath is not None:
path = module.__path__
path.append(subpath)
loader.load_module(packageName)
- module.__path__ = path
+ for path_item in path:
+ if path_item not in module.__path__:
+ module.__path__.append(path_item)
return subpath
def declare_namespace(packageName):
@@ -2252,7 +2311,9 @@ class Distribution(object):
self.insert_on(path)
if path is sys.path:
fixup_namespace_packages(self.location)
- list(map(declare_namespace, self._get_metadata('namespace_packages.txt')))
+ for pkg in self._get_metadata('namespace_packages.txt'):
+ if pkg in sys.modules:
+ declare_namespace(pkg)
def egg_name(self):
"""Return what this distribution's standard .egg filename should be"""
@@ -2685,26 +2746,8 @@ def _initialize(g):
_initialize(globals())
# Prepare the master working set and make the ``require()`` API available
-_declare_state('object', working_set = WorkingSet())
-try:
- # Does the main program list any requirements?
- from __main__ import __requires__
-except ImportError:
- pass # No: just use the default working set based on sys.path
-else:
- # Yes: ensure the requirements are met, by prefixing sys.path if necessary
- try:
- working_set.require(__requires__)
- except VersionConflict: # try it without defaults already on sys.path
- working_set = WorkingSet([]) # by starting with an empty path
- for dist in working_set.resolve(
- parse_requirements(__requires__), Environment()
- ):
- working_set.add(dist)
- for entry in sys.path: # add any missing entries from sys.path
- if entry not in working_set.entries:
- working_set.add_entry(entry)
- sys.path[:] = working_set.entries # then copy back to sys.path
+working_set = WorkingSet._build_master()
+_declare_state('object', working_set=working_set)
require = working_set.require
iter_entry_points = working_set.iter_entry_points
diff --git a/pip/_vendor/six.py b/pip/_vendor/six.py
index 7ec7f1bec..019130f7b 100644
--- a/pip/_vendor/six.py
+++ b/pip/_vendor/six.py
@@ -25,7 +25,7 @@ import sys
import types
__author__ = "Benjamin Peterson <benjamin@python.org>"
-__version__ = "1.5.2"
+__version__ = "1.6.1"
# Useful for very coarse version differentiation.
@@ -83,7 +83,11 @@ class _LazyDescr(object):
self.name = name
def __get__(self, obj, tp):
- result = self._resolve()
+ try:
+ result = self._resolve()
+ except ImportError:
+ # See the nice big comment in MovedModule.__getattr__.
+ raise AttributeError("%s could not be imported " % self.name)
setattr(obj, self.name, result) # Invokes __set__.
# This is a bit ugly, but it avoids running this again.
delattr(obj.__class__, self.name)
@@ -105,15 +109,22 @@ class MovedModule(_LazyDescr):
return _import_module(self.mod)
def __getattr__(self, attr):
- # Hack around the Django autoreloader. The reloader tries to get
- # __file__ or __name__ of every module in sys.modules. This doesn't work
- # well if this MovedModule is for an module that is unavailable on this
- # machine (like winreg on Unix systems). Thus, we pretend __file__ and
- # __name__ don't exist if the module hasn't been loaded yet. See issues
- # #51 and #53.
- if attr in ("__file__", "__name__") and self.mod not in sys.modules:
- raise AttributeError
- _module = self._resolve()
+ # It turns out many Python frameworks like to traverse sys.modules and
+ # try to load various attributes. This causes problems if this is a
+ # platform-specific module on the wrong platform, like _winreg on
+ # Unixes. Therefore, we silently pretend unimportable modules do not
+ # have any attributes. See issues #51, #53, #56, and #63 for the full
+ # tales of woe.
+ #
+ # First, if possible, avoid loading the module just to look at __file__,
+ # __name__, or __path__.
+ if (attr in ("__file__", "__name__", "__path__") and
+ self.mod not in sys.modules):
+ raise AttributeError(attr)
+ try:
+ _module = self._resolve()
+ except ImportError:
+ raise AttributeError(attr)
value = getattr(_module, attr)
setattr(self, attr, value)
return value
@@ -222,6 +233,7 @@ _moved_attributes = [
MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"),
MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"),
+ MovedModule("xmlrpc_server", "xmlrpclib", "xmlrpc.server"),
MovedModule("winreg", "_winreg"),
]
for attr in _moved_attributes:
@@ -241,6 +253,7 @@ class Module_six_moves_urllib_parse(_LazyModule):
_urllib_parse_moved_attributes = [
MovedAttribute("ParseResult", "urlparse", "urllib.parse"),
+ MovedAttribute("SplitResult", "urlparse", "urllib.parse"),
MovedAttribute("parse_qs", "urlparse", "urllib.parse"),
MovedAttribute("parse_qsl", "urlparse", "urllib.parse"),
MovedAttribute("urldefrag", "urlparse", "urllib.parse"),
@@ -254,6 +267,7 @@ _urllib_parse_moved_attributes = [
MovedAttribute("unquote", "urllib", "urllib.parse"),
MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
MovedAttribute("urlencode", "urllib", "urllib.parse"),
+ MovedAttribute("splitquery", "urllib", "urllib.parse"),
]
for attr in _urllib_parse_moved_attributes:
setattr(Module_six_moves_urllib_parse, attr.name, attr)
diff --git a/pip/_vendor/vendor.txt b/pip/_vendor/vendor.txt
index 1ce0bc05c..032fdeecd 100644
--- a/pip/_vendor/vendor.txt
+++ b/pip/_vendor/vendor.txt
@@ -1,5 +1,5 @@
-distlib==0.1.7
-html5lib==1.0b1
-six==1.5.2
-colorama==0.2.7
+distlib==0.1.8
+html5lib==1.0b3
+six==1.6.1
+colorama==0.3.1
requests==2.2.1
diff --git a/pip/wheel.py b/pip/wheel.py
index 839259df4..4e9803f23 100644
--- a/pip/wheel.py
+++ b/pip/wheel.py
@@ -134,10 +134,11 @@ def get_entrypoints(filename):
def move_wheel_files(name, req, wheeldir, user=False, home=None, root=None,
- pycompile=True):
+ pycompile=True, scheme=None):
"""Install a wheel"""
- scheme = distutils_scheme(name, user=user, home=home, root=root)
+ if not scheme:
+ scheme = distutils_scheme(name, user=user, home=home, root=root)
if root_is_purelib(name, wheeldir):
lib_dir = scheme['purelib']
@@ -177,6 +178,7 @@ def move_wheel_files(name, req, wheeldir, user=False, home=None, root=None,
for dir, subdirs, files in os.walk(source):
basedir = dir[len(source):].lstrip(os.path.sep)
+ destdir = os.path.join(dest, basedir)
if is_base and basedir.split(os.path.sep, 1)[0].endswith('.data'):
continue
for s in subdirs:
@@ -190,15 +192,21 @@ def move_wheel_files(name, req, wheeldir, user=False, home=None, root=None,
and s.lower().startswith(req.project_name.replace('-', '_').lower())):
assert not info_dir, 'Multiple .dist-info directories'
info_dir.append(destsubdir)
- if not os.path.exists(destsubdir):
- os.makedirs(destsubdir)
for f in files:
# Skip unwanted files
if filter and filter(f):
continue
srcfile = os.path.join(dir, f)
destfile = os.path.join(dest, basedir, f)
- shutil.move(srcfile, destfile)
+ # directory creation is lazy and after the file filtering above
+ # to ensure we don't install empty dirs; empty dirs can't be
+ # uninstalled.
+ if not os.path.exists(destdir):
+ os.makedirs(destdir)
+ # use copy2 (not move) to be extra sure we're not moving
+ # directories over; copy2 fails for directories. this would
+ # fail tests (not during released/user execution)
+ shutil.copy2(srcfile, destfile)
changed = False
if fixer:
changed = fixer(destfile)
diff --git a/setup.py b/setup.py
index 961da9684..7ad4e4faa 100644
--- a/setup.py
+++ b/setup.py
@@ -63,7 +63,7 @@ setup(name="pip",
keywords='easy_install distutils setuptools egg virtualenv',
author='The pip developers',
author_email='python-virtualenv@groups.google.com',
- url='http://www.pip-installer.org',
+ url='https://pip.pypa.io/',
license='MIT',
packages=find_packages(exclude=["contrib", "docs", "tests*"]),
package_data={
diff --git a/tests/data/packages/sample-1.2.0-py2.py3-none-any.whl b/tests/data/packages/sample-1.2.0-py2.py3-none-any.whl
new file mode 100644
index 000000000..5a64d6b41
--- /dev/null
+++ b/tests/data/packages/sample-1.2.0-py2.py3-none-any.whl
Binary files differ
diff --git a/tests/data/src/sample/MANIFEST.in b/tests/data/src/sample/MANIFEST.in
new file mode 100644
index 000000000..df5350843
--- /dev/null
+++ b/tests/data/src/sample/MANIFEST.in
@@ -0,0 +1,8 @@
+include DESCRIPTION.rst
+
+# Include the test suite (FIXME: does not work yet)
+# recursive-include tests *
+
+# If using Python 2.6 or less, then have to include package data, even though
+# it's already declared in setup.py
+include sample/*.dat
diff --git a/tests/data/src/sample/data/data_file b/tests/data/src/sample/data/data_file
new file mode 100644
index 000000000..7c0646bfd
--- /dev/null
+++ b/tests/data/src/sample/data/data_file
@@ -0,0 +1 @@
+some data \ No newline at end of file
diff --git a/tests/data/src/sample/sample/__init__.py b/tests/data/src/sample/sample/__init__.py
new file mode 100644
index 000000000..c1699a747
--- /dev/null
+++ b/tests/data/src/sample/sample/__init__.py
@@ -0,0 +1,5 @@
+__version__ = '1.2.0'
+
+def main():
+ """Entry point for the application script"""
+ print("Call your main application code here")
diff --git a/tests/data/src/sample/sample/package_data.dat b/tests/data/src/sample/sample/package_data.dat
new file mode 100644
index 000000000..7c0646bfd
--- /dev/null
+++ b/tests/data/src/sample/sample/package_data.dat
@@ -0,0 +1 @@
+some data \ No newline at end of file
diff --git a/tests/data/src/sample/setup.cfg b/tests/data/src/sample/setup.cfg
new file mode 100644
index 000000000..79bc67848
--- /dev/null
+++ b/tests/data/src/sample/setup.cfg
@@ -0,0 +1,5 @@
+[bdist_wheel]
+# This flag says that the code is written to work on both Python 2 and Python
+# 3. If at all possible, it is good practice to do this. If you cannot, you
+# will need to generate wheels for each Python version that you support.
+universal=1
diff --git a/tests/data/src/sample/setup.py b/tests/data/src/sample/setup.py
new file mode 100644
index 000000000..c86ab8a3c
--- /dev/null
+++ b/tests/data/src/sample/setup.py
@@ -0,0 +1,103 @@
+from setuptools import setup, find_packages
+import codecs
+import os
+import re
+
+here = os.path.abspath(os.path.dirname(__file__))
+
+# Read the version number from a source file.
+# Why read it, and not import?
+# see https://groups.google.com/d/topic/pypa-dev/0PkjVpcxTzQ/discussion
+def find_version(*file_paths):
+ # Open in Latin-1 so that we avoid encoding errors.
+ # Use codecs.open for Python 2 compatibility
+ with codecs.open(os.path.join(here, *file_paths), 'r', 'latin1') as f:
+ version_file = f.read()
+
+ # The version line must have the form
+ # __version__ = 'ver'
+ version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
+ version_file, re.M)
+ if version_match:
+ return version_match.group(1)
+ raise RuntimeError("Unable to find version string.")
+
+
+# Get the long description from the relevant file
+with codecs.open('DESCRIPTION.rst', encoding='utf-8') as f:
+ long_description = f.read()
+
+setup(
+ name="sample",
+ version=find_version('sample', '__init__.py'),
+ description="A sample Python project",
+ long_description=long_description,
+
+ # The project URL.
+ url='https://github.com/pypa/sampleproject',
+
+ # Author details
+ author='The Python Packaging Authority',
+ author_email='pypa-dev@googlegroups.com',
+
+ # Choose your license
+ license='MIT',
+
+ classifiers=[
+ # How mature is this project? Common values are
+ # 3 - Alpha
+ # 4 - Beta
+ # 5 - Production/Stable
+ 'Development Status :: 3 - Alpha',
+
+ # Indicate who your project is intended for
+ 'Intended Audience :: Developers',
+ 'Topic :: Software Development :: Build Tools',
+
+ # Pick your license as you wish (should match "license" above)
+ 'License :: OSI Approved :: MIT License',
+
+ # Specify the Python versions you support here. In particular, ensure
+ # that you indicate whether you support Python 2, Python 3 or both.
+ 'Programming Language :: Python :: 2',
+ 'Programming Language :: Python :: 2.6',
+ 'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3',
+ 'Programming Language :: Python :: 3.1',
+ 'Programming Language :: Python :: 3.2',
+ 'Programming Language :: Python :: 3.3',
+ ],
+
+ # What does your project relate to?
+ keywords='sample setuptools development',
+
+ # You can just specify the packages manually here if your project is
+ # simple. Or you can use find_packages.
+ packages=find_packages(exclude=["contrib", "docs", "tests*"]),
+
+ # List run-time dependencies here. These will be installed by pip when your
+ # project is installed.
+ install_requires = ['peppercorn'],
+
+ # If there are data files included in your packages that need to be
+ # installed, specify them here. If using Python 2.6 or less, then these
+ # have to be included in MANIFEST.in as well.
+ package_data={
+ 'sample': ['package_data.dat'],
+ },
+
+ # Although 'package_data' is the preferred approach, in some case you may
+ # need to place data files outside of your packages.
+ # see http://docs.python.org/3.4/distutils/setupscript.html#installing-additional-files
+ # In this case, 'data_file' will be installed into '<sys.prefix>/my_data'
+ data_files=[('my_data', ['data/data_file'])],
+
+ # To provide executable scripts, use entry points in preference to the
+ # "scripts" keyword. Entry points provide cross-platform support and allow
+ # pip to create the appropriate form of executable for the target platform.
+ entry_points={
+ 'console_scripts': [
+ 'sample=sample:main',
+ ],
+ },
+)
diff --git a/tests/functional/test_uninstall.py b/tests/functional/test_uninstall.py
index 9b408de6b..3e5add755 100644
--- a/tests/functional/test_uninstall.py
+++ b/tests/functional/test_uninstall.py
@@ -228,3 +228,14 @@ def test_uninstallpathset_non_local(mock_logger):
uninstall_set = UninstallPathSet(test_dist)
uninstall_set.remove() #with no files added to set; which is the case when trying to remove non-local dists
mock_logger.notify.assert_any_call("Not uninstalling pip at %s, outside environment %s" % (nonlocal_path, sys.prefix)), mock_logger.notify.mock_calls
+
+def test_uninstall_wheel(script, data):
+ """
+ Test uninstalling a wheel
+ """
+ package = data.packages.join("simple.dist-0.1-py2.py3-none-any.whl")
+ result = script.pip('install', package, '--no-index')
+ dist_info_folder = script.site_packages / 'simple.dist-0.1.dist-info'
+ assert dist_info_folder in result.files_created
+ result2 = script.pip('uninstall', 'simple.dist', '-y')
+ assert_all_changes(result, result2, [])
diff --git a/tests/functional/test_wheel.py b/tests/functional/test_wheel.py
index 7f0607fba..1a7bb63bb 100644
--- a/tests/functional/test_wheel.py
+++ b/tests/functional/test_wheel.py
@@ -9,7 +9,7 @@ from pip import wheel
from pip.download import path_to_url as path_to_url_d
from pip.locations import write_delete_marker_file
from pip.status_codes import PREVIOUS_BUILD_DIR_ERROR
-from tests.lib import pyversion_nodot, path_to_url
+from tests.lib import pyversion, path_to_url
def test_pip_wheel_fails_without_wheel(script, data):
@@ -26,7 +26,7 @@ def test_pip_wheel_success(script, data):
"""
script.pip('install', 'wheel')
result = script.pip('wheel', '--no-index', '-f', data.find_links, 'simple==3.0')
- wheel_file_name = 'simple-3.0-py%s-none-any.whl' % pyversion_nodot
+ wheel_file_name = 'simple-3.0-py%s-none-any.whl' % pyversion[0]
wheel_file_path = script.scratch/'wheelhouse'/wheel_file_name
assert wheel_file_path in result.files_created, result.stdout
assert "Successfully built simple" in result.stdout, result.stdout
@@ -52,7 +52,7 @@ def test_pip_wheel_fail(script, data):
"""
script.pip('install', 'wheel')
result = script.pip('wheel', '--no-index', '-f', data.find_links, 'wheelbroken==0.1')
- wheel_file_name = 'wheelbroken-0.1-py%s-none-any.whl' % pyversion_nodot
+ wheel_file_name = 'wheelbroken-0.1-py%s-none-any.whl' % pyversion[0]
wheel_file_path = script.scratch/'wheelhouse'/wheel_file_name
assert wheel_file_path not in result.files_created, (wheel_file_path, result.files_created)
assert "FakeError" in result.stdout, result.stdout
@@ -73,7 +73,7 @@ def test_pip_wheel_ignore_wheels_editables(script, data):
simple
""" % (local_wheel, local_editable)))
result = script.pip('wheel', '--no-index', '-f', data.find_links, '-r', script.scratch_path / 'reqs.txt')
- wheel_file_name = 'simple-3.0-py%s-none-any.whl' % pyversion_nodot
+ wheel_file_name = 'simple-3.0-py%s-none-any.whl' % pyversion[0]
wheel_file_path = script.scratch/'wheelhouse'/wheel_file_name
assert wheel_file_path in result.files_created, (wheel_file_path, result.files_created)
assert "Successfully built simple" in result.stdout, result.stdout
@@ -102,9 +102,9 @@ def test_pip_wheel_source_deps(script, data):
# 'requires_source' is a wheel that depends on the 'source' project
script.pip('install', 'wheel')
result = script.pip('wheel', '--use-wheel', '--no-index', '-f', data.find_links, 'requires_source')
- wheel_file_name = 'source-1.0-py%s-none-any.whl' % pyversion_nodot
+ wheel_file_name = 'source-1.0-py%s-none-any.whl' % pyversion[0]
wheel_file_path = script.scratch/'wheelhouse'/wheel_file_name
- assert wheel_file_path in result.files_created, result.stdout
+ assert wheel_file_path in result.files_created, result.files_created
assert "Successfully built source" in result.stdout, result.stdout
diff --git a/tests/unit/test_locations.py b/tests/unit/test_locations.py
index 01e6f7585..1e1cc2437 100644
--- a/tests/unit/test_locations.py
+++ b/tests/unit/test_locations.py
@@ -28,7 +28,7 @@ class TestLocations:
self.username = "example"
self.patch()
- def tearDown(self):
+ def teardown(self):
self.revert_patch()
shutil.rmtree(self.tempdir, ignore_errors=True)
diff --git a/tests/unit/test_wheel.py b/tests/unit/test_wheel.py
index 9b1061b9f..074a16d19 100644
--- a/tests/unit/test_wheel.py
+++ b/tests/unit/test_wheel.py
@@ -2,14 +2,12 @@
import os
import pytest
+from mock import patch
-import pkg_resources
-from mock import patch, Mock
+from pip._vendor import pkg_resources
from pip import wheel
-from pip.exceptions import (
- InstallationError, InvalidWheelFilename, UnsupportedWheel,
-)
-from pip.index import PackageFinder
+from pip.exceptions import InvalidWheelFilename, UnsupportedWheel
+from pip.req import InstallRequirement
from pip.util import unpack_file
from tests.lib import assert_raises_regexp
@@ -225,3 +223,56 @@ class TestPEP425Tags(object):
with patch('pip.pep425tags.sysconfig.get_config_var', raises_ioerror):
assert len(pip.pep425tags.get_supported())
+class TestMoveWheelFiles(object):
+ """
+ Tests for moving files from wheel src to scheme paths
+ """
+
+ def prep(self, data, tmpdir):
+ self.name = 'sample'
+ self.wheelpath = data.packages.join('sample-1.2.0-py2.py3-none-any.whl')
+ self.req = pkg_resources.Requirement.parse('sample')
+ self.src = os.path.join(tmpdir, 'src')
+ self.dest = os.path.join(tmpdir, 'dest')
+ unpack_file(self.wheelpath, self.src, None, None)
+ self.scheme = {
+ 'scripts': os.path.join(self.dest, 'bin'),
+ 'purelib': os.path.join(self.dest, 'lib'),
+ 'data': os.path.join(self.dest, 'data'),
+ }
+ self.src_dist_info = os.path.join(
+ self.src, 'sample-1.2.0.dist-info')
+ self.dest_dist_info = os.path.join(
+ self.scheme['purelib'], 'sample-1.2.0.dist-info')
+
+ def assert_installed(self):
+ # lib
+ assert os.path.isdir(
+ os.path.join(self.scheme['purelib'], 'sample'))
+ # dist-info
+ metadata = os.path.join(self.dest_dist_info, 'METADATA')
+ assert os.path.isfile(metadata)
+ # data files
+ data_file = os.path.join(self.scheme['data'], 'my_data', 'data_file')
+ assert os.path.isfile(data_file)
+ # package data
+ pkg_data = os.path.join(self.scheme['purelib'], 'sample', 'package_data.dat')
+
+ def test_std_install(self, data, tmpdir):
+ self.prep(data, tmpdir)
+ wheel.move_wheel_files(self.name, self.req, self.src, scheme=self.scheme)
+ self.assert_installed()
+
+ def test_dist_info_contains_empty_dir(self, data, tmpdir):
+ """
+ Test that empty dirs are not installed
+ """
+ # e.g. https://github.com/pypa/pip/issues/1632#issuecomment-38027275
+ self.prep(data, tmpdir)
+ src_empty_dir = os.path.join(self.src_dist_info, 'empty_dir', 'empty_dir')
+ os.makedirs(src_empty_dir)
+ assert os.path.isdir(src_empty_dir)
+ wheel.move_wheel_files(self.name, self.req, self.src, scheme=self.scheme)
+ self.assert_installed()
+ assert not os.path.isdir(os.path.join(self.dest_dist_info, 'empty_dir'))
+
diff --git a/tox.ini b/tox.ini
index 52b6ab503..20842e06f 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,6 +1,6 @@
[tox]
envlist =
- collect,docs,py26,py27,py32,py33,pypy
+ docs,py26,py27,py32,py33,py34,pypy
[testenv]
deps =