duty 1.6.0__py3-none-any.whl → 1.6.2__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.
- duty/__init__.py +49 -2
- duty/__main__.py +1 -1
- duty/_internal/__init__.py +0 -0
- duty/_internal/callables/__init__.py +34 -0
- duty/{callables → _internal/callables}/_io.py +2 -0
- duty/_internal/callables/autoflake.py +132 -0
- duty/_internal/callables/black.py +176 -0
- duty/_internal/callables/blacken_docs.py +92 -0
- duty/_internal/callables/build.py +76 -0
- duty/_internal/callables/coverage.py +716 -0
- duty/_internal/callables/flake8.py +222 -0
- duty/_internal/callables/git_changelog.py +178 -0
- duty/_internal/callables/griffe.py +227 -0
- duty/_internal/callables/interrogate.py +152 -0
- duty/_internal/callables/isort.py +573 -0
- duty/_internal/callables/mkdocs.py +256 -0
- duty/_internal/callables/mypy.py +496 -0
- duty/_internal/callables/pytest.py +475 -0
- duty/_internal/callables/ruff.py +399 -0
- duty/_internal/callables/safety.py +82 -0
- duty/_internal/callables/ssort.py +38 -0
- duty/_internal/callables/twine.py +284 -0
- duty/_internal/cli.py +322 -0
- duty/_internal/collection.py +246 -0
- duty/_internal/context.py +111 -0
- duty/{debug.py → _internal/debug.py} +13 -15
- duty/_internal/decorator.py +111 -0
- duty/_internal/exceptions.py +12 -0
- duty/_internal/tools/__init__.py +41 -0
- duty/{tools → _internal/tools}/_autoflake.py +8 -4
- duty/{tools → _internal/tools}/_base.py +15 -2
- duty/{tools → _internal/tools}/_black.py +5 -5
- duty/{tools → _internal/tools}/_blacken_docs.py +10 -5
- duty/{tools → _internal/tools}/_build.py +4 -4
- duty/{tools → _internal/tools}/_coverage.py +8 -4
- duty/{tools → _internal/tools}/_flake8.py +10 -12
- duty/{tools → _internal/tools}/_git_changelog.py +8 -4
- duty/{tools → _internal/tools}/_griffe.py +8 -4
- duty/{tools → _internal/tools}/_interrogate.py +4 -4
- duty/{tools → _internal/tools}/_isort.py +8 -6
- duty/{tools → _internal/tools}/_mkdocs.py +8 -4
- duty/{tools → _internal/tools}/_mypy.py +5 -5
- duty/{tools → _internal/tools}/_pytest.py +8 -4
- duty/{tools → _internal/tools}/_ruff.py +11 -5
- duty/{tools → _internal/tools}/_safety.py +13 -8
- duty/{tools → _internal/tools}/_ssort.py +10 -6
- duty/{tools → _internal/tools}/_twine.py +11 -5
- duty/_internal/tools/_yore.py +96 -0
- duty/_internal/validation.py +266 -0
- duty/callables/__init__.py +4 -4
- duty/callables/autoflake.py +11 -126
- duty/callables/black.py +12 -171
- duty/callables/blacken_docs.py +11 -86
- duty/callables/build.py +12 -71
- duty/callables/coverage.py +12 -711
- duty/callables/flake8.py +12 -217
- duty/callables/git_changelog.py +12 -173
- duty/callables/griffe.py +12 -222
- duty/callables/interrogate.py +12 -147
- duty/callables/isort.py +12 -568
- duty/callables/mkdocs.py +12 -251
- duty/callables/mypy.py +11 -490
- duty/callables/pytest.py +12 -470
- duty/callables/ruff.py +12 -394
- duty/callables/safety.py +11 -76
- duty/callables/ssort.py +12 -33
- duty/callables/twine.py +12 -279
- duty/cli.py +10 -316
- duty/collection.py +12 -228
- duty/context.py +12 -107
- duty/decorator.py +12 -108
- duty/exceptions.py +13 -10
- duty/tools.py +63 -0
- duty/validation.py +12 -262
- {duty-1.6.0.dist-info → duty-1.6.2.dist-info}/METADATA +5 -4
- duty-1.6.2.dist-info/RECORD +81 -0
- {duty-1.6.0.dist-info → duty-1.6.2.dist-info}/WHEEL +1 -1
- {duty-1.6.0.dist-info → duty-1.6.2.dist-info}/entry_points.txt +1 -1
- duty/tools/__init__.py +0 -50
- duty/tools/_yore.py +0 -54
- duty-1.6.0.dist-info/RECORD +0 -55
- {duty-1.6.0.dist-info → duty-1.6.2.dist-info}/licenses/LICENSE +0 -0
duty/collection.py
CHANGED
|
@@ -1,233 +1,17 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Deprecated. Import from `duty` directly."""
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# YORE: Bump 2: Remove file.
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
from copy import deepcopy
|
|
8
|
-
from importlib import util as importlib_util
|
|
9
|
-
from typing import Any, Callable, ClassVar, Union
|
|
5
|
+
import warnings
|
|
6
|
+
from typing import Any
|
|
10
7
|
|
|
11
|
-
from duty.
|
|
8
|
+
from duty._internal import collection
|
|
12
9
|
|
|
13
|
-
DutyListType = list[Union[str, Callable, "Duty"]]
|
|
14
|
-
default_duties_file = "duties.py"
|
|
15
10
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
self,
|
|
24
|
-
name: str,
|
|
25
|
-
description: str,
|
|
26
|
-
function: Callable,
|
|
27
|
-
collection: Collection | None = None,
|
|
28
|
-
aliases: set | None = None,
|
|
29
|
-
pre: DutyListType | None = None,
|
|
30
|
-
post: DutyListType | None = None,
|
|
31
|
-
opts: dict[str, Any] | None = None,
|
|
32
|
-
) -> None:
|
|
33
|
-
"""Initialize the duty.
|
|
34
|
-
|
|
35
|
-
Parameters:
|
|
36
|
-
name: The duty name.
|
|
37
|
-
description: The duty description.
|
|
38
|
-
function: The duty function.
|
|
39
|
-
collection: The collection on which to attach this duty.
|
|
40
|
-
aliases: A list of aliases for this duty.
|
|
41
|
-
pre: A list of duties to run before this one.
|
|
42
|
-
post: A list of duties to run after this one.
|
|
43
|
-
opts: Options used to create the context instance.
|
|
44
|
-
"""
|
|
45
|
-
self.name = name
|
|
46
|
-
self.description = description
|
|
47
|
-
self.function = function
|
|
48
|
-
self.aliases = aliases or set()
|
|
49
|
-
self.pre = pre or []
|
|
50
|
-
self.post = post or []
|
|
51
|
-
self.options = opts or self.default_options
|
|
52
|
-
self.options_override: dict = {}
|
|
53
|
-
|
|
54
|
-
self.collection: Collection | None = None
|
|
55
|
-
if collection:
|
|
56
|
-
collection.add(self)
|
|
57
|
-
|
|
58
|
-
@property
|
|
59
|
-
def context(self) -> Context:
|
|
60
|
-
"""Return a new context instance.
|
|
61
|
-
|
|
62
|
-
Returns:
|
|
63
|
-
A new context instance.
|
|
64
|
-
"""
|
|
65
|
-
return Context(self.options, self.options_override)
|
|
66
|
-
|
|
67
|
-
def run(self, *args: Any, **kwargs: Any) -> None:
|
|
68
|
-
"""Run the duty.
|
|
69
|
-
|
|
70
|
-
This is just a shortcut for `duty(duty.context, *args, **kwargs)`.
|
|
71
|
-
|
|
72
|
-
Parameters:
|
|
73
|
-
args: Positional arguments passed to the function.
|
|
74
|
-
kwargs: Keyword arguments passed to the function.
|
|
75
|
-
"""
|
|
76
|
-
self(self.context, *args, **kwargs)
|
|
77
|
-
|
|
78
|
-
def run_duties(self, context: Context, duties_list: DutyListType) -> None:
|
|
79
|
-
"""Run a list of duties.
|
|
80
|
-
|
|
81
|
-
Parameters:
|
|
82
|
-
context: The context to use.
|
|
83
|
-
duties_list: The list of duties to run.
|
|
84
|
-
|
|
85
|
-
Raises:
|
|
86
|
-
RuntimeError: When a duty name is given to pre or post duties.
|
|
87
|
-
Indeed, without a parent collection, it is impossible
|
|
88
|
-
to find another duty by its name.
|
|
89
|
-
"""
|
|
90
|
-
for duty_item in duties_list:
|
|
91
|
-
if callable(duty_item):
|
|
92
|
-
# Item is a proper duty, or a callable: run it.
|
|
93
|
-
duty_item(context)
|
|
94
|
-
elif isinstance(duty_item, str):
|
|
95
|
-
# Item is a reference to a duty.
|
|
96
|
-
if self.collection is None:
|
|
97
|
-
raise RuntimeError(f"Can't find duty by name without a collection ({duty_item})")
|
|
98
|
-
# Get the duty and run it.
|
|
99
|
-
self.collection.get(duty_item)(context)
|
|
100
|
-
|
|
101
|
-
def __call__(self, context: Context, *args: Any, **kwargs: Any) -> None:
|
|
102
|
-
"""Run the duty function.
|
|
103
|
-
|
|
104
|
-
Parameters:
|
|
105
|
-
context: The context to use.
|
|
106
|
-
args: Positional arguments passed to the function.
|
|
107
|
-
kwargs: Keyword arguments passed to the function.
|
|
108
|
-
"""
|
|
109
|
-
self.run_duties(context, self.pre)
|
|
110
|
-
self.function(context, *args, **kwargs)
|
|
111
|
-
self.run_duties(context, self.post)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
class Collection:
|
|
115
|
-
"""A collection of duties.
|
|
116
|
-
|
|
117
|
-
Attributes:
|
|
118
|
-
path: The path to the duties file.
|
|
119
|
-
duties: The list of duties.
|
|
120
|
-
aliases: A dictionary of aliases pointing to their respective duties.
|
|
121
|
-
"""
|
|
122
|
-
|
|
123
|
-
def __init__(self, path: str = default_duties_file) -> None:
|
|
124
|
-
"""Initialize the collection.
|
|
125
|
-
|
|
126
|
-
Parameters:
|
|
127
|
-
path: The path to the duties file.
|
|
128
|
-
"""
|
|
129
|
-
self.path = path
|
|
130
|
-
self.duties: dict[str, Duty] = {}
|
|
131
|
-
self.aliases: dict[str, Duty] = {}
|
|
132
|
-
|
|
133
|
-
def clear(self) -> None:
|
|
134
|
-
"""Clear the collection."""
|
|
135
|
-
self.duties.clear()
|
|
136
|
-
self.aliases.clear()
|
|
137
|
-
|
|
138
|
-
def names(self) -> list[str]:
|
|
139
|
-
"""Return the list of duties names and aliases.
|
|
140
|
-
|
|
141
|
-
Returns:
|
|
142
|
-
The list of duties names and aliases.
|
|
143
|
-
"""
|
|
144
|
-
return list(self.duties.keys()) + list(self.aliases.keys())
|
|
145
|
-
|
|
146
|
-
def completion_candidates(self, args: tuple[str, ...]) -> list[str]:
|
|
147
|
-
"""Find shell completion candidates within this collection.
|
|
148
|
-
|
|
149
|
-
Returns:
|
|
150
|
-
The list of shell completion candidates, sorted alphabetically.
|
|
151
|
-
"""
|
|
152
|
-
# Find last duty name in args.
|
|
153
|
-
name = None
|
|
154
|
-
names = set(self.names())
|
|
155
|
-
for arg in reversed(args):
|
|
156
|
-
if arg in names:
|
|
157
|
-
name = arg
|
|
158
|
-
break
|
|
159
|
-
|
|
160
|
-
completion_names = sorted(names)
|
|
161
|
-
|
|
162
|
-
# If no duty found, return names.
|
|
163
|
-
if name is None:
|
|
164
|
-
return completion_names
|
|
165
|
-
|
|
166
|
-
params = [
|
|
167
|
-
f"{param.name}="
|
|
168
|
-
for param in inspect.signature(self.get(name).function).parameters.values()
|
|
169
|
-
if param.kind is not param.VAR_POSITIONAL
|
|
170
|
-
][1:]
|
|
171
|
-
|
|
172
|
-
# If duty found, return names *and* duty parameters.
|
|
173
|
-
return completion_names + sorted(params)
|
|
174
|
-
|
|
175
|
-
def get(self, name_or_alias: str) -> Duty:
|
|
176
|
-
"""Get a duty by its name or alias.
|
|
177
|
-
|
|
178
|
-
Parameters:
|
|
179
|
-
name_or_alias: The name or alias of the duty.
|
|
180
|
-
|
|
181
|
-
Returns:
|
|
182
|
-
A duty.
|
|
183
|
-
"""
|
|
184
|
-
try:
|
|
185
|
-
return self.duties[name_or_alias]
|
|
186
|
-
except KeyError:
|
|
187
|
-
return self.aliases[name_or_alias]
|
|
188
|
-
|
|
189
|
-
def format_help(self) -> str:
|
|
190
|
-
"""Format a message listing the duties.
|
|
191
|
-
|
|
192
|
-
Returns:
|
|
193
|
-
A string listing the duties and their summary.
|
|
194
|
-
"""
|
|
195
|
-
lines = []
|
|
196
|
-
# 20 makes the summary aligned with options description
|
|
197
|
-
longest_name = max(*(len(name) for name in self.duties), 20)
|
|
198
|
-
for name, duty in self.duties.items():
|
|
199
|
-
description = duty.description.split("\n")[0]
|
|
200
|
-
lines.append(f"{name:{longest_name}} {description}")
|
|
201
|
-
return "\n".join(lines)
|
|
202
|
-
|
|
203
|
-
def add(self, duty: Duty) -> None:
|
|
204
|
-
"""Add a duty to the collection.
|
|
205
|
-
|
|
206
|
-
Parameters:
|
|
207
|
-
duty: The duty to add.
|
|
208
|
-
"""
|
|
209
|
-
if duty.collection is not None:
|
|
210
|
-
# we must copy the duty to be able to add it
|
|
211
|
-
# in multiple collections
|
|
212
|
-
duty = deepcopy(duty)
|
|
213
|
-
duty.collection = self
|
|
214
|
-
self.duties[duty.name] = duty
|
|
215
|
-
for alias in duty.aliases:
|
|
216
|
-
self.aliases[alias] = duty
|
|
217
|
-
|
|
218
|
-
def load(self, path: str | None = None) -> None:
|
|
219
|
-
"""Load duties from a Python file.
|
|
220
|
-
|
|
221
|
-
Parameters:
|
|
222
|
-
path: The path to the Python file to load.
|
|
223
|
-
Uses the collection's path by default.
|
|
224
|
-
"""
|
|
225
|
-
path = path or self.path
|
|
226
|
-
spec = importlib_util.spec_from_file_location("duty.duties", path)
|
|
227
|
-
if spec:
|
|
228
|
-
duties = importlib_util.module_from_spec(spec)
|
|
229
|
-
sys.modules["duty.duties"] = duties
|
|
230
|
-
spec.loader.exec_module(duties) # type: ignore[union-attr]
|
|
231
|
-
declared_duties = inspect.getmembers(duties, lambda member: isinstance(member, Duty))
|
|
232
|
-
for _, duty in declared_duties:
|
|
233
|
-
self.add(duty)
|
|
11
|
+
def __getattr__(name: str) -> Any:
|
|
12
|
+
warnings.warn(
|
|
13
|
+
"Importing from `duty.collection` is deprecated. Import from `duty` directly.",
|
|
14
|
+
DeprecationWarning,
|
|
15
|
+
stacklevel=2,
|
|
16
|
+
)
|
|
17
|
+
return getattr(collection, name)
|
duty/context.py
CHANGED
|
@@ -1,112 +1,17 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Deprecated. Import from `duty` directly."""
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# YORE: Bump 2: Remove file.
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
from
|
|
7
|
-
from typing import TYPE_CHECKING, Any, Callable, Union
|
|
5
|
+
import warnings
|
|
6
|
+
from typing import Any
|
|
8
7
|
|
|
9
|
-
from
|
|
8
|
+
from duty._internal import context
|
|
10
9
|
|
|
11
|
-
from duty.exceptions import DutyFailure
|
|
12
|
-
from duty.tools import Tool
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"""A simple context class.
|
|
22
|
-
|
|
23
|
-
Context instances are passed to functions decorated with `duty`.
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
def __init__(self, options: dict[str, Any], options_override: dict[str, Any] | None = None) -> None:
|
|
27
|
-
"""Initialize the context.
|
|
28
|
-
|
|
29
|
-
Parameters:
|
|
30
|
-
options: Base options specified in `@duty(**options)`.
|
|
31
|
-
options_override: Options that override `run` and `@duty` options.
|
|
32
|
-
This argument is used to allow users to override options from the CLI or environment.
|
|
33
|
-
"""
|
|
34
|
-
self._options = options
|
|
35
|
-
self._option_stack: list[dict[str, Any]] = []
|
|
36
|
-
self._options_override = options_override or {}
|
|
37
|
-
|
|
38
|
-
@contextmanager
|
|
39
|
-
def cd(self, directory: str) -> Iterator:
|
|
40
|
-
"""Change working directory as a context manager.
|
|
41
|
-
|
|
42
|
-
Parameters:
|
|
43
|
-
directory: The directory to go into.
|
|
44
|
-
|
|
45
|
-
Yields:
|
|
46
|
-
Nothing.
|
|
47
|
-
"""
|
|
48
|
-
if not directory:
|
|
49
|
-
yield
|
|
50
|
-
return
|
|
51
|
-
old_wd = os.getcwd()
|
|
52
|
-
os.chdir(directory)
|
|
53
|
-
try:
|
|
54
|
-
yield
|
|
55
|
-
finally:
|
|
56
|
-
os.chdir(old_wd)
|
|
57
|
-
|
|
58
|
-
def run(self, cmd: CmdType, **options: Any) -> str:
|
|
59
|
-
"""Run a command in a subprocess or a Python callable.
|
|
60
|
-
|
|
61
|
-
Parameters:
|
|
62
|
-
cmd: A command or a Python callable.
|
|
63
|
-
options: Options passed to `failprint` functions.
|
|
64
|
-
|
|
65
|
-
Raises:
|
|
66
|
-
DutyFailure: When the exit code / function result is greather than 0.
|
|
67
|
-
|
|
68
|
-
Returns:
|
|
69
|
-
The output of the command.
|
|
70
|
-
"""
|
|
71
|
-
final_options = dict(self._options)
|
|
72
|
-
final_options.update(options)
|
|
73
|
-
|
|
74
|
-
if "command" not in final_options and isinstance(cmd, Tool):
|
|
75
|
-
with suppress(ValueError):
|
|
76
|
-
final_options["command"] = cmd.cli_command
|
|
77
|
-
|
|
78
|
-
allow_overrides = final_options.pop("allow_overrides", True)
|
|
79
|
-
workdir = final_options.pop("workdir", None)
|
|
80
|
-
|
|
81
|
-
if allow_overrides:
|
|
82
|
-
final_options.update(self._options_override)
|
|
83
|
-
|
|
84
|
-
with self.cd(workdir):
|
|
85
|
-
try:
|
|
86
|
-
result = failprint_run(cmd, **final_options)
|
|
87
|
-
except KeyboardInterrupt as ki:
|
|
88
|
-
raise DutyFailure(130) from ki
|
|
89
|
-
|
|
90
|
-
if result.code:
|
|
91
|
-
raise DutyFailure(result.code)
|
|
92
|
-
|
|
93
|
-
return result.output
|
|
94
|
-
|
|
95
|
-
@contextmanager
|
|
96
|
-
def options(self, **opts: Any) -> Iterator:
|
|
97
|
-
"""Change options as a context manager.
|
|
98
|
-
|
|
99
|
-
Can be nested as will, previous options will pop once out of the with clause.
|
|
100
|
-
|
|
101
|
-
Parameters:
|
|
102
|
-
**opts: Options used in `run`.
|
|
103
|
-
|
|
104
|
-
Yields:
|
|
105
|
-
Nothing.
|
|
106
|
-
"""
|
|
107
|
-
self._option_stack.append(self._options)
|
|
108
|
-
self._options = {**self._options, **opts}
|
|
109
|
-
try:
|
|
110
|
-
yield
|
|
111
|
-
finally:
|
|
112
|
-
self._options = self._option_stack.pop()
|
|
11
|
+
def __getattr__(name: str) -> Any:
|
|
12
|
+
warnings.warn(
|
|
13
|
+
"Importing from `duty.context` is deprecated. Import from `duty` directly.",
|
|
14
|
+
DeprecationWarning,
|
|
15
|
+
stacklevel=2,
|
|
16
|
+
)
|
|
17
|
+
return getattr(context, name)
|
duty/decorator.py
CHANGED
|
@@ -1,113 +1,17 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Deprecated. Import from `duty` directly."""
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# YORE: Bump 2: Remove file.
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
from
|
|
7
|
-
from typing import TYPE_CHECKING, Any, Callable, overload
|
|
5
|
+
import warnings
|
|
6
|
+
from typing import Any
|
|
8
7
|
|
|
9
|
-
from duty.
|
|
8
|
+
from duty._internal import decorator
|
|
10
9
|
|
|
11
|
-
if TYPE_CHECKING:
|
|
12
|
-
from collections.abc import Iterable
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return wrapper
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def create_duty(
|
|
26
|
-
func: Callable,
|
|
27
|
-
*,
|
|
28
|
-
name: str | None = None,
|
|
29
|
-
aliases: Iterable[str] | None = None,
|
|
30
|
-
pre: DutyListType | None = None,
|
|
31
|
-
post: DutyListType | None = None,
|
|
32
|
-
skip_if: bool = False,
|
|
33
|
-
skip_reason: str | None = None,
|
|
34
|
-
**opts: Any,
|
|
35
|
-
) -> Duty:
|
|
36
|
-
"""Register a duty in the collection.
|
|
37
|
-
|
|
38
|
-
Parameters:
|
|
39
|
-
func: The callable to register as a duty.
|
|
40
|
-
name: The duty name.
|
|
41
|
-
aliases: A set of aliases for this duty.
|
|
42
|
-
pre: Pre-duties.
|
|
43
|
-
post: Post-duties.
|
|
44
|
-
skip_if: Skip running the duty if the given condition is met.
|
|
45
|
-
skip_reason: Custom message when skipping.
|
|
46
|
-
opts: Options passed to the context.
|
|
47
|
-
|
|
48
|
-
Returns:
|
|
49
|
-
The registered duty.
|
|
50
|
-
"""
|
|
51
|
-
aliases = set(aliases) if aliases else set()
|
|
52
|
-
name = name or func.__name__
|
|
53
|
-
dash_name = name.replace("_", "-")
|
|
54
|
-
if name != dash_name:
|
|
55
|
-
aliases.add(name)
|
|
56
|
-
name = dash_name
|
|
57
|
-
description = inspect.getdoc(func) or ""
|
|
58
|
-
if skip_if:
|
|
59
|
-
func = _skip(func, skip_reason or f"{dash_name}: skipped")
|
|
60
|
-
duty = Duty(name, description, func, aliases=aliases, pre=pre, post=post, opts=opts)
|
|
61
|
-
duty.__name__ = name # type: ignore[attr-defined]
|
|
62
|
-
duty.__doc__ = description
|
|
63
|
-
duty.__wrapped__ = func # type: ignore[attr-defined]
|
|
64
|
-
return duty
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
@overload
|
|
68
|
-
def duty(**kwargs: Any) -> Callable[[Callable], Duty]: ...
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
@overload
|
|
72
|
-
def duty(func: Callable) -> Duty: ...
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def duty(*args: Any, **kwargs: Any) -> Callable | Duty:
|
|
76
|
-
"""Decorate a callable to transform it and register it as a duty.
|
|
77
|
-
|
|
78
|
-
Parameters:
|
|
79
|
-
args: One callable.
|
|
80
|
-
kwargs: Context options.
|
|
81
|
-
|
|
82
|
-
Raises:
|
|
83
|
-
ValueError: When the decorator is misused.
|
|
84
|
-
|
|
85
|
-
Examples:
|
|
86
|
-
Decorate a function:
|
|
87
|
-
|
|
88
|
-
```python
|
|
89
|
-
@duty
|
|
90
|
-
def clean(ctx):
|
|
91
|
-
ctx.run("rm -rf build", silent=True)
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
Pass options to the context:
|
|
95
|
-
|
|
96
|
-
```python
|
|
97
|
-
@duty(silent=True)
|
|
98
|
-
def clean(ctx):
|
|
99
|
-
ctx.run("rm -rf build") # silent=True is implied
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
Returns:
|
|
103
|
-
A duty when used without parentheses, a decorator otherwise.
|
|
104
|
-
"""
|
|
105
|
-
if args:
|
|
106
|
-
if len(args) > 1:
|
|
107
|
-
raise ValueError("The duty decorator accepts only one positional argument")
|
|
108
|
-
return create_duty(args[0], **kwargs)
|
|
109
|
-
|
|
110
|
-
def decorator(func: Callable) -> Duty:
|
|
111
|
-
return create_duty(func, **kwargs)
|
|
112
|
-
|
|
113
|
-
return decorator
|
|
11
|
+
def __getattr__(name: str) -> Any:
|
|
12
|
+
warnings.warn(
|
|
13
|
+
"Importing from `duty.decorator` is deprecated. Import from `duty` directly.",
|
|
14
|
+
DeprecationWarning,
|
|
15
|
+
stacklevel=2,
|
|
16
|
+
)
|
|
17
|
+
return getattr(decorator, name)
|
duty/exceptions.py
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Deprecated. Import from `duty` directly."""
|
|
2
2
|
|
|
3
|
+
# YORE: Bump 2: Remove file.
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
import warnings
|
|
6
|
+
from typing import Any
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
"""Initialize the object.
|
|
8
|
+
from duty._internal import exceptions
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
|
|
11
|
+
def __getattr__(name: str) -> Any:
|
|
12
|
+
warnings.warn(
|
|
13
|
+
"Importing from `duty.exceptions` is deprecated. Import from `duty` directly.",
|
|
14
|
+
DeprecationWarning,
|
|
15
|
+
stacklevel=2,
|
|
16
|
+
)
|
|
17
|
+
return getattr(exceptions, name)
|
duty/tools.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Our collection of tools."""
|
|
2
|
+
|
|
3
|
+
import warnings
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from duty._internal.tools import (
|
|
7
|
+
autoflake,
|
|
8
|
+
black,
|
|
9
|
+
blacken_docs,
|
|
10
|
+
build,
|
|
11
|
+
coverage,
|
|
12
|
+
flake8,
|
|
13
|
+
git_changelog,
|
|
14
|
+
griffe,
|
|
15
|
+
interrogate,
|
|
16
|
+
isort,
|
|
17
|
+
mkdocs,
|
|
18
|
+
mypy,
|
|
19
|
+
pytest,
|
|
20
|
+
ruff,
|
|
21
|
+
safety,
|
|
22
|
+
ssort,
|
|
23
|
+
twine,
|
|
24
|
+
yore,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"autoflake",
|
|
29
|
+
"black",
|
|
30
|
+
"blacken_docs",
|
|
31
|
+
"build",
|
|
32
|
+
"coverage",
|
|
33
|
+
"flake8",
|
|
34
|
+
"git_changelog",
|
|
35
|
+
"griffe",
|
|
36
|
+
"interrogate",
|
|
37
|
+
"isort",
|
|
38
|
+
"mkdocs",
|
|
39
|
+
"mypy",
|
|
40
|
+
"pytest",
|
|
41
|
+
"ruff",
|
|
42
|
+
"safety",
|
|
43
|
+
"ssort",
|
|
44
|
+
"twine",
|
|
45
|
+
"yore",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# YORE: Bump 2: Remove block.
|
|
50
|
+
def __getattr__(name: str) -> Any:
|
|
51
|
+
"""Return the tool or lazy object by name."""
|
|
52
|
+
from failprint import lazy # noqa: F401,PLC0415
|
|
53
|
+
|
|
54
|
+
from duty._internal.tools._base import LazyStderr, LazyStdout, Tool # noqa: F401,PLC0415
|
|
55
|
+
|
|
56
|
+
if name in locals():
|
|
57
|
+
warnings.warn(
|
|
58
|
+
f"Importing `{name}` from `duty.tools` is deprecated, import directly from `duty` instead.",
|
|
59
|
+
DeprecationWarning,
|
|
60
|
+
stacklevel=2,
|
|
61
|
+
)
|
|
62
|
+
return locals()[name]
|
|
63
|
+
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|