frozendict   A
last analyzed

Complexity

Total Complexity 4

Size/Duplication

Total Lines 10
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 10
c 0
b 0
f 0
wmc 4

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __hash__() 0 2 1
A __key() 0 2 2
A __eq__() 0 2 1
1
# -*- coding: utf-8 -*-
2
"""Common collection classes."""
3
from __future__ import print_function, division, absolute_import
4
from functools import reduce
5
from collections import Mapping, Set
6
7
from .compat import isiterable, iteritems, odict, text_type
8
9
10
def make_immutable(value):
11
    # this function is recursive, and if nested data structures fold back on themselves,
12
    #   there will likely be recursion errors
13
    if isinstance(value, Mapping):
14
        if isinstance(value, frozendict):
15
            return value
16
        return frozendict((k, make_immutable(v)) for k, v in iteritems(value))
17
    elif isinstance(value, Set):
18
        if isinstance(value, frozenset):
19
            return value
20
        return frozenset(make_immutable(v) for v in value)
21
    elif isiterable(value):
22
        if isinstance(value, tuple):
23
            return value
24
        return tuple(make_immutable(v) for v in value)
25
    else:
26
        return value
27
28
29
# http://stackoverflow.com/a/14620633/2127762
30
class AttrDict(dict):
31
    """Sub-classes dict, and further allows attribute-like access to dictionary items.
32
33
    Examples:
34
        >>> d = AttrDict({'a': 1})
35
        >>> d.a, d['a'], d.get('a')
36
        (1, 1, 1)
37
        >>> d.b = 2
38
        >>> d.b, d['b']
39
        (2, 2)
40
    """
41
    def __init__(self, *args, **kwargs):
42
        super(AttrDict, self).__init__(*args, **kwargs)
43
        self.__dict__ = self
44
45
46
class frozendict(odict):
47
48
    def __key(self):
49
        return tuple((k, self[k]) for k in sorted(self))
50
51
    def __hash__(self):
52
        return hash(self.__key())
53
54
    def __eq__(self, other):
55
        return self.__key() == other.__key()
56
57
58
def first(seq, key=lambda x: bool(x), default=None, apply=lambda x: x):
59
    """Give the first value that satisfies the key test.
60
61
    Args:
62
        seq (iterable):
63
        key (callable): test for each element of iterable
64
        default: returned when all elements fail test
65
        apply (callable): applied to element before return, but not to default value
66
67
    Returns: first element in seq that passes key, mutated with optional apply
68
69
    Examples:
70
        >>> first([0, False, None, [], (), 42])
71
        42
72
        >>> first([0, False, None, [], ()]) is None
73
        True
74
        >>> first([0, False, None, [], ()], default='ohai')
75
        'ohai'
76
        >>> import re
77
        >>> m = first(re.match(regex, 'abc') for regex in ['b.*', 'a(.*)'])
78
        >>> m.group(1)
79
        'bc'
80
81
        The optional `key` argument specifies a one-argument predicate function
82
        like that used for `filter()`.  The `key` argument, if supplied, must be
83
        in keyword form.  For example:
84
        >>> first([1, 1, 3, 4, 5], key=lambda x: x % 2 == 0)
85
        4
86
87
    """
88
    return next((apply(x) for x in seq if key(x)), default() if callable(default) else default)
89
90
91
def firstitem(map, key=lambda k, v: bool(k), default=None, apply=lambda k, v: (k, v)):
92
    return next((apply(k, v) for k, v in map if key(k, v)), default)
93
94
95
def last(seq, key=lambda x: bool(x), default=None, apply=lambda x: x):
96
    return next((apply(x) for x in reversed(seq) if key(x)), default)
97
98
99
def call_each(seq):
100
    """Calls each element of sequence to invoke the side effect.
101
102
    Args:
103
        seq:
104
105
    Returns: None
106
107
    """
108
    try:
109
        reduce(lambda _, y: y(), seq)
110
    except TypeError as e:
111
        if text_type(e) != "reduce() of empty sequence with no initial value":
112
            raise
113