python-code-validator 0.1.2__tar.gz → 0.2.1__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.1.2/src/python_code_validator.egg-info → python_code_validator-0.2.1}/PKG-INFO +13 -30
- {python_code_validator-0.1.2 → python_code_validator-0.2.1}/README.md +2 -2
- python_code_validator-0.2.1/pyproject.toml +120 -0
- python_code_validator-0.2.1/src/code_validator/__init__.py +57 -0
- python_code_validator-0.2.1/src/code_validator/__main__.py +20 -0
- python_code_validator-0.2.1/src/code_validator/cli.py +112 -0
- {python_code_validator-0.1.2 → python_code_validator-0.2.1}/src/code_validator/components/ast_utils.py +6 -2
- {python_code_validator-0.1.2 → python_code_validator-0.2.1}/src/code_validator/components/definitions.py +6 -5
- {python_code_validator-0.1.2 → python_code_validator-0.2.1}/src/code_validator/components/factories.py +35 -16
- {python_code_validator-0.1.2 → python_code_validator-0.2.1}/src/code_validator/components/scope_handler.py +5 -3
- python_code_validator-0.2.1/src/code_validator/config.py +152 -0
- python_code_validator-0.2.1/src/code_validator/core.py +223 -0
- python_code_validator-0.2.1/src/code_validator/output.py +236 -0
- {python_code_validator-0.1.2 → python_code_validator-0.2.1}/src/code_validator/rules_library/basic_rules.py +23 -16
- {python_code_validator-0.1.2 → python_code_validator-0.2.1}/src/code_validator/rules_library/constraint_logic.py +301 -257
- {python_code_validator-0.1.2 → python_code_validator-0.2.1}/src/code_validator/rules_library/selector_nodes.py +66 -5
- {python_code_validator-0.1.2 → python_code_validator-0.2.1/src/python_code_validator.egg-info}/PKG-INFO +13 -30
- {python_code_validator-0.1.2 → python_code_validator-0.2.1}/src/python_code_validator.egg-info/requires.txt +2 -2
- {python_code_validator-0.1.2 → python_code_validator-0.2.1}/tests/test_validator.py +9 -10
- python_code_validator-0.1.2/pyproject.toml +0 -62
- python_code_validator-0.1.2/src/code_validator/__init__.py +0 -25
- python_code_validator-0.1.2/src/code_validator/__main__.py +0 -11
- python_code_validator-0.1.2/src/code_validator/cli.py +0 -88
- python_code_validator-0.1.2/src/code_validator/config.py +0 -99
- python_code_validator-0.1.2/src/code_validator/core.py +0 -100
- python_code_validator-0.1.2/src/code_validator/output.py +0 -90
- {python_code_validator-0.1.2 → python_code_validator-0.2.1}/LICENSE +0 -0
- {python_code_validator-0.1.2 → python_code_validator-0.2.1}/setup.cfg +0 -0
- {python_code_validator-0.1.2 → python_code_validator-0.2.1}/src/code_validator/components/__init__.py +0 -0
- {python_code_validator-0.1.2 → python_code_validator-0.2.1}/src/code_validator/exceptions.py +0 -0
- {python_code_validator-0.1.2 → python_code_validator-0.2.1}/src/code_validator/rules_library/__init__.py +0 -0
- {python_code_validator-0.1.2 → python_code_validator-0.2.1}/src/python_code_validator.egg-info/SOURCES.txt +0 -0
- {python_code_validator-0.1.2 → python_code_validator-0.2.1}/src/python_code_validator.egg-info/dependency_links.txt +0 -0
- {python_code_validator-0.1.2 → python_code_validator-0.2.1}/src/python_code_validator.egg-info/entry_points.txt +0 -0
- {python_code_validator-0.1.2 → python_code_validator-0.2.1}/src/python_code_validator.egg-info/top_level.txt +0 -0
- {python_code_validator-0.1.2 → python_code_validator-0.2.1}/tests/test_components.py +0 -0
- {python_code_validator-0.1.2 → python_code_validator-0.2.1}/tests/test_scope_handler.py +0 -0
@@ -1,49 +1,32 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: python-code-validator
|
3
|
-
Version: 0.1
|
4
|
-
Summary: A flexible framework for static validation of Python code
|
3
|
+
Version: 0.2.1
|
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: MIT
|
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
|
-
|
6
|
+
License-Expression: MIT
|
28
7
|
Project-URL: Homepage, https://github.com/Qu1nel/PythonCodeValidator
|
29
8
|
Project-URL: Documentation, https://pythoncodevalidator.readthedocs.io/en/latest/
|
30
9
|
Project-URL: Bug Tracker, https://github.com/Qu1nel/PythonCodeValidator/issues
|
31
|
-
Keywords: validation,linter,static analysis,testing,education
|
32
|
-
Classifier: Development Status ::
|
10
|
+
Keywords: validation,linter,static analysis,testing,education,ast
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
12
|
+
Classifier: Intended Audience :: Developers
|
13
|
+
Classifier: Intended Audience :: Education
|
33
14
|
Classifier: Programming Language :: Python :: 3
|
34
15
|
Classifier: Programming Language :: Python :: 3.11
|
35
16
|
Classifier: Programming Language :: Python :: 3.12
|
36
|
-
Classifier: License :: OSI Approved :: MIT License
|
37
17
|
Classifier: Operating System :: OS Independent
|
18
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
19
|
+
Classifier: Topic :: Software Development :: Testing
|
20
|
+
Classifier: Topic :: Education
|
38
21
|
Requires-Python: >=3.11
|
39
22
|
Description-Content-Type: text/markdown
|
40
23
|
License-File: LICENSE
|
24
|
+
Requires-Dist: flake8>=7.0.0
|
41
25
|
Provides-Extra: dev
|
42
26
|
Requires-Dist: ruff>=0.4.0; extra == "dev"
|
43
|
-
Requires-Dist: flake8>=7.0.0; extra == "dev"
|
44
27
|
Requires-Dist: build; extra == "dev"
|
45
28
|
Requires-Dist: twine; extra == "dev"
|
46
|
-
Requires-Dist: coverage>=7.5.0; extra == "dev"
|
29
|
+
Requires-Dist: coverage[toml]>=7.5.0; extra == "dev"
|
47
30
|
Provides-Extra: docs
|
48
31
|
Requires-Dist: sphinx>=7.0.0; extra == "docs"
|
49
32
|
Requires-Dist: furo; extra == "docs"
|
@@ -282,7 +265,7 @@ Validation failed.
|
|
282
265
|
**[Read the Docs](https://[your-project].readthedocs.io)**.
|
283
266
|
- **Developer's Guide**: For a deep dive into the architecture, see the
|
284
267
|
**[How It Works guide](./docs/how_it_works/index.md)**.
|
285
|
-
- **Interactive AI-Powered Docs**:
|
268
|
+
- **Interactive AI-Powered Docs**: **[DeepWiki](https://deepwiki.com/Qu1nel/PythonCodeValidator)**.
|
286
269
|
|
287
270
|
## 🤝 Contributing
|
288
271
|
|
@@ -229,7 +229,7 @@ Validation failed.
|
|
229
229
|
**[Read the Docs](https://[your-project].readthedocs.io)**.
|
230
230
|
- **Developer's Guide**: For a deep dive into the architecture, see the
|
231
231
|
**[How It Works guide](./docs/how_it_works/index.md)**.
|
232
|
-
- **Interactive AI-Powered Docs**:
|
232
|
+
- **Interactive AI-Powered Docs**: **[DeepWiki](https://deepwiki.com/Qu1nel/PythonCodeValidator)**.
|
233
233
|
|
234
234
|
## 🤝 Contributing
|
235
235
|
|
@@ -253,4 +253,4 @@ Email: **[covach.qn@gmail.com](mailto:covach.qn@gmail.com)** Telegram: **[@qnlln
|
|
253
253
|
|
254
254
|
<br/>
|
255
255
|
|
256
|
-
<p align="right"><a href="./LICENSE">MIT</a> © <a href="https://github.com/Qu1nel/">Ivan Kovach</a></p>
|
256
|
+
<p align="right"><a href="./LICENSE">MIT</a> © <a href="https://github.com/Qu1nel/">Ivan Kovach</a></p>
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# ==============================================================================
|
2
|
+
# Build System Configuration (PEP 517/518)
|
3
|
+
# ==============================================================================
|
4
|
+
[build-system]
|
5
|
+
requires = ["setuptools>=61.0"]
|
6
|
+
build-backend = "setuptools.build_meta"
|
7
|
+
|
8
|
+
# ==============================================================================
|
9
|
+
# Project Metadata (PEP 621)
|
10
|
+
# ==============================================================================
|
11
|
+
[project]
|
12
|
+
name = "python-code-validator"
|
13
|
+
version = "0.2.1"
|
14
|
+
description = "A flexible, AST-based framework for static validation of Python code using declarative JSON rules."
|
15
|
+
keywords = ["validation", "linter", "static analysis", "testing", "education", "ast"]
|
16
|
+
authors = [{ name = "Qu1nel", email = "covach.qn@gmail.com" }]
|
17
|
+
readme = "README.md"
|
18
|
+
requires-python = ">=3.11"
|
19
|
+
license-files = ["LICEN[CS]E*"]
|
20
|
+
license = "MIT"
|
21
|
+
classifiers = [
|
22
|
+
"Development Status :: 4 - Beta",
|
23
|
+
"Intended Audience :: Developers",
|
24
|
+
"Intended Audience :: Education",
|
25
|
+
"Programming Language :: Python :: 3",
|
26
|
+
"Programming Language :: Python :: 3.11",
|
27
|
+
"Programming Language :: Python :: 3.12",
|
28
|
+
"Operating System :: OS Independent",
|
29
|
+
"Topic :: Software Development :: Quality Assurance",
|
30
|
+
"Topic :: Software Development :: Testing",
|
31
|
+
"Topic :: Education",
|
32
|
+
]
|
33
|
+
|
34
|
+
dependencies = [
|
35
|
+
"flake8>=7.0.0"
|
36
|
+
]
|
37
|
+
|
38
|
+
[project.urls]
|
39
|
+
"Homepage" = "https://github.com/Qu1nel/PythonCodeValidator"
|
40
|
+
"Documentation" = "https://pythoncodevalidator.readthedocs.io/en/latest/"
|
41
|
+
"Bug Tracker" = "https://github.com/Qu1nel/PythonCodeValidator/issues"
|
42
|
+
|
43
|
+
|
44
|
+
[project.scripts]
|
45
|
+
validate-code = "code_validator.cli:run_from_cli"
|
46
|
+
|
47
|
+
|
48
|
+
[project.optional-dependencies]
|
49
|
+
dev = [
|
50
|
+
"ruff>=0.4.0",
|
51
|
+
"build",
|
52
|
+
"twine",
|
53
|
+
"coverage[toml]>=7.5.0",
|
54
|
+
]
|
55
|
+
docs = [
|
56
|
+
"sphinx>=7.0.0",
|
57
|
+
"furo",
|
58
|
+
"myst-parser",
|
59
|
+
"sphinx-design",
|
60
|
+
]
|
61
|
+
|
62
|
+
# ==============================================================================
|
63
|
+
# Tool Configuration
|
64
|
+
# ==============================================================================
|
65
|
+
|
66
|
+
[tool.ruff]
|
67
|
+
line-length = 120
|
68
|
+
exclude = [
|
69
|
+
".venv",
|
70
|
+
"build",
|
71
|
+
"dist",
|
72
|
+
"src/python_code_validator.egg-info",
|
73
|
+
"tests/fixtures",
|
74
|
+
]
|
75
|
+
|
76
|
+
[tool.ruff.lint]
|
77
|
+
select = [
|
78
|
+
"E", # pycodestyle errors
|
79
|
+
"W", # pycodestyle warnings
|
80
|
+
"F", # pyflakes
|
81
|
+
"I", # isort
|
82
|
+
"B", # flake8-bugbear
|
83
|
+
"C4", # flake8-comprehensions
|
84
|
+
"D", # pydocstyle
|
85
|
+
]
|
86
|
+
ignore = []
|
87
|
+
|
88
|
+
[tool.ruff.lint.per-file-ignores]
|
89
|
+
"tests/*" = [
|
90
|
+
"D100", # Missing docstring in public module
|
91
|
+
"D101", # Missing docstring in public class
|
92
|
+
"D102", # Missing docstring in public method
|
93
|
+
"D103", # Missing docstring in public function
|
94
|
+
"D104", # Missing docstring in public package
|
95
|
+
"D107", # Missing docstring in __init__
|
96
|
+
]
|
97
|
+
"src/code_validator/components/__init__.py" = ["D104"]
|
98
|
+
"src/code_validator/rules_library/__init__.py" = ["D104"]
|
99
|
+
"src/code_validator/components/factories.py" = ["D107"]
|
100
|
+
|
101
|
+
[tool.ruff.lint.pydocstyle]
|
102
|
+
convention = "google"
|
103
|
+
|
104
|
+
[tool.ruff.lint.isort]
|
105
|
+
known-first-party = ["code_validator", "tests"]
|
106
|
+
|
107
|
+
[tool.ruff.format]
|
108
|
+
quote-style = "double"
|
109
|
+
|
110
|
+
[tool.coverage.run]
|
111
|
+
omit = [
|
112
|
+
"src/code_validator/__main__.py",
|
113
|
+
"src/code_validator/cli.py",
|
114
|
+
"tests/*",
|
115
|
+
"*/__init__.py",
|
116
|
+
]
|
117
|
+
|
118
|
+
[tool.coverage.report]
|
119
|
+
fail_under = 85
|
120
|
+
show_missing = true
|
@@ -0,0 +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.2.1"
|
@@ -0,0 +1,20 @@
|
|
1
|
+
"""Enables running the validator as a package.
|
2
|
+
|
3
|
+
This file allows the package to be executed directly from the command line
|
4
|
+
using the ``-m`` flag with Python (e.g., ``python -m code_validator``). It
|
5
|
+
serves as the primary entry point that finds and invokes the command-line
|
6
|
+
interface logic defined in the `cli` module.
|
7
|
+
|
8
|
+
Example:
|
9
|
+
You can run the validator package like this from the project root:
|
10
|
+
|
11
|
+
.. code-block:: bash
|
12
|
+
|
13
|
+
python -m code_validator path/to/solution.py path/to/rules.json
|
14
|
+
|
15
|
+
"""
|
16
|
+
|
17
|
+
from .cli import run_from_cli
|
18
|
+
|
19
|
+
if __name__ == "__main__":
|
20
|
+
run_from_cli()
|
@@ -0,0 +1,112 @@
|
|
1
|
+
"""Defines the command-line interface for the code validator.
|
2
|
+
|
3
|
+
This module is responsible for parsing command-line arguments, setting up the
|
4
|
+
application configuration, and orchestrating the main validation workflow. It acts
|
5
|
+
as the primary entry point for user interaction when the tool is called from
|
6
|
+
the terminal.
|
7
|
+
|
8
|
+
The main function, `run_from_cli`, handles the entire application lifecycle,
|
9
|
+
including robust top-level error handling to ensure meaningful exit codes.
|
10
|
+
"""
|
11
|
+
|
12
|
+
import argparse
|
13
|
+
import sys
|
14
|
+
from pathlib import Path
|
15
|
+
|
16
|
+
from . import __version__
|
17
|
+
from .config import AppConfig, ExitCode, LogLevel
|
18
|
+
from .core import StaticValidator
|
19
|
+
from .exceptions import CodeValidatorError
|
20
|
+
from .output import Console, setup_logging
|
21
|
+
|
22
|
+
|
23
|
+
def setup_arg_parser() -> argparse.ArgumentParser:
|
24
|
+
"""Creates and configures the argument parser for the CLI.
|
25
|
+
|
26
|
+
This function defines all positional and optional arguments that the
|
27
|
+
`validate-code` command accepts, including their types, help messages,
|
28
|
+
and default values.
|
29
|
+
|
30
|
+
Returns:
|
31
|
+
argparse.ArgumentParser: A fully configured parser instance ready to
|
32
|
+
process command-line arguments.
|
33
|
+
"""
|
34
|
+
parser = argparse.ArgumentParser(
|
35
|
+
prog="validate-code",
|
36
|
+
description="Validates a Python source file against a set of JSON rules.",
|
37
|
+
)
|
38
|
+
|
39
|
+
parser.add_argument("solution_path", type=Path, help="Path to the Python solution file to validate.")
|
40
|
+
parser.add_argument("rules_path", type=Path, help="Path to the JSON file with validation rules.")
|
41
|
+
|
42
|
+
parser.add_argument(
|
43
|
+
"--log",
|
44
|
+
type=LogLevel,
|
45
|
+
default=LogLevel.ERROR,
|
46
|
+
help=("Set the logging level for stderr (TRACE, DEBUG, INFO, WARNING, ERROR, CRITICAL). Default: ERROR."),
|
47
|
+
)
|
48
|
+
parser.add_argument(
|
49
|
+
"--quiet", action="store_true", help="Suppress all stdout output (validation errors and final verdict)."
|
50
|
+
)
|
51
|
+
parser.add_argument("--no-verdict", action="store_true", help="Suppress stdout output verdict, show failed rules.")
|
52
|
+
parser.add_argument("--stop-on-first-fail", action="store_true", help="Stop after the first failed rule.")
|
53
|
+
parser.add_argument("--version", "-v", action="version", version=f"%(prog)s {__version__}")
|
54
|
+
return parser
|
55
|
+
|
56
|
+
|
57
|
+
def run_from_cli() -> None:
|
58
|
+
"""Runs the full application lifecycle from the command line.
|
59
|
+
|
60
|
+
This is the main entry point for the `validate-code` script. It performs
|
61
|
+
the following steps:
|
62
|
+
1. Parses command-line arguments.
|
63
|
+
2. Initializes the logger, console, and configuration.
|
64
|
+
3. Instantiates and runs the `StaticValidator`.
|
65
|
+
4. Handles all top-level exceptions and exits with an appropriate status code.
|
66
|
+
|
67
|
+
Raises:
|
68
|
+
SystemExit: This function will always terminate the process with an
|
69
|
+
exit code defined in the `ExitCode` enum.
|
70
|
+
"""
|
71
|
+
parser = setup_arg_parser()
|
72
|
+
args = parser.parse_args()
|
73
|
+
|
74
|
+
logger = setup_logging(args.log)
|
75
|
+
console = Console(logger, is_quiet=args.quiet, show_verdict=not args.no_verdict)
|
76
|
+
console.print(f"Level of logging: {args.log}", level=LogLevel.DEBUG)
|
77
|
+
config = AppConfig(
|
78
|
+
solution_path=args.solution_path,
|
79
|
+
rules_path=args.rules_path,
|
80
|
+
log_level=args.log,
|
81
|
+
is_quiet=args.quiet,
|
82
|
+
stop_on_first_fail=args.stop_on_first_fail,
|
83
|
+
)
|
84
|
+
console.print(f"Config is: {config}", level=LogLevel.TRACE)
|
85
|
+
|
86
|
+
try:
|
87
|
+
console.print(f"Starting validation for: {config.solution_path}", level=LogLevel.INFO)
|
88
|
+
validator = StaticValidator(config, console)
|
89
|
+
|
90
|
+
console.print("Start of validation..", level=LogLevel.TRACE)
|
91
|
+
is_valid = validator.run()
|
92
|
+
console.print(f"End of validation with result: {is_valid = }", level=LogLevel.TRACE)
|
93
|
+
|
94
|
+
if is_valid:
|
95
|
+
console.print("Validation successful.", level=LogLevel.INFO, is_verdict=True)
|
96
|
+
sys.exit(ExitCode.SUCCESS)
|
97
|
+
else:
|
98
|
+
console.print("Validation failed.", level=LogLevel.INFO, is_verdict=True)
|
99
|
+
sys.exit(ExitCode.VALIDATION_FAILED)
|
100
|
+
|
101
|
+
except CodeValidatorError as e:
|
102
|
+
console.print("Error: Internal Error of validator!", level=LogLevel.CRITICAL)
|
103
|
+
logger.exception(f"Traceback for CodeValidatorError: {e}")
|
104
|
+
sys.exit(ExitCode.VALIDATION_FAILED)
|
105
|
+
except FileNotFoundError as e:
|
106
|
+
console.print(f"Error: File not found - {e.filename}!", level=LogLevel.CRITICAL)
|
107
|
+
logger.exception(f"Traceback for FileNotFoundError: {e}")
|
108
|
+
sys.exit(ExitCode.FILE_NOT_FOUND)
|
109
|
+
except Exception as e:
|
110
|
+
console.print(f"An unexpected error occurred: {e.__class__.__name__}!", level=LogLevel.CRITICAL)
|
111
|
+
logger.exception(f"Traceback for unexpected error: {e}")
|
112
|
+
sys.exit(ExitCode.UNEXPECTED_ERROR)
|
@@ -1,4 +1,9 @@
|
|
1
|
-
"""Provides utility functions for working with Python's Abstract Syntax Trees (AST).
|
1
|
+
"""Provides utility functions for working with Python's Abstract Syntax Trees (AST).
|
2
|
+
|
3
|
+
This module contains helper functions that perform common operations on AST nodes,
|
4
|
+
such as enriching the tree with parent references. These utilities are used by
|
5
|
+
various components of the validator to simplify complex tree analysis.
|
6
|
+
"""
|
2
7
|
|
3
8
|
import ast
|
4
9
|
|
@@ -15,7 +20,6 @@ def enrich_ast_with_parents(tree: ast.Module) -> None:
|
|
15
20
|
"""
|
16
21
|
for node in ast.walk(tree):
|
17
22
|
for child in ast.iter_child_nodes(node):
|
18
|
-
# Dynamically add a reference to the parent node.
|
19
23
|
child.parent = node
|
20
24
|
|
21
25
|
|
@@ -1,10 +1,11 @@
|
|
1
|
-
"""Defines the core component interfaces using Protocols.
|
1
|
+
"""Defines the core component interfaces for the validator using Protocols.
|
2
2
|
|
3
3
|
This module establishes the fundamental "contracts" for the main architectural
|
4
|
-
components
|
5
|
-
|
6
|
-
|
7
|
-
|
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.
|
8
9
|
"""
|
9
10
|
|
10
11
|
import ast
|
@@ -1,17 +1,18 @@
|
|
1
1
|
"""Contains factories for creating rule, selector, and constraint objects.
|
2
2
|
|
3
3
|
This module implements the Factory Method design pattern to decouple the core
|
4
|
-
validator engine from the concrete
|
5
|
-
responsible for parsing raw dictionary configurations from
|
6
|
-
the appropriate handler classes
|
4
|
+
validator engine from the concrete implementations of its components. Factories
|
5
|
+
are responsible for parsing raw dictionary configurations from the main JSON
|
6
|
+
rules file and instantiating the appropriate handler classes from the
|
7
|
+
`rules_library`.
|
7
8
|
"""
|
8
9
|
|
9
10
|
import dataclasses
|
10
11
|
from typing import Any, Type, TypeVar
|
11
12
|
|
12
|
-
from ..config import ConstraintConfig, FullRuleCheck, FullRuleConfig, SelectorConfig, ShortRuleConfig
|
13
|
+
from ..config import ConstraintConfig, FullRuleCheck, FullRuleConfig, LogLevel, SelectorConfig, ShortRuleConfig
|
13
14
|
from ..exceptions import RuleParsingError
|
14
|
-
from ..output import Console
|
15
|
+
from ..output import Console, log_initialization
|
15
16
|
from ..rules_library.basic_rules import CheckLinterRule, CheckSyntaxRule, FullRuleHandler
|
16
17
|
from ..rules_library.constraint_logic import (
|
17
18
|
IsForbiddenConstraint,
|
@@ -70,6 +71,7 @@ class RuleFactory:
|
|
70
71
|
_constraint_factory (ConstraintFactory): A factory for creating constraint objects.
|
71
72
|
"""
|
72
73
|
|
74
|
+
@log_initialization(level=LogLevel.TRACE)
|
73
75
|
def __init__(self, console: Console):
|
74
76
|
"""Initializes the RuleFactory.
|
75
77
|
|
@@ -100,8 +102,10 @@ class RuleFactory:
|
|
100
102
|
required keys, or specifies an unknown type.
|
101
103
|
"""
|
102
104
|
rule_id = rule_config.get("rule_id")
|
105
|
+
self._console.print(f"Start parsing rule ({rule_id}):\n{rule_config}", level=LogLevel.TRACE)
|
103
106
|
try:
|
104
107
|
if "type" in rule_config:
|
108
|
+
self._console.print(f"Rule {rule_id} is shorted rule - {rule_config['type']}", level=LogLevel.DEBUG)
|
105
109
|
config = _create_dataclass_from_dict(ShortRuleConfig, rule_config)
|
106
110
|
return self._create_short_rule(config)
|
107
111
|
|
@@ -109,20 +113,31 @@ class RuleFactory:
|
|
109
113
|
raw_selector_cfg = rule_config["check"]["selector"]
|
110
114
|
raw_constraint_cfg = rule_config["check"]["constraint"]
|
111
115
|
|
112
|
-
selector = self._selector_factory.create(raw_selector_cfg)
|
113
|
-
constraint = self._constraint_factory.create(raw_constraint_cfg)
|
114
|
-
|
115
116
|
selector_cfg = _create_dataclass_from_dict(SelectorConfig, raw_selector_cfg)
|
116
117
|
constraint_cfg = _create_dataclass_from_dict(ConstraintConfig, raw_constraint_cfg)
|
118
|
+
|
119
|
+
selector = self._selector_factory.create(selector_cfg)
|
120
|
+
constraint = self._constraint_factory.create(constraint_cfg)
|
121
|
+
|
122
|
+
self._console.print(
|
123
|
+
f"Rule {rule_id} is general rule with: selector - "
|
124
|
+
f"{selector_cfg.type}, constraint - {raw_constraint_cfg['type']}",
|
125
|
+
level=LogLevel.DEBUG,
|
126
|
+
)
|
127
|
+
|
117
128
|
check_cfg = FullRuleCheck(selector=selector_cfg, constraint=constraint_cfg)
|
129
|
+
self._console.print(f"Create FullRuleCheck: {check_cfg}", level=LogLevel.TRACE)
|
130
|
+
|
118
131
|
config = FullRuleConfig(
|
119
132
|
rule_id=rule_config["rule_id"],
|
120
133
|
message=rule_config["message"],
|
121
134
|
check=check_cfg,
|
122
135
|
is_critical=rule_config.get("is_critical", False),
|
123
136
|
)
|
137
|
+
self._console.print(f"Create FullRuleConfig: {config}", level=LogLevel.TRACE)
|
124
138
|
return FullRuleHandler(config, selector, constraint, self._console)
|
125
139
|
else:
|
140
|
+
self._console.print(f"Invalid syntax of rule: {rule_id}", level=LogLevel.WARNING)
|
126
141
|
raise RuleParsingError("Rule must contain 'type' or 'check' key.", rule_id)
|
127
142
|
except (TypeError, KeyError, RuleParsingError) as e:
|
128
143
|
raise RuleParsingError(f"Invalid config for rule '{rule_id}': {e}", rule_id) from e
|
@@ -164,21 +179,23 @@ class SelectorFactory:
|
|
164
179
|
any state.
|
165
180
|
"""
|
166
181
|
|
182
|
+
@log_initialization(level=LogLevel.TRACE)
|
183
|
+
def __init__(self) -> None:
|
184
|
+
pass
|
185
|
+
|
167
186
|
@staticmethod
|
168
|
-
def create(
|
187
|
+
def create(config: SelectorConfig) -> Selector:
|
169
188
|
"""Creates a specific selector instance based on its type.
|
170
189
|
|
171
190
|
This method uses the 'type' field from the selector configuration
|
172
191
|
to determine which concrete Selector class to instantiate.
|
173
192
|
|
174
193
|
Args:
|
175
|
-
|
194
|
+
config: The 'selector' block from a JSON rule.
|
176
195
|
|
177
196
|
Returns:
|
178
197
|
An instance of a class that conforms to the Selector protocol.
|
179
198
|
"""
|
180
|
-
config = _create_dataclass_from_dict(SelectorConfig, selector_config)
|
181
|
-
|
182
199
|
match config.type:
|
183
200
|
case "function_def":
|
184
201
|
return FunctionDefSelector(name=config.name, in_scope_config=config.in_scope)
|
@@ -209,21 +226,23 @@ class ConstraintFactory:
|
|
209
226
|
to a list of AST nodes. This class uses a static `create` method.
|
210
227
|
"""
|
211
228
|
|
229
|
+
@log_initialization(level=LogLevel.TRACE)
|
230
|
+
def __init__(self) -> None:
|
231
|
+
pass
|
232
|
+
|
212
233
|
@staticmethod
|
213
|
-
def create(
|
234
|
+
def create(config: ConstraintConfig) -> Constraint:
|
214
235
|
"""Creates a specific constraint instance based on its type.
|
215
236
|
|
216
237
|
This method uses the 'type' field from the constraint configuration
|
217
238
|
to determine which concrete Constraint class to instantiate.
|
218
239
|
|
219
240
|
Args:
|
220
|
-
|
241
|
+
config: The 'constraint' block from a JSON rule.
|
221
242
|
|
222
243
|
Returns:
|
223
244
|
An instance of a class that conforms to the Constraint protocol.
|
224
245
|
"""
|
225
|
-
config = _create_dataclass_from_dict(ConstraintConfig, constraint_config)
|
226
|
-
|
227
246
|
match config.type:
|
228
247
|
case "is_required":
|
229
248
|
return IsRequiredConstraint(count=config.count)
|
@@ -1,8 +1,10 @@
|
|
1
1
|
"""Provides functionality to find and isolate specific scopes within an AST.
|
2
2
|
|
3
|
-
This module contains
|
4
|
-
|
5
|
-
|
3
|
+
This module contains a key helper function, `find_scope_node`, which is used
|
4
|
+
by `ScopedSelector` instances. Its purpose is to traverse the AST and return
|
5
|
+
a specific subtree (e.g., a function body or class body) based on the
|
6
|
+
`in_scope` configuration from a JSON rule. This allows rules to be applied
|
7
|
+
with high precision to specific parts of the source code.
|
6
8
|
"""
|
7
9
|
|
8
10
|
import ast
|