diff options
Diffstat (limited to 'venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/distlib/compat.py')
-rw-r--r-- | venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/distlib/compat.py | 1120 |
1 files changed, 0 insertions, 1120 deletions
diff --git a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/distlib/compat.py b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/distlib/compat.py deleted file mode 100644 index 09929b0..0000000 --- a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/distlib/compat.py +++ /dev/null | |||
@@ -1,1120 +0,0 @@ | |||
1 | # -*- coding: utf-8 -*- | ||
2 | # | ||
3 | # Copyright (C) 2013-2017 Vinay Sajip. | ||
4 | # Licensed to the Python Software Foundation under a contributor agreement. | ||
5 | # See LICENSE.txt and CONTRIBUTORS.txt. | ||
6 | # | ||
7 | from __future__ import absolute_import | ||
8 | |||
9 | import os | ||
10 | import re | ||
11 | import sys | ||
12 | |||
13 | try: | ||
14 | import ssl | ||
15 | except ImportError: # pragma: no cover | ||
16 | ssl = None | ||
17 | |||
18 | if sys.version_info[0] < 3: # pragma: no cover | ||
19 | from StringIO import StringIO | ||
20 | string_types = basestring, | ||
21 | text_type = unicode | ||
22 | from types import FileType as file_type | ||
23 | import __builtin__ as builtins | ||
24 | import ConfigParser as configparser | ||
25 | from ._backport import shutil | ||
26 | from urlparse import urlparse, urlunparse, urljoin, urlsplit, urlunsplit | ||
27 | from urllib import (urlretrieve, quote as _quote, unquote, url2pathname, | ||
28 | pathname2url, ContentTooShortError, splittype) | ||
29 | |||
30 | def quote(s): | ||
31 | if isinstance(s, unicode): | ||
32 | s = s.encode('utf-8') | ||
33 | return _quote(s) | ||
34 | |||
35 | import urllib2 | ||
36 | from urllib2 import (Request, urlopen, URLError, HTTPError, | ||
37 | HTTPBasicAuthHandler, HTTPPasswordMgr, | ||
38 | HTTPHandler, HTTPRedirectHandler, | ||
39 | build_opener) | ||
40 | if ssl: | ||
41 | from urllib2 import HTTPSHandler | ||
42 | import httplib | ||
43 | import xmlrpclib | ||
44 | import Queue as queue | ||
45 | from HTMLParser import HTMLParser | ||
46 | import htmlentitydefs | ||
47 | raw_input = raw_input | ||
48 | from itertools import ifilter as filter | ||
49 | from itertools import ifilterfalse as filterfalse | ||
50 | |||
51 | _userprog = None | ||
52 | def splituser(host): | ||
53 | """splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]'.""" | ||
54 | global _userprog | ||
55 | if _userprog is None: | ||
56 | import re | ||
57 | _userprog = re.compile('^(.*)@(.*)$') | ||
58 | |||
59 | match = _userprog.match(host) | ||
60 | if match: return match.group(1, 2) | ||
61 | return None, host | ||
62 | |||
63 | else: # pragma: no cover | ||
64 | from io import StringIO | ||
65 | string_types = str, | ||
66 | text_type = str | ||
67 | from io import TextIOWrapper as file_type | ||
68 | import builtins | ||
69 | import configparser | ||
70 | import shutil | ||
71 | from urllib.parse import (urlparse, urlunparse, urljoin, splituser, quote, | ||
72 | unquote, urlsplit, urlunsplit, splittype) | ||
73 | from urllib.request import (urlopen, urlretrieve, Request, url2pathname, | ||
74 | pathname2url, | ||
75 | HTTPBasicAuthHandler, HTTPPasswordMgr, | ||
76 | HTTPHandler, HTTPRedirectHandler, | ||
77 | build_opener) | ||
78 | if ssl: | ||
79 | from urllib.request import HTTPSHandler | ||
80 | from urllib.error import HTTPError, URLError, ContentTooShortError | ||
81 | import http.client as httplib | ||
82 | import urllib.request as urllib2 | ||
83 | import xmlrpc.client as xmlrpclib | ||
84 | import queue | ||
85 | from html.parser import HTMLParser | ||
86 | import html.entities as htmlentitydefs | ||
87 | raw_input = input | ||
88 | from itertools import filterfalse | ||
89 | filter = filter | ||
90 | |||
91 | try: | ||
92 | from ssl import match_hostname, CertificateError | ||
93 | except ImportError: # pragma: no cover | ||
94 | class CertificateError(ValueError): | ||
95 | pass | ||
96 | |||
97 | |||
98 | def _dnsname_match(dn, hostname, max_wildcards=1): | ||
99 | """Matching according to RFC 6125, section 6.4.3 | ||
100 | |||
101 | http://tools.ietf.org/html/rfc6125#section-6.4.3 | ||
102 | """ | ||
103 | pats = [] | ||
104 | if not dn: | ||
105 | return False | ||
106 | |||
107 | parts = dn.split('.') | ||
108 | leftmost, remainder = parts[0], parts[1:] | ||
109 | |||
110 | wildcards = leftmost.count('*') | ||
111 | if wildcards > max_wildcards: | ||
112 | # Issue #17980: avoid denials of service by refusing more | ||
113 | # than one wildcard per fragment. A survey of established | ||
114 | # policy among SSL implementations showed it to be a | ||
115 | # reasonable choice. | ||
116 | raise CertificateError( | ||
117 | "too many wildcards in certificate DNS name: " + repr(dn)) | ||
118 | |||
119 | # speed up common case w/o wildcards | ||
120 | if not wildcards: | ||
121 | return dn.lower() == hostname.lower() | ||
122 | |||
123 | # RFC 6125, section 6.4.3, subitem 1. | ||
124 | # The client SHOULD NOT attempt to match a presented identifier in which | ||
125 | # the wildcard character comprises a label other than the left-most label. | ||
126 | if leftmost == '*': | ||
127 | # When '*' is a fragment by itself, it matches a non-empty dotless | ||
128 | # fragment. | ||
129 | pats.append('[^.]+') | ||
130 | elif leftmost.startswith('xn--') or hostname.startswith('xn--'): | ||
131 | # RFC 6125, section 6.4.3, subitem 3. | ||
132 | # The client SHOULD NOT attempt to match a presented identifier | ||
133 | # where the wildcard character is embedded within an A-label or | ||
134 | # U-label of an internationalized domain name. | ||
135 | pats.append(re.escape(leftmost)) | ||
136 | else: | ||
137 | # Otherwise, '*' matches any dotless string, e.g. www* | ||
138 | pats.append(re.escape(leftmost).replace(r'\*', '[^.]*')) | ||
139 | |||
140 | # add the remaining fragments, ignore any wildcards | ||
141 | for frag in remainder: | ||
142 | pats.append(re.escape(frag)) | ||
143 | |||
144 | pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) | ||
145 | return pat.match(hostname) | ||
146 | |||
147 | |||
148 | def match_hostname(cert, hostname): | ||
149 | """Verify that *cert* (in decoded format as returned by | ||
150 | SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 | ||
151 | rules are followed, but IP addresses are not accepted for *hostname*. | ||
152 | |||
153 | CertificateError is raised on failure. On success, the function | ||
154 | returns nothing. | ||
155 | """ | ||
156 | if not cert: | ||
157 | raise ValueError("empty or no certificate, match_hostname needs a " | ||
158 | "SSL socket or SSL context with either " | ||
159 | "CERT_OPTIONAL or CERT_REQUIRED") | ||
160 | dnsnames = [] | ||
161 | san = cert.get('subjectAltName', ()) | ||
162 | for key, value in san: | ||
163 | if key == 'DNS': | ||
164 | if _dnsname_match(value, hostname): | ||
165 | return | ||
166 | dnsnames.append(value) | ||
167 | if not dnsnames: | ||
168 | # The subject is only checked when there is no dNSName entry | ||
169 | # in subjectAltName | ||
170 | for sub in cert.get('subject', ()): | ||
171 | for key, value in sub: | ||
172 | # XXX according to RFC 2818, the most specific Common Name | ||
173 | # must be used. | ||
174 | if key == 'commonName': | ||
175 | if _dnsname_match(value, hostname): | ||
176 | return | ||
177 | dnsnames.append(value) | ||
178 | if len(dnsnames) > 1: | ||
179 | raise CertificateError("hostname %r " | ||
180 | "doesn't match either of %s" | ||
181 | % (hostname, ', '.join(map(repr, dnsnames)))) | ||
182 | elif len(dnsnames) == 1: | ||
183 | raise CertificateError("hostname %r " | ||
184 | "doesn't match %r" | ||
185 | % (hostname, dnsnames[0])) | ||
186 | else: | ||
187 | raise CertificateError("no appropriate commonName or " | ||
188 | "subjectAltName fields were found") | ||
189 | |||
190 | |||
191 | try: | ||
192 | from types import SimpleNamespace as Container | ||
193 | except ImportError: # pragma: no cover | ||
194 | class Container(object): | ||
195 | """ | ||
196 | A generic container for when multiple values need to be returned | ||
197 | """ | ||
198 | def __init__(self, **kwargs): | ||
199 | self.__dict__.update(kwargs) | ||
200 | |||
201 | |||
202 | try: | ||
203 | from shutil import which | ||
204 | except ImportError: # pragma: no cover | ||
205 | # Implementation from Python 3.3 | ||
206 | def which(cmd, mode=os.F_OK | os.X_OK, path=None): | ||
207 | """Given a command, mode, and a PATH string, return the path which | ||
208 | conforms to the given mode on the PATH, or None if there is no such | ||
209 | file. | ||
210 | |||
211 | `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result | ||
212 | of os.environ.get("PATH"), or can be overridden with a custom search | ||
213 | path. | ||
214 | |||
215 | """ | ||
216 | # Check that a given file can be accessed with the correct mode. | ||
217 | # Additionally check that `file` is not a directory, as on Windows | ||
218 | # directories pass the os.access check. | ||
219 | def _access_check(fn, mode): | ||
220 | return (os.path.exists(fn) and os.access(fn, mode) | ||
221 | and not os.path.isdir(fn)) | ||
222 | |||
223 | # If we're given a path with a directory part, look it up directly rather | ||
224 | # than referring to PATH directories. This includes checking relative to the | ||
225 | # current directory, e.g. ./script | ||
226 | if os.path.dirname(cmd): | ||
227 | if _access_check(cmd, mode): | ||
228 | return cmd | ||
229 | return None | ||
230 | |||
231 | if path is None: | ||
232 | path = os.environ.get("PATH", os.defpath) | ||
233 | if not path: | ||
234 | return None | ||
235 | path = path.split(os.pathsep) | ||
236 | |||
237 | if sys.platform == "win32": | ||
238 | # The current directory takes precedence on Windows. | ||
239 | if not os.curdir in path: | ||
240 | path.insert(0, os.curdir) | ||
241 | |||
242 | # PATHEXT is necessary to check on Windows. | ||
243 | pathext = os.environ.get("PATHEXT", "").split(os.pathsep) | ||
244 | # See if the given file matches any of the expected path extensions. | ||
245 | # This will allow us to short circuit when given "python.exe". | ||
246 | # If it does match, only test that one, otherwise we have to try | ||
247 | # others. | ||
248 | if any(cmd.lower().endswith(ext.lower()) for ext in pathext): | ||
249 | files = [cmd] | ||
250 | else: | ||
251 | files = [cmd + ext for ext in pathext] | ||
252 | else: | ||
253 | # On other platforms you don't have things like PATHEXT to tell you | ||
254 | # what file suffixes are executable, so just pass on cmd as-is. | ||
255 | files = [cmd] | ||
256 | |||
257 | seen = set() | ||
258 | for dir in path: | ||
259 | normdir = os.path.normcase(dir) | ||
260 | if not normdir in seen: | ||
261 | seen.add(normdir) | ||
262 | for thefile in files: | ||
263 | name = os.path.join(dir, thefile) | ||
264 | if _access_check(name, mode): | ||
265 | return name | ||
266 | return None | ||
267 | |||
268 | |||
269 | # ZipFile is a context manager in 2.7, but not in 2.6 | ||
270 | |||
271 | from zipfile import ZipFile as BaseZipFile | ||
272 | |||
273 | if hasattr(BaseZipFile, '__enter__'): # pragma: no cover | ||
274 | ZipFile = BaseZipFile | ||
275 | else: # pragma: no cover | ||
276 | from zipfile import ZipExtFile as BaseZipExtFile | ||
277 | |||
278 | class ZipExtFile(BaseZipExtFile): | ||
279 | def __init__(self, base): | ||
280 | self.__dict__.update(base.__dict__) | ||
281 | |||
282 | def __enter__(self): | ||
283 | return self | ||
284 | |||
285 | def __exit__(self, *exc_info): | ||
286 | self.close() | ||
287 | # return None, so if an exception occurred, it will propagate | ||
288 | |||
289 | class ZipFile(BaseZipFile): | ||
290 | def __enter__(self): | ||
291 | return self | ||
292 | |||
293 | def __exit__(self, *exc_info): | ||
294 | self.close() | ||
295 | # return None, so if an exception occurred, it will propagate | ||
296 | |||
297 | def open(self, *args, **kwargs): | ||
298 | base = BaseZipFile.open(self, *args, **kwargs) | ||
299 | return ZipExtFile(base) | ||
300 | |||
301 | try: | ||
302 | from platform import python_implementation | ||
303 | except ImportError: # pragma: no cover | ||
304 | def python_implementation(): | ||
305 | """Return a string identifying the Python implementation.""" | ||
306 | if 'PyPy' in sys.version: | ||
307 | return 'PyPy' | ||
308 | if os.name == 'java': | ||
309 | return 'Jython' | ||
310 | if sys.version.startswith('IronPython'): | ||
311 | return 'IronPython' | ||
312 | return 'CPython' | ||
313 | |||
314 | try: | ||
315 | import sysconfig | ||
316 | except ImportError: # pragma: no cover | ||
317 | from ._backport import sysconfig | ||
318 | |||
319 | try: | ||
320 | callable = callable | ||
321 | except NameError: # pragma: no cover | ||
322 | from collections import Callable | ||
323 | |||
324 | def callable(obj): | ||
325 | return isinstance(obj, Callable) | ||
326 | |||
327 | |||
328 | try: | ||
329 | fsencode = os.fsencode | ||
330 | fsdecode = os.fsdecode | ||
331 | except AttributeError: # pragma: no cover | ||
332 | # Issue #99: on some systems (e.g. containerised), | ||
333 | # sys.getfilesystemencoding() returns None, and we need a real value, | ||
334 | # so fall back to utf-8. From the CPython 2.7 docs relating to Unix and | ||
335 | # sys.getfilesystemencoding(): the return value is "the user’s preference | ||
336 | # according to the result of nl_langinfo(CODESET), or None if the | ||
337 | # nl_langinfo(CODESET) failed." | ||
338 | _fsencoding = sys.getfilesystemencoding() or 'utf-8' | ||
339 | if _fsencoding == 'mbcs': | ||
340 | _fserrors = 'strict' | ||
341 | else: | ||
342 | _fserrors = 'surrogateescape' | ||
343 | |||
344 | def fsencode(filename): | ||
345 | if isinstance(filename, bytes): | ||
346 | return filename | ||
347 | elif isinstance(filename, text_type): | ||
348 | return filename.encode(_fsencoding, _fserrors) | ||
349 | else: | ||
350 | raise TypeError("expect bytes or str, not %s" % | ||
351 | type(filename).__name__) | ||
352 | |||
353 | def fsdecode(filename): | ||
354 | if isinstance(filename, text_type): | ||
355 | return filename | ||
356 | elif isinstance(filename, bytes): | ||
357 | return filename.decode(_fsencoding, _fserrors) | ||
358 | else: | ||
359 | raise TypeError("expect bytes or str, not %s" % | ||
360 | type(filename).__name__) | ||
361 | |||
362 | try: | ||
363 | from tokenize import detect_encoding | ||
364 | except ImportError: # pragma: no cover | ||
365 | from codecs import BOM_UTF8, lookup | ||
366 | import re | ||
367 | |||
368 | cookie_re = re.compile(r"coding[:=]\s*([-\w.]+)") | ||
369 | |||
370 | def _get_normal_name(orig_enc): | ||
371 | """Imitates get_normal_name in tokenizer.c.""" | ||
372 | # Only care about the first 12 characters. | ||
373 | enc = orig_enc[:12].lower().replace("_", "-") | ||
374 | if enc == "utf-8" or enc.startswith("utf-8-"): | ||
375 | return "utf-8" | ||
376 | if enc in ("latin-1", "iso-8859-1", "iso-latin-1") or \ | ||
377 | enc.startswith(("latin-1-", "iso-8859-1-", "iso-latin-1-")): | ||
378 | return "iso-8859-1" | ||
379 | return orig_enc | ||
380 | |||
381 | def detect_encoding(readline): | ||
382 | """ | ||
383 | The detect_encoding() function is used to detect the encoding that should | ||
384 | be used to decode a Python source file. It requires one argument, readline, | ||
385 | in the same way as the tokenize() generator. | ||
386 | |||
387 | It will call readline a maximum of twice, and return the encoding used | ||
388 | (as a string) and a list of any lines (left as bytes) it has read in. | ||
389 | |||
390 | It detects the encoding from the presence of a utf-8 bom or an encoding | ||
391 | cookie as specified in pep-0263. If both a bom and a cookie are present, | ||
392 | but disagree, a SyntaxError will be raised. If the encoding cookie is an | ||
393 | invalid charset, raise a SyntaxError. Note that if a utf-8 bom is found, | ||
394 | 'utf-8-sig' is returned. | ||
395 | |||
396 | If no encoding is specified, then the default of 'utf-8' will be returned. | ||
397 | """ | ||
398 | try: | ||
399 | filename = readline.__self__.name | ||
400 | except AttributeError: | ||
401 | filename = None | ||
402 | bom_found = False | ||
403 | encoding = None | ||
404 | default = 'utf-8' | ||
405 | def read_or_stop(): | ||
406 | try: | ||
407 | return readline() | ||
408 | except StopIteration: | ||
409 | return b'' | ||
410 | |||
411 | def find_cookie(line): | ||
412 | try: | ||
413 | # Decode as UTF-8. Either the line is an encoding declaration, | ||
414 | # in which case it should be pure ASCII, or it must be UTF-8 | ||
415 | # per default encoding. | ||
416 | line_string = line.decode('utf-8') | ||
417 | except UnicodeDecodeError: | ||
418 | msg = "invalid or missing encoding declaration" | ||
419 | if filename is not None: | ||
420 | msg = '{} for {!r}'.format(msg, filename) | ||
421 | raise SyntaxError(msg) | ||
422 | |||
423 | matches = cookie_re.findall(line_string) | ||
424 | if not matches: | ||
425 | return None | ||
426 | encoding = _get_normal_name(matches[0]) | ||
427 | try: | ||
428 | codec = lookup(encoding) | ||
429 | except LookupError: | ||
430 | # This behaviour mimics the Python interpreter | ||
431 | if filename is None: | ||
432 | msg = "unknown encoding: " + encoding | ||
433 | else: | ||
434 | msg = "unknown encoding for {!r}: {}".format(filename, | ||
435 | encoding) | ||
436 | raise SyntaxError(msg) | ||
437 | |||
438 | if bom_found: | ||
439 | if codec.name != 'utf-8': | ||
440 | # This behaviour mimics the Python interpreter | ||
441 | if filename is None: | ||
442 | msg = 'encoding problem: utf-8' | ||
443 | else: | ||
444 | msg = 'encoding problem for {!r}: utf-8'.format(filename) | ||
445 | raise SyntaxError(msg) | ||
446 | encoding += '-sig' | ||
447 | return encoding | ||
448 | |||
449 | first = read_or_stop() | ||
450 | if first.startswith(BOM_UTF8): | ||
451 | bom_found = True | ||
452 | first = first[3:] | ||
453 | default = 'utf-8-sig' | ||
454 | if not first: | ||
455 | return default, [] | ||
456 | |||
457 | encoding = find_cookie(first) | ||
458 | if encoding: | ||
459 | return encoding, [first] | ||
460 | |||
461 | second = read_or_stop() | ||
462 | if not second: | ||
463 | return default, [first] | ||
464 | |||
465 | encoding = find_cookie(second) | ||
466 | if encoding: | ||
467 | return encoding, [first, second] | ||
468 | |||
469 | return default, [first, second] | ||
470 | |||
471 | # For converting & <-> & etc. | ||
472 | try: | ||
473 | from html import escape | ||
474 | except ImportError: | ||
475 | from cgi import escape | ||
476 | if sys.version_info[:2] < (3, 4): | ||
477 | unescape = HTMLParser().unescape | ||
478 | else: | ||
479 | from html import unescape | ||
480 | |||
481 | try: | ||
482 | from collections import ChainMap | ||
483 | except ImportError: # pragma: no cover | ||
484 | from collections import MutableMapping | ||
485 | |||
486 | try: | ||
487 | from reprlib import recursive_repr as _recursive_repr | ||
488 | except ImportError: | ||
489 | def _recursive_repr(fillvalue='...'): | ||
490 | ''' | ||
491 | Decorator to make a repr function return fillvalue for a recursive | ||
492 | call | ||
493 | ''' | ||
494 | |||
495 | def decorating_function(user_function): | ||
496 | repr_running = set() | ||
497 | |||
498 | def wrapper(self): | ||
499 | key = id(self), get_ident() | ||
500 | if key in repr_running: | ||
501 | return fillvalue | ||
502 | repr_running.add(key) | ||
503 | try: | ||
504 | result = user_function(self) | ||
505 | finally: | ||
506 | repr_running.discard(key) | ||
507 | return result | ||
508 | |||
509 | # Can't use functools.wraps() here because of bootstrap issues | ||
510 | wrapper.__module__ = getattr(user_function, '__module__') | ||
511 | wrapper.__doc__ = getattr(user_function, '__doc__') | ||
512 | wrapper.__name__ = getattr(user_function, '__name__') | ||
513 | wrapper.__annotations__ = getattr(user_function, '__annotations__', {}) | ||
514 | return wrapper | ||
515 | |||
516 | return decorating_function | ||
517 | |||
518 | class ChainMap(MutableMapping): | ||
519 | ''' A ChainMap groups multiple dicts (or other mappings) together | ||
520 | to create a single, updateable view. | ||
521 | |||
522 | The underlying mappings are stored in a list. That list is public and can | ||
523 | accessed or updated using the *maps* attribute. There is no other state. | ||
524 | |||
525 | Lookups search the underlying mappings successively until a key is found. | ||
526 | In contrast, writes, updates, and deletions only operate on the first | ||
527 | mapping. | ||
528 | |||
529 | ''' | ||
530 | |||
531 | def __init__(self, *maps): | ||
532 | '''Initialize a ChainMap by setting *maps* to the given mappings. | ||
533 | If no mappings are provided, a single empty dictionary is used. | ||
534 | |||
535 | ''' | ||
536 | self.maps = list(maps) or [{}] # always at least one map | ||
537 | |||
538 | def __missing__(self, key): | ||
539 | raise KeyError(key) | ||
540 | |||
541 | def __getitem__(self, key): | ||
542 | for mapping in self.maps: | ||
543 | try: | ||
544 | return mapping[key] # can't use 'key in mapping' with defaultdict | ||
545 | except KeyError: | ||
546 | pass | ||
547 | return self.__missing__(key) # support subclasses that define __missing__ | ||
548 | |||
549 | def get(self, key, default=None): | ||
550 | return self[key] if key in self else default | ||
551 | |||
552 | def __len__(self): | ||
553 | return len(set().union(*self.maps)) # reuses stored hash values if possible | ||
554 | |||
555 | def __iter__(self): | ||
556 | return iter(set().union(*self.maps)) | ||
557 | |||
558 | def __contains__(self, key): | ||
559 | return any(key in m for m in self.maps) | ||
560 | |||
561 | def __bool__(self): | ||
562 | return any(self.maps) | ||
563 | |||
564 | @_recursive_repr() | ||
565 | def __repr__(self): | ||
566 | return '{0.__class__.__name__}({1})'.format( | ||
567 | self, ', '.join(map(repr, self.maps))) | ||
568 | |||
569 | @classmethod | ||
570 | def fromkeys(cls, iterable, *args): | ||
571 | 'Create a ChainMap with a single dict created from the iterable.' | ||
572 | return cls(dict.fromkeys(iterable, *args)) | ||
573 | |||
574 | def copy(self): | ||
575 | 'New ChainMap or subclass with a new copy of maps[0] and refs to maps[1:]' | ||
576 | return self.__class__(self.maps[0].copy(), *self.maps[1:]) | ||
577 | |||
578 | __copy__ = copy | ||
579 | |||
580 | def new_child(self): # like Django's Context.push() | ||
581 | 'New ChainMap with a new dict followed by all previous maps.' | ||
582 | return self.__class__({}, *self.maps) | ||
583 | |||
584 | @property | ||
585 | def parents(self): # like Django's Context.pop() | ||
586 | 'New ChainMap from maps[1:].' | ||
587 | return self.__class__(*self.maps[1:]) | ||
588 | |||
589 | def __setitem__(self, key, value): | ||
590 | self.maps[0][key] = value | ||
591 | |||
592 | def __delitem__(self, key): | ||
593 | try: | ||
594 | del self.maps[0][key] | ||
595 | except KeyError: | ||
596 | raise KeyError('Key not found in the first mapping: {!r}'.format(key)) | ||
597 | |||
598 | def popitem(self): | ||
599 | 'Remove and return an item pair from maps[0]. Raise KeyError is maps[0] is empty.' | ||
600 | try: | ||
601 | return self.maps[0].popitem() | ||
602 | except KeyError: | ||
603 | raise KeyError('No keys found in the first mapping.') | ||
604 | |||
605 | def pop(self, key, *args): | ||
606 | 'Remove *key* from maps[0] and return its value. Raise KeyError if *key* not in maps[0].' | ||
607 | try: | ||
608 | return self.maps[0].pop(key, *args) | ||
609 | except KeyError: | ||
610 | raise KeyError('Key not found in the first mapping: {!r}'.format(key)) | ||
611 | |||
612 | def clear(self): | ||
613 | 'Clear maps[0], leaving maps[1:] intact.' | ||
614 | self.maps[0].clear() | ||
615 | |||
616 | try: | ||
617 | from importlib.util import cache_from_source # Python >= 3.4 | ||
618 | except ImportError: # pragma: no cover | ||
619 | try: | ||
620 | from imp import cache_from_source | ||
621 | except ImportError: # pragma: no cover | ||
622 | def cache_from_source(path, debug_override=None): | ||
623 | assert path.endswith('.py') | ||
624 | if debug_override is None: | ||
625 | debug_override = __debug__ | ||
626 | if debug_override: | ||
627 | suffix = 'c' | ||
628 | else: | ||
629 | suffix = 'o' | ||
630 | return path + suffix | ||
631 | |||
632 | try: | ||
633 | from collections import OrderedDict | ||
634 | except ImportError: # pragma: no cover | ||
635 | ## {{{ http://code.activestate.com/recipes/576693/ (r9) | ||
636 | # Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy. | ||
637 | # Passes Python2.7's test suite and incorporates all the latest updates. | ||
638 | try: | ||
639 | from thread import get_ident as _get_ident | ||
640 | except ImportError: | ||
641 | from dummy_thread import get_ident as _get_ident | ||
642 | |||
643 | try: | ||
644 | from _abcoll import KeysView, ValuesView, ItemsView | ||
645 | except ImportError: | ||
646 | pass | ||
647 | |||
648 | |||
649 | class OrderedDict(dict): | ||
650 | 'Dictionary that remembers insertion order' | ||
651 | # An inherited dict maps keys to values. | ||
652 | # The inherited dict provides __getitem__, __len__, __contains__, and get. | ||
653 | # The remaining methods are order-aware. | ||
654 | # Big-O running times for all methods are the same as for regular dictionaries. | ||
655 | |||
656 | # The internal self.__map dictionary maps keys to links in a doubly linked list. | ||
657 | # The circular doubly linked list starts and ends with a sentinel element. | ||
658 | # The sentinel element never gets deleted (this simplifies the algorithm). | ||
659 | # Each link is stored as a list of length three: [PREV, NEXT, KEY]. | ||
660 | |||
661 | def __init__(self, *args, **kwds): | ||
662 | '''Initialize an ordered dictionary. Signature is the same as for | ||
663 | regular dictionaries, but keyword arguments are not recommended | ||
664 | because their insertion order is arbitrary. | ||
665 | |||
666 | ''' | ||
667 | if len(args) > 1: | ||
668 | raise TypeError('expected at most 1 arguments, got %d' % len(args)) | ||
669 | try: | ||
670 | self.__root | ||
671 | except AttributeError: | ||
672 | self.__root = root = [] # sentinel node | ||
673 | root[:] = [root, root, None] | ||
674 | self.__map = {} | ||
675 | self.__update(*args, **kwds) | ||
676 | |||
677 | def __setitem__(self, key, value, dict_setitem=dict.__setitem__): | ||
678 | 'od.__setitem__(i, y) <==> od[i]=y' | ||
679 | # Setting a new item creates a new link which goes at the end of the linked | ||
680 | # list, and the inherited dictionary is updated with the new key/value pair. | ||
681 | if key not in self: | ||
682 | root = self.__root | ||
683 | last = root[0] | ||
684 | last[1] = root[0] = self.__map[key] = [last, root, key] | ||
685 | dict_setitem(self, key, value) | ||
686 | |||
687 | def __delitem__(self, key, dict_delitem=dict.__delitem__): | ||
688 | 'od.__delitem__(y) <==> del od[y]' | ||
689 | # Deleting an existing item uses self.__map to find the link which is | ||
690 | # then removed by updating the links in the predecessor and successor nodes. | ||
691 | dict_delitem(self, key) | ||
692 | link_prev, link_next, key = self.__map.pop(key) | ||
693 | link_prev[1] = link_next | ||
694 | link_next[0] = link_prev | ||
695 | |||
696 | def __iter__(self): | ||
697 | 'od.__iter__() <==> iter(od)' | ||
698 | root = self.__root | ||
699 | curr = root[1] | ||
700 | while curr is not root: | ||
701 | yield curr[2] | ||
702 | curr = curr[1] | ||
703 | |||
704 | def __reversed__(self): | ||
705 | 'od.__reversed__() <==> reversed(od)' | ||
706 | root = self.__root | ||
707 | curr = root[0] | ||
708 | while curr is not root: | ||
709 | yield curr[2] | ||
710 | curr = curr[0] | ||
711 | |||
712 | def clear(self): | ||
713 | 'od.clear() -> None. Remove all items from od.' | ||
714 | try: | ||
715 | for node in self.__map.itervalues(): | ||
716 | del node[:] | ||
717 | root = self.__root | ||
718 | root[:] = [root, root, None] | ||
719 | self.__map.clear() | ||
720 | except AttributeError: | ||
721 | pass | ||
722 | dict.clear(self) | ||
723 | |||
724 | def popitem(self, last=True): | ||
725 | '''od.popitem() -> (k, v), return and remove a (key, value) pair. | ||
726 | Pairs are returned in LIFO order if last is true or FIFO order if false. | ||
727 | |||
728 | ''' | ||
729 | if not self: | ||
730 | raise KeyError('dictionary is empty') | ||
731 | root = self.__root | ||
732 | if last: | ||
733 | link = root[0] | ||
734 | link_prev = link[0] | ||
735 | link_prev[1] = root | ||
736 | root[0] = link_prev | ||
737 | else: | ||
738 | link = root[1] | ||
739 | link_next = link[1] | ||
740 | root[1] = link_next | ||
741 | link_next[0] = root | ||
742 | key = link[2] | ||
743 | del self.__map[key] | ||
744 | value = dict.pop(self, key) | ||
745 | return key, value | ||
746 | |||
747 | # -- the following methods do not depend on the internal structure -- | ||
748 | |||
749 | def keys(self): | ||
750 | 'od.keys() -> list of keys in od' | ||
751 | return list(self) | ||
752 | |||
753 | def values(self): | ||
754 | 'od.values() -> list of values in od' | ||
755 | return [self[key] for key in self] | ||
756 | |||
757 | def items(self): | ||
758 | 'od.items() -> list of (key, value) pairs in od' | ||
759 | return [(key, self[key]) for key in self] | ||
760 | |||
761 | def iterkeys(self): | ||
762 | 'od.iterkeys() -> an iterator over the keys in od' | ||
763 | return iter(self) | ||
764 | |||
765 | def itervalues(self): | ||
766 | 'od.itervalues -> an iterator over the values in od' | ||
767 | for k in self: | ||
768 | yield self[k] | ||
769 | |||
770 | def iteritems(self): | ||
771 | 'od.iteritems -> an iterator over the (key, value) items in od' | ||
772 | for k in self: | ||
773 | yield (k, self[k]) | ||
774 | |||
775 | def update(*args, **kwds): | ||
776 | '''od.update(E, **F) -> None. Update od from dict/iterable E and F. | ||
777 | |||
778 | If E is a dict instance, does: for k in E: od[k] = E[k] | ||
779 | If E has a .keys() method, does: for k in E.keys(): od[k] = E[k] | ||
780 | Or if E is an iterable of items, does: for k, v in E: od[k] = v | ||
781 | In either case, this is followed by: for k, v in F.items(): od[k] = v | ||
782 | |||
783 | ''' | ||
784 | if len(args) > 2: | ||
785 | raise TypeError('update() takes at most 2 positional ' | ||
786 | 'arguments (%d given)' % (len(args),)) | ||
787 | elif not args: | ||
788 | raise TypeError('update() takes at least 1 argument (0 given)') | ||
789 | self = args[0] | ||
790 | # Make progressively weaker assumptions about "other" | ||
791 | other = () | ||
792 | if len(args) == 2: | ||
793 | other = args[1] | ||
794 | if isinstance(other, dict): | ||
795 | for key in other: | ||
796 | self[key] = other[key] | ||
797 | elif hasattr(other, 'keys'): | ||
798 | for key in other.keys(): | ||
799 | self[key] = other[key] | ||
800 | else: | ||
801 | for key, value in other: | ||
802 | self[key] = value | ||
803 | for key, value in kwds.items(): | ||
804 | self[key] = value | ||
805 | |||
806 | __update = update # let subclasses override update without breaking __init__ | ||
807 | |||
808 | __marker = object() | ||
809 | |||
810 | def pop(self, key, default=__marker): | ||
811 | '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value. | ||
812 | If key is not found, d is returned if given, otherwise KeyError is raised. | ||
813 | |||
814 | ''' | ||
815 | if key in self: | ||
816 | result = self[key] | ||
817 | del self[key] | ||
818 | return result | ||
819 | if default is self.__marker: | ||
820 | raise KeyError(key) | ||
821 | return default | ||
822 | |||
823 | def setdefault(self, key, default=None): | ||
824 | 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' | ||
825 | if key in self: | ||
826 | return self[key] | ||
827 | self[key] = default | ||
828 | return default | ||
829 | |||
830 | def __repr__(self, _repr_running=None): | ||
831 | 'od.__repr__() <==> repr(od)' | ||
832 | if not _repr_running: _repr_running = {} | ||
833 | call_key = id(self), _get_ident() | ||
834 | if call_key in _repr_running: | ||
835 | return '...' | ||
836 | _repr_running[call_key] = 1 | ||
837 | try: | ||
838 | if not self: | ||
839 | return '%s()' % (self.__class__.__name__,) | ||
840 | return '%s(%r)' % (self.__class__.__name__, self.items()) | ||
841 | finally: | ||
842 | del _repr_running[call_key] | ||
843 | |||
844 | def __reduce__(self): | ||
845 | 'Return state information for pickling' | ||
846 | items = [[k, self[k]] for k in self] | ||
847 | inst_dict = vars(self).copy() | ||
848 | for k in vars(OrderedDict()): | ||
849 | inst_dict.pop(k, None) | ||
850 | if inst_dict: | ||
851 | return (self.__class__, (items,), inst_dict) | ||
852 | return self.__class__, (items,) | ||
853 | |||
854 | def copy(self): | ||
855 | 'od.copy() -> a shallow copy of od' | ||
856 | return self.__class__(self) | ||
857 | |||
858 | @classmethod | ||
859 | def fromkeys(cls, iterable, value=None): | ||
860 | '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S | ||
861 | and values equal to v (which defaults to None). | ||
862 | |||
863 | ''' | ||
864 | d = cls() | ||
865 | for key in iterable: | ||
866 | d[key] = value | ||
867 | return d | ||
868 | |||
869 | def __eq__(self, other): | ||
870 | '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive | ||
871 | while comparison to a regular mapping is order-insensitive. | ||
872 | |||
873 | ''' | ||
874 | if isinstance(other, OrderedDict): | ||
875 | return len(self)==len(other) and self.items() == other.items() | ||
876 | return dict.__eq__(self, other) | ||
877 | |||
878 | def __ne__(self, other): | ||
879 | return not self == other | ||
880 | |||
881 | # -- the following methods are only used in Python 2.7 -- | ||
882 | |||
883 | def viewkeys(self): | ||
884 | "od.viewkeys() -> a set-like object providing a view on od's keys" | ||
885 | return KeysView(self) | ||
886 | |||
887 | def viewvalues(self): | ||
888 | "od.viewvalues() -> an object providing a view on od's values" | ||
889 | return ValuesView(self) | ||
890 | |||
891 | def viewitems(self): | ||
892 | "od.viewitems() -> a set-like object providing a view on od's items" | ||
893 | return ItemsView(self) | ||
894 | |||
895 | try: | ||
896 | from logging.config import BaseConfigurator, valid_ident | ||
897 | except ImportError: # pragma: no cover | ||
898 | IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I) | ||
899 | |||
900 | |||
901 | def valid_ident(s): | ||
902 | m = IDENTIFIER.match(s) | ||
903 | if not m: | ||
904 | raise ValueError('Not a valid Python identifier: %r' % s) | ||
905 | return True | ||
906 | |||
907 | |||
908 | # The ConvertingXXX classes are wrappers around standard Python containers, | ||
909 | # and they serve to convert any suitable values in the container. The | ||
910 | # conversion converts base dicts, lists and tuples to their wrapped | ||
911 | # equivalents, whereas strings which match a conversion format are converted | ||
912 | # appropriately. | ||
913 | # | ||
914 | # Each wrapper should have a configurator attribute holding the actual | ||
915 | # configurator to use for conversion. | ||
916 | |||
917 | class ConvertingDict(dict): | ||
918 | """A converting dictionary wrapper.""" | ||
919 | |||
920 | def __getitem__(self, key): | ||
921 | value = dict.__getitem__(self, key) | ||
922 | result = self.configurator.convert(value) | ||
923 | #If the converted value is different, save for next time | ||
924 | if value is not result: | ||
925 | self[key] = result | ||
926 | if type(result) in (ConvertingDict, ConvertingList, | ||
927 | ConvertingTuple): | ||
928 | result.parent = self | ||
929 | result.key = key | ||
930 | return result | ||
931 | |||
932 | def get(self, key, default=None): | ||
933 | value = dict.get(self, key, default) | ||
934 | result = self.configurator.convert(value) | ||
935 | #If the converted value is different, save for next time | ||
936 | if value is not result: | ||
937 | self[key] = result | ||
938 | if type(result) in (ConvertingDict, ConvertingList, | ||
939 | ConvertingTuple): | ||
940 | result.parent = self | ||
941 | result.key = key | ||
942 | return result | ||
943 | |||
944 | def pop(self, key, default=None): | ||
945 | value = dict.pop(self, key, default) | ||
946 | result = self.configurator.convert(value) | ||
947 | if value is not result: | ||
948 | if type(result) in (ConvertingDict, ConvertingList, | ||
949 | ConvertingTuple): | ||
950 | result.parent = self | ||
951 | result.key = key | ||
952 | return result | ||
953 | |||
954 | class ConvertingList(list): | ||
955 | """A converting list wrapper.""" | ||
956 | def __getitem__(self, key): | ||
957 | value = list.__getitem__(self, key) | ||
958 | result = self.configurator.convert(value) | ||
959 | #If the converted value is different, save for next time | ||
960 | if value is not result: | ||
961 | self[key] = result | ||
962 | if type(result) in (ConvertingDict, ConvertingList, | ||
963 | ConvertingTuple): | ||
964 | result.parent = self | ||
965 | result.key = key | ||
966 | return result | ||
967 | |||
968 | def pop(self, idx=-1): | ||
969 | value = list.pop(self, idx) | ||
970 | result = self.configurator.convert(value) | ||
971 | if value is not result: | ||
972 | if type(result) in (ConvertingDict, ConvertingList, | ||
973 | ConvertingTuple): | ||
974 | result.parent = self | ||
975 | return result | ||
976 | |||
977 | class ConvertingTuple(tuple): | ||
978 | """A converting tuple wrapper.""" | ||
979 | def __getitem__(self, key): | ||
980 | value = tuple.__getitem__(self, key) | ||
981 | result = self.configurator.convert(value) | ||
982 | if value is not result: | ||
983 | if type(result) in (ConvertingDict, ConvertingList, | ||
984 | ConvertingTuple): | ||
985 | result.parent = self | ||
986 | result.key = key | ||
987 | return result | ||
988 | |||
989 | class BaseConfigurator(object): | ||
990 | """ | ||
991 | The configurator base class which defines some useful defaults. | ||
992 | """ | ||
993 | |||
994 | CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$') | ||
995 | |||
996 | WORD_PATTERN = re.compile(r'^\s*(\w+)\s*') | ||
997 | DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*') | ||
998 | INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*') | ||
999 | DIGIT_PATTERN = re.compile(r'^\d+$') | ||
1000 | |||
1001 | value_converters = { | ||
1002 | 'ext' : 'ext_convert', | ||
1003 | 'cfg' : 'cfg_convert', | ||
1004 | } | ||
1005 | |||
1006 | # We might want to use a different one, e.g. importlib | ||
1007 | importer = staticmethod(__import__) | ||
1008 | |||
1009 | def __init__(self, config): | ||
1010 | self.config = ConvertingDict(config) | ||
1011 | self.config.configurator = self | ||
1012 | |||
1013 | def resolve(self, s): | ||
1014 | """ | ||
1015 | Resolve strings to objects using standard import and attribute | ||
1016 | syntax. | ||
1017 | """ | ||
1018 | name = s.split('.') | ||
1019 | used = name.pop(0) | ||
1020 | try: | ||
1021 | found = self.importer(used) | ||
1022 | for frag in name: | ||
1023 | used += '.' + frag | ||
1024 | try: | ||
1025 | found = getattr(found, frag) | ||
1026 | except AttributeError: | ||
1027 | self.importer(used) | ||
1028 | found = getattr(found, frag) | ||
1029 | return found | ||
1030 | except ImportError: | ||
1031 | e, tb = sys.exc_info()[1:] | ||
1032 | v = ValueError('Cannot resolve %r: %s' % (s, e)) | ||
1033 | v.__cause__, v.__traceback__ = e, tb | ||
1034 | raise v | ||
1035 | |||
1036 | def ext_convert(self, value): | ||
1037 | """Default converter for the ext:// protocol.""" | ||
1038 | return self.resolve(value) | ||
1039 | |||
1040 | def cfg_convert(self, value): | ||
1041 | """Default converter for the cfg:// protocol.""" | ||
1042 | rest = value | ||
1043 | m = self.WORD_PATTERN.match(rest) | ||
1044 | if m is None: | ||
1045 | raise ValueError("Unable to convert %r" % value) | ||
1046 | else: | ||
1047 | rest = rest[m.end():] | ||
1048 | d = self.config[m.groups()[0]] | ||
1049 | #print d, rest | ||
1050 | while rest: | ||
1051 | m = self.DOT_PATTERN.match(rest) | ||
1052 | if m: | ||
1053 | d = d[m.groups()[0]] | ||
1054 | else: | ||
1055 | m = self.INDEX_PATTERN.match(rest) | ||
1056 | if m: | ||
1057 | idx = m.groups()[0] | ||
1058 | if not self.DIGIT_PATTERN.match(idx): | ||
1059 | d = d[idx] | ||
1060 | else: | ||
1061 | try: | ||
1062 | n = int(idx) # try as number first (most likely) | ||
1063 | d = d[n] | ||
1064 | except TypeError: | ||
1065 | d = d[idx] | ||
1066 | if m: | ||
1067 | rest = rest[m.end():] | ||
1068 | else: | ||
1069 | raise ValueError('Unable to convert ' | ||
1070 | '%r at %r' % (value, rest)) | ||
1071 | #rest should be empty | ||
1072 | return d | ||
1073 | |||
1074 | def convert(self, value): | ||
1075 | """ | ||
1076 | Convert values to an appropriate type. dicts, lists and tuples are | ||
1077 | replaced by their converting alternatives. Strings are checked to | ||
1078 | see if they have a conversion format and are converted if they do. | ||
1079 | """ | ||
1080 | if not isinstance(value, ConvertingDict) and isinstance(value, dict): | ||
1081 | value = ConvertingDict(value) | ||
1082 | value.configurator = self | ||
1083 | elif not isinstance(value, ConvertingList) and isinstance(value, list): | ||
1084 | value = ConvertingList(value) | ||
1085 | value.configurator = self | ||
1086 | elif not isinstance(value, ConvertingTuple) and\ | ||
1087 | isinstance(value, tuple): | ||
1088 | value = ConvertingTuple(value) | ||
1089 | value.configurator = self | ||
1090 | elif isinstance(value, string_types): | ||
1091 | m = self.CONVERT_PATTERN.match(value) | ||
1092 | if m: | ||
1093 | d = m.groupdict() | ||
1094 | prefix = d['prefix'] | ||
1095 | converter = self.value_converters.get(prefix, None) | ||
1096 | if converter: | ||
1097 | suffix = d['suffix'] | ||
1098 | converter = getattr(self, converter) | ||
1099 | value = converter(suffix) | ||
1100 | return value | ||
1101 | |||
1102 | def configure_custom(self, config): | ||
1103 | """Configure an object with a user-supplied factory.""" | ||
1104 | c = config.pop('()') | ||
1105 | if not callable(c): | ||
1106 | c = self.resolve(c) | ||
1107 | props = config.pop('.', None) | ||
1108 | # Check for valid identifiers | ||
1109 | kwargs = dict([(k, config[k]) for k in config if valid_ident(k)]) | ||
1110 | result = c(**kwargs) | ||
1111 | if props: | ||
1112 | for name, value in props.items(): | ||
1113 | setattr(result, name, value) | ||
1114 | return result | ||
1115 | |||
1116 | def as_tuple(self, value): | ||
1117 | """Utility function which converts lists to tuples.""" | ||
1118 | if isinstance(value, list): | ||
1119 | value = tuple(value) | ||
1120 | return value | ||