python-code-validator 0.1.2__py3-none-any.whl → 0.1.3__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.
- code_validator/__init__.py +43 -11
- code_validator/__main__.py +12 -3
- code_validator/cli.py +22 -8
- code_validator/components/ast_utils.py +6 -2
- code_validator/components/definitions.py +6 -5
- code_validator/components/factories.py +4 -3
- code_validator/components/scope_handler.py +5 -3
- code_validator/config.py +58 -6
- code_validator/core.py +113 -20
- code_validator/rules_library/basic_rules.py +16 -14
- code_validator/rules_library/constraint_logic.py +36 -1
- code_validator/rules_library/selector_nodes.py +57 -5
- {python_code_validator-0.1.2.dist-info → python_code_validator-0.1.3.dist-info}/METADATA +11 -6
- python_code_validator-0.1.3.dist-info/RECORD +22 -0
- python_code_validator-0.1.2.dist-info/RECORD +0 -22
- {python_code_validator-0.1.2.dist-info → python_code_validator-0.1.3.dist-info}/WHEEL +0 -0
- {python_code_validator-0.1.2.dist-info → python_code_validator-0.1.3.dist-info}/entry_points.txt +0 -0
- {python_code_validator-0.1.2.dist-info → python_code_validator-0.1.3.dist-info}/licenses/LICENSE +0 -0
- {python_code_validator-0.1.2.dist-info → python_code_validator-0.1.3.dist-info}/top_level.txt +0 -0
code_validator/__init__.py
CHANGED
@@ -1,17 +1,47 @@
|
|
1
1
|
"""A flexible framework for static validation of Python code.
|
2
2
|
|
3
|
-
This package provides a comprehensive toolkit for statically analyzing Python
|
4
|
-
code based on a declarative set of rules defined in a JSON format. It
|
5
|
-
for checking syntax, style, structure, and constraints without
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
+
|
12
42
|
"""
|
13
43
|
|
14
|
-
from .config import AppConfig, ExitCode
|
44
|
+
from .config import AppConfig, ExitCode, LogLevel
|
15
45
|
from .core import StaticValidator
|
16
46
|
from .exceptions import RuleParsingError, ValidationFailedError
|
17
47
|
|
@@ -19,7 +49,9 @@ __all__ = [
|
|
19
49
|
"StaticValidator",
|
20
50
|
"AppConfig",
|
21
51
|
"ExitCode",
|
52
|
+
"LogLevel",
|
22
53
|
"ValidationFailedError",
|
23
54
|
"RuleParsingError",
|
24
55
|
]
|
25
|
-
|
56
|
+
|
57
|
+
__version__ = "0.1.3"
|
code_validator/__main__.py
CHANGED
@@ -1,8 +1,17 @@
|
|
1
|
-
"""Enables running the validator as a
|
1
|
+
"""Enables running the validator as a package.
|
2
2
|
|
3
3
|
This file allows the package to be executed directly from the command line
|
4
|
-
using
|
5
|
-
that invokes 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
|
+
|
6
15
|
"""
|
7
16
|
|
8
17
|
from .cli import run_from_cli
|
code_validator/cli.py
CHANGED
@@ -2,7 +2,11 @@
|
|
2
2
|
|
3
3
|
This module is responsible for parsing command-line arguments, setting up the
|
4
4
|
application configuration, and orchestrating the main validation workflow. It acts
|
5
|
-
as the primary entry point for user interaction
|
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.
|
6
10
|
"""
|
7
11
|
|
8
12
|
import argparse
|
@@ -19,8 +23,13 @@ from .output import Console, setup_logging
|
|
19
23
|
def setup_arg_parser() -> argparse.ArgumentParser:
|
20
24
|
"""Creates and configures the argument parser for the CLI.
|
21
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
|
+
|
22
30
|
Returns:
|
23
|
-
|
31
|
+
argparse.ArgumentParser: A fully configured parser instance ready to
|
32
|
+
process command-line arguments.
|
24
33
|
"""
|
25
34
|
parser = argparse.ArgumentParser(
|
26
35
|
prog="validate-code",
|
@@ -32,7 +41,7 @@ def setup_arg_parser() -> argparse.ArgumentParser:
|
|
32
41
|
"-l",
|
33
42
|
"--log-level",
|
34
43
|
type=LogLevel,
|
35
|
-
choices=LogLevel,
|
44
|
+
choices=list(LogLevel),
|
36
45
|
default=LogLevel.WARNING,
|
37
46
|
help="Set the logging level (default: WARNING).",
|
38
47
|
)
|
@@ -45,14 +54,20 @@ def setup_arg_parser() -> argparse.ArgumentParser:
|
|
45
54
|
def run_from_cli() -> None:
|
46
55
|
"""Runs the full application lifecycle from the command line.
|
47
56
|
|
48
|
-
This
|
49
|
-
|
50
|
-
|
57
|
+
This is the main entry point for the `validate-code` script. It performs
|
58
|
+
the following steps:
|
59
|
+
1. Parses command-line arguments.
|
60
|
+
2. Initializes the logger, console, and configuration.
|
61
|
+
3. Instantiates and runs the `StaticValidator`.
|
62
|
+
4. Handles all top-level exceptions and exits with an appropriate status code.
|
63
|
+
|
64
|
+
Raises:
|
65
|
+
SystemExit: This function will always terminate the process with an
|
66
|
+
exit code defined in the `ExitCode` enum.
|
51
67
|
"""
|
52
68
|
parser = setup_arg_parser()
|
53
69
|
args = parser.parse_args()
|
54
70
|
|
55
|
-
# 1. Setup environment
|
56
71
|
logger = setup_logging(args.log_level)
|
57
72
|
console = Console(logger, is_silent=args.silent)
|
58
73
|
config = AppConfig(
|
@@ -63,7 +78,6 @@ def run_from_cli() -> None:
|
|
63
78
|
stop_on_first_fail=args.stop_on_first_fail,
|
64
79
|
)
|
65
80
|
|
66
|
-
# 2. Run main logic with robust error handling
|
67
81
|
try:
|
68
82
|
console.print(f"Starting validation for: {config.solution_path}", level=LogLevel.INFO)
|
69
83
|
validator = StaticValidator(config, console)
|
@@ -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,9 +1,10 @@
|
|
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
|
@@ -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
|
code_validator/config.py
CHANGED
@@ -34,7 +34,15 @@ class LogLevel(StrEnum):
|
|
34
34
|
|
35
35
|
@dataclass(frozen=True)
|
36
36
|
class AppConfig:
|
37
|
-
"""Stores the main application configuration from CLI arguments.
|
37
|
+
"""Stores the main application configuration from CLI arguments.
|
38
|
+
|
39
|
+
Attributes:
|
40
|
+
solution_path: The file path to the Python solution to be validated.
|
41
|
+
rules_path: The file path to the JSON rules file.
|
42
|
+
log_level: The minimum logging level for console output.
|
43
|
+
is_silent: If True, suppresses all non-log output to stdout.
|
44
|
+
stop_on_first_fail: If True, halts validation after the first failed rule.
|
45
|
+
"""
|
38
46
|
|
39
47
|
solution_path: Path
|
40
48
|
rules_path: Path
|
@@ -45,7 +53,18 @@ class AppConfig:
|
|
45
53
|
|
46
54
|
@dataclass(frozen=True)
|
47
55
|
class SelectorConfig:
|
48
|
-
"""Represents the configuration for a Selector component from a JSON rule.
|
56
|
+
"""Represents the configuration for a Selector component from a JSON rule.
|
57
|
+
|
58
|
+
This dataclass captures all possible keys within the "selector" block
|
59
|
+
of a JSON validation rule.
|
60
|
+
|
61
|
+
Attributes:
|
62
|
+
type: The type of the selector to be used (e.g., "function_def").
|
63
|
+
name: A generic name parameter used by many selectors (e.g., the name
|
64
|
+
of a function, class, or module).
|
65
|
+
node_type: The AST node type name for the `ast_node` selector.
|
66
|
+
in_scope: The scope in which to apply the selector.
|
67
|
+
"""
|
49
68
|
|
50
69
|
type: str
|
51
70
|
name: str | None = None
|
@@ -55,7 +74,21 @@ class SelectorConfig:
|
|
55
74
|
|
56
75
|
@dataclass(frozen=True)
|
57
76
|
class ConstraintConfig:
|
58
|
-
"""Represents the configuration for a Constraint component from a JSON rule.
|
77
|
+
"""Represents the configuration for a Constraint component from a JSON rule.
|
78
|
+
|
79
|
+
This dataclass captures all possible keys within the "constraint" block
|
80
|
+
of a JSON validation rule.
|
81
|
+
|
82
|
+
Attributes:
|
83
|
+
type: The type of the constraint to be applied (e.g., "is_required").
|
84
|
+
count: The exact number of nodes expected. Used by `is_required`.
|
85
|
+
parent_name: The expected parent class name. Used by `must_inherit_from`.
|
86
|
+
expected_type: The expected Python type name. Used by `must_be_type`.
|
87
|
+
allowed_names: A list of permitted names. Used by `name_must_be_in`.
|
88
|
+
allowed_values: A list of permitted literal values. Used by `value_must_be_in`.
|
89
|
+
names: A list of expected argument names. Used by `must_have_args`.
|
90
|
+
exact_match: A boolean flag for argument matching. Used by `must_have_args`.
|
91
|
+
"""
|
59
92
|
|
60
93
|
type: str
|
61
94
|
count: int | None = None
|
@@ -69,7 +102,12 @@ class ConstraintConfig:
|
|
69
102
|
|
70
103
|
@dataclass(frozen=True)
|
71
104
|
class FullRuleCheck:
|
72
|
-
"""Represents the 'check' block within a full validation rule.
|
105
|
+
"""Represents the 'check' block within a full validation rule.
|
106
|
+
|
107
|
+
Attributes:
|
108
|
+
selector: The configuration for the selector component.
|
109
|
+
constraint: The configuration for the constraint component.
|
110
|
+
"""
|
73
111
|
|
74
112
|
selector: SelectorConfig
|
75
113
|
constraint: ConstraintConfig
|
@@ -77,7 +115,14 @@ class FullRuleCheck:
|
|
77
115
|
|
78
116
|
@dataclass(frozen=True)
|
79
117
|
class ShortRuleConfig:
|
80
|
-
"""Represents a 'short' (pre-defined) validation rule from JSON.
|
118
|
+
"""Represents a 'short' (pre-defined) validation rule from JSON.
|
119
|
+
|
120
|
+
Attributes:
|
121
|
+
rule_id: The unique integer identifier for the rule.
|
122
|
+
type: The string identifier for the short rule (e.g., "check_syntax").
|
123
|
+
message: The error message to display if the rule fails.
|
124
|
+
params: A dictionary of optional parameters for the rule.
|
125
|
+
"""
|
81
126
|
|
82
127
|
rule_id: int
|
83
128
|
type: str
|
@@ -87,7 +132,14 @@ class ShortRuleConfig:
|
|
87
132
|
|
88
133
|
@dataclass(frozen=True)
|
89
134
|
class FullRuleConfig:
|
90
|
-
"""Represents a 'full' (custom) validation rule with selector and constraint.
|
135
|
+
"""Represents a 'full' (custom) validation rule with a selector and constraint.
|
136
|
+
|
137
|
+
Attributes:
|
138
|
+
rule_id: The unique integer identifier for the rule.
|
139
|
+
message: The error message to display if the rule fails.
|
140
|
+
check: An object containing the selector and constraint configurations.
|
141
|
+
is_critical: If True, validation halts if this rule fails.
|
142
|
+
"""
|
91
143
|
|
92
144
|
rule_id: int
|
93
145
|
message: str
|
code_validator/core.py
CHANGED
@@ -1,3 +1,44 @@
|
|
1
|
+
"""The core engine of the Python Code Validator.
|
2
|
+
|
3
|
+
This module contains the main orchestrator class, `StaticValidator`, which is
|
4
|
+
responsible for managing the entire validation lifecycle. It loads the source
|
5
|
+
code and a set of JSON rules, then uses a factory-based component system to
|
6
|
+
execute each rule and report the results.
|
7
|
+
|
8
|
+
The core is designed to be decoupled from the specific implementations of rules,
|
9
|
+
selectors, and constraints, allowing for high extensibility.
|
10
|
+
|
11
|
+
Example:
|
12
|
+
To run a validation, you would typically use the CLI, but the core can also
|
13
|
+
be used programmatically:
|
14
|
+
|
15
|
+
.. code-block:: python
|
16
|
+
|
17
|
+
from code_validator import StaticValidator, AppConfig, LogLevel
|
18
|
+
from code_validator.output import Console, setup_logging
|
19
|
+
from pathlib import Path
|
20
|
+
import logging
|
21
|
+
|
22
|
+
logger = logging.getLogger(__name__)
|
23
|
+
console = Console(logger)
|
24
|
+
config = AppConfig(
|
25
|
+
solution_path=Path("path/to/solution.py"),
|
26
|
+
rules_path=Path("path/to/rules.json"),
|
27
|
+
log_level=LogLevel.INFO,
|
28
|
+
is_silent=False,
|
29
|
+
stop_on_first_fail=False
|
30
|
+
)
|
31
|
+
|
32
|
+
validator = StaticValidator(config, console)
|
33
|
+
is_valid = validator.run()
|
34
|
+
|
35
|
+
if is_valid:
|
36
|
+
print("Validation Passed!")
|
37
|
+
else:
|
38
|
+
print(f"Validation Failed. Errors in: {validator.failed_rules_id}")
|
39
|
+
|
40
|
+
"""
|
41
|
+
|
1
42
|
import ast
|
2
43
|
import json
|
3
44
|
|
@@ -10,25 +51,50 @@ from .output import Console
|
|
10
51
|
|
11
52
|
|
12
53
|
class StaticValidator:
|
13
|
-
"""Orchestrates the static validation process.
|
54
|
+
"""Orchestrates the static validation process.
|
55
|
+
|
56
|
+
This class is the main entry point for running a validation session. It
|
57
|
+
manages loading of source files and rules, parsing the code into an AST,
|
58
|
+
and iterating through the rules to execute them.
|
59
|
+
|
60
|
+
Attributes:
|
61
|
+
_config (AppConfig): The application configuration object.
|
62
|
+
_console (Console): The handler for all logging and stdout printing.
|
63
|
+
_rule_factory (RuleFactory): The factory responsible for creating rule objects.
|
64
|
+
_source_code (str): The raw text content of the Python file being validated.
|
65
|
+
_ast_tree (ast.Module | None): The Abstract Syntax Tree of the source code.
|
66
|
+
_rules (list[Rule]): A list of initialized, executable rule objects.
|
67
|
+
_failed_rules (list[int]): A list of rule IDs that failed during the run.
|
68
|
+
"""
|
14
69
|
|
15
70
|
def __init__(self, config: AppConfig, console: Console):
|
16
|
-
"""Initializes the
|
71
|
+
"""Initializes the StaticValidator.
|
72
|
+
|
73
|
+
Args:
|
74
|
+
config: An `AppConfig` object containing all necessary run
|
75
|
+
configurations, such as file paths and flags.
|
76
|
+
console: A `Console` object for handling all output.
|
77
|
+
"""
|
17
78
|
self._config = config
|
18
79
|
self._console = console
|
19
80
|
self._rule_factory = RuleFactory(self._console)
|
20
81
|
self._source_code: str = ""
|
21
82
|
self._ast_tree: ast.Module | None = None
|
22
|
-
self.
|
83
|
+
self._rules: list[Rule] = []
|
23
84
|
self._failed_rules: list[int] = []
|
24
85
|
|
25
86
|
@property
|
26
87
|
def failed_rules_id(self) -> list[int]:
|
27
|
-
"""
|
88
|
+
"""list[int]: A list of rule IDs that failed during the last run."""
|
28
89
|
return self._failed_rules
|
29
90
|
|
30
91
|
def _load_source_code(self) -> None:
|
31
|
-
"""Loads the content of the student's solution file.
|
92
|
+
"""Loads the content of the student's solution file into memory.
|
93
|
+
|
94
|
+
Raises:
|
95
|
+
FileNotFoundError: If the source file specified in the config does not exist.
|
96
|
+
RuleParsingError: If the source file cannot be read for any other reason.
|
97
|
+
"""
|
32
98
|
self._console.print(f"Reading source file: {self._config.solution_path}")
|
33
99
|
try:
|
34
100
|
self._source_code = self._config.solution_path.read_text(encoding="utf-8")
|
@@ -38,25 +104,42 @@ class StaticValidator:
|
|
38
104
|
raise RuleParsingError(f"Cannot read source file: {e}") from e
|
39
105
|
|
40
106
|
def _parse_ast_tree(self) -> bool:
|
41
|
-
"""Parses the loaded source code into an AST.
|
107
|
+
"""Parses the loaded source code into an AST and enriches it.
|
108
|
+
|
109
|
+
This method attempts to parse the source code. If successful, it calls
|
110
|
+
a helper to add parent references to each node in the tree, which is
|
111
|
+
crucial for many advanced checks. If a `SyntaxError` occurs, it
|
112
|
+
checks if a `check_syntax` rule was defined to provide a custom message.
|
113
|
+
|
114
|
+
Returns:
|
115
|
+
bool: True if parsing was successful, False otherwise.
|
116
|
+
"""
|
42
117
|
self._console.print("Parsing Abstract Syntax Tree (AST)...")
|
43
118
|
try:
|
44
119
|
self._ast_tree = ast.parse(self._source_code)
|
45
120
|
enrich_ast_with_parents(self._ast_tree)
|
46
121
|
return True
|
47
122
|
except SyntaxError as e:
|
48
|
-
|
49
|
-
for rule in self._validation_rules:
|
123
|
+
for rule in self._rules:
|
50
124
|
if getattr(rule.config, "type", None) == "check_syntax":
|
51
|
-
self._console.print(rule.config.message, level=
|
125
|
+
self._console.print(rule.config.message, level=LogLevel.ERROR)
|
52
126
|
self._failed_rules.append(rule.config.rule_id)
|
53
127
|
return False
|
54
|
-
|
55
|
-
self._console.print(f"Syntax Error found: {e}", level="ERROR")
|
128
|
+
self._console.print(f"Syntax Error found: {e}", level=LogLevel.ERROR)
|
56
129
|
return False
|
57
130
|
|
58
131
|
def _load_and_parse_rules(self) -> None:
|
59
|
-
"""Loads and parses the JSON file
|
132
|
+
"""Loads and parses the JSON file into executable Rule objects.
|
133
|
+
|
134
|
+
This method reads the JSON rules file, validates its basic structure,
|
135
|
+
and then uses the `RuleFactory` to instantiate a list of concrete
|
136
|
+
Rule objects.
|
137
|
+
|
138
|
+
Raises:
|
139
|
+
FileNotFoundError: If the rules file does not exist.
|
140
|
+
RuleParsingError: If the JSON is malformed or a rule configuration
|
141
|
+
is invalid.
|
142
|
+
"""
|
60
143
|
self._console.print(f"Loading rules from: {self._config.rules_path}")
|
61
144
|
try:
|
62
145
|
rules_data = json.loads(self._config.rules_path.read_text(encoding="utf-8"))
|
@@ -64,18 +147,29 @@ class StaticValidator:
|
|
64
147
|
if not isinstance(raw_rules, list):
|
65
148
|
raise RuleParsingError("`validation_rules` key not found or is not a list.")
|
66
149
|
|
67
|
-
self.
|
68
|
-
self._console.print(f"Successfully parsed {len(self.
|
150
|
+
self._rules = [self._rule_factory.create(rule) for rule in raw_rules]
|
151
|
+
self._console.print(f"Successfully parsed {len(self._rules)} rules.")
|
69
152
|
except json.JSONDecodeError as e:
|
70
153
|
raise RuleParsingError(f"Invalid JSON in rules file: {e}") from e
|
71
154
|
except FileNotFoundError:
|
72
155
|
raise
|
73
156
|
|
74
157
|
def run(self) -> bool:
|
75
|
-
"""Runs the entire validation process.
|
158
|
+
"""Runs the entire validation process from start to finish.
|
159
|
+
|
160
|
+
This is the main public method of the class. It orchestrates the
|
161
|
+
sequence of loading, parsing, and rule execution.
|
162
|
+
|
163
|
+
Returns:
|
164
|
+
bool: True if all validation rules passed, False otherwise.
|
165
|
+
|
166
|
+
Raises:
|
167
|
+
RuleParsingError: Propagated from loading/parsing steps.
|
168
|
+
FileNotFoundError: Propagated from loading steps.
|
169
|
+
"""
|
76
170
|
try:
|
77
171
|
self._load_source_code()
|
78
|
-
self._load_and_parse_rules()
|
172
|
+
self._load_and_parse_rules()
|
79
173
|
|
80
174
|
if not self._parse_ast_tree():
|
81
175
|
return False
|
@@ -83,18 +177,17 @@ class StaticValidator:
|
|
83
177
|
except (FileNotFoundError, RuleParsingError):
|
84
178
|
raise
|
85
179
|
|
86
|
-
for rule in self.
|
87
|
-
# check_syntax уже обработан в _parse_ast_tree, пропускаем его
|
180
|
+
for rule in self._rules:
|
88
181
|
if getattr(rule.config, "type", None) == "check_syntax":
|
89
182
|
continue
|
90
183
|
|
91
184
|
self._console.print(f"Executing rule: {rule.config.rule_id}", level=LogLevel.DEBUG)
|
92
185
|
is_passed = rule.execute(self._ast_tree, self._source_code)
|
93
186
|
if not is_passed:
|
94
|
-
self._console.print(rule.config.message, level=
|
187
|
+
self._console.print(rule.config.message, level=LogLevel.ERROR)
|
95
188
|
self._failed_rules.append(rule.config.rule_id)
|
96
189
|
if getattr(rule.config, "is_critical", False) or self._config.stop_on_first_fail:
|
97
|
-
self._console.print("Critical rule failed. Halting validation.", level=
|
190
|
+
self._console.print("Critical rule failed. Halting validation.", level=LogLevel.WARNING)
|
98
191
|
break
|
99
192
|
|
100
193
|
return not self._failed_rules
|
@@ -1,10 +1,10 @@
|
|
1
|
-
# src/code_validator/rules_library/basic_rules.py
|
2
|
-
|
3
1
|
"""Contains concrete implementations of executable validation rules.
|
4
2
|
|
5
3
|
This module defines the handler classes for both "short" (pre-defined) and
|
6
|
-
"full" (custom selector/constraint) rules. Each class
|
7
|
-
protocol and encapsulates the logic for a
|
4
|
+
"full" (custom selector/constraint) rules. Each class in this module implements
|
5
|
+
the `Rule` protocol from `definitions.py` and encapsulates the logic for a
|
6
|
+
specific type of validation check. The `RuleFactory` uses these classes to
|
7
|
+
instantiate the correct handler for each rule defined in a JSON file.
|
8
8
|
"""
|
9
9
|
|
10
10
|
import ast
|
@@ -77,10 +77,10 @@ class CheckLinterRule(Rule):
|
|
77
77
|
True if no PEP8 violations are found, False otherwise.
|
78
78
|
"""
|
79
79
|
if not source_code:
|
80
|
-
self._console.print("Source code is empty, skipping PEP8 check.", level=
|
80
|
+
self._console.print("Source code is empty, skipping PEP8 check.", level=LogLevel.WARNING)
|
81
81
|
return True
|
82
82
|
|
83
|
-
self._console.print(f"Rule {self.config.rule_id}: Running flake8 linter...", level=
|
83
|
+
self._console.print(f"Rule {self.config.rule_id}: Running flake8 linter...", level=LogLevel.DEBUG)
|
84
84
|
|
85
85
|
params = self.config.params
|
86
86
|
args = [sys.executable, "-m", "flake8", "-"]
|
@@ -102,19 +102,21 @@ class CheckLinterRule(Rule):
|
|
102
102
|
|
103
103
|
if process.returncode != 0 and process.stdout:
|
104
104
|
linter_output = process.stdout.strip()
|
105
|
-
self._console.print(f"Flake8 found issues:\n{linter_output}", level=
|
105
|
+
self._console.print(f"Flake8 found issues:\n{linter_output}", level=LogLevel.DEBUG)
|
106
106
|
return False
|
107
107
|
elif process.returncode != 0:
|
108
|
-
self._console.print(
|
108
|
+
self._console.print(
|
109
|
+
f"Flake8 exited with code {process.returncode}:\n{process.stderr}", level=LogLevel.ERROR
|
110
|
+
)
|
109
111
|
return False
|
110
112
|
|
111
|
-
self._console.print("PEP8 check passed.", level=
|
113
|
+
self._console.print("PEP8 check passed.", level=LogLevel.ERROR)
|
112
114
|
return True
|
113
115
|
except FileNotFoundError:
|
114
|
-
self._console.print("flake8 not found. Is it installed in the venv?", level=
|
116
|
+
self._console.print("flake8 not found. Is it installed in the venv?", level=LogLevel.CRITICAL)
|
115
117
|
return False
|
116
118
|
except Exception as e:
|
117
|
-
self._console.print(f"An unexpected error occurred while running flake8: {e}", level=
|
119
|
+
self._console.print(f"An unexpected error occurred while running flake8: {e}", level=LogLevel.CRITICAL)
|
118
120
|
return False
|
119
121
|
|
120
122
|
|
@@ -157,11 +159,11 @@ class FullRuleHandler(Rule):
|
|
157
159
|
The boolean result of applying the constraint to the selected nodes.
|
158
160
|
"""
|
159
161
|
if not tree:
|
160
|
-
self._console.print("AST not available, skipping rule.", level=
|
162
|
+
self._console.print("AST not available, skipping rule.", level=LogLevel.WARNING)
|
161
163
|
return True
|
162
164
|
|
163
|
-
self._console.print(f"Applying selector: {self._selector.__class__.__name__}", level=
|
165
|
+
self._console.print(f"Applying selector: {self._selector.__class__.__name__}", level=LogLevel.DEBUG)
|
164
166
|
selected_nodes = self._selector.select(tree)
|
165
167
|
|
166
|
-
self._console.print(f"Applying constraint: {self._constraint.__class__.__name__}", level=
|
168
|
+
self._console.print(f"Applying constraint: {self._constraint.__class__.__name__}", level=LogLevel.DEBUG)
|
167
169
|
return self._constraint.check(selected_nodes)
|
@@ -2,7 +2,11 @@
|
|
2
2
|
|
3
3
|
Each class in this module implements the `Constraint` protocol and encapsulates
|
4
4
|
the logic for a specific condition that can be checked against a list of
|
5
|
-
AST nodes
|
5
|
+
AST nodes. These classes are instantiated by the `ConstraintFactory` based on
|
6
|
+
the "constraint" block in a JSON rule.
|
7
|
+
|
8
|
+
The module also includes helper functions for processing AST nodes, which are
|
9
|
+
used internally by the constraint classes.
|
6
10
|
"""
|
7
11
|
|
8
12
|
import ast
|
@@ -66,6 +70,12 @@ class MustInheritFromConstraint(Constraint):
|
|
66
70
|
"""
|
67
71
|
|
68
72
|
def __init__(self, **kwargs: Any):
|
73
|
+
"""Initializes the constraint.
|
74
|
+
|
75
|
+
Args:
|
76
|
+
**kwargs: Keyword arguments from the JSON rule's constraint config.
|
77
|
+
Expects `parent_name` (str) specifying the required parent class.
|
78
|
+
"""
|
69
79
|
self.parent_name_to_find: str | None = kwargs.get("parent_name")
|
70
80
|
|
71
81
|
def check(self, nodes: list[ast.AST]) -> bool:
|
@@ -105,6 +115,12 @@ class MustBeTypeConstraint(Constraint):
|
|
105
115
|
"""
|
106
116
|
|
107
117
|
def __init__(self, **kwargs: Any):
|
118
|
+
"""Initializes the constraint.
|
119
|
+
|
120
|
+
Args:
|
121
|
+
**kwargs: Keyword arguments from the JSON rule's constraint config.
|
122
|
+
Expects `expected_type` (str) with the name of the required type.
|
123
|
+
"""
|
108
124
|
self.expected_type_str: str | None = kwargs.get("expected_type")
|
109
125
|
self.type_map = {
|
110
126
|
"str": str,
|
@@ -164,6 +180,12 @@ class NameMustBeInConstraint(Constraint):
|
|
164
180
|
"""
|
165
181
|
|
166
182
|
def __init__(self, **kwargs: Any):
|
183
|
+
"""Initializes the constraint.
|
184
|
+
|
185
|
+
Args:
|
186
|
+
**kwargs: Keyword arguments from the JSON rule's constraint config.
|
187
|
+
Expects `allowed_names` (list[str]) containing the valid names.
|
188
|
+
"""
|
167
189
|
self.allowed_names = set(kwargs.get("allowed_names", []))
|
168
190
|
|
169
191
|
@staticmethod
|
@@ -194,6 +216,12 @@ class ValueMustBeInConstraint(Constraint):
|
|
194
216
|
"""
|
195
217
|
|
196
218
|
def __init__(self, **kwargs: Any):
|
219
|
+
"""Initializes the constraint.
|
220
|
+
|
221
|
+
Args:
|
222
|
+
**kwargs: Keyword arguments from the JSON rule's constraint config.
|
223
|
+
Expects `allowed_values` (list) containing the valid literal values.
|
224
|
+
"""
|
197
225
|
self.allowed_values = set(kwargs.get("allowed_values", []))
|
198
226
|
|
199
227
|
def check(self, nodes: list[ast.AST]) -> bool:
|
@@ -224,6 +252,13 @@ class MustHaveArgsConstraint(Constraint):
|
|
224
252
|
"""
|
225
253
|
|
226
254
|
def __init__(self, **kwargs: Any):
|
255
|
+
"""Initializes the constraint.
|
256
|
+
|
257
|
+
Args:
|
258
|
+
**kwargs: Keyword arguments from the JSON rule's constraint config.
|
259
|
+
Can accept `count` (int), `names` (list[str]), and
|
260
|
+
`exact_match` (bool).
|
261
|
+
"""
|
227
262
|
self.expected_count: int | None = kwargs.get("count")
|
228
263
|
self.expected_names: list[str] | None = kwargs.get("names")
|
229
264
|
self.exact_match: bool = kwargs.get("exact_match", True)
|
@@ -3,7 +3,8 @@
|
|
3
3
|
Each class in this module implements the `Selector` protocol and is responsible
|
4
4
|
for finding and returning specific types of nodes from an Abstract Syntax Tree.
|
5
5
|
They use `ast.walk` to traverse the tree and can be constrained to specific
|
6
|
-
scopes
|
6
|
+
scopes via the `ScopedSelector` base class, which uses the `scope_handler`.
|
7
|
+
These classes are instantiated by the `SelectorFactory`.
|
7
8
|
"""
|
8
9
|
|
9
10
|
import ast
|
@@ -27,11 +28,11 @@ class ScopedSelector(Selector):
|
|
27
28
|
"""
|
28
29
|
|
29
30
|
def __init__(self, **kwargs: Any):
|
30
|
-
"""Initializes the ScopedSelector.
|
31
|
+
"""Initializes the ScopedSelector base class.
|
31
32
|
|
32
33
|
Args:
|
33
|
-
**kwargs: Keyword arguments
|
34
|
-
|
34
|
+
**kwargs: Keyword arguments passed from a subclass constructor.
|
35
|
+
It extracts the `in_scope` configuration.
|
35
36
|
"""
|
36
37
|
self.in_scope_config = kwargs.get("in_scope")
|
37
38
|
|
@@ -67,6 +68,12 @@ class FunctionDefSelector(ScopedSelector):
|
|
67
68
|
"""
|
68
69
|
|
69
70
|
def __init__(self, **kwargs: Any):
|
71
|
+
"""Initializes the FunctionDefSelector.
|
72
|
+
|
73
|
+
Args:
|
74
|
+
**kwargs: Keyword arguments from the JSON rule's selector config.
|
75
|
+
Expects `name` (str) and optionally `in_scope` (dict).
|
76
|
+
"""
|
70
77
|
super().__init__(**kwargs)
|
71
78
|
self.name_to_find = kwargs.get("name")
|
72
79
|
|
@@ -118,6 +125,12 @@ class ImportStatementSelector(ScopedSelector):
|
|
118
125
|
"""
|
119
126
|
|
120
127
|
def __init__(self, **kwargs: Any):
|
128
|
+
"""Initializes the ImportStatementSelector.
|
129
|
+
|
130
|
+
Args:
|
131
|
+
**kwargs: Keyword arguments from the JSON rule's selector config.
|
132
|
+
Expects `name` (str) for the module name and optionally `in_scope`.
|
133
|
+
"""
|
121
134
|
super().__init__(**kwargs)
|
122
135
|
self.module_name_to_find = kwargs.get("name")
|
123
136
|
|
@@ -188,6 +201,12 @@ class AssignmentSelector(ScopedSelector):
|
|
188
201
|
"""
|
189
202
|
|
190
203
|
def __init__(self, **kwargs: Any):
|
204
|
+
"""Initializes the AssignmentSelector.
|
205
|
+
|
206
|
+
Args:
|
207
|
+
**kwargs: Keyword arguments from the JSON rule's selector config.
|
208
|
+
Expects `name` (str) for the assignment target and optionally `in_scope`.
|
209
|
+
"""
|
191
210
|
super().__init__(**kwargs)
|
192
211
|
self.target_name_to_find = kwargs.get("name")
|
193
212
|
|
@@ -221,6 +240,13 @@ class UsageSelector(ScopedSelector):
|
|
221
240
|
"""
|
222
241
|
|
223
242
|
def __init__(self, **kwargs: Any):
|
243
|
+
"""Initializes the UsageSelector.
|
244
|
+
|
245
|
+
Args:
|
246
|
+
**kwargs: Keyword arguments from the JSON rule's selector config.
|
247
|
+
Expects `name` (str) for the variable/attribute being used and
|
248
|
+
optionally `in_scope`.
|
249
|
+
"""
|
224
250
|
super().__init__(**kwargs)
|
225
251
|
self.variable_name_to_find = kwargs.get("name")
|
226
252
|
|
@@ -248,10 +274,30 @@ class LiteralSelector(ScopedSelector):
|
|
248
274
|
"""
|
249
275
|
|
250
276
|
def __init__(self, **kwargs: Any):
|
277
|
+
"""Initializes the LiteralSelector.
|
278
|
+
|
279
|
+
Args:
|
280
|
+
**kwargs: Keyword arguments from the JSON rule's selector config.
|
281
|
+
Expects `name` (str) to be "string" or "number" and optionally
|
282
|
+
`in_scope`.
|
283
|
+
"""
|
251
284
|
super().__init__(**kwargs)
|
252
|
-
self.literal_type = kwargs.get("name")
|
285
|
+
self.literal_type = kwargs.get("name")
|
253
286
|
|
254
287
|
def select(self, tree: ast.Module) -> list[ast.AST]:
|
288
|
+
"""Finds all ast.Constant nodes that match the type criteria.
|
289
|
+
|
290
|
+
This method traverses the given AST (or a sub-tree defined by `in_scope`)
|
291
|
+
and collects all number or string literals. It contains special logic
|
292
|
+
to intelligently ignore nodes that are likely to be docstrings or parts
|
293
|
+
of f-strings to avoid false positives.
|
294
|
+
|
295
|
+
Args:
|
296
|
+
tree: The root of the AST (the module object) to be searched.
|
297
|
+
|
298
|
+
Returns:
|
299
|
+
A list of `ast.Constant` nodes matching the criteria.
|
300
|
+
"""
|
255
301
|
search_tree = self._get_search_tree(tree)
|
256
302
|
if not search_tree:
|
257
303
|
return []
|
@@ -295,6 +341,12 @@ class AstNodeSelector(ScopedSelector):
|
|
295
341
|
"""
|
296
342
|
|
297
343
|
def __init__(self, **kwargs: Any):
|
344
|
+
"""Initializes the AstNodeSelector.
|
345
|
+
|
346
|
+
Args:
|
347
|
+
**kwargs: Keyword arguments from the JSON rule's selector config.
|
348
|
+
Expects `node_type` (str or list[str]) and optionally `in_scope`.
|
349
|
+
"""
|
298
350
|
super().__init__(**kwargs)
|
299
351
|
node_type_arg = kwargs.get("node_type")
|
300
352
|
|
@@ -1,7 +1,7 @@
|
|
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.1.3
|
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
6
|
License: MIT License
|
7
7
|
|
@@ -28,22 +28,27 @@ License: MIT License
|
|
28
28
|
Project-URL: Homepage, https://github.com/Qu1nel/PythonCodeValidator
|
29
29
|
Project-URL: Documentation, https://pythoncodevalidator.readthedocs.io/en/latest/
|
30
30
|
Project-URL: Bug Tracker, https://github.com/Qu1nel/PythonCodeValidator/issues
|
31
|
-
Keywords: validation,linter,static analysis,testing,education
|
32
|
-
Classifier: Development Status ::
|
31
|
+
Keywords: validation,linter,static analysis,testing,education,ast
|
32
|
+
Classifier: Development Status :: 4 - Beta
|
33
|
+
Classifier: Intended Audience :: Developers
|
34
|
+
Classifier: Intended Audience :: Education
|
33
35
|
Classifier: Programming Language :: Python :: 3
|
34
36
|
Classifier: Programming Language :: Python :: 3.11
|
35
37
|
Classifier: Programming Language :: Python :: 3.12
|
36
38
|
Classifier: License :: OSI Approved :: MIT License
|
37
39
|
Classifier: Operating System :: OS Independent
|
40
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
41
|
+
Classifier: Topic :: Software Development :: Testing
|
42
|
+
Classifier: Topic :: Education
|
38
43
|
Requires-Python: >=3.11
|
39
44
|
Description-Content-Type: text/markdown
|
40
45
|
License-File: LICENSE
|
46
|
+
Requires-Dist: flake8>=7.0.0
|
41
47
|
Provides-Extra: dev
|
42
48
|
Requires-Dist: ruff>=0.4.0; extra == "dev"
|
43
|
-
Requires-Dist: flake8>=7.0.0; extra == "dev"
|
44
49
|
Requires-Dist: build; extra == "dev"
|
45
50
|
Requires-Dist: twine; extra == "dev"
|
46
|
-
Requires-Dist: coverage>=7.5.0; extra == "dev"
|
51
|
+
Requires-Dist: coverage[toml]>=7.5.0; extra == "dev"
|
47
52
|
Provides-Extra: docs
|
48
53
|
Requires-Dist: sphinx>=7.0.0; extra == "docs"
|
49
54
|
Requires-Dist: furo; extra == "docs"
|
@@ -0,0 +1,22 @@
|
|
1
|
+
code_validator/__init__.py,sha256=3eCuRsidy-Tc4QASdz7mwRIiGYOumb9uPxY3ob85HBs,1721
|
2
|
+
code_validator/__main__.py,sha256=Z41EoJqX03AI11gnku_Iwt6rP8SPUkYuxwN7P51qgLc,600
|
3
|
+
code_validator/cli.py,sha256=PtPxlgKdbAAEKDpI1Xk7bZ5CKVYr9efIlOgh45Igjxk,4119
|
4
|
+
code_validator/config.py,sha256=qLuGsFPn61RS97clV8zHd43jb9EBIBwBzl1jrSKLD2w,5052
|
5
|
+
code_validator/core.py,sha256=n4aP_ohE_9qoX9lqbmPSvMZouxomWxauFiwgjzA1_ys,8003
|
6
|
+
code_validator/exceptions.py,sha256=XkiRNQ25FWJkjS2wBaUaKQcEL5WF9tN_HSV3tqJwDcE,1627
|
7
|
+
code_validator/output.py,sha256=VRJLGwm6X9i8SnovosNrHu96ueZXz9GIvUQXy4xDtHw,3304
|
8
|
+
code_validator/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
|
+
code_validator/components/ast_utils.py,sha256=v0N3F58oxNMg7bUY6-8x0AeoLsxrzrp_bLTGNVr5EMU,1589
|
10
|
+
code_validator/components/definitions.py,sha256=9WEXQ_i-S4Vega6HqOdjreZE_1cgJPC7guZodTKtzhc,3208
|
11
|
+
code_validator/components/factories.py,sha256=0-U--7pekt-fIvqb_WesWh_ehKCvJ7ROObx5GU-yjGc,10492
|
12
|
+
code_validator/components/scope_handler.py,sha256=q4cFK_YzoYvAqPkw9wPPxgPe-aO0kgy6gk0ETWduj-U,2593
|
13
|
+
code_validator/rules_library/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
14
|
+
code_validator/rules_library/basic_rules.py,sha256=VYm6t_En0t6D42vp6vtCr5XV6-M4xuPupX8Hz6-eveI,6858
|
15
|
+
code_validator/rules_library/constraint_logic.py,sha256=CRnzMgLR17Ja2d4M3jWBO-xLLPB6Vpk7iusGMoyJ17s,10932
|
16
|
+
code_validator/rules_library/selector_nodes.py,sha256=hWzvar1tvnkEFReCFLSw5oTK9HETNmdNVcDD3_57Iyg,14306
|
17
|
+
python_code_validator-0.1.3.dist-info/licenses/LICENSE,sha256=Lq69RwIO4Dge7OsjgAamJfYSDq2DWI2yzVYI1VX1s6c,1089
|
18
|
+
python_code_validator-0.1.3.dist-info/METADATA,sha256=NlDRdvgugKe3hWC-Z8q1h7_lrqZBJ4Jpd6YbjD3nuVU,12081
|
19
|
+
python_code_validator-0.1.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
20
|
+
python_code_validator-0.1.3.dist-info/entry_points.txt,sha256=pw_HijiZyPxokVJHStTkGCwheTjukDomdk81JyHzv74,66
|
21
|
+
python_code_validator-0.1.3.dist-info/top_level.txt,sha256=yowMDfABI5oqgW3hhTdec_7UHGeprkvc2BnqRzNbI5w,15
|
22
|
+
python_code_validator-0.1.3.dist-info/RECORD,,
|
@@ -1,22 +0,0 @@
|
|
1
|
-
code_validator/__init__.py,sha256=etZcfEkc_LFR8gqYPeH6hitwZ5-HS2WKSjl6LW9M8PY,961
|
2
|
-
code_validator/__main__.py,sha256=c7P8Lz3EuwYRHarTEp_DExMUauod9X42p6aTZkpvi10,330
|
3
|
-
code_validator/cli.py,sha256=fLbSP_4ABZGZTH6-L4oAXKVRTW-OVMdpcZgT9F4ibtY,3466
|
4
|
-
code_validator/config.py,sha256=kTqD8-SFbtUQ9oif-TylqNFPadTq5JSGBv84cI0R8dY,2657
|
5
|
-
code_validator/core.py,sha256=4sPdlunmODfNpDrP9QH2jIrGFVLFlo_r8MAoB4_63vo,4560
|
6
|
-
code_validator/exceptions.py,sha256=XkiRNQ25FWJkjS2wBaUaKQcEL5WF9tN_HSV3tqJwDcE,1627
|
7
|
-
code_validator/output.py,sha256=VRJLGwm6X9i8SnovosNrHu96ueZXz9GIvUQXy4xDtHw,3304
|
8
|
-
code_validator/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
|
-
code_validator/components/ast_utils.py,sha256=7DzyKmLekj_qtuZcS8BrcWXqQXVEPPQ_rwh3BmpIk9o,1412
|
10
|
-
code_validator/components/definitions.py,sha256=cFbKL7UZYHqWjz2gJCmZ6fU_ZdQ5L_h1tlQBYkxSO7Q,3126
|
11
|
-
code_validator/components/factories.py,sha256=qrQotS7lyqIGhoNGRvSNQKy6p9YJKQC7YaPi4eCtpww,10436
|
12
|
-
code_validator/components/scope_handler.py,sha256=vb4pCE-4DyxGkRYGeW2JWxP2IiZH3uRRfReo0IBgM5Y,2459
|
13
|
-
code_validator/rules_library/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
14
|
-
code_validator/rules_library/basic_rules.py,sha256=0K45Ed8yjy-pXDZpI1Xvgj94zvaFz-PucFRz__X4uRI,6652
|
15
|
-
code_validator/rules_library/constraint_logic.py,sha256=X95g0673hoZey3LGCBH6AscbIuHYq_aY9WRKIkJhOnk,9525
|
16
|
-
code_validator/rules_library/selector_nodes.py,sha256=JxhUBXS95Dad_60Ut-8XkW2MFM-7bkeRb_yk7vXlNPE,12200
|
17
|
-
python_code_validator-0.1.2.dist-info/licenses/LICENSE,sha256=Lq69RwIO4Dge7OsjgAamJfYSDq2DWI2yzVYI1VX1s6c,1089
|
18
|
-
python_code_validator-0.1.2.dist-info/METADATA,sha256=lYCstaPpPsj2KDOze-aYZFkr_NDi3Cy8UAgUCJHB2Mo,11829
|
19
|
-
python_code_validator-0.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
20
|
-
python_code_validator-0.1.2.dist-info/entry_points.txt,sha256=pw_HijiZyPxokVJHStTkGCwheTjukDomdk81JyHzv74,66
|
21
|
-
python_code_validator-0.1.2.dist-info/top_level.txt,sha256=yowMDfABI5oqgW3hhTdec_7UHGeprkvc2BnqRzNbI5w,15
|
22
|
-
python_code_validator-0.1.2.dist-info/RECORD,,
|
File without changes
|
{python_code_validator-0.1.2.dist-info → python_code_validator-0.1.3.dist-info}/entry_points.txt
RENAMED
File without changes
|
{python_code_validator-0.1.2.dist-info → python_code_validator-0.1.3.dist-info}/licenses/LICENSE
RENAMED
File without changes
|
{python_code_validator-0.1.2.dist-info → python_code_validator-0.1.3.dist-info}/top_level.txt
RENAMED
File without changes
|