mandos.model.query_utils   F
last analyzed

Complexity

Total Complexity 82

Size/Duplication

Total Lines 365
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 253
dl 0
loc 365
rs 2
c 0
b 0
f 0
wmc 82

42 Methods

Rating   Name   Duplication   Size   Complexity  
A JsonNavigatorListOfLists.__rshift__() 0 2 1
A JsonNavigatorListOfOptionals.__truediv__() 0 7 2
A Fns.key_is_in() 0 3 2
B JsonNavigator._go_inside() 0 15 6
A JsonNavigatorListOfOptionals._go_inside() 0 6 2
A Fns.extract_group_1() 0 15 4
A JsonNavigator._filter() 0 8 3
A Fns.join_nonnulls() 0 7 2
B Fns.split_and_flatten_nonnulls() 0 14 6
A JsonNavigatorListOfOptionals._filter() 0 4 1
A Fns.not_null() 0 3 2
A Fns.key_is_not_in() 0 3 2
A JsonNavigatorListOfLists.__truediv__() 0 10 2
A JsonNavigatorListOfLists._go_inside() 0 8 1
A JsonNavigatorListOfLists._filter() 0 2 1
A JsonNavigatorListOfOptionals.to_list() 0 3 1
A JsonNavigatorSingleOptional.get() 0 3 1
A Fns.roman_to_arabic() 0 8 1
A Fns.request_only() 0 12 3
A JsonNavigator.__mod__() 0 7 3
A Fns.n_bar_items() 0 6 1
A JsonNavigator.create() 0 7 2
A FilterFn.__call__() 0 2 1
A Fns.construct() 0 11 1
A JsonNavigatorListOfLists.__floordiv__() 0 2 1
A Fns.key_equals() 0 3 2
A Fns.req_is() 0 9 4
A JsonNavigatorSingleOptional.__floordiv__() 0 4 1
A JsonNavigatorListOfOptionals.to_set() 0 3 1
A Fns.lowercase_unless_acronym() 0 7 2
A Fns.key_does_not_equal() 0 3 2
A JsonNavigator.get() 0 3 1
A Fns.require_only() 0 3 1
A Fns.identity() 0 3 2
A JsonNavigator.__truediv__() 0 7 2
A JsonNavigatorListOfOptionals.__floordiv__() 0 4 1
A JsonNavigator.__floordiv__() 0 2 1
A JsonNavigator.__rshift__() 0 2 1
A Fns.has_key() 0 3 2
A JsonNavigatorListOfOptionals.__rshift__() 0 3 1
A Fns.split() 0 8 2
A Fns.split_bars_to_int() 0 6 1

1 Function

Rating   Name   Duplication   Size   Complexity  
A _get_conversion_fn() 0 7 3

How to fix   Complexity   

Complexity

Complex classes like mandos.model.query_utils 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
"""
2
Support classes to help with querying and processing web data.
3
"""
4
from __future__ import annotations
5
6
import logging
7
import re
8
from dataclasses import dataclass
9
from typing import (
10
    Any,
11
    Callable,
12
    Sequence,
13
    Set,
14
    List,
15
    Union,
16
    Optional,
17
    Type,
18
    TypeVar,
19
    Iterable,
20
    FrozenSet,
21
)
22
23
import numpy as np
0 ignored issues
show
introduced by
Unable to import 'numpy'
Loading history...
Unused Code introduced by
Unused numpy imported as np
Loading history...
24
from pocketutils.tools.base_tools import BaseTools
0 ignored issues
show
introduced by
Unable to import 'pocketutils.tools.base_tools'
Loading history...
25
from pocketutils.tools.string_tools import StringTools
0 ignored issues
show
introduced by
Unable to import 'pocketutils.tools.string_tools'
Loading history...
26
from pocketutils.core.dot_dict import NestedDotDict
0 ignored issues
show
introduced by
Unable to import 'pocketutils.core.dot_dict'
Loading history...
27
28
logger = logging.getLogger("mandos")
29
30
empty_frozenset = frozenset([])
31
32
33
@dataclass(frozen=True)
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
34
class FilterFn:
35
    keep_if: Callable[[Any], bool]
36
37
    def __call__(self, *args, **kwargs):
38
        return self.keep_if(*args, **kwargs)
39
40
41
class Fns:
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
42
    @classmethod
43
    def has_key(cls, key: str) -> FilterFn:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
44
        return FilterFn(lambda content: key in content)
45
46
    @classmethod
47
    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...
48
        return FilterFn(lambda content: content.get(key) != disallowed_value)
49
50
    @classmethod
51
    def key_equals(cls, key: str, allowed_value: Any) -> FilterFn:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
52
        return FilterFn(lambda content: content.get(key) == allowed_value)
53
54
    @classmethod
55
    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...
56
        return FilterFn(lambda content: content.get(key) not in disallowed_values)
57
58
    @classmethod
59
    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...
60
        return FilterFn(lambda content: content.get(key) in allowed_values)
61
62
    @classmethod
63
    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...
64
        """
65
        Function that constructs an instance whose attributes are from the passed list.
66
        """
67
68
        def construct(things: Iterable[str]) -> Optional[str]:
69
            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...
70
            return tp(*x)
71
72
        return construct
73
74
    @classmethod
75
    def join_nonnulls(cls) -> Callable[[Iterable[str]], Optional[str]]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
76
        def opt_join(things: Iterable[str]) -> Optional[str]:
77
            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...
78
            return None if len(x) == 0 else ";".join(x)
79
80
        return opt_join
81
82
    @classmethod
83
    def split_and_flatten_nonnulls(
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
84
        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...
85
    ) -> Callable[[Iterable[Union[str, int, float, None]]], Set[str]]:
86
        def split_flat(things: Iterable[str]) -> Set[str]:
87
            results = set()
88
            for thing in things:
89
                if thing is not None and thing != float("NaN") or not skip_nulls:
90
                    # let it fail if skip_nulls is False
91
                    for bit in str(thing).split(sep):
92
                        results.add(bit.strip())
93
            return results
94
95
        return split_flat
96
97
    @classmethod
98
    def request_only(cls) -> Callable[[Iterable[str]], Optional[str]]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
99
        def only_nonreq(things: Iterable[str]) -> Optional[str]:
100
            things = [s.strip() for s in things]
101
            if len(things) > 1:
0 ignored issues
show
Unused Code introduced by
Unnecessary "elif" after "raise"
Loading history...
102
                raise ValueError(f"{len(things)} items in {things}")
103
            elif len(things) == 0:
104
                return None
105
            else:
106
                return things[0]
107
108
        return only_nonreq
109
110
    @classmethod
111
    def roman_to_arabic(
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
112
        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...
113
    ) -> Callable[[str], int]:
114
        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...
115
            return StringTools.roman_to_arabic(s.strip(), min_val=min_val, max_val=max_val)
116
117
        return roman_to_arabic
118
119
    @classmethod
120
    def require_only(cls) -> Callable[[Iterable[str]], Optional[str]]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
121
        return BaseTools.only
122
123
    @classmethod
124
    def extract_group_1(
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
125
        cls, pattern: Union[str, re.Pattern]
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
126
    ) -> Callable[[Optional[Any]], Optional[str]]:
127
        pattern = pattern if isinstance(pattern, re.Pattern) else re.compile(pattern)
128
129
        def _match(thing: Optional[str]) -> Optional[str]:
130
            if thing is None:
131
                return None
132
            match = pattern.fullmatch(str(thing))
133
            if match is None:
134
                return None
135
            return match.group(1)
136
137
        return _match
138
139
    @classmethod
140
    def lowercase_unless_acronym(cls) -> Callable[[str], str]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
141
        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...
142
            s = s.strip()
143
            return s if s.isupper() else s.lower()
144
145
        return lowercase_unless_acronym
146
147
    @classmethod
148
    def split_bars_to_int(cls, sep: str = "||") -> Callable[[Optional[str]], FrozenSet[int]]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
149
        def split_bars_to_int(value: str) -> FrozenSet[int]:
150
            return frozenset([int(x) for x in cls.split(sep)(value)])
151
152
        return split_bars_to_int
153
154
    @classmethod
155
    def split(cls, sep: str = "||") -> Callable[[Optional[str]], FrozenSet[str]]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
156
        def split_bars(value: str) -> FrozenSet[str]:
157
            if value is None:
158
                return empty_frozenset
159
            return frozenset([s.strip() for s in value.split(sep)])
160
161
        return split_bars
162
163
    @classmethod
164
    def n_bar_items(cls, sep: str = "||") -> Callable[[Optional[str]], int]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
165
        def n_bar_items(value: str) -> int:
166
            return len(cls.split(sep)(value))
167
168
        return n_bar_items
169
170
    @classmethod
171
    def not_null(cls) -> Callable[[Any], bool]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
172
        return lambda value: value is not None
173
174
    @classmethod
175
    def identity(cls) -> Callable[[T], T]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
176
        return lambda value: value
177
178
    @classmethod
179
    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...
180
        def req_is(value):
181
            if not isinstance(value, type_):
182
                raise ValueError(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
189
def _get_conversion_fn(fn: Union[None, str, Callable[[Any], Any]]) -> Callable[[Any], Any]:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "fn" 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...
190
    if fn is None:
191
        return Fns.identity
192
    if isinstance(fn, str):
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
193
        return Fns.request_only()
194
    else:
195
        return fn
196
197
198
T = TypeVar("T", covariant=True)
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...
199
V = TypeVar("V", covariant=True)
0 ignored issues
show
Coding Style Naming introduced by
Class name "V" 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...
200
201
202
@dataclass(frozen=True, eq=True)
0 ignored issues
show
Documentation introduced by
Empty class docstring
Loading history...
203
class AbstractJsonNavigator:
204
    """"""
205
206
207
@dataclass(frozen=True, eq=True)
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
208
class JsonNavigatorSingleOptional(AbstractJsonNavigator):
209
    contents: Optional[T]
210
211
    @property
212
    def get(self) -> T:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
213
        return self.contents
214
215
    def __floordiv__(
216
        self, conversion: Union[Type[T], Callable[[T], V]]
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
217
    ) -> JsonNavigatorSingleOptional:
218
        return JsonNavigatorSingleOptional(conversion(self.contents))
219
220
221
@dataclass(frozen=True, eq=True)
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
222
class JsonNavigatorListOfOptionals(AbstractJsonNavigator):
223
    contents: List[Optional[T]]
224
225
    @property
226
    def to_list(self) -> List[T]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
227
        return [k for k in self.contents if k is not None]
228
229
    @property
230
    def to_set(self) -> FrozenSet[T]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
231
        return frozenset([k for k in self.contents if k is not None])
232
233
    def __truediv__(
234
        self, key: Union[None, str, FilterFn, Callable[[Any], Any]]
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
235
    ) -> JsonNavigatorListOfOptionals:
236
        if isinstance(key, FilterFn):
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
237
            return self._filter(key)
238
        else:
239
            return self._go_inside(key)
240
241
    def __rshift__(self, key: Callable[[List[Optional[Any]]], Any]) -> JsonNavigatorSingleOptional:
242
        # we can't skip 2
243
        return self // key
244
245
    def __floordiv__(
246
        self, key: Callable[[List[Optional[Any]]], Any]
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
247
    ) -> JsonNavigatorSingleOptional:
248
        return JsonNavigatorSingleOptional(key(self.contents))
249
250
    def _go_inside(
251
        self, key: Union[None, str, Callable[[Any], Any]]
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
252
    ) -> JsonNavigatorListOfOptionals:
253
        fn = _get_conversion_fn(key)
0 ignored issues
show
Coding Style Naming introduced by
Variable name "fn" 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...
254
        return JsonNavigatorListOfOptionals(
255
            [None if content is None else fn(content) for content in self.contents]
256
        )
257
258
    def _filter(
259
        self, keep_if: Union[Callable[[Optional[T]], bool]]
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
260
    ) -> JsonNavigatorListOfOptionals:
261
        return JsonNavigatorListOfOptionals([z for z in self.contents if keep_if(z)])
262
263
264
@dataclass(frozen=True, eq=True)
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
265
class JsonNavigatorListOfLists(AbstractJsonNavigator):
266
    contents: List[List[Any]]
267
268
    def __truediv__(
269
        self,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
270
        keys: Union[
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
271
            Sequence[Union[None, str, Callable[[Any], Any]]], FilterFn, Callable[[List[T]], Any]
272
        ],
273
    ) -> JsonNavigatorListOfLists:
274
        if isinstance(keys, FilterFn):
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
275
            return self._filter(keys)
276
        else:
277
            return self._go_inside(keys)
278
279
    def __rshift__(self, conversion: Callable[[List[List[T]]], Any]) -> JsonNavigatorSingleOptional:
280
        return JsonNavigatorSingleOptional(conversion(self.contents))
281
282
    def __floordiv__(self, conversion: Callable[[List[T]], Any]) -> JsonNavigatorListOfOptionals:
283
        return JsonNavigatorListOfOptionals([conversion(z) for z in self.contents])
284
285
    def _go_inside(
286
        self, keys: Sequence[Union[None, str, Callable[[Any], Any]]]
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
287
    ) -> JsonNavigatorListOfLists:
288
        fns = [_get_conversion_fn(fn) for fn in keys]
289
        return JsonNavigatorListOfLists(
290
            [
291
                [fn(value) for value, fn in BaseTools.zip_list(contents, fns)]
292
                for contents in self.contents
293
            ]
294
        )
295
296
    def _filter(self, keep_if: FilterFn) -> JsonNavigatorListOfLists:
297
        return JsonNavigatorListOfLists([z for z in self.contents if keep_if(z)])
298
299
300
@dataclass(frozen=True, eq=True)
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
301
class JsonNavigator(AbstractJsonNavigator):
302
    contents: List[NestedDotDict]
303
304
    @property
305
    def get(self) -> List[NestedDotDict]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
306
        return self.contents
307
308
    @classmethod
309
    def create(
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
310
        cls, dct: Union[dict, NestedDotDict, Sequence[dict], Sequence[NestedDotDict]]
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
311
    ) -> JsonNavigator:
312
        if hasattr(dct, "items"):
313
            dct = [dct]
314
        return JsonNavigator([NestedDotDict(dict(**d, _landmark="")) for d in dct])
315
316
    def __truediv__(
317
        self, key: Union[int, str, FilterFn, Callable[[NestedDotDict], NestedDotDict]]
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
318
    ) -> JsonNavigator:
319
        if isinstance(key, FilterFn):
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
320
            return self._filter(key)
321
        else:
322
            return self._go_inside(key)
323
324
    def _go_inside(self, key: Union[int, str]) -> JsonNavigator:
325
        new = []
326
        for z in self.contents:
0 ignored issues
show
Coding Style Naming introduced by
Variable name "z" 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...
327
            if key in z:
328
                # nav = z.get_as("_nav", list, [])
329
                # nav.append(z[key])
330
                if isinstance(z.get(key), list):
331
                    new.extend([NestedDotDict(dict(**m)) for m in z[key]])
332
                elif isinstance(z.get(key), NestedDotDict):
333
                    new.append(NestedDotDict(dict(**z[key])))
334
                elif isinstance(z.get(key), dict):
335
                    new.append(NestedDotDict(dict(**z[key])))
336
                else:
337
                    raise ValueError(f"{key} value is {type(z[key])}: {z[key]}")
338
        return JsonNavigator(new)
339
340
    def _filter(self, keep_where: FilterFn) -> JsonNavigator:
341
        if callable(keep_where):
0 ignored issues
show
unused-code introduced by
Unnecessary "else" after "return"
Loading history...
342
            return JsonNavigator([z for z in self.contents if keep_where(z)])
343
        else:
344
            key, values = keep_where
345
            if not isinstance(values, (Set, FrozenSet, List)):
0 ignored issues
show
introduced by
Second argument of isinstance is not a type
Loading history...
346
                values = {values}
347
            return JsonNavigator([z for z in self.contents if z.get(key) in values])
348
349
    def __mod__(self, key: Union[int, str]) -> JsonNavigator:
350
        new = {}
351
        for z in self.contents:
0 ignored issues
show
Coding Style Naming introduced by
Variable name "z" 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...
352
            if z[key] in new:
353
                raise ValueError(f"{key} found twice")
354
            new[z[key]] = z
355
        return JsonNavigator([NestedDotDict(new)])
356
357
    def __floordiv__(self, keys: Sequence[str]) -> JsonNavigatorListOfLists:
358
        return JsonNavigatorListOfLists([[z.get(key) for key in keys] for z in self.contents])
359
360
    def __rshift__(self, key: str) -> JsonNavigatorListOfOptionals:
361
        return JsonNavigatorListOfOptionals([z.get(key) for z in self.contents])
362
363
364
__all__ = ["JsonNavigator", "JsonNavigatorListOfLists", "Fns", "FilterFn"]
365