Coverage for arguably/arg.py: 100%
15 statements
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-10 01:01 +0000
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-10 01:01 +0000
1"""
2A collection of methods for adding a modifier to a parameter. Should be used in `Annotated[]`.
4Examples:
5 ```python
6 def foo(
7 *,
8 verbose: Annotated[int, arguably.arg.count()],
9 ):
10 ```
11"""
13import enum
14from typing import Union, Callable, Any
16import arguably._modifiers as mods
19def required() -> mods.RequiredModifier:
20 """
21 Marks a field as required. For `*args` or a `list[]`, requires at least one item.
23 Returns:
24 A value for use with `Annotated[]`, stating that this parameter is required.
26 Examples:
27 ```python
28 import arguably
29 from typing import Annotated
31 @arguably.command
32 def email(
33 from_: str,
34 *to: Annotated[str, arguably.arg.required()]
35 ):
36 print(f"{from_=}", f"{to=}")
38 if __name__ == "__main__":
39 arguably.run()
40 ```
42 ```console
43 user@machine:~$ python3 arg-required.py -h
44 usage: arg-required.py [-h] from to [to ...]
46 positional arguments:
47 from (type: str)
48 to (type: str)
50 options:
51 -h, --help show this help message and exit
52 ```
53 ```console
54 user@machine:~$ python3 arg-required.py sender@example.com
55 usage: arg-required.py [-h] from to [to ...]
56 arg-required.py: error: the following arguments are required: to
57 ```
58 """
59 return mods.RequiredModifier()
62def count() -> mods.CountedModifier:
63 """
64 Counts the number of times a flag is given. For example, `-vvvv` would yield `4`.
66 Returns:
67 A value for use with `Annotated[]`, stating that this parameter should be counted.
69 Examples:
70 ```python
71 import arguably
72 from typing import Annotated
74 @arguably.command
75 def process(
76 *,
77 verbose: Annotated[int, arguably.arg.count()],
78 ):
79 \"\"\"
80 :param verbose: [-v] verbosity
81 \"\"\"
82 print(f"{verbose=}")
84 if __name__ == "__main__":
85 arguably.run()
86 ```
88 ```console
89 user@machine:~$ python3 arg-count.py -vvvv
90 verbose=4
91 ```
92 """
93 return mods.CountedModifier()
96def choices(*choices: Union[str, enum.Enum]) -> mods.ChoicesModifier:
97 """
98 Specifies a fixed set of values that a parameter is allowed to be. If a parameter is an `enum.Enum` type, this
99 logic is already used to restrict the inputs to be one of the enum entries.
101 Args:
102 *choices: The allowed values. Must all be of the same type, and be compatible with the annotated type for
103 this parameter.
105 Returns:
106 A value for use with `Annotated[]`, stating that this parameter has a fixed set of choices.
108 Examples:
109 ```python
110 import arguably
111 from typing import Annotated
113 @arguably.command
114 def move(
115 direction: Annotated[str, arguably.arg.choices("left", "right", "up", "down")]
116 ):
117 \"\"\"An enum is usually recommended for cases like this\"\"\"
118 print(f"{direction=}")
120 if __name__ == "__main__":
121 arguably.run()
122 ```
124 ```console
125 user@machine:~$ python3 arg-choices.py north
126 usage: arg-choices.py [-h] {left,right,up,down}
127 arg-choices.py: error: argument direction: invalid choice: 'north' (choose from 'left', 'right', 'up', 'down')
128 ```
129 """
130 return mods.ChoicesModifier(choices)
133def missing(omit_value: str) -> mods.MissingArgDefaultModifier:
134 """
135 Allows an option to be specified, but its value be omitted. In the case where the value is given, the value is
136 used, but if it is omitted, `omit_value` will be used.
138 Args:
139 omit_value: The value that will be used if the flag is present, but the value is omitted.
141 Returns:
142 A value for use with `Annotated[]`, stating that this parameter has a special value if the flag is present,
143 but no value is provided.
145 Examples:
146 ```python
147 import arguably
148 from pathlib import Path
149 from typing import Annotated
151 @arguably.command
152 def do_something(
153 *,
154 log: Annotated[Path | None, arguably.arg.missing("~/.log.txt")] = None
155 ):
156 print(f"{log=}")
158 if __name__ == "__main__":
159 arguably.run()
160 ```
162 ```console
163 user@machine:~$ python3 arg-missing.py
164 log=None
165 user@machine:~$ python3 arg-missing.py --log
166 log=PosixPath('~/.log.txt')
167 user@machine:~$ python3 arg-missing.py --log foo.log
168 log=PosixPath('foo.log')
169 ```
170 """
171 return mods.MissingArgDefaultModifier(omit_value)
174def handler(func: Callable[[str], Any]) -> mods.HandlerModifier:
175 """
176 Causes a user-provided handler to be used to process the input string, instead of trying to process it using
177 the types from type annotations.
179 Args:
180 func: The function to call to process the input string.
182 Returns:
183 A value for use with `Annotated[]`, stating that this parameter has a specific handler to call.
185 Examples:
186 ```python
187 import arguably
188 from typing import Annotated
190 @arguably.command
191 def handle_it(
192 version: Annotated[int, arguably.arg.handler(lambda s: int(s.split("-")[-1]))] = None
193 ):
194 print(f"{version=}")
196 if __name__ == "__main__":
197 arguably.run()
198 ```
200 ```console
201 user@machine:~$ python3 arg-handler.py Python-3
202 version=3
203 ```
204 """
205 return mods.HandlerModifier(func)
208def builder() -> mods.BuilderModifier:
209 """
210 Causes the arguably builder logic to be used instead of trying to instantiate the type from the input string.
212 Returns:
213 A value for use with `Annotated[]`, stating that this parameter should use the builder logic.
215 Examples:
216 ```python
217 import arguably
218 from dataclasses import dataclass
219 from typing import Annotated
221 class Nic: ...
223 @arguably.subtype(alias="tap")
224 @dataclass
225 class TapNic(Nic):
226 model: str
228 @arguably.subtype(alias="user")
229 @dataclass
230 class UserNic(Nic):
231 hostfwd: str
233 @arguably.command
234 def qemu_style(*, nic: Annotated[list[Nic], arguably.arg.builder()]):
235 print(f"{nic=}")
237 if __name__ == "__main__":
238 arguably.run()
239 ```
241 ```console
242 user@machine:~$ ./build.py --nic tap,model=e1000 --nic user,hostfwd=tcp::10022-:22
243 nic=[TapNic(model='e1000'), UserNic(hostfwd='tcp::10022-:22')]
244 ```
245 """
246 return mods.BuilderModifier()