Passed
Push — dependabot/pip/flake8-bugbear-... ( 16d864...b4f9fc )
by
unknown
01:45
created

Mapx.get_int()   A

Complexity

Conditions 4

Size

Total Lines 10
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 9
nop 2
dl 0
loc 10
rs 9.95
c 0
b 0
f 0
1
"""
2
Support classes to help with querying and processing web data.
3
"""
4
from __future__ import annotations
5
6
import re
7
from datetime import date, datetime
8
from typing import (
9
    Any,
10
    Callable,
11
    Set,
12
    Union,
13
    Optional,
14
    Type,
15
    TypeVar,
16
    Iterable,
17
    FrozenSet,
18
)
19
20
from pocketutils.tools.base_tools import BaseTools
0 ignored issues
show
introduced by
Unable to import 'pocketutils.tools.base_tools'
Loading history...
21
from pocketutils.tools.string_tools import StringTools
0 ignored issues
show
introduced by
Unable to import 'pocketutils.tools.string_tools'
Loading history...
22
23
from mandos import logger
0 ignored issues
show
Unused Code introduced by
Unused logger imported from mandos
Loading history...
24
from mandos.model.pubchem_support._nav_model import FilterFn
25
26
T = TypeVar("T")
0 ignored issues
show
Coding Style Naming introduced by
Class name "T" doesn't conform to PascalCase naming style ('[^\\W\\da-z][^\\W_]+$' 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...
27
28
empty_frozenset = frozenset([])
29
30
31
class Filter:
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
32
    @classmethod
33
    def has_key(cls, key: str) -> FilterFn:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
34
        return FilterFn(lambda content: key in content)
35
36
    @classmethod
37
    def key_does_not_equal(cls, key: str, disallowed_value: Any) -> FilterFn:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
38
        return FilterFn(lambda content: content.get(key) != disallowed_value)
39
40
    @classmethod
41
    def key_equals(cls, key: str, allowed_value: Any) -> FilterFn:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
42
        return FilterFn(lambda content: content.get(key) == allowed_value)
43
44
    @classmethod
45
    def key_is_not_in(cls, key: str, disallowed_values: Set[Any]) -> FilterFn:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
46
        return FilterFn(lambda content: content.get(key) not in disallowed_values)
47
48
    @classmethod
49
    def key_is_in(cls, key: str, allowed_values: Set[Any]) -> FilterFn:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
50
        return FilterFn(lambda content: content.get(key) in allowed_values)
51
52
53
class Flatmap:
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
54
    @classmethod
55
    def construct(cls, tp: Type[T]) -> Callable[[Iterable[Any]], T]:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "tp" 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...
56
        """
57
        Function that constructs an instance whose attributes are from the passed list.
58
        """
59
60
        def construct(things: Iterable[str]) -> Optional[str]:
61
            return tp(*things)
62
63
        return construct
64
65
    @classmethod
66
    def join_nonnulls(cls, sep: str = "; ") -> Callable[[Iterable[str]], Optional[str]]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
67
        def opt_join(things: Iterable[str]) -> Optional[str]:
68
            x = [s.strip() for s in things]
0 ignored issues
show
Coding Style Naming introduced by
Variable name "x" 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...
69
            return None if len(x) == 0 else sep.join(x)
70
71
        return opt_join
72
73
    @classmethod
74
    def request_only(cls) -> Callable[[Iterable[str]], Optional[str]]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
75
        def only_nonreq(things: Iterable[str]) -> Optional[str]:
76
            # TODO: Did I mean to excludeNone here?
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
77
            things = [s.strip() for s in things if s is not None]
78
            if len(things) > 1:
0 ignored issues
show
Unused Code introduced by
Unnecessary "elif" after "raise"
Loading history...
79
                raise ValueError(f"{len(things)} items in {things}")
80
            elif len(things) == 0:
81
                return None
82
            else:
83
                return things[0]
84
85
        return only_nonreq
86
87
    @classmethod
88
    def require_only(cls) -> Callable[[Iterable[str]], Optional[str]]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
89
        return BaseTools.only
90
91
92
class Mapx:
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
93
    @classmethod
94
    def roman_to_arabic(
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
95
        cls, min_val: Optional[int] = None, max_val: Optional[int] = None
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
96
    ) -> Callable[[str], int]:
97
        def roman_to_arabic(s: str) -> int:
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...
98
            return StringTools.roman_to_arabic(s.strip(), min_val=min_val, max_val=max_val)
99
100
        return roman_to_arabic
101
102
    @classmethod
103
    def split_and_flatten_nonnulls(
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
104
        cls, sep: str, skip_nulls: bool = False
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
105
    ) -> Callable[[Iterable[Union[str, int, float, None]]], Set[str]]:
106
        def split_flat(things: Iterable[str]) -> Set[str]:
107
            results = set()
108
            for thing in things:
109
                if thing is not None and thing != float("NaN") or not skip_nulls:
110
                    # let it fail if skip_nulls is False
111
                    for bit in str(thing).split(sep):
112
                        results.add(bit.strip())
113
            return results
114
115
        return split_flat
116
117
    @classmethod
118
    def extract_group_1(
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
119
        cls, pattern: Union[str, re.Pattern]
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
120
    ) -> Callable[[Optional[Any]], Optional[str]]:
121
        pattern = pattern if isinstance(pattern, re.Pattern) else re.compile(pattern)
122
123
        def _match(thing: Optional[str]) -> Optional[str]:
124
            if thing is None:
125
                return None
126
            match = pattern.fullmatch(str(thing))
127
            if match is None:
128
                return None
129
            return match.group(1)
130
131
        return _match
132
133
    @classmethod
134
    def lowercase_unless_acronym(cls) -> Callable[[str], str]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
135
        def lowercase_unless_acronym(s: str) -> 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...
136
            s = s.strip()
137
            return s if s.isupper() else s.lower()
138
139
        return lowercase_unless_acronym
140
141
    @classmethod
142
    def n_bar_items(cls, sep: str = "||") -> Callable[[Optional[str]], int]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
143
        def n_bar_items(value: str) -> int:
144
            return len(value.split(sep))
145
146
        return n_bar_items
147
148
    @classmethod
149
    def not_null(cls) -> Callable[[Any], bool]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
150
        return lambda value: value is not None
151
152
    @classmethod
153
    def identity(cls) -> Callable[[T], T]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
154
        return lambda value: value
155
156
    @classmethod
157
    def int_date(cls, nullable: bool = False) -> Callable[[Optional[str]], Optional[date]]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
158
        def int_date(s: Optional[str]) -> Optional[date]:
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...
159
            if s is None and nullable:
160
                return None
161
            return datetime.strptime(str(s).strip(), "%Y%m%d").date()
162
163
        return int_date
164
165
    @classmethod
166
    def get_int(cls, nullable: bool = False) -> Callable[[str], Optional[int]]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
167
        def get_int(value):
168
            if nullable and value is None:
0 ignored issues
show
unused-code introduced by
Unnecessary "elif" after "return"
Loading history...
169
                return None
170
            elif not isinstance(value, (int, float, str)):
171
                raise TypeError(f"{value} is a {type(value)}, not int-like")
172
            return int(value)
173
174
        return get_int
175
176
    @classmethod
177
    def req_is(cls, type_, nullable: bool = False, then_convert=None) -> Callable[[str], str]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
178
        def req_is(value):
179
            if nullable and value is None:
180
                pass
181
            elif not isinstance(value, type_):
182
                raise TypeError(f"{value} is a {type(value)}, not {type_}")
183
            return value if then_convert is None else then_convert(value)
184
185
        req_is.__name__ = f"req_is_{type_}" + ("_or_null" if nullable else "")
186
        return req_is
187
188
    @classmethod
189
    def str_to(
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
190
        cls, type_: Callable[[str], T], nullable: bool = False, flex_type: bool = False
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
191
    ) -> Callable[[str], str]:
192
        def str_to(value: Optional[str]) -> Optional[T]:
193
            if type_ is not None and not flex_type and not isinstance(value, str):
194
                raise TypeError(f"{value} is a {type(value)}, not str")
195
            if not nullable and value is None:
0 ignored issues
show
Unused Code introduced by
Unnecessary "elif" after "raise"
Loading history...
196
                raise ValueError(f"Value for type {type_} is None")
197
            elif value is None:
198
                return None
199
            return type_(str(value).strip())
200
201
        str_to.__name__ = f"req_is_{type_}" + ("_or_null" if nullable else "")
202
        return str_to
203
204
    @classmethod
205
    def split_to(
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
206
        cls, type_, sep: str = "||", nullable: bool = False
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
207
    ) -> Callable[[Optional[str]], FrozenSet[int]]:
208
        def split_to(value: str) -> FrozenSet[int]:
209
            return frozenset([type_(x) for x in cls.split(sep, nullable=nullable)(value)])
210
211
        return split_to
212
213
    @classmethod
214
    def split(cls, sep: str, nullable: bool = False) -> Callable[[Optional[str]], FrozenSet[str]]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
215
        def split(value: str) -> FrozenSet[str]:
216
            if value is None and not nullable:
217
                raise ValueError(f"Value is None")
0 ignored issues
show
introduced by
Using an f-string that does not have any interpolated variables
Loading history...
218
            if value is None:
219
                return empty_frozenset
220
            return frozenset([s.strip() for s in str(value).split(sep)])
221
222
        return split
223
224
225
__all__ = ["Filter", "Mapx", "Flatmap"]
226