duty 1.6.0__py3-none-any.whl → 1.6.1__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.
Files changed (82) hide show
  1. duty/__init__.py +49 -2
  2. duty/__main__.py +1 -1
  3. duty/_internal/__init__.py +0 -0
  4. duty/_internal/callables/__init__.py +34 -0
  5. duty/{callables → _internal/callables}/_io.py +2 -0
  6. duty/_internal/callables/autoflake.py +132 -0
  7. duty/_internal/callables/black.py +176 -0
  8. duty/_internal/callables/blacken_docs.py +92 -0
  9. duty/_internal/callables/build.py +76 -0
  10. duty/_internal/callables/coverage.py +716 -0
  11. duty/_internal/callables/flake8.py +222 -0
  12. duty/_internal/callables/git_changelog.py +178 -0
  13. duty/_internal/callables/griffe.py +227 -0
  14. duty/_internal/callables/interrogate.py +152 -0
  15. duty/_internal/callables/isort.py +573 -0
  16. duty/_internal/callables/mkdocs.py +256 -0
  17. duty/_internal/callables/mypy.py +496 -0
  18. duty/_internal/callables/pytest.py +475 -0
  19. duty/_internal/callables/ruff.py +399 -0
  20. duty/_internal/callables/safety.py +82 -0
  21. duty/_internal/callables/ssort.py +38 -0
  22. duty/_internal/callables/twine.py +284 -0
  23. duty/_internal/cli.py +322 -0
  24. duty/_internal/collection.py +246 -0
  25. duty/_internal/context.py +111 -0
  26. duty/{debug.py → _internal/debug.py} +13 -15
  27. duty/_internal/decorator.py +111 -0
  28. duty/_internal/exceptions.py +12 -0
  29. duty/_internal/tools/__init__.py +41 -0
  30. duty/{tools → _internal/tools}/_autoflake.py +8 -4
  31. duty/{tools → _internal/tools}/_base.py +15 -2
  32. duty/{tools → _internal/tools}/_black.py +5 -5
  33. duty/{tools → _internal/tools}/_blacken_docs.py +10 -5
  34. duty/{tools → _internal/tools}/_build.py +4 -4
  35. duty/{tools → _internal/tools}/_coverage.py +8 -4
  36. duty/{tools → _internal/tools}/_flake8.py +10 -12
  37. duty/{tools → _internal/tools}/_git_changelog.py +8 -4
  38. duty/{tools → _internal/tools}/_griffe.py +8 -4
  39. duty/{tools → _internal/tools}/_interrogate.py +4 -4
  40. duty/{tools → _internal/tools}/_isort.py +8 -6
  41. duty/{tools → _internal/tools}/_mkdocs.py +8 -4
  42. duty/{tools → _internal/tools}/_mypy.py +5 -5
  43. duty/{tools → _internal/tools}/_pytest.py +8 -4
  44. duty/{tools → _internal/tools}/_ruff.py +11 -5
  45. duty/{tools → _internal/tools}/_safety.py +13 -8
  46. duty/{tools → _internal/tools}/_ssort.py +10 -6
  47. duty/{tools → _internal/tools}/_twine.py +11 -5
  48. duty/_internal/tools/_yore.py +96 -0
  49. duty/_internal/validation.py +266 -0
  50. duty/callables/__init__.py +4 -4
  51. duty/callables/autoflake.py +11 -126
  52. duty/callables/black.py +12 -171
  53. duty/callables/blacken_docs.py +11 -86
  54. duty/callables/build.py +12 -71
  55. duty/callables/coverage.py +12 -711
  56. duty/callables/flake8.py +12 -217
  57. duty/callables/git_changelog.py +12 -173
  58. duty/callables/griffe.py +12 -222
  59. duty/callables/interrogate.py +12 -147
  60. duty/callables/isort.py +12 -568
  61. duty/callables/mkdocs.py +12 -251
  62. duty/callables/mypy.py +11 -490
  63. duty/callables/pytest.py +12 -470
  64. duty/callables/ruff.py +12 -394
  65. duty/callables/safety.py +11 -76
  66. duty/callables/ssort.py +12 -33
  67. duty/callables/twine.py +12 -279
  68. duty/cli.py +10 -316
  69. duty/collection.py +12 -228
  70. duty/context.py +12 -107
  71. duty/decorator.py +12 -108
  72. duty/exceptions.py +13 -10
  73. duty/tools.py +63 -0
  74. duty/validation.py +12 -262
  75. {duty-1.6.0.dist-info → duty-1.6.1.dist-info}/METADATA +4 -3
  76. duty-1.6.1.dist-info/RECORD +81 -0
  77. {duty-1.6.0.dist-info → duty-1.6.1.dist-info}/WHEEL +1 -1
  78. {duty-1.6.0.dist-info → duty-1.6.1.dist-info}/entry_points.txt +1 -1
  79. duty/tools/__init__.py +0 -50
  80. duty/tools/_yore.py +0 -54
  81. duty-1.6.0.dist-info/RECORD +0 -55
  82. {duty-1.6.0.dist-info → duty-1.6.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,14 +1,13 @@
1
- """Callable for [Black](https://github.com/psf/black)."""
2
-
3
1
  from __future__ import annotations
4
2
 
5
- from duty.tools._base import Tool
3
+ from duty._internal.tools._base import Tool
6
4
 
7
5
 
8
6
  class black(Tool): # noqa: N801
9
7
  """Call [Black](https://github.com/psf/black)."""
10
8
 
11
9
  cli_name = "black"
10
+ """The name of the executable on PATH."""
12
11
 
13
12
  def __init__(
14
13
  self,
@@ -179,6 +178,7 @@ class black(Tool): # noqa: N801
179
178
  super().__init__(cli_args)
180
179
 
181
180
  def __call__(self) -> None:
182
- from black import main as run_black
181
+ """Run the command."""
182
+ from black import main as run_black # noqa: PLC0415
183
183
 
184
- return run_black(self.cli_args, prog_name="black")
184
+ run_black(self.cli_args, prog_name="black")
@@ -1,5 +1,3 @@
1
- """Callable for [blacken-docs](https://github.com/adamchainz/blacken-docs)."""
2
-
3
1
  from __future__ import annotations
4
2
 
5
3
  import re
@@ -7,7 +5,7 @@ from pathlib import Path
7
5
  from re import Pattern
8
6
  from typing import TYPE_CHECKING
9
7
 
10
- from duty.tools._base import Tool
8
+ from duty._internal.tools._base import Tool
11
9
 
12
10
  if TYPE_CHECKING:
13
11
  from collections.abc import Sequence
@@ -17,6 +15,7 @@ class blacken_docs(Tool): # noqa: N801
17
15
  """Call [blacken-docs](https://github.com/adamchainz/blacken-docs)."""
18
16
 
19
17
  cli_name = "blacken-docs"
18
+ """The name of the executable on PATH."""
20
19
 
21
20
  def __init__(
22
21
  self,
@@ -64,11 +63,17 @@ class blacken_docs(Tool): # noqa: N801
64
63
 
65
64
  @property
66
65
  def cli_command(self) -> str:
66
+ """The equivalent CLI command."""
67
67
  raise ValueError("This command cannot be translated to a CLI command.")
68
68
 
69
69
  def __call__(self) -> int:
70
- import black
71
- from blacken_docs import format_file
70
+ """Run the command.
71
+
72
+ Returns:
73
+ The exit code of the command.
74
+ """
75
+ import black # noqa: PLC0415
76
+ from blacken_docs import format_file # noqa: PLC0415
72
77
 
73
78
  # Restore locals.
74
79
  exts = self.py_args["exts"]
@@ -1,16 +1,15 @@
1
- """Callable for [build](https://github.com/pypa/build)."""
2
-
3
1
  from __future__ import annotations
4
2
 
5
3
  from typing import Literal
6
4
 
7
- from duty.tools._base import Tool
5
+ from duty._internal.tools._base import Tool
8
6
 
9
7
 
10
8
  class build(Tool): # noqa: N801
11
9
  """Call [build](https://github.com/pypa/build)."""
12
10
 
13
11
  cli_name = "pyproject-build"
12
+ """The name of the executable on PATH."""
14
13
 
15
14
  def __init__(
16
15
  self,
@@ -79,6 +78,7 @@ class build(Tool): # noqa: N801
79
78
  super().__init__(cli_args)
80
79
 
81
80
  def __call__(self) -> None:
82
- from build.__main__ import main as run_build
81
+ """Run the command."""
82
+ from build.__main__ import main as run_build # noqa: PLC0415
83
83
 
84
84
  run_build(self.cli_args)
@@ -1,16 +1,15 @@
1
- """Callable for [Coverage.py](https://github.com/nedbat/coveragepy)."""
2
-
3
1
  from __future__ import annotations
4
2
 
5
3
  from typing import Literal
6
4
 
7
- from duty.tools._base import Tool
5
+ from duty._internal.tools._base import Tool
8
6
 
9
7
 
10
8
  class coverage(Tool): # noqa: N801
11
9
  """Call [Coverage.py](https://github.com/nedbat/coveragepy)."""
12
10
 
13
11
  cli_name = "coverage"
12
+ """The name of the executable on PATH."""
14
13
 
15
14
  @classmethod
16
15
  def annotate(
@@ -716,6 +715,11 @@ class coverage(Tool): # noqa: N801
716
715
  return cls(cli_args)
717
716
 
718
717
  def __call__(self) -> int | None:
719
- from coverage.cmdline import main as run_coverage
718
+ """Run the command.
719
+
720
+ Returns:
721
+ The exit code of the command.
722
+ """
723
+ from coverage.cmdline import main as run_coverage # noqa: PLC0415
720
724
 
721
725
  return run_coverage(self.cli_args)
@@ -1,17 +1,15 @@
1
- """Callable for [Flake8](https://github.com/PyCQA/flake8)."""
2
-
3
1
  from __future__ import annotations
4
2
 
5
- import sys
6
3
  from typing import Literal
7
4
 
8
- from duty.tools._base import Tool
5
+ from duty._internal.tools._base import Tool
9
6
 
10
7
 
11
8
  class flake8(Tool): # noqa: N801
12
9
  """Call [Flake8](https://github.com/PyCQA/flake8)."""
13
10
 
14
11
  cli_name = "flake8"
12
+ """The name of the executable on PATH."""
15
13
 
16
14
  def __init__(
17
15
  self,
@@ -220,11 +218,11 @@ class flake8(Tool): # noqa: N801
220
218
  super().__init__(cli_args)
221
219
 
222
220
  def __call__(self) -> int:
223
- from flake8.main import main as run_flake8
224
-
225
- old_sys_argv = sys.argv
226
- sys.argv = ["flake8", *self.cli_args]
227
- try:
228
- return run_flake8()
229
- finally:
230
- sys.argv = old_sys_argv
221
+ """Run the command.
222
+
223
+ Returns:
224
+ The exit code of the command.
225
+ """
226
+ from flake8.main.cli import main as run_flake8 # noqa: PLC0415
227
+
228
+ return run_flake8(self.cli_args)
@@ -1,16 +1,15 @@
1
- """Callable for [git-changelog](https://github.com/pawamoy/git-changelog)."""
2
-
3
1
  from __future__ import annotations
4
2
 
5
3
  from typing import Literal
6
4
 
7
- from duty.tools._base import Tool
5
+ from duty._internal.tools._base import Tool
8
6
 
9
7
 
10
8
  class git_changelog(Tool): # noqa: N801
11
9
  """Call [git-changelog](https://github.com/pawamoy/git-changelog)."""
12
10
 
13
11
  cli_name = "git-changelog"
12
+ """The name of the executable on PATH."""
14
13
 
15
14
  def __init__(
16
15
  self,
@@ -181,6 +180,11 @@ class git_changelog(Tool): # noqa: N801
181
180
  super().__init__(cli_args)
182
181
 
183
182
  def __call__(self) -> int:
184
- from git_changelog.cli import main as run_git_changelog
183
+ """Run the command.
184
+
185
+ Returns:
186
+ The exit code of the command.
187
+ """
188
+ from git_changelog.cli import main as run_git_changelog # noqa: PLC0415
185
189
 
186
190
  return run_git_changelog(self.cli_args)
@@ -1,16 +1,15 @@
1
- """Callable for [Griffe](https://github.com/mkdocstrings/griffe)."""
2
-
3
1
  from __future__ import annotations
4
2
 
5
3
  from typing import Literal
6
4
 
7
- from duty.tools._base import Tool
5
+ from duty._internal.tools._base import Tool
8
6
 
9
7
 
10
8
  class griffe(Tool): # noqa: N801
11
9
  """Call [Griffe](https://github.com/mkdocstrings/griffe)."""
12
10
 
13
11
  cli_name = "griffe"
12
+ """The name of the executable on PATH."""
14
13
 
15
14
  @classmethod
16
15
  def check(
@@ -221,6 +220,11 @@ class griffe(Tool): # noqa: N801
221
220
  return cls(cli_args)
222
221
 
223
222
  def __call__(self) -> int:
224
- from griffe import main as run_griffe
223
+ """Run the command.
224
+
225
+ Returns:
226
+ The exit code of the command.
227
+ """
228
+ from griffe import main as run_griffe # noqa: PLC0415
225
229
 
226
230
  return run_griffe(self.cli_args)
@@ -1,10 +1,8 @@
1
- """Callable for [Interrogate](https://github.com/econchick/interrogate)."""
2
-
3
1
  from __future__ import annotations
4
2
 
5
3
  from typing import Literal
6
4
 
7
- from duty.tools._base import Tool
5
+ from duty._internal.tools._base import Tool
8
6
 
9
7
  _BADGE_STYLE = Literal["flat", "flat-square", "flat-square-modified", "for-the-badge", "plastic", "social"]
10
8
 
@@ -13,6 +11,7 @@ class interrogate(Tool): # noqa: N801
13
11
  """Call [Interrogate](https://github.com/econchick/interrogate)."""
14
12
 
15
13
  cli_name = "interrogate"
14
+ """The name of the executable on PATH."""
16
15
 
17
16
  def __init__(
18
17
  self,
@@ -155,6 +154,7 @@ class interrogate(Tool): # noqa: N801
155
154
  super().__init__(cli_args)
156
155
 
157
156
  def __call__(self) -> None:
158
- from interrogate.cli import main as run_interrogate
157
+ """Run the command."""
158
+ from interrogate.cli import main as run_interrogate # noqa: PLC0415
159
159
 
160
160
  return run_interrogate(self.cli_args)
@@ -1,10 +1,8 @@
1
- """Callable for [isort](https://github.com/PyCQA/isort)."""
2
-
3
1
  from __future__ import annotations
4
2
 
5
3
  from typing import Literal
6
4
 
7
- from duty.tools._base import Tool
5
+ from duty._internal.tools._base import Tool
8
6
 
9
7
  Multiline = Literal[
10
8
  "GRID",
@@ -37,6 +35,9 @@ Multiline = Literal[
37
35
  class isort(Tool): # noqa: N801
38
36
  """Call [isort](https://github.com/PyCQA/isort)."""
39
37
 
38
+ cli_name = "isort"
39
+ """The name of the executable on PATH."""
40
+
40
41
  def __init__(
41
42
  self,
42
43
  *files: str,
@@ -573,7 +574,8 @@ class isort(Tool): # noqa: N801
573
574
 
574
575
  super().__init__(cli_args)
575
576
 
576
- def __call__(self) -> int:
577
- from isort.main import main as run_isort
577
+ def __call__(self) -> None:
578
+ """Run the command."""
579
+ from isort.main import main as run_isort # noqa: PLC0415
578
580
 
579
- return run_isort(self.cli_args)
581
+ run_isort(self.cli_args)
@@ -1,14 +1,13 @@
1
- """Callable for [MkDocs](https://github.com/mkdocs/mkdocs)."""
2
-
3
1
  from __future__ import annotations
4
2
 
5
- from duty.tools._base import Tool
3
+ from duty._internal.tools._base import Tool
6
4
 
7
5
 
8
6
  class mkdocs(Tool): # noqa: N801
9
7
  """Call [MkDocs](https://github.com/mkdocs/mkdocs)."""
10
8
 
11
9
  cli_name = "mkdocs"
10
+ """The name of the executable on PATH."""
12
11
 
13
12
  @classmethod
14
13
  def build(
@@ -266,6 +265,11 @@ class mkdocs(Tool): # noqa: N801
266
265
  return cls(cli_args)
267
266
 
268
267
  def __call__(self) -> int:
269
- from mkdocs.__main__ import cli as run_mkdocs
268
+ """Run the command.
269
+
270
+ Returns:
271
+ The exit code of the command.
272
+ """
273
+ from mkdocs.__main__ import cli as run_mkdocs # noqa: PLC0415
270
274
 
271
275
  return run_mkdocs(self.cli_args)
@@ -1,16 +1,15 @@
1
- """Callable for [Mypy](https://github.com/python/mypy)."""
2
-
3
1
  from __future__ import annotations
4
2
 
5
3
  from typing import Literal
6
4
 
7
- from duty.tools._base import LazyStderr, LazyStdout, Tool
5
+ from duty._internal.tools._base import LazyStderr, LazyStdout, Tool
8
6
 
9
7
 
10
8
  class mypy(Tool): # noqa: N801
11
9
  """Call [Mypy](https://github.com/python/mypy)."""
12
10
 
13
11
  cli_name = "mypy"
12
+ """The name of the executable on PATH."""
14
13
 
15
14
  def __init__(
16
15
  self,
@@ -492,9 +491,10 @@ class mypy(Tool): # noqa: N801
492
491
  super().__init__(cli_args)
493
492
 
494
493
  def __call__(self) -> None:
495
- from mypy.main import main as run_mypy
494
+ """Run the command."""
495
+ from mypy.main import main as run_mypy # noqa: PLC0415
496
496
 
497
- return run_mypy(
497
+ run_mypy(
498
498
  args=self.cli_args,
499
499
  stdout=LazyStdout(),
500
500
  stderr=LazyStderr(),
@@ -1,16 +1,15 @@
1
- """Callable for [pytest](https://github.com/pytest-dev/pytest)."""
2
-
3
1
  from __future__ import annotations
4
2
 
5
3
  from typing import Literal
6
4
 
7
- from duty.tools._base import Tool
5
+ from duty._internal.tools._base import Tool
8
6
 
9
7
 
10
8
  class pytest(Tool): # noqa: N801
11
9
  """Call [pytest](https://github.com/pytest-dev/pytest)."""
12
10
 
13
11
  cli_name = "pytest"
12
+ """The name of the executable on PATH."""
14
13
 
15
14
  def __init__(
16
15
  self,
@@ -478,6 +477,11 @@ class pytest(Tool): # noqa: N801
478
477
  super().__init__(cli_args)
479
478
 
480
479
  def __call__(self) -> int:
481
- from pytest import main as run_pytest # noqa: PT013
480
+ """Run the command.
481
+
482
+ Returns:
483
+ The exit code of the command.
484
+ """
485
+ from pytest import main as run_pytest # noqa: PT013,PLC0415
482
486
 
483
487
  return run_pytest(self.cli_args)
@@ -1,5 +1,3 @@
1
- """Callable for [Ruff](https://github.com/astral-sh/ruff)."""
2
-
3
1
  from __future__ import annotations
4
2
 
5
3
  import os
@@ -7,12 +5,12 @@ import subprocess
7
5
  import sys
8
6
  from functools import cache
9
7
 
10
- from duty.tools._base import Tool
8
+ from duty._internal.tools._base import Tool
11
9
 
12
10
 
13
11
  @cache
14
12
  def _find_ruff() -> str:
15
- from ruff.__main__ import find_ruff_bin
13
+ from ruff.__main__ import find_ruff_bin # noqa: PLC0415
16
14
 
17
15
  try:
18
16
  return find_ruff_bin()
@@ -34,6 +32,7 @@ class ruff(Tool): # noqa: N801
34
32
  """Call [Ruff](https://github.com/astral-sh/ruff)."""
35
33
 
36
34
  cli_name = "ruff"
35
+ """The name of the executable on PATH."""
37
36
 
38
37
  @classmethod
39
38
  def check(
@@ -319,6 +318,7 @@ class ruff(Tool): # noqa: N801
319
318
  @classmethod
320
319
  def rule(
321
320
  cls,
321
+ rule: str,
322
322
  *,
323
323
  output_format: str | None = None,
324
324
  verbose: bool = False,
@@ -328,12 +328,13 @@ class ruff(Tool): # noqa: N801
328
328
  """Explain a rule.
329
329
 
330
330
  Parameters:
331
+ rule: A rule code, or `--all`.
331
332
  output_format: Output format (default: pretty, possible values: text, json, pretty).
332
333
  verbose: Enable verbose logging.
333
334
  quiet: Print lint violations, but nothing else.
334
335
  silent: Disable all logging (but still exit with status code "1" upon detecting lint violations).
335
336
  """
336
- cli_args = ["rule"]
337
+ cli_args = ["rule", rule]
337
338
 
338
339
  if output_format:
339
340
  cli_args.append("--format")
@@ -441,6 +442,11 @@ class ruff(Tool): # noqa: N801
441
442
  return cls(cli_args)
442
443
 
443
444
  def __call__(self) -> int:
445
+ """Run the command.
446
+
447
+ Returns:
448
+ The exit code of the command.
449
+ """
444
450
  process = subprocess.run( # noqa: S603
445
451
  [_find_ruff(), *self.cli_args],
446
452
  capture_output=True,
@@ -1,5 +1,3 @@
1
- """Callable for [Safety](https://github.com/pyupio/safety)."""
2
-
3
1
  from __future__ import annotations
4
2
 
5
3
  import importlib
@@ -7,7 +5,7 @@ import sys
7
5
  from io import StringIO
8
6
  from typing import TYPE_CHECKING, Literal, cast
9
7
 
10
- from duty.tools._base import Tool
8
+ from duty._internal.tools._base import Tool
11
9
 
12
10
  if TYPE_CHECKING:
13
11
  from collections.abc import Sequence
@@ -17,6 +15,7 @@ class safety(Tool): # noqa: N801
17
15
  """Call [Safety](https://github.com/pyupio/safety)."""
18
16
 
19
17
  cli_name = "safety"
18
+ """The name of the executable on PATH."""
20
19
 
21
20
  @classmethod
22
21
  def check(
@@ -44,9 +43,15 @@ class safety(Tool): # noqa: N801
44
43
 
45
44
  @property
46
45
  def cli_command(self) -> str:
46
+ """The equivalent CLI command."""
47
47
  raise ValueError("This command cannot be translated to a CLI command.")
48
48
 
49
49
  def __call__(self) -> bool:
50
+ """Run the command.
51
+
52
+ Returns:
53
+ False when vulnerabilities are found.
54
+ """
50
55
  requirements = self.py_args["requirements"]
51
56
  ignore_vulns = self.py_args["ignore_vulns"]
52
57
  formatter = self.py_args["formatter"]
@@ -64,19 +69,19 @@ class safety(Tool): # noqa: N801
64
69
  importlib.invalidate_caches()
65
70
 
66
71
  # reload original, unpatched safety
67
- from safety.formatter import SafetyFormatter
68
- from safety.safety import calculate_remediations, check
69
- from safety.util import read_requirements
72
+ from safety.formatter import SafetyFormatter # noqa: PLC0415
73
+ from safety.safety import calculate_remediations, check # noqa: PLC0415
74
+ from safety.util import read_requirements # noqa: PLC0415
70
75
 
71
76
  # check using safety as a library
72
77
  if isinstance(requirements, (list, tuple, set)):
73
78
  requirements = "\n".join(requirements)
74
- packages = list(read_requirements(StringIO(cast(str, requirements))))
79
+ packages = list(read_requirements(StringIO(cast("str", requirements))))
75
80
 
76
81
  # TODO: Safety 3 support, merge once support for v2 is dropped.
77
82
  check_kwargs = {"packages": packages, "ignore_vulns": ignore_vulns}
78
83
  try:
79
- from safety.auth.cli_utils import build_client_session
84
+ from safety.auth.cli_utils import build_client_session # noqa: PLC0415
80
85
 
81
86
  client_session, _ = build_client_session()
82
87
  check_kwargs["session"] = client_session
@@ -1,16 +1,15 @@
1
- """Callable for [ssort](https://github.com/bwhmather/ssort)."""
2
-
3
1
  from __future__ import annotations
4
2
 
5
3
  import sys
6
4
 
7
- from duty.tools._base import Tool
5
+ from duty._internal.tools._base import Tool
8
6
 
9
7
 
10
8
  class ssort(Tool): # noqa: N801
11
9
  """Call [ssort](https://github.com/bwhmather/ssort)."""
12
10
 
13
11
  cli_name = "ssort"
12
+ """The name of the executable on PATH."""
14
13
 
15
14
  def __init__(
16
15
  self,
@@ -33,12 +32,17 @@ class ssort(Tool): # noqa: N801
33
32
  if check:
34
33
  cli_args.append("--check")
35
34
 
36
- def __call__(self) -> int:
37
- from ssort._main import main as run_ssort
35
+ def __call__(self) -> None:
36
+ """Run the command.
37
+
38
+ Returns:
39
+ The exit code of the command.
40
+ """
41
+ from ssort._main import main as run_ssort # noqa: PLC0415
38
42
 
39
43
  old_sys_argv = sys.argv
40
44
  sys.argv = ["ssort", *self.cli_args]
41
45
  try:
42
- return run_ssort()
46
+ run_ssort()
43
47
  finally:
44
48
  sys.argv = old_sys_argv
@@ -1,14 +1,15 @@
1
- """Callable for [Twine](https://github.com/pypa/twine)."""
2
-
3
1
  from __future__ import annotations
4
2
 
5
- from duty.tools._base import Tool
3
+ from typing import Any
4
+
5
+ from duty._internal.tools._base import Tool
6
6
 
7
7
 
8
8
  class twine(Tool): # noqa: N801
9
9
  """Call [Twine](https://github.com/pypa/twine)."""
10
10
 
11
11
  cli_name = "twine"
12
+ """The name of the executable on PATH."""
12
13
 
13
14
  @classmethod
14
15
  def check(
@@ -283,7 +284,12 @@ class twine(Tool): # noqa: N801
283
284
 
284
285
  return cls(cli_args)
285
286
 
286
- def __call__(self) -> None:
287
- from twine.cli import dispatch as run_twine
287
+ def __call__(self) -> Any:
288
+ """Run the command.
289
+
290
+ Returns:
291
+ The return value of the corresponding Twine command / entrypoint.
292
+ """
293
+ from twine.cli import dispatch as run_twine # noqa: PLC0415
288
294
 
289
295
  return run_twine(self.cli_args)