crackerjack 0.14.4__tar.gz → 0.14.5__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 (61) hide show
  1. {crackerjack-0.14.4 → crackerjack-0.14.5}/PKG-INFO +3 -3
  2. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.pre-commit-config.yaml +3 -3
  3. crackerjack-0.14.5/crackerjack/.ruff_cache/0.11.2/4070660268492669020 +0 -0
  4. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/crackerjack.py +121 -59
  5. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/pyproject.toml +3 -3
  6. {crackerjack-0.14.4 → crackerjack-0.14.5}/pyproject.toml +3 -3
  7. crackerjack-0.14.5/tests/data/docstrings_sample.txt +20 -0
  8. {crackerjack-0.14.4 → crackerjack-0.14.5}/tests/test_crackerjack.py +26 -10
  9. crackerjack-0.14.4/tests/data/docstrings_sample.txt +0 -10
  10. {crackerjack-0.14.4 → crackerjack-0.14.5}/LICENSE +0 -0
  11. {crackerjack-0.14.4 → crackerjack-0.14.5}/README.md +0 -0
  12. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.coverage +0 -0
  13. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.gitignore +0 -0
  14. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.libcst.codemod.yaml +0 -0
  15. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.pdm.toml +0 -0
  16. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.pytest_cache/.gitignore +0 -0
  17. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.pytest_cache/CACHEDIR.TAG +0 -0
  18. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.pytest_cache/README.md +0 -0
  19. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.pytest_cache/v/cache/nodeids +0 -0
  20. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.pytest_cache/v/cache/stepwise +0 -0
  21. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/.gitignore +0 -0
  22. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.1.11/3256171999636029978 +0 -0
  23. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.1.14/602324811142551221 +0 -0
  24. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.1.4/10355199064880463147 +0 -0
  25. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.1.6/15140459877605758699 +0 -0
  26. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.1.7/1790508110482614856 +0 -0
  27. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.1.9/17041001205004563469 +0 -0
  28. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.2.0/10047773857155985907 +0 -0
  29. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.2.1/8522267973936635051 +0 -0
  30. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.2.2/18053836298936336950 +0 -0
  31. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.3.0/12548816621480535786 +0 -0
  32. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.3.3/11081883392474770722 +0 -0
  33. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.3.4/676973378459347183 +0 -0
  34. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.3.5/16311176246009842383 +0 -0
  35. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.5.7/1493622539551733492 +0 -0
  36. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.5.7/6231957614044513175 +0 -0
  37. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.5.7/9932762556785938009 +0 -0
  38. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.6.0/11982804814124138945 +0 -0
  39. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.6.0/12055761203849489982 +0 -0
  40. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.6.2/1206147804896221174 +0 -0
  41. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.6.4/1206147804896221174 +0 -0
  42. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.6.5/1206147804896221174 +0 -0
  43. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.6.7/3657366982708166874 +0 -0
  44. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.6.9/285614542852677309 +0 -0
  45. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.7.1/1024065805990144819 +0 -0
  46. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.7.1/285614542852677309 +0 -0
  47. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.7.3/16061516852537040135 +0 -0
  48. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.8.4/16354268377385700367 +0 -0
  49. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.9.10/12813592349865671909 +0 -0
  50. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.9.10/923908772239632759 +0 -0
  51. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.9.3/13948373885254993391 +0 -0
  52. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.9.9/12813592349865671909 +0 -0
  53. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/0.9.9/8843823720003377982 +0 -0
  54. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/.ruff_cache/CACHEDIR.TAG +0 -0
  55. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/__init__.py +0 -0
  56. {crackerjack-0.14.4 → crackerjack-0.14.5}/crackerjack/__main__.py +0 -0
  57. {crackerjack-0.14.4 → crackerjack-0.14.5}/tests/__init__.py +0 -0
  58. {crackerjack-0.14.4 → crackerjack-0.14.5}/tests/data/comments_sample.txt +0 -0
  59. {crackerjack-0.14.4 → crackerjack-0.14.5}/tests/data/expected_comments_sample.txt +0 -0
  60. {crackerjack-0.14.4 → crackerjack-0.14.5}/tests/data/init.py +0 -0
  61. {crackerjack-0.14.4 → crackerjack-0.14.5}/tests/test_main.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: crackerjack
3
- Version: 0.14.4
3
+ Version: 0.14.5
4
4
  Summary: Default template for PDM package
5
5
  Keywords: black,ruff,mypy,creosote,refurb
6
6
  Author-Email: lesleslie <les@wedgwoodwebworks.com>
@@ -23,12 +23,12 @@ Project-URL: documentation, https://github.com/lesleslie/crackerjack
23
23
  Project-URL: repository, https://github.com/lesleslie/crackerjack
24
24
  Requires-Python: >=3.13
25
25
  Requires-Dist: autotyping>=24.9.0
26
- Requires-Dist: pre-commit>=4.1.0
26
+ Requires-Dist: pre-commit>=4.2.0
27
27
  Requires-Dist: pytest>=8.3.5
28
28
  Requires-Dist: pydantic>=2.10.6
29
29
  Requires-Dist: pdm-bump>=0.9.10
30
30
  Requires-Dist: pdm>=2.22.4
31
- Requires-Dist: uv>=0.6.6
31
+ Requires-Dist: uv>=0.6.9
32
32
  Requires-Dist: pytest-cov>=6.0.0
33
33
  Requires-Dist: pytest-mock>=3.14.0
34
34
  Requires-Dist: tomli-w>=1.2.0
@@ -17,7 +17,7 @@ repos:
17
17
  - id: check-added-large-files
18
18
  name: check-added-large-files
19
19
  - repo: https://github.com/astral-sh/ruff-pre-commit
20
- rev: v0.9.10
20
+ rev: v0.11.2
21
21
  hooks:
22
22
  - id: ruff-format
23
23
  - id: ruff
@@ -65,11 +65,11 @@ repos:
65
65
  - id: bandit
66
66
  args: ["-c", "pyproject.toml"]
67
67
  - repo: https://github.com/RobertCraigie/pyright-python
68
- rev: v1.1.396
68
+ rev: v1.1.397
69
69
  hooks:
70
70
  - id: pyright
71
71
  - repo: https://github.com/astral-sh/ruff-pre-commit
72
- rev: v0.9.10
72
+ rev: v0.11.2
73
73
  hooks:
74
74
  - id: ruff
75
75
  - id: ruff-format
@@ -5,13 +5,13 @@ import subprocess
5
5
  import tokenize
6
6
  import typing as t
7
7
  from contextlib import suppress
8
+ from dataclasses import dataclass, field
8
9
  from pathlib import Path
9
10
  from subprocess import CompletedProcess
10
11
  from subprocess import run as execute
11
12
  from token import STRING
12
13
  from tomllib import loads
13
14
 
14
- from pydantic import BaseModel
15
15
  from rich.console import Console
16
16
  from tomli_w import dumps
17
17
 
@@ -20,7 +20,8 @@ interactive_hooks = ("refurb", "bandit", "pyright")
20
20
  default_python_version = "3.13"
21
21
 
22
22
 
23
- class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
23
+ @dataclass
24
+ class CodeCleaner:
24
25
  console: Console
25
26
 
26
27
  def clean_files(self, pkg_dir: Path | None) -> None:
@@ -35,20 +36,20 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
35
36
  def clean_file(self, file_path: Path) -> None:
36
37
  try:
37
38
  if file_path.resolve() == Path(__file__).resolve():
38
- print(f"Skipping cleaning of {file_path} (self file).")
39
+ self.console.print(f"Skipping cleaning of {file_path} (self file).")
39
40
  return
40
41
  except Exception as e:
41
- print(f"Error comparing file paths: {e}")
42
+ self.console.print(f"Error comparing file paths: {e}")
42
43
  try:
43
44
  code = file_path.read_text()
44
45
  code = self.remove_docstrings(code)
45
46
  code = self.remove_line_comments(code)
46
47
  code = self.remove_extra_whitespace(code)
47
48
  code = self.reformat_code(code)
48
- file_path.write_text(code)
49
- print(f"Cleaned: {file_path}")
49
+ file_path.write_text(code) # type: ignore
50
+ self.console.print(f"Cleaned: {file_path}")
50
51
  except Exception as e:
51
- print(f"Error cleaning {file_path}: {e}")
52
+ self.console.print(f"Error cleaning {file_path}: {e}")
52
53
 
53
54
  def remove_line_comments(self, code: str) -> str:
54
55
  new_lines = []
@@ -66,31 +67,87 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
66
67
  new_lines.append(code_part)
67
68
  return "\n".join(new_lines)
68
69
 
70
+ def _is_triple_quoted(self, token_string: str) -> bool:
71
+ triple_quote_patterns = [
72
+ ('"""', '"""'),
73
+ ("'''", "'''"),
74
+ ('r"""', '"""'),
75
+ ("r'''", "'''"),
76
+ ]
77
+ return any(
78
+ token_string.startswith(start) and token_string.endswith(end)
79
+ for start, end in triple_quote_patterns
80
+ )
81
+
82
+ def _is_module_docstring(
83
+ self, tokens: list[tokenize.TokenInfo], i: int, indent_level: int
84
+ ) -> bool:
85
+ if i <= 0 or indent_level != 0:
86
+ return False
87
+ preceding_tokens = tokens[:i]
88
+ return not preceding_tokens
89
+
90
+ def _is_function_or_class_docstring(
91
+ self,
92
+ tokens: list[tokenize.TokenInfo],
93
+ i: int,
94
+ last_token_type: t.Any,
95
+ last_token_string: str,
96
+ ) -> bool:
97
+ if last_token_type != tokenize.OP or last_token_string != ":": # nosec B105
98
+ return False
99
+ for prev_idx in range(i - 1, max(0, i - 20), -1):
100
+ prev_token = tokens[prev_idx]
101
+ if prev_token[1] in ("def", "class") and prev_token[0] == tokenize.NAME:
102
+ return True
103
+ elif prev_token[0] == tokenize.DEDENT:
104
+ break
105
+ return False
106
+
107
+ def _is_variable_docstring(
108
+ self, tokens: list[tokenize.TokenInfo], i: int, indent_level: int
109
+ ) -> bool:
110
+ if indent_level <= 0:
111
+ return False
112
+ for prev_idx in range(i - 1, max(0, i - 10), -1):
113
+ if tokens[prev_idx][0]:
114
+ return True
115
+ return False
116
+
69
117
  def remove_docstrings(self, source: str) -> str:
70
118
  try:
71
119
  io_obj = io.StringIO(source)
72
- output_tokens = []
73
- first_token_stack = [True]
74
120
  tokens = list(tokenize.generate_tokens(io_obj.readline))
75
- for tok in tokens:
76
- token_type = tok.type
121
+ result_tokens = []
122
+ indent_level = 0
123
+ last_non_ws_token_type = None
124
+ last_non_ws_token_string = "" # nosec B105
125
+ for i, token in enumerate(tokens):
126
+ token_type, token_string, _, _, _ = token
77
127
  if token_type == tokenize.INDENT:
78
- first_token_stack.append(True)
128
+ indent_level += 1
79
129
  elif token_type == tokenize.DEDENT:
80
- if len(first_token_stack) > 1:
81
- first_token_stack.pop()
82
- if token_type == STRING and first_token_stack[-1]:
83
- first_token_stack[-1] = False
84
- continue
85
- else:
86
- if token_type not in (
87
- tokenize.NEWLINE,
88
- tokenize.NL,
89
- tokenize.COMMENT,
90
- ):
91
- first_token_stack[-1] = False
92
- output_tokens.append(tok)
93
- return tokenize.untokenize(output_tokens)
130
+ indent_level -= 1
131
+ if token_type == STRING and self._is_triple_quoted(token_string):
132
+ is_docstring = (
133
+ self._is_module_docstring(tokens, i, indent_level)
134
+ or self._is_function_or_class_docstring(
135
+ tokens, i, last_non_ws_token_type, last_non_ws_token_string
136
+ )
137
+ or self._is_variable_docstring(tokens, i, indent_level)
138
+ )
139
+ if is_docstring:
140
+ continue
141
+ if token_type not in (
142
+ tokenize.NL,
143
+ tokenize.NEWLINE,
144
+ tokenize.INDENT,
145
+ tokenize.DEDENT,
146
+ ):
147
+ last_non_ws_token_type = token_type
148
+ last_non_ws_token_string = token_string
149
+ result_tokens.append(token)
150
+ return tokenize.untokenize(result_tokens)
94
151
  except Exception as e:
95
152
  self.console.print(f"Error removing docstrings: {e}")
96
153
  return source
@@ -105,7 +162,7 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
105
162
  cleaned_lines.append(line)
106
163
  return "\n".join(cleaned_lines)
107
164
 
108
- def reformat_code(self, code: str) -> str:
165
+ def reformat_code(self, code: str) -> str | None:
109
166
  try:
110
167
  import tempfile
111
168
 
@@ -124,21 +181,22 @@ class CodeCleaner(BaseModel, arbitrary_types_allowed=True):
124
181
  if result.returncode == 0:
125
182
  formatted_code = temp_path.read_text()
126
183
  else:
127
- print(f"Ruff formatting failed: {result.stderr}")
184
+ self.console.print(f"Ruff formatting failed: {result.stderr}")
128
185
  formatted_code = code
129
186
  except Exception as e:
130
- print(f"Error running Ruff: {e}")
187
+ self.console.print(f"Error running Ruff: {e}")
131
188
  formatted_code = code
132
189
  finally:
133
190
  with suppress(FileNotFoundError):
134
191
  temp_path.unlink()
135
192
  return formatted_code
136
193
  except Exception as e:
137
- print(f"Error during reformatting: {e}")
194
+ self.console.print(f"Error during reformatting: {e}")
138
195
  return code
139
196
 
140
197
 
141
- class ConfigManager(BaseModel, arbitrary_types_allowed=True):
198
+ @dataclass
199
+ class ConfigManager:
142
200
  our_path: Path
143
201
  pkg_path: Path
144
202
  pkg_name: str
@@ -262,14 +320,15 @@ class ConfigManager(BaseModel, arbitrary_types_allowed=True):
262
320
  return execute(cmd, **kwargs)
263
321
 
264
322
 
265
- class ProjectManager(BaseModel, arbitrary_types_allowed=True):
323
+ @dataclass
324
+ class ProjectManager:
266
325
  our_path: Path
267
326
  pkg_path: Path
268
- pkg_dir: Path | None = None
269
- pkg_name: str = "crackerjack"
270
327
  console: Console
271
328
  code_cleaner: CodeCleaner
272
329
  config_manager: ConfigManager
330
+ pkg_dir: Path | None = None
331
+ pkg_name: str = "crackerjack"
273
332
  dry_run: bool = False
274
333
 
275
334
  def run_interactive(self, hook: str) -> None:
@@ -321,39 +380,42 @@ class ProjectManager(BaseModel, arbitrary_types_allowed=True):
321
380
  return execute(cmd, **kwargs)
322
381
 
323
382
 
324
- class Crackerjack(BaseModel, arbitrary_types_allowed=True):
325
- our_path: Path = Path(__file__).parent
326
- pkg_path: Path = Path(Path.cwd())
383
+ @dataclass
384
+ class Crackerjack:
385
+ our_path: Path = field(default_factory=lambda: Path(__file__).parent)
386
+ pkg_path: Path = field(default_factory=lambda: Path(Path.cwd()))
327
387
  pkg_dir: Path | None = None
328
388
  pkg_name: str = "crackerjack"
329
389
  python_version: str = default_python_version
330
- console: Console = Console(force_terminal=True)
390
+ console: Console = field(default_factory=lambda: Console(force_terminal=True))
331
391
  dry_run: bool = False
332
392
  code_cleaner: CodeCleaner | None = None
333
393
  config_manager: ConfigManager | None = None
334
394
  project_manager: ProjectManager | None = None
335
395
 
336
- def __init__(self, **data: t.Any) -> None:
337
- super().__init__(**data)
338
- self.code_cleaner = CodeCleaner(console=self.console)
339
- self.config_manager = ConfigManager(
340
- our_path=self.our_path,
341
- pkg_path=self.pkg_path,
342
- pkg_name=self.pkg_name,
343
- console=self.console,
344
- python_version=self.python_version,
345
- dry_run=self.dry_run,
346
- )
347
- self.project_manager = ProjectManager(
348
- our_path=self.our_path,
349
- pkg_path=self.pkg_path,
350
- pkg_dir=self.pkg_dir,
351
- pkg_name=self.pkg_name,
352
- console=self.console,
353
- code_cleaner=self.code_cleaner,
354
- config_manager=self.config_manager,
355
- dry_run=self.dry_run,
356
- )
396
+ def __post_init__(self) -> None:
397
+ if self.code_cleaner is None:
398
+ self.code_cleaner = CodeCleaner(console=self.console)
399
+ if self.config_manager is None:
400
+ self.config_manager = ConfigManager(
401
+ our_path=self.our_path,
402
+ pkg_path=self.pkg_path,
403
+ pkg_name=self.pkg_name,
404
+ console=self.console,
405
+ python_version=self.python_version,
406
+ dry_run=self.dry_run,
407
+ )
408
+ if self.project_manager is None:
409
+ self.project_manager = ProjectManager(
410
+ our_path=self.our_path,
411
+ pkg_path=self.pkg_path,
412
+ pkg_dir=self.pkg_dir,
413
+ pkg_name=self.pkg_name,
414
+ console=self.console,
415
+ code_cleaner=self.code_cleaner,
416
+ config_manager=self.config_manager,
417
+ dry_run=self.dry_run,
418
+ )
357
419
 
358
420
  def _setup_package(self) -> None:
359
421
  self.pkg_name = self.pkg_path.stem.lower().replace("-", "_")
@@ -147,7 +147,7 @@ pythonPlatform = "Darwin"
147
147
 
148
148
  [project]
149
149
  name = "crackerjack"
150
- version = "0.14.3"
150
+ version = "0.14.4"
151
151
  description = "Default template for PDM package"
152
152
  requires-python = ">=3.13"
153
153
  readme = "README.md"
@@ -174,12 +174,12 @@ classifiers = [
174
174
  ]
175
175
  dependencies = [
176
176
  "autotyping>=24.9.0",
177
- "pre-commit>=4.1.0",
177
+ "pre-commit>=4.2.0",
178
178
  "pytest>=8.3.5",
179
179
  "pydantic>=2.10.6",
180
180
  "pdm-bump>=0.9.10",
181
181
  "pdm>=2.22.4",
182
- "uv>=0.6.6",
182
+ "uv>=0.6.9",
183
183
  "pytest-cov>=6.0.0",
184
184
  "pytest-mock>=3.14.0",
185
185
  "tomli-w>=1.2.0",
@@ -156,7 +156,7 @@ pythonPlatform = "Darwin"
156
156
 
157
157
  [project]
158
158
  name = "crackerjack"
159
- version = "0.14.4"
159
+ version = "0.14.5"
160
160
  description = "Default template for PDM package"
161
161
  requires-python = ">=3.13"
162
162
  readme = "README.md"
@@ -183,12 +183,12 @@ classifiers = [
183
183
  ]
184
184
  dependencies = [
185
185
  "autotyping>=24.9.0",
186
- "pre-commit>=4.1.0",
186
+ "pre-commit>=4.2.0",
187
187
  "pytest>=8.3.5",
188
188
  "pydantic>=2.10.6",
189
189
  "pdm-bump>=0.9.10",
190
190
  "pdm>=2.22.4",
191
- "uv>=0.6.6",
191
+ "uv>=0.6.9",
192
192
  "pytest-cov>=6.0.0",
193
193
  "pytest-mock>=3.14.0",
194
194
  "tomli-w>=1.2.0",
@@ -0,0 +1,20 @@
1
+
2
+ def test_func():
3
+ """This is a docstring."""
4
+ return True
5
+
6
+ class TestClass:
7
+ """Class docstring."""
8
+
9
+ def method1(self):
10
+ '''Method docstring.'''
11
+ pass
12
+
13
+ def method2(self):
14
+ """
15
+ Method docstring.
16
+
17
+ This is a multi-line docstring.
18
+
19
+ """
20
+ pass
@@ -1,12 +1,12 @@
1
1
  import os
2
2
  import typing as t
3
3
  from contextlib import suppress
4
+ from dataclasses import dataclass
4
5
  from enum import Enum
5
6
  from pathlib import Path
6
7
  from unittest.mock import MagicMock, patch
7
8
 
8
9
  import pytest
9
- from pydantic import BaseModel
10
10
  from rich.console import Console
11
11
  from crackerjack.crackerjack import (
12
12
  CodeCleaner,
@@ -25,18 +25,19 @@ class BumpOption(str, Enum):
25
25
  return self.value
26
26
 
27
27
 
28
- class OptionsForTesting(BaseModel):
28
+ @dataclass
29
+ class OptionsForTesting:
29
30
  commit: bool = False
30
31
  interactive: bool = False
31
32
  doc: bool = False
32
33
  no_config_updates: bool = False
33
- publish: t.Optional[BumpOption] = None
34
- bump: t.Optional[BumpOption] = None
34
+ publish: BumpOption | None = None
35
+ bump: BumpOption | None = None
35
36
  verbose: bool = False
36
37
  update_precommit: bool = False
37
38
  clean: bool = False
38
39
  test: bool = False
39
- all: t.Optional[BumpOption] = None
40
+ all: BumpOption | None = None
40
41
 
41
42
 
42
43
  @pytest.fixture
@@ -98,6 +99,8 @@ class TestCrackerjackProcess:
98
99
  kwargs["publish"] = BumpOption(kwargs["publish"])
99
100
  if "bump" in kwargs and isinstance(kwargs["bump"], str):
100
101
  kwargs["bump"] = BumpOption(kwargs["bump"])
102
+ if "all" in kwargs and isinstance(kwargs["all"], str):
103
+ kwargs["all"] = BumpOption(kwargs["all"])
101
104
  return OptionsForTesting(**kwargs)
102
105
 
103
106
  return _create_options
@@ -901,11 +904,15 @@ class TestCrackerjackProcess:
901
904
  Path(__file__).parent / "data" / "docstrings_sample.txt"
902
905
  ).read_text()
903
906
  cleaned_code = code_cleaner.remove_docstrings(code_with_docstrings)
907
+ print(cleaned_code)
904
908
  assert '"""This is a docstring."""' not in cleaned_code, (
905
909
  f"Got: {cleaned_code!r}"
906
910
  )
907
911
  assert '"""Class docstring."""' not in cleaned_code, f"Got: {cleaned_code!r}"
908
- assert '"""Method docstring."""' not in cleaned_code, f"Got: {cleaned_code!r}"
912
+ assert "'''Method docstring.'''" not in cleaned_code, f"Got: {cleaned_code!r}"
913
+ assert "This is a multi-line docstring." not in cleaned_code, (
914
+ f"Got: {cleaned_code!r}"
915
+ )
909
916
 
910
917
  def test_code_cleaner_remove_line_comments(self) -> None:
911
918
  from pathlib import Path
@@ -961,16 +968,25 @@ class TestCrackerjackProcess:
961
968
  def test_code_cleaner_reformat_code_failure(
962
969
  self, mock_execute: MagicMock, mock_console_print: MagicMock, tmp_path: Path
963
970
  ) -> None:
964
- code_cleaner = CodeCleaner(console=Console())
971
+ from rich.console import Console
972
+
973
+ console = Console()
974
+
975
+ code_cleaner = CodeCleaner(console=console)
965
976
  code_to_format = "def test_func():\n return True\n"
977
+
966
978
  with patch("subprocess.run") as mock_run:
967
979
  mock_run.return_value = MagicMock(returncode=1, stderr="Formatting error")
968
- with patch("builtins.print") as mock_print:
969
- with patch("pathlib.Path.write_text"):
980
+
981
+ with patch("pathlib.Path.write_text"):
982
+ with patch.object(console, "print") as mock_console_print_method:
970
983
  formatted_code = code_cleaner.reformat_code(code_to_format)
984
+
971
985
  assert formatted_code == code_to_format
986
+
972
987
  mock_run.assert_called_once()
973
- mock_print.assert_any_call(
988
+
989
+ mock_console_print_method.assert_any_call(
974
990
  "Ruff formatting failed: Formatting error"
975
991
  )
976
992
 
@@ -1,10 +0,0 @@
1
- """def test_func():
2
- \"\"\"This is a docstring.\"\"\"
3
- return True
4
-
5
- class TestClass:
6
- \"\"\"Class docstring.\"\"\"
7
- def method(self):
8
- \"\"\"Method docstring.\"\"\"
9
- pass
10
- """
File without changes
File without changes