ransacklib 0.1.10__tar.gz

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.

@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Rajmund H. Hruska
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,60 @@
1
+ Metadata-Version: 2.4
2
+ Name: ransacklib
3
+ Version: 0.1.10
4
+ Summary: A modern, extensible language for manipulation with structured data
5
+ Author-email: "Rajmund H. Hruška" <rajmund.hruska@cesnet.cz>
6
+ License-Expression: MIT
7
+ Project-URL: Repository, https://gitlab.cesnet.cz/713/mentat/ransack
8
+ Project-URL: Documentation, https://ransack-125e0a.gitlab-pages.cesnet.cz/index.html
9
+ Project-URL: Issues, https://gitlab.cesnet.cz/713/mentat/ransack/-/issues
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Requires-Python: >=3.9
15
+ Description-Content-Type: text/x-rst
16
+ License-File: LICENSE
17
+ Requires-Dist: lark<1.3.0,>=1.2.2
18
+ Requires-Dist: ipranges<0.2,>=0.1.12
19
+ Provides-Extra: dev
20
+ Requires-Dist: pytest; extra == "dev"
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
+ Requires-Dist: sphinx; extra == "dev"
26
+ Requires-Dist: sphinx_rtd_theme; extra == "dev"
27
+ Requires-Dist: black; extra == "dev"
28
+ Requires-Dist: mypy; extra == "dev"
29
+ Requires-Dist: isort; extra == "dev"
30
+ Dynamic: license-file
31
+
32
+ Welcome to ransack
33
+ ==================
34
+
35
+ **ransack** is a modern, extensible language for manipulation with structured data.
36
+
37
+ Structured data --- like `JSON <https://json.org>`_, `YAML <https://yaml.org/>`_, `TOML <https://toml.io/>`_ and domain-specific formats such as `IDEA <https://idea.cesnet.cz>`_ --- form the backbone of many modern applications. These formats appear in configuration files, security logs, telemetry systems, and beyond.
38
+
39
+ **ransack** was designed to meet the increasing need for a robust and expressive language to query, filter, and inspect structured data. Whether used in Python code, as part of a log analysis tool, or as a compiler frontend for other systems, **ransack** provides a flexible foundation.
40
+
41
+ Why ransack?
42
+ ------------
43
+
44
+ ransack is a new implementation and improvement over existing libraries like *Pynspect*, which was widely used in security monitoring systems like `NEMEA <https://nemea.liberouter.org/>`_ and `Mentat <https://mentat.cesnet.cz>`_. Compared to older tools, ransack:
45
+
46
+ - supports **user-defined variables**
47
+ - enables **multi-argument functions**
48
+ - is **extensible** and **modular**
49
+ - supports **multiple backends** (e.g., Python evaluation, SQL translation)
50
+ - offers a clean internal architecture for future enhancements
51
+
52
+ Key features
53
+ ------------
54
+
55
+ - a simple and expressive syntax for filters and conditions
56
+ - support for context-aware variables and data scoping
57
+ - predefined functions
58
+ - support for IPv4/IPv6, datetimes, string and list manipulation
59
+ - safe and maintainable implementation using `Lark <https://lark-parser.readthedocs.io/en/stable/>`_ for parsing
60
+
@@ -0,0 +1,29 @@
1
+ Welcome to ransack
2
+ ==================
3
+
4
+ **ransack** is a modern, extensible language for manipulation with structured data.
5
+
6
+ Structured data --- like `JSON <https://json.org>`_, `YAML <https://yaml.org/>`_, `TOML <https://toml.io/>`_ and domain-specific formats such as `IDEA <https://idea.cesnet.cz>`_ --- form the backbone of many modern applications. These formats appear in configuration files, security logs, telemetry systems, and beyond.
7
+
8
+ **ransack** was designed to meet the increasing need for a robust and expressive language to query, filter, and inspect structured data. Whether used in Python code, as part of a log analysis tool, or as a compiler frontend for other systems, **ransack** provides a flexible foundation.
9
+
10
+ Why ransack?
11
+ ------------
12
+
13
+ ransack is a new implementation and improvement over existing libraries like *Pynspect*, which was widely used in security monitoring systems like `NEMEA <https://nemea.liberouter.org/>`_ and `Mentat <https://mentat.cesnet.cz>`_. Compared to older tools, ransack:
14
+
15
+ - supports **user-defined variables**
16
+ - enables **multi-argument functions**
17
+ - is **extensible** and **modular**
18
+ - supports **multiple backends** (e.g., Python evaluation, SQL translation)
19
+ - offers a clean internal architecture for future enhancements
20
+
21
+ Key features
22
+ ------------
23
+
24
+ - a simple and expressive syntax for filters and conditions
25
+ - support for context-aware variables and data scoping
26
+ - predefined functions
27
+ - support for IPv4/IPv6, datetimes, string and list manipulation
28
+ - safe and maintainable implementation using `Lark <https://lark-parser.readthedocs.io/en/stable/>`_ for parsing
29
+
@@ -0,0 +1,58 @@
1
+ [build-system]
2
+ requires = ["setuptools >= 61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ransacklib"
7
+ dynamic = ["version"]
8
+ description = "A modern, extensible language for manipulation with structured data"
9
+ license = "MIT"
10
+ readme = "README.rst"
11
+ dependencies = [
12
+ "lark>=1.2.2,<1.3.0",
13
+ "ipranges>=0.1.12,<0.2",
14
+ ]
15
+ requires-python = ">=3.9"
16
+ authors = [
17
+ {name = "Rajmund H. Hruška", email = "rajmund.hruska@cesnet.cz"}
18
+ ]
19
+ classifiers = [
20
+ "Programming Language :: Python :: 3",
21
+ "Topic :: Software Development :: Libraries :: Python Modules",
22
+ "Intended Audience :: Developers",
23
+ "Operating System :: OS Independent"
24
+ ]
25
+
26
+ [project.optional-dependencies]
27
+ dev = [
28
+ "pytest",
29
+ "pytest-cov",
30
+ "flake8",
31
+ "flake8-pytest-style",
32
+ "flake8-bugbear",
33
+ "sphinx",
34
+ "sphinx_rtd_theme",
35
+ "black",
36
+ "mypy",
37
+ "isort",
38
+ ]
39
+
40
+ [project.urls]
41
+ Repository = "https://gitlab.cesnet.cz/713/mentat/ransack"
42
+ Documentation = "https://ransack-125e0a.gitlab-pages.cesnet.cz/index.html"
43
+ Issues = "https://gitlab.cesnet.cz/713/mentat/ransack/-/issues"
44
+
45
+ [tool.setuptools]
46
+ packages = ["ransack"]
47
+
48
+ [tool.setuptools.dynamic]
49
+ version = {attr = "ransack.__version__"}
50
+
51
+ [tool.setuptools.package-data]
52
+ ransack = ["py.typed"]
53
+
54
+ [tool.mypy]
55
+ mypy_path = "$MYPY_CONFIG_FILE_DIR/typings"
56
+
57
+ [tool.isort]
58
+ profile = "black"
@@ -0,0 +1,15 @@
1
+ from .exceptions import EvaluationError, ParseError, RansackError, ShapeError
2
+ from .parser import Parser
3
+ from .transformer import Filter, get_values
4
+
5
+ __version__ = "0.1.10"
6
+
7
+ __all__ = (
8
+ "get_values",
9
+ "Parser",
10
+ "Filter",
11
+ "RansackError",
12
+ "ParseError",
13
+ "ShapeError",
14
+ "EvaluationError",
15
+ )
@@ -0,0 +1,118 @@
1
+ """Custom exceptions for Ransack query parsing, evaluation, and shape validation."""
2
+
3
+ from typing import Any, Optional
4
+
5
+
6
+ class RansackError(Exception):
7
+ """Base exception for all query-related errors."""
8
+
9
+
10
+ class PositionInfoMixin:
11
+ """Mixin class that adds source position metadata to exceptions."""
12
+
13
+ def __init__(
14
+ self,
15
+ line: int,
16
+ column: int,
17
+ context: Optional[str] = None,
18
+ start_pos: Optional[int] = None,
19
+ end_pos: Optional[int] = None,
20
+ end_line: Optional[int] = None,
21
+ end_column: Optional[int] = None,
22
+ *args,
23
+ **kwargs,
24
+ ):
25
+ self.line = line
26
+ self.column = column
27
+ self.context = context
28
+ self.start_pos = start_pos
29
+ self.end_pos = end_pos
30
+ self.end_line = end_line
31
+ self.end_column = end_column
32
+ super().__init__(*args, **kwargs)
33
+
34
+
35
+ class ParseError(PositionInfoMixin, RansackError):
36
+ """Raised when the input query cannot be parsed."""
37
+
38
+ def __str__(self):
39
+ return f"Syntax error at line {self.line}, column {self.column}.\n\n{self.context}" # noqa
40
+
41
+
42
+ class ShapeError(PositionInfoMixin, RansackError):
43
+ """Raised when a query has an invalid shape or structure."""
44
+
45
+ def __init__(self, msg: str, **kwargs):
46
+ self.msg = msg
47
+ super().__init__(**kwargs)
48
+
49
+ def __str__(self):
50
+ return f"Error at line {self.line}, column {self.column}.\n\n{self.msg}\n\n{self.context}" # noqa
51
+
52
+
53
+ class EvaluationError(PositionInfoMixin, RansackError):
54
+ """Raised when a query fails during evaluation."""
55
+
56
+ def __init__(self, msg: str, **kwargs):
57
+ self.msg = msg
58
+ super().__init__(**kwargs)
59
+
60
+ def __str__(self):
61
+ return f"Error at line {self.line}, column {self.column}.\n\n{self.msg}"
62
+
63
+
64
+ class OperatorNotFoundError(RansackError):
65
+ """Exception raised when an operator is not found for the provided types."""
66
+
67
+ def __init__(
68
+ self,
69
+ operator: str,
70
+ types: tuple[type | str, type | str],
71
+ values: tuple[Any, Any] | None = None,
72
+ ):
73
+ """
74
+ Initialize the exception with operator, operand types, and optional values.
75
+
76
+ Args:
77
+ operator: The operator that was attempted.
78
+ types: The types of the operands.
79
+ values: The values of the operands. Defaults to None.
80
+ """
81
+ self.operator = operator
82
+ self.types = types
83
+ self.values = values
84
+ message = f"Operator '{operator}' not found for types {types}"
85
+ if values:
86
+ message += f" with values {values}"
87
+ super().__init__(message)
88
+
89
+
90
+ def add_caret_to_context(
91
+ context: str, line: int, column: int, original_data: str, context_start_pos: int
92
+ ) -> str:
93
+ """
94
+ Inserts a caret (^) under the character at (line, column) relative
95
+ to the full input. `context` is a slice of the full input string.
96
+ `line` and `column` are from the parser (1-based).
97
+ """
98
+ # Recalculate absolute position
99
+ lines_up_to_error = original_data.splitlines()[0 : line - 1]
100
+ absolute_error_pos = (
101
+ sum(len(lines) + 1 for lines in lines_up_to_error) + column - 1
102
+ ) # +1 for newline
103
+
104
+ caret_pos_in_context = absolute_error_pos - context_start_pos
105
+
106
+ # Sanity check
107
+ if caret_pos_in_context < 0 or caret_pos_in_context > len(context):
108
+ return context # fallback, avoid crashing
109
+
110
+ # Find line offset in context
111
+ line_start = context.rfind("\n", 0, caret_pos_in_context) + 1
112
+ line_end = context.find("\n", caret_pos_in_context)
113
+ if line_end == -1:
114
+ line_end = len(context)
115
+
116
+ caret_line = " " * (caret_pos_in_context - line_start) + "^"
117
+
118
+ return context[:line_end] + "\n" + caret_line + context[line_end:]
@@ -0,0 +1,38 @@
1
+ """
2
+ function.py - Defines built-in functions usable in query expressions.
3
+
4
+ This module provides a set of predefined utility functions that can be invoked from
5
+ within query expressions. These functions enable common operations such as determining
6
+ the length of a value or obtaining the current time.
7
+
8
+ Functions:
9
+ - length(x): Returns the length of a string, list, or IP range/network.
10
+ - now(): Returns the current UTC datetime.
11
+
12
+ These functions are registered in the `predefined_functions` dictionary and are
13
+ automatically available during query evaluation.
14
+
15
+ Example usage in query:
16
+ length(some_field) > 3 or now() > 2024-01-01T00:00:00Z
17
+ """
18
+
19
+ import datetime
20
+
21
+ from ipranges import IP4, IP6, IP4Net, IP4Range, IP6Net, IP6Range
22
+
23
+
24
+ def length(x):
25
+ if isinstance(x, (list, str, IP4, IP4Net, IP4Range, IP6, IP6Net, IP6Range)):
26
+ return len(x)
27
+ raise TypeError(f"Function length is not defined for value {x}")
28
+
29
+
30
+ def now():
31
+ return datetime.datetime.now(datetime.timezone.utc)
32
+
33
+
34
+ predefined_functions = {
35
+ "length": length,
36
+ "len": length,
37
+ "now": now,
38
+ }