Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/distlib/compat.py: 92%

39 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-25 06:51 +0000

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# 

7from __future__ import absolute_import 

8 

9import os 

10import re 

11import sys 

12 

13try: 

14 import ssl 

15except ImportError: # pragma: no cover 

16 ssl = None 

17 

18if 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 urlparse import urlparse, urlunparse, urljoin, urlsplit, urlunsplit 

26 from urllib import (urlretrieve, quote as _quote, unquote, url2pathname, 

27 pathname2url, ContentTooShortError, splittype) 

28 

29 def quote(s): 

30 if isinstance(s, unicode): 

31 s = s.encode('utf-8') 

32 return _quote(s) 

33 

34 import urllib2 

35 from urllib2 import (Request, urlopen, URLError, HTTPError, 

36 HTTPBasicAuthHandler, HTTPPasswordMgr, 

37 HTTPHandler, HTTPRedirectHandler, 

38 build_opener) 

39 if ssl: 

40 from urllib2 import HTTPSHandler 

41 import httplib 

42 import xmlrpclib 

43 import Queue as queue 

44 from HTMLParser import HTMLParser 

45 import htmlentitydefs 

46 raw_input = raw_input 

47 from itertools import ifilter as filter 

48 from itertools import ifilterfalse as filterfalse 

49 

50 # Leaving this around for now, in case it needs resurrecting in some way 

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 

63else: # 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, 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 

92try: 

93 from ssl import match_hostname, CertificateError 

94except ImportError: # pragma: no cover 

95 class CertificateError(ValueError): 

96 pass 

97 

98 

99 def _dnsname_match(dn, hostname, max_wildcards=1): 

100 """Matching according to RFC 6125, section 6.4.3 

101 

102 http://tools.ietf.org/html/rfc6125#section-6.4.3 

103 """ 

104 pats = [] 

105 if not dn: 

106 return False 

107 

108 parts = dn.split('.') 

109 leftmost, remainder = parts[0], parts[1:] 

110 

111 wildcards = leftmost.count('*') 

112 if wildcards > max_wildcards: 

113 # Issue #17980: avoid denials of service by refusing more 

114 # than one wildcard per fragment. A survey of established 

115 # policy among SSL implementations showed it to be a 

116 # reasonable choice. 

117 raise CertificateError( 

118 "too many wildcards in certificate DNS name: " + repr(dn)) 

119 

120 # speed up common case w/o wildcards 

121 if not wildcards: 

122 return dn.lower() == hostname.lower() 

123 

124 # RFC 6125, section 6.4.3, subitem 1. 

125 # The client SHOULD NOT attempt to match a presented identifier in which 

126 # the wildcard character comprises a label other than the left-most label. 

127 if leftmost == '*': 

128 # When '*' is a fragment by itself, it matches a non-empty dotless 

129 # fragment. 

130 pats.append('[^.]+') 

131 elif leftmost.startswith('xn--') or hostname.startswith('xn--'): 

132 # RFC 6125, section 6.4.3, subitem 3. 

133 # The client SHOULD NOT attempt to match a presented identifier 

134 # where the wildcard character is embedded within an A-label or 

135 # U-label of an internationalized domain name. 

136 pats.append(re.escape(leftmost)) 

137 else: 

138 # Otherwise, '*' matches any dotless string, e.g. www* 

139 pats.append(re.escape(leftmost).replace(r'\*', '[^.]*')) 

140 

141 # add the remaining fragments, ignore any wildcards 

142 for frag in remainder: 

143 pats.append(re.escape(frag)) 

144 

145 pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) 

146 return pat.match(hostname) 

147 

148 

149 def match_hostname(cert, hostname): 

150 """Verify that *cert* (in decoded format as returned by 

151 SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 

152 rules are followed, but IP addresses are not accepted for *hostname*. 

153 

154 CertificateError is raised on failure. On success, the function 

155 returns nothing. 

156 """ 

157 if not cert: 

158 raise ValueError("empty or no certificate, match_hostname needs a " 

159 "SSL socket or SSL context with either " 

160 "CERT_OPTIONAL or CERT_REQUIRED") 

161 dnsnames = [] 

162 san = cert.get('subjectAltName', ()) 

163 for key, value in san: 

164 if key == 'DNS': 

165 if _dnsname_match(value, hostname): 

166 return 

167 dnsnames.append(value) 

168 if not dnsnames: 

169 # The subject is only checked when there is no dNSName entry 

170 # in subjectAltName 

171 for sub in cert.get('subject', ()): 

172 for key, value in sub: 

173 # XXX according to RFC 2818, the most specific Common Name 

174 # must be used. 

175 if key == 'commonName': 

176 if _dnsname_match(value, hostname): 

177 return 

178 dnsnames.append(value) 

179 if len(dnsnames) > 1: 

180 raise CertificateError("hostname %r " 

181 "doesn't match either of %s" 

182 % (hostname, ', '.join(map(repr, dnsnames)))) 

183 elif len(dnsnames) == 1: 

184 raise CertificateError("hostname %r " 

185 "doesn't match %r" 

186 % (hostname, dnsnames[0])) 

187 else: 

188 raise CertificateError("no appropriate commonName or " 

189 "subjectAltName fields were found") 

190 

191 

192try: 

193 from types import SimpleNamespace as Container 

194except ImportError: # pragma: no cover 

195 class Container(object): 

196 """ 

197 A generic container for when multiple values need to be returned 

198 """ 

199 def __init__(self, **kwargs): 

200 self.__dict__.update(kwargs) 

201 

202 

203try: 

204 from shutil import which 

205except ImportError: # pragma: no cover 

206 # Implementation from Python 3.3 

207 def which(cmd, mode=os.F_OK | os.X_OK, path=None): 

208 """Given a command, mode, and a PATH string, return the path which 

209 conforms to the given mode on the PATH, or None if there is no such 

210 file. 

211 

212 `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result 

213 of os.environ.get("PATH"), or can be overridden with a custom search 

214 path. 

215 

216 """ 

217 # Check that a given file can be accessed with the correct mode. 

218 # Additionally check that `file` is not a directory, as on Windows 

219 # directories pass the os.access check. 

220 def _access_check(fn, mode): 

221 return (os.path.exists(fn) and os.access(fn, mode) 

222 and not os.path.isdir(fn)) 

223 

224 # If we're given a path with a directory part, look it up directly rather 

225 # than referring to PATH directories. This includes checking relative to the 

226 # current directory, e.g. ./script 

227 if os.path.dirname(cmd): 

228 if _access_check(cmd, mode): 

229 return cmd 

230 return None 

231 

232 if path is None: 

233 path = os.environ.get("PATH", os.defpath) 

234 if not path: 

235 return None 

236 path = path.split(os.pathsep) 

237 

238 if sys.platform == "win32": 

239 # The current directory takes precedence on Windows. 

240 if not os.curdir in path: 

241 path.insert(0, os.curdir) 

242 

243 # PATHEXT is necessary to check on Windows. 

244 pathext = os.environ.get("PATHEXT", "").split(os.pathsep) 

245 # See if the given file matches any of the expected path extensions. 

246 # This will allow us to short circuit when given "python.exe". 

247 # If it does match, only test that one, otherwise we have to try 

248 # others. 

249 if any(cmd.lower().endswith(ext.lower()) for ext in pathext): 

250 files = [cmd] 

251 else: 

252 files = [cmd + ext for ext in pathext] 

253 else: 

254 # On other platforms you don't have things like PATHEXT to tell you 

255 # what file suffixes are executable, so just pass on cmd as-is. 

256 files = [cmd] 

257 

258 seen = set() 

259 for dir in path: 

260 normdir = os.path.normcase(dir) 

261 if not normdir in seen: 

262 seen.add(normdir) 

263 for thefile in files: 

264 name = os.path.join(dir, thefile) 

265 if _access_check(name, mode): 

266 return name 

267 return None 

268 

269 

270# ZipFile is a context manager in 2.7, but not in 2.6 

271 

272from zipfile import ZipFile as BaseZipFile 

273 

274if hasattr(BaseZipFile, '__enter__'): # pragma: no cover 

275 ZipFile = BaseZipFile 

276else: # pragma: no cover 

277 from zipfile import ZipExtFile as BaseZipExtFile 

278 

279 class ZipExtFile(BaseZipExtFile): 

280 def __init__(self, base): 

281 self.__dict__.update(base.__dict__) 

282 

283 def __enter__(self): 

284 return self 

285 

286 def __exit__(self, *exc_info): 

287 self.close() 

288 # return None, so if an exception occurred, it will propagate 

289 

290 class ZipFile(BaseZipFile): 

291 def __enter__(self): 

292 return self 

293 

294 def __exit__(self, *exc_info): 

295 self.close() 

296 # return None, so if an exception occurred, it will propagate 

297 

298 def open(self, *args, **kwargs): 

299 base = BaseZipFile.open(self, *args, **kwargs) 

300 return ZipExtFile(base) 

301 

302try: 

303 from platform import python_implementation 

304except ImportError: # pragma: no cover 

305 def python_implementation(): 

306 """Return a string identifying the Python implementation.""" 

307 if 'PyPy' in sys.version: 

308 return 'PyPy' 

309 if os.name == 'java': 

310 return 'Jython' 

311 if sys.version.startswith('IronPython'): 

312 return 'IronPython' 

313 return 'CPython' 

314 

315import shutil 

316import sysconfig 

317 

318try: 

319 callable = callable 

320except NameError: # pragma: no cover 

321 from collections.abc import Callable 

322 

323 def callable(obj): 

324 return isinstance(obj, Callable) 

325 

326 

327try: 

328 fsencode = os.fsencode 

329 fsdecode = os.fsdecode 

330except AttributeError: # pragma: no cover 

331 # Issue #99: on some systems (e.g. containerised), 

332 # sys.getfilesystemencoding() returns None, and we need a real value, 

333 # so fall back to utf-8. From the CPython 2.7 docs relating to Unix and 

334 # sys.getfilesystemencoding(): the return value is "the user’s preference 

335 # according to the result of nl_langinfo(CODESET), or None if the 

336 # nl_langinfo(CODESET) failed." 

337 _fsencoding = sys.getfilesystemencoding() or 'utf-8' 

338 if _fsencoding == 'mbcs': 

339 _fserrors = 'strict' 

340 else: 

341 _fserrors = 'surrogateescape' 

342 

343 def fsencode(filename): 

344 if isinstance(filename, bytes): 

345 return filename 

346 elif isinstance(filename, text_type): 

347 return filename.encode(_fsencoding, _fserrors) 

348 else: 

349 raise TypeError("expect bytes or str, not %s" % 

350 type(filename).__name__) 

351 

352 def fsdecode(filename): 

353 if isinstance(filename, text_type): 

354 return filename 

355 elif isinstance(filename, bytes): 

356 return filename.decode(_fsencoding, _fserrors) 

357 else: 

358 raise TypeError("expect bytes or str, not %s" % 

359 type(filename).__name__) 

360 

361try: 

362 from tokenize import detect_encoding 

363except ImportError: # pragma: no cover 

364 from codecs import BOM_UTF8, lookup 

365 import re 

366 

367 cookie_re = re.compile(r"coding[:=]\s*([-\w.]+)") 

368 

369 def _get_normal_name(orig_enc): 

370 """Imitates get_normal_name in tokenizer.c.""" 

371 # Only care about the first 12 characters. 

372 enc = orig_enc[:12].lower().replace("_", "-") 

373 if enc == "utf-8" or enc.startswith("utf-8-"): 

374 return "utf-8" 

375 if enc in ("latin-1", "iso-8859-1", "iso-latin-1") or \ 

376 enc.startswith(("latin-1-", "iso-8859-1-", "iso-latin-1-")): 

377 return "iso-8859-1" 

378 return orig_enc 

379 

380 def detect_encoding(readline): 

381 """ 

382 The detect_encoding() function is used to detect the encoding that should 

383 be used to decode a Python source file. It requires one argument, readline, 

384 in the same way as the tokenize() generator. 

385 

386 It will call readline a maximum of twice, and return the encoding used 

387 (as a string) and a list of any lines (left as bytes) it has read in. 

388 

389 It detects the encoding from the presence of a utf-8 bom or an encoding 

390 cookie as specified in pep-0263. If both a bom and a cookie are present, 

391 but disagree, a SyntaxError will be raised. If the encoding cookie is an 

392 invalid charset, raise a SyntaxError. Note that if a utf-8 bom is found, 

393 'utf-8-sig' is returned. 

394 

395 If no encoding is specified, then the default of 'utf-8' will be returned. 

396 """ 

397 try: 

398 filename = readline.__self__.name 

399 except AttributeError: 

400 filename = None 

401 bom_found = False 

402 encoding = None 

403 default = 'utf-8' 

404 def read_or_stop(): 

405 try: 

406 return readline() 

407 except StopIteration: 

408 return b'' 

409 

410 def find_cookie(line): 

411 try: 

412 # Decode as UTF-8. Either the line is an encoding declaration, 

413 # in which case it should be pure ASCII, or it must be UTF-8 

414 # per default encoding. 

415 line_string = line.decode('utf-8') 

416 except UnicodeDecodeError: 

417 msg = "invalid or missing encoding declaration" 

418 if filename is not None: 

419 msg = '{} for {!r}'.format(msg, filename) 

420 raise SyntaxError(msg) 

421 

422 matches = cookie_re.findall(line_string) 

423 if not matches: 

424 return None 

425 encoding = _get_normal_name(matches[0]) 

426 try: 

427 codec = lookup(encoding) 

428 except LookupError: 

429 # This behaviour mimics the Python interpreter 

430 if filename is None: 

431 msg = "unknown encoding: " + encoding 

432 else: 

433 msg = "unknown encoding for {!r}: {}".format(filename, 

434 encoding) 

435 raise SyntaxError(msg) 

436 

437 if bom_found: 

438 if codec.name != 'utf-8': 

439 # This behaviour mimics the Python interpreter 

440 if filename is None: 

441 msg = 'encoding problem: utf-8' 

442 else: 

443 msg = 'encoding problem for {!r}: utf-8'.format(filename) 

444 raise SyntaxError(msg) 

445 encoding += '-sig' 

446 return encoding 

447 

448 first = read_or_stop() 

449 if first.startswith(BOM_UTF8): 

450 bom_found = True 

451 first = first[3:] 

452 default = 'utf-8-sig' 

453 if not first: 

454 return default, [] 

455 

456 encoding = find_cookie(first) 

457 if encoding: 

458 return encoding, [first] 

459 

460 second = read_or_stop() 

461 if not second: 

462 return default, [first] 

463 

464 encoding = find_cookie(second) 

465 if encoding: 

466 return encoding, [first, second] 

467 

468 return default, [first, second] 

469 

470# For converting & <-> &amp; etc. 

471try: 

472 from html import escape 

473except ImportError: 

474 from cgi import escape 

475if sys.version_info[:2] < (3, 4): 

476 unescape = HTMLParser().unescape 

477else: 

478 from html import unescape 

479 

480try: 

481 from collections import ChainMap 

482except ImportError: # pragma: no cover 

483 from collections import MutableMapping 

484 

485 try: 

486 from reprlib import recursive_repr as _recursive_repr 

487 except ImportError: 

488 def _recursive_repr(fillvalue='...'): 

489 ''' 

490 Decorator to make a repr function return fillvalue for a recursive 

491 call 

492 ''' 

493 

494 def decorating_function(user_function): 

495 repr_running = set() 

496 

497 def wrapper(self): 

498 key = id(self), get_ident() 

499 if key in repr_running: 

500 return fillvalue 

501 repr_running.add(key) 

502 try: 

503 result = user_function(self) 

504 finally: 

505 repr_running.discard(key) 

506 return result 

507 

508 # Can't use functools.wraps() here because of bootstrap issues 

509 wrapper.__module__ = getattr(user_function, '__module__') 

510 wrapper.__doc__ = getattr(user_function, '__doc__') 

511 wrapper.__name__ = getattr(user_function, '__name__') 

512 wrapper.__annotations__ = getattr(user_function, '__annotations__', {}) 

513 return wrapper 

514 

515 return decorating_function 

516 

517 class ChainMap(MutableMapping): 

518 ''' A ChainMap groups multiple dicts (or other mappings) together 

519 to create a single, updateable view. 

520 

521 The underlying mappings are stored in a list. That list is public and can 

522 accessed or updated using the *maps* attribute. There is no other state. 

523 

524 Lookups search the underlying mappings successively until a key is found. 

525 In contrast, writes, updates, and deletions only operate on the first 

526 mapping. 

527 

528 ''' 

529 

530 def __init__(self, *maps): 

531 '''Initialize a ChainMap by setting *maps* to the given mappings. 

532 If no mappings are provided, a single empty dictionary is used. 

533 

534 ''' 

535 self.maps = list(maps) or [{}] # always at least one map 

536 

537 def __missing__(self, key): 

538 raise KeyError(key) 

539 

540 def __getitem__(self, key): 

541 for mapping in self.maps: 

542 try: 

543 return mapping[key] # can't use 'key in mapping' with defaultdict 

544 except KeyError: 

545 pass 

546 return self.__missing__(key) # support subclasses that define __missing__ 

547 

548 def get(self, key, default=None): 

549 return self[key] if key in self else default 

550 

551 def __len__(self): 

552 return len(set().union(*self.maps)) # reuses stored hash values if possible 

553 

554 def __iter__(self): 

555 return iter(set().union(*self.maps)) 

556 

557 def __contains__(self, key): 

558 return any(key in m for m in self.maps) 

559 

560 def __bool__(self): 

561 return any(self.maps) 

562 

563 @_recursive_repr() 

564 def __repr__(self): 

565 return '{0.__class__.__name__}({1})'.format( 

566 self, ', '.join(map(repr, self.maps))) 

567 

568 @classmethod 

569 def fromkeys(cls, iterable, *args): 

570 'Create a ChainMap with a single dict created from the iterable.' 

571 return cls(dict.fromkeys(iterable, *args)) 

572 

573 def copy(self): 

574 'New ChainMap or subclass with a new copy of maps[0] and refs to maps[1:]' 

575 return self.__class__(self.maps[0].copy(), *self.maps[1:]) 

576 

577 __copy__ = copy 

578 

579 def new_child(self): # like Django's Context.push() 

580 'New ChainMap with a new dict followed by all previous maps.' 

581 return self.__class__({}, *self.maps) 

582 

583 @property 

584 def parents(self): # like Django's Context.pop() 

585 'New ChainMap from maps[1:].' 

586 return self.__class__(*self.maps[1:]) 

587 

588 def __setitem__(self, key, value): 

589 self.maps[0][key] = value 

590 

591 def __delitem__(self, key): 

592 try: 

593 del self.maps[0][key] 

594 except KeyError: 

595 raise KeyError('Key not found in the first mapping: {!r}'.format(key)) 

596 

597 def popitem(self): 

598 'Remove and return an item pair from maps[0]. Raise KeyError is maps[0] is empty.' 

599 try: 

600 return self.maps[0].popitem() 

601 except KeyError: 

602 raise KeyError('No keys found in the first mapping.') 

603 

604 def pop(self, key, *args): 

605 'Remove *key* from maps[0] and return its value. Raise KeyError if *key* not in maps[0].' 

606 try: 

607 return self.maps[0].pop(key, *args) 

608 except KeyError: 

609 raise KeyError('Key not found in the first mapping: {!r}'.format(key)) 

610 

611 def clear(self): 

612 'Clear maps[0], leaving maps[1:] intact.' 

613 self.maps[0].clear() 

614 

615try: 

616 from importlib.util import cache_from_source # Python >= 3.4 

617except ImportError: # pragma: no cover 

618 def cache_from_source(path, debug_override=None): 

619 assert path.endswith('.py') 

620 if debug_override is None: 

621 debug_override = __debug__ 

622 if debug_override: 

623 suffix = 'c' 

624 else: 

625 suffix = 'o' 

626 return path + suffix 

627 

628try: 

629 from collections import OrderedDict 

630except ImportError: # pragma: no cover 

631## {{{ http://code.activestate.com/recipes/576693/ (r9) 

632# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy. 

633# Passes Python2.7's test suite and incorporates all the latest updates. 

634 try: 

635 from thread import get_ident as _get_ident 

636 except ImportError: 

637 from dummy_thread import get_ident as _get_ident 

638 

639 try: 

640 from _abcoll import KeysView, ValuesView, ItemsView 

641 except ImportError: 

642 pass 

643 

644 

645 class OrderedDict(dict): 

646 'Dictionary that remembers insertion order' 

647 # An inherited dict maps keys to values. 

648 # The inherited dict provides __getitem__, __len__, __contains__, and get. 

649 # The remaining methods are order-aware. 

650 # Big-O running times for all methods are the same as for regular dictionaries. 

651 

652 # The internal self.__map dictionary maps keys to links in a doubly linked list. 

653 # The circular doubly linked list starts and ends with a sentinel element. 

654 # The sentinel element never gets deleted (this simplifies the algorithm). 

655 # Each link is stored as a list of length three: [PREV, NEXT, KEY]. 

656 

657 def __init__(self, *args, **kwds): 

658 '''Initialize an ordered dictionary. Signature is the same as for 

659 regular dictionaries, but keyword arguments are not recommended 

660 because their insertion order is arbitrary. 

661 

662 ''' 

663 if len(args) > 1: 

664 raise TypeError('expected at most 1 arguments, got %d' % len(args)) 

665 try: 

666 self.__root 

667 except AttributeError: 

668 self.__root = root = [] # sentinel node 

669 root[:] = [root, root, None] 

670 self.__map = {} 

671 self.__update(*args, **kwds) 

672 

673 def __setitem__(self, key, value, dict_setitem=dict.__setitem__): 

674 'od.__setitem__(i, y) <==> od[i]=y' 

675 # Setting a new item creates a new link which goes at the end of the linked 

676 # list, and the inherited dictionary is updated with the new key/value pair. 

677 if key not in self: 

678 root = self.__root 

679 last = root[0] 

680 last[1] = root[0] = self.__map[key] = [last, root, key] 

681 dict_setitem(self, key, value) 

682 

683 def __delitem__(self, key, dict_delitem=dict.__delitem__): 

684 'od.__delitem__(y) <==> del od[y]' 

685 # Deleting an existing item uses self.__map to find the link which is 

686 # then removed by updating the links in the predecessor and successor nodes. 

687 dict_delitem(self, key) 

688 link_prev, link_next, key = self.__map.pop(key) 

689 link_prev[1] = link_next 

690 link_next[0] = link_prev 

691 

692 def __iter__(self): 

693 'od.__iter__() <==> iter(od)' 

694 root = self.__root 

695 curr = root[1] 

696 while curr is not root: 

697 yield curr[2] 

698 curr = curr[1] 

699 

700 def __reversed__(self): 

701 'od.__reversed__() <==> reversed(od)' 

702 root = self.__root 

703 curr = root[0] 

704 while curr is not root: 

705 yield curr[2] 

706 curr = curr[0] 

707 

708 def clear(self): 

709 'od.clear() -> None. Remove all items from od.' 

710 try: 

711 for node in self.__map.itervalues(): 

712 del node[:] 

713 root = self.__root 

714 root[:] = [root, root, None] 

715 self.__map.clear() 

716 except AttributeError: 

717 pass 

718 dict.clear(self) 

719 

720 def popitem(self, last=True): 

721 '''od.popitem() -> (k, v), return and remove a (key, value) pair. 

722 Pairs are returned in LIFO order if last is true or FIFO order if false. 

723 

724 ''' 

725 if not self: 

726 raise KeyError('dictionary is empty') 

727 root = self.__root 

728 if last: 

729 link = root[0] 

730 link_prev = link[0] 

731 link_prev[1] = root 

732 root[0] = link_prev 

733 else: 

734 link = root[1] 

735 link_next = link[1] 

736 root[1] = link_next 

737 link_next[0] = root 

738 key = link[2] 

739 del self.__map[key] 

740 value = dict.pop(self, key) 

741 return key, value 

742 

743 # -- the following methods do not depend on the internal structure -- 

744 

745 def keys(self): 

746 'od.keys() -> list of keys in od' 

747 return list(self) 

748 

749 def values(self): 

750 'od.values() -> list of values in od' 

751 return [self[key] for key in self] 

752 

753 def items(self): 

754 'od.items() -> list of (key, value) pairs in od' 

755 return [(key, self[key]) for key in self] 

756 

757 def iterkeys(self): 

758 'od.iterkeys() -> an iterator over the keys in od' 

759 return iter(self) 

760 

761 def itervalues(self): 

762 'od.itervalues -> an iterator over the values in od' 

763 for k in self: 

764 yield self[k] 

765 

766 def iteritems(self): 

767 'od.iteritems -> an iterator over the (key, value) items in od' 

768 for k in self: 

769 yield (k, self[k]) 

770 

771 def update(*args, **kwds): 

772 '''od.update(E, **F) -> None. Update od from dict/iterable E and F. 

773 

774 If E is a dict instance, does: for k in E: od[k] = E[k] 

775 If E has a .keys() method, does: for k in E.keys(): od[k] = E[k] 

776 Or if E is an iterable of items, does: for k, v in E: od[k] = v 

777 In either case, this is followed by: for k, v in F.items(): od[k] = v 

778 

779 ''' 

780 if len(args) > 2: 

781 raise TypeError('update() takes at most 2 positional ' 

782 'arguments (%d given)' % (len(args),)) 

783 elif not args: 

784 raise TypeError('update() takes at least 1 argument (0 given)') 

785 self = args[0] 

786 # Make progressively weaker assumptions about "other" 

787 other = () 

788 if len(args) == 2: 

789 other = args[1] 

790 if isinstance(other, dict): 

791 for key in other: 

792 self[key] = other[key] 

793 elif hasattr(other, 'keys'): 

794 for key in other.keys(): 

795 self[key] = other[key] 

796 else: 

797 for key, value in other: 

798 self[key] = value 

799 for key, value in kwds.items(): 

800 self[key] = value 

801 

802 __update = update # let subclasses override update without breaking __init__ 

803 

804 __marker = object() 

805 

806 def pop(self, key, default=__marker): 

807 '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value. 

808 If key is not found, d is returned if given, otherwise KeyError is raised. 

809 

810 ''' 

811 if key in self: 

812 result = self[key] 

813 del self[key] 

814 return result 

815 if default is self.__marker: 

816 raise KeyError(key) 

817 return default 

818 

819 def setdefault(self, key, default=None): 

820 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' 

821 if key in self: 

822 return self[key] 

823 self[key] = default 

824 return default 

825 

826 def __repr__(self, _repr_running=None): 

827 'od.__repr__() <==> repr(od)' 

828 if not _repr_running: _repr_running = {} 

829 call_key = id(self), _get_ident() 

830 if call_key in _repr_running: 

831 return '...' 

832 _repr_running[call_key] = 1 

833 try: 

834 if not self: 

835 return '%s()' % (self.__class__.__name__,) 

836 return '%s(%r)' % (self.__class__.__name__, self.items()) 

837 finally: 

838 del _repr_running[call_key] 

839 

840 def __reduce__(self): 

841 'Return state information for pickling' 

842 items = [[k, self[k]] for k in self] 

843 inst_dict = vars(self).copy() 

844 for k in vars(OrderedDict()): 

845 inst_dict.pop(k, None) 

846 if inst_dict: 

847 return (self.__class__, (items,), inst_dict) 

848 return self.__class__, (items,) 

849 

850 def copy(self): 

851 'od.copy() -> a shallow copy of od' 

852 return self.__class__(self) 

853 

854 @classmethod 

855 def fromkeys(cls, iterable, value=None): 

856 '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S 

857 and values equal to v (which defaults to None). 

858 

859 ''' 

860 d = cls() 

861 for key in iterable: 

862 d[key] = value 

863 return d 

864 

865 def __eq__(self, other): 

866 '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive 

867 while comparison to a regular mapping is order-insensitive. 

868 

869 ''' 

870 if isinstance(other, OrderedDict): 

871 return len(self)==len(other) and self.items() == other.items() 

872 return dict.__eq__(self, other) 

873 

874 def __ne__(self, other): 

875 return not self == other 

876 

877 # -- the following methods are only used in Python 2.7 -- 

878 

879 def viewkeys(self): 

880 "od.viewkeys() -> a set-like object providing a view on od's keys" 

881 return KeysView(self) 

882 

883 def viewvalues(self): 

884 "od.viewvalues() -> an object providing a view on od's values" 

885 return ValuesView(self) 

886 

887 def viewitems(self): 

888 "od.viewitems() -> a set-like object providing a view on od's items" 

889 return ItemsView(self) 

890 

891try: 

892 from logging.config import BaseConfigurator, valid_ident 

893except ImportError: # pragma: no cover 

894 IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I) 

895 

896 

897 def valid_ident(s): 

898 m = IDENTIFIER.match(s) 

899 if not m: 

900 raise ValueError('Not a valid Python identifier: %r' % s) 

901 return True 

902 

903 

904 # The ConvertingXXX classes are wrappers around standard Python containers, 

905 # and they serve to convert any suitable values in the container. The 

906 # conversion converts base dicts, lists and tuples to their wrapped 

907 # equivalents, whereas strings which match a conversion format are converted 

908 # appropriately. 

909 # 

910 # Each wrapper should have a configurator attribute holding the actual 

911 # configurator to use for conversion. 

912 

913 class ConvertingDict(dict): 

914 """A converting dictionary wrapper.""" 

915 

916 def __getitem__(self, key): 

917 value = dict.__getitem__(self, key) 

918 result = self.configurator.convert(value) 

919 #If the converted value is different, save for next time 

920 if value is not result: 

921 self[key] = result 

922 if type(result) in (ConvertingDict, ConvertingList, 

923 ConvertingTuple): 

924 result.parent = self 

925 result.key = key 

926 return result 

927 

928 def get(self, key, default=None): 

929 value = dict.get(self, key, default) 

930 result = self.configurator.convert(value) 

931 #If the converted value is different, save for next time 

932 if value is not result: 

933 self[key] = result 

934 if type(result) in (ConvertingDict, ConvertingList, 

935 ConvertingTuple): 

936 result.parent = self 

937 result.key = key 

938 return result 

939 

940 def pop(self, key, default=None): 

941 value = dict.pop(self, key, default) 

942 result = self.configurator.convert(value) 

943 if value is not result: 

944 if type(result) in (ConvertingDict, ConvertingList, 

945 ConvertingTuple): 

946 result.parent = self 

947 result.key = key 

948 return result 

949 

950 class ConvertingList(list): 

951 """A converting list wrapper.""" 

952 def __getitem__(self, key): 

953 value = list.__getitem__(self, key) 

954 result = self.configurator.convert(value) 

955 #If the converted value is different, save for next time 

956 if value is not result: 

957 self[key] = result 

958 if type(result) in (ConvertingDict, ConvertingList, 

959 ConvertingTuple): 

960 result.parent = self 

961 result.key = key 

962 return result 

963 

964 def pop(self, idx=-1): 

965 value = list.pop(self, idx) 

966 result = self.configurator.convert(value) 

967 if value is not result: 

968 if type(result) in (ConvertingDict, ConvertingList, 

969 ConvertingTuple): 

970 result.parent = self 

971 return result 

972 

973 class ConvertingTuple(tuple): 

974 """A converting tuple wrapper.""" 

975 def __getitem__(self, key): 

976 value = tuple.__getitem__(self, key) 

977 result = self.configurator.convert(value) 

978 if value is not result: 

979 if type(result) in (ConvertingDict, ConvertingList, 

980 ConvertingTuple): 

981 result.parent = self 

982 result.key = key 

983 return result 

984 

985 class BaseConfigurator(object): 

986 """ 

987 The configurator base class which defines some useful defaults. 

988 """ 

989 

990 CONVERT_PATTERN = re.compile(r'^(?P<prefix>[a-z]+)://(?P<suffix>.*)$') 

991 

992 WORD_PATTERN = re.compile(r'^\s*(\w+)\s*') 

993 DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*') 

994 INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*') 

995 DIGIT_PATTERN = re.compile(r'^\d+$') 

996 

997 value_converters = { 

998 'ext' : 'ext_convert', 

999 'cfg' : 'cfg_convert', 

1000 } 

1001 

1002 # We might want to use a different one, e.g. importlib 

1003 importer = staticmethod(__import__) 

1004 

1005 def __init__(self, config): 

1006 self.config = ConvertingDict(config) 

1007 self.config.configurator = self 

1008 

1009 def resolve(self, s): 

1010 """ 

1011 Resolve strings to objects using standard import and attribute 

1012 syntax. 

1013 """ 

1014 name = s.split('.') 

1015 used = name.pop(0) 

1016 try: 

1017 found = self.importer(used) 

1018 for frag in name: 

1019 used += '.' + frag 

1020 try: 

1021 found = getattr(found, frag) 

1022 except AttributeError: 

1023 self.importer(used) 

1024 found = getattr(found, frag) 

1025 return found 

1026 except ImportError: 

1027 e, tb = sys.exc_info()[1:] 

1028 v = ValueError('Cannot resolve %r: %s' % (s, e)) 

1029 v.__cause__, v.__traceback__ = e, tb 

1030 raise v 

1031 

1032 def ext_convert(self, value): 

1033 """Default converter for the ext:// protocol.""" 

1034 return self.resolve(value) 

1035 

1036 def cfg_convert(self, value): 

1037 """Default converter for the cfg:// protocol.""" 

1038 rest = value 

1039 m = self.WORD_PATTERN.match(rest) 

1040 if m is None: 

1041 raise ValueError("Unable to convert %r" % value) 

1042 else: 

1043 rest = rest[m.end():] 

1044 d = self.config[m.groups()[0]] 

1045 #print d, rest 

1046 while rest: 

1047 m = self.DOT_PATTERN.match(rest) 

1048 if m: 

1049 d = d[m.groups()[0]] 

1050 else: 

1051 m = self.INDEX_PATTERN.match(rest) 

1052 if m: 

1053 idx = m.groups()[0] 

1054 if not self.DIGIT_PATTERN.match(idx): 

1055 d = d[idx] 

1056 else: 

1057 try: 

1058 n = int(idx) # try as number first (most likely) 

1059 d = d[n] 

1060 except TypeError: 

1061 d = d[idx] 

1062 if m: 

1063 rest = rest[m.end():] 

1064 else: 

1065 raise ValueError('Unable to convert ' 

1066 '%r at %r' % (value, rest)) 

1067 #rest should be empty 

1068 return d 

1069 

1070 def convert(self, value): 

1071 """ 

1072 Convert values to an appropriate type. dicts, lists and tuples are 

1073 replaced by their converting alternatives. Strings are checked to 

1074 see if they have a conversion format and are converted if they do. 

1075 """ 

1076 if not isinstance(value, ConvertingDict) and isinstance(value, dict): 

1077 value = ConvertingDict(value) 

1078 value.configurator = self 

1079 elif not isinstance(value, ConvertingList) and isinstance(value, list): 

1080 value = ConvertingList(value) 

1081 value.configurator = self 

1082 elif not isinstance(value, ConvertingTuple) and\ 

1083 isinstance(value, tuple): 

1084 value = ConvertingTuple(value) 

1085 value.configurator = self 

1086 elif isinstance(value, string_types): 

1087 m = self.CONVERT_PATTERN.match(value) 

1088 if m: 

1089 d = m.groupdict() 

1090 prefix = d['prefix'] 

1091 converter = self.value_converters.get(prefix, None) 

1092 if converter: 

1093 suffix = d['suffix'] 

1094 converter = getattr(self, converter) 

1095 value = converter(suffix) 

1096 return value 

1097 

1098 def configure_custom(self, config): 

1099 """Configure an object with a user-supplied factory.""" 

1100 c = config.pop('()') 

1101 if not callable(c): 

1102 c = self.resolve(c) 

1103 props = config.pop('.', None) 

1104 # Check for valid identifiers 

1105 kwargs = dict([(k, config[k]) for k in config if valid_ident(k)]) 

1106 result = c(**kwargs) 

1107 if props: 

1108 for name, value in props.items(): 

1109 setattr(result, name, value) 

1110 return result 

1111 

1112 def as_tuple(self, value): 

1113 """Utility function which converts lists to tuples.""" 

1114 if isinstance(value, list): 

1115 value = tuple(value) 

1116 return value