python-code-validator 0.2.1__tar.gz → 0.4.0__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.
- {python_code_validator-0.2.1/src/python_code_validator.egg-info → python_code_validator-0.4.0}/PKG-INFO +24 -2
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/pyproject.toml +3 -3
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/src/code_validator/__init__.py +57 -57
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/src/code_validator/cli.py +26 -10
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/src/code_validator/components/definitions.py +91 -89
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/src/code_validator/components/factories.py +262 -262
- python_code_validator-0.4.0/src/code_validator/components/typo_detection/__init__.py +25 -0
- python_code_validator-0.4.0/src/code_validator/components/typo_detection/algorithms.py +117 -0
- python_code_validator-0.4.0/src/code_validator/components/typo_detection/detector.py +292 -0
- python_code_validator-0.4.0/src/code_validator/components/typo_detection/formatters.py +230 -0
- python_code_validator-0.4.0/src/code_validator/components/typo_detection/scope_analyzer.py +211 -0
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/src/code_validator/config.py +4 -2
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/src/code_validator/core.py +289 -223
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/src/code_validator/output.py +247 -236
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/src/code_validator/rules_library/basic_rules.py +201 -174
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/src/code_validator/rules_library/constraint_logic.py +81 -0
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/src/code_validator/rules_library/selector_nodes.py +473 -380
- {python_code_validator-0.2.1 → python_code_validator-0.4.0/src/python_code_validator.egg-info}/PKG-INFO +24 -2
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/src/python_code_validator.egg-info/SOURCES.txt +6 -0
- python_code_validator-0.4.0/tests/test_scope_validation.py +196 -0
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/tests/test_validator.py +11 -9
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/LICENSE +0 -0
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/README.md +0 -0
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/setup.cfg +0 -0
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/src/code_validator/__main__.py +0 -0
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/src/code_validator/components/__init__.py +0 -0
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/src/code_validator/components/ast_utils.py +0 -0
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/src/code_validator/components/scope_handler.py +0 -0
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/src/code_validator/exceptions.py +0 -0
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/src/code_validator/rules_library/__init__.py +0 -0
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/src/python_code_validator.egg-info/dependency_links.txt +0 -0
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/src/python_code_validator.egg-info/entry_points.txt +0 -0
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/src/python_code_validator.egg-info/requires.txt +0 -0
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/src/python_code_validator.egg-info/top_level.txt +0 -0
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/tests/test_components.py +0 -0
- {python_code_validator-0.2.1 → python_code_validator-0.4.0}/tests/test_scope_handler.py +0 -0
|
@@ -1,9 +1,30 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-code-validator
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: A flexible, AST-based framework for static validation of Python code using declarative JSON rules.
|
|
5
5
|
Author-email: Qu1nel <covach.qn@gmail.com>
|
|
6
|
-
License
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2025 Ivan Kovach
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
7
28
|
Project-URL: Homepage, https://github.com/Qu1nel/PythonCodeValidator
|
|
8
29
|
Project-URL: Documentation, https://pythoncodevalidator.readthedocs.io/en/latest/
|
|
9
30
|
Project-URL: Bug Tracker, https://github.com/Qu1nel/PythonCodeValidator/issues
|
|
@@ -14,6 +35,7 @@ Classifier: Intended Audience :: Education
|
|
|
14
35
|
Classifier: Programming Language :: Python :: 3
|
|
15
36
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
37
|
Classifier: Programming Language :: Python :: 3.12
|
|
38
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
39
|
Classifier: Operating System :: OS Independent
|
|
18
40
|
Classifier: Topic :: Software Development :: Quality Assurance
|
|
19
41
|
Classifier: Topic :: Software Development :: Testing
|
|
@@ -10,14 +10,13 @@ build-backend = "setuptools.build_meta"
|
|
|
10
10
|
# ==============================================================================
|
|
11
11
|
[project]
|
|
12
12
|
name = "python-code-validator"
|
|
13
|
-
version = "0.
|
|
13
|
+
version = "0.4.0"
|
|
14
14
|
description = "A flexible, AST-based framework for static validation of Python code using declarative JSON rules."
|
|
15
15
|
keywords = ["validation", "linter", "static analysis", "testing", "education", "ast"]
|
|
16
16
|
authors = [{ name = "Qu1nel", email = "covach.qn@gmail.com" }]
|
|
17
17
|
readme = "README.md"
|
|
18
18
|
requires-python = ">=3.11"
|
|
19
|
-
license
|
|
20
|
-
license = "MIT"
|
|
19
|
+
license = { file = "LICENSE" }
|
|
21
20
|
classifiers = [
|
|
22
21
|
"Development Status :: 4 - Beta",
|
|
23
22
|
"Intended Audience :: Developers",
|
|
@@ -25,6 +24,7 @@ classifiers = [
|
|
|
25
24
|
"Programming Language :: Python :: 3",
|
|
26
25
|
"Programming Language :: Python :: 3.11",
|
|
27
26
|
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"License :: OSI Approved :: MIT License",
|
|
28
28
|
"Operating System :: OS Independent",
|
|
29
29
|
"Topic :: Software Development :: Quality Assurance",
|
|
30
30
|
"Topic :: Software Development :: Testing",
|
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
"""A flexible framework for static validation of Python code.
|
|
2
|
-
|
|
3
|
-
This package provides a comprehensive toolkit for statically analyzing Python
|
|
4
|
-
source code based on a declarative set of rules defined in a JSON format. It
|
|
5
|
-
allows for checking syntax, style, structure, and constraints without
|
|
6
|
-
executing the code.
|
|
7
|
-
|
|
8
|
-
The primary entry point for using this package programmatically is the
|
|
9
|
-
`StaticValidator` class.
|
|
10
|
-
|
|
11
|
-
Example:
|
|
12
|
-
A minimal example of using the validator as a library.
|
|
13
|
-
|
|
14
|
-
.. code-block:: python
|
|
15
|
-
|
|
16
|
-
from code_validator import StaticValidator, AppConfig, LogLevel
|
|
17
|
-
from code_validator.output import Console, setup_logging
|
|
18
|
-
from pathlib import Path
|
|
19
|
-
|
|
20
|
-
# Basic setup
|
|
21
|
-
logger = setup_logging(LogLevel.INFO)
|
|
22
|
-
console = Console(logger)
|
|
23
|
-
config = AppConfig(
|
|
24
|
-
solution_path=Path("path/to/solution.py"),
|
|
25
|
-
rules_path=Path("path/to/rules.json"),
|
|
26
|
-
log_level=LogLevel.INFO,
|
|
27
|
-
is_silent=False,
|
|
28
|
-
stop_on_first_fail=False
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
# Run validation
|
|
32
|
-
validator = StaticValidator(config, console)
|
|
33
|
-
is_valid = validator.run()
|
|
34
|
-
|
|
35
|
-
if is_valid:
|
|
36
|
-
print("Validation Passed!")
|
|
37
|
-
|
|
38
|
-
Attributes:
|
|
39
|
-
__version__ (str): The current version of the package.
|
|
40
|
-
__all__ (list[str]): The list of public objects exposed by the package.
|
|
41
|
-
|
|
42
|
-
"""
|
|
43
|
-
|
|
44
|
-
from .config import AppConfig, ExitCode, LogLevel
|
|
45
|
-
from .core import StaticValidator
|
|
46
|
-
from .exceptions import RuleParsingError, ValidationFailedError
|
|
47
|
-
|
|
48
|
-
__all__ = [
|
|
49
|
-
"StaticValidator",
|
|
50
|
-
"AppConfig",
|
|
51
|
-
"ExitCode",
|
|
52
|
-
"LogLevel",
|
|
53
|
-
"ValidationFailedError",
|
|
54
|
-
"RuleParsingError",
|
|
55
|
-
]
|
|
56
|
-
|
|
57
|
-
__version__ = "0.
|
|
1
|
+
"""A flexible framework for static validation of Python code.
|
|
2
|
+
|
|
3
|
+
This package provides a comprehensive toolkit for statically analyzing Python
|
|
4
|
+
source code based on a declarative set of rules defined in a JSON format. It
|
|
5
|
+
allows for checking syntax, style, structure, and constraints without
|
|
6
|
+
executing the code.
|
|
7
|
+
|
|
8
|
+
The primary entry point for using this package programmatically is the
|
|
9
|
+
`StaticValidator` class.
|
|
10
|
+
|
|
11
|
+
Example:
|
|
12
|
+
A minimal example of using the validator as a library.
|
|
13
|
+
|
|
14
|
+
.. code-block:: python
|
|
15
|
+
|
|
16
|
+
from code_validator import StaticValidator, AppConfig, LogLevel
|
|
17
|
+
from code_validator.output import Console, setup_logging
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
# Basic setup
|
|
21
|
+
logger = setup_logging(LogLevel.INFO)
|
|
22
|
+
console = Console(logger)
|
|
23
|
+
config = AppConfig(
|
|
24
|
+
solution_path=Path("path/to/solution.py"),
|
|
25
|
+
rules_path=Path("path/to/rules.json"),
|
|
26
|
+
log_level=LogLevel.INFO,
|
|
27
|
+
is_silent=False,
|
|
28
|
+
stop_on_first_fail=False
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# Run validation
|
|
32
|
+
validator = StaticValidator(config, console)
|
|
33
|
+
is_valid = validator.run()
|
|
34
|
+
|
|
35
|
+
if is_valid:
|
|
36
|
+
print("Validation Passed!")
|
|
37
|
+
|
|
38
|
+
Attributes:
|
|
39
|
+
__version__ (str): The current version of the package.
|
|
40
|
+
__all__ (list[str]): The list of public objects exposed by the package.
|
|
41
|
+
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
from .config import AppConfig, ExitCode, LogLevel
|
|
45
|
+
from .core import StaticValidator
|
|
46
|
+
from .exceptions import RuleParsingError, ValidationFailedError
|
|
47
|
+
|
|
48
|
+
__all__ = [
|
|
49
|
+
"StaticValidator",
|
|
50
|
+
"AppConfig",
|
|
51
|
+
"ExitCode",
|
|
52
|
+
"LogLevel",
|
|
53
|
+
"ValidationFailedError",
|
|
54
|
+
"RuleParsingError",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
__version__ = "0.4.0"
|
|
@@ -43,13 +43,22 @@ def setup_arg_parser() -> argparse.ArgumentParser:
|
|
|
43
43
|
"--log",
|
|
44
44
|
type=LogLevel,
|
|
45
45
|
default=LogLevel.ERROR,
|
|
46
|
-
help=
|
|
46
|
+
help="Set the logging level for stderr (TRACE, DEBUG, INFO, WARNING, ERROR, CRITICAL). Default: ERROR.",
|
|
47
47
|
)
|
|
48
48
|
parser.add_argument(
|
|
49
49
|
"--quiet", action="store_true", help="Suppress all stdout output (validation errors and final verdict)."
|
|
50
50
|
)
|
|
51
51
|
parser.add_argument("--no-verdict", action="store_true", help="Suppress stdout output verdict, show failed rules.")
|
|
52
|
-
parser.add_argument(
|
|
52
|
+
parser.add_argument(
|
|
53
|
+
"--max-messages",
|
|
54
|
+
type=int,
|
|
55
|
+
default=0,
|
|
56
|
+
metavar="N",
|
|
57
|
+
help="Maximum number of error messages to display. 0 for no limit. Default: 0.",
|
|
58
|
+
)
|
|
59
|
+
parser.add_argument(
|
|
60
|
+
"-x", "--exit-on-first-error", action="store_true", help="Exit instantly on the first error found."
|
|
61
|
+
)
|
|
53
62
|
parser.add_argument("--version", "-v", action="version", version=f"%(prog)s {__version__}")
|
|
54
63
|
return parser
|
|
55
64
|
|
|
@@ -79,7 +88,8 @@ def run_from_cli() -> None:
|
|
|
79
88
|
rules_path=args.rules_path,
|
|
80
89
|
log_level=args.log,
|
|
81
90
|
is_quiet=args.quiet,
|
|
82
|
-
|
|
91
|
+
exit_on_first_error=args.exit_on_first_error,
|
|
92
|
+
max_messages=args.max_messages,
|
|
83
93
|
)
|
|
84
94
|
console.print(f"Config is: {config}", level=LogLevel.TRACE)
|
|
85
95
|
|
|
@@ -95,18 +105,24 @@ def run_from_cli() -> None:
|
|
|
95
105
|
console.print("Validation successful.", level=LogLevel.INFO, is_verdict=True)
|
|
96
106
|
sys.exit(ExitCode.SUCCESS)
|
|
97
107
|
else:
|
|
98
|
-
console.print("Validation failed.", level=LogLevel.
|
|
108
|
+
console.print("Validation failed.", level=LogLevel.WARNING, is_verdict=True)
|
|
99
109
|
sys.exit(ExitCode.VALIDATION_FAILED)
|
|
100
110
|
|
|
101
111
|
except CodeValidatorError as e:
|
|
102
|
-
console.print(
|
|
103
|
-
|
|
112
|
+
console.print(
|
|
113
|
+
f"Error: An internal validator error occurred: {e}", level=LogLevel.CRITICAL, show_user=True, exc_info=True
|
|
114
|
+
)
|
|
104
115
|
sys.exit(ExitCode.VALIDATION_FAILED)
|
|
105
116
|
except FileNotFoundError as e:
|
|
106
|
-
console.print(
|
|
107
|
-
|
|
117
|
+
console.print(
|
|
118
|
+
f"Error: Input file not found: {e.filename}", level=LogLevel.CRITICAL, show_user=True, exc_info=True
|
|
119
|
+
)
|
|
108
120
|
sys.exit(ExitCode.FILE_NOT_FOUND)
|
|
109
121
|
except Exception as e:
|
|
110
|
-
console.print(
|
|
111
|
-
|
|
122
|
+
console.print(
|
|
123
|
+
f"Error: An unexpected error occurred: {e.__class__.__name__}. See logs for detailed traceback.",
|
|
124
|
+
level=LogLevel.CRITICAL,
|
|
125
|
+
show_user=True,
|
|
126
|
+
exc_info=True,
|
|
127
|
+
)
|
|
112
128
|
sys.exit(ExitCode.UNEXPECTED_ERROR)
|
|
@@ -1,89 +1,91 @@
|
|
|
1
|
-
"""Defines the core component interfaces for the validator using Protocols.
|
|
2
|
-
|
|
3
|
-
This module establishes the fundamental "contracts" for the main architectural
|
|
4
|
-
components: Rules, Selectors, and Constraints. By using `typing.Protocol`, we
|
|
5
|
-
ensure that any class conforming to these interfaces can be used interchangeably
|
|
6
|
-
by the system's factories and core engine. This enables a flexible and
|
|
7
|
-
decoupled plugin-style architecture, where new components can be added without
|
|
8
|
-
modifying the core logic.
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
import ast
|
|
12
|
-
from typing import Protocol, runtime_checkable
|
|
13
|
-
|
|
14
|
-
from ..config import FullRuleConfig, ShortRuleConfig
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@runtime_checkable
|
|
18
|
-
class Selector(Protocol):
|
|
19
|
-
"""An interface for objects that find and select specific nodes from an AST.
|
|
20
|
-
|
|
21
|
-
A Selector's main responsibility is to traverse the Abstract Syntax Tree (AST)
|
|
22
|
-
of a Python source file and return a list of nodes that match a specific
|
|
23
|
-
criterion (e.g., all function definitions, all import statements).
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
def select(self, tree: ast.Module) -> list[ast.AST]:
|
|
27
|
-
"""Selects and returns a list of relevant AST nodes from the tree.
|
|
28
|
-
|
|
29
|
-
Args:
|
|
30
|
-
tree: The full, parsed AST of the source code to be searched.
|
|
31
|
-
|
|
32
|
-
Returns:
|
|
33
|
-
A list of ast.AST nodes that match the selector's criteria.
|
|
34
|
-
An empty list should be returned if no matching nodes are found.
|
|
35
|
-
"""
|
|
36
|
-
...
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
@runtime_checkable
|
|
40
|
-
class Constraint(Protocol):
|
|
41
|
-
"""An interface for objects that apply a condition to a set of AST nodes.
|
|
42
|
-
|
|
43
|
-
A Constraint takes the list of nodes found by a Selector and checks if they
|
|
44
|
-
satisfy a specific condition (e.g., the list must not be empty, the node
|
|
45
|
-
must inherit from a specific class).
|
|
46
|
-
"""
|
|
47
|
-
|
|
48
|
-
def check(self, nodes: list[ast.AST]) -> bool:
|
|
49
|
-
"""Checks if the given list of nodes satisfies the constraint.
|
|
50
|
-
|
|
51
|
-
Args:
|
|
52
|
-
nodes: A list of AST nodes provided by a Selector.
|
|
53
|
-
|
|
54
|
-
Returns:
|
|
55
|
-
True if the constraint is satisfied, False otherwise.
|
|
56
|
-
"""
|
|
57
|
-
...
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
@runtime_checkable
|
|
61
|
-
class Rule(Protocol):
|
|
62
|
-
"""An interface for any complete, executable validation rule.
|
|
63
|
-
|
|
64
|
-
A Rule represents a single, self-contained validation check. It can be a
|
|
65
|
-
"short" rule (like a linter check) or a "full" rule that internally uses
|
|
66
|
-
a Selector and a Constraint. The core validator engine interacts with
|
|
67
|
-
objects conforming to this protocol.
|
|
68
|
-
|
|
69
|
-
Attributes:
|
|
70
|
-
config: The dataclass object holding the configuration for this rule,
|
|
71
|
-
parsed from the JSON file.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
1
|
+
"""Defines the core component interfaces for the validator using Protocols.
|
|
2
|
+
|
|
3
|
+
This module establishes the fundamental "contracts" for the main architectural
|
|
4
|
+
components: Rules, Selectors, and Constraints. By using `typing.Protocol`, we
|
|
5
|
+
ensure that any class conforming to these interfaces can be used interchangeably
|
|
6
|
+
by the system's factories and core engine. This enables a flexible and
|
|
7
|
+
decoupled plugin-style architecture, where new components can be added without
|
|
8
|
+
modifying the core logic.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import ast
|
|
12
|
+
from typing import Protocol, runtime_checkable
|
|
13
|
+
|
|
14
|
+
from ..config import FullRuleConfig, ShortRuleConfig
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@runtime_checkable
|
|
18
|
+
class Selector(Protocol):
|
|
19
|
+
"""An interface for objects that find and select specific nodes from an AST.
|
|
20
|
+
|
|
21
|
+
A Selector's main responsibility is to traverse the Abstract Syntax Tree (AST)
|
|
22
|
+
of a Python source file and return a list of nodes that match a specific
|
|
23
|
+
criterion (e.g., all function definitions, all import statements).
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def select(self, tree: ast.Module) -> list[ast.AST]:
|
|
27
|
+
"""Selects and returns a list of relevant AST nodes from the tree.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
tree: The full, parsed AST of the source code to be searched.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
A list of ast.AST nodes that match the selector's criteria.
|
|
34
|
+
An empty list should be returned if no matching nodes are found.
|
|
35
|
+
"""
|
|
36
|
+
...
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@runtime_checkable
|
|
40
|
+
class Constraint(Protocol):
|
|
41
|
+
"""An interface for objects that apply a condition to a set of AST nodes.
|
|
42
|
+
|
|
43
|
+
A Constraint takes the list of nodes found by a Selector and checks if they
|
|
44
|
+
satisfy a specific condition (e.g., the list must not be empty, the node
|
|
45
|
+
must inherit from a specific class).
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def check(self, nodes: list[ast.AST]) -> bool:
|
|
49
|
+
"""Checks if the given list of nodes satisfies the constraint.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
nodes: A list of AST nodes provided by a Selector.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
True if the constraint is satisfied, False otherwise.
|
|
56
|
+
"""
|
|
57
|
+
...
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@runtime_checkable
|
|
61
|
+
class Rule(Protocol):
|
|
62
|
+
"""An interface for any complete, executable validation rule.
|
|
63
|
+
|
|
64
|
+
A Rule represents a single, self-contained validation check. It can be a
|
|
65
|
+
"short" rule (like a linter check) or a "full" rule that internally uses
|
|
66
|
+
a Selector and a Constraint. The core validator engine interacts with
|
|
67
|
+
objects conforming to this protocol.
|
|
68
|
+
|
|
69
|
+
Attributes:
|
|
70
|
+
config: The dataclass object holding the configuration for this rule,
|
|
71
|
+
parsed from the JSON file.
|
|
72
|
+
typo_suggestion: Optional typo suggestion message for user display.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
config: FullRuleConfig | ShortRuleConfig
|
|
76
|
+
typo_suggestion: str | None
|
|
77
|
+
|
|
78
|
+
def execute(self, tree: ast.Module | None, source_code: str | None = None) -> bool:
|
|
79
|
+
"""Executes the validation rule.
|
|
80
|
+
|
|
81
|
+
Depending on the rule type, this method might operate on the AST, the
|
|
82
|
+
raw source code, or both.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
tree: The full AST of the source code (for structural checks).
|
|
86
|
+
source_code: The raw source code string (e.g., for linter checks).
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
True if the validation check passes, False otherwise.
|
|
90
|
+
"""
|
|
91
|
+
...
|