Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pip/_vendor/distlib/compat.py: 90%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

40 statements  

1# -*- coding: utf-8 -*- 

2# 

3# Copyright (C) 2013-2026 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 shutil 

12import sys 

13 

14try: 

15 import ssl 

16except ImportError: # pragma: no cover 

17 ssl = None 

18 

19if sys.version_info[0] < 3: # pragma: no cover 

20 from StringIO import StringIO 

21 string_types = basestring, 

22 text_type = unicode 

23 from types import FileType as file_type 

24 import __builtin__ as builtins 

25 import ConfigParser as configparser 

26 from urlparse import urlparse, urlunparse, urljoin, urlsplit, urlunsplit 

27 from urllib import (urlretrieve, quote as _quote, unquote, url2pathname, pathname2url, ContentTooShortError, 

28 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, HTTPBasicAuthHandler, HTTPPasswordMgr, HTTPHandler, 

37 HTTPRedirectHandler, build_opener) 

38 if ssl: 

39 from urllib2 import HTTPSHandler 

40 import httplib 

41 import xmlrpclib 

42 import Queue as queue 

43 from HTMLParser import HTMLParser 

44 import htmlentitydefs 

45 raw_input = raw_input 

46 from itertools import ifilter as filter 

47 from itertools import ifilterfalse as filterfalse 

48 

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

50 # _userprog = None 

51 # def splituser(host): 

52 # """splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]'.""" 

53 # global _userprog 

54 # if _userprog is None: 

55 # import re 

56 # _userprog = re.compile('^(.*)@(.*)$') 

57 

58 # match = _userprog.match(host) 

59 # if match: return match.group(1, 2) 

60 # return None, host 

61 

62else: # pragma: no cover 

63 from io import StringIO 

64 string_types = str, 

65 text_type = str 

66 from io import TextIOWrapper as file_type 

67 import builtins 

68 import configparser 

69 from urllib.parse import (urlparse, urlunparse, urljoin, quote, unquote, urlsplit, urlunsplit, splittype) 

70 from urllib.request import (urlopen, urlretrieve, Request, url2pathname, pathname2url, HTTPBasicAuthHandler, 

71 HTTPPasswordMgr, HTTPHandler, HTTPRedirectHandler, build_opener) 

72 if ssl: 

73 from urllib.request import HTTPSHandler 

74 from urllib.error import HTTPError, URLError, ContentTooShortError 

75 import http.client as httplib 

76 import urllib.request as urllib2 

77 import xmlrpc.client as xmlrpclib 

78 import queue 

79 from html.parser import HTMLParser 

80 import html.entities as htmlentitydefs 

81 raw_input = input 

82 from itertools import filterfalse 

83 filter = filter 

84 

85try: 

86 from ssl import match_hostname, CertificateError 

87except ImportError: # pragma: no cover 

88 

89 class CertificateError(ValueError): 

90 pass 

91 

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

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

94 

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

96 """ 

97 pats = [] 

98 if not dn: 

99 return False 

100 

101 parts = dn.split('.') 

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

103 

104 wildcards = leftmost.count('*') 

105 if wildcards > max_wildcards: 

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

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

108 # policy among SSL implementations showed it to be a 

109 # reasonable choice. 

110 raise CertificateError('Too many wildcards in certificate DNS name: %r' % dn) 

111 

112 # speed up common case w/o wildcards 

113 if not wildcards: 

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

115 

116 # RFC 6125, section 6.4.3, subitem 1. 

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

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

119 if leftmost == '*': 

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

121 # fragment. 

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

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

124 # RFC 6125, section 6.4.3, subitem 3. 

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

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

127 # U-label of an internationalized domain name. 

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

129 else: 

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

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

132 

133 # add the remaining fragments, ignore any wildcards 

134 for frag in remainder: 

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

136 

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

138 return pat.match(hostname) 

139 

140 def match_hostname(cert, hostname): 

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

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

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

144 

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

146 returns nothing. 

147 """ 

148 if not cert: 

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

150 "SSL socket or SSL context with either " 

151 "CERT_OPTIONAL or CERT_REQUIRED") 

152 dnsnames = [] 

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

154 for key, value in san: 

155 if key == 'DNS': 

156 if _dnsname_match(value, hostname): 

157 return 

158 dnsnames.append(value) 

159 if not dnsnames: 

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

161 # in subjectAltName 

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

163 for key, value in sub: 

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

165 # must be used. 

166 if key == 'commonName': 

167 if _dnsname_match(value, hostname): 

168 return 

169 dnsnames.append(value) 

170 if len(dnsnames) > 1: 

171 raise CertificateError("hostname %r " 

172 "doesn't match either of %s" % (hostname, ', '.join(map(repr, dnsnames)))) 

173 elif len(dnsnames) == 1: 

174 raise CertificateError("hostname %r " 

175 "doesn't match %r" % (hostname, dnsnames[0])) 

176 else: 

177 raise CertificateError("no appropriate commonName or " 

178 "subjectAltName fields were found") 

179 

180 

181try: 

182 from types import SimpleNamespace as Container 

183except ImportError: # pragma: no cover 

184 

185 class Container(object): 

186 """ 

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

188 """ 

189 

190 def __init__(self, **kwargs): 

191 self.__dict__.update(kwargs) 

192 

193 

194try: 

195 from shutil import which 

196except ImportError: # pragma: no cover 

197 # Implementation from Python 3.3 

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

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

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

201 file. 

202 

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

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

205 path. 

206 

207 """ 

208 

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

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

211 # directories pass the os.access check. 

212 def _access_check(fn, mode): 

213 return (os.path.exists(fn) and os.access(fn, mode) and not os.path.isdir(fn)) 

214 

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

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

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

218 if os.path.dirname(cmd): 

219 if _access_check(cmd, mode): 

220 return cmd 

221 return None 

222 

223 if path is None: 

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

225 if not path: 

226 return None 

227 path = path.split(os.pathsep) 

228 

229 if sys.platform == "win32": 

230 # The current directory takes precedence on Windows. 

231 if os.curdir not in path: 

232 path.insert(0, os.curdir) 

233 

234 # PATHEXT is necessary to check on Windows. 

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

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

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

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

239 # others. 

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

241 files = [cmd] 

242 else: 

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

244 else: 

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

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

247 files = [cmd] 

248 

249 seen = set() 

250 for dir in path: 

251 normdir = os.path.normcase(dir) 

252 if normdir not in seen: 

253 seen.add(normdir) 

254 for thefile in files: 

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

256 if _access_check(name, mode): 

257 return name 

258 return None 

259 

260 

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

262 

263from zipfile import ZipFile as BaseZipFile 

264 

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

266 ZipFile = BaseZipFile 

267else: # pragma: no cover 

268 from zipfile import ZipExtFile as BaseZipExtFile 

269 

270 class ZipExtFile(BaseZipExtFile): 

271 

272 def __init__(self, base): 

273 self.__dict__.update(base.__dict__) 

274 

275 def __enter__(self): 

276 return self 

277 

278 def __exit__(self, *exc_info): 

279 self.close() 

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

281 

282 class ZipFile(BaseZipFile): 

283 

284 def __enter__(self): 

285 return self 

286 

287 def __exit__(self, *exc_info): 

288 self.close() 

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

290 

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

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

293 return ZipExtFile(base) 

294 

295 

296try: 

297 from platform import python_implementation 

298except ImportError: # pragma: no cover 

299 

300 def python_implementation(): 

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

302 if 'PyPy' in sys.version: 

303 return 'PyPy' 

304 if os.name == 'java': 

305 return 'Jython' 

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

307 return 'IronPython' 

308 return 'CPython' 

309 

310 

311import sysconfig 

312 

313try: 

314 callable = callable 

315except NameError: # pragma: no cover 

316 from collections.abc import Callable 

317 

318 def callable(obj): 

319 return isinstance(obj, Callable) 

320 

321 

322try: 

323 fsencode = os.fsencode 

324 fsdecode = os.fsdecode 

325except AttributeError: # pragma: no cover 

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

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

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

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

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

331 # nl_langinfo(CODESET) failed." 

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

333 if _fsencoding == 'mbcs': 

334 _fserrors = 'strict' 

335 else: 

336 _fserrors = 'surrogateescape' 

337 

338 def fsencode(filename): 

339 if isinstance(filename, bytes): 

340 return filename 

341 elif isinstance(filename, text_type): 

342 return filename.encode(_fsencoding, _fserrors) 

343 else: 

344 raise TypeError("expect bytes or str, not %s" % type(filename).__name__) 

345 

346 def fsdecode(filename): 

347 if isinstance(filename, text_type): 

348 return filename 

349 elif isinstance(filename, bytes): 

350 return filename.decode(_fsencoding, _fserrors) 

351 else: 

352 raise TypeError("expect bytes or str, not %s" % type(filename).__name__) 

353 

354 

355try: 

356 from tokenize import detect_encoding 

357except ImportError: # pragma: no cover 

358 from codecs import BOM_UTF8, lookup 

359 

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

361 

362 def _get_normal_name(orig_enc): 

363 """Imitates get_normal_name in tokenizer.c.""" 

364 # Only care about the first 12 characters. 

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

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

367 return "utf-8" 

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

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

370 return "iso-8859-1" 

371 return orig_enc 

372 

373 def detect_encoding(readline): 

374 """ 

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

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

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

378 

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

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

381 

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

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

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

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

386 'utf-8-sig' is returned. 

387 

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

389 """ 

390 try: 

391 filename = readline.__self__.name 

392 except AttributeError: 

393 filename = None 

394 bom_found = False 

395 encoding = None 

396 default = 'utf-8' 

397 

398 def read_or_stop(): 

399 try: 

400 return readline() 

401 except StopIteration: 

402 return b'' 

403 

404 def find_cookie(line): 

405 try: 

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

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

408 # per default encoding. 

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

410 except UnicodeDecodeError: 

411 msg = "invalid or missing encoding declaration" 

412 if filename is not None: 

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

414 raise SyntaxError(msg) 

415 

416 matches = cookie_re.findall(line_string) 

417 if not matches: 

418 return None 

419 encoding = _get_normal_name(matches[0]) 

420 try: 

421 codec = lookup(encoding) 

422 except LookupError: 

423 # This behaviour mimics the Python interpreter 

424 if filename is None: 

425 msg = "unknown encoding: " + encoding 

426 else: 

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

428 raise SyntaxError(msg) 

429 

430 if bom_found: 

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

432 # This behaviour mimics the Python interpreter 

433 if filename is None: 

434 msg = 'Encoding problem: utf-8' 

435 else: 

436 msg = 'Encoding problem for %r: utf-8' % filename 

437 raise SyntaxError(msg) 

438 encoding += '-sig' 

439 return encoding 

440 

441 first = read_or_stop() 

442 if first.startswith(BOM_UTF8): 

443 bom_found = True 

444 first = first[3:] 

445 default = 'utf-8-sig' 

446 if not first: 

447 return default, [] 

448 

449 encoding = find_cookie(first) 

450 if encoding: 

451 return encoding, [first] 

452 

453 second = read_or_stop() 

454 if not second: 

455 return default, [first] 

456 

457 encoding = find_cookie(second) 

458 if encoding: 

459 return encoding, [first, second] 

460 

461 return default, [first, second] 

462 

463 

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

465try: 

466 from html import escape 

467except ImportError: 

468 from cgi import escape 

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

470 unescape = HTMLParser().unescape 

471else: 

472 from html import unescape 

473 

474try: 

475 from collections import ChainMap 

476except ImportError: # pragma: no cover 

477 from collections import MutableMapping 

478 

479 try: 

480 from reprlib import recursive_repr as _recursive_repr 

481 except ImportError: 

482 

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

484 ''' 

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

486 call 

487 ''' 

488 

489 def decorating_function(user_function): 

490 repr_running = set() 

491 

492 def wrapper(self): 

493 key = id(self), get_ident() 

494 if key in repr_running: 

495 return fillvalue 

496 repr_running.add(key) 

497 try: 

498 result = user_function(self) 

499 finally: 

500 repr_running.discard(key) 

501 return result 

502 

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

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

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

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

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

508 return wrapper 

509 

510 return decorating_function 

511 

512 class ChainMap(MutableMapping): 

513 ''' 

514 A ChainMap groups multiple dicts (or other mappings) together 

515 to create a single, updateable view. 

516 

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

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

519 

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

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

522 mapping. 

523 ''' 

524 

525 def __init__(self, *maps): 

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

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

528 

529 ''' 

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

531 

532 def __missing__(self, key): 

533 raise KeyError(key) 

534 

535 def __getitem__(self, key): 

536 for mapping in self.maps: 

537 try: 

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

539 except KeyError: 

540 pass 

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

542 

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

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

545 

546 def __len__(self): 

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

548 

549 def __iter__(self): 

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

551 

552 def __contains__(self, key): 

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

554 

555 def __bool__(self): 

556 return any(self.maps) 

557 

558 @_recursive_repr() 

559 def __repr__(self): 

560 return '{0.__class__.__name__}({1})'.format(self, ', '.join(map(repr, self.maps))) 

561 

562 @classmethod 

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

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

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

566 

567 def copy(self): 

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

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

570 

571 __copy__ = copy 

572 

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

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

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

576 

577 @property 

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

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

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

581 

582 def __setitem__(self, key, value): 

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

584 

585 def __delitem__(self, key): 

586 try: 

587 del self.maps[0][key] 

588 except KeyError: 

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

590 

591 def popitem(self): 

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

593 try: 

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

595 except KeyError: 

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

597 

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

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

600 try: 

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

602 except KeyError: 

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

604 

605 def clear(self): 

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

607 self.maps[0].clear() 

608 

609 

610try: 

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

612except ImportError: # pragma: no cover 

613 

614 def cache_from_source(path, debug_override=None, optimization=None): 

615 assert path.endswith('.py') 

616 if debug_override is None: 

617 debug_override = __debug__ 

618 if debug_override: 

619 suffix = 'c' 

620 else: 

621 suffix = 'o' 

622 return path + suffix 

623 

624 

625try: 

626 from collections import OrderedDict 

627except ImportError: # pragma: no cover 

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

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

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

631 try: 

632 from thread import get_ident as _get_ident 

633 except ImportError: 

634 from dummy_thread import get_ident as _get_ident 

635 

636 try: 

637 from _abcoll import KeysView, ValuesView, ItemsView 

638 except ImportError: 

639 pass 

640 

641 class OrderedDict(dict): 

642 'Dictionary that remembers insertion order' 

643 

644 # An inherited dict maps keys to values. 

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

646 # The remaining methods are order-aware. 

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

648 

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

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

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

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

653 

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

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

656 regular dictionaries, but keyword arguments are not recommended 

657 because their insertion order is arbitrary. 

658 

659 ''' 

660 if len(args) > 1: 

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

662 try: 

663 self.__root 

664 except AttributeError: 

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

666 root[:] = [root, root, None] 

667 self.__map = {} 

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

669 

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

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

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

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

674 if key not in self: 

675 root = self.__root 

676 last = root[0] 

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

678 dict_setitem(self, key, value) 

679 

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

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

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

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

684 dict_delitem(self, key) 

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

686 link_prev[1] = link_next 

687 link_next[0] = link_prev 

688 

689 def __iter__(self): 

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

691 root = self.__root 

692 curr = root[1] 

693 while curr is not root: 

694 yield curr[2] 

695 curr = curr[1] 

696 

697 def __reversed__(self): 

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

699 root = self.__root 

700 curr = root[0] 

701 while curr is not root: 

702 yield curr[2] 

703 curr = curr[0] 

704 

705 def clear(self): 

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

707 try: 

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

709 del node[:] 

710 root = self.__root 

711 root[:] = [root, root, None] 

712 self.__map.clear() 

713 except AttributeError: 

714 pass 

715 dict.clear(self) 

716 

717 def popitem(self, last=True): 

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

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

720 

721 ''' 

722 if not self: 

723 raise KeyError('dictionary is empty') 

724 root = self.__root 

725 if last: 

726 link = root[0] 

727 link_prev = link[0] 

728 link_prev[1] = root 

729 root[0] = link_prev 

730 else: 

731 link = root[1] 

732 link_next = link[1] 

733 root[1] = link_next 

734 link_next[0] = root 

735 key = link[2] 

736 del self.__map[key] 

737 value = dict.pop(self, key) 

738 return key, value 

739 

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

741 

742 def keys(self): 

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

744 return list(self) 

745 

746 def values(self): 

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

748 return [self[key] for key in self] 

749 

750 def items(self): 

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

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

753 

754 def iterkeys(self): 

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

756 return iter(self) 

757 

758 def itervalues(self): 

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

760 for k in self: 

761 yield self[k] 

762 

763 def iteritems(self): 

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

765 for k in self: 

766 yield (k, self[k]) 

767 

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

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

770 

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

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

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

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

775 

776 ''' 

777 if len(args) > 2: 

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

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

780 elif not args: 

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

782 self = args[0] 

783 # Make progressively weaker assumptions about "other" 

784 other = () 

785 if len(args) == 2: 

786 other = args[1] 

787 if isinstance(other, dict): 

788 for key in other: 

789 self[key] = other[key] 

790 elif hasattr(other, 'keys'): 

791 for key in other.keys(): 

792 self[key] = other[key] 

793 else: 

794 for key, value in other: 

795 self[key] = value 

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

797 self[key] = value 

798 

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

800 

801 __marker = object() 

802 

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

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

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

806 

807 ''' 

808 if key in self: 

809 result = self[key] 

810 del self[key] 

811 return result 

812 if default is self.__marker: 

813 raise KeyError(key) 

814 return default 

815 

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

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

818 if key in self: 

819 return self[key] 

820 self[key] = default 

821 return default 

822 

823 def __repr__(self, _repr_running=None): 

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

825 if not _repr_running: 

826 _repr_running = {} 

827 call_key = id(self), _get_ident() 

828 if call_key in _repr_running: 

829 return '...' 

830 _repr_running[call_key] = 1 

831 try: 

832 if not self: 

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

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

835 finally: 

836 del _repr_running[call_key] 

837 

838 def __reduce__(self): 

839 'Return state information for pickling' 

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

841 inst_dict = vars(self).copy() 

842 for k in vars(OrderedDict()): 

843 inst_dict.pop(k, None) 

844 if inst_dict: 

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

846 return self.__class__, (items, ) 

847 

848 def copy(self): 

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

850 return self.__class__(self) 

851 

852 @classmethod 

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

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

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

856 

857 ''' 

858 d = cls() 

859 for key in iterable: 

860 d[key] = value 

861 return d 

862 

863 def __eq__(self, other): 

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

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

866 

867 ''' 

868 if isinstance(other, OrderedDict): 

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

870 return dict.__eq__(self, other) 

871 

872 def __ne__(self, other): 

873 return not self == other 

874 

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

876 

877 def viewkeys(self): 

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

879 return KeysView(self) 

880 

881 def viewvalues(self): 

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

883 return ValuesView(self) 

884 

885 def viewitems(self): 

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

887 return ItemsView(self) 

888 

889 

890try: 

891 from logging.config import BaseConfigurator, valid_ident 

892except ImportError: # pragma: no cover 

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

894 

895 def valid_ident(s): 

896 m = IDENTIFIER.match(s) 

897 if not m: 

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

899 return True 

900 

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

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

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

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

905 # appropriately. 

906 # 

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

908 # configurator to use for conversion. 

909 

910 class ConvertingDict(dict): 

911 """A converting dictionary wrapper.""" 

912 

913 def __getitem__(self, key): 

914 value = dict.__getitem__(self, key) 

915 result = self.configurator.convert(value) 

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

917 if value is not result: 

918 self[key] = result 

919 if type(result) in (ConvertingDict, ConvertingList, ConvertingTuple): 

920 result.parent = self 

921 result.key = key 

922 return result 

923 

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

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

926 result = self.configurator.convert(value) 

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

928 if value is not result: 

929 self[key] = result 

930 if type(result) in (ConvertingDict, ConvertingList, ConvertingTuple): 

931 result.parent = self 

932 result.key = key 

933 return result 

934 

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

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

937 result = self.configurator.convert(value) 

938 if value is not result: 

939 if type(result) in (ConvertingDict, ConvertingList, ConvertingTuple): 

940 result.parent = self 

941 result.key = key 

942 return result 

943 

944 class ConvertingList(list): 

945 """A converting list wrapper.""" 

946 

947 def __getitem__(self, key): 

948 value = list.__getitem__(self, key) 

949 result = self.configurator.convert(value) 

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

951 if value is not result: 

952 self[key] = result 

953 if type(result) in (ConvertingDict, ConvertingList, ConvertingTuple): 

954 result.parent = self 

955 result.key = key 

956 return result 

957 

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

959 value = list.pop(self, idx) 

960 result = self.configurator.convert(value) 

961 if value is not result: 

962 if type(result) in (ConvertingDict, ConvertingList, ConvertingTuple): 

963 result.parent = self 

964 return result 

965 

966 class ConvertingTuple(tuple): 

967 """A converting tuple wrapper.""" 

968 

969 def __getitem__(self, key): 

970 value = tuple.__getitem__(self, key) 

971 result = self.configurator.convert(value) 

972 if value is not result: 

973 if type(result) in (ConvertingDict, ConvertingList, ConvertingTuple): 

974 result.parent = self 

975 result.key = key 

976 return result 

977 

978 class BaseConfigurator(object): 

979 """ 

980 The configurator base class which defines some useful defaults. 

981 """ 

982 

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

984 

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

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

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

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

989 

990 value_converters = { 

991 'ext': 'ext_convert', 

992 'cfg': 'cfg_convert', 

993 } 

994 

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

996 importer = staticmethod(__import__) 

997 

998 def __init__(self, config): 

999 self.config = ConvertingDict(config) 

1000 self.config.configurator = self 

1001 

1002 def resolve(self, s): 

1003 """ 

1004 Resolve strings to objects using standard import and attribute 

1005 syntax. 

1006 """ 

1007 name = s.split('.') 

1008 used = name.pop(0) 

1009 try: 

1010 found = self.importer(used) 

1011 for frag in name: 

1012 used += '.' + frag 

1013 try: 

1014 found = getattr(found, frag) 

1015 except AttributeError: 

1016 self.importer(used) 

1017 found = getattr(found, frag) 

1018 return found 

1019 except ImportError: 

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

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

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

1023 raise v 

1024 

1025 def ext_convert(self, value): 

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

1027 return self.resolve(value) 

1028 

1029 def cfg_convert(self, value): 

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

1031 rest = value 

1032 m = self.WORD_PATTERN.match(rest) 

1033 if m is None: 

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

1035 else: 

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

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

1038 while rest: 

1039 m = self.DOT_PATTERN.match(rest) 

1040 if m: 

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

1042 else: 

1043 m = self.INDEX_PATTERN.match(rest) 

1044 if m: 

1045 idx = m.groups()[0] 

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

1047 d = d[idx] 

1048 else: 

1049 try: 

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

1051 d = d[n] 

1052 except TypeError: 

1053 d = d[idx] 

1054 if m: 

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

1056 else: 

1057 raise ValueError('Unable to convert ' 

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

1059 # rest should be empty 

1060 return d 

1061 

1062 def convert(self, value): 

1063 """ 

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

1065 replaced by their converting alternatives. Strings are checked to 

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

1067 """ 

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

1069 value = ConvertingDict(value) 

1070 value.configurator = self 

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

1072 value = ConvertingList(value) 

1073 value.configurator = self 

1074 elif not isinstance(value, ConvertingTuple) and isinstance(value, tuple): 

1075 value = ConvertingTuple(value) 

1076 value.configurator = self 

1077 elif isinstance(value, string_types): 

1078 m = self.CONVERT_PATTERN.match(value) 

1079 if m: 

1080 d = m.groupdict() 

1081 prefix = d['prefix'] 

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

1083 if converter: 

1084 suffix = d['suffix'] 

1085 converter = getattr(self, converter) 

1086 value = converter(suffix) 

1087 return value 

1088 

1089 def configure_custom(self, config): 

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

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

1092 if not callable(c): 

1093 c = self.resolve(c) 

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

1095 # Check for valid identifiers 

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

1097 result = c(**kwargs) 

1098 if props: 

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

1100 setattr(result, name, value) 

1101 return result 

1102 

1103 def as_tuple(self, value): 

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

1105 if isinstance(value, list): 

1106 value = tuple(value) 

1107 return value