simplify_intersection()   F
last analyzed

Complexity

Conditions 15

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 15.3888

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 15
c 1
b 0
f 0
dl 0
loc 33
ccs 22
cts 25
cp 0.88
crap 15.3888
rs 2.7451

How to fix   Complexity   

Complexity

Complex classes like simplify_intersection() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1 1
import operator
2 1
import itertools
3
4 1
from ppp_datamodel.nodes import Resource
5 1
from ppp_datamodel.nodes.list_operators import *
6
7 1
__all__ = ['simplify']
8
9 1
def partition(pred, list_):
10
    # Partition the items in lists / non-lists
11 1
    list_ = [x for x in list_ if x]
12 1
    lists = filter(pred, list_)
13 1
    not_lists = itertools.filterfalse(pred, list_)
14 1
    return (lists, not_lists)
15
16 1
def simplify_union(tree):
17
    # Trivial cases
18 1
    if len(tree.list) == 0:
19 1
        return List([])
20 1
    elif len(tree.list) == 1:
21 1
        return tree.list[0]
22
23 1
    (lists, non_lists) = partition(lambda x:isinstance(x, (List, Resource)),
24
                                   tree.list)
25
    # Make union of lists
26 1
    lists = [x.list if isinstance(x, List) else [x] for x in lists]
27 1
    lists = list(set(itertools.chain(*lists)))
28
29 1
    non_lists = list(non_lists)
30 1
    if lists:
31 1
        all_ = non_lists
32 1
        all_.append(List(lists))
33 1
        return Union(all_)
34
    elif len(non_lists) == 1:
35
        return non_lists
36
    else:
37
        return Union(non_lists)
38
39 1
def simplify_intersection(tree):
40
    # Trivial cases
41 1
    if len(tree.list) == 0:
42 1
        return tree
43 1
    elif len(tree.list) == 1:
44 1
        return tree.list[0]
45
46 1
    (a, b) = partition(lambda x:isinstance(x, (List, Resource)), tree.list)
47 1
    (lists, non_lists) = partition(lambda x:isinstance(x, (List, Resource)),
48
                                   tree.list)
49 1
    lists = list(lists)
50
    # Make intersection of lists
51 1
    try:
52
        # Efficient algorithm if everything is hashable
53 1
        lists = [set(x.list) if isinstance(x, List) else {x} for x in lists] or [set()]
54 1
        lists = list(lists[0].intersection(*lists[1:]))
55 1
    except TypeError:
56
        # If there is a non-hashable value, fallback to a less efficient algorithm
57 1
        lists = [x.list if isinstance(x, List) else [x] for x in lists] or [[]]
58 1
        intersected_lists = list(lists[0])
59 1
        for l in lists[1:]:
60 1
            intersected_lists = [x for x in intersected_lists if x in l]
61 1
        lists = intersected_lists
62
63 1
    non_lists = list(non_lists)
64 1
    if lists:
65 1
        all_ = non_lists
66 1
        all_.append(simplify_list(List(lists)))
67 1
        return Intersection(all_)
68
    elif len(non_lists) == 1:
69
        return non_lists
70
    else:
71
        return Intersection(non_lists)
72
73 1
def simplify_list(tree):
74 1
    if len(tree.list) == 1:
75 1
        return tree.list[0]
76
    else:
77 1
        return tree
78
79 1
predicates = {
80
        Union: simplify_union,
81
        Intersection: simplify_intersection,
82
        List: simplify_list,
83
        }
84
85 1
def predicate(tree):
86 1
    for (cls, f) in predicates.items():
87 1
        if isinstance(tree, cls):
88 1
            return f(tree)
89 1
    return tree
90
91 1
def simplify(tree):
92 1
    old_tree = None
93 1
    while old_tree != tree:
94 1
        old_tree = tree
95 1
        tree = tree.traverse(predicate)
96
    return tree
97