Completed
Pull Request — develop (#15)
by Kale
01:10
created

_Null   A

Complexity

Total Complexity 3

Size/Duplication

Total Lines 18
Duplicated Lines 0 %

Importance

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

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __nonzero__() 0 2 1
A __len__() 0 2 1
A __bool__() 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
class _Null(object):
11
    """
12
    Examples:
13
        >>> len(_Null())
14
        0
15
        >>> bool(_Null())
16
        False
17
        >>> _Null().__nonzero__()
18
        False
19
    """
20
    def __nonzero__(self):
21
        return self.__bool__()
22
23
    def __bool__(self):
24
        return False
25
26
    def __len__(self):
27
        return 0
28
29
30
# Use this NULL object when needing to distinguish a value from None
31
# For example, when parsing json, you may need to determine if a json key was given and set
32
#   to null, or the key didn't exist at all.  There could be a bit of potential confusion here,
33
#   because in python null == None, while here I'm defining NULL to mean 'not defined'.
34
NULL = _Null()
35
36
37
def make_immutable(value):
38
    # this function is recursive, and if nested data structures fold back on themselves,
39
    #   there will likely be recursion errors
40
    if isinstance(value, Mapping):
41
        if isinstance(value, frozendict):
42
            return value
43
        return frozendict((k, make_immutable(v)) for k, v in iteritems(value))
44
    elif isinstance(value, Set):
45
        if isinstance(value, frozenset):
46
            return value
47
        return frozenset(make_immutable(v) for v in value)
48
    elif isiterable(value):
49
        if isinstance(value, tuple):
50
            return value
51
        return tuple(make_immutable(v) for v in value)
52
    else:
53
        return value
54
55
56
# http://stackoverflow.com/a/14620633/2127762
57
class AttrDict(dict):
58
    """Sub-classes dict, and further allows attribute-like access to dictionary items.
59
60
    Examples:
61
        >>> d = AttrDict({'a': 1})
62
        >>> d.a, d['a'], d.get('a')
63
        (1, 1, 1)
64
        >>> d.b = 2
65
        >>> d.b, d['b']
66
        (2, 2)
67
    """
68
    def __init__(self, *args, **kwargs):
69
        super(AttrDict, self).__init__(*args, **kwargs)
70
        self.__dict__ = self
71
72
73
class frozendict(odict):
74
75
    def __key(self):
76
        return tuple((k, self[k]) for k in sorted(self))
77
78
    def __hash__(self):
79
        return hash(self.__key())
80
81
    def __eq__(self, other):
82
        return self.__key() == other.__key()
83
84
85
def first(seq, key=lambda x: bool(x), default=None, apply=lambda x: x):
86
    """Give the first value that satisfies the key test.
87
88
    Args:
89
        seq (iterable):
90
        key (callable): test for each element of iterable
91
        default: returned when all elements fail test
92
        apply (callable): applied to element before return, but not to default value
93
94
    Returns: first element in seq that passes key, mutated with optional apply
95
96
    Examples:
97
        >>> first([0, False, None, [], (), 42])
98
        42
99
        >>> first([0, False, None, [], ()]) is None
100
        True
101
        >>> first([0, False, None, [], ()], default='ohai')
102
        'ohai'
103
        >>> import re
104
        >>> m = first(re.match(regex, 'abc') for regex in ['b.*', 'a(.*)'])
105
        >>> m.group(1)
106
        'bc'
107
108
        The optional `key` argument specifies a one-argument predicate function
109
        like that used for `filter()`.  The `key` argument, if supplied, must be
110
        in keyword form.  For example:
111
        >>> first([1, 1, 3, 4, 5], key=lambda x: x % 2 == 0)
112
        4
113
114
    """
115
    return next((apply(x) for x in seq if key(x)), default() if callable(default) else default)
116
117
118
def firstitem(map, key=lambda k, v: bool(k), default=None, apply=lambda k, v: (k, v)):
119
    return next((apply(k, v) for k, v in map if key(k, v)), default)
120
121
122
def last(seq, key=lambda x: bool(x), default=None, apply=lambda x: x):
123
    return next((apply(x) for x in reversed(seq) if key(x)), default)
124
125
126
def call_each(seq):
127
    """Calls each element of sequence to invoke the side effect.
128
129
    Args:
130
        seq:
131
132
    Returns: None
133
134
    """
135
    try:
136
        reduce(lambda _, y: y(), seq)
137
    except TypeError as e:
138
        if text_type(e) != "reduce() of empty sequence with no initial value":
139
            raise
140