diff options
author | Donald Stufft <donald@stufft.io> | 2014-05-02 21:56:11 -0400 |
---|---|---|
committer | Donald Stufft <donald@stufft.io> | 2014-05-02 21:56:11 -0400 |
commit | 86353875a92ca06fb42f8ad31322c83666781462 (patch) | |
tree | 1cb4e6cfae5305e5a9bd8bd1df43c4a5640c5a04 | |
parent | fb8738edd671a1762ca0e5041eb6b09dca54b980 (diff) | |
parent | 060ffb1ec9ec62f6c2f1434b5b20ce268d206b00 (diff) | |
download | pip-86353875a92ca06fb42f8ad31322c83666781462.tar.gz |
Merge pull request #1783 from dstufft/upgrades
Upgrade the bundled projects.
32 files changed, 1105 insertions, 791 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 7c8c1656b..10bbc4f7f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -12,6 +12,9 @@ Changelog * 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). + 1.5.4 (2014-02-21) ------------------ 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/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
|