Passed
Push — main ( ddff4b...7b3fbc )
by Douglas
04:33
created

mandos.analysis.filtration.Filtration.from_file()   A

Complexity

Conditions 1

Size

Total Lines 4
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nop 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
"""
2
Tool to filter annotations.
3
"""
4
from __future__ import annotations
5
6
import re
7
from dataclasses import dataclass
8
import enum
9
from datetime import datetime
10
from pathlib import Path
11
from typing import Union, Sequence, Mapping, Set, Optional
12
13
from pocketutils.core.dot_dict import NestedDotDict
0 ignored issues
show
introduced by
Unable to import 'pocketutils.core.dot_dict'
Loading history...
14
15
from mandos.model.hits import AbstractHit, HitFrame
16
17
_Type = Union[str, int, float, datetime]
18
19
20
class Operator(enum.Enum):
0 ignored issues
show
Documentation introduced by
Empty class docstring
Loading history...
21
    """"""
22
23
    eq = enum.auto()
24
    ne = enum.auto()
25
    lt = enum.auto()
26
    gt = enum.auto()
27
    le = enum.auto()
28
    ge = enum.auto()
29
    like = enum.auto()
30
    is_in = enum.auto()
31
    not_in = enum.auto()
32
33
    @classmethod
34
    def parse(cls, s: str):
0 ignored issues
show
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
introduced by
Missing function or method docstring
Loading history...
35
        if isinstance(s, Operator):
36
            return s
37
        if s in cls:
38
            return cls[s]
39
        return cls.rev_symbols().get(s)
40
41
    @property
42
    def symbol(self) -> str:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
43
        return dict(
44
            eq="=", ne="!=", lt="<", gt=">", le="<=", ge=">=", like="$", is_in="<<", not_in="!<<"
45
        )[self.name]
46
47
    @classmethod
48
    def rev_symbols(cls) -> Mapping[str, Operator]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
49
        return {e.symbol: e for e in cls}
50
51
52
@dataclass(frozen=True, repr=True)
0 ignored issues
show
Documentation introduced by
Empty class docstring
Loading history...
53
class Expression:
54
    """"""
55
56
    op: Operator
0 ignored issues
show
Coding Style Naming introduced by
Attribute name "op" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
57
    val: _Type
58
    extra: Optional[Set[_Type]]
59
60
    def matches(self, value: _Type) -> bool:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
61
        if type(value) != type(self.val):
0 ignored issues
show
introduced by
Using type() instead of isinstance() for a typecheck.
Loading history...
62
            raise TypeError(f"{type(value)} != {type(self.val)}")
63
        if self.op is Operator.like:
64
            return re.compile(self.val).fullmatch(str(value)) is not None
65
        if self.op is Operator.is_in:
66
            return value in self.extra
67
        if self.op is Operator.not_in:
68
            return value not in self.extra
69
        call = f"__{self.op}__"
70
        return getattr(value, call) != getattr(self.val, call)
71
72
    @classmethod
73
    def parse(cls, s: str, context: NestedDotDict) -> Expression:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
Coding Style Naming introduced by
Argument name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
74
        op = Operator.parse(s)
0 ignored issues
show
Coding Style Naming introduced by
Variable name "op" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
75
        if op is None:
76
            op = Operator.eq
0 ignored issues
show
Coding Style Naming introduced by
Variable name "op" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
77
        val = s[len(op.symbol) :]
78
        return Expression(op, val, set(context.get(val, [])))
79
80
81
class Filtration:
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
82
    def __init__(self, key_to_statements: Mapping[str, Mapping[str, Sequence[Expression]]]):
83
        self._x = key_to_statements
84
85
    @classmethod
86
    def from_file(cls, path: Path) -> Filtration:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
87
        dot = NestedDotDict.read_toml(path)
88
        return cls.from_toml(dot)
89
90
    @classmethod
91
    def from_toml(cls, dot: NestedDotDict) -> Filtration:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
92
        data = dot.get("mandos.filter", [])
93
        return Filtration({d["key"]: {k: v for k, v in d.items() if k != "key"} for d in data})
94
95
    def apply(self, df: HitFrame) -> HitFrame:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "df" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
introduced by
Missing function or method docstring
Loading history...
96
        hits = [h for h in df.to_hits() if self.keep(h)]
97
        return HitFrame.from_hits(hits)
98
99
    def keep(self, hit: AbstractHit) -> bool:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
100
        if hit.search_key not in self._x:
101
            return True
102
        for field, values in self._x[hit.search_key].items():
0 ignored issues
show
Unused Code introduced by
The variable values seems to be unused.
Loading history...
103
            if not hasattr(hit, field):
104
                raise ValueError(f"No field {field} in {hit.__class__.__name__}")
105
            if not self._matches(getattr(hit, field), field):
106
                return False
107
        return True
108
109
    def _matches(self, actual: _Type, allowed: Sequence[Expression]) -> bool:
0 ignored issues
show
Unused Code introduced by
Either all return statements in a function should return an expression, or none of them should.
Loading history...
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
110
        for e in allowed:
0 ignored issues
show
Coding Style Naming introduced by
Variable name "e" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
111
            if e.matches(actual):
112
                return True
113
114
115
__all__ = ["Filtration"]
116