omlish 0.0.0.dev148__py3-none-any.whl → 0.0.0.dev150__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev148'
2
- __revision__ = '01263e3c2d1b82ca19c3d1d6916041ca4276737b'
1
+ __version__ = '0.0.0.dev150'
2
+ __revision__ = 'ab779edf0b58378203e055980af0fedacedb20aa'
3
3
 
4
4
 
5
5
  #
omlish/argparse/all.py ADDED
@@ -0,0 +1,45 @@
1
+ # ruff: noqa: I001
2
+ import argparse
3
+
4
+ from .cli import ( # noqa
5
+ ArgparseArg as Arg,
6
+ argparse_arg as arg,
7
+
8
+ ArgparseCommandFn as CommandFn,
9
+ ArgparseCommand as Command,
10
+ argparse_command as command,
11
+
12
+ ArgparseCli as Cli,
13
+ )
14
+
15
+
16
+ ##
17
+
18
+
19
+ SUPPRESS = argparse.SUPPRESS
20
+
21
+ OPTIONAL = argparse.OPTIONAL
22
+ ZERO_OR_MORE = argparse.ZERO_OR_MORE
23
+ ONE_OR_MORE = argparse.ONE_OR_MORE
24
+ PARSER = argparse.PARSER
25
+ REMAINDER = argparse.REMAINDER
26
+
27
+ HelpFormatter = argparse.HelpFormatter
28
+ RawDescriptionHelpFormatter = argparse.RawDescriptionHelpFormatter
29
+ RawTextHelpFormatter = argparse.RawTextHelpFormatter
30
+ ArgumentDefaultsHelpFormatter = argparse.ArgumentDefaultsHelpFormatter
31
+
32
+ MetavarTypeHelpFormatter = argparse.MetavarTypeHelpFormatter
33
+
34
+ ArgumentError = argparse.ArgumentError
35
+ ArgumentTypeError = argparse.ArgumentTypeError
36
+
37
+ Action = argparse.Action
38
+ BooleanOptionalAction = argparse.BooleanOptionalAction
39
+ SubParsersAction = argparse._SubParsersAction # noqa
40
+
41
+ FileType = argparse.FileType
42
+
43
+ Namespace = argparse.Namespace
44
+
45
+ ArgumentParser = argparse.ArgumentParser
@@ -1,3 +1,5 @@
1
+ # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
1
3
  """
2
4
  TODO:
3
5
  - default command
@@ -9,53 +11,23 @@ import functools
9
11
  import sys
10
12
  import typing as ta
11
13
 
12
- from . import c3
13
- from . import check
14
+ from ..lite.check import check
14
15
 
15
16
 
16
17
  T = ta.TypeVar('T')
17
18
 
18
19
 
19
- ##
20
-
21
-
22
- SUPPRESS = argparse.SUPPRESS
23
-
24
- OPTIONAL = argparse.OPTIONAL
25
- ZERO_OR_MORE = argparse.ZERO_OR_MORE
26
- ONE_OR_MORE = argparse.ONE_OR_MORE
27
- PARSER = argparse.PARSER
28
- REMAINDER = argparse.REMAINDER
29
-
30
- HelpFormatter = argparse.HelpFormatter
31
- RawDescriptionHelpFormatter = argparse.RawDescriptionHelpFormatter
32
- RawTextHelpFormatter = argparse.RawTextHelpFormatter
33
- ArgumentDefaultsHelpFormatter = argparse.ArgumentDefaultsHelpFormatter
34
-
35
- MetavarTypeHelpFormatter = argparse.MetavarTypeHelpFormatter
36
-
37
- ArgumentError = argparse.ArgumentError
38
- ArgumentTypeError = argparse.ArgumentTypeError
39
-
40
- Action = argparse.Action
41
- BooleanOptionalAction = argparse.BooleanOptionalAction
42
- SubParsersAction = argparse._SubParsersAction # noqa
43
-
44
- FileType = argparse.FileType
45
-
46
- Namespace = argparse.Namespace
47
-
48
- ArgumentParser = argparse.ArgumentParser
20
+ ArgparseCommandFn = ta.Callable[[], ta.Optional[int]] # ta.TypeAlias
49
21
 
50
22
 
51
23
  ##
52
24
 
53
25
 
54
26
  @dc.dataclass(eq=False)
55
- class Arg:
27
+ class ArgparseArg:
56
28
  args: ta.Sequence[ta.Any]
57
29
  kwargs: ta.Mapping[str, ta.Any]
58
- dest: str | None = None
30
+ dest: ta.Optional[str] = None
59
31
 
60
32
  def __get__(self, instance, owner=None):
61
33
  if instance is None:
@@ -63,26 +35,23 @@ class Arg:
63
35
  return getattr(instance.args, self.dest) # type: ignore
64
36
 
65
37
 
66
- def arg(*args, **kwargs) -> Arg:
67
- return Arg(args, kwargs)
38
+ def argparse_arg(*args, **kwargs) -> ArgparseArg:
39
+ return ArgparseArg(args, kwargs)
68
40
 
69
41
 
70
42
  #
71
43
 
72
44
 
73
- CommandFn = ta.Callable[[], int | None]
74
-
75
-
76
45
  @dc.dataclass(eq=False)
77
- class Command:
46
+ class ArgparseCommand:
78
47
  name: str
79
- fn: CommandFn
80
- args: ta.Sequence[Arg] = () # noqa
48
+ fn: ArgparseCommandFn
49
+ args: ta.Sequence[ArgparseArg] = () # noqa
81
50
 
82
- _: dc.KW_ONLY
51
+ # _: dc.KW_ONLY
83
52
 
84
- aliases: ta.Sequence[str] | None = None
85
- parent: ta.Optional['Command'] = None
53
+ aliases: ta.Optional[ta.Sequence[str]] = None
54
+ parent: ta.Optional['ArgparseCommand'] = None
86
55
  accepts_unknown: bool = False
87
56
 
88
57
  def __post_init__(self) -> None:
@@ -95,9 +64,9 @@ class Command:
95
64
  for a in self.aliases or []:
96
65
  check_name(a)
97
66
 
98
- check.callable(self.fn)
99
- check.arg(all(isinstance(a, Arg) for a in self.args))
100
- check.isinstance(self.parent, (Command, None))
67
+ check.arg(callable(self.fn))
68
+ check.arg(all(isinstance(a, ArgparseArg) for a in self.args))
69
+ check.isinstance(self.parent, (ArgparseCommand, type(None)))
101
70
  check.isinstance(self.accepts_unknown, bool)
102
71
 
103
72
  functools.update_wrapper(self, self.fn)
@@ -107,25 +76,25 @@ class Command:
107
76
  return self
108
77
  return dc.replace(self, fn=self.fn.__get__(instance, owner)) # noqa
109
78
 
110
- def __call__(self, *args, **kwargs) -> int | None:
79
+ def __call__(self, *args, **kwargs) -> ta.Optional[int]:
111
80
  return self.fn(*args, **kwargs)
112
81
 
113
82
 
114
- def command(
115
- *args: Arg,
116
- name: str | None = None,
117
- aliases: ta.Iterable[str] | None = None,
118
- parent: Command | None = None,
83
+ def argparse_command(
84
+ *args: ArgparseArg,
85
+ name: ta.Optional[str] = None,
86
+ aliases: ta.Optional[ta.Iterable[str]] = None,
87
+ parent: ta.Optional[ArgparseCommand] = None,
119
88
  accepts_unknown: bool = False,
120
- ) -> ta.Any: # ta.Callable[[CommandFn], Command]: # FIXME
89
+ ) -> ta.Any: # ta.Callable[[ArgparseCommandFn], ArgparseCommand]: # FIXME
121
90
  for arg in args:
122
- check.isinstance(arg, Arg)
123
- check.isinstance(name, (str, None))
124
- check.isinstance(parent, (Command, None))
91
+ check.isinstance(arg, ArgparseArg)
92
+ check.isinstance(name, (str, type(None)))
93
+ check.isinstance(parent, (ArgparseCommand, type(None)))
125
94
  check.not_isinstance(aliases, str)
126
95
 
127
96
  def inner(fn):
128
- return Command(
97
+ return ArgparseCommand(
129
98
  (name if name is not None else fn.__name__).replace('_', '-'),
130
99
  fn,
131
100
  args,
@@ -140,7 +109,7 @@ def command(
140
109
  ##
141
110
 
142
111
 
143
- def get_arg_ann_kwargs(ann: ta.Any) -> ta.Mapping[str, ta.Any]:
112
+ def _get_argparse_arg_ann_kwargs(ann: ta.Any) -> ta.Mapping[str, ta.Any]:
144
113
  if ann is str:
145
114
  return {}
146
115
  elif ann is int:
@@ -153,48 +122,51 @@ def get_arg_ann_kwargs(ann: ta.Any) -> ta.Mapping[str, ta.Any]:
153
122
  raise TypeError(ann)
154
123
 
155
124
 
156
- class _AnnotationBox:
157
-
125
+ class _ArgparseCliAnnotationBox:
158
126
  def __init__(self, annotations: ta.Mapping[str, ta.Any]) -> None:
159
127
  super().__init__()
160
128
  self.__annotations__ = annotations # type: ignore
161
129
 
162
130
 
163
- class _CliMeta(type):
131
+ class ArgparseCli:
132
+ def __init__(self, argv: ta.Optional[ta.Sequence[str]] = None) -> None:
133
+ super().__init__()
134
+
135
+ self._argv = argv if argv is not None else sys.argv[1:]
164
136
 
165
- def __new__(mcls, name: str, bases: ta.Sequence[type], namespace: ta.Mapping[str, ta.Any]) -> type:
166
- if not bases:
167
- return super().__new__(mcls, name, tuple(bases), dict(namespace))
137
+ self._args, self._unknown_args = self.get_parser().parse_known_args(self._argv)
168
138
 
169
- bases = list(bases)
170
- namespace = dict(namespace)
139
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
140
+ super().__init_subclass__(**kwargs)
141
+
142
+ ns = cls.__dict__
171
143
 
172
144
  objs = {}
173
- mro = c3.merge([list(b.__mro__) for b in bases])
174
- for bns in [bcls.__dict__ for bcls in reversed(mro)] + [namespace]:
145
+ mro = cls.__mro__[::-1]
146
+ for bns in [bcls.__dict__ for bcls in reversed(mro)] + [ns]:
175
147
  bseen = set() # type: ignore
176
148
  for k, v in bns.items():
177
- if isinstance(v, (Command, Arg)):
149
+ if isinstance(v, (ArgparseCommand, ArgparseArg)):
178
150
  check.not_in(v, bseen)
179
151
  bseen.add(v)
180
152
  objs[k] = v
181
153
  elif k in objs:
182
154
  del [k]
183
155
 
184
- anns = ta.get_type_hints(_AnnotationBox({
156
+ anns = ta.get_type_hints(_ArgparseCliAnnotationBox({
185
157
  **{k: v for bcls in reversed(mro) for k, v in getattr(bcls, '__annotations__', {}).items()},
186
- **namespace.get('__annotations__', {}),
187
- }), globalns=namespace.get('__globals__', {}))
158
+ **ns.get('__annotations__', {}),
159
+ }), globalns=ns.get('__globals__', {}))
188
160
 
189
- if 'parser' in namespace:
190
- parser = check.isinstance(namespace.pop('parser'), ArgumentParser)
161
+ if '_parser' in ns:
162
+ parser = check.isinstance(ns['_parser'], argparse.ArgumentParser)
191
163
  else:
192
- parser = ArgumentParser()
193
- namespace['_parser'] = parser
164
+ parser = argparse.ArgumentParser()
165
+ setattr(cls, '_parser', parser)
194
166
 
195
167
  subparsers = parser.add_subparsers()
196
168
  for att, obj in objs.items():
197
- if isinstance(obj, Command):
169
+ if isinstance(obj, ArgparseCommand):
198
170
  if obj.parent is not None:
199
171
  raise NotImplementedError
200
172
  for cn in [obj.name, *(obj.aliases or [])]:
@@ -215,9 +187,9 @@ class _CliMeta(type):
215
187
  cparser.add_argument(*arg.args, **arg.kwargs)
216
188
  cparser.set_defaults(_cmd=obj)
217
189
 
218
- elif isinstance(obj, Arg):
190
+ elif isinstance(obj, ArgparseArg):
219
191
  if att in anns:
220
- akwargs = get_arg_ann_kwargs(anns[att])
192
+ akwargs = _get_argparse_arg_ann_kwargs(anns[att])
221
193
  obj.kwargs = {**akwargs, **obj.kwargs}
222
194
  if not obj.dest:
223
195
  if 'dest' in obj.kwargs:
@@ -229,22 +201,10 @@ class _CliMeta(type):
229
201
  else:
230
202
  raise TypeError(obj)
231
203
 
232
- return super().__new__(mcls, name, tuple(bases), namespace)
233
-
234
-
235
- class Cli(metaclass=_CliMeta):
236
-
237
- def __init__(self, argv: ta.Sequence[str] | None = None) -> None:
238
- super().__init__()
239
-
240
- self._argv = argv if argv is not None else sys.argv[1:]
241
-
242
- self._args, self._unknown_args = self.get_parser().parse_known_args(self._argv)
243
-
244
- _parser: ta.ClassVar[ArgumentParser]
204
+ _parser: ta.ClassVar[argparse.ArgumentParser]
245
205
 
246
206
  @classmethod
247
- def get_parser(cls) -> ArgumentParser:
207
+ def get_parser(cls) -> argparse.ArgumentParser:
248
208
  return cls._parser
249
209
 
250
210
  @property
@@ -252,17 +212,17 @@ class Cli(metaclass=_CliMeta):
252
212
  return self._argv
253
213
 
254
214
  @property
255
- def args(self) -> Namespace:
215
+ def args(self) -> argparse.Namespace:
256
216
  return self._args
257
217
 
258
218
  @property
259
219
  def unknown_args(self) -> ta.Sequence[str]:
260
220
  return self._unknown_args
261
221
 
262
- def _run_cmd(self, cmd: Command) -> int | None:
222
+ def _run_cmd(self, cmd: ArgparseCommand) -> ta.Optional[int]:
263
223
  return cmd.__get__(self, type(self))()
264
224
 
265
- def __call__(self) -> int | None:
225
+ def __call__(self) -> ta.Optional[int]:
266
226
  cmd = getattr(self.args, '_cmd', None)
267
227
 
268
228
  if self._unknown_args and not (cmd is not None and cmd.accepts_unknown):
@@ -270,7 +230,7 @@ class Cli(metaclass=_CliMeta):
270
230
  if (parser := self.get_parser()).exit_on_error: # type: ignore
271
231
  parser.error(msg)
272
232
  else:
273
- raise ArgumentError(None, msg)
233
+ raise argparse.ArgumentError(None, msg)
274
234
 
275
235
  if cmd is None:
276
236
  self.get_parser().print_help()