gruff-py 0.1.0__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.
- gruff_py-0.1.0.dist-info/METADATA +279 -0
- gruff_py-0.1.0.dist-info/RECORD +216 -0
- gruff_py-0.1.0.dist-info/WHEEL +4 -0
- gruff_py-0.1.0.dist-info/entry_points.txt +2 -0
- gruff_py-0.1.0.dist-info/licenses/LICENSE.md +21 -0
- gruffpy/__init__.py +5 -0
- gruffpy/__main__.py +9 -0
- gruffpy/analysis/__init__.py +15 -0
- gruffpy/analysis/report.py +163 -0
- gruffpy/analysis/run_diagnostic.py +37 -0
- gruffpy/analysis/runner.py +286 -0
- gruffpy/analysis/schema.py +5 -0
- gruffpy/cli.py +741 -0
- gruffpy/cli_menu.py +131 -0
- gruffpy/cli_options.py +769 -0
- gruffpy/cli_state.py +54 -0
- gruffpy/command/__init__.py +4 -0
- gruffpy/command/dashboard_page_renderer.py +229 -0
- gruffpy/command/dashboard_server.py +282 -0
- gruffpy/command/metric_calibration.py +723 -0
- gruffpy/command/rule_docs.py +322 -0
- gruffpy/config/__init__.py +13 -0
- gruffpy/config/analysis_config.py +182 -0
- gruffpy/config/dead_code_allowlist.py +66 -0
- gruffpy/config/exceptions.py +5 -0
- gruffpy/config/loader.py +382 -0
- gruffpy/config/rule_selection.py +69 -0
- gruffpy/config/rule_settings.py +180 -0
- gruffpy/config/yaml_loader.py +41 -0
- gruffpy/finding/__init__.py +19 -0
- gruffpy/finding/confidence.py +9 -0
- gruffpy/finding/fail_threshold.py +50 -0
- gruffpy/finding/finding.py +98 -0
- gruffpy/finding/fingerprint.py +48 -0
- gruffpy/finding/output_format.py +31 -0
- gruffpy/finding/pillar.py +20 -0
- gruffpy/finding/rule_tier.py +7 -0
- gruffpy/finding/severity.py +27 -0
- gruffpy/parser/__init__.py +4 -0
- gruffpy/parser/analysis_unit.py +49 -0
- gruffpy/parser/python_parser.py +61 -0
- gruffpy/py.typed +0 -0
- gruffpy/reporting/__init__.py +19 -0
- gruffpy/reporting/finding_display_filter.py +103 -0
- gruffpy/reporting/github_annotations_reporter.py +55 -0
- gruffpy/reporting/hotspot_reporter.py +36 -0
- gruffpy/reporting/html_reporter.py +442 -0
- gruffpy/reporting/json_reporter.py +41 -0
- gruffpy/reporting/markdown_reporter.py +108 -0
- gruffpy/reporting/sarif_reporter.py +184 -0
- gruffpy/reporting/text_reporter.py +115 -0
- gruffpy/rule/__init__.py +6 -0
- gruffpy/rule/_python_dynamism.py +309 -0
- gruffpy/rule/builtins.py +11 -0
- gruffpy/rule/catalog.py +574 -0
- gruffpy/rule/complexity/__init__.py +0 -0
- gruffpy/rule/complexity/_halstead.py +213 -0
- gruffpy/rule/complexity/_walks.py +88 -0
- gruffpy/rule/complexity/cognitive_complexity_rule.py +310 -0
- gruffpy/rule/complexity/cyclomatic_complexity_rule.py +159 -0
- gruffpy/rule/complexity/halstead_volume_rule.py +106 -0
- gruffpy/rule/complexity/maintainability_index_rule.py +133 -0
- gruffpy/rule/complexity/nesting_depth_rule.py +191 -0
- gruffpy/rule/complexity/npath_complexity_rule.py +248 -0
- gruffpy/rule/context.py +34 -0
- gruffpy/rule/dead_code/__init__.py +0 -0
- gruffpy/rule/dead_code/unused_private_attribute_rule.py +209 -0
- gruffpy/rule/dead_code/unused_private_function_rule.py +300 -0
- gruffpy/rule/definition.py +70 -0
- gruffpy/rule/design/__init__.py +3 -0
- gruffpy/rule/design/single_implementor_protocol_rule.py +454 -0
- gruffpy/rule/docs/__init__.py +0 -0
- gruffpy/rule/docs/_comment_scanner.py +50 -0
- gruffpy/rule/docs/_docstring_parser.py +138 -0
- gruffpy/rule/docs/_helpers.py +182 -0
- gruffpy/rule/docs/complex_branch_rationale_rule.py +227 -0
- gruffpy/rule/docs/dataclass_attributes_rule.py +270 -0
- gruffpy/rule/docs/ignore_directive_reason_rule.py +173 -0
- gruffpy/rule/docs/missing_class_docstring_rule.py +115 -0
- gruffpy/rule/docs/missing_function_docstring_rule.py +158 -0
- gruffpy/rule/docs/missing_module_docstring_rule.py +105 -0
- gruffpy/rule/docs/missing_param_doc_rule.py +191 -0
- gruffpy/rule/docs/missing_raises_doc_rule.py +126 -0
- gruffpy/rule/docs/missing_readme_rule.py +86 -0
- gruffpy/rule/docs/missing_return_doc_rule.py +129 -0
- gruffpy/rule/docs/stale_param_doc_rule.py +136 -0
- gruffpy/rule/docs/todo_density_rule.py +108 -0
- gruffpy/rule/docs/useless_docstring_rule.py +298 -0
- gruffpy/rule/naming/__init__.py +0 -0
- gruffpy/rule/naming/_allowlists.py +23 -0
- gruffpy/rule/naming/_identifier_tokenizer.py +64 -0
- gruffpy/rule/naming/abbreviation_rule.py +205 -0
- gruffpy/rule/naming/boolean_prefix_rule.py +315 -0
- gruffpy/rule/naming/confusing_name_rule.py +109 -0
- gruffpy/rule/naming/generic_function_rule.py +116 -0
- gruffpy/rule/naming/hungarian_notation_rule.py +200 -0
- gruffpy/rule/naming/identifier_quality_rule.py +181 -0
- gruffpy/rule/naming/module_name_mismatch_rule.py +267 -0
- gruffpy/rule/naming/parameter_type_name_rule.py +393 -0
- gruffpy/rule/naming/short_variable_rule.py +178 -0
- gruffpy/rule/naming/test_naming_consistency_rule.py +142 -0
- gruffpy/rule/project_rule.py +19 -0
- gruffpy/rule/registry.py +198 -0
- gruffpy/rule/rule.py +21 -0
- gruffpy/rule/security/__init__.py +0 -0
- gruffpy/rule/security/_security_metadata.py +127 -0
- gruffpy/rule/security/_security_node_helper.py +265 -0
- gruffpy/rule/security/_security_taint_helper.py +376 -0
- gruffpy/rule/security/cors_wildcard_with_credentials_rule.py +156 -0
- gruffpy/rule/security/dangerous_function_call_rule.py +122 -0
- gruffpy/rule/security/disabled_ssl_verification_rule.py +383 -0
- gruffpy/rule/security/django_mark_safe_rule.py +161 -0
- gruffpy/rule/security/django_raw_sql_rule.py +139 -0
- gruffpy/rule/security/error_suppression_rule.py +134 -0
- gruffpy/rule/security/extract_compact_user_input_rule.py +121 -0
- gruffpy/rule/security/flask_debug_enabled_rule.py +175 -0
- gruffpy/rule/security/hardcoded_bind_all_interfaces_rule.py +172 -0
- gruffpy/rule/security/hardcoded_framework_secret_key_rule.py +149 -0
- gruffpy/rule/security/header_injection_rule.py +114 -0
- gruffpy/rule/security/insecure_random_rule.py +132 -0
- gruffpy/rule/security/insecure_temp_file_rule.py +186 -0
- gruffpy/rule/security/insecure_tls_protocol_rule.py +132 -0
- gruffpy/rule/security/jinja2_autoescape_off_rule.py +185 -0
- gruffpy/rule/security/paramiko_no_host_key_check_rule.py +175 -0
- gruffpy/rule/security/path_traversal_rule.py +191 -0
- gruffpy/rule/security/shell_injection_rule.py +137 -0
- gruffpy/rule/security/silent_except_rule.py +109 -0
- gruffpy/rule/security/sql_concatenation_rule.py +160 -0
- gruffpy/rule/security/ssrf_rule.py +172 -0
- gruffpy/rule/security/unsafe_pickle_rule.py +166 -0
- gruffpy/rule/security/unsafe_yaml_load_rule.py +355 -0
- gruffpy/rule/security/variable_import_rule.py +103 -0
- gruffpy/rule/security/weak_crypto_rule.py +260 -0
- gruffpy/rule/security/xxe_rule.py +231 -0
- gruffpy/rule/sensitive_data/__init__.py +0 -0
- gruffpy/rule/sensitive_data/_secret_scanner_helper.py +138 -0
- gruffpy/rule/sensitive_data/api_key_pattern_rule.py +108 -0
- gruffpy/rule/sensitive_data/aws_access_key_rule.py +90 -0
- gruffpy/rule/sensitive_data/database_url_password_rule.py +137 -0
- gruffpy/rule/sensitive_data/hardcoded_env_value_rule.py +120 -0
- gruffpy/rule/sensitive_data/high_entropy_string_rule.py +120 -0
- gruffpy/rule/sensitive_data/jwt_token_rule.py +80 -0
- gruffpy/rule/sensitive_data/phi_pattern_rule.py +103 -0
- gruffpy/rule/sensitive_data/pii_test_fixture_rule.py +132 -0
- gruffpy/rule/sensitive_data/private_key_rule.py +86 -0
- gruffpy/rule/size/__init__.py +3 -0
- gruffpy/rule/size/_lines.py +89 -0
- gruffpy/rule/size/attribute_count_rule.py +172 -0
- gruffpy/rule/size/average_function_length_rule.py +150 -0
- gruffpy/rule/size/class_length_rule.py +129 -0
- gruffpy/rule/size/file_length_rule.py +88 -0
- gruffpy/rule/size/function_length_rule.py +135 -0
- gruffpy/rule/size/parameter_count_rule.py +128 -0
- gruffpy/rule/size/public_method_count_rule.py +134 -0
- gruffpy/rule/test_quality/__init__.py +0 -0
- gruffpy/rule/test_quality/_pytest_config.py +159 -0
- gruffpy/rule/test_quality/_test_quality_node_helper.py +359 -0
- gruffpy/rule/test_quality/_test_quality_scope.py +33 -0
- gruffpy/rule/test_quality/conditional_logic_rule.py +97 -0
- gruffpy/rule/test_quality/eager_test_rule.py +104 -0
- gruffpy/rule/test_quality/empty_parametrize_rule.py +99 -0
- gruffpy/rule/test_quality/exception_type_only_rule.py +124 -0
- gruffpy/rule/test_quality/excessive_mocking_rule.py +98 -0
- gruffpy/rule/test_quality/extends_production_class_rule.py +149 -0
- gruffpy/rule/test_quality/global_state_mutation_rule.py +95 -0
- gruffpy/rule/test_quality/loop_assertion_without_message_rule.py +102 -0
- gruffpy/rule/test_quality/loop_in_test_rule.py +98 -0
- gruffpy/rule/test_quality/magic_number_assertion_rule.py +317 -0
- gruffpy/rule/test_quality/mock_only_test_rule.py +120 -0
- gruffpy/rule/test_quality/mock_without_expectation_rule.py +125 -0
- gruffpy/rule/test_quality/mocking_domain_object_rule.py +136 -0
- gruffpy/rule/test_quality/multiple_aaa_cycles_rule.py +129 -0
- gruffpy/rule/test_quality/mystery_guest_rule.py +121 -0
- gruffpy/rule/test_quality/naming_consistency_rule.py +103 -0
- gruffpy/rule/test_quality/no_assertions_rule.py +107 -0
- gruffpy/rule/test_quality/parametrize_annotation_rule.py +142 -0
- gruffpy/rule/test_quality/private_reflection_rule.py +107 -0
- gruffpy/rule/test_quality/pytest_coverage_source_missing_rule.py +86 -0
- gruffpy/rule/test_quality/pytest_deprecations_not_fatal_rule.py +85 -0
- gruffpy/rule/test_quality/pytest_strict_config_missing_rule.py +93 -0
- gruffpy/rule/test_quality/repeated_structure_missing_parametrize_rule.py +133 -0
- gruffpy/rule/test_quality/setup_bloat_rule.py +114 -0
- gruffpy/rule/test_quality/skipped_without_reason_rule.py +156 -0
- gruffpy/rule/test_quality/sleep_in_test_rule.py +98 -0
- gruffpy/rule/test_quality/sut_not_called_rule.py +273 -0
- gruffpy/rule/test_quality/tautological_type_assertion_rule.py +140 -0
- gruffpy/rule/test_quality/test_function_too_long_rule.py +105 -0
- gruffpy/rule/test_quality/test_longer_than_sut_rule.py +130 -0
- gruffpy/rule/test_quality/testdox_readability_rule.py +100 -0
- gruffpy/rule/test_quality/trivial_assertion_rule.py +104 -0
- gruffpy/rule/test_quality/trivial_snapshot_rule.py +113 -0
- gruffpy/rule/test_quality/unused_mock_rule.py +109 -0
- gruffpy/rule/waste/__init__.py +0 -0
- gruffpy/rule/waste/commented_out_code_rule.py +155 -0
- gruffpy/rule/waste/empty_class_rule.py +152 -0
- gruffpy/rule/waste/empty_function_rule.py +123 -0
- gruffpy/rule/waste/one_line_function_rule.py +166 -0
- gruffpy/rule/waste/redundant_variable_rule.py +151 -0
- gruffpy/rule/waste/unreachable_code_rule.py +231 -0
- gruffpy/rule/waste/unused_import_rule.py +208 -0
- gruffpy/rule/waste/unused_parameter_rule.py +211 -0
- gruffpy/scoring/__init__.py +25 -0
- gruffpy/scoring/composite_finding_factory.py +151 -0
- gruffpy/scoring/file_score.py +60 -0
- gruffpy/scoring/grade.py +52 -0
- gruffpy/scoring/pillar_score.py +52 -0
- gruffpy/scoring/score_calculator.py +196 -0
- gruffpy/scoring/score_report.py +48 -0
- gruffpy/source/__init__.py +20 -0
- gruffpy/source/discovery.py +343 -0
- gruffpy/source/gitignore.py +170 -0
- gruffpy/source/source_file.py +32 -0
- gruffpy/suppression/__init__.py +11 -0
- gruffpy/suppression/filter.py +37 -0
- gruffpy/suppression/parser.py +273 -0
- gruffpy/version.py +4 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gruff-py
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python project quality analyser.
|
|
5
|
+
Project-URL: Homepage, https://github.com/blundergoat/gruff-py
|
|
6
|
+
Project-URL: Repository, https://github.com/blundergoat/gruff-py
|
|
7
|
+
Project-URL: Issues, https://github.com/blundergoat/gruff-py/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/blundergoat/gruff-py/blob/main/CHANGELOG.md
|
|
9
|
+
Author: Matthew Hansen
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE.md
|
|
12
|
+
Keywords: code-quality,code-review,dashboard,dead-code,linter,quality-gate,sarif,security-scanner,static-analysis,test-quality
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
23
|
+
Classifier: Topic :: Software Development :: Testing
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Python: >=3.11
|
|
26
|
+
Requires-Dist: click>=8.1
|
|
27
|
+
Requires-Dist: docstring-parser<1,>=0.15
|
|
28
|
+
Requires-Dist: pathspec>=0.12
|
|
29
|
+
Requires-Dist: pyyaml>=6.0
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
32
|
+
Requires-Dist: pre-commit>=3.7; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
34
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
35
|
+
Requires-Dist: types-pyyaml>=6.0; extra == 'dev'
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
# gruff-py
|
|
39
|
+
|
|
40
|
+
`gruff-py` is the Python implementation of **gruff**, an opinionated project
|
|
41
|
+
quality analyser. It walks Python projects, applies a broad rule catalogue, and
|
|
42
|
+
emits scored reports for local review, CI, code scanning, and a browser
|
|
43
|
+
dashboard.
|
|
44
|
+
|
|
45
|
+
It is heuristic static analysis. Use it beside tools such as `ruff`, `mypy`,
|
|
46
|
+
`pytest`, and security scanners, not as a replacement for them.
|
|
47
|
+
|
|
48
|
+
## Status
|
|
49
|
+
|
|
50
|
+
`0.1.0` is the first public release.
|
|
51
|
+
|
|
52
|
+
- Python 3.11+
|
|
53
|
+
- 116 rules across 10 active quality pillars
|
|
54
|
+
- Text, JSON, HTML, Markdown, GitHub annotation, hotspot, and SARIF reports
|
|
55
|
+
- Local dashboard served from `127.0.0.1` by default
|
|
56
|
+
- `.gruff-py.yaml` and `[tool.gruff-py]` configuration
|
|
57
|
+
- PHP-compatible finding fingerprints
|
|
58
|
+
- Python import package `gruffpy` with a typed package marker via `py.typed`
|
|
59
|
+
- MIT licensed (see [`LICENSE.md`](LICENSE.md))
|
|
60
|
+
|
|
61
|
+
## Install
|
|
62
|
+
|
|
63
|
+
From this repository:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
uv sync
|
|
67
|
+
./bin/gruff-py --help
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The package entry point is `gruff-py`, so `uv run gruff-py --help` is
|
|
71
|
+
equivalent after `uv sync`.
|
|
72
|
+
|
|
73
|
+
After the package is published:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pipx install gruff-py
|
|
77
|
+
gruff-py --help
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Quick Start
|
|
81
|
+
|
|
82
|
+
Analyse a project:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
uv run gruff-py analyse src/
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Emit JSON for automation:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
uv run gruff-py analyse src/ --format json --fail-on error > gruff.json
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Emit SARIF for code scanning:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
uv run gruff-py analyse src/ --format sarif --fail-on none > gruff.sarif
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Create a standalone HTML report:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
uv run gruff-py analyse src/ --format html --report-interactive > gruff-report.html
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Run the local dashboard:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
uv run gruff-py dashboard src/ --report-interactive
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Then open:
|
|
113
|
+
|
|
114
|
+
```text
|
|
115
|
+
http://127.0.0.1:8765/
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## CLI
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
gruff-py [GLOBAL OPTIONS] <command>
|
|
122
|
+
gruff-py analyse [OPTIONS] [PATHS]...
|
|
123
|
+
gruff-py report [OPTIONS] [PATHS]...
|
|
124
|
+
gruff-py summary [OPTIONS] [PATHS]...
|
|
125
|
+
gruff-py list-rules [OPTIONS]
|
|
126
|
+
gruff-py dashboard [OPTIONS] [PATHS]...
|
|
127
|
+
gruff-py completion [SHELL]
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Global options mirror the gruff-php CLI surface: `--silent`, `--quiet`,
|
|
131
|
+
`--version`, `--ansi` / `--no-ansi`, `--no-interaction`, and `--verbose`.
|
|
132
|
+
|
|
133
|
+
Common `analyse` and `report` options:
|
|
134
|
+
|
|
135
|
+
| Option | Meaning |
|
|
136
|
+
|---|---|
|
|
137
|
+
| `--format text` | Terminal summary, the default |
|
|
138
|
+
| `--format json` | Full `gruff-py.analysis.v1` payload |
|
|
139
|
+
| `--format html` | Self-contained dark HTML report |
|
|
140
|
+
| `--format markdown` | Pull-request or issue comment summary |
|
|
141
|
+
| `--format github` | GitHub Actions annotation commands |
|
|
142
|
+
| `--format hotspot` | `gruff-py.hotspot.v1` file offender JSON |
|
|
143
|
+
| `--format sarif` | SARIF 2.1.0 code-scanning output |
|
|
144
|
+
| `--fail-on error` | Exit non-zero for findings at or above the threshold |
|
|
145
|
+
| `--no-config` | Ignore `.gruff-py.yaml` and `[tool.gruff-py]` |
|
|
146
|
+
| `--include-ignored` | Scan default-ignored directories and `.gitignore` exclusions |
|
|
147
|
+
| `--min-severity warning` | Display only warning/error findings |
|
|
148
|
+
| `--include-pillar documentation` | Display only selected pillar findings |
|
|
149
|
+
| `--exclude-rule docs.missing-function-docstring` | Hide selected rule findings |
|
|
150
|
+
|
|
151
|
+
Additional commands:
|
|
152
|
+
|
|
153
|
+
| Command | Meaning |
|
|
154
|
+
|---|---|
|
|
155
|
+
| `gruff-py report --format html --output gruff.html` | Render an HTML or JSON report to a file or stdout |
|
|
156
|
+
| `gruff-py summary --format text` | Print compact per-pillar, top-rule, and top-file counts |
|
|
157
|
+
| `gruff-py list-rules --format json` | Print registered rule metadata |
|
|
158
|
+
| `gruff-py list` | List available commands |
|
|
159
|
+
| `gruff-py help analyse` | Display command help |
|
|
160
|
+
| `gruff-py completion bash` | Dump a shell completion script |
|
|
161
|
+
|
|
162
|
+
Exit codes:
|
|
163
|
+
|
|
164
|
+
| Code | Meaning |
|
|
165
|
+
|---|---|
|
|
166
|
+
| `0` | Run completed and no finding reached `--fail-on` |
|
|
167
|
+
| `1` | At least one finding reached `--fail-on` |
|
|
168
|
+
| `2` | Input, parse, or configuration diagnostic |
|
|
169
|
+
|
|
170
|
+
## Configuration
|
|
171
|
+
|
|
172
|
+
Config is optional. Precedence is:
|
|
173
|
+
|
|
174
|
+
1. `--config <path>`
|
|
175
|
+
2. `.gruff-py.yaml` in the project root
|
|
176
|
+
3. `[tool.gruff-py]` in `pyproject.toml`
|
|
177
|
+
4. Built-in defaults
|
|
178
|
+
|
|
179
|
+
Example `.gruff-py.yaml`:
|
|
180
|
+
|
|
181
|
+
```yaml
|
|
182
|
+
paths:
|
|
183
|
+
ignore:
|
|
184
|
+
- "tests/fixtures/**"
|
|
185
|
+
|
|
186
|
+
selection:
|
|
187
|
+
excludeRules:
|
|
188
|
+
- docs.missing-module-docstring
|
|
189
|
+
|
|
190
|
+
rules:
|
|
191
|
+
size.file-length:
|
|
192
|
+
threshold: 900
|
|
193
|
+
severity: error
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
See [Configuration](docs/CONFIGURATION.md) for the full shape.
|
|
197
|
+
|
|
198
|
+
## Quality Pillars
|
|
199
|
+
|
|
200
|
+
gruff-py scores findings across these active pillars:
|
|
201
|
+
|
|
202
|
+
- `size`
|
|
203
|
+
- `complexity`
|
|
204
|
+
- `maintainability`
|
|
205
|
+
- `dead-code`
|
|
206
|
+
- `naming`
|
|
207
|
+
- `documentation`
|
|
208
|
+
- `security`
|
|
209
|
+
- `sensitive-data`
|
|
210
|
+
- `test-quality`
|
|
211
|
+
- `design`
|
|
212
|
+
|
|
213
|
+
`modernisation`, `coupling`, `architecture`, and `mutation` are reserved schema
|
|
214
|
+
or future catalogue names. They do not all have shipping rules in `0.1`.
|
|
215
|
+
|
|
216
|
+
See [Rules](docs/RULES.md) for the rule catalogue.
|
|
217
|
+
|
|
218
|
+
## Reports And Dashboard
|
|
219
|
+
|
|
220
|
+
- [Reports](docs/REPORTING.md) explains every output format and CI use case.
|
|
221
|
+
- [Dashboard](docs/DASHBOARD.md) documents the local browser dashboard.
|
|
222
|
+
|
|
223
|
+
The JSON schema string is `gruff-py.analysis.v1`; hotspot output uses
|
|
224
|
+
`gruff-py.hotspot.v1`. Finding fingerprints are 16-character SHA-256 derivatives
|
|
225
|
+
kept compatible with the PHP implementation. SARIF is rendered from the same
|
|
226
|
+
native report data without changing native schemas or fingerprints; SARIF result
|
|
227
|
+
fingerprints use `partialFingerprints.gruffFingerprint`.
|
|
228
|
+
|
|
229
|
+
## Development
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
uv sync --extra dev
|
|
233
|
+
uv run ruff check src tests
|
|
234
|
+
uv run ruff format --check src tests
|
|
235
|
+
uv run mypy src
|
|
236
|
+
uv run pytest
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
The Makefile mirrors these commands:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
make check
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Note that `make check` uses the `lint` target, which runs `ruff check --fix`.
|
|
246
|
+
Use the explicit commands above when you need non-mutating release verification.
|
|
247
|
+
|
|
248
|
+
### Performance harness
|
|
249
|
+
|
|
250
|
+
`scripts/test-performance.sh` runs a fixed workload matrix (cold-start,
|
|
251
|
+
analyse on `src/`/`tests/`, reporter variants, synthetic 100/1000-file
|
|
252
|
+
fixtures) with median/p95/min/max wall-clock, peak RSS via
|
|
253
|
+
`/usr/bin/time -v`, and per-rule cost attribution from `cProfile`. Baselines
|
|
254
|
+
live under `scripts/performance-baselines/<host>.json`; pass `--baseline` to
|
|
255
|
+
fail the script on regressions.
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
make perf-quick # CI smoke: cold-start + analyse-src vs baseline
|
|
259
|
+
make perf # full suite vs baseline
|
|
260
|
+
make perf-baseline # overwrite the linux-x86_64 baseline with the current run
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Project Docs
|
|
264
|
+
|
|
265
|
+
- [Changelog](CHANGELOG.md)
|
|
266
|
+
- [Contributing](CONTRIBUTING.md)
|
|
267
|
+
- [Code of Conduct](CODE_OF_CONDUCT.md)
|
|
268
|
+
- [Security](SECURITY.md)
|
|
269
|
+
- [Support](SUPPORT.md)
|
|
270
|
+
- [Release checklist](docs/RELEASING.md)
|
|
271
|
+
- [License](LICENSE.md)
|
|
272
|
+
|
|
273
|
+
## Author
|
|
274
|
+
|
|
275
|
+
Built by [Matthew Hansen](https://www.blundergoat.com/about).
|
|
276
|
+
|
|
277
|
+
## License
|
|
278
|
+
|
|
279
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
gruffpy/__init__.py,sha256=aoU75zQDJTDD1hToVoLBeL9T_7yW2KftihlTFMmEQfw,110
|
|
2
|
+
gruffpy/__main__.py,sha256=F-EyR4jUxKQGRm8MFmYllIMYTbJDktDvQ7fNkpcAQNs,186
|
|
3
|
+
gruffpy/cli.py,sha256=EUici4IMwGwzfg7LOegkS5J0N2qrxKzVPan0mlpexsw,25508
|
|
4
|
+
gruffpy/cli_menu.py,sha256=Tohr3-nYUfN-cZuAGhlV8UnL4lDOpR7Phd2Ble3MREw,4467
|
|
5
|
+
gruffpy/cli_options.py,sha256=VBeKEnQN2R68fUoNamQT3MzYzbKa4puKSHNPeK6cCS0,23870
|
|
6
|
+
gruffpy/cli_state.py,sha256=c0iQoPPmRSU3NL8iuEjb5pExJ-XlONtXq9A2FNiAI5w,1727
|
|
7
|
+
gruffpy/version.py,sha256=GVX_W8Ucx_0_wT9d0LFP3BQ78HeRR4bU_PyEn9QAqXE,108
|
|
8
|
+
gruffpy/analysis/__init__.py,sha256=gG0LlebiSdba_0ozJgUha1p-LIIkD3q9V6QfvgPNHcI,385
|
|
9
|
+
gruffpy/analysis/report.py,sha256=2gylEAlZif_p5yYEnSGNLGLCI5bmBYLMN3rtVQW4H1k,5655
|
|
10
|
+
gruffpy/analysis/run_diagnostic.py,sha256=1s75jeZXh-WMnwdyU8El_nwt4Ben-c01xr1VdqtBmig,1087
|
|
11
|
+
gruffpy/analysis/runner.py,sha256=Z78FYmeAAYMl8N6s-FvMP-Sex9gyowwNgPbIVkIJuf8,10046
|
|
12
|
+
gruffpy/analysis/schema.py,sha256=-smudlMOPvDu8HKoFw7WZDXiJLXGy7FE5WuyaZKCfVo,204
|
|
13
|
+
gruffpy/command/__init__.py,sha256=HizXq80wBBkjTmqXtQKj3jWybLdgtcI8Ti8N-sCCD7g,241
|
|
14
|
+
gruffpy/command/dashboard_page_renderer.py,sha256=sMaeyOBiEqJ3IlRuE2omgRZrA0_0fZC2qpzLaqnteCk,16846
|
|
15
|
+
gruffpy/command/dashboard_server.py,sha256=-He0zJ1wShih1WBblG2bYHzURRp3-jEZneomACoiCsE,10577
|
|
16
|
+
gruffpy/command/metric_calibration.py,sha256=RhWm_-gaerSLHDNnlpzbSabF1UysIcH_os3xuFMs2h0,23238
|
|
17
|
+
gruffpy/command/rule_docs.py,sha256=jEJBe-TMrnSeNVhRr9c4xMs--BRX3JUoLGUCO215Dwg,10485
|
|
18
|
+
gruffpy/config/__init__.py,sha256=6fzJ5RX8_XxVZx_TjwUmC01MzMVdV5CzA1HVbCVwgUM,382
|
|
19
|
+
gruffpy/config/analysis_config.py,sha256=gDmdeJCep7HDMYtfDnfpyaYf0Tlu1ipli-GqDUrpEqI,7173
|
|
20
|
+
gruffpy/config/dead_code_allowlist.py,sha256=t1Pn0E-VW-VhAIr9YkwvM6QFR4lCrQfB04SHxNxiPKE,2324
|
|
21
|
+
gruffpy/config/exceptions.py,sha256=_XVoQ6qowk6k6VMiPFscCxa9HiogqIiRH11WBuqI8mw,188
|
|
22
|
+
gruffpy/config/loader.py,sha256=KfAqhEWVJdN1NAqTX-gXz8NUNHYu-e5OmKHKLWvcNOc,15862
|
|
23
|
+
gruffpy/config/rule_selection.py,sha256=86Ppz0acvUiAO1Ff7QssOC3z61zarUBmExzhWsiwQFk,2352
|
|
24
|
+
gruffpy/config/rule_settings.py,sha256=swJY9D7JuTsKxY4jewBFE5_-fZDfAR4aPG-M2Wu4vVs,6313
|
|
25
|
+
gruffpy/config/yaml_loader.py,sha256=wlTQdSk6YlS0-FnumOgWad0bhC13jkrwzbTVRT4p3og,1309
|
|
26
|
+
gruffpy/finding/__init__.py,sha256=basTypb_vso9w1UBu43UPIcgG5UaJ2EofMa8b5CLHEc,555
|
|
27
|
+
gruffpy/finding/confidence.py,sha256=MDha1Pl9k3J16irJS4dCAlCzVbgnsSUp6-TEuiTQABU,178
|
|
28
|
+
gruffpy/finding/fail_threshold.py,sha256=KoKBRiN3C9l1442v8kAwulfKmeEzoTvaKQOEmUrPZN4,1541
|
|
29
|
+
gruffpy/finding/finding.py,sha256=F1dzdK3cTBweVx8tEGBcTu-B6kc__GolW9bqSuJb-pw,3516
|
|
30
|
+
gruffpy/finding/fingerprint.py,sha256=Zyt6afAnQnRnWaEgdHJr8Q4b1F92GD_-596OxDkB58w,1668
|
|
31
|
+
gruffpy/finding/output_format.py,sha256=lXDA1ajPhV4iz-MQ2MFipbSi0ryiLvrvqQH2jEXQElw,820
|
|
32
|
+
gruffpy/finding/pillar.py,sha256=kA85TbkWj0HxeGz9GxACL1Uk1D6j463qgbkxN1kvu0w,549
|
|
33
|
+
gruffpy/finding/rule_tier.py,sha256=1xzhHGMOm_jVzaD70RKCuuJ4nYe2TkueZ9MK6T9qmIc,149
|
|
34
|
+
gruffpy/finding/severity.py,sha256=ycLqVVDqZnA3HD9I_NhFPSXfptS0u0kRt8SdY-RFTsg,744
|
|
35
|
+
gruffpy/parser/__init__.py,sha256=Jjwnc0wq-hBAnSlyZPZzHKe785erjPjBrcod54h3N24,196
|
|
36
|
+
gruffpy/parser/analysis_unit.py,sha256=ry-EzdBBGIo9cjEeVT0uPFHOHxKQ4m3PQpEGw1zBvX4,1369
|
|
37
|
+
gruffpy/parser/python_parser.py,sha256=JrOT72BA0xVMjg76TbMIXHtvELOHprFowOhYs6EnKso,2093
|
|
38
|
+
gruffpy/reporting/__init__.py,sha256=IeBjmZr8dA6Hhr_DzikZIInpTRfxaOn_0N7NOEtKy1M,720
|
|
39
|
+
gruffpy/reporting/finding_display_filter.py,sha256=HbHTwKGYQOVEWwJTx3EBzSP3tfcMtJGpposhKu8w91A,3734
|
|
40
|
+
gruffpy/reporting/github_annotations_reporter.py,sha256=v563demTexIv5OWpp-J8yKF0SBkV-YRpXeK75sKIX78,1844
|
|
41
|
+
gruffpy/reporting/hotspot_reporter.py,sha256=ccNC2r5QDEjOavE6hYtNZL54smj1XRAK-sCeScmueTg,1436
|
|
42
|
+
gruffpy/reporting/html_reporter.py,sha256=v6JqYp1AjHOFdOOCe87-svo8GL2zMhTJvwEMCrhrV6Q,32717
|
|
43
|
+
gruffpy/reporting/json_reporter.py,sha256=Zn92xOeuUpDjOnIrmBgPr_XcoivSS5u_wPNtWV4HanI,1395
|
|
44
|
+
gruffpy/reporting/markdown_reporter.py,sha256=zs5spPv5l80gw8Q0JdSrKoOW1govNBtCozIL4VhnIR8,3941
|
|
45
|
+
gruffpy/reporting/sarif_reporter.py,sha256=VWOpOEYJrzqs8mQvo0BBwxGNZIQrJSV3MRNRqdRqIZw,6719
|
|
46
|
+
gruffpy/reporting/text_reporter.py,sha256=mLajbVCudfogAlW0eMKrsxjdHZLQB_L3h9yo8iYlmIU,4200
|
|
47
|
+
gruffpy/rule/__init__.py,sha256=TCxKdR8kOS_nV7Nhj4yARZrTVChEuAnwyxCGRyK2thk,281
|
|
48
|
+
gruffpy/rule/_python_dynamism.py,sha256=xqk23s4JjxFluv_RtJKSvIpYaZ8VvkpqRLDEocYVxtM,9893
|
|
49
|
+
gruffpy/rule/builtins.py,sha256=L49WloGobWfm-4PsXB-um5kk__wzizEk6YPJvSbs97c,299
|
|
50
|
+
gruffpy/rule/catalog.py,sha256=5vZYZkVJjrWwjbQXqxbICM3Sb3WH3MM6HCG9lZJR2X0,25814
|
|
51
|
+
gruffpy/rule/context.py,sha256=0dKbh0dKOP-0RvO2ALz_LQpvLTDgyIUWtc5tBwUi_RU,1159
|
|
52
|
+
gruffpy/rule/definition.py,sha256=EMexl4Aee7BpWr6453GR7qGra89ckREU9KSlAjndovs,2733
|
|
53
|
+
gruffpy/rule/project_rule.py,sha256=fx5JzYW4cx2K0qkmm2BDtbrBE-QSsffDGXZlXl6nG-0,570
|
|
54
|
+
gruffpy/rule/registry.py,sha256=lsmiSVpI7D_bZqHj2jTXx6PtpUTFAZsTowPr45wlErg,7035
|
|
55
|
+
gruffpy/rule/rule.py,sha256=aSm_UGYTlXZ4m1Dg4_dXqbWM4vb4oOS-K1oTPOfby2w,662
|
|
56
|
+
gruffpy/rule/complexity/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
57
|
+
gruffpy/rule/complexity/_halstead.py,sha256=jbIKbpWx9wjLEu5Y8xD24aqPacuPnOws0hEW6iAXkrg,7720
|
|
58
|
+
gruffpy/rule/complexity/_walks.py,sha256=8DN3DTAOv48Fe1OL1X7WOVKBTlzsuk8f0N7pcauofxI,2903
|
|
59
|
+
gruffpy/rule/complexity/cognitive_complexity_rule.py,sha256=AkxPkvRfaMhQK9t-HvA6OFSs4sxxvYRvfb-h9yxkWl8,12007
|
|
60
|
+
gruffpy/rule/complexity/cyclomatic_complexity_rule.py,sha256=bWkp51kgdf1SnaiOCkDMf-7m4MFTS0Gcc3IGleUpsy8,5547
|
|
61
|
+
gruffpy/rule/complexity/halstead_volume_rule.py,sha256=R5JYbCzCVgu00hL7SoqAKiAowX8e6Av6LU_-HguhELY,4074
|
|
62
|
+
gruffpy/rule/complexity/maintainability_index_rule.py,sha256=VesKEFiEQpOqwoOc3yZwJoqPCg7nJK9nYk6hO3AZ1UQ,5207
|
|
63
|
+
gruffpy/rule/complexity/nesting_depth_rule.py,sha256=e_ULU_Hlc-WFxUQPMrGgM5hMMykLgzO6Ehe3jUvRm9w,6362
|
|
64
|
+
gruffpy/rule/complexity/npath_complexity_rule.py,sha256=yCIR0hr5SQXQ3shndR_adw7_f2ClkcHlX7WLJ0X_fT4,8199
|
|
65
|
+
gruffpy/rule/dead_code/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
66
|
+
gruffpy/rule/dead_code/unused_private_attribute_rule.py,sha256=_0tQp9t7BQMFU8_GrKzl4UZZnuBZp7TQShgVXm12uM8,7387
|
|
67
|
+
gruffpy/rule/dead_code/unused_private_function_rule.py,sha256=gGZhYmuM1qt5egzbDZ-HCy-bqF2c8PYfcG06FPSrEfQ,10479
|
|
68
|
+
gruffpy/rule/design/__init__.py,sha256=TBD7Mmb2wGNiCnoYvhA1Y_DYWp_GYzN2DBWfd-kVUNM,140
|
|
69
|
+
gruffpy/rule/design/single_implementor_protocol_rule.py,sha256=PaKCnZ4fQ52FScBqRb4XSynRUNyp9_S175qXO54-klU,14972
|
|
70
|
+
gruffpy/rule/docs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
71
|
+
gruffpy/rule/docs/_comment_scanner.py,sha256=nW4IqnXMagaRwGCboubPGlQQSVDy1pNctpOn2SOe5XQ,1433
|
|
72
|
+
gruffpy/rule/docs/_docstring_parser.py,sha256=guOdoBMH9wSynRTUYrL5Wqvyr1IOHlWnd2GCDfEw5lU,4334
|
|
73
|
+
gruffpy/rule/docs/_helpers.py,sha256=6tjEOVEL4vEEXG34qUXw7xx4pdN8tv3DASLLeUUFtvM,6112
|
|
74
|
+
gruffpy/rule/docs/complex_branch_rationale_rule.py,sha256=3h17C87aLJWuwuQCkppGGSbsZ1TcFud731DpxHP5OR4,7572
|
|
75
|
+
gruffpy/rule/docs/dataclass_attributes_rule.py,sha256=PghMF3ZEB1yUql1M2YBzPpgJW_YtV7d4LbRmi2REPVE,9583
|
|
76
|
+
gruffpy/rule/docs/ignore_directive_reason_rule.py,sha256=ZmHdIhgmmamEwcZ8f5pezw6H7xvoVoWL_JEiSOSs7yw,5526
|
|
77
|
+
gruffpy/rule/docs/missing_class_docstring_rule.py,sha256=ubq_2f9niDolncX7kQXIKk77xQuCgA504hV0tgQ6mN4,4112
|
|
78
|
+
gruffpy/rule/docs/missing_function_docstring_rule.py,sha256=lnLURsaeVqHQoYk82_f0afQi4wM3AGX1nmS8BloEjL4,5662
|
|
79
|
+
gruffpy/rule/docs/missing_module_docstring_rule.py,sha256=EDejbxKiZVyZJIWqSHof62HjOnATJr3wzQPXqjiDgbs,3738
|
|
80
|
+
gruffpy/rule/docs/missing_param_doc_rule.py,sha256=xzUHTmuuPCgaj401C_YpG4zcDUCyHYIoRHJ95JRUUyg,6435
|
|
81
|
+
gruffpy/rule/docs/missing_raises_doc_rule.py,sha256=p__0bFX6WbuT7q1PAdmUqopy4foIbVdxl3aW09X9JoQ,4251
|
|
82
|
+
gruffpy/rule/docs/missing_readme_rule.py,sha256=T0KxpKQSjlw9-IZQI6g_yFZe046udpwMVI2KVQdWXBE,2975
|
|
83
|
+
gruffpy/rule/docs/missing_return_doc_rule.py,sha256=-ZCXvTWO3iDR9DdDMvsBJoFbafk13pvWX9fjdqKZgKY,4497
|
|
84
|
+
gruffpy/rule/docs/stale_param_doc_rule.py,sha256=d4wtK09iXyl8UoKuXT_WZteZDWaQIm-igM-aQwsviNY,4747
|
|
85
|
+
gruffpy/rule/docs/todo_density_rule.py,sha256=v1W5_NWbE72jNfB3yal-2as6m8-BjcYfhdMMQoErfHk,4107
|
|
86
|
+
gruffpy/rule/docs/useless_docstring_rule.py,sha256=Nmo1f9qKXbIQMohq_rvjwORYg_q0SiF8dje4I3fFinI,8971
|
|
87
|
+
gruffpy/rule/naming/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
88
|
+
gruffpy/rule/naming/_allowlists.py,sha256=VONfvnMrEpEHCFfm0o_MPZvcbh8PatmEztpDt3hfXXo,318
|
|
89
|
+
gruffpy/rule/naming/_identifier_tokenizer.py,sha256=YZtd2Dv1sxsk6PCHWLdLG0FeWJSt0RLPXa9w9NeSKYs,1987
|
|
90
|
+
gruffpy/rule/naming/abbreviation_rule.py,sha256=UVir1cpp-bmWuog5v06Igw0yz16jwp6uJouvZPILN9M,6859
|
|
91
|
+
gruffpy/rule/naming/boolean_prefix_rule.py,sha256=HLE6AD-40ke7JrAUMbWN9ItG_Q3u3Mo3rHeEDV3YGlM,10143
|
|
92
|
+
gruffpy/rule/naming/confusing_name_rule.py,sha256=jb6Wa2G3KCcFDaUqGUwIWWYVPh2CzQHW5CQZi4tN3j4,4022
|
|
93
|
+
gruffpy/rule/naming/generic_function_rule.py,sha256=DrANy-3CLwppmBVf2c0Lfrcc5w2tvtv-wwIFrjuCR3M,4451
|
|
94
|
+
gruffpy/rule/naming/hungarian_notation_rule.py,sha256=p6-EM27i6dULHB51JrMMjmwwjagCdXafdrfa9KkXlHI,7039
|
|
95
|
+
gruffpy/rule/naming/identifier_quality_rule.py,sha256=lGUskuyTqrpHA40Ztn8YOAIckHlL1uutM9X-rsRLaiw,6485
|
|
96
|
+
gruffpy/rule/naming/module_name_mismatch_rule.py,sha256=VDmmhG1VtFXw4nAUdemthltUIlYdfcefHwQFBQoSWQ8,8840
|
|
97
|
+
gruffpy/rule/naming/parameter_type_name_rule.py,sha256=i6ZuB2E4f2VuJl-a7mHNv6SPUfei8tzwzYlLQcvHTxg,14130
|
|
98
|
+
gruffpy/rule/naming/short_variable_rule.py,sha256=zNgEB0Mnh5NA4JdiTpiCNCrYF5zLIkw1o7IGKgg7Txc,6322
|
|
99
|
+
gruffpy/rule/naming/test_naming_consistency_rule.py,sha256=vDqRb5pt9-9WfY72MiK71DQQ2iuwpCND7eXk_uHTCI0,5266
|
|
100
|
+
gruffpy/rule/security/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
101
|
+
gruffpy/rule/security/_security_metadata.py,sha256=sPJpD2Ac9QN5i-OF_FNwn06qpxpWcjWp9RIbG6U4bls,4004
|
|
102
|
+
gruffpy/rule/security/_security_node_helper.py,sha256=HTrS19VYsHhw8J5xtwFXD44DCsjXBegrl6qo8MSHs7M,8007
|
|
103
|
+
gruffpy/rule/security/_security_taint_helper.py,sha256=GnDk7xiyOZoFV83Os8Gdm5s9hT3fK5YXP6jvyRBlCEc,14435
|
|
104
|
+
gruffpy/rule/security/cors_wildcard_with_credentials_rule.py,sha256=AAXBH_15LkoXfPFPYdwF9NZodJr_SY3CrGZ6C5EnaMY,6008
|
|
105
|
+
gruffpy/rule/security/dangerous_function_call_rule.py,sha256=7GBGnRs98TL-iIlgd5BDJ2a7_T1txHV28vAVbZAz4Vw,4346
|
|
106
|
+
gruffpy/rule/security/disabled_ssl_verification_rule.py,sha256=Os1vyifxrMWql7jKQhlN8RXOlPCHcCM6BMTZmQTUZH0,14017
|
|
107
|
+
gruffpy/rule/security/django_mark_safe_rule.py,sha256=1ANsnANFkScR2SloM5tTeuo6msQcwAslz7_yFKISoZM,5829
|
|
108
|
+
gruffpy/rule/security/django_raw_sql_rule.py,sha256=BErJXjHip2gTq5kxm_TMTBJblzXgJ2QS9aVPiF0R6QQ,5125
|
|
109
|
+
gruffpy/rule/security/error_suppression_rule.py,sha256=5n0ZHUthFlMhzXsKwQjr6wkly62kW5jCdS8hvJG_JgA,5271
|
|
110
|
+
gruffpy/rule/security/extract_compact_user_input_rule.py,sha256=TKklw6h1t6SbHSXSfS7KL5a00cIXOfKuWMlHez5xtxY,4819
|
|
111
|
+
gruffpy/rule/security/flask_debug_enabled_rule.py,sha256=hSHWH1tFJXqBxgbfIsHypxnI51i5N6mE210U-qG3uCs,6170
|
|
112
|
+
gruffpy/rule/security/hardcoded_bind_all_interfaces_rule.py,sha256=XIyPDUzxJP4kpw0Z7rHTASieV29w9r0-pdeE0BsvWyw,6296
|
|
113
|
+
gruffpy/rule/security/hardcoded_framework_secret_key_rule.py,sha256=et7CYkBnG0PXqFrEYdyvs7MAY_g0bbikstmfYRxzLBI,5688
|
|
114
|
+
gruffpy/rule/security/header_injection_rule.py,sha256=bZD6UNwXnEj6G-oSOzNwEg2i-yYBXKmbS8BjItdbImY,4015
|
|
115
|
+
gruffpy/rule/security/insecure_random_rule.py,sha256=Qqa2ltBSxLFaybbkZShZ5H0CtipYFM0jAucZ_JhW4CE,4804
|
|
116
|
+
gruffpy/rule/security/insecure_temp_file_rule.py,sha256=MEPZj87ySrSCV-2TzQIT_7BjJNzxB7GI6TpGUaUyvPI,6829
|
|
117
|
+
gruffpy/rule/security/insecure_tls_protocol_rule.py,sha256=Dyts5fmWxitF4RA6PJckFwF6JFtxtc85qLEa8IuVNNE,4799
|
|
118
|
+
gruffpy/rule/security/jinja2_autoescape_off_rule.py,sha256=mmvCTGiuPw28LVdgQP-k_j4ivIUJtT1KU3GF-B9FSBQ,6695
|
|
119
|
+
gruffpy/rule/security/paramiko_no_host_key_check_rule.py,sha256=DSqFesbJyd4yNCBoCvYgc7h8biEtBeV8UgA_FFlS_Vg,6611
|
|
120
|
+
gruffpy/rule/security/path_traversal_rule.py,sha256=-k1KOzqgbjol4937ZuY8OgyA5acIhlgvyIrVWmkHFns,7188
|
|
121
|
+
gruffpy/rule/security/shell_injection_rule.py,sha256=Uu9W7CuD67y-1bnCHq6wHhAjh0ZrQFoscS9Ut3jRaPc,5042
|
|
122
|
+
gruffpy/rule/security/silent_except_rule.py,sha256=YaxiQ_1vfPRaHjdpYn78NNxzqZOqTQRNENjizYgR7xg,4246
|
|
123
|
+
gruffpy/rule/security/sql_concatenation_rule.py,sha256=9ESjsh4BnRWFPEJ9UR6lYEVhDKym6Ic7a2AW07msv7Y,5851
|
|
124
|
+
gruffpy/rule/security/ssrf_rule.py,sha256=IMK0twyTS5FsGpTMox4FL-eYD0ZnF26snbBkM0kBbzo,6620
|
|
125
|
+
gruffpy/rule/security/unsafe_pickle_rule.py,sha256=o7QRlzPl04Mk9KlNeoyNjq7BcSX27aKqq5mnlNeb8l8,5666
|
|
126
|
+
gruffpy/rule/security/unsafe_yaml_load_rule.py,sha256=vtmY0R4QMPavtokDJsiFcHXb9P9hKGALDTiqWEJgnCA,12423
|
|
127
|
+
gruffpy/rule/security/variable_import_rule.py,sha256=9iuoKFRdPEc8bN9NGuHi41T8MGdbFqu-dnIr-M2MIhE,4011
|
|
128
|
+
gruffpy/rule/security/weak_crypto_rule.py,sha256=nriYTuK9hJsa4Le6jwib-xP4ytoku02QN32JNzQqOJE,9140
|
|
129
|
+
gruffpy/rule/security/xxe_rule.py,sha256=vBBHJzAVLjCwVuqiiUHG0cODJ_TiGVy6jdLgAILC_v0,8375
|
|
130
|
+
gruffpy/rule/sensitive_data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
131
|
+
gruffpy/rule/sensitive_data/_secret_scanner_helper.py,sha256=RJdpYfoc9664aeSgPdqxINJwXWzeQl3ucNjmtMxoNWA,4539
|
|
132
|
+
gruffpy/rule/sensitive_data/api_key_pattern_rule.py,sha256=hyZ6epn9jzkzDO_Z_vX4CtJAbaFQEKdKJv9-bXh1YDA,4326
|
|
133
|
+
gruffpy/rule/sensitive_data/aws_access_key_rule.py,sha256=YPAkvCw0MJWuMXvTdEtlZVDQonORrhFJrGp7FMlLYoA,3550
|
|
134
|
+
gruffpy/rule/sensitive_data/database_url_password_rule.py,sha256=kYtigpbLsle7ETC023_9aax1fgY3_qF43AAcKuK-1eU,4772
|
|
135
|
+
gruffpy/rule/sensitive_data/hardcoded_env_value_rule.py,sha256=XtZ2a0hcyPu0kd8YjZowsFwOVmF31akZtYczOLOYcDM,4928
|
|
136
|
+
gruffpy/rule/sensitive_data/high_entropy_string_rule.py,sha256=ouuCm1m3k_tjEP7Cd2uCeBXwB5EzyQ4CWEJoF51Km0Y,4862
|
|
137
|
+
gruffpy/rule/sensitive_data/jwt_token_rule.py,sha256=lry7yhIuDfsMqcms4S6HguU2zVaDPI0c5ZxbMy0WZ94,3138
|
|
138
|
+
gruffpy/rule/sensitive_data/phi_pattern_rule.py,sha256=GkRLqk-I7CBt86nB-tdIKggvP9hcbK-vppdJY0o2GDM,3994
|
|
139
|
+
gruffpy/rule/sensitive_data/pii_test_fixture_rule.py,sha256=WVcpA2d2k7xO5gfMQr1RjkfA3bTDZwup_9BvBtNZc-0,4881
|
|
140
|
+
gruffpy/rule/sensitive_data/private_key_rule.py,sha256=vjXit-Ti5puI4PuofRbijh7CYUFvoUlEA07omK0qdVU,3345
|
|
141
|
+
gruffpy/rule/size/__init__.py,sha256=drNTdePLLLceQMgKwm7ViKVIpapljDGLGdTTvGJJzgc,92
|
|
142
|
+
gruffpy/rule/size/_lines.py,sha256=UUMmqqU7nG3zg4sgdR0BvM7HKuVbses7cPKC44dyfvs,3126
|
|
143
|
+
gruffpy/rule/size/attribute_count_rule.py,sha256=KCdHr4IZt5v2FMRcJAz0xAgiOJ49fhWDt55eAXMxN0E,6465
|
|
144
|
+
gruffpy/rule/size/average_function_length_rule.py,sha256=oblfMvIk1Ra7NkOCr08T5djDLfTzn7S4_50esQao0v0,5446
|
|
145
|
+
gruffpy/rule/size/class_length_rule.py,sha256=d_tZKBrUjFF_T1H9ThnNCCRpNzub08rMNNH9BybAwhs,4481
|
|
146
|
+
gruffpy/rule/size/file_length_rule.py,sha256=fdvbAIrWb-60IRP0KdLvvwdHkpvo47igbtH0vYSyOa4,3404
|
|
147
|
+
gruffpy/rule/size/function_length_rule.py,sha256=_Vae7aI7hD3R-n6XZcX0fsv3UEc8vgUZ2t2LMlE9fs4,4797
|
|
148
|
+
gruffpy/rule/size/parameter_count_rule.py,sha256=Kb2xjGnyy8hZpibfnZ0qLDLE6c5Io0-G1oHVckuvlok,4863
|
|
149
|
+
gruffpy/rule/size/public_method_count_rule.py,sha256=ZZ9xgbXiGYvGemquYjKTK_Bu7nH5_bcBIWXCAlRBdBk,5019
|
|
150
|
+
gruffpy/rule/test_quality/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
151
|
+
gruffpy/rule/test_quality/_pytest_config.py,sha256=-obKn8apxOD7XK-GwM--pdQMyxUMXkl9o4u9wpFXuig,5151
|
|
152
|
+
gruffpy/rule/test_quality/_test_quality_node_helper.py,sha256=DaCb7eNSxP3rQ1Lr9MsNNVuhM0HUAuFG5BpAkZuKVGQ,11432
|
|
153
|
+
gruffpy/rule/test_quality/_test_quality_scope.py,sha256=cvQUjyRgdynuxyM-tlyPwlOp30PB9OUFikaAOkacjco,1197
|
|
154
|
+
gruffpy/rule/test_quality/conditional_logic_rule.py,sha256=Zl9fcwXKtQyLTMlQbFnftfNBkl3muFx0jNqw5pMm8kc,3910
|
|
155
|
+
gruffpy/rule/test_quality/eager_test_rule.py,sha256=-UfUnWw4yR2S2c5M_-2dpujGMJEIOZO6sxJEulIb3QU,4060
|
|
156
|
+
gruffpy/rule/test_quality/empty_parametrize_rule.py,sha256=uNanpS7UT-_SbU38cnF-ACkboOeAabdZRopJGQtMoig,4025
|
|
157
|
+
gruffpy/rule/test_quality/exception_type_only_rule.py,sha256=hhyzyrwjWy2ZXRSS3fyfN12mbu8HeN_gsGtnpu3jwTM,4686
|
|
158
|
+
gruffpy/rule/test_quality/excessive_mocking_rule.py,sha256=NTH9xtGbW7C6gNjHFCMEVTwAaVKgPa3p5D2v7BrcZBA,3906
|
|
159
|
+
gruffpy/rule/test_quality/extends_production_class_rule.py,sha256=aSejBrtofljWxGaINpvCAP55eVKjXXzRLUz7so4FPe0,5235
|
|
160
|
+
gruffpy/rule/test_quality/global_state_mutation_rule.py,sha256=wYb2PiUMAmdcFEOOBf93yx7lKANCwanblftweu5ALGI,3758
|
|
161
|
+
gruffpy/rule/test_quality/loop_assertion_without_message_rule.py,sha256=XnI9A_oO70WyRv7uZl9uPjH1nRLLIYQOgmhehMf4gyE,4263
|
|
162
|
+
gruffpy/rule/test_quality/loop_in_test_rule.py,sha256=fmUFFni1iLYlcU5STsc_Sv5KJDhIzB5_cdWk-ltDFQY,3870
|
|
163
|
+
gruffpy/rule/test_quality/magic_number_assertion_rule.py,sha256=RCsDRb89BZEaKbzwO7QbSJD6VVFZpfZm6LFEHZ_kfwQ,9965
|
|
164
|
+
gruffpy/rule/test_quality/mock_only_test_rule.py,sha256=O8qK_hBAyHaSfr3DwtmdF_0hK235tjPXaoUaYg2VU8s,4553
|
|
165
|
+
gruffpy/rule/test_quality/mock_without_expectation_rule.py,sha256=T52RLmaBANimY-aoTFALRCLGBbCqVLrm8KflMNBK-Vw,4797
|
|
166
|
+
gruffpy/rule/test_quality/mocking_domain_object_rule.py,sha256=YS_XbFjeBaoGEfhQhGnAXOqBTXXZ4gadPlnb0Bo4Y4k,5344
|
|
167
|
+
gruffpy/rule/test_quality/multiple_aaa_cycles_rule.py,sha256=3yQ4Zr8txr17Aorb_rL3ePMYtcBW26GdAp_py6APr6Q,4801
|
|
168
|
+
gruffpy/rule/test_quality/mystery_guest_rule.py,sha256=DjzHQLJtXapF6GYzp6iZYf4_70UjkRqcDtlr_BbB7YY,4646
|
|
169
|
+
gruffpy/rule/test_quality/naming_consistency_rule.py,sha256=Q3tpHlw6EpLtcVnEWu3RBLbyhsFDSuudNyqHyIJRJrI,4110
|
|
170
|
+
gruffpy/rule/test_quality/no_assertions_rule.py,sha256=tUybZcQWeKO-d6SXb1d6qINuHxpH6ewdtN8apMyxXi0,4119
|
|
171
|
+
gruffpy/rule/test_quality/parametrize_annotation_rule.py,sha256=f-OS6dwSOU3kPoG5mJjINnpRkkMSEXkm6mmD0r_Vogc,5302
|
|
172
|
+
gruffpy/rule/test_quality/private_reflection_rule.py,sha256=AFPdvEejIERzzX04z5LdxNvl2rFDiAI_acBWmyP4jwI,4396
|
|
173
|
+
gruffpy/rule/test_quality/pytest_coverage_source_missing_rule.py,sha256=5LGhR_00TsIfeQVihho07LGvH-WpVFh5Q9vjcLCSd0g,3697
|
|
174
|
+
gruffpy/rule/test_quality/pytest_deprecations_not_fatal_rule.py,sha256=Y6Zr1mw3BlLGCTjVYNnva-hoCPQEqDL6F-B1PmzvWFA,3622
|
|
175
|
+
gruffpy/rule/test_quality/pytest_strict_config_missing_rule.py,sha256=uoQM9yxjScHZK28Hq0zfGuX0ZarQU-7TXtpXZio-owQ,3836
|
|
176
|
+
gruffpy/rule/test_quality/repeated_structure_missing_parametrize_rule.py,sha256=EDMe1w6TQux81WYjXwTidlCT0T9QRLtXT8NUibxEtoI,5534
|
|
177
|
+
gruffpy/rule/test_quality/setup_bloat_rule.py,sha256=0SaJuhLf9om7MiBonE4auSjdXzXRg6H1bSiSsfFvH_w,4415
|
|
178
|
+
gruffpy/rule/test_quality/skipped_without_reason_rule.py,sha256=A8fpVs98lW_kVBHqpYxvt6EEqjAbJJbjGb3njRPt16s,5767
|
|
179
|
+
gruffpy/rule/test_quality/sleep_in_test_rule.py,sha256=Bhz-1d19VpIZbE9_aprbLE_eaTZ_GZwRxRiau4hf2Uk,4002
|
|
180
|
+
gruffpy/rule/test_quality/sut_not_called_rule.py,sha256=C2istLsO5toXBS-Z_eWI8oQHWlLG__oQ_jEC-jqP4aA,10350
|
|
181
|
+
gruffpy/rule/test_quality/tautological_type_assertion_rule.py,sha256=tEIkOOCdF66IFePA4fIx67psKGVOos1gyWYVTWIgqMI,5458
|
|
182
|
+
gruffpy/rule/test_quality/test_function_too_long_rule.py,sha256=YGAJwRKnzbas6wlzjRDRa_r66I7hlNCLcp-iWo_G4zw,4544
|
|
183
|
+
gruffpy/rule/test_quality/test_longer_than_sut_rule.py,sha256=V38fhzNO5k1KTClfa-r2ps-oK6LkSo9CbyfXkS4VHkc,5217
|
|
184
|
+
gruffpy/rule/test_quality/testdox_readability_rule.py,sha256=SmKuP7Ooek_Ev78NonHvLc40riaNExg1zsTsYVz2q3o,4194
|
|
185
|
+
gruffpy/rule/test_quality/trivial_assertion_rule.py,sha256=trdqaI5wHo3EnUWOA8y9xFfO917cjjL-rnWtF8zws2o,4124
|
|
186
|
+
gruffpy/rule/test_quality/trivial_snapshot_rule.py,sha256=BaPpImPtA81ZchAKD5vbBtVJrtUSeKyM4asJlJ3WWvw,4461
|
|
187
|
+
gruffpy/rule/test_quality/unused_mock_rule.py,sha256=CxhQ6-YMO4A2wU0we6CMJ3LWKQmdyKBLpF2H4r8qxL8,4256
|
|
188
|
+
gruffpy/rule/waste/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
189
|
+
gruffpy/rule/waste/commented_out_code_rule.py,sha256=BPdoJbi2Zp_HeSCG58dR1p-AsQCvHKOzsBkByfhn5WA,5616
|
|
190
|
+
gruffpy/rule/waste/empty_class_rule.py,sha256=Nk1WqHs2wYm2VFjG9JE9eQQ6yGSMlAM3AJf-SV1yrIU,5163
|
|
191
|
+
gruffpy/rule/waste/empty_function_rule.py,sha256=KP4I2YIBByOy_3jFWd3Xb8tBT0t72f5DIGEuL4Y3N-Q,4239
|
|
192
|
+
gruffpy/rule/waste/one_line_function_rule.py,sha256=tmHbdvhDSA514U-LRJFGULq6PkFIqmYNQCSI2xJaX1Y,6180
|
|
193
|
+
gruffpy/rule/waste/redundant_variable_rule.py,sha256=OvtgSClM30kTPHcHDMqQhWjlV0Pp9Gs_s7uuhwWwNgU,5407
|
|
194
|
+
gruffpy/rule/waste/unreachable_code_rule.py,sha256=eT-40p983RSknJMI3HQrIFCBRi1aY0jS7Wx4CUZEWKw,8880
|
|
195
|
+
gruffpy/rule/waste/unused_import_rule.py,sha256=lNYSo2D6TgRCjYkUy5lauwekA7BbEuY7gK9AJhdgYYw,7654
|
|
196
|
+
gruffpy/rule/waste/unused_parameter_rule.py,sha256=h8fTo2bRr-SNl0FQ7WRVDZbJ09psNNLSR-25nPxaTLQ,7270
|
|
197
|
+
gruffpy/scoring/__init__.py,sha256=Z4zlSlbEBTNm5iowqMbdyfAcorPOotHYA5W_8TJjT9U,633
|
|
198
|
+
gruffpy/scoring/composite_finding_factory.py,sha256=YnR3OwzzujRZhnz2bngYFNyz8JQyGvhwkP_3tdtIKqE,5396
|
|
199
|
+
gruffpy/scoring/file_score.py,sha256=23rC8qzCxPBclBNw_PWH3q24b9jiBzgOi1gPbQhF9_U,2105
|
|
200
|
+
gruffpy/scoring/grade.py,sha256=CmEixVlAzofqxnRN3rdgXCTjox8POmikLpiQwqaBa9Q,1456
|
|
201
|
+
gruffpy/scoring/pillar_score.py,sha256=H1xkPgJCULEbyZEqkAOkOjEiI1kqYDOv-7z0_npHx4g,1791
|
|
202
|
+
gruffpy/scoring/score_calculator.py,sha256=snztYUSFV3bG6rJu2eUoDKqJkhlxmjhTfp38wNvjgO0,7527
|
|
203
|
+
gruffpy/scoring/score_report.py,sha256=IY24J6VbyMN54yrR7sPSUVcC9sWTaFHTD8OERQ5pOnM,1731
|
|
204
|
+
gruffpy/source/__init__.py,sha256=BsoxqpzeaBwTL2aLO2Z6RgKshF96PnWszi72sv_SjM0,500
|
|
205
|
+
gruffpy/source/discovery.py,sha256=zfxXlHFgsuAQ6KCph2u0TkYUNrJ2p-oewvs2MhLOzkE,10707
|
|
206
|
+
gruffpy/source/gitignore.py,sha256=CueIOG7OmyljBoqnKTyfwd9mouAk56kbbzh7LMDWGVg,5649
|
|
207
|
+
gruffpy/source/source_file.py,sha256=9fIcV21JkxQ8uRk2xJXM1NpKXwXrAs3Cwbws2-uYXog,997
|
|
208
|
+
gruffpy/suppression/__init__.py,sha256=u1HcPfl87XUCGFj1ueRrjp58PiCuBOQXK-XLKfTG2I4,371
|
|
209
|
+
gruffpy/suppression/filter.py,sha256=GCp8AxGRMk_9xdM_Pz531Bd_5amVqie4j9Ww9i3UsuY,1205
|
|
210
|
+
gruffpy/suppression/parser.py,sha256=45NAoRkcrWMzcm70XgfoDyLTEGgIhiaqD9F5rp0G74g,9014
|
|
211
|
+
gruffpy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
212
|
+
gruff_py-0.1.0.dist-info/METADATA,sha256=UNwdKTH8-TEOZ2wHiCNOVRmyWm3Qavzei7WtIEwnooM,8068
|
|
213
|
+
gruff_py-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
214
|
+
gruff_py-0.1.0.dist-info/entry_points.txt,sha256=mxpZH9qHp-MeX0jFxXXxtgEUOqEYrztQKXWKj-h_SfY,46
|
|
215
|
+
gruff_py-0.1.0.dist-info/licenses/LICENSE.md,sha256=S32zDN0QnatTqL5uCIyvb2hmg4CXYPfWSWaHqsvdbv0,1073
|
|
216
|
+
gruff_py-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Matthew Hansen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
gruffpy/__init__.py
ADDED
gruffpy/__main__.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from gruffpy.analysis.report import AnalysisReport
|
|
2
|
+
from gruffpy.analysis.run_diagnostic import RunDiagnostic
|
|
3
|
+
from gruffpy.analysis.schema import (
|
|
4
|
+
ANALYSIS_SCHEMA_VERSION,
|
|
5
|
+
BASELINE_SCHEMA_VERSION,
|
|
6
|
+
HOTSPOT_SCHEMA_VERSION,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"ANALYSIS_SCHEMA_VERSION",
|
|
11
|
+
"AnalysisReport",
|
|
12
|
+
"BASELINE_SCHEMA_VERSION",
|
|
13
|
+
"HOTSPOT_SCHEMA_VERSION",
|
|
14
|
+
"RunDiagnostic",
|
|
15
|
+
]
|