crackerjack 0.20.7__py3-none-any.whl → 0.20.11__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/errors.py CHANGED
@@ -1,15 +1,7 @@
1
- """Error handling module for Crackerjack.
2
-
3
- This module defines a structured error system with error codes, detailed error messages,
4
- and recovery suggestions. It provides a consistent way to handle errors throughout
5
- the application.
6
- """
7
-
8
1
  import sys
9
2
  import typing as t
10
3
  from enum import Enum
11
4
  from pathlib import Path
12
-
13
5
  from rich.console import Console
14
6
  from rich.panel import Panel
15
7
 
@@ -19,34 +11,27 @@ class ErrorCode(Enum):
19
11
  CONFIG_PARSE_ERROR = 1002
20
12
  INVALID_CONFIG = 1003
21
13
  MISSING_CONFIG_FIELD = 1004
22
-
23
14
  COMMAND_EXECUTION_ERROR = 2001
24
15
  COMMAND_TIMEOUT = 2002
25
16
  EXTERNAL_TOOL_ERROR = 2003
26
17
  PDM_INSTALL_ERROR = 2004
27
18
  PRE_COMMIT_ERROR = 2005
28
-
29
19
  TEST_EXECUTION_ERROR = 3001
30
20
  TEST_FAILURE = 3002
31
21
  BENCHMARK_REGRESSION = 3003
32
-
33
22
  BUILD_ERROR = 4001
34
23
  PUBLISH_ERROR = 4002
35
24
  VERSION_BUMP_ERROR = 4003
36
25
  AUTHENTICATION_ERROR = 4004
37
-
38
26
  GIT_COMMAND_ERROR = 5001
39
27
  PULL_REQUEST_ERROR = 5002
40
28
  COMMIT_ERROR = 5003
41
-
42
29
  FILE_NOT_FOUND = 6001
43
30
  PERMISSION_ERROR = 6002
44
31
  FILE_READ_ERROR = 6003
45
32
  FILE_WRITE_ERROR = 6004
46
-
47
33
  CODE_CLEANING_ERROR = 7001
48
34
  FORMATTING_ERROR = 7002
49
-
50
35
  UNKNOWN_ERROR = 9001
51
36
  NOT_IMPLEMENTED = 9002
52
37
  UNEXPECTED_ERROR = 9999
@@ -122,15 +107,12 @@ def handle_error(
122
107
  else:
123
108
  title = f"❌ Error {error.error_code.value}: {error.error_code.name}"
124
109
  content = [error.message]
125
-
126
110
  if verbose and error.details:
127
111
  content.extend(("\n[bold]Details:[/bold]", str(error.details)))
128
-
129
112
  if error.recovery:
130
113
  content.extend(
131
114
  ("\n[bold green]Recovery suggestion:[/bold green]", str(error.recovery))
132
115
  )
133
-
134
116
  console.print(
135
117
  Panel(
136
118
  "\n".join(content),
@@ -140,7 +122,6 @@ def handle_error(
140
122
  expand=False,
141
123
  )
142
124
  )
143
-
144
125
  if exit_on_error:
145
126
  sys.exit(error.exit_code)
146
127
 
@@ -167,7 +148,6 @@ def check_command_result(
167
148
  details = f"Command '{command}' failed with return code {result.returncode}."
168
149
  if stderr:
169
150
  details += f"\nStandard error output:\n{stderr}"
170
-
171
151
  raise ExecutionError(
172
152
  message=error_message,
173
153
  error_code=error_code,
@@ -1,14 +1,7 @@
1
- """Interactive CLI module using Rich for enhanced user experience.
2
-
3
- This module provides an enhanced interactive CLI experience using Rich library components.
4
- It includes progress tracking, interactive prompts, and detailed error reporting.
5
- """
6
-
7
1
  import time
8
2
  import typing as t
9
3
  from enum import Enum, auto
10
4
  from pathlib import Path
11
-
12
5
  from rich.box import ROUNDED
13
6
  from rich.console import Console
14
7
  from rich.layout import Layout
@@ -25,7 +18,6 @@ from rich.prompt import Confirm, Prompt
25
18
  from rich.table import Table
26
19
  from rich.text import Text
27
20
  from rich.tree import Tree
28
-
29
21
  from .errors import CrackerjackError, ErrorCode, handle_error
30
22
 
31
23
 
@@ -39,10 +31,7 @@ class TaskStatus(Enum):
39
31
 
40
32
  class Task:
41
33
  def __init__(
42
- self,
43
- name: str,
44
- description: str,
45
- dependencies: list["Task"] | None = None,
34
+ self, name: str, description: str, dependencies: list["Task"] | None = None
46
35
  ) -> None:
47
36
  self.name = name
48
37
  self.description = description
@@ -77,8 +66,10 @@ class Task:
77
66
 
78
67
  def can_run(self) -> bool:
79
68
  return all(
80
- dep.status in (TaskStatus.SUCCESS, TaskStatus.SKIPPED)
81
- for dep in self.dependencies
69
+ (
70
+ dep.status in (TaskStatus.SUCCESS, TaskStatus.SKIPPED)
71
+ for dep in self.dependencies
72
+ )
82
73
  )
83
74
 
84
75
  def __str__(self) -> str:
@@ -92,10 +83,7 @@ class WorkflowManager:
92
83
  self.current_task: Task | None = None
93
84
 
94
85
  def add_task(
95
- self,
96
- name: str,
97
- description: str,
98
- dependencies: list[str] | None = None,
86
+ self, name: str, description: str, dependencies: list[str] | None = None
99
87
  ) -> Task:
100
88
  dep_tasks = []
101
89
  if dependencies:
@@ -103,7 +91,6 @@ class WorkflowManager:
103
91
  if dep_name not in self.tasks:
104
92
  raise ValueError(f"Dependency task '{dep_name}' not found")
105
93
  dep_tasks.append(self.tasks[dep_name])
106
-
107
94
  task = Task(name, description, dep_tasks)
108
95
  self.tasks[name] = task
109
96
  return task
@@ -113,21 +100,23 @@ class WorkflowManager:
113
100
  if (
114
101
  task.status == TaskStatus.PENDING
115
102
  and task.can_run()
116
- and task != self.current_task
103
+ and (task != self.current_task)
117
104
  ):
118
105
  return task
119
106
  return None
120
107
 
121
108
  def all_tasks_completed(self) -> bool:
122
109
  return all(
123
- task.status in (TaskStatus.SUCCESS, TaskStatus.FAILED, TaskStatus.SKIPPED)
124
- for task in self.tasks.values()
110
+ (
111
+ task.status
112
+ in (TaskStatus.SUCCESS, TaskStatus.FAILED, TaskStatus.SKIPPED)
113
+ for task in self.tasks.values()
114
+ )
125
115
  )
126
116
 
127
117
  def run_task(self, task: Task, func: t.Callable[[], t.Any]) -> bool:
128
118
  self.current_task = task
129
119
  task.start()
130
-
131
120
  try:
132
121
  func()
133
122
  task.complete()
@@ -151,11 +140,9 @@ class WorkflowManager:
151
140
 
152
141
  def display_task_tree(self) -> None:
153
142
  tree = Tree("Workflow")
154
-
155
143
  for task in self.tasks.values():
156
144
  if not task.dependencies:
157
145
  self._add_task_to_tree(task, tree)
158
-
159
146
  self.console.print(tree)
160
147
 
161
148
  def _add_task_to_tree(self, task: Task, parent: Tree) -> None:
@@ -169,9 +156,7 @@ class WorkflowManager:
169
156
  status = "[blue]⏩[/blue]"
170
157
  else:
171
158
  status = "[grey]⏸️[/grey]"
172
-
173
159
  branch = parent.add(f"{status} {task.name} - {task.description}")
174
-
175
160
  for dependent in self.tasks.values():
176
161
  if task in dependent.dependencies:
177
162
  self._add_task_to_tree(dependent, branch)
@@ -186,14 +171,12 @@ class InteractiveCLI:
186
171
  title = Text("Crackerjack", style="bold cyan")
187
172
  version_text = Text(f"v{version}", style="dim cyan")
188
173
  subtitle = Text("Your Python project management toolkit", style="italic")
189
-
190
174
  panel = Panel(
191
175
  f"{title} {version_text}\n{subtitle}",
192
176
  box=ROUNDED,
193
177
  border_style="cyan",
194
178
  expand=False,
195
179
  )
196
-
197
180
  self.console.print(panel)
198
181
  self.console.print()
199
182
 
@@ -213,18 +196,14 @@ class InteractiveCLI:
213
196
 
214
197
  def setup_layout(self) -> Layout:
215
198
  layout = Layout()
216
-
217
199
  layout.split(
218
200
  Layout(name="header", size=3),
219
201
  Layout(name="main"),
220
202
  Layout(name="footer", size=3),
221
203
  )
222
-
223
204
  layout["main"].split_row(
224
- Layout(name="tasks", ratio=1),
225
- Layout(name="details", ratio=2),
205
+ Layout(name="tasks", ratio=1), Layout(name="details", ratio=2)
226
206
  )
227
-
228
207
  return layout
229
208
 
230
209
  def show_task_status(self, task: Task) -> Panel:
@@ -243,25 +222,16 @@ class InteractiveCLI:
243
222
  else:
244
223
  status = "[grey]⏸️ Pending[/grey]"
245
224
  style = "dim"
246
-
247
225
  duration = task.duration
248
226
  duration_text = f"Duration: {duration:.2f}s" if duration else ""
249
-
250
227
  content = f"{task.name}: {task.description}\nStatus: {status}\n{duration_text}"
251
-
252
228
  if task.error:
253
229
  content += f"\n[red]Error: {task.error.message}[/red]"
254
230
  if task.error.details:
255
231
  content += f"\n[dim red]Details: {task.error.details}[/dim red]"
256
232
  if task.error.recovery:
257
233
  content += f"\n[yellow]Recovery: {task.error.recovery}[/yellow]"
258
-
259
- return Panel(
260
- content,
261
- title=task.name,
262
- border_style=style,
263
- expand=False,
264
- )
234
+ return Panel(content, title=task.name, border_style=style, expand=False)
265
235
 
266
236
  def show_task_table(self) -> Table:
267
237
  table = Table(
@@ -270,12 +240,10 @@ class InteractiveCLI:
270
240
  show_header=True,
271
241
  header_style="bold cyan",
272
242
  )
273
-
274
243
  table.add_column("Task", style="cyan")
275
244
  table.add_column("Status")
276
245
  table.add_column("Duration")
277
246
  table.add_column("Dependencies")
278
-
279
247
  for task in self.workflow.tasks.values():
280
248
  if task.status == TaskStatus.RUNNING:
281
249
  status = "[yellow]⏳ Running[/yellow]"
@@ -287,36 +255,19 @@ class InteractiveCLI:
287
255
  status = "[blue]⏩ Skipped[/blue]"
288
256
  else:
289
257
  status = "[grey]⏸️ Pending[/grey]"
290
-
291
258
  duration = task.duration
292
259
  duration_text = f"{duration:.2f}s" if duration else "-"
293
-
294
- deps = ", ".join(dep.name for dep in task.dependencies) or "-"
295
-
260
+ deps = ", ".join((dep.name for dep in task.dependencies)) or "-"
296
261
  table.add_row(task.name, status, duration_text, deps)
297
-
298
262
  return table
299
263
 
300
264
  def run_interactive(self) -> None:
301
265
  self.console.clear()
302
266
  layout = self.setup_layout()
303
-
304
267
  layout["header"].update(
305
- Panel(
306
- "Crackerjack Interactive Mode",
307
- style="bold cyan",
308
- box=ROUNDED,
309
- )
310
- )
311
-
312
- layout["footer"].update(
313
- Panel(
314
- "Press Ctrl+C to exit",
315
- style="dim",
316
- box=ROUNDED,
317
- )
268
+ Panel("Crackerjack Interactive Mode", style="bold cyan", box=ROUNDED)
318
269
  )
319
-
270
+ layout["footer"].update(Panel("Press Ctrl+C to exit", style="dim", box=ROUNDED))
320
271
  progress = Progress(
321
272
  SpinnerColumn(),
322
273
  TextColumn("[bold blue]{task.description}"),
@@ -324,41 +275,31 @@ class InteractiveCLI:
324
275
  TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
325
276
  TimeElapsedColumn(),
326
277
  )
327
-
328
278
  total_tasks = len(self.workflow.tasks)
329
279
  progress_task = progress.add_task("Running workflow", total=total_tasks)
330
280
  completed_tasks = 0
331
-
332
281
  with Live(layout, refresh_per_second=4, screen=True) as live:
333
282
  try:
334
283
  while not self.workflow.all_tasks_completed():
335
284
  layout["tasks"].update(self.show_task_table())
336
-
337
285
  next_task = self.workflow.get_next_task()
338
286
  if not next_task:
339
287
  break
340
-
341
288
  layout["details"].update(self.show_task_status(next_task))
342
-
343
289
  live.stop()
344
290
  should_run = Confirm.ask(
345
- f"Run task '{next_task.name}'?",
346
- default=True,
291
+ f"Run task '{next_task.name}'?", default=True
347
292
  )
348
293
  live.start()
349
-
350
294
  if not should_run:
351
295
  next_task.skip()
352
296
  continue
353
-
354
297
  next_task.start()
355
298
  layout["details"].update(self.show_task_status(next_task))
356
299
  time.sleep(1)
357
-
358
300
  import random
359
301
 
360
302
  success = random.choice([True, True, True, False])
361
-
362
303
  if success:
363
304
  next_task.complete(True)
364
305
  completed_tasks += 1
@@ -372,48 +313,40 @@ class InteractiveCLI:
372
313
  recovery=f"Retry the '{next_task.name}' task.",
373
314
  )
374
315
  next_task.fail(error)
375
-
376
316
  progress.update(progress_task, completed=completed_tasks)
377
-
378
317
  layout["details"].update(self.show_task_status(next_task))
379
-
380
318
  layout["tasks"].update(self.show_task_table())
381
-
382
319
  successful = sum(
383
- 1
384
- for task in self.workflow.tasks.values()
385
- if task.status == TaskStatus.SUCCESS
320
+ (
321
+ 1
322
+ for task in self.workflow.tasks.values()
323
+ if task.status == TaskStatus.SUCCESS
324
+ )
386
325
  )
387
326
  failed = sum(
388
- 1
389
- for task in self.workflow.tasks.values()
390
- if task.status == TaskStatus.FAILED
327
+ (
328
+ 1
329
+ for task in self.workflow.tasks.values()
330
+ if task.status == TaskStatus.FAILED
331
+ )
391
332
  )
392
333
  skipped = sum(
393
- 1
394
- for task in self.workflow.tasks.values()
395
- if task.status == TaskStatus.SKIPPED
334
+ (
335
+ 1
336
+ for task in self.workflow.tasks.values()
337
+ if task.status == TaskStatus.SKIPPED
338
+ )
396
339
  )
397
-
398
340
  summary = Panel(
399
- f"Workflow completed!\n\n"
400
- f"[green]✅ Successful tasks: {successful}[/green]\n"
401
- f"[red]❌ Failed tasks: {failed}[/red]\n"
402
- f"[blue]⏩ Skipped tasks: {skipped}[/blue]",
341
+ f"Workflow completed!\n\n[green]✅ Successful tasks: {successful}[/green]\n[red]❌ Failed tasks: {failed}[/red]\n[blue]⏩ Skipped tasks: {skipped}[/blue]",
403
342
  title="Summary",
404
343
  border_style="cyan",
405
344
  )
406
345
  layout["details"].update(summary)
407
-
408
346
  except KeyboardInterrupt:
409
347
  layout["footer"].update(
410
- Panel(
411
- "Interrupted by user",
412
- style="yellow",
413
- box=ROUNDED,
414
- )
348
+ Panel("Interrupted by user", style="yellow", box=ROUNDED)
415
349
  )
416
-
417
350
  self.console.print("\nWorkflow Status:")
418
351
  self.workflow.display_task_tree()
419
352
 
@@ -421,53 +354,38 @@ class InteractiveCLI:
421
354
  self, prompt: str, directory: Path, default: str | None = None
422
355
  ) -> Path:
423
356
  self.console.print(f"\n[bold]{prompt}[/bold]")
424
-
425
357
  files = list(directory.iterdir())
426
358
  files.sort()
427
-
428
359
  table = Table(title=f"Files in {directory}", box=ROUNDED)
429
360
  table.add_column("#", style="cyan")
430
361
  table.add_column("Filename", style="green")
431
362
  table.add_column("Size", style="blue")
432
363
  table.add_column("Modified", style="yellow")
433
-
434
364
  for i, file in enumerate(files, 1):
435
365
  if file.is_file():
436
366
  size = f"{file.stat().st_size / 1024:.1f} KB"
437
367
  mtime = time.strftime(
438
- "%Y-%m-%d %H:%M:%S",
439
- time.localtime(file.stat().st_mtime),
368
+ "%Y-%m-%d %H:%M:%S", time.localtime(file.stat().st_mtime)
440
369
  )
441
370
  table.add_row(str(i), file.name, size, mtime)
442
-
443
371
  self.console.print(table)
444
-
445
- selection = Prompt.ask(
446
- "Enter file number or name",
447
- default=default or "",
448
- )
449
-
372
+ selection = Prompt.ask("Enter file number or name", default=default or "")
450
373
  if selection.isdigit() and 1 <= int(selection) <= len(files):
451
374
  return files[int(selection) - 1]
452
375
  else:
453
376
  for file in files:
454
377
  if file.name == selection:
455
378
  return file
456
-
457
379
  return directory / selection
458
380
 
459
381
  def confirm_dangerous_action(self, action: str, details: str) -> bool:
460
382
  panel = Panel(
461
- f"[bold red]WARNING: {action}[/bold red]\n\n{details}\n\n"
462
- "This action cannot be undone. Please type the action name to confirm.",
383
+ f"[bold red]WARNING: {action}[/bold red]\n\n{details}\n\nThis action cannot be undone. Please type the action name to confirm.",
463
384
  title="Confirmation Required",
464
385
  border_style="red",
465
386
  )
466
-
467
387
  self.console.print(panel)
468
-
469
388
  confirmation = Prompt.ask("Type the action name to confirm")
470
-
471
389
  return confirmation.lower() == action.lower()
472
390
 
473
391
  def show_error(self, error: CrackerjackError, verbose: bool = False) -> None:
@@ -477,7 +395,6 @@ class InteractiveCLI:
477
395
  def launch_interactive_cli(version: str) -> None:
478
396
  console = Console()
479
397
  cli = InteractiveCLI(console)
480
-
481
398
  cli.show_banner(version)
482
399
  cli.create_standard_workflow()
483
400
  cli.run_interactive()
crackerjack/py313.py CHANGED
@@ -1,13 +1,3 @@
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
1
  import subprocess
12
2
  import typing
13
3
  from enum import Enum, auto
@@ -31,21 +21,16 @@ class CommandResult(TypedDict):
31
21
  def process_command_output(result: CommandResult) -> tuple[bool, str]:
32
22
  match result:
33
23
  case {"success": True, "stdout": stdout} if stdout.strip():
34
- return True, stdout
35
-
24
+ return (True, stdout)
36
25
  case {"success": True}:
37
- return True, "Command completed successfully with no output"
38
-
26
+ return (True, "Command completed successfully with no output")
39
27
  case {"success": False, "exit_code": code, "stderr": stderr} if code == 127:
40
- return False, f"Command not found: {stderr}"
41
-
28
+ return (False, f"Command not found: {stderr}")
42
29
  case {"success": False, "exit_code": code} if code > 0:
43
- return False, f"Command failed with exit code {code}: {result['stderr']}"
44
-
30
+ return (False, f"Command failed with exit code {code}: {result['stderr']}")
45
31
  case _:
46
32
  pass
47
-
48
- return False, "Unknown command result pattern"
33
+ return (False, "Unknown command result pattern")
49
34
 
50
35
 
51
36
  class HookStatus(Enum):
@@ -66,24 +51,18 @@ def analyze_hook_result(result: HookResult) -> str:
66
51
  match result:
67
52
  case {"status": HookStatus.SUCCESS, "hook_id": hook_id}:
68
53
  return f"✅ Hook {hook_id} passed successfully"
69
-
70
54
  case {"status": HookStatus.FAILURE, "hook_id": hook_id, "output": output} if (
71
55
  "fixable" in output
72
56
  ):
73
57
  return f"🔧 Hook {hook_id} failed with fixable issues"
74
-
75
58
  case {"status": HookStatus.FAILURE, "hook_id": hook_id}:
76
59
  return f"❌ Hook {hook_id} failed"
77
-
78
60
  case {"status": HookStatus.SKIPPED, "hook_id": hook_id}:
79
61
  return f"⏩ Hook {hook_id} was skipped"
80
-
81
62
  case {"status": HookStatus.ERROR, "hook_id": hook_id, "output": output}:
82
63
  return f"💥 Hook {hook_id} encountered an error: {output}"
83
-
84
64
  case _:
85
65
  pass
86
-
87
66
  return "Unknown hook result pattern"
88
67
 
89
68
 
@@ -106,29 +85,22 @@ class ModernConfigManager:
106
85
  def categorize_file(file_path: Path) -> str:
107
86
  path_str = str(file_path)
108
87
  name = file_path
109
-
110
88
  match path_str:
111
89
  case s if name.suffix == ".py" and "/tests/" in s:
112
90
  return "Python Test File"
113
-
114
91
  case s if name.suffix == ".py" and "__init__.py" in name.name:
115
92
  return "Python Module Init"
116
-
117
93
  case s if name.suffix == ".py":
118
94
  return "Python Source File"
119
-
120
95
  case s if name.suffix in {".md", ".rst", ".txt"}:
121
96
  return "Documentation File"
122
-
123
97
  case s if name.stem.startswith(".") or name.name in {
124
98
  ".gitignore",
125
99
  ".pre-commit-config.yaml",
126
100
  }:
127
101
  return "Configuration File"
128
-
129
102
  case _:
130
103
  pass
131
-
132
104
  return "Unknown File Type"
133
105
 
134
106
 
@@ -138,13 +110,11 @@ def process_hook_results[T, R](
138
110
  failure_handler: typing.Callable[[T], R],
139
111
  ) -> list[R]:
140
112
  processed_results = []
141
-
142
113
  for result in results:
143
114
  if isinstance(result, dict) and result.get("status") == HookStatus.SUCCESS:
144
115
  processed_results.append(success_handler(result))
145
116
  else:
146
117
  processed_results.append(failure_handler(result))
147
-
148
118
  return processed_results
149
119
 
150
120
 
@@ -156,26 +126,21 @@ class EnhancedCommandRunner:
156
126
  import time
157
127
 
158
128
  start_time = time.time()
159
-
160
129
  try:
161
130
  process = subprocess.run(
162
131
  cmd, capture_output=True, text=True, cwd=self.working_dir, **kwargs
163
132
  )
164
-
165
133
  duration_ms = (time.time() - start_time) * 1000
166
-
167
134
  return CommandResult(
168
- success=(process.returncode == 0),
135
+ success=process.returncode == 0,
169
136
  exit_code=process.returncode,
170
137
  stdout=process.stdout,
171
138
  stderr=process.stderr,
172
139
  command=cmd,
173
140
  duration_ms=duration_ms,
174
141
  )
175
-
176
142
  except subprocess.SubprocessError as e:
177
143
  duration_ms = (time.time() - start_time) * 1000
178
-
179
144
  return CommandResult(
180
145
  success=False,
181
146
  exit_code=-1,
@@ -192,30 +157,25 @@ class EnhancedCommandRunner:
192
157
  def clean_python_code(code: str) -> str:
193
158
  lines = code.splitlines()
194
159
  cleaned_lines = []
195
-
196
160
  for line in lines:
197
161
  match line.strip():
198
162
  case "":
199
163
  if not cleaned_lines or cleaned_lines[-1].strip():
200
164
  cleaned_lines.append("")
201
-
202
165
  case s if s.startswith(("import ", "from ")):
203
166
  cleaned_lines.append(line)
204
-
205
167
  case s if s.startswith("#"):
206
168
  continue
207
-
208
- case s if "#" in s and not any(
209
- skip in s for skip in ("# noqa", "# type:", "# pragma", "# skip")
169
+ case s if "#" in s and (
170
+ not any(
171
+ (skip in s for skip in ("# noqa", "# type:", "# pragma", "# skip"))
172
+ )
210
173
  ):
211
174
  code_part = line.split("#", 1)[0].rstrip()
212
175
  if code_part:
213
176
  cleaned_lines.append(code_part)
214
-
215
177
  case s if s.startswith('"""') or s.startswith("'''"):
216
178
  continue
217
-
218
179
  case _:
219
180
  cleaned_lines.append(line)
220
-
221
181
  return "\n".join(cleaned_lines)
@@ -4,7 +4,7 @@ requires = [ "pdm-backend" ]
4
4
 
5
5
  [project]
6
6
  name = "crackerjack"
7
- version = "0.20.6"
7
+ version = "0.20.10"
8
8
  description = "Crackerjack: code quality toolkit"
9
9
  readme = "README.md"
10
10
  keywords = [
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: crackerjack
3
- Version: 0.20.7
3
+ Version: 0.20.11
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>