omlish 0.0.0.dev148__py3-none-any.whl → 0.0.0.dev150__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.
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()