|
5 | 5 | - math.ceil (for Python 2.7) |
6 | 6 | - collections.OrderedDict (for Python 2.6) |
7 | 7 | - collections.Counter (for Python 2.6) |
| 8 | +- collections.ChainMap (for all versions prior to Python 3.3) |
8 | 9 | - itertools.count (for Python 2.6, with step parameter) |
| 10 | +- subprocess.check_output (for Python 2.6) |
9 | 11 | """ |
10 | 12 |
|
11 | | -from math import ceil |
| 13 | +import sys |
12 | 14 | import subprocess |
| 15 | +from math import ceil |
| 16 | +from collections import MutableMapping |
13 | 17 |
|
14 | | -from future.utils import iteritems, itervalues, PY26 |
| 18 | +from future.utils import iteritems, itervalues, PY26, PY3 |
15 | 19 |
|
16 | 20 |
|
17 | 21 | def _ceil(x): |
@@ -297,6 +301,15 @@ def viewitems(self): |
297 | 301 | except ImportError: |
298 | 302 | pass |
299 | 303 |
|
| 304 | +######################################################################## |
| 305 | +### Counter |
| 306 | +######################################################################## |
| 307 | + |
| 308 | +def __count_elements(mapping, iterable): |
| 309 | + 'Tally elements from the iterable.' |
| 310 | + mapping_get = mapping.get |
| 311 | + for elem in iterable: |
| 312 | + mapping[elem] = mapping_get(elem, 0) + 1 |
300 | 313 |
|
301 | 314 | class _Counter(dict): |
302 | 315 |
|
@@ -513,14 +526,176 @@ def _count(start=0, step=1): |
513 | 526 | start += step |
514 | 527 |
|
515 | 528 |
|
516 | | -if not PY26: |
517 | | - from math import ceil |
518 | | - from collections import OrderedDict, Counter |
519 | | - from subprocess import check_output |
520 | | - from itertools import count |
521 | | -else: |
522 | | - ceil = _ceil |
| 529 | +######################################################################## |
| 530 | +### reprlib.recursive_repr decorator from Py3.4 |
| 531 | +######################################################################## |
| 532 | + |
| 533 | +from itertools import islice |
| 534 | +try: |
| 535 | + from _thread import get_ident |
| 536 | +except ImportError: |
| 537 | + from _dummy_thread import get_ident |
| 538 | + |
| 539 | +def _recursive_repr(fillvalue='...'): |
| 540 | + 'Decorator to make a repr function return fillvalue for a recursive call' |
| 541 | + |
| 542 | + def decorating_function(user_function): |
| 543 | + repr_running = set() |
| 544 | + |
| 545 | + def wrapper(self): |
| 546 | + key = id(self), get_ident() |
| 547 | + if key in repr_running: |
| 548 | + return fillvalue |
| 549 | + repr_running.add(key) |
| 550 | + try: |
| 551 | + result = user_function(self) |
| 552 | + finally: |
| 553 | + repr_running.discard(key) |
| 554 | + return result |
| 555 | + |
| 556 | + # Can't use functools.wraps() here because of bootstrap issues |
| 557 | + wrapper.__module__ = getattr(user_function, '__module__') |
| 558 | + wrapper.__doc__ = getattr(user_function, '__doc__') |
| 559 | + wrapper.__name__ = getattr(user_function, '__name__') |
| 560 | + wrapper.__annotations__ = getattr(user_function, '__annotations__', {}) |
| 561 | + return wrapper |
| 562 | + |
| 563 | + return decorating_function |
| 564 | + |
| 565 | + |
| 566 | +######################################################################## |
| 567 | +### ChainMap (helper for configparser and string.Template) |
| 568 | +### From the Py3.4 source code. See also: |
| 569 | +### https://github.com/kkxue/Py2ChainMap/blob/master/py2chainmap.py |
| 570 | +######################################################################## |
| 571 | + |
| 572 | +class _ChainMap(MutableMapping): |
| 573 | + ''' A ChainMap groups multiple dicts (or other mappings) together |
| 574 | + to create a single, updateable view. |
| 575 | +
|
| 576 | + The underlying mappings are stored in a list. That list is public and can |
| 577 | + accessed or updated using the *maps* attribute. There is no other state. |
| 578 | +
|
| 579 | + Lookups search the underlying mappings successively until a key is found. |
| 580 | + In contrast, writes, updates, and deletions only operate on the first |
| 581 | + mapping. |
| 582 | +
|
| 583 | + ''' |
| 584 | + |
| 585 | + def __init__(self, *maps): |
| 586 | + '''Initialize a ChainMap by setting *maps* to the given mappings. |
| 587 | + If no mappings are provided, a single empty dictionary is used. |
| 588 | +
|
| 589 | + ''' |
| 590 | + self.maps = list(maps) or [{}] # always at least one map |
| 591 | + |
| 592 | + def __missing__(self, key): |
| 593 | + raise KeyError(key) |
| 594 | + |
| 595 | + def __getitem__(self, key): |
| 596 | + for mapping in self.maps: |
| 597 | + try: |
| 598 | + return mapping[key] # can't use 'key in mapping' with defaultdict |
| 599 | + except KeyError: |
| 600 | + pass |
| 601 | + return self.__missing__(key) # support subclasses that define __missing__ |
| 602 | + |
| 603 | + def get(self, key, default=None): |
| 604 | + return self[key] if key in self else default |
| 605 | + |
| 606 | + def __len__(self): |
| 607 | + return len(set().union(*self.maps)) # reuses stored hash values if possible |
| 608 | + |
| 609 | + def __iter__(self): |
| 610 | + return iter(set().union(*self.maps)) |
| 611 | + |
| 612 | + def __contains__(self, key): |
| 613 | + return any(key in m for m in self.maps) |
| 614 | + |
| 615 | + def __bool__(self): |
| 616 | + return any(self.maps) |
| 617 | + |
| 618 | + # Py2 compatibility: |
| 619 | + __nonzero__ = __bool__ |
| 620 | + |
| 621 | + @_recursive_repr() |
| 622 | + def __repr__(self): |
| 623 | + return '{0.__class__.__name__}({1})'.format( |
| 624 | + self, ', '.join(map(repr, self.maps))) |
| 625 | + |
| 626 | + @classmethod |
| 627 | + def fromkeys(cls, iterable, *args): |
| 628 | + 'Create a ChainMap with a single dict created from the iterable.' |
| 629 | + return cls(dict.fromkeys(iterable, *args)) |
| 630 | + |
| 631 | + def copy(self): |
| 632 | + 'New ChainMap or subclass with a new copy of maps[0] and refs to maps[1:]' |
| 633 | + return self.__class__(self.maps[0].copy(), *self.maps[1:]) |
| 634 | + |
| 635 | + __copy__ = copy |
| 636 | + |
| 637 | + def new_child(self, m=None): # like Django's Context.push() |
| 638 | + ''' |
| 639 | + New ChainMap with a new map followed by all previous maps. If no |
| 640 | + map is provided, an empty dict is used. |
| 641 | + ''' |
| 642 | + if m is None: |
| 643 | + m = {} |
| 644 | + return self.__class__(m, *self.maps) |
| 645 | + |
| 646 | + @property |
| 647 | + def parents(self): # like Django's Context.pop() |
| 648 | + 'New ChainMap from maps[1:].' |
| 649 | + return self.__class__(*self.maps[1:]) |
| 650 | + |
| 651 | + def __setitem__(self, key, value): |
| 652 | + self.maps[0][key] = value |
| 653 | + |
| 654 | + def __delitem__(self, key): |
| 655 | + try: |
| 656 | + del self.maps[0][key] |
| 657 | + except KeyError: |
| 658 | + raise KeyError('Key not found in the first mapping: {!r}'.format(key)) |
| 659 | + |
| 660 | + def popitem(self): |
| 661 | + 'Remove and return an item pair from maps[0]. Raise KeyError is maps[0] is empty.' |
| 662 | + try: |
| 663 | + return self.maps[0].popitem() |
| 664 | + except KeyError: |
| 665 | + raise KeyError('No keys found in the first mapping.') |
| 666 | + |
| 667 | + def pop(self, key, *args): |
| 668 | + 'Remove *key* from maps[0] and return its value. Raise KeyError if *key* not in maps[0].' |
| 669 | + try: |
| 670 | + return self.maps[0].pop(key, *args) |
| 671 | + except KeyError: |
| 672 | + raise KeyError('Key not found in the first mapping: {!r}'.format(key)) |
| 673 | + |
| 674 | + def clear(self): |
| 675 | + 'Clear maps[0], leaving maps[1:] intact.' |
| 676 | + self.maps[0].clear() |
| 677 | + |
| 678 | + |
| 679 | +if sys.version_info <= (2, 6): |
523 | 680 | OrderedDict = _OrderedDict |
524 | 681 | Counter = _Counter |
525 | 682 | check_output = _check_output |
526 | 683 | count = _count |
| 684 | +else: |
| 685 | + from collections import OrderedDict, Counter |
| 686 | + from subprocess import check_output |
| 687 | + from itertools import count |
| 688 | + |
| 689 | +if sys.version_info < (3, 0): |
| 690 | + ceil = _ceil |
| 691 | + _count_elements = __count_elements |
| 692 | +else: |
| 693 | + from math import ceil |
| 694 | + from collections import _count_elements |
| 695 | + |
| 696 | +if sys.version_info < (3, 3): |
| 697 | + recursive_repr = _recursive_repr |
| 698 | + ChainMap = _ChainMap |
| 699 | +else: |
| 700 | + from reprlib import recursive_repr |
| 701 | + from collections import ChainMap |
0 commit comments