crackerjack 0.20.0__py3-none-any.whl → 0.20.2__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.
@@ -34,7 +34,7 @@ repos:
34
34
  - keyring
35
35
 
36
36
  - repo: https://github.com/astral-sh/uv-pre-commit
37
- rev: 0.7.10
37
+ rev: 0.7.12
38
38
  hooks:
39
39
  - id: uv-lock
40
40
  files: ^pyproject\.toml$
@@ -55,7 +55,7 @@ repos:
55
55
  - tomli
56
56
 
57
57
  - repo: https://github.com/astral-sh/ruff-pre-commit
58
- rev: v0.11.12
58
+ rev: v0.11.13
59
59
  hooks:
60
60
  - id: ruff-check
61
61
  - id: ruff-format
crackerjack/__main__.py CHANGED
@@ -34,6 +34,8 @@ class Options(BaseModel):
34
34
  benchmark: bool = False
35
35
  benchmark_regression: bool = False
36
36
  benchmark_regression_threshold: float = 5.0
37
+ test_workers: int = 0
38
+ test_timeout: int = 0
37
39
  all: BumpOption | None = None
38
40
  ai_agent: bool = False
39
41
  create_pr: bool = False
@@ -103,6 +105,16 @@ cli_options = {
103
105
  "--benchmark-regression-threshold",
104
106
  help="Maximum allowed performance regression percentage (default: 5.0%).",
105
107
  ),
108
+ "test_workers": typer.Option(
109
+ 0,
110
+ "--test-workers",
111
+ help="Number of parallel workers for running tests (0 = auto-detect, 1 = disable parallelization).",
112
+ ),
113
+ "test_timeout": typer.Option(
114
+ 0,
115
+ "--test-timeout",
116
+ help="Timeout in seconds for individual tests (0 = use default based on project size).",
117
+ ),
106
118
  "skip_hooks": typer.Option(
107
119
  False,
108
120
  "-s",
@@ -154,6 +166,8 @@ def main(
154
166
  benchmark_regression_threshold: float = cli_options[
155
167
  "benchmark_regression_threshold"
156
168
  ],
169
+ test_workers: int = cli_options["test_workers"],
170
+ test_timeout: int = cli_options["test_timeout"],
157
171
  skip_hooks: bool = cli_options["skip_hooks"],
158
172
  create_pr: bool = cli_options["create_pr"],
159
173
  rich_ui: bool = cli_options["rich_ui"],
@@ -173,6 +187,8 @@ def main(
173
187
  benchmark=benchmark,
174
188
  benchmark_regression=benchmark_regression,
175
189
  benchmark_regression_threshold=benchmark_regression_threshold,
190
+ test_workers=test_workers,
191
+ test_timeout=test_timeout,
176
192
  skip_hooks=skip_hooks,
177
193
  all=all,
178
194
  ai_agent=ai_agent,
@@ -1,8 +1,10 @@
1
1
  import io
2
2
  import os
3
3
  import platform
4
+ import queue
4
5
  import re
5
6
  import subprocess
7
+ import threading
6
8
  import time
7
9
  import tokenize
8
10
  import typing as t
@@ -42,6 +44,8 @@ class OptionsProtocol(t.Protocol):
42
44
  benchmark: bool
43
45
  benchmark_regression: bool
44
46
  benchmark_regression_threshold: float
47
+ test_workers: int = 0
48
+ test_timeout: int = 0
45
49
  publish: t.Any | None
46
50
  bump: t.Any | None
47
51
  all: t.Any | None
@@ -683,6 +687,22 @@ class Crackerjack:
683
687
  if options.verbose:
684
688
  test.append("-v")
685
689
 
690
+ # Detect project size to adjust timeouts and parallelization
691
+ project_size = self._detect_project_size()
692
+
693
+ # User can override the timeout, otherwise use project size to determine
694
+ if options.test_timeout > 0:
695
+ test_timeout = options.test_timeout
696
+ else:
697
+ # Use a longer timeout for larger projects
698
+ test_timeout = (
699
+ 300
700
+ if project_size == "large"
701
+ else 120
702
+ if project_size == "medium"
703
+ else 60
704
+ )
705
+
686
706
  test.extend(
687
707
  [
688
708
  "--capture=fd", # Capture stdout/stderr at file descriptor level
@@ -690,7 +710,7 @@ class Crackerjack:
690
710
  "--no-header", # Reduce output noise
691
711
  "--disable-warnings", # Disable warning capture
692
712
  "--durations=0", # Show slowest tests to identify potential hanging tests
693
- "--timeout=60", # 1-minute timeout for tests
713
+ f"--timeout={test_timeout}", # Dynamic timeout based on project size or user override
694
714
  ]
695
715
  )
696
716
 
@@ -711,11 +731,60 @@ class Crackerjack:
711
731
  ]
712
732
  )
713
733
  else:
714
- # No benchmarks - use parallel execution for speed
715
- test.append("-xvs")
734
+ # Use user-specified number of workers if provided
735
+ if options.test_workers > 0:
736
+ # User explicitly set number of workers
737
+ if options.test_workers == 1:
738
+ # Single worker means no parallelism, just use normal pytest mode
739
+ test.append("-vs")
740
+ else:
741
+ # Use specified number of workers
742
+ test.extend(["-xvs", "-n", str(options.test_workers)])
743
+ else:
744
+ # Auto-detect based on project size
745
+ if project_size == "large":
746
+ # For large projects, use a fixed number of workers to avoid overwhelming the system
747
+ test.extend(
748
+ ["-xvs", "-n", "2"]
749
+ ) # Only 2 parallel processes for large projects
750
+ elif project_size == "medium":
751
+ test.extend(
752
+ ["-xvs", "-n", "auto"]
753
+ ) # Auto-detect number of processes but limit it
754
+ else:
755
+ test.append("-xvs") # Default behavior for small projects
716
756
 
717
757
  return test
718
758
 
759
+ def _detect_project_size(self) -> str:
760
+ """Detect the approximate size of the project to adjust test parameters.
761
+
762
+ Returns:
763
+ "small", "medium", or "large" based on codebase size
764
+ """
765
+ # Check for known large projects by name
766
+ if self.pkg_name in ("acb", "fastblocks"):
767
+ return "large"
768
+
769
+ # Count Python files to estimate project size
770
+ try:
771
+ py_files = list(self.pkg_path.rglob("*.py"))
772
+ test_files = list(self.pkg_path.rglob("test_*.py"))
773
+
774
+ total_files = len(py_files)
775
+ num_test_files = len(test_files)
776
+
777
+ # Rough heuristics for project size
778
+ if total_files > 100 or num_test_files > 50:
779
+ return "large"
780
+ elif total_files > 50 or num_test_files > 20:
781
+ return "medium"
782
+ else:
783
+ return "small"
784
+ except Exception:
785
+ # Default to medium in case of error
786
+ return "medium"
787
+
719
788
  def _setup_test_environment(self) -> None:
720
789
  os.environ["PYTHONASYNCIO_DEBUG"] = "0" # Disable asyncio debug mode
721
790
  os.environ["RUNNING_UNDER_CRACKERJACK"] = "1" # Signal to conftest.py
@@ -725,9 +794,29 @@ class Crackerjack:
725
794
  def _run_pytest_process(
726
795
  self, test_command: list[str]
727
796
  ) -> subprocess.CompletedProcess[str]:
797
+ import queue
798
+
728
799
  from .errors import ErrorCode, ExecutionError, handle_error
729
800
 
730
801
  try:
802
+ # Detect project size to determine appropriate timeout
803
+ project_size = self._detect_project_size()
804
+ # Longer timeouts for larger projects
805
+ global_timeout = (
806
+ 1200
807
+ if project_size == "large"
808
+ else 600
809
+ if project_size == "medium"
810
+ else 300
811
+ )
812
+
813
+ # Show timeout information
814
+ self.console.print(f"[blue]Project size detected as: {project_size}[/blue]")
815
+ self.console.print(
816
+ f"[blue]Using global timeout of {global_timeout} seconds[/blue]"
817
+ )
818
+
819
+ # Use non-blocking IO to avoid deadlocks
731
820
  process = subprocess.Popen(
732
821
  test_command,
733
822
  stdout=subprocess.PIPE,
@@ -736,21 +825,62 @@ class Crackerjack:
736
825
  bufsize=1,
737
826
  universal_newlines=True,
738
827
  )
739
- timeout = 300
740
- start_time = time.time()
828
+
741
829
  stdout_data = []
742
830
  stderr_data = []
831
+
832
+ # Output collection queues
833
+ stdout_queue = queue.Queue()
834
+ stderr_queue = queue.Queue()
835
+
836
+ # Use separate threads to read from stdout and stderr to prevent deadlocks
837
+ def read_output(
838
+ pipe: t.TextIO,
839
+ output_queue: "queue.Queue[str]",
840
+ data_collector: list[str],
841
+ ) -> None:
842
+ try:
843
+ for line in iter(pipe.readline, ""):
844
+ output_queue.put(line)
845
+ data_collector.append(line)
846
+ except (OSError, ValueError):
847
+ # Pipe has been closed
848
+ pass
849
+ finally:
850
+ pipe.close()
851
+
852
+ # Start output reader threads
853
+ stdout_thread = threading.Thread(
854
+ target=read_output,
855
+ args=(process.stdout, stdout_queue, stdout_data),
856
+ daemon=True,
857
+ )
858
+ stderr_thread = threading.Thread(
859
+ target=read_output,
860
+ args=(process.stderr, stderr_queue, stderr_data),
861
+ daemon=True,
862
+ )
863
+
864
+ stdout_thread.start()
865
+ stderr_thread.start()
866
+
867
+ # Start time for timeout tracking
868
+ start_time = time.time()
869
+
870
+ # Process is running, monitor and display output until completion or timeout
743
871
  while process.poll() is None:
744
- if time.time() - start_time > timeout:
872
+ # Check for timeout
873
+ elapsed = time.time() - start_time
874
+ if elapsed > global_timeout:
745
875
  error = ExecutionError(
746
- message="Test execution timed out after 5 minutes.",
876
+ message=f"Test execution timed out after {global_timeout // 60} minutes.",
747
877
  error_code=ErrorCode.COMMAND_TIMEOUT,
748
- details=f"Command: {' '.join(test_command)}\nTimeout: {timeout} seconds",
878
+ details=f"Command: {' '.join(test_command)}\nTimeout: {global_timeout} seconds",
749
879
  recovery="Check for infinite loops or deadlocks in your tests. Consider increasing the timeout or optimizing your tests.",
750
880
  )
751
881
 
752
882
  self.console.print(
753
- "[red]Test execution timed out after 5 minutes. Terminating...[/red]"
883
+ f"[red]Test execution timed out after {global_timeout // 60} minutes. Terminating...[/red]"
754
884
  )
755
885
  process.terminate()
756
886
  try:
@@ -762,26 +892,27 @@ class Crackerjack:
762
892
  )
763
893
  break
764
894
 
765
- if process.stdout:
766
- line = process.stdout.readline()
767
- if line:
768
- stdout_data.append(line)
769
- self.console.print(line, end="")
770
- if process.stderr:
771
- line = process.stderr.readline()
772
- if line:
773
- stderr_data.append(line)
774
- self.console.print(f"[red]{line}[/red]", end="")
775
- time.sleep(0.1)
776
-
777
- if process.stdout:
778
- for line in process.stdout:
779
- stdout_data.append(line)
780
- self.console.print(line, end="")
781
- if process.stderr:
782
- for line in process.stderr:
783
- stderr_data.append(line)
784
- self.console.print(f"[red]{line}[/red]", end="")
895
+ # Print any available output
896
+ self._process_output_queue(stdout_queue, stderr_queue)
897
+
898
+ # Small sleep to avoid CPU spinning but still be responsive
899
+ time.sleep(0.05)
900
+
901
+ # Periodically output a heartbeat for very long-running tests
902
+ if elapsed > 60 and elapsed % 60 < 0.1: # Roughly every minute
903
+ self.console.print(
904
+ f"[blue]Tests still running, elapsed time: {int(elapsed)} seconds...[/blue]"
905
+ )
906
+
907
+ # Process has exited, get remaining output
908
+ time.sleep(0.1) # Allow threads to flush final output
909
+ self._process_output_queue(stdout_queue, stderr_queue)
910
+
911
+ # Ensure threads are done
912
+ if stdout_thread.is_alive():
913
+ stdout_thread.join(1.0)
914
+ if stderr_thread.is_alive():
915
+ stderr_thread.join(1.0)
785
916
 
786
917
  returncode = process.returncode or 0
787
918
  stdout = "".join(stdout_data)
@@ -807,6 +938,28 @@ class Crackerjack:
807
938
 
808
939
  return subprocess.CompletedProcess(test_command, 1, "", str(e))
809
940
 
941
+ def _process_output_queue(
942
+ self, stdout_queue: "queue.Queue[str]", stderr_queue: "queue.Queue[str]"
943
+ ) -> None:
944
+ """Process and display output from the queues without blocking."""
945
+ # Process stdout
946
+ while not stdout_queue.empty():
947
+ try:
948
+ line = stdout_queue.get_nowait()
949
+ if line:
950
+ self.console.print(line, end="")
951
+ except queue.Empty:
952
+ break
953
+
954
+ # Process stderr
955
+ while not stderr_queue.empty():
956
+ try:
957
+ line = stderr_queue.get_nowait()
958
+ if line:
959
+ self.console.print(f"[red]{line}[/red]", end="")
960
+ except queue.Empty:
961
+ break
962
+
810
963
  def _report_test_results(
811
964
  self, result: subprocess.CompletedProcess[str], ai_agent: str
812
965
  ) -> None:
@@ -4,7 +4,7 @@ requires = [ "pdm-backend" ]
4
4
 
5
5
  [project]
6
6
  name = "crackerjack"
7
- version = "0.19.8"
7
+ version = "0.20.1"
8
8
  description = "Crackerjack: code quality toolkit"
9
9
  readme = "README.md"
10
10
  keywords = [
@@ -56,7 +56,7 @@ dependencies = [
56
56
  "rich>=14",
57
57
  "tomli-w>=1.2",
58
58
  "typer>=0.16",
59
- "uv>=0.7.10",
59
+ "uv>=0.7.12",
60
60
  ]
61
61
  urls.documentation = "https://github.com/lesleslie/crackerjack"
62
62
  urls.homepage = "https://github.com/lesleslie/crackerjack"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: crackerjack
3
- Version: 0.20.0
3
+ Version: 0.20.2
4
4
  Summary: Crackerjack: code quality toolkit
5
5
  Keywords: bandit,black,creosote,mypy,pyright,pytest,refurb,ruff
6
6
  Author-Email: lesleslie <les@wedgwoodwebworks.com>
@@ -37,7 +37,7 @@ Requires-Dist: pytest-xdist>=3.7
37
37
  Requires-Dist: rich>=14
38
38
  Requires-Dist: tomli-w>=1.2
39
39
  Requires-Dist: typer>=0.16
40
- Requires-Dist: uv>=0.7.10
40
+ Requires-Dist: uv>=0.7.12
41
41
  Description-Content-Type: text/markdown
42
42
 
43
43
  # Crackerjack: Elevate Your Python Development
@@ -192,9 +192,35 @@ Crackerjack provides advanced testing capabilities powered by pytest:
192
192
  ### Standard Testing
193
193
 
194
194
  - **Parallel Test Execution:** Tests run in parallel by default using pytest-xdist for faster execution
195
- - **Timeout Protection:** All tests have a default 60-second timeout to prevent hanging tests
195
+ - **Smart Parallelization:** Automatically adjusts the number of worker processes based on project size
196
+ - **Timeout Protection:** Tests have dynamic timeouts based on project size to prevent hanging tests
196
197
  - **Coverage Reports:** Automatically generates test coverage reports with configurable thresholds
197
198
 
199
+ ### Advanced Test Configuration
200
+
201
+ Crackerjack offers fine-grained control over test execution:
202
+
203
+ - **Worker Control:** Set the number of parallel workers with `--test-workers` (0 = auto-detect, 1 = disable parallelization)
204
+ - **Timeout Control:** Customize test timeouts with `--test-timeout` (in seconds)
205
+ - **Project Size Detection:** Automatically detects project size and adjusts timeout and parallelization settings
206
+ - **Deadlock Prevention:** Uses advanced threading techniques to prevent deadlocks in test output processing
207
+ - **Progress Tracking:** Shows periodic heartbeat messages for long-running tests
208
+
209
+ Example test execution options:
210
+ ```bash
211
+ # Run tests with a single worker (no parallelization)
212
+ python -m crackerjack -t --test-workers=1
213
+
214
+ # Run tests with a specific number of workers (e.g., 4)
215
+ python -m crackerjack -t --test-workers=4
216
+
217
+ # Run tests with a custom timeout (5 minutes per test)
218
+ python -m crackerjack -t --test-timeout=300
219
+
220
+ # Combine options for maximum control
221
+ python -m crackerjack -t --test-workers=2 --test-timeout=600
222
+ ```
223
+
198
224
  ### Benchmark Testing
199
225
 
200
226
  Crackerjack includes benchmark testing capabilities:
@@ -278,10 +304,16 @@ class MyOptions:
278
304
  # Process options
279
305
  self.clean = True # Clean code (remove docstrings, comments, etc.)
280
306
  self.test = True # Run tests using pytest
307
+ self.skip_hooks = False # Skip running pre-commit hooks
308
+
309
+ # Test execution options
310
+ self.test_workers = 2 # Number of parallel workers (0 = auto-detect, 1 = disable parallelization)
311
+ self.test_timeout = 120 # Timeout in seconds for individual tests (0 = use default based on project size)
312
+
313
+ # Benchmark options
281
314
  self.benchmark = False # Run tests in benchmark mode
282
315
  self.benchmark_regression = False # Fail tests if benchmarks regress beyond threshold
283
316
  self.benchmark_regression_threshold = 5.0 # Threshold percentage for benchmark regression
284
- self.skip_hooks = False # Skip running pre-commit hooks
285
317
 
286
318
  # Version and publishing options
287
319
  self.publish = None # Publish to PyPI (micro, minor, major)
@@ -321,6 +353,8 @@ runner.process(MyOptions())
321
353
  - `-s`, `--skip-hooks`: Skip running pre-commit hooks (useful with `-t`).
322
354
  - `-x`, `--clean`: Clean code by removing docstrings, line comments, and extra whitespace.
323
355
  - `-t`, `--test`: Run tests using `pytest`.
356
+ - `--test-workers`: Set the number of parallel workers for testing (0 = auto-detect, 1 = disable parallelization).
357
+ - `--test-timeout`: Set the timeout in seconds for individual tests (0 = use default based on project size).
324
358
  - `--benchmark`: Run tests in benchmark mode (disables parallel execution).
325
359
  - `--benchmark-regression`: Fail tests if benchmarks regress beyond threshold.
326
360
  - `--benchmark-regression-threshold`: Set threshold percentage for benchmark regression (default 5.0%).
@@ -356,6 +390,28 @@ runner.process(MyOptions())
356
390
  python -m crackerjack -t -s
357
391
  ```
358
392
 
393
+ #### Test Execution Options
394
+
395
+ - **Single-Process Testing** - Run tests sequentially (no parallelization):
396
+ ```bash
397
+ python -m crackerjack -t --test-workers=1
398
+ ```
399
+
400
+ - **Customized Parallel Testing** - Run tests with a specific number of workers:
401
+ ```bash
402
+ python -m crackerjack -t --test-workers=4
403
+ ```
404
+
405
+ - **Long-Running Tests** - Increase test timeout for complex tests:
406
+ ```bash
407
+ python -m crackerjack -t --test-timeout=600
408
+ ```
409
+
410
+ - **Optimized for Large Projects** - Reduce workers and increase timeout for large codebases:
411
+ ```bash
412
+ python -m crackerjack -t --test-workers=2 --test-timeout=300
413
+ ```
414
+
359
415
  #### Version Management
360
416
 
361
417
  - **Bump and Publish** - Bump version and publish to PyPI:
@@ -1,11 +1,11 @@
1
- crackerjack-0.20.0.dist-info/METADATA,sha256=t81zxXt6ujA39tWQNwQYgv2at3gFYDZ3iKudx6N23c8,24058
2
- crackerjack-0.20.0.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
3
- crackerjack-0.20.0.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
4
- crackerjack-0.20.0.dist-info/licenses/LICENSE,sha256=fDt371P6_6sCu7RyqiZH_AhT1LdN3sN1zjBtqEhDYCk,1531
1
+ crackerjack-0.20.2.dist-info/METADATA,sha256=JxIeMJ6WVmtpOW1CYU2eP6mLxb36wqiZNOIHYWcVZ8I,26354
2
+ crackerjack-0.20.2.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
3
+ crackerjack-0.20.2.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
4
+ crackerjack-0.20.2.dist-info/licenses/LICENSE,sha256=fDt371P6_6sCu7RyqiZH_AhT1LdN3sN1zjBtqEhDYCk,1531
5
5
  crackerjack/.gitignore,sha256=4DYG7ZoVEHR5Tv1gQliRWmsNku5Fw2_k756cG_t12Cg,185
6
6
  crackerjack/.libcst.codemod.yaml,sha256=a8DlErRAIPV1nE6QlyXPAzTOgkB24_spl2E9hphuf5s,772
7
7
  crackerjack/.pdm.toml,sha256=dZe44HRcuxxCFESGG8SZIjmc-cGzSoyK3Hs6t4NYA8w,23
8
- crackerjack/.pre-commit-config.yaml,sha256=5xN133ZjeVpOlWRcoOOfu-tdzQvfWfRaZeVLM1B-XNk,2935
8
+ crackerjack/.pre-commit-config.yaml,sha256=r7T5Valb9febof0yH4tBu2u8PhQRfQBmf1W-gJIkJSI,2935
9
9
  crackerjack/.pytest_cache/.gitignore,sha256=Ptcxtl0GFQwTji2tsL4Gl1UIiKa0frjEXsya26i46b0,37
10
10
  crackerjack/.pytest_cache/CACHEDIR.TAG,sha256=N9yI75oKvt2-gQU6bdj9-xOvthMEXqHrSlyBWnSjveQ,191
11
11
  crackerjack/.pytest_cache/README.md,sha256=c_1vzN2ALEGaay2YPWwxc7fal1WKxLWJ7ewt_kQ9ua0,302
@@ -23,6 +23,7 @@ crackerjack/.ruff_cache/0.11.12/16869036553936192448,sha256=pYYUCDrYh7fPq8xkFLxv
23
23
  crackerjack/.ruff_cache/0.11.12/1867267426380906393,sha256=2w4M0Lrjd9flwuq6uJxehTbm7FVUcK5sL2sz1gS2Yvo,256
24
24
  crackerjack/.ruff_cache/0.11.12/4240757255861806333,sha256=uph5uIRG-XnF7ywAEcCxqqgIkWALPCvJFcwCgnNfTI4,77
25
25
  crackerjack/.ruff_cache/0.11.12/4441409093023629623,sha256=eHrESew3XCFJ2WqmKvtGLO1r4mY5Q_mv7yGlDmM1sSc,153
26
+ crackerjack/.ruff_cache/0.11.13/1867267426380906393,sha256=-JUoRdDv_1mo2i4m_u0dAa9ByNdbOSvEGWE_LiSl6tc,256
26
27
  crackerjack/.ruff_cache/0.11.2/4070660268492669020,sha256=FTRTUmvj6nZw_QQBp_WHI-h3_iqRejzL39api-9wTvs,224
27
28
  crackerjack/.ruff_cache/0.11.3/9818742842212983150,sha256=U-4mT__a-OljovvAJvv5M6X7TCMa3dReLXx3kTNGgwU,224
28
29
  crackerjack/.ruff_cache/0.11.4/9818742842212983150,sha256=QF9j6-3MH_d0pDNotdbF2hlqCL66SxN8OLVKR3PZyZU,224
@@ -58,10 +59,10 @@ crackerjack/.ruff_cache/0.9.9/12813592349865671909,sha256=tmr8_vhRD2OxsVuMfbJPdT
58
59
  crackerjack/.ruff_cache/0.9.9/8843823720003377982,sha256=e4ymkXfQsUg5e_mtO34xTsaTvs1uA3_fI216Qq9qCAM,136
59
60
  crackerjack/.ruff_cache/CACHEDIR.TAG,sha256=WVMVbX4MVkpCclExbq8m-IcOZIOuIZf5FrYw5Pk-Ma4,43
60
61
  crackerjack/__init__.py,sha256=w5jukdION0D0fyeKYl-7hfCPzI0isXbEjzdjw8RecKA,840
61
- crackerjack/__main__.py,sha256=sNLM6iFkZlw3vpL1ziyc3NXjS7GEQNXguiF0hLOoe_M,6189
62
- crackerjack/crackerjack.py,sha256=mQtluOnLNbQe-bec3-WGpOGcgBQq1GG7L9m4r9y0cW0,46864
62
+ crackerjack/__main__.py,sha256=jg-eO0Z1VZkx5F-97dsd1rxOQ0uwHWabuKma8thUJVw,6779
63
+ crackerjack/crackerjack.py,sha256=OWw-EyQzDZsoxH-snikhxuQL8t0Es4MD9bsGfu6zwv4,52600
63
64
  crackerjack/errors.py,sha256=OtbmtA912kzDOWVo6JASuFbaMU-VhmQD_fUNsvnWCZc,4311
64
65
  crackerjack/interactive.py,sha256=Ay7_s3pc4ntc_3F_bRKBsWxmjor6nkN9v6tqqJe1iRw,15904
65
66
  crackerjack/py313.py,sha256=VgthlcpLL6nNDcg3evvEmGNYWCdMuojnMhow58ISEdY,6184
66
- crackerjack/pyproject.toml,sha256=6b9I1m0mEnaIswIs6dostny_TPEeAebvsVYvz7eCohs,4843
67
- crackerjack-0.20.0.dist-info/RECORD,,
67
+ crackerjack/pyproject.toml,sha256=2SSoGzy_2YAUS7Tuf8nnShK3JTRCc0mBHEdm89QUQ4E,4843
68
+ crackerjack-0.20.2.dist-info/RECORD,,