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
@@ -0,0 +1,399 @@
1
+ # YORE: Bump 2: Remove file.
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import subprocess
7
+ import sys
8
+ from functools import cache
9
+
10
+ from failprint import lazy
11
+
12
+
13
+ @cache
14
+ def _find_ruff() -> str:
15
+ from ruff.__main__ import find_ruff_bin # noqa: PLC0415
16
+
17
+ try:
18
+ return find_ruff_bin()
19
+ except FileNotFoundError:
20
+ paths = os.environ["PATH"]
21
+ for path in paths.split(os.pathsep):
22
+ ruff = os.path.join(path, "ruff")
23
+ if os.path.exists(ruff):
24
+ return ruff
25
+ py_version = f"{sys.version_info[0]}.{sys.version_info[1]}"
26
+ pypackages_bin = os.path.join("__pypackages__", py_version, "bin")
27
+ ruff = os.path.join(pypackages_bin, "ruff")
28
+ if os.path.exists(ruff):
29
+ return ruff
30
+ return "ruff"
31
+
32
+
33
+ def _run(
34
+ *args: str,
35
+ verbose: bool = False,
36
+ quiet: bool = False,
37
+ silent: bool = False,
38
+ ) -> int:
39
+ cli_args = list(args)
40
+
41
+ if verbose:
42
+ cli_args.append("--verbose")
43
+
44
+ if quiet:
45
+ cli_args.append("--quiet")
46
+
47
+ if silent:
48
+ cli_args.append("--silent")
49
+
50
+ process = subprocess.run([_find_ruff(), *cli_args], capture_output=True, text=True, check=False) # noqa: S603
51
+ print(process.stdout) # noqa: T201
52
+ return process.returncode
53
+
54
+
55
+ @lazy(name="ruff.check")
56
+ def check(
57
+ *files: str,
58
+ config: str | None = None,
59
+ fix: bool | None = None,
60
+ show_source: bool | None = None,
61
+ show_fixes: bool | None = None,
62
+ diff: bool | None = None,
63
+ watch: bool | None = None,
64
+ fix_only: bool | None = None,
65
+ output_format: str | None = None,
66
+ statistics: bool | None = None,
67
+ add_noqa: bool | None = None,
68
+ show_files: bool | None = None,
69
+ show_settings: bool | None = None,
70
+ select: list[str] | None = None,
71
+ ignore: list[str] | None = None,
72
+ extend_select: list[str] | None = None,
73
+ per_file_ignores: dict[str, list[str]] | None = None,
74
+ fixable: list[str] | None = None,
75
+ unfixable: list[str] | None = None,
76
+ exclude: list[str] | None = None,
77
+ extend_exclude: list[str] | None = None,
78
+ respect_gitignore: bool | None = None,
79
+ force_exclude: bool | None = None,
80
+ no_cache: bool | None = None,
81
+ isolated: bool | None = None,
82
+ cache_dir: str | None = None,
83
+ stdin_filename: str | None = None,
84
+ exit_zero: bool | None = None,
85
+ exit_non_zero_on_fix: bool | None = None,
86
+ verbose: bool = False,
87
+ quiet: bool = False,
88
+ silent: bool = False,
89
+ ) -> int:
90
+ """Run Ruff on the given files or directories.
91
+
92
+ Parameters:
93
+ fix: Attempt to automatically fix lint violations.
94
+ config: Path to the `pyproject.toml` or `ruff.toml` file to use for configuration.
95
+ show_source: Show violations with source code.
96
+ show_fixes: Show an enumeration of all autofixed lint violations.
97
+ diff: Avoid writing any fixed files back; instead, output a diff for each changed file to stdout.
98
+ watch: Run in watch mode by re-running whenever files change.
99
+ fix_only: Fix any fixable lint violations, but don't report on leftover violations. Implies `--fix`.
100
+ output_format: Output serialization format for violations (env: RUFF_FORMAT=) (possible values: text, json, junit, grouped, github, gitlab, pylint).
101
+ statistics: Show counts for every rule with at least one violation.
102
+ add_noqa: Enable automatic additions of `noqa` directives to failing lines.
103
+ show_files: See the files Ruff will be run against with the current settings.
104
+ show_settings: See the settings Ruff will use to lint a given Python file.
105
+ select: Comma-separated list of rule codes to enable (or ALL, to enable all rules).
106
+ ignore: Comma-separated list of rule codes to disable.
107
+ extend_select: Like --select, but adds additional rule codes on top of the selected ones.
108
+ per_file_ignores: List of mappings from file pattern to code to exclude.
109
+ fixable: List of rule codes to treat as eligible for autofix. Only applicable when autofix itself is enabled (e.g., via `--fix`).
110
+ unfixable: List of rule codes to treat as ineligible for autofix. Only applicable when autofix itself is enabled (e.g., via `--fix`).
111
+ exclude: List of paths, used to omit files and/or directories from analysis.
112
+ extend_exclude: Like --exclude, but adds additional files and directories on top of those already excluded.
113
+ respect_gitignore: Respect file exclusions via `.gitignore` and other standard ignore files.
114
+ force_exclude: Enforce exclusions, even for paths passed to Ruff directly on the command-line.
115
+ no_cache: Disable cache reads.
116
+ isolated: Ignore all configuration files.
117
+ cache_dir: Path to the cache directory (env: RUFF_CACHE_DIR=).
118
+ stdin_filename: The name of the file when passing it through stdin.
119
+ exit_zero: Exit with status code "0", even upon detecting lint violations.
120
+ exit_non_zero_on_fix: Exit with a non-zero status code if any files were modified via autofix, even if no lint violations remain.
121
+ verbose: Enable verbose logging.
122
+ quiet: Print lint violations, but nothing else.
123
+ silent: Disable all logging (but still exit with status code "1" upon detecting lint violations).
124
+ """
125
+ cli_args = list(files)
126
+
127
+ if fix:
128
+ cli_args.append("--fix")
129
+
130
+ if show_source:
131
+ cli_args.append("--show-source")
132
+
133
+ if show_fixes:
134
+ cli_args.append("--show-fixes")
135
+
136
+ if diff:
137
+ cli_args.append("--diff")
138
+
139
+ if watch:
140
+ cli_args.append("--watch")
141
+
142
+ if fix_only:
143
+ cli_args.append("--fix-only")
144
+
145
+ if output_format:
146
+ cli_args.append("--format")
147
+ cli_args.append(output_format)
148
+
149
+ if config:
150
+ cli_args.append("--config")
151
+ cli_args.append(config)
152
+
153
+ if statistics:
154
+ cli_args.append("--statistics")
155
+
156
+ if add_noqa:
157
+ cli_args.append("--add-noqa")
158
+
159
+ if show_files:
160
+ cli_args.append("--show-files")
161
+
162
+ if show_settings:
163
+ cli_args.append("--show-settings")
164
+
165
+ if select:
166
+ cli_args.append("--select")
167
+ cli_args.append(",".join(select))
168
+
169
+ if ignore:
170
+ cli_args.append("--ignore")
171
+ cli_args.append(",".join(ignore))
172
+
173
+ if extend_select:
174
+ cli_args.append("--extend-select")
175
+ cli_args.append(",".join(extend_select))
176
+
177
+ if per_file_ignores:
178
+ cli_args.append("--per-file-ignores")
179
+ cli_args.append(
180
+ " ".join(f"{path}:{','.join(codes)}" for path, codes in per_file_ignores.items()),
181
+ )
182
+
183
+ if fixable:
184
+ cli_args.append("--fixable")
185
+ cli_args.append(",".join(fixable))
186
+
187
+ if unfixable:
188
+ cli_args.append("--unfixable")
189
+ cli_args.append(",".join(unfixable))
190
+
191
+ if exclude:
192
+ cli_args.append("--exclude")
193
+ cli_args.append(",".join(exclude))
194
+
195
+ if extend_exclude:
196
+ cli_args.append("--extend-exclude")
197
+ cli_args.append(",".join(extend_exclude))
198
+
199
+ if respect_gitignore:
200
+ cli_args.append("--respect-gitignore")
201
+
202
+ if force_exclude:
203
+ cli_args.append("--force-exclude")
204
+
205
+ if no_cache:
206
+ cli_args.append("--no-cache")
207
+
208
+ if isolated:
209
+ cli_args.append("--isolated")
210
+
211
+ if cache_dir:
212
+ cli_args.append("--cache-dir")
213
+ cli_args.append(cache_dir)
214
+
215
+ if stdin_filename:
216
+ cli_args.append("--stdin-filename")
217
+ cli_args.append(stdin_filename)
218
+
219
+ if exit_zero:
220
+ cli_args.append("--exit-zero")
221
+
222
+ if exit_non_zero_on_fix:
223
+ cli_args.append("--exit-non-zero-on-fix")
224
+
225
+ return _run("check", *cli_args, verbose=verbose, quiet=quiet, silent=silent)
226
+
227
+
228
+ @lazy(name="ruff.format")
229
+ def format(
230
+ *files: str,
231
+ config: str | None = None,
232
+ check: bool | None = None,
233
+ diff: bool | None = None,
234
+ target_version: str | None = None,
235
+ preview: bool | None = None,
236
+ exclude: list[str] | None = None,
237
+ extend_exclude: list[str] | None = None,
238
+ respect_gitignore: bool | None = None,
239
+ force_exclude: bool | None = None,
240
+ no_cache: bool | None = None,
241
+ isolated: bool | None = None,
242
+ cache_dir: str | None = None,
243
+ stdin_filename: str | None = None,
244
+ verbose: bool = False,
245
+ quiet: bool = False,
246
+ silent: bool = False,
247
+ ) -> int:
248
+ """Run Ruff formatter on the given files or directories.
249
+
250
+ Parameters:
251
+ check: Avoid writing any formatted files back; instead, exit with a non-zero status code if any files would have been modified, and zero otherwise
252
+ config: Path to the `pyproject.toml` or `ruff.toml` file to use for configuration
253
+ diff: Avoid writing any fixed files back; instead, output a diff for each changed file to stdout
254
+ target_version: The minimum Python version that should be supported [possible values: py37, py38, py39, py310, py311, py312]
255
+ preview: Enable preview mode; enables unstable formatting
256
+ exclude: List of paths, used to omit files and/or directories from analysis
257
+ extend_exclude: Like --exclude, but adds additional files and directories on top of those already excluded
258
+ respect_gitignore: Respect file exclusions via `.gitignore` and other standard ignore files
259
+ force_exclude: Enforce exclusions, even for paths passed to Ruff directly on the command-line
260
+ no_cache: Disable cache reads
261
+ isolated: Ignore all configuration files
262
+ cache_dir: Path to the cache directory [env: RUFF_CACHE_DIR=]
263
+ stdin_filename: The name of the file when passing it through stdin
264
+ verbose: Enable verbose logging.
265
+ quiet: Print lint violations, but nothing else.
266
+ silent: Disable all logging (but still exit with status code "1" upon detecting lint violations).
267
+ """
268
+ cli_args = list(files)
269
+
270
+ if check:
271
+ cli_args.append("--check")
272
+
273
+ if diff:
274
+ cli_args.append("--diff")
275
+
276
+ if config:
277
+ cli_args.append("--config")
278
+ cli_args.append(config)
279
+
280
+ if target_version:
281
+ cli_args.append("--target-version")
282
+ cli_args.append(target_version)
283
+
284
+ if preview:
285
+ cli_args.append("--preview")
286
+
287
+ if exclude:
288
+ cli_args.append("--exclude")
289
+ cli_args.append(",".join(exclude))
290
+
291
+ if extend_exclude:
292
+ cli_args.append("--extend-exclude")
293
+ cli_args.append(",".join(extend_exclude))
294
+
295
+ if respect_gitignore:
296
+ cli_args.append("--respect-gitignore")
297
+
298
+ if force_exclude:
299
+ cli_args.append("--force-exclude")
300
+
301
+ if no_cache:
302
+ cli_args.append("--no-cache")
303
+
304
+ if isolated:
305
+ cli_args.append("--isolated")
306
+
307
+ if cache_dir:
308
+ cli_args.append("--cache-dir")
309
+ cli_args.append(cache_dir)
310
+
311
+ if stdin_filename:
312
+ cli_args.append("--stdin-filename")
313
+ cli_args.append(stdin_filename)
314
+
315
+ return _run("format", *cli_args, verbose=verbose, quiet=quiet, silent=silent)
316
+
317
+
318
+ @lazy(name="ruff.rule")
319
+ def rule(
320
+ *,
321
+ output_format: str | None = None,
322
+ verbose: bool = False,
323
+ quiet: bool = False,
324
+ silent: bool = False,
325
+ ) -> int:
326
+ """Explain a rule.
327
+
328
+ Parameters:
329
+ output_format: Output format (default: pretty, possible values: text, json, pretty).
330
+ verbose: Enable verbose logging.
331
+ quiet: Print lint violations, but nothing else.
332
+ silent: Disable all logging (but still exit with status code "1" upon detecting lint violations).
333
+ """
334
+ cli_args = []
335
+
336
+ if output_format:
337
+ cli_args.append("--format")
338
+ cli_args.append(output_format)
339
+
340
+ return _run("rule", *cli_args, verbose=verbose, quiet=quiet, silent=silent)
341
+
342
+
343
+ @lazy(name="ruff.config")
344
+ def config(
345
+ *,
346
+ verbose: bool = False,
347
+ quiet: bool = False,
348
+ silent: bool = False,
349
+ ) -> int:
350
+ """List or describe the available configuration options.
351
+
352
+ Parameters:
353
+ verbose: Enable verbose logging.
354
+ quiet: Print lint violations, but nothing else.
355
+ silent: Disable all logging (but still exit with status code "1" upon detecting lint violations).
356
+ """
357
+ return _run("config", verbose=verbose, quiet=quiet, silent=silent)
358
+
359
+
360
+ @lazy(name="ruff.linter")
361
+ def linter(
362
+ *,
363
+ output_format: str | None = None,
364
+ verbose: bool = False,
365
+ quiet: bool = False,
366
+ silent: bool = False,
367
+ ) -> int:
368
+ """List all supported upstream linters.
369
+
370
+ Parameters:
371
+ output_format: Output format (default: pretty) (possible values: text, json, pretty).
372
+ verbose: Enable verbose logging.
373
+ quiet: Print lint violations, but nothing else.
374
+ silent: Disable all logging (but still exit with status code "1" upon detecting lint violations).
375
+ """
376
+ cli_args = []
377
+
378
+ if output_format:
379
+ cli_args.append("--format")
380
+ cli_args.append(output_format)
381
+
382
+ return _run("linter", *cli_args, verbose=verbose, quiet=quiet, silent=silent)
383
+
384
+
385
+ @lazy(name="ruff.clean")
386
+ def clean(
387
+ *,
388
+ verbose: bool = False,
389
+ quiet: bool = False,
390
+ silent: bool = False,
391
+ ) -> int:
392
+ """Clear any caches in the current directory and any subdirectories.
393
+
394
+ Parameters:
395
+ verbose: Enable verbose logging.
396
+ quiet: Print lint violations, but nothing else.
397
+ silent: Disable all logging (but still exit with status code "1" upon detecting lint violations).
398
+ """
399
+ return _run("clean", verbose=verbose, quiet=quiet, silent=silent)
@@ -0,0 +1,82 @@
1
+ # YORE: Bump 2: Remove file.
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib
6
+ import sys
7
+ from io import StringIO
8
+ from typing import TYPE_CHECKING, Literal, cast
9
+
10
+ from failprint import lazy
11
+
12
+ if TYPE_CHECKING:
13
+ from collections.abc import Sequence
14
+
15
+
16
+ @lazy(name="safety.check")
17
+ def check(
18
+ requirements: str | Sequence[str],
19
+ *,
20
+ ignore_vulns: dict[str, str] | None = None,
21
+ formatter: Literal["json", "bare", "text"] = "text",
22
+ full_report: bool = True,
23
+ ) -> bool:
24
+ """Run the safety check command.
25
+
26
+ This function makes sure we load the original, unpatched version of safety.
27
+
28
+ Parameters:
29
+ requirements: Python "requirements" (list of pinned dependencies).
30
+ ignore_vulns: Vulnerabilities to ignore.
31
+ formatter: Report format.
32
+ full_report: Whether to output a full report.
33
+
34
+ Returns:
35
+ Success/failure.
36
+ """
37
+ # set default parameter values
38
+ ignore_vulns = ignore_vulns or {}
39
+
40
+ # undo possible patching
41
+ # see https://github.com/pyupio/safety/issues/348
42
+ for module in sys.modules:
43
+ if module.startswith("safety.") or module == "safety":
44
+ del sys.modules[module]
45
+
46
+ importlib.invalidate_caches()
47
+
48
+ # reload original, unpatched safety
49
+ from safety.formatter import SafetyFormatter # noqa: PLC0415
50
+ from safety.safety import calculate_remediations, check # noqa: PLC0415
51
+ from safety.util import read_requirements # noqa: PLC0415
52
+
53
+ # check using safety as a library
54
+ if isinstance(requirements, (list, tuple, set)):
55
+ requirements = "\n".join(requirements)
56
+ packages = list(read_requirements(StringIO(cast("str", requirements))))
57
+
58
+ # TODO: Safety 3 support, merge once support for v2 is dropped.
59
+ check_kwargs = {"packages": packages, "ignore_vulns": ignore_vulns}
60
+ try:
61
+ from safety.auth.cli_utils import build_client_session # noqa: PLC0415
62
+
63
+ client_session, _ = build_client_session()
64
+ check_kwargs["session"] = client_session
65
+ except ImportError:
66
+ pass
67
+
68
+ vulns, db_full = check(**check_kwargs)
69
+ remediations = calculate_remediations(vulns, db_full)
70
+ output_report = SafetyFormatter(formatter).render_vulnerabilities(
71
+ announcements=[],
72
+ vulnerabilities=vulns,
73
+ remediations=remediations,
74
+ full=full_report,
75
+ packages=packages,
76
+ )
77
+
78
+ # print report, return status
79
+ if vulns:
80
+ print(output_report) # noqa: T201
81
+ return False
82
+ return True
@@ -0,0 +1,38 @@
1
+ # YORE: Bump 2: Remove file.
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+
7
+ from failprint import lazy
8
+
9
+
10
+ @lazy(name="ssort")
11
+ def run(
12
+ *files: str,
13
+ diff: bool | None = None,
14
+ check: bool | None = None,
15
+ ) -> int:
16
+ """Run `ssort`.
17
+
18
+ Parameters:
19
+ *files: Files to format.
20
+ diff: Prints a diff of all changes ssort would make to a file.
21
+ check: Check the file for unsorted statements. Returns 0 if nothing needs to be changed. Otherwise returns 1.
22
+ """
23
+ from ssort._main import main as ssort # noqa: PLC0415
24
+
25
+ cli_args = list(files)
26
+
27
+ if diff:
28
+ cli_args.append("--diff")
29
+
30
+ if check:
31
+ cli_args.append("--check")
32
+
33
+ old_sys_argv = sys.argv
34
+ sys.argv = ["ssort*", *cli_args]
35
+ try:
36
+ return ssort()
37
+ finally:
38
+ sys.argv = old_sys_argv