Passed
Push — dependabot/pip/pyarrow-4.0.1 ( ca09ce...b2836e )
by
unknown
02:18 queued 20s
created

Mapx.str_to()   B

Complexity

Conditions 8

Size

Total Lines 15
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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