ransacklib 0.1.10__py3-none-any.whl → 0.1.11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ransacklib might be problematic. Click here for more details.
- ransack/__init__.py +5 -5
- ransack/exceptions.py +9 -7
- ransack/operator.py +7 -7
- ransack/parser.py +30 -17
- ransack/transformer.py +32 -69
- {ransacklib-0.1.10.dist-info → ransacklib-0.1.11.dist-info}/METADATA +3 -7
- ransacklib-0.1.11.dist-info/RECORD +12 -0
- {ransacklib-0.1.10.dist-info → ransacklib-0.1.11.dist-info}/WHEEL +1 -1
- ransacklib-0.1.10.dist-info/RECORD +0 -12
- {ransacklib-0.1.10.dist-info → ransacklib-0.1.11.dist-info}/licenses/LICENSE +0 -0
- {ransacklib-0.1.10.dist-info → ransacklib-0.1.11.dist-info}/top_level.txt +0 -0
ransack/__init__.py
CHANGED
|
@@ -2,14 +2,14 @@ from .exceptions import EvaluationError, ParseError, RansackError, ShapeError
|
|
|
2
2
|
from .parser import Parser
|
|
3
3
|
from .transformer import Filter, get_values
|
|
4
4
|
|
|
5
|
-
__version__ = "0.1.
|
|
5
|
+
__version__ = "0.1.11"
|
|
6
6
|
|
|
7
7
|
__all__ = (
|
|
8
|
-
"
|
|
9
|
-
"Parser",
|
|
8
|
+
"EvaluationError",
|
|
10
9
|
"Filter",
|
|
11
|
-
"RansackError",
|
|
12
10
|
"ParseError",
|
|
11
|
+
"Parser",
|
|
12
|
+
"RansackError",
|
|
13
13
|
"ShapeError",
|
|
14
|
-
"
|
|
14
|
+
"get_values",
|
|
15
15
|
)
|
ransack/exceptions.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Custom exceptions for Ransack query parsing, evaluation, and shape validation."""
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class RansackError(Exception):
|
|
@@ -14,11 +14,11 @@ class PositionInfoMixin:
|
|
|
14
14
|
self,
|
|
15
15
|
line: int,
|
|
16
16
|
column: int,
|
|
17
|
-
context:
|
|
18
|
-
start_pos:
|
|
19
|
-
end_pos:
|
|
20
|
-
end_line:
|
|
21
|
-
end_column:
|
|
17
|
+
context: str | None = None,
|
|
18
|
+
start_pos: int | None = None,
|
|
19
|
+
end_pos: int | None = None,
|
|
20
|
+
end_line: int | None = None,
|
|
21
|
+
end_column: int | None = None,
|
|
22
22
|
*args,
|
|
23
23
|
**kwargs,
|
|
24
24
|
):
|
|
@@ -36,7 +36,9 @@ class ParseError(PositionInfoMixin, RansackError):
|
|
|
36
36
|
"""Raised when the input query cannot be parsed."""
|
|
37
37
|
|
|
38
38
|
def __str__(self):
|
|
39
|
-
return
|
|
39
|
+
return (
|
|
40
|
+
f"Syntax error at line {self.line}, column {self.column}.\n\n{self.context}"
|
|
41
|
+
)
|
|
40
42
|
|
|
41
43
|
|
|
42
44
|
class ShapeError(PositionInfoMixin, RansackError):
|
ransack/operator.py
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import
|
|
2
|
-
from collections.abc import MutableSequence
|
|
1
|
+
from collections.abc import Callable, MutableSequence
|
|
3
2
|
from datetime import datetime, timedelta, timezone
|
|
4
3
|
from functools import partial
|
|
5
4
|
from numbers import Number
|
|
6
5
|
from operator import add, eq, ge, gt, le, lt, mod, mul, sub, truediv
|
|
7
|
-
from typing import Any
|
|
6
|
+
from typing import Any
|
|
8
7
|
|
|
9
8
|
from ipranges import IP4, IP6, IP4Net, IP4Range, IP6Net, IP6Range
|
|
10
9
|
|
|
11
10
|
from .exceptions import OperatorNotFoundError
|
|
12
11
|
|
|
13
|
-
IP =
|
|
12
|
+
IP = IP4 | IP4Net | IP4Range | IP6 | IP6Net | IP6Range
|
|
14
13
|
Operand = type | str
|
|
15
14
|
|
|
16
15
|
commutative_operators = {"+", "*"}
|
|
@@ -189,7 +188,7 @@ def _comp_ip_ip(op: str, ip1: IP, ip2: IP) -> bool:
|
|
|
189
188
|
case "<=":
|
|
190
189
|
return ip1.low() <= ip2.high()
|
|
191
190
|
case "=":
|
|
192
|
-
return ip1
|
|
191
|
+
return ip1 in ip2 or ip2 in ip1
|
|
193
192
|
case _:
|
|
194
193
|
raise OperatorNotFoundError(op, ("ip", "ip"), (ip1, ip2))
|
|
195
194
|
|
|
@@ -349,10 +348,12 @@ def _get_comp_dict(op: str, comp: Callable) -> dict[tuple[Operand, Operand], Cal
|
|
|
349
348
|
(datetime, tuple): partial(_comp_scalar_range, op),
|
|
350
349
|
(tuple, Number): partial(_comp_range_scalar, op),
|
|
351
350
|
(tuple, datetime): partial(_comp_range_scalar, op),
|
|
351
|
+
(str, MutableSequence): partial(_comp_scalar_list, op),
|
|
352
352
|
("ip", MutableSequence): partial(_comp_scalar_list, op),
|
|
353
353
|
(Number, MutableSequence): partial(_comp_scalar_list, op),
|
|
354
354
|
(datetime, MutableSequence): partial(_comp_scalar_list, op),
|
|
355
355
|
(timedelta, MutableSequence): partial(_comp_scalar_list, op),
|
|
356
|
+
(MutableSequence, str): partial(_comp_list_scalar, op),
|
|
356
357
|
(MutableSequence, "ip"): partial(_comp_list_scalar, op),
|
|
357
358
|
(MutableSequence, Number): partial(_comp_list_scalar, op),
|
|
358
359
|
(MutableSequence, datetime): partial(_comp_list_scalar, op),
|
|
@@ -421,7 +422,7 @@ _operator_map: dict[str, dict[tuple[Operand, Operand], Callable]] = {
|
|
|
421
422
|
">=": _get_comp_dict(">=", ge),
|
|
422
423
|
"<=": _get_comp_dict("<=", le),
|
|
423
424
|
"<": _get_comp_dict("<", lt),
|
|
424
|
-
"=": _get_comp_dict("=", eq),
|
|
425
|
+
"=": _get_comp_dict("=", eq) | {(str, str): eq},
|
|
425
426
|
".": {
|
|
426
427
|
(str, str): _concat,
|
|
427
428
|
(MutableSequence, MutableSequence): _concat,
|
|
@@ -430,7 +431,6 @@ _operator_map: dict[str, dict[tuple[Operand, Operand], Callable]] = {
|
|
|
430
431
|
(str, str): lambda value, pattern: pattern in value,
|
|
431
432
|
(MutableSequence, str): lambda t, x: any(x in elem for elem in t),
|
|
432
433
|
},
|
|
433
|
-
"like": {(str, str): lambda data, pattern: re.match(pattern, data) is not None},
|
|
434
434
|
"in": {
|
|
435
435
|
("ip", "ip"): lambda left, right: left in right,
|
|
436
436
|
(str, MutableSequence): _in_scalar_list,
|
ransack/parser.py
CHANGED
|
@@ -11,6 +11,8 @@ Classes:
|
|
|
11
11
|
input data into an abstract syntax tree (AST).
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
+
import os
|
|
15
|
+
from pathlib import Path
|
|
14
16
|
from typing import Any, no_type_check
|
|
15
17
|
|
|
16
18
|
from lark import Lark, Token, Tree
|
|
@@ -37,24 +39,23 @@ class Parser:
|
|
|
37
39
|
?start: or_expr
|
|
38
40
|
|
|
39
41
|
?or_expr: and_expr
|
|
40
|
-
| or_expr ("or" | "||") and_expr
|
|
42
|
+
| or_expr ("or"i | "||") and_expr -> or_op
|
|
41
43
|
|
|
42
44
|
?and_expr: not_expr
|
|
43
|
-
| and_expr ("and" | "&&") not_expr -> and_op
|
|
45
|
+
| and_expr ("and"i | "&&") not_expr -> and_op
|
|
44
46
|
|
|
45
47
|
?not_expr: comparison
|
|
46
|
-
| ("not" | "!") comparison
|
|
48
|
+
| ("not"i | "!") comparison -> not_op
|
|
47
49
|
|
|
48
50
|
?comparison: sum
|
|
49
|
-
| sum ">" sum
|
|
50
|
-
| sum ">=" sum
|
|
51
|
-
| sum "<" sum
|
|
52
|
-
| sum "<=" sum
|
|
53
|
-
| sum "=" sum
|
|
54
|
-
| sum "==" sum
|
|
55
|
-
| sum
|
|
56
|
-
| sum
|
|
57
|
-
| sum ("contains" | "CONTAINS") sum -> contains_op
|
|
51
|
+
| sum ">" sum -> gt
|
|
52
|
+
| sum ">=" sum -> gte
|
|
53
|
+
| sum "<" sum -> lt
|
|
54
|
+
| sum "<=" sum -> lte
|
|
55
|
+
| sum "=" sum -> any_eq
|
|
56
|
+
| sum "==" sum -> eq
|
|
57
|
+
| sum "in"i sum -> in_op
|
|
58
|
+
| sum "contains"i sum -> contains_op
|
|
58
59
|
|
|
59
60
|
?sum: product
|
|
60
61
|
| sum "+" product -> add
|
|
@@ -93,11 +94,11 @@ class Parser:
|
|
|
93
94
|
| IPV6_RANGE -> ipv6_range
|
|
94
95
|
| IPV6_CIDR -> ipv6_cidr
|
|
95
96
|
|
|
96
|
-
datetime: DATE
|
|
97
|
-
| DATE
|
|
97
|
+
datetime: DATE "T"i? TIME -> datetime_full
|
|
98
|
+
| DATE -> datetime_only_date
|
|
98
99
|
|
|
99
100
|
function: FUNCTION [args] ")"
|
|
100
|
-
args:
|
|
101
|
+
args: or_expr ("," or_expr)*
|
|
101
102
|
|
|
102
103
|
list: "[" [atom ("," atom)*] "]"
|
|
103
104
|
|
|
@@ -112,7 +113,7 @@ class Parser:
|
|
|
112
113
|
TIME.2: /[0-9]{2}:[0-9]{2}:[0-9]{2}(?:\.[0-9]+)?(?:[Zz]|(?:[+-][0-9]{2}:[0-9]{2}))?/
|
|
113
114
|
TIMEDELTA.2: /([0-9]+[D|d])?[0-9]{2}:[0-9]{2}:[0-9]{2}/
|
|
114
115
|
STRING: /"([^"]+)"|\'([^\']+)\'/
|
|
115
|
-
VARIABLE: /\.?[_a-zA-Z][-_a-zA-Z0-9]*(?:\.?[
|
|
116
|
+
VARIABLE: /\.?[_a-zA-Z][-_a-zA-Z0-9]*(?:\.?[_a-zA-Z][-_a-zA-Z0-9]*)*/
|
|
116
117
|
FUNCTION.2: /[_a-zA-Z][_a-zA-Z0-9]*\(/
|
|
117
118
|
|
|
118
119
|
%import common.WS
|
|
@@ -128,7 +129,19 @@ class Parser:
|
|
|
128
129
|
be referenced in queries. Context variables override data variables unless
|
|
129
130
|
the data variable is explicitly accessed with a leading dot.
|
|
130
131
|
"""
|
|
131
|
-
|
|
132
|
+
# Determine the cache file of the grammar. By default, it's in the home
|
|
133
|
+
# directory. But when the environment variable is set, use the value
|
|
134
|
+
# from that variable. This is useful for GitLab CI/CD.
|
|
135
|
+
cache_path = os.getenv("RANSACK_CACHE_PATH")
|
|
136
|
+
if not cache_path:
|
|
137
|
+
cache_path = str(Path("~/.cache/ransack_grammar_cache").expanduser())
|
|
138
|
+
|
|
139
|
+
self.parser = Lark(
|
|
140
|
+
self.grammar,
|
|
141
|
+
parser="lalr",
|
|
142
|
+
propagate_positions=True,
|
|
143
|
+
cache=cache_path,
|
|
144
|
+
)
|
|
132
145
|
self.shaper = ExpressionTransformer(context)
|
|
133
146
|
|
|
134
147
|
@no_type_check
|
ransack/transformer.py
CHANGED
|
@@ -21,14 +21,14 @@ Classes:
|
|
|
21
21
|
import re
|
|
22
22
|
from collections.abc import Mapping, MutableSequence
|
|
23
23
|
from datetime import datetime, timedelta, timezone
|
|
24
|
-
from typing import Any,
|
|
24
|
+
from typing import Any, cast
|
|
25
25
|
|
|
26
26
|
from ipranges import IP4, IP6, IP4Net, IP4Range, IP6Net, IP6Range
|
|
27
27
|
from lark import Token, Transformer, Tree, v_args
|
|
28
28
|
from lark.tree import Meta
|
|
29
29
|
from lark.visitors import Interpreter
|
|
30
30
|
|
|
31
|
-
from .exceptions import EvaluationError
|
|
31
|
+
from .exceptions import EvaluationError, RansackError
|
|
32
32
|
from .function import predefined_functions
|
|
33
33
|
from .operator import binary_operation
|
|
34
34
|
|
|
@@ -106,8 +106,8 @@ def _create_meta(token: Token | TokenWrapper) -> Meta:
|
|
|
106
106
|
|
|
107
107
|
|
|
108
108
|
def _get_data_value(
|
|
109
|
-
path: str, data:
|
|
110
|
-
) ->
|
|
109
|
+
path: str, data: Mapping | MutableSequence | None
|
|
110
|
+
) -> tuple[Any, bool]:
|
|
111
111
|
"""
|
|
112
112
|
Retrieves a value from the data structure (dictionary-like or list-like)
|
|
113
113
|
based on a dotted path.
|
|
@@ -149,7 +149,7 @@ def _get_data_value(
|
|
|
149
149
|
return None, False
|
|
150
150
|
return _get_data_value(remaining_path, data[key])
|
|
151
151
|
|
|
152
|
-
|
|
152
|
+
if isinstance(data, MutableSequence):
|
|
153
153
|
# Aggregate results from all list elements
|
|
154
154
|
aggregated: Any = []
|
|
155
155
|
for item in data:
|
|
@@ -166,7 +166,7 @@ def _get_data_value(
|
|
|
166
166
|
return None, False
|
|
167
167
|
|
|
168
168
|
|
|
169
|
-
def get_values(data:
|
|
169
|
+
def get_values(data: Mapping | MutableSequence | None, path: str) -> list:
|
|
170
170
|
"""
|
|
171
171
|
Public API method to retrieve values from a nested data structure
|
|
172
172
|
using a dotted path. Returns a flat list of values or an empty list
|
|
@@ -435,48 +435,6 @@ class ExpressionTransformer(Transformer):
|
|
|
435
435
|
return Tree("var_from_context", [self.context[var]], _create_meta(var))
|
|
436
436
|
return Tree("var_from_data", [TokenWrapper(var, var)], _create_meta(var))
|
|
437
437
|
|
|
438
|
-
def range_op(self, start: TokenWrapper, end: TokenWrapper):
|
|
439
|
-
"""
|
|
440
|
-
Transforms a range operation (start to end) into an appropriate range object.
|
|
441
|
-
|
|
442
|
-
Parameters:
|
|
443
|
-
start: The start token of the range.
|
|
444
|
-
end: The end token of the range.
|
|
445
|
-
|
|
446
|
-
Returns:
|
|
447
|
-
A tree node wrapping a range object. If the tokens represent
|
|
448
|
-
IP addresses, the range will be transformed into an IP4Range
|
|
449
|
-
or IP6Range object, depending on the IP version; otherwise,
|
|
450
|
-
it will return a generic range operation node.
|
|
451
|
-
"""
|
|
452
|
-
|
|
453
|
-
def _create_ip_node(range_class) -> Tree[TokenWrapper]:
|
|
454
|
-
"""
|
|
455
|
-
Helper function to create a tree node for IP ranges.
|
|
456
|
-
|
|
457
|
-
Parameters:
|
|
458
|
-
range_class: The class (IP4Range or IP6Range) to instantiate.
|
|
459
|
-
|
|
460
|
-
Returns:
|
|
461
|
-
A tree node wrapping the created IP range object.
|
|
462
|
-
"""
|
|
463
|
-
return Tree(
|
|
464
|
-
"ip",
|
|
465
|
-
[
|
|
466
|
-
TokenWrapper(
|
|
467
|
-
_add_tokens(_start.token, _end.token),
|
|
468
|
-
range_class(f"{_start.token}-{_end.token}"),
|
|
469
|
-
)
|
|
470
|
-
],
|
|
471
|
-
)
|
|
472
|
-
|
|
473
|
-
_start, _end = start.children[0], end.children[0]
|
|
474
|
-
if isinstance(_start.real_value, IP4) and isinstance(_end.real_value, IP4):
|
|
475
|
-
return _create_ip_node(IP4Range)
|
|
476
|
-
elif isinstance(_start.real_value, IP6) and isinstance(_end.real_value, IP6):
|
|
477
|
-
return _create_ip_node(IP6Range)
|
|
478
|
-
return Tree("range_op", [start, end])
|
|
479
|
-
|
|
480
438
|
|
|
481
439
|
@v_args(inline=True)
|
|
482
440
|
class Filter(Interpreter):
|
|
@@ -489,7 +447,7 @@ class Filter(Interpreter):
|
|
|
489
447
|
|
|
490
448
|
data = None
|
|
491
449
|
|
|
492
|
-
def eval(self, tree: Tree, data:
|
|
450
|
+
def eval(self, tree: Tree, data: dict | None = None):
|
|
493
451
|
"""
|
|
494
452
|
Evaluates the given tree using the provided data.
|
|
495
453
|
|
|
@@ -501,6 +459,7 @@ class Filter(Interpreter):
|
|
|
501
459
|
The result of evaluating the tree.
|
|
502
460
|
"""
|
|
503
461
|
self.data = data if data is not None else {}
|
|
462
|
+
self.function_calls: dict[tuple[str, Tree | None], Any] = {}
|
|
504
463
|
|
|
505
464
|
res = self.visit(tree)
|
|
506
465
|
|
|
@@ -570,6 +529,8 @@ class Filter(Interpreter):
|
|
|
570
529
|
def _binary_operation(self, op: str, l_tree: Tree, r_tree: Tree) -> Any:
|
|
571
530
|
try:
|
|
572
531
|
return binary_operation(op, self.visit(l_tree), self.visit(r_tree))
|
|
532
|
+
except RansackError:
|
|
533
|
+
raise
|
|
573
534
|
except Exception as e:
|
|
574
535
|
raise EvaluationError(
|
|
575
536
|
str(e),
|
|
@@ -659,19 +620,6 @@ class Filter(Interpreter):
|
|
|
659
620
|
"""
|
|
660
621
|
return not self.visit(tree)
|
|
661
622
|
|
|
662
|
-
def like_op(self, l_tree: Tree, r_tree: Tree) -> bool:
|
|
663
|
-
"""
|
|
664
|
-
Checks if a string matches a regular expression pattern.
|
|
665
|
-
|
|
666
|
-
Parameters:
|
|
667
|
-
l_tree: Subtree representing the string to check.
|
|
668
|
-
r_tree: Subtree representing the regex pattern.
|
|
669
|
-
|
|
670
|
-
Returns:
|
|
671
|
-
True if the string matches the pattern, otherwise False.
|
|
672
|
-
"""
|
|
673
|
-
return self._binary_operation("like", l_tree, r_tree)
|
|
674
|
-
|
|
675
623
|
def in_op(self, l_tree: Tree, r_tree: Tree) -> bool:
|
|
676
624
|
"""
|
|
677
625
|
Checks if a value exists within a data structure.
|
|
@@ -762,18 +710,25 @@ class Filter(Interpreter):
|
|
|
762
710
|
"""
|
|
763
711
|
return [self.visit(x) for x in data.children if x is not None]
|
|
764
712
|
|
|
765
|
-
def range_op(self, l_tree: Tree, r_tree: Tree) -> tuple:
|
|
713
|
+
def range_op(self, l_tree: Tree, r_tree: Tree) -> tuple | IP4Range | IP6Range:
|
|
766
714
|
"""
|
|
767
|
-
Creates a tuple representing a range.
|
|
715
|
+
Creates a tuple representing a range, or IP4Range/IP6Range.
|
|
768
716
|
|
|
769
717
|
Parameters:
|
|
770
718
|
l_tree: Subtree representing the start of the range.
|
|
771
719
|
r_tree: Subtree representing the end of the range.
|
|
772
720
|
|
|
773
721
|
Returns:
|
|
774
|
-
A tuple containing the start and end values
|
|
722
|
+
A tuple containing the start and end values or IP4Range/IP6Range
|
|
723
|
+
object for ip ranges.
|
|
775
724
|
"""
|
|
776
|
-
|
|
725
|
+
start = self.visit(l_tree)
|
|
726
|
+
end = self.visit(r_tree)
|
|
727
|
+
if isinstance(start, IP4) and isinstance(end, IP4):
|
|
728
|
+
return IP4Range(f"{start}-{end}")
|
|
729
|
+
if isinstance(start, IP6) and isinstance(end, IP6):
|
|
730
|
+
return IP6Range(f"{start}-{end}")
|
|
731
|
+
return (start, end)
|
|
777
732
|
|
|
778
733
|
def exists_op(self, path: Token) -> bool:
|
|
779
734
|
"""
|
|
@@ -809,6 +764,9 @@ class Filter(Interpreter):
|
|
|
809
764
|
"""
|
|
810
765
|
Calls a predefined function with the given arguments.
|
|
811
766
|
|
|
767
|
+
Caches the returned value for a given tuple - function name and
|
|
768
|
+
its arguments - so it can return the same value for subsequent calls.
|
|
769
|
+
|
|
812
770
|
Parameters:
|
|
813
771
|
name: The name of the function as a TokenWrapper.
|
|
814
772
|
args: A Tree object containing function arguments or None.
|
|
@@ -819,12 +777,17 @@ class Filter(Interpreter):
|
|
|
819
777
|
Raises:
|
|
820
778
|
ValueError: If the function name is not found in predefined functions.
|
|
821
779
|
"""
|
|
822
|
-
function_name = name.real_value
|
|
780
|
+
function_name = cast("str", name.real_value)
|
|
781
|
+
# If called again with the same args, return the cached value.
|
|
782
|
+
if (key := (function_name, args)) in self.function_calls:
|
|
783
|
+
return self.function_calls[key]
|
|
823
784
|
if function_name in predefined_functions:
|
|
824
785
|
try:
|
|
825
|
-
|
|
786
|
+
res = predefined_functions[function_name](
|
|
826
787
|
*(self.visit(x) for x in (args.children if args else [])) # type: ignore
|
|
827
788
|
)
|
|
789
|
+
self.function_calls[key] = res
|
|
790
|
+
return res
|
|
828
791
|
except TypeError as e:
|
|
829
792
|
raise EvaluationError(
|
|
830
793
|
str(e),
|
|
@@ -834,7 +797,7 @@ class Filter(Interpreter):
|
|
|
834
797
|
end_line=args.meta.end_line if args else name.end_line,
|
|
835
798
|
end_column=args.meta.end_column if args else name.end_column,
|
|
836
799
|
end_pos=args.meta.end_pos if args else name.end_pos,
|
|
837
|
-
)
|
|
800
|
+
) from None
|
|
838
801
|
raise EvaluationError(
|
|
839
802
|
f"Function '{name.real_value}' was not found.",
|
|
840
803
|
line=name.line,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ransacklib
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.11
|
|
4
4
|
Summary: A modern, extensible language for manipulation with structured data
|
|
5
5
|
Author-email: "Rajmund H. Hruška" <rajmund.hruska@cesnet.cz>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -11,7 +11,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
11
11
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
12
12
|
Classifier: Intended Audience :: Developers
|
|
13
13
|
Classifier: Operating System :: OS Independent
|
|
14
|
-
Requires-Python: >=3.
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
15
|
Description-Content-Type: text/x-rst
|
|
16
16
|
License-File: LICENSE
|
|
17
17
|
Requires-Dist: lark<1.3.0,>=1.2.2
|
|
@@ -19,14 +19,10 @@ Requires-Dist: ipranges<0.2,>=0.1.12
|
|
|
19
19
|
Provides-Extra: dev
|
|
20
20
|
Requires-Dist: pytest; extra == "dev"
|
|
21
21
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
22
|
-
Requires-Dist: flake8; extra == "dev"
|
|
23
|
-
Requires-Dist: flake8-pytest-style; extra == "dev"
|
|
24
|
-
Requires-Dist: flake8-bugbear; extra == "dev"
|
|
25
22
|
Requires-Dist: sphinx; extra == "dev"
|
|
26
23
|
Requires-Dist: sphinx_rtd_theme; extra == "dev"
|
|
27
|
-
Requires-Dist: black; extra == "dev"
|
|
28
24
|
Requires-Dist: mypy; extra == "dev"
|
|
29
|
-
Requires-Dist:
|
|
25
|
+
Requires-Dist: ruff; extra == "dev"
|
|
30
26
|
Dynamic: license-file
|
|
31
27
|
|
|
32
28
|
Welcome to ransack
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
ransack/__init__.py,sha256=l6a6YK8gm6OkFofd_d0VZp3Gxkb7IAmLlqa83oFZ5J8,313
|
|
2
|
+
ransack/exceptions.py,sha256=Ca-r6uQf7Ou-zsQwV3JDqsrEvRVx6Em2Y0ZwZwa1YkI,3732
|
|
3
|
+
ransack/function.py,sha256=u2WR0ZpwETR1MsZCpur680PExeWqH6phQLSTI0DloGI,1080
|
|
4
|
+
ransack/operator.py,sha256=82azEryAMDAwQmCdMPuxdEKFJY46DpuRi639ccuKlLM,16217
|
|
5
|
+
ransack/parser.py,sha256=RuL1whRgm5AhzXX0PrjNevti9fzPL60p_jeC7R15dUs,7230
|
|
6
|
+
ransack/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
ransack/transformer.py,sha256=WaIdcZ9xXIbzkOc2v92JdzbmMPKTjxC0ijcTKDjhSfk,27128
|
|
8
|
+
ransacklib-0.1.11.dist-info/licenses/LICENSE,sha256=mUbyu3f_FVAKt9MzuOjDggjsvV-WtwgSPlKPe_xKRBo,1074
|
|
9
|
+
ransacklib-0.1.11.dist-info/METADATA,sha256=kmcFprjAYOXOUVzoIO6I8PG-TK9z-40WgpeluDi0cRI,2715
|
|
10
|
+
ransacklib-0.1.11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
11
|
+
ransacklib-0.1.11.dist-info/top_level.txt,sha256=qVBXu4XHhFMobvS9sDB-r6EQB6hwLQUEiSvX0N7DelM,8
|
|
12
|
+
ransacklib-0.1.11.dist-info/RECORD,,
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
ransack/__init__.py,sha256=QLp0go5FdQXrASgnBNaNKTxEEst54OBWYVG3KgTkVCU,313
|
|
2
|
-
ransack/exceptions.py,sha256=GWV2XlgGYeuGr5I3gPL3vlmakacOg--lAtFCy5iQbsw,3741
|
|
3
|
-
ransack/function.py,sha256=u2WR0ZpwETR1MsZCpur680PExeWqH6phQLSTI0DloGI,1080
|
|
4
|
-
ransack/operator.py,sha256=hpdPNZdSxTqCl6il6j6PZnqMH6HniBCiAdhjqL-dbik,16201
|
|
5
|
-
ransack/parser.py,sha256=aovOrJ4ZKcb0rSn8LJm2GiQZkn9JBaWssAfQ0pj9gUo,7010
|
|
6
|
-
ransack/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
ransack/transformer.py,sha256=Ku9DjD3zy4Z1JJduwR_IqqLxmk9BvMct2mmxjzxNaR0,28272
|
|
8
|
-
ransacklib-0.1.10.dist-info/licenses/LICENSE,sha256=mUbyu3f_FVAKt9MzuOjDggjsvV-WtwgSPlKPe_xKRBo,1074
|
|
9
|
-
ransacklib-0.1.10.dist-info/METADATA,sha256=yDcMa86-7G-gME6hHei7GlIzeGRIdENF0NN4tDwhAkk,2887
|
|
10
|
-
ransacklib-0.1.10.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
|
|
11
|
-
ransacklib-0.1.10.dist-info/top_level.txt,sha256=qVBXu4XHhFMobvS9sDB-r6EQB6hwLQUEiSvX0N7DelM,8
|
|
12
|
-
ransacklib-0.1.10.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|