AttributeFilter.excluding()   A
last analyzed

Complexity

Conditions 3

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
c 1
b 0
f 0
dl 0
loc 4
rs 10
1
"""
2
Utilities for filtering sequences.
3
"""
4
5
from itertools import product, chain
6
from operator import attrgetter, itemgetter
7
from typing import Dict, Generator, List, Tuple
0 ignored issues
show
Configuration introduced by
The import typing could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
8
9
10
class AttributeFilter:
11
    """
12
    Filter a sequence of objects/namedtuples by on attribute value predicates.
13
14
    Parameters
15
    ----------
16
    keys: group of attribute keys to use as filter set.
17
    predicates: values of attribute set to use in filtering.
18
    """
19
20
    slots = ('keys', 'predicates', 'indexer')
21
22
    def __init__(self, keys: Tuple[str], predicates: List[Tuple]):
23
        self.keys = keys
24
        self.predicates = set(predicates)
25
        self.indexer = attrgetter(*keys)
0 ignored issues
show
Coding Style introduced by
Usage of * or ** arguments should usually be done with care.

Generally, there is nothing wrong with usage of * or ** arguments. For readability of the code base, we suggest to not over-use these language constructs though.

For more information, we can recommend this blog post from Ned Batchelder including its comments which also touches this aspect.

Loading history...
26
27
    def including(self, sequence) -> Generator:
28
        """Include the sequence elements matching the filter set."""
29
        return (element for element in sequence
30
                if self.indexer(element) in self.predicates)
31
32
    def excluding(self, sequence) -> Generator:
33
        """Exclude the sequence elements matching the filter set."""
34
        return (element for element in sequence
35
                if self.indexer(element) not in self.predicates)
36
37
38
def create_key_filter(properties: Dict[str, list]) -> List[Tuple]:
39
    """Generate combinations of key, value pairs for each key in properties.
40
41
    Examples
42
    --------
43
    properties = {'ent': ['geo_rev', 'supply_chain'], 'own', 'fi'}
44
    >> create_key_filter(properties)
45
      --> [('ent', 'geo_rev'), ('ent', 'supply_chain'), ('own', 'fi')]
46
    """
47
48
    combinations = (product([k], v) for k, v in properties.items())
49
50
    return chain.from_iterable(combinations)
51
52
53
def create_indexer(indexes: list):
54
    """Create indexer function to pluck values from list."""
55
56
    if len(indexes) == 1:
57
        index = indexes[0]
58
        return lambda x: (x[index],)
59
    else:
60
        return itemgetter(*indexes)
0 ignored issues
show
Coding Style introduced by
Usage of * or ** arguments should usually be done with care.

Generally, there is nothing wrong with usage of * or ** arguments. For readability of the code base, we suggest to not over-use these language constructs though.

For more information, we can recommend this blog post from Ned Batchelder including its comments which also touches this aspect.

Loading history...
61