python-code-validator 0.1.2__py3-none-any.whl → 0.2.1__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 +48 -24
- code_validator/components/ast_utils.py +6 -2
- code_validator/components/definitions.py +6 -5
- code_validator/components/factories.py +35 -16
- code_validator/components/scope_handler.py +5 -3
- code_validator/config.py +60 -7
- code_validator/core.py +161 -38
- code_validator/output.py +160 -14
- code_validator/rules_library/basic_rules.py +23 -16
- code_validator/rules_library/constraint_logic.py +301 -257
- code_validator/rules_library/selector_nodes.py +66 -5
- {python_code_validator-0.1.2.dist-info → python_code_validator-0.2.1.dist-info}/METADATA +13 -30
- python_code_validator-0.2.1.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.2.1.dist-info}/WHEEL +0 -0
- {python_code_validator-0.1.2.dist-info → python_code_validator-0.2.1.dist-info}/entry_points.txt +0 -0
- {python_code_validator-0.1.2.dist-info → python_code_validator-0.2.1.dist-info}/licenses/LICENSE +0 -0
- {python_code_validator-0.1.2.dist-info → python_code_validator-0.2.1.dist-info}/top_level.txt +0 -0
@@ -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
|
@@ -12,6 +13,7 @@ from typing import Any
|
|
12
13
|
from ..components.ast_utils import get_full_name
|
13
14
|
from ..components.definitions import Selector
|
14
15
|
from ..components.scope_handler import find_scope_node
|
16
|
+
from ..output import LogLevel, log_initialization
|
15
17
|
|
16
18
|
|
17
19
|
class ScopedSelector(Selector):
|
@@ -27,11 +29,11 @@ class ScopedSelector(Selector):
|
|
27
29
|
"""
|
28
30
|
|
29
31
|
def __init__(self, **kwargs: Any):
|
30
|
-
"""Initializes the ScopedSelector.
|
32
|
+
"""Initializes the ScopedSelector base class.
|
31
33
|
|
32
34
|
Args:
|
33
|
-
**kwargs: Keyword arguments
|
34
|
-
|
35
|
+
**kwargs: Keyword arguments passed from a subclass constructor.
|
36
|
+
It extracts the `in_scope` configuration.
|
35
37
|
"""
|
36
38
|
self.in_scope_config = kwargs.get("in_scope")
|
37
39
|
|
@@ -66,7 +68,14 @@ class FunctionDefSelector(ScopedSelector):
|
|
66
68
|
name (str): The name of the function to find. Use "*" to find all.
|
67
69
|
"""
|
68
70
|
|
71
|
+
@log_initialization(level=LogLevel.TRACE)
|
69
72
|
def __init__(self, **kwargs: Any):
|
73
|
+
"""Initializes the FunctionDefSelector.
|
74
|
+
|
75
|
+
Args:
|
76
|
+
**kwargs: Keyword arguments from the JSON rule's selector config.
|
77
|
+
Expects `name` (str) and optionally `in_scope` (dict).
|
78
|
+
"""
|
70
79
|
super().__init__(**kwargs)
|
71
80
|
self.name_to_find = kwargs.get("name")
|
72
81
|
|
@@ -91,6 +100,7 @@ class ClassDefSelector(ScopedSelector):
|
|
91
100
|
name (str): The name of the class to find. Use "*" to find all.
|
92
101
|
"""
|
93
102
|
|
103
|
+
@log_initialization(level=LogLevel.TRACE)
|
94
104
|
def __init__(self, **kwargs: Any):
|
95
105
|
"""Initializes the selector."""
|
96
106
|
super().__init__(**kwargs)
|
@@ -117,7 +127,14 @@ class ImportStatementSelector(ScopedSelector):
|
|
117
127
|
name (str): The name of the module to find (e.g., "os", "requests").
|
118
128
|
"""
|
119
129
|
|
130
|
+
@log_initialization(level=LogLevel.TRACE)
|
120
131
|
def __init__(self, **kwargs: Any):
|
132
|
+
"""Initializes the ImportStatementSelector.
|
133
|
+
|
134
|
+
Args:
|
135
|
+
**kwargs: Keyword arguments from the JSON rule's selector config.
|
136
|
+
Expects `name` (str) for the module name and optionally `in_scope`.
|
137
|
+
"""
|
121
138
|
super().__init__(**kwargs)
|
122
139
|
self.module_name_to_find = kwargs.get("name")
|
123
140
|
|
@@ -156,6 +173,7 @@ class FunctionCallSelector(ScopedSelector):
|
|
156
173
|
name (str): The full name of the function being called.
|
157
174
|
"""
|
158
175
|
|
176
|
+
@log_initialization(level=LogLevel.TRACE)
|
159
177
|
def __init__(self, **kwargs: Any):
|
160
178
|
"""Initializes the selector."""
|
161
179
|
super().__init__(**kwargs)
|
@@ -187,7 +205,14 @@ class AssignmentSelector(ScopedSelector):
|
|
187
205
|
name (str): The full name of the variable or attribute being assigned to.
|
188
206
|
"""
|
189
207
|
|
208
|
+
@log_initialization(level=LogLevel.TRACE)
|
190
209
|
def __init__(self, **kwargs: Any):
|
210
|
+
"""Initializes the AssignmentSelector.
|
211
|
+
|
212
|
+
Args:
|
213
|
+
**kwargs: Keyword arguments from the JSON rule's selector config.
|
214
|
+
Expects `name` (str) for the assignment target and optionally `in_scope`.
|
215
|
+
"""
|
191
216
|
super().__init__(**kwargs)
|
192
217
|
self.target_name_to_find = kwargs.get("name")
|
193
218
|
|
@@ -220,7 +245,15 @@ class UsageSelector(ScopedSelector):
|
|
220
245
|
name (str): The name of the variable or attribute being used.
|
221
246
|
"""
|
222
247
|
|
248
|
+
@log_initialization(level=LogLevel.TRACE)
|
223
249
|
def __init__(self, **kwargs: Any):
|
250
|
+
"""Initializes the UsageSelector.
|
251
|
+
|
252
|
+
Args:
|
253
|
+
**kwargs: Keyword arguments from the JSON rule's selector config.
|
254
|
+
Expects `name` (str) for the variable/attribute being used and
|
255
|
+
optionally `in_scope`.
|
256
|
+
"""
|
224
257
|
super().__init__(**kwargs)
|
225
258
|
self.variable_name_to_find = kwargs.get("name")
|
226
259
|
|
@@ -247,11 +280,32 @@ class LiteralSelector(ScopedSelector):
|
|
247
280
|
name (str): The type of literal to find. Supported: "number", "string".
|
248
281
|
"""
|
249
282
|
|
283
|
+
@log_initialization(level=LogLevel.TRACE)
|
250
284
|
def __init__(self, **kwargs: Any):
|
285
|
+
"""Initializes the LiteralSelector.
|
286
|
+
|
287
|
+
Args:
|
288
|
+
**kwargs: Keyword arguments from the JSON rule's selector config.
|
289
|
+
Expects `name` (str) to be "string" or "number" and optionally
|
290
|
+
`in_scope`.
|
291
|
+
"""
|
251
292
|
super().__init__(**kwargs)
|
252
|
-
self.literal_type = kwargs.get("name")
|
293
|
+
self.literal_type = kwargs.get("name")
|
253
294
|
|
254
295
|
def select(self, tree: ast.Module) -> list[ast.AST]:
|
296
|
+
"""Finds all ast.Constant nodes that match the type criteria.
|
297
|
+
|
298
|
+
This method traverses the given AST (or a sub-tree defined by `in_scope`)
|
299
|
+
and collects all number or string literals. It contains special logic
|
300
|
+
to intelligently ignore nodes that are likely to be docstrings or parts
|
301
|
+
of f-strings to avoid false positives.
|
302
|
+
|
303
|
+
Args:
|
304
|
+
tree: The root of the AST (the module object) to be searched.
|
305
|
+
|
306
|
+
Returns:
|
307
|
+
A list of `ast.Constant` nodes matching the criteria.
|
308
|
+
"""
|
255
309
|
search_tree = self._get_search_tree(tree)
|
256
310
|
if not search_tree:
|
257
311
|
return []
|
@@ -294,7 +348,14 @@ class AstNodeSelector(ScopedSelector):
|
|
294
348
|
as defined in the `ast` module (e.g., "For", "While", "Try").
|
295
349
|
"""
|
296
350
|
|
351
|
+
@log_initialization(level=LogLevel.TRACE)
|
297
352
|
def __init__(self, **kwargs: Any):
|
353
|
+
"""Initializes the AstNodeSelector.
|
354
|
+
|
355
|
+
Args:
|
356
|
+
**kwargs: Keyword arguments from the JSON rule's selector config.
|
357
|
+
Expects `node_type` (str or list[str]) and optionally `in_scope`.
|
358
|
+
"""
|
298
359
|
super().__init__(**kwargs)
|
299
360
|
node_type_arg = kwargs.get("node_type")
|
300
361
|
|
@@ -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
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
code_validator/__init__.py,sha256=uag-AMj0a1I953aW7JLcxS6q0f_rueJWvO3AmGOWgUI,1721
|
2
|
+
code_validator/__main__.py,sha256=Z41EoJqX03AI11gnku_Iwt6rP8SPUkYuxwN7P51qgLc,600
|
3
|
+
code_validator/cli.py,sha256=-8pl9iv1ufRWiRKrY0vtk3ncZu6tFj6ZrKa0F-ruuZ4,4826
|
4
|
+
code_validator/config.py,sha256=ELi19GC1YeLEZZbm49FO8a1URLBrZxAuOGkrHTK8FvE,5071
|
5
|
+
code_validator/core.py,sha256=HyXXxECxsAgiXOl9tMZVqg0Xptx7nA-64IDVv_W4Svo,10117
|
6
|
+
code_validator/exceptions.py,sha256=XkiRNQ25FWJkjS2wBaUaKQcEL5WF9tN_HSV3tqJwDcE,1627
|
7
|
+
code_validator/output.py,sha256=Saydp1D6JRIzHroK12K38lnoMZCJaIEDyPD1ZXgJFGA,9137
|
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=XvsSXbTHHmPrOvAseSYc9Xl8z-Zu5K5Eb8aWAvIgkuQ,11327
|
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=RGlHEDnd0Cn72ofbE8Y7wAl9h7iZR8reR80v2oiICTo,7118
|
15
|
+
code_validator/rules_library/constraint_logic.py,sha256=-gN6GaIZvPfHqiLCzH5Jwuz6DcPaQKlepbMo2r5V2Yc,11026
|
16
|
+
code_validator/rules_library/selector_nodes.py,sha256=wCAnQRSdEB8HM8avTy1wkJzyb6v2EQgHIPASXruiqbc,14733
|
17
|
+
python_code_validator-0.2.1.dist-info/licenses/LICENSE,sha256=Lq69RwIO4Dge7OsjgAamJfYSDq2DWI2yzVYI1VX1s6c,1089
|
18
|
+
python_code_validator-0.2.1.dist-info/METADATA,sha256=EKuyRnPMen6M3C5YgSWHXmI1BjOyTP_-ttXgIfZDGKc,10794
|
19
|
+
python_code_validator-0.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
20
|
+
python_code_validator-0.2.1.dist-info/entry_points.txt,sha256=pw_HijiZyPxokVJHStTkGCwheTjukDomdk81JyHzv74,66
|
21
|
+
python_code_validator-0.2.1.dist-info/top_level.txt,sha256=yowMDfABI5oqgW3hhTdec_7UHGeprkvc2BnqRzNbI5w,15
|
22
|
+
python_code_validator-0.2.1.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.2.1.dist-info}/entry_points.txt
RENAMED
File without changes
|
{python_code_validator-0.1.2.dist-info → python_code_validator-0.2.1.dist-info}/licenses/LICENSE
RENAMED
File without changes
|
{python_code_validator-0.1.2.dist-info → python_code_validator-0.2.1.dist-info}/top_level.txt
RENAMED
File without changes
|