crackerjack 0.18.12__tar.gz → 0.19.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. crackerjack-0.18.12/README.md → crackerjack-0.19.0/PKG-INFO +86 -0
  2. crackerjack-0.18.12/PKG-INFO → crackerjack-0.19.0/README.md +44 -41
  3. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.pre-commit-config.yaml +19 -13
  4. crackerjack-0.19.0/crackerjack/.ruff_cache/0.11.12/4441409093023629623 +0 -0
  5. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/__main__.py +18 -0
  6. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/crackerjack.py +44 -6
  7. crackerjack-0.19.0/crackerjack/pyproject.toml +233 -0
  8. {crackerjack-0.18.12 → crackerjack-0.19.0}/pyproject.toml +151 -140
  9. {crackerjack-0.18.12 → crackerjack-0.19.0}/tests/conftest.py +37 -1
  10. {crackerjack-0.18.12 → crackerjack-0.19.0}/tests/test_crackerjack.py +51 -19
  11. {crackerjack-0.18.12 → crackerjack-0.19.0}/tests/test_crackerjack_runner.py +4 -0
  12. {crackerjack-0.18.12 → crackerjack-0.19.0}/tests/test_main.py +56 -1
  13. crackerjack-0.18.12/crackerjack/pyproject.toml +0 -225
  14. {crackerjack-0.18.12 → crackerjack-0.19.0}/LICENSE +0 -0
  15. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.gitignore +0 -0
  16. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.libcst.codemod.yaml +0 -0
  17. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.pdm.toml +0 -0
  18. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.pytest_cache/.gitignore +0 -0
  19. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.pytest_cache/CACHEDIR.TAG +0 -0
  20. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.pytest_cache/README.md +0 -0
  21. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.pytest_cache/v/cache/nodeids +0 -0
  22. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.pytest_cache/v/cache/stepwise +0 -0
  23. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/.gitignore +0 -0
  24. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.1.11/3256171999636029978 +0 -0
  25. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.1.14/602324811142551221 +0 -0
  26. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.1.4/10355199064880463147 +0 -0
  27. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.1.6/15140459877605758699 +0 -0
  28. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.1.7/1790508110482614856 +0 -0
  29. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.1.9/17041001205004563469 +0 -0
  30. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.11.11/18187162184424859798 +0 -0
  31. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.11.12/16869036553936192448 +0 -0
  32. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.11.2/4070660268492669020 +0 -0
  33. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.11.3/9818742842212983150 +0 -0
  34. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.11.4/9818742842212983150 +0 -0
  35. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.11.6/3557596832929915217 +0 -0
  36. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.11.7/10386934055395314831 +0 -0
  37. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.11.7/3557596832929915217 +0 -0
  38. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.11.8/530407680854991027 +0 -0
  39. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.2.0/10047773857155985907 +0 -0
  40. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.2.1/8522267973936635051 +0 -0
  41. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.2.2/18053836298936336950 +0 -0
  42. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.3.0/12548816621480535786 +0 -0
  43. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.3.3/11081883392474770722 +0 -0
  44. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.3.4/676973378459347183 +0 -0
  45. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.3.5/16311176246009842383 +0 -0
  46. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.5.7/1493622539551733492 +0 -0
  47. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.5.7/6231957614044513175 +0 -0
  48. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.5.7/9932762556785938009 +0 -0
  49. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.6.0/11982804814124138945 +0 -0
  50. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.6.0/12055761203849489982 +0 -0
  51. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.6.2/1206147804896221174 +0 -0
  52. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.6.4/1206147804896221174 +0 -0
  53. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.6.5/1206147804896221174 +0 -0
  54. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.6.7/3657366982708166874 +0 -0
  55. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.6.9/285614542852677309 +0 -0
  56. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.7.1/1024065805990144819 +0 -0
  57. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.7.1/285614542852677309 +0 -0
  58. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.7.3/16061516852537040135 +0 -0
  59. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.8.4/16354268377385700367 +0 -0
  60. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.9.10/12813592349865671909 +0 -0
  61. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.9.10/923908772239632759 +0 -0
  62. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.9.3/13948373885254993391 +0 -0
  63. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.9.9/12813592349865671909 +0 -0
  64. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/0.9.9/8843823720003377982 +0 -0
  65. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/.ruff_cache/CACHEDIR.TAG +0 -0
  66. {crackerjack-0.18.12 → crackerjack-0.19.0}/crackerjack/__init__.py +0 -0
  67. {crackerjack-0.18.12 → crackerjack-0.19.0}/tests/TESTING.md +0 -0
  68. {crackerjack-0.18.12 → crackerjack-0.19.0}/tests/__init__.py +0 -0
  69. {crackerjack-0.18.12 → crackerjack-0.19.0}/tests/data/comments_sample.txt +0 -0
  70. {crackerjack-0.18.12 → crackerjack-0.19.0}/tests/data/docstrings_sample.txt +0 -0
  71. {crackerjack-0.18.12 → crackerjack-0.19.0}/tests/data/expected_comments_sample.txt +0 -0
  72. {crackerjack-0.18.12 → crackerjack-0.19.0}/tests/data/init.py +0 -0
  73. {crackerjack-0.18.12 → crackerjack-0.19.0}/tests/test_pytest_features.py +0 -0
@@ -1,3 +1,45 @@
1
+ Metadata-Version: 2.1
2
+ Name: crackerjack
3
+ Version: 0.19.0
4
+ Summary: Crackerjack: code quality toolkit
5
+ Keywords: bandit,black,creosote,mypy,pyright,pytest,refurb,ruff
6
+ Author-Email: lesleslie <les@wedgwoodwebworks.com>
7
+ Maintainer-Email: lesleslie <les@wedgwoodwebworks.com>
8
+ License: BSD-3-CLAUSE
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Environment :: Console
11
+ Classifier: License :: OSI Approved :: BSD License
12
+ Classifier: Operating System :: POSIX
13
+ Classifier: Programming Language :: Python
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Classifier: Topic :: Software Development :: Quality Assurance
18
+ Classifier: Topic :: Software Development :: Testing
19
+ Classifier: Topic :: Utilities
20
+ Classifier: Typing :: Typed
21
+ Project-URL: documentation, https://github.com/lesleslie/crackerjack
22
+ Project-URL: homepage, https://github.com/lesleslie/crackerjack
23
+ Project-URL: repository, https://github.com/lesleslie/crackerjack
24
+ Requires-Python: >=3.13
25
+ Requires-Dist: autotyping>=24.9
26
+ Requires-Dist: pdm>=2.24.2
27
+ Requires-Dist: pdm-bump>=0.9.12
28
+ Requires-Dist: pre-commit>=4.2
29
+ Requires-Dist: pydantic>=2.11.5
30
+ Requires-Dist: pytest>=8.3.5
31
+ Requires-Dist: pytest-asyncio>=1
32
+ Requires-Dist: pytest-benchmark>=5.1
33
+ Requires-Dist: pytest-cov>=6.1.1
34
+ Requires-Dist: pytest-mock>=3.14.1
35
+ Requires-Dist: pytest-timeout>=2.4
36
+ Requires-Dist: pytest-xdist>=3.7
37
+ Requires-Dist: rich>=14
38
+ Requires-Dist: tomli-w>=1.2
39
+ Requires-Dist: typer>=0.16
40
+ Requires-Dist: uv>=0.7.9
41
+ Description-Content-Type: text/markdown
42
+
1
43
  # Crackerjack: Elevate Your Python Development
2
44
 
3
45
  [![Code style: crackerjack](https://img.shields.io/badge/code%20style-crackerjack-000042)](https://github.com/lesleslie/crackerjack)
@@ -135,6 +177,44 @@ Crackerjack projects adhere to these guidelines:
135
177
  - **Clear Code:** Avoid overly complex code.
136
178
  - **Modular:** Functions should do one thing well.
137
179
 
180
+ ## Testing Features
181
+
182
+ Crackerjack provides advanced testing capabilities powered by pytest:
183
+
184
+ ### Standard Testing
185
+
186
+ - **Parallel Test Execution:** Tests run in parallel by default using pytest-xdist for faster execution
187
+ - **Timeout Protection:** All tests have a default 60-second timeout to prevent hanging tests
188
+ - **Coverage Reports:** Automatically generates test coverage reports with configurable thresholds
189
+
190
+ ### Benchmark Testing
191
+
192
+ Crackerjack includes benchmark testing capabilities:
193
+
194
+ - **Performance Measurement:** Run tests with `--benchmark` to measure execution time and performance
195
+ - **Regression Testing:** Use `--benchmark-regression` to detect performance regressions
196
+ - **Configurable Thresholds:** Set custom regression thresholds with `--benchmark-regression-threshold`
197
+ - **Compatibility Management:** Automatically disables parallel execution when running benchmarks
198
+ - **CI Integration:** Track performance across commits with benchmark history
199
+
200
+ When benchmarks are run, Crackerjack:
201
+ 1. Disables parallel test execution (as pytest-benchmark is incompatible with pytest-xdist)
202
+ 2. Configures the pytest-benchmark plugin with optimized settings
203
+ 3. Compares benchmark results against previous runs when regression testing is enabled
204
+ 4. Fails tests if performance decreases beyond the specified threshold
205
+
206
+ Example benchmark usage:
207
+ ```bash
208
+ # Run benchmarks
209
+ python -m crackerjack -t --benchmark
210
+
211
+ # Run benchmarks with regression testing (fail if >5% slower)
212
+ python -m crackerjack -t --benchmark-regression
213
+
214
+ # Run benchmarks with custom regression threshold (10%)
215
+ python -m crackerjack -t --benchmark-regression --benchmark-regression-threshold=10.0
216
+ ```
217
+
138
218
  ## Installation
139
219
 
140
220
  1. **Python:** Ensure you have Python 3.13 installed.
@@ -185,6 +265,9 @@ class MyOptions:
185
265
  # Process options
186
266
  self.clean = True # Clean code (remove docstrings, comments, etc.)
187
267
  self.test = True # Run tests using pytest
268
+ self.benchmark = False # Run tests in benchmark mode
269
+ self.benchmark_regression = False # Fail tests if benchmarks regress beyond threshold
270
+ self.benchmark_regression_threshold = 5.0 # Threshold percentage for benchmark regression
188
271
  self.skip_hooks = False # Skip running pre-commit hooks
189
272
 
190
273
  # Version and publishing options
@@ -225,6 +308,9 @@ runner.process(MyOptions())
225
308
  - `-s`, `--skip-hooks`: Skip running pre-commit hooks (useful with `-t`).
226
309
  - `-x`, `--clean`: Clean code by removing docstrings, line comments, and extra whitespace.
227
310
  - `-t`, `--test`: Run tests using `pytest`.
311
+ - `--benchmark`: Run tests in benchmark mode (disables parallel execution).
312
+ - `--benchmark-regression`: Fail tests if benchmarks regress beyond threshold.
313
+ - `--benchmark-regression-threshold`: Set threshold percentage for benchmark regression (default 5.0%).
228
314
  - `-a`, `--all`: Run with `-x -t -p <micro|minor|major> -c` development options.
229
315
  - `--ai-agent`: Enable AI agent mode with structured output (see [AI Agent Integration](#ai-agent-integration)).
230
316
  - `--help`: Display help.
@@ -1,44 +1,3 @@
1
- Metadata-Version: 2.1
2
- Name: crackerjack
3
- Version: 0.18.12
4
- Summary: Default template for PDM package
5
- Keywords: black,ruff,mypy,creosote,refurb,pyright,bandit,pytest
6
- Author-Email: lesleslie <les@wedgwoodwebworks.com>
7
- Maintainer-Email: lesleslie <les@wedgwoodwebworks.com>
8
- License: BSD-3-CLAUSE
9
- Classifier: Environment :: Console
10
- Classifier: Operating System :: POSIX
11
- Classifier: Programming Language :: Python
12
- Classifier: Programming Language :: Python :: 3.13
13
- Classifier: Development Status :: 4 - Beta
14
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
15
- Classifier: Topic :: Software Development :: Quality Assurance
16
- Classifier: Topic :: Software Development :: Testing
17
- Classifier: Topic :: Utilities
18
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
- Classifier: License :: OSI Approved :: BSD License
20
- Classifier: Typing :: Typed
21
- Project-URL: homepage, https://github.com/lesleslie/crackerjack
22
- Project-URL: documentation, https://github.com/lesleslie/crackerjack
23
- Project-URL: repository, https://github.com/lesleslie/crackerjack
24
- Requires-Python: >=3.13
25
- Requires-Dist: autotyping>=24.9.0
26
- Requires-Dist: pre-commit>=4.2.0
27
- Requires-Dist: pytest>=8.3.5
28
- Requires-Dist: pydantic>=2.11.5
29
- Requires-Dist: pdm-bump>=0.9.12
30
- Requires-Dist: pdm>=2.24.2
31
- Requires-Dist: uv>=0.7.9
32
- Requires-Dist: pytest-cov>=6.1.1
33
- Requires-Dist: pytest-mock>=3.14.1
34
- Requires-Dist: tomli-w>=1.2.0
35
- Requires-Dist: pytest-asyncio>=1.0.0
36
- Requires-Dist: rich>=14.0.0
37
- Requires-Dist: typer>=0.16.0
38
- Requires-Dist: pytest-timeout>=2.4.0
39
- Requires-Dist: pytest-xdist>=3.7.0
40
- Description-Content-Type: text/markdown
41
-
42
1
  # Crackerjack: Elevate Your Python Development
43
2
 
44
3
  [![Code style: crackerjack](https://img.shields.io/badge/code%20style-crackerjack-000042)](https://github.com/lesleslie/crackerjack)
@@ -176,6 +135,44 @@ Crackerjack projects adhere to these guidelines:
176
135
  - **Clear Code:** Avoid overly complex code.
177
136
  - **Modular:** Functions should do one thing well.
178
137
 
138
+ ## Testing Features
139
+
140
+ Crackerjack provides advanced testing capabilities powered by pytest:
141
+
142
+ ### Standard Testing
143
+
144
+ - **Parallel Test Execution:** Tests run in parallel by default using pytest-xdist for faster execution
145
+ - **Timeout Protection:** All tests have a default 60-second timeout to prevent hanging tests
146
+ - **Coverage Reports:** Automatically generates test coverage reports with configurable thresholds
147
+
148
+ ### Benchmark Testing
149
+
150
+ Crackerjack includes benchmark testing capabilities:
151
+
152
+ - **Performance Measurement:** Run tests with `--benchmark` to measure execution time and performance
153
+ - **Regression Testing:** Use `--benchmark-regression` to detect performance regressions
154
+ - **Configurable Thresholds:** Set custom regression thresholds with `--benchmark-regression-threshold`
155
+ - **Compatibility Management:** Automatically disables parallel execution when running benchmarks
156
+ - **CI Integration:** Track performance across commits with benchmark history
157
+
158
+ When benchmarks are run, Crackerjack:
159
+ 1. Disables parallel test execution (as pytest-benchmark is incompatible with pytest-xdist)
160
+ 2. Configures the pytest-benchmark plugin with optimized settings
161
+ 3. Compares benchmark results against previous runs when regression testing is enabled
162
+ 4. Fails tests if performance decreases beyond the specified threshold
163
+
164
+ Example benchmark usage:
165
+ ```bash
166
+ # Run benchmarks
167
+ python -m crackerjack -t --benchmark
168
+
169
+ # Run benchmarks with regression testing (fail if >5% slower)
170
+ python -m crackerjack -t --benchmark-regression
171
+
172
+ # Run benchmarks with custom regression threshold (10%)
173
+ python -m crackerjack -t --benchmark-regression --benchmark-regression-threshold=10.0
174
+ ```
175
+
179
176
  ## Installation
180
177
 
181
178
  1. **Python:** Ensure you have Python 3.13 installed.
@@ -226,6 +223,9 @@ class MyOptions:
226
223
  # Process options
227
224
  self.clean = True # Clean code (remove docstrings, comments, etc.)
228
225
  self.test = True # Run tests using pytest
226
+ self.benchmark = False # Run tests in benchmark mode
227
+ self.benchmark_regression = False # Fail tests if benchmarks regress beyond threshold
228
+ self.benchmark_regression_threshold = 5.0 # Threshold percentage for benchmark regression
229
229
  self.skip_hooks = False # Skip running pre-commit hooks
230
230
 
231
231
  # Version and publishing options
@@ -266,6 +266,9 @@ runner.process(MyOptions())
266
266
  - `-s`, `--skip-hooks`: Skip running pre-commit hooks (useful with `-t`).
267
267
  - `-x`, `--clean`: Clean code by removing docstrings, line comments, and extra whitespace.
268
268
  - `-t`, `--test`: Run tests using `pytest`.
269
+ - `--benchmark`: Run tests in benchmark mode (disables parallel execution).
270
+ - `--benchmark-regression`: Fail tests if benchmarks regress beyond threshold.
271
+ - `--benchmark-regression-threshold`: Set threshold percentage for benchmark regression (default 5.0%).
269
272
  - `-a`, `--all`: Run with `-x -t -p <micro|minor|major> -c` development options.
270
273
  - `--ai-agent`: Enable AI agent mode with structured output (see [AI Agent Integration](#ai-agent-integration)).
271
274
  - `--help`: Display help.
@@ -3,12 +3,14 @@ repos:
3
3
  rev: 2.24.2 # a PDM release exposing the hook
4
4
  hooks:
5
5
  - id: pdm-lock-check
6
- # - id: pdm-export
7
- # args: [ '-o', 'requirements.txt', '--without-hashes' ]
8
- # files: ^pdm.lock$
9
6
  - id: pdm-sync
10
7
  additional_dependencies:
11
8
  - keyring
9
+ - repo: https://github.com/astral-sh/uv-pre-commit
10
+ rev: 0.7.9
11
+ hooks:
12
+ - id: uv-lock
13
+ files: ^pyproject\.toml$
12
14
  - repo: https://github.com/pre-commit/pre-commit-hooks
13
15
  rev: v5.0.0
14
16
  hooks:
@@ -22,11 +24,24 @@ repos:
22
24
  name: check-toml
23
25
  - id: check-added-large-files
24
26
  name: check-added-large-files
27
+ - repo: https://github.com/Yelp/detect-secrets
28
+ rev: v1.5.0
29
+ hooks:
30
+ - id: detect-secrets
31
+ exclude: "pdm.lock"
32
+ - repo: https://github.com/abravalheri/validate-pyproject
33
+ rev: v0.24.1
34
+ hooks:
35
+ - id: validate-pyproject
36
+ - repo: https://github.com/tox-dev/pyproject-fmt
37
+ rev: "v2.6.0"
38
+ hooks:
39
+ - id: pyproject-fmt
25
40
  - repo: https://github.com/astral-sh/ruff-pre-commit
26
41
  rev: v0.11.12
27
42
  hooks:
28
- - id: ruff-format
29
43
  - id: ruff-check
44
+ - id: ruff-format
30
45
  - repo: https://github.com/jendrikseipp/vulture
31
46
  rev: 'v2.14'
32
47
  hooks:
@@ -35,10 +50,6 @@ repos:
35
50
  rev: v4.0.3
36
51
  hooks:
37
52
  - id: creosote
38
- - repo: https://github.com/ikamensh/flynt/
39
- rev: '1.0.1'
40
- hooks:
41
- - id: flynt
42
53
  - repo: https://github.com/codespell-project/codespell
43
54
  rev: v2.4.1
44
55
  hooks:
@@ -74,11 +85,6 @@ repos:
74
85
  rev: v1.1.401
75
86
  hooks:
76
87
  - id: pyright
77
- - repo: https://github.com/astral-sh/ruff-pre-commit
78
- rev: v0.11.12
79
- hooks:
80
- - id: ruff-check
81
- - id: ruff-format
82
88
  # - repo: https://github.com/pdoc3/pdoc
83
89
  # rev: master
84
90
  # hooks:
@@ -32,6 +32,8 @@ class Options(BaseModel):
32
32
  clean: bool = False
33
33
  test: bool = False
34
34
  benchmark: bool = False
35
+ benchmark_regression: bool = False
36
+ benchmark_regression_threshold: float = 5.0
35
37
  all: BumpOption | None = None
36
38
  ai_agent: bool = False
37
39
  create_pr: bool = False
@@ -90,6 +92,16 @@ cli_options = {
90
92
  "--benchmark",
91
93
  help="Run tests in benchmark mode (disables parallel execution).",
92
94
  ),
95
+ "benchmark_regression": typer.Option(
96
+ False,
97
+ "--benchmark-regression",
98
+ help="Fail tests if benchmarks regress beyond threshold.",
99
+ ),
100
+ "benchmark_regression_threshold": typer.Option(
101
+ 5.0,
102
+ "--benchmark-regression-threshold",
103
+ help="Maximum allowed performance regression percentage (default: 5.0%).",
104
+ ),
93
105
  "skip_hooks": typer.Option(
94
106
  False,
95
107
  "-s",
@@ -132,6 +144,10 @@ def main(
132
144
  clean: bool = cli_options["clean"],
133
145
  test: bool = cli_options["test"],
134
146
  benchmark: bool = cli_options["benchmark"],
147
+ benchmark_regression: bool = cli_options["benchmark_regression"],
148
+ benchmark_regression_threshold: float = cli_options[
149
+ "benchmark_regression_threshold"
150
+ ],
135
151
  skip_hooks: bool = cli_options["skip_hooks"],
136
152
  create_pr: bool = cli_options["create_pr"],
137
153
  ai_agent: bool = cli_options["ai_agent"],
@@ -148,6 +164,8 @@ def main(
148
164
  clean=clean,
149
165
  test=test,
150
166
  benchmark=benchmark,
167
+ benchmark_regression=benchmark_regression,
168
+ benchmark_regression_threshold=benchmark_regression_threshold,
151
169
  skip_hooks=skip_hooks,
152
170
  all=all,
153
171
  ai_agent=ai_agent,
@@ -39,7 +39,9 @@ class OptionsProtocol(t.Protocol):
39
39
  update_precommit: bool
40
40
  clean: bool
41
41
  test: bool
42
- benchmark: bool = False
42
+ benchmark: bool
43
+ benchmark_regression: bool
44
+ benchmark_regression_threshold: float
43
45
  publish: t.Any | None
44
46
  bump: t.Any | None
45
47
  all: t.Any | None
@@ -295,10 +297,10 @@ class ConfigManager:
295
297
  for k, v in {
296
298
  x: self.swap_package_name(y)
297
299
  for x, y in value.items()
298
- if isinstance(y, (str, list)) and "crackerjack" in str(y)
300
+ if isinstance(y, str | list) and "crackerjack" in str(y)
299
301
  }.items():
300
302
  settings[setting][k] = v
301
- elif isinstance(value, (str, list)) and "crackerjack" in str(value):
303
+ elif isinstance(value, str | list) and "crackerjack" in str(value):
302
304
  value = self.swap_package_name(value)
303
305
  settings[setting] = value
304
306
  if setting in (
@@ -492,9 +494,27 @@ class Crackerjack:
492
494
  self.code_cleaner.clean_files(tests_dir)
493
495
 
494
496
  def _prepare_pytest_command(self, options: OptionsProtocol) -> list[str]:
497
+ """Prepare pytest command with appropriate options.
498
+
499
+ Configures pytest command with:
500
+ - Standard options for formatting and output control
501
+ - Benchmark options when benchmark mode is enabled
502
+ - Benchmark regression options when regression testing is enabled
503
+ - Parallel execution via xdist for non-benchmark tests
504
+
505
+ Benchmark and parallel execution (xdist) are incompatible, so the command
506
+ automatically disables parallelism when benchmarks are enabled.
507
+
508
+ Args:
509
+ options: Command options with benchmark and test settings
510
+
511
+ Returns:
512
+ List of command-line arguments for pytest
513
+ """
495
514
  test = ["pytest"]
496
515
  if options.verbose:
497
516
  test.append("-v")
517
+
498
518
  test.extend(
499
519
  [
500
520
  "--capture=fd", # Capture stdout/stderr at file descriptor level
@@ -505,9 +525,27 @@ class Crackerjack:
505
525
  "--timeout=60", # 1-minute timeout for tests
506
526
  ]
507
527
  )
508
- # Add benchmark flag if enabled (disables parallel execution)
509
- if options.benchmark:
510
- test.append("--benchmark")
528
+
529
+ # Benchmarks and parallel testing are incompatible
530
+ # Handle them mutually exclusively
531
+ if options.benchmark or options.benchmark_regression:
532
+ # When running benchmarks, avoid parallel execution
533
+ # and apply specific benchmark options
534
+ if options.benchmark:
535
+ test.append("--benchmark")
536
+
537
+ # Add benchmark regression testing options if enabled
538
+ if options.benchmark_regression:
539
+ test.extend(
540
+ [
541
+ "--benchmark-regression",
542
+ f"--benchmark-regression-threshold={options.benchmark_regression_threshold}",
543
+ ]
544
+ )
545
+ else:
546
+ # No benchmarks - use parallel execution for speed
547
+ test.append("-xvs")
548
+
511
549
  return test
512
550
 
513
551
  def _setup_test_environment(self) -> None:
@@ -0,0 +1,233 @@
1
+ [build-system]
2
+ build-backend = "pdm.backend"
3
+ requires = [ "pdm-backend" ]
4
+
5
+ [project]
6
+ name = "crackerjack"
7
+ version = "0.18.12"
8
+ description = "Crackerjack: code quality toolkit"
9
+ readme = "README.md"
10
+ keywords = [
11
+ "bandit",
12
+ "black",
13
+ "creosote",
14
+ "mypy",
15
+ "pyright",
16
+ "pytest",
17
+ "refurb",
18
+ "ruff",
19
+ ]
20
+ license.text = "BSD-3-CLAUSE"
21
+ maintainers = [
22
+ { name = "lesleslie", email = "les@wedgwoodwebworks.com" },
23
+ ]
24
+
25
+ authors = [
26
+ { name = "lesleslie", email = "les@wedgwoodwebworks.com" },
27
+ ]
28
+ requires-python = ">=3.13"
29
+ classifiers = [
30
+ "Development Status :: 4 - Beta",
31
+ "Environment :: Console",
32
+ "License :: OSI Approved :: BSD License",
33
+ "Operating System :: POSIX",
34
+ "Programming Language :: Python",
35
+ "Programming Language :: Python :: 3 :: Only",
36
+ "Programming Language :: Python :: 3.13",
37
+ "Topic :: Software Development :: Libraries :: Python Modules",
38
+ "Topic :: Software Development :: Quality Assurance",
39
+ "Topic :: Software Development :: Testing",
40
+ "Topic :: Utilities",
41
+ "Typing :: Typed",
42
+ ]
43
+ dependencies = [
44
+ "autotyping>=24.9",
45
+ "pdm>=2.24.2",
46
+ "pdm-bump>=0.9.12",
47
+ "pre-commit>=4.2",
48
+ "pydantic>=2.11.5",
49
+ "pytest>=8.3.5",
50
+ "pytest-asyncio>=1",
51
+ "pytest-benchmark>=5.1",
52
+ "pytest-cov>=6.1.1",
53
+ "pytest-mock>=3.14.1",
54
+ "pytest-timeout>=2.4",
55
+ "pytest-xdist>=3.7",
56
+ "rich>=14",
57
+ "tomli-w>=1.2",
58
+ "typer>=0.16",
59
+ "uv>=0.7.9",
60
+ ]
61
+ urls.documentation = "https://github.com/lesleslie/crackerjack"
62
+ urls.homepage = "https://github.com/lesleslie/crackerjack"
63
+ urls.repository = "https://github.com/lesleslie/crackerjack"
64
+
65
+ [tool.ruff]
66
+ target-version = "py313"
67
+ line-length = 88
68
+ fix = true
69
+ unsafe-fixes = true
70
+
71
+ show-fixes = true
72
+ output-format = "full"
73
+ format.docstring-code-format = true
74
+ lint.extend-select = [
75
+ "C901",
76
+ "D",
77
+ "F", # pyflakes
78
+ "I",
79
+ "UP", # pyupgrade (includes F-string conversion)
80
+ ]
81
+ lint.ignore = [
82
+ "D100",
83
+ "D101",
84
+ "D102",
85
+ "D103",
86
+ "D104",
87
+ "D105",
88
+ "D106",
89
+ "D107",
90
+ "F821",
91
+ ]
92
+ lint.fixable = [ "ALL" ]
93
+ lint.unfixable = [ ]
94
+ lint.isort.no-lines-before = [
95
+ "first-party",
96
+ ]
97
+ lint.mccabe.max-complexity = 13
98
+ lint.pydocstyle.convention = "google"
99
+
100
+ [tool.codespell]
101
+ skip = "*/data/*"
102
+ quiet-level = 3
103
+ ignore-words-list = "crate,uptodate,nd"
104
+
105
+ [tool.pytest.ini_options]
106
+ # Core pytest configuration
107
+ asyncio_mode = "auto"
108
+ asyncio_default_fixture_loop_scope = "function"
109
+ python_files = [ "test_*.py", "*_test.py" ]
110
+ testpaths = [ "tests", "crackerjack" ]
111
+ python_classes = [ "Test*" ]
112
+ python_functions = [ "test_*" ]
113
+
114
+ # Markers
115
+ markers = [
116
+ "unit: marks test as a unit test",
117
+ "benchmark: mark test as a benchmark (disables parallel execution)",
118
+ ]
119
+
120
+ # Default timeout settings
121
+ timeout = 60
122
+ timeout_method = "thread"
123
+
124
+ # Test command options
125
+ addopts = "--cov=crackerjack --cov-report=term --cov-fail-under=80"
126
+
127
+ [tool.pytest.benchmark]
128
+ disable_gc = true
129
+ warmup = false
130
+ warmup_iterations = 0
131
+
132
+ [tool.coverage.run]
133
+ branch = false
134
+ source = [ "crackerjack" ]
135
+ data_file = ".coverage"
136
+ parallel = false
137
+ omit = [
138
+ "*/tests/*",
139
+ "*/site-packages/*",
140
+ "*/__pycache__/*",
141
+ "*/__init__.py",
142
+ "*/_version.py",
143
+ "*/conftest.py",
144
+ "*/test_*.py",
145
+ "*/_test.py",
146
+ ]
147
+
148
+ [tool.coverage.report]
149
+ exclude_also = [
150
+ "pragma: no cover",
151
+ "def __repr__",
152
+ "raise NotImplementedError",
153
+ "if __name__ == .__main__.:",
154
+ "pass",
155
+ "raise ImportError",
156
+ "except ImportError",
157
+ "def __str__",
158
+ "@abstractmethod",
159
+ ]
160
+ ignore_errors = false
161
+
162
+ [tool.pyright]
163
+ verboseOutput = true
164
+ include = [
165
+ "crackerjack",
166
+ ]
167
+ exclude = [
168
+ "scratch",
169
+ ]
170
+ extraPaths = [
171
+ ".venv/lib/python3.13/site-packages/",
172
+ ]
173
+ typeCheckingMode = "strict"
174
+ reportMissingTypeStubs = false
175
+ reportOptionalMemberAccess = false
176
+ reportOptionalCall = false
177
+ reportUnknownMemberType = false
178
+ reportUnknownVariableType = false
179
+ reportUnknownArgumentType = false
180
+ reportInvalidTypeForm = false
181
+ reportUnknownLambdaType = false
182
+ reportUnknownParameterType = "warning"
183
+ reportPrivateUsage = false
184
+ pythonVersion = "3.13"
185
+ pythonPlatform = "Darwin"
186
+
187
+ [tool.vulture]
188
+ min_confidence = 86
189
+ paths = [ "crackerjack" ]
190
+ ignore_names = [ "cls" ]
191
+
192
+ [tool.creosote]
193
+ paths = [
194
+ "crackerjack",
195
+ ]
196
+ deps-file = "pyproject.toml"
197
+ exclude-deps = [
198
+ "pdm-bump",
199
+ "autotyping",
200
+ "pre-commit",
201
+ "pytest",
202
+ "pytest-asyncio",
203
+ "pytest-cov",
204
+ "pytest-mock",
205
+ "pytest-xdist",
206
+ "pytest-benchmark",
207
+ "pdm",
208
+ "pyfiglet",
209
+ "pyyaml",
210
+ "uv",
211
+ "tomli-w",
212
+ "google-crc32c",
213
+ "pytest-timeout",
214
+ ]
215
+
216
+ [tool.refurb]
217
+ enable_all = true
218
+
219
+ [tool.bandit]
220
+ target = [
221
+ "crackerjack",
222
+ ]
223
+ skips = [
224
+ "B101",
225
+ "B301",
226
+ "B311",
227
+ "B403",
228
+ "B404",
229
+ "B602",
230
+ "B603",
231
+ "B607",
232
+ "B704",
233
+ ]