-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathargparse_range.py
More file actions
90 lines (82 loc) · 3.5 KB
/
argparse_range.py
File metadata and controls
90 lines (82 loc) · 3.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
"""
注:本文件由https://github.com/aatifsyed/argparse-range修改而来。
"""
import argparse
from typing import Any, Callable, Iterable, Optional, Sequence, Tuple, TypeVar, Union
T = TypeVar("T", int, float)
def range_action(minimum: T, maximum: T, range_formatter: Callable=str):
"""Return an Action which will limit the input argument to be within the given range (inclusive)"""
if not minimum < maximum:
raise TypeError(f"minimum {minimum} must be less than maximum {maximum}")
class RangeAction(argparse.Action):
def __init__(
self,
option_strings: Sequence[str],
dest: str,
nargs: Union[int, str, None] = None,
const: Union[T, None] = None,
default: Union[T, str, None] = None,
type: Union[
Callable[[str], T], Callable[[str], T], argparse.FileType, None
] = None,
choices: Optional[Iterable[T]] = None,
required: bool = False,
help: Optional[str] = None,
metavar: Union[str, Tuple[str, ...], None] = None,
) -> None:
self.range_formatter: Callable = range_formatter
self.range_comment: str = f"(must be in range {range_formatter(minimum)}..={range_formatter(maximum)})"
if isinstance(help, str):
help = f"{help} {self.range_comment}"
else:
help = self.range_comment
if isinstance(default, str) and type is None:
raise RuntimeWarning(
f"RangeAction has default {default} with type {__builtins__.type(default)}, which may lead to inconsistent types in the returned Namespace"
)
super().__init__(
option_strings,
dest,
nargs=nargs,
const=const,
default=default,
type=type,
choices=choices,
required=required,
help=help,
metavar=metavar,
)
def __call__(
self,
parser: argparse.ArgumentParser,
namespace: argparse.Namespace,
values: Union[str, Sequence[Any], None],
option_string: Union[str, None] = None,
) -> None:
def check_value(v):
if not minimum <= v <= maximum:
raise argparse.ArgumentError(
argument=self,
message=f"Invalid choice: {self.range_formatter(v)} {self.range_comment}",
)
converter: Callable[[str], T]
if callable(self.type) and not isinstance(self.type, argparse.FileType):
converter = self.type
elif isinstance(minimum, int):
converter = lambda s: int(s)
elif isinstance(minimum, float):
converter = lambda s: float(s)
if isinstance(values, list):
values = [converter(v) for v in values]
for v in values:
check_value(v)
elif isinstance(values, str):
values = converter(values)
check_value(values)
elif values is None:
pass # User specified `nargs="?"`
else:
# User specified a `type`, and it was a single argument, and that conversion has already happened
check_value(values)
setattr(namespace, self.dest, values)
return RangeAction