crackerjack 0.19.7__py3-none-any.whl → 0.20.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.
crackerjack/py313.py ADDED
@@ -0,0 +1,221 @@
1
+ """Python 3.13+ specific features and enhancements.
2
+
3
+ This module contains implementations that leverage the latest Python 3.13+ features.
4
+ It serves as a showcase for modern Python capabilities including:
5
+ - Pattern matching
6
+ - PEP 695 type parameter syntax
7
+ - Self type annotations
8
+ - More precise type hints
9
+ """
10
+
11
+ import subprocess
12
+ import typing
13
+ from enum import Enum, auto
14
+ from pathlib import Path
15
+ from typing import Any, Self, TypedDict
16
+
17
+
18
+ class CommandRunner[TReturn]:
19
+ def run_command(self, cmd: list[str], **kwargs: Any) -> TReturn: ...
20
+
21
+
22
+ class CommandResult(TypedDict):
23
+ success: bool
24
+ exit_code: int
25
+ stdout: str
26
+ stderr: str
27
+ command: list[str]
28
+ duration_ms: float
29
+
30
+
31
+ def process_command_output(result: CommandResult) -> tuple[bool, str]:
32
+ match result:
33
+ case {"success": True, "stdout": stdout} if stdout.strip():
34
+ return True, stdout
35
+
36
+ case {"success": True}:
37
+ return True, "Command completed successfully with no output"
38
+
39
+ case {"success": False, "exit_code": code, "stderr": stderr} if code == 127:
40
+ return False, f"Command not found: {stderr}"
41
+
42
+ case {"success": False, "exit_code": code} if code > 0:
43
+ return False, f"Command failed with exit code {code}: {result['stderr']}"
44
+
45
+ case _:
46
+ pass
47
+
48
+ return False, "Unknown command result pattern"
49
+
50
+
51
+ class HookStatus(Enum):
52
+ SUCCESS = auto()
53
+ FAILURE = auto()
54
+ SKIPPED = auto()
55
+ ERROR = auto()
56
+
57
+
58
+ class HookResult(TypedDict):
59
+ status: HookStatus
60
+ hook_id: str
61
+ output: str
62
+ files: list[str]
63
+
64
+
65
+ def analyze_hook_result(result: HookResult) -> str:
66
+ match result:
67
+ case {"status": HookStatus.SUCCESS, "hook_id": hook_id}:
68
+ return f"✅ Hook {hook_id} passed successfully"
69
+
70
+ case {"status": HookStatus.FAILURE, "hook_id": hook_id, "output": output} if (
71
+ "fixable" in output
72
+ ):
73
+ return f"🔧 Hook {hook_id} failed with fixable issues"
74
+
75
+ case {"status": HookStatus.FAILURE, "hook_id": hook_id}:
76
+ return f"❌ Hook {hook_id} failed"
77
+
78
+ case {"status": HookStatus.SKIPPED, "hook_id": hook_id}:
79
+ return f"⏩ Hook {hook_id} was skipped"
80
+
81
+ case {"status": HookStatus.ERROR, "hook_id": hook_id, "output": output}:
82
+ return f"💥 Hook {hook_id} encountered an error: {output}"
83
+
84
+ case _:
85
+ pass
86
+
87
+ return "Unknown hook result pattern"
88
+
89
+
90
+ class ModernConfigManager:
91
+ def __init__(self, config_path: Path) -> None:
92
+ self.config_path = config_path
93
+ self.config: dict[str, Any] = {}
94
+
95
+ def load(self) -> Self:
96
+ return self
97
+
98
+ def update(self, key: str, value: Any) -> Self:
99
+ self.config[key] = value
100
+ return self
101
+
102
+ def save(self) -> Self:
103
+ return self
104
+
105
+
106
+ def categorize_file(file_path: Path) -> str:
107
+ path_str = str(file_path)
108
+ name = file_path
109
+
110
+ match path_str:
111
+ case s if name.suffix == ".py" and "/tests/" in s:
112
+ return "Python Test File"
113
+
114
+ case s if name.suffix == ".py" and "__init__.py" in name.name:
115
+ return "Python Module Init"
116
+
117
+ case s if name.suffix == ".py":
118
+ return "Python Source File"
119
+
120
+ case s if name.suffix in {".md", ".rst", ".txt"}:
121
+ return "Documentation File"
122
+
123
+ case s if name.stem.startswith(".") or name.name in {
124
+ ".gitignore",
125
+ ".pre-commit-config.yaml",
126
+ }:
127
+ return "Configuration File"
128
+
129
+ case _:
130
+ pass
131
+
132
+ return "Unknown File Type"
133
+
134
+
135
+ def process_hook_results[T, R](
136
+ results: list[T],
137
+ success_handler: typing.Callable[[T], R],
138
+ failure_handler: typing.Callable[[T], R],
139
+ ) -> list[R]:
140
+ processed_results = []
141
+
142
+ for result in results:
143
+ if isinstance(result, dict) and result.get("status") == HookStatus.SUCCESS:
144
+ processed_results.append(success_handler(result))
145
+ else:
146
+ processed_results.append(failure_handler(result))
147
+
148
+ return processed_results
149
+
150
+
151
+ class EnhancedCommandRunner:
152
+ def __init__(self, working_dir: Path | None = None) -> None:
153
+ self.working_dir = working_dir
154
+
155
+ def run(self, cmd: list[str], **kwargs: Any) -> CommandResult:
156
+ import time
157
+
158
+ start_time = time.time()
159
+
160
+ try:
161
+ process = subprocess.run(
162
+ cmd, capture_output=True, text=True, cwd=self.working_dir, **kwargs
163
+ )
164
+
165
+ duration_ms = (time.time() - start_time) * 1000
166
+
167
+ return CommandResult(
168
+ success=(process.returncode == 0),
169
+ exit_code=process.returncode,
170
+ stdout=process.stdout,
171
+ stderr=process.stderr,
172
+ command=cmd,
173
+ duration_ms=duration_ms,
174
+ )
175
+
176
+ except subprocess.SubprocessError as e:
177
+ duration_ms = (time.time() - start_time) * 1000
178
+
179
+ return CommandResult(
180
+ success=False,
181
+ exit_code=-1,
182
+ stdout="",
183
+ stderr=str(e),
184
+ command=cmd,
185
+ duration_ms=duration_ms,
186
+ )
187
+
188
+ def handle_result(self, result: CommandResult) -> tuple[bool, str]:
189
+ return process_command_output(result)
190
+
191
+
192
+ def clean_python_code(code: str) -> str:
193
+ lines = code.splitlines()
194
+ cleaned_lines = []
195
+
196
+ for line in lines:
197
+ match line.strip():
198
+ case "":
199
+ if not cleaned_lines or cleaned_lines[-1].strip():
200
+ cleaned_lines.append("")
201
+
202
+ case s if s.startswith(("import ", "from ")):
203
+ cleaned_lines.append(line)
204
+
205
+ case s if s.startswith("#"):
206
+ continue
207
+
208
+ case s if "#" in s and not any(
209
+ skip in s for skip in ("# noqa", "# type:", "# pragma", "# skip")
210
+ ):
211
+ code_part = line.split("#", 1)[0].rstrip()
212
+ if code_part:
213
+ cleaned_lines.append(code_part)
214
+
215
+ case s if s.startswith('"""') or s.startswith("'''"):
216
+ continue
217
+
218
+ case _:
219
+ cleaned_lines.append(line)
220
+
221
+ return "\n".join(cleaned_lines)
@@ -4,59 +4,59 @@ requires = [ "pdm-backend" ]
4
4
 
5
5
  [project]
6
6
  name = "crackerjack"
7
- version = "0.19.6"
7
+ version = "0.19.8"
8
8
  description = "Crackerjack: code quality toolkit"
9
9
  readme = "README.md"
10
10
  keywords = [
11
- "bandit",
12
- "black",
13
- "creosote",
14
- "mypy",
15
- "pyright",
16
- "pytest",
17
- "refurb",
18
- "ruff",
11
+ "bandit",
12
+ "black",
13
+ "creosote",
14
+ "mypy",
15
+ "pyright",
16
+ "pytest",
17
+ "refurb",
18
+ "ruff",
19
19
  ]
20
20
  license.text = "BSD-3-CLAUSE"
21
21
  maintainers = [
22
- { name = "lesleslie", email = "les@wedgwoodwebworks.com" },
22
+ { name = "lesleslie", email = "les@wedgwoodwebworks.com" },
23
23
  ]
24
24
 
25
25
  authors = [
26
- { name = "lesleslie", email = "les@wedgwoodwebworks.com" },
26
+ { name = "lesleslie", email = "les@wedgwoodwebworks.com" },
27
27
  ]
28
28
  requires-python = ">=3.13"
29
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",
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
42
  ]
43
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.4",
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.10",
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.4",
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.10",
60
60
  ]
61
61
  urls.documentation = "https://github.com/lesleslie/crackerjack"
62
62
  urls.homepage = "https://github.com/lesleslie/crackerjack"
@@ -72,28 +72,28 @@ show-fixes = true
72
72
  output-format = "full"
73
73
  format.docstring-code-format = true
74
74
  lint.extend-select = [
75
- "C901",
76
- "D",
77
- "F", # pyflakes
78
- "I",
79
- "UP", # pyupgrade (includes F-string conversion)
75
+ "C901",
76
+ "D",
77
+ "F", # pyflakes
78
+ "I",
79
+ "UP", # pyupgrade (includes F-string conversion)
80
80
  ]
81
81
  lint.ignore = [
82
- "D100",
83
- "D101",
84
- "D102",
85
- "D103",
86
- "D104",
87
- "D105",
88
- "D106",
89
- "D107",
90
- "F821",
91
- "UP040",
82
+ "D100",
83
+ "D101",
84
+ "D102",
85
+ "D103",
86
+ "D104",
87
+ "D105",
88
+ "D106",
89
+ "D107",
90
+ "F821",
91
+ "UP040",
92
92
  ]
93
93
  lint.fixable = [ "ALL" ]
94
94
  lint.unfixable = [ ]
95
95
  lint.isort.no-lines-before = [
96
- "first-party",
96
+ "first-party",
97
97
  ]
98
98
  lint.mccabe.max-complexity = 13
99
99
  lint.pydocstyle.convention = "google"
@@ -105,7 +105,7 @@ ignore-words-list = "crate,uptodate,nd"
105
105
 
106
106
  [tool.pyproject-fmt]
107
107
  column_width = 120
108
- indent = 2
108
+ indent = 4
109
109
 
110
110
  [tool.pytest.ini_options]
111
111
  # Core pytest configuration
@@ -118,9 +118,9 @@ python_functions = [ "test_*" ]
118
118
 
119
119
  # Markers
120
120
  markers = [
121
- "unit: marks test as a unit test",
122
- "benchmark: mark test as a benchmark (disables parallel execution)",
123
- "integration: marks test as an integration test",
121
+ "unit: marks test as a unit test",
122
+ "benchmark: mark test as a benchmark (disables parallel execution)",
123
+ "integration: marks test as an integration test",
124
124
  ]
125
125
 
126
126
  # Default timeout settings
@@ -141,40 +141,40 @@ source = [ "crackerjack" ]
141
141
  data_file = ".coverage"
142
142
  parallel = false
143
143
  omit = [
144
- "*/tests/*",
145
- "*/site-packages/*",
146
- "*/__pycache__/*",
147
- "*/__init__.py",
148
- "*/_version.py",
149
- "*/conftest.py",
150
- "*/test_*.py",
151
- "*/_test.py",
144
+ "*/tests/*",
145
+ "*/site-packages/*",
146
+ "*/__pycache__/*",
147
+ "*/__init__.py",
148
+ "*/_version.py",
149
+ "*/conftest.py",
150
+ "*/test_*.py",
151
+ "*/_test.py",
152
152
  ]
153
153
 
154
154
  [tool.coverage.report]
155
155
  exclude_also = [
156
- "pragma: no cover",
157
- "def __repr__",
158
- "raise NotImplementedError",
159
- "if __name__ == .__main__.:",
160
- "pass",
161
- "raise ImportError",
162
- "except ImportError",
163
- "def __str__",
164
- "@abstractmethod",
156
+ "pragma: no cover",
157
+ "def __repr__",
158
+ "raise NotImplementedError",
159
+ "if __name__ == .__main__.:",
160
+ "pass",
161
+ "raise ImportError",
162
+ "except ImportError",
163
+ "def __str__",
164
+ "@abstractmethod",
165
165
  ]
166
166
  ignore_errors = false
167
167
 
168
168
  [tool.pyright]
169
169
  verboseOutput = true
170
170
  include = [
171
- "crackerjack",
171
+ "crackerjack",
172
172
  ]
173
173
  exclude = [
174
- "scratch",
174
+ "scratch",
175
175
  ]
176
176
  extraPaths = [
177
- ".venv/lib/python3.13/site-packages/",
177
+ ".venv/lib/python3.13/site-packages/",
178
178
  ]
179
179
  typeCheckingMode = "strict"
180
180
  reportMissingTypeStubs = false
@@ -196,26 +196,26 @@ ignore_names = [ "cls" ]
196
196
 
197
197
  [tool.creosote]
198
198
  paths = [
199
- "crackerjack",
199
+ "crackerjack",
200
200
  ]
201
201
  deps-file = "pyproject.toml"
202
202
  exclude-deps = [
203
- "pdm-bump",
204
- "autotyping",
205
- "pre-commit",
206
- "pytest",
207
- "pytest-asyncio",
208
- "pytest-cov",
209
- "pytest-mock",
210
- "pytest-xdist",
211
- "pytest-benchmark",
212
- "pdm",
213
- "pyfiglet",
214
- "pyyaml",
215
- "uv",
216
- "tomli-w",
217
- "google-crc32c",
218
- "pytest-timeout",
203
+ "pdm-bump",
204
+ "autotyping",
205
+ "pre-commit",
206
+ "pytest",
207
+ "pytest-asyncio",
208
+ "pytest-cov",
209
+ "pytest-mock",
210
+ "pytest-xdist",
211
+ "pytest-benchmark",
212
+ "pdm",
213
+ "pyfiglet",
214
+ "pyyaml",
215
+ "uv",
216
+ "tomli-w",
217
+ "google-crc32c",
218
+ "pytest-timeout",
219
219
  ]
220
220
 
221
221
  [tool.refurb]
@@ -224,16 +224,16 @@ quiet = true
224
224
 
225
225
  [tool.bandit]
226
226
  target = [
227
- "crackerjack",
227
+ "crackerjack",
228
228
  ]
229
229
  skips = [
230
- "B101",
231
- "B301",
232
- "B311",
233
- "B403",
234
- "B404",
235
- "B602",
236
- "B603",
237
- "B607",
238
- "B704",
230
+ "B101",
231
+ "B301",
232
+ "B311",
233
+ "B403",
234
+ "B404",
235
+ "B602",
236
+ "B603",
237
+ "B607",
238
+ "B704",
239
239
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: crackerjack
3
- Version: 0.19.7
3
+ Version: 0.20.0
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>
@@ -82,7 +82,7 @@ Crackerjack integrates powerful tools like Ruff, PDM, pre-commit, pytest, and mo
82
82
  ### Quick Start
83
83
 
84
84
  If you're new to Crackerjack, follow these steps:
85
- 1. **Install Python 3.13:** Ensure you have Python 3.13 installed.
85
+ 1. **Install Python 3.13:** Ensure you have Python 3.13 or higher installed.
86
86
  2. **Install PDM:**
87
87
  ```
88
88
  pipx install pdm
@@ -98,6 +98,11 @@ If you're new to Crackerjack, follow these steps:
98
98
  python -m crackerjack
99
99
  ```
100
100
 
101
+ Or use the interactive Rich UI:
102
+ ```
103
+ python -m crackerjack --rich-ui
104
+ ```
105
+
101
106
  ---
102
107
 
103
108
  ## The Crackerjack Philosophy
@@ -137,9 +142,12 @@ Crackerjack is built on the following core principles:
137
142
 
138
143
  ### Developer Experience
139
144
  - **Command-Line Interface:** Simple, intuitive CLI with comprehensive options
145
+ - **Interactive Rich UI:** Visual workflow with real-time task tracking, progress visualization, and interactive prompts
146
+ - **Structured Error Handling:** Clear error messages with error codes, detailed explanations, and recovery suggestions
140
147
  - **Programmatic API:** Can be integrated into your own Python scripts and workflows
141
148
  - **AI Agent Integration:** Structured output format for integration with AI assistants, with complete style rules available in [RULES.md](RULES.md) for AI tool customization
142
149
  - **Verbose Mode:** Detailed output for debugging and understanding what's happening
150
+ - **Python 3.13+ Features:** Leverages the latest Python language features including PEP 695 type parameter syntax, Self type annotations, and structural pattern matching
143
151
 
144
152
  ## Pre-commit Hooks
145
153
 
@@ -217,7 +225,7 @@ python -m crackerjack -t --benchmark-regression --benchmark-regression-threshold
217
225
 
218
226
  ## Installation
219
227
 
220
- 1. **Python:** Ensure you have Python 3.13 installed.
228
+ 1. **Python:** Ensure you have Python 3.13 or higher installed.
221
229
  2. **PDM:** Install [PDM](https://pdm.fming.dev/) using `pipx`:
222
230
 
223
231
  ```
@@ -231,6 +239,11 @@ python -m crackerjack -t --benchmark-regression --benchmark-regression-threshold
231
239
  python -m crackerjack
232
240
  ```
233
241
 
242
+ Or with the interactive Rich UI:
243
+ ```
244
+ python -m crackerjack --rich-ui
245
+ ```
246
+
234
247
  ## Usage
235
248
 
236
249
  ### Command Line
@@ -312,6 +325,7 @@ runner.process(MyOptions())
312
325
  - `--benchmark-regression`: Fail tests if benchmarks regress beyond threshold.
313
326
  - `--benchmark-regression-threshold`: Set threshold percentage for benchmark regression (default 5.0%).
314
327
  - `-a`, `--all`: Run with `-x -t -p <micro|minor|major> -c` development options.
328
+ - `--rich-ui`: Enable the interactive Rich UI for a more user-friendly experience with visual progress tracking and interactive prompts.
315
329
  - `--ai-agent`: Enable AI agent mode with structured output (see [AI Agent Integration](#ai-agent-integration)).
316
330
  - `--help`: Display help.
317
331
 
@@ -387,6 +401,11 @@ runner.process(MyOptions())
387
401
  python -m crackerjack -i
388
402
  ```
389
403
 
404
+ - **Rich Interactive Mode** - Run with the interactive Rich UI:
405
+ ```bash
406
+ python -m crackerjack --rich-ui
407
+ ```
408
+
390
409
  - **AI Integration** - Run with structured output for AI tools:
391
410
  ```bash
392
411
  python -m crackerjack --ai-agent --test
@@ -413,6 +432,79 @@ python -m crackerjack --ai-agent --test
413
432
 
414
433
  For detailed information about using Crackerjack with AI agents, including the structured output format and programmatic usage, see [README-AI-AGENT.md](README-AI-AGENT.md).
415
434
 
435
+ ## Interactive Rich UI
436
+
437
+ Crackerjack now offers an enhanced interactive experience through its Rich UI:
438
+
439
+ - **Visual Workflow:** See a visual representation of the entire task workflow with dependencies
440
+ - **Real-time Progress:** Track task progress with interactive progress bars and status indicators
441
+ - **Task Management:** Confirm tasks before execution and view detailed status information
442
+ - **Error Visualization:** Errors are presented in a structured, easy-to-understand format with recovery suggestions
443
+ - **File Selection:** Interactive file browser for operations that require selecting files
444
+
445
+ To use the Rich UI, run Crackerjack with the `--rich-ui` flag:
446
+
447
+ ```bash
448
+ python -m crackerjack --rich-ui
449
+ ```
450
+
451
+ This launches an interactive terminal interface where you can:
452
+ 1. View all available tasks and their dependencies
453
+ 2. Confirm each task before execution
454
+ 3. Get detailed status information for running tasks
455
+ 4. See a summary of completed, failed, and skipped tasks
456
+ 5. Visualize error details with recovery suggestions
457
+
458
+ ## Structured Error Handling
459
+
460
+ Crackerjack implements a comprehensive error handling system that provides:
461
+
462
+ - **Error Categories:** Errors are categorized by type (configuration, execution, testing, etc.)
463
+ - **Error Codes:** Each error has a unique numeric code for easy reference
464
+ - **Detailed Messages:** Clear, descriptive messages explain what went wrong
465
+ - **Recovery Suggestions:** Where possible, errors include recovery suggestions to help resolve issues
466
+ - **Rich Formatting:** Errors are presented with clear visual formatting (when using Rich UI or verbose mode)
467
+
468
+ Error types include:
469
+ - Configuration errors (1000-1999)
470
+ - Execution errors (2000-2999)
471
+ - Test errors (3000-3999)
472
+ - Publishing errors (4000-4999)
473
+ - Git errors (5000-5999)
474
+ - File operation errors (6000-6999)
475
+ - Code cleaning errors (7000-7999)
476
+ - Generic errors (9000-9999)
477
+
478
+ Use the `-v` or `--verbose` flag to see more detailed error information:
479
+
480
+ ```bash
481
+ python -m crackerjack -v
482
+ ```
483
+
484
+ For the most comprehensive error details with visual formatting, combine verbose mode with the Rich UI:
485
+
486
+ ```bash
487
+ python -m crackerjack --rich-ui -v
488
+ ```
489
+
490
+ ## Python 3.13+ Features
491
+
492
+ Crackerjack is designed to leverage the latest Python 3.13+ language features:
493
+
494
+ - **Type Parameter Syntax (PEP 695):** Uses the new, more concise syntax for generic type parameters
495
+ - **Self Type:** Leverages the `Self` type for better method chaining and builder patterns
496
+ - **Structural Pattern Matching:** Uses pattern matching for cleaner code, especially in configuration and command processing
497
+ - **Enhanced Type Hints:** More precise type hints with union types using the pipe operator
498
+ - **Modern Dictionary Patterns:** Leverages structural pattern matching with dictionaries for cleaner data handling
499
+
500
+ These modern Python features contribute to:
501
+ - More readable and maintainable code
502
+ - Better static type checking with tools like pyright
503
+ - Cleaner, more concise implementations
504
+ - Enhanced error handling and pattern recognition
505
+
506
+ Crackerjack provides examples of these features in action, serving as a reference for modern Python development practices.
507
+
416
508
  ## Contributing
417
509
 
418
510
  Crackerjack is an evolving project. Contributions are welcome! Please open a pull request or issue.