muscles-cli 1.0.0__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.
- muscles/cli/Makefile +26 -0
- muscles/cli/__about__.py +6 -0
- muscles/cli/__init__.py +66 -0
- muscles/cli/__main__.py +5 -0
- muscles/cli/cli/__init__.py +6 -0
- muscles/cli/cli/cli.py +44 -0
- muscles/cli/cli/colory.py +15 -0
- muscles/cli/cli/command.py +778 -0
- muscles/cli/cli/constants.py +3 -0
- muscles/cli/cli/error_handler.py +6 -0
- muscles/cli/cli/func.py +221 -0
- muscles/cli/cli/instance.py +47 -0
- muscles/cli/cli/strategy.py +111 -0
- muscles/cli/providers.py +198 -0
- muscles/cli/streaming.py +50 -0
- muscles/cli/tooling.py +927 -0
- muscles_cli-1.0.0.dist-info/METADATA +165 -0
- muscles_cli-1.0.0.dist-info/RECORD +20 -0
- muscles_cli-1.0.0.dist-info/WHEEL +4 -0
- muscles_cli-1.0.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,778 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import typing
|
|
3
|
+
from typing import Any, Callable, Dict, List, cast
|
|
4
|
+
from functools import wraps
|
|
5
|
+
import os
|
|
6
|
+
import shutil
|
|
7
|
+
from .colory import Colors
|
|
8
|
+
from .func import argsparse, parse_arguments
|
|
9
|
+
from .cli import CliCommand
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def print_help(instance):
|
|
13
|
+
"""
|
|
14
|
+
Выводит подсказу в качестве результата работы команды
|
|
15
|
+
|
|
16
|
+
:param instance:
|
|
17
|
+
:return:
|
|
18
|
+
"""
|
|
19
|
+
try:
|
|
20
|
+
size = shutil.get_terminal_size(fallback=(100, 50))
|
|
21
|
+
rows, columns = size.lines, size.columns
|
|
22
|
+
except:
|
|
23
|
+
rows = 50
|
|
24
|
+
columns = 100
|
|
25
|
+
|
|
26
|
+
def split_str(string, length):
|
|
27
|
+
if len(string) <= length:
|
|
28
|
+
yield string
|
|
29
|
+
else:
|
|
30
|
+
for i in range(0, len(string), length):
|
|
31
|
+
yield string[i:i + length].strip()
|
|
32
|
+
|
|
33
|
+
print(f"\n")
|
|
34
|
+
print(f"{Colors.WARNING}", "help - command list", f"{Colors.ENDC}")
|
|
35
|
+
print(f"{Colors.WARNING}", "<command_group> help - help with the command", f"{Colors.ENDC}")
|
|
36
|
+
print("\n")
|
|
37
|
+
|
|
38
|
+
if hasattr(instance, '_children'):
|
|
39
|
+
if isinstance(instance, Group) and instance.command_name != 'cli':
|
|
40
|
+
print(f"{Colors.HEADER}")
|
|
41
|
+
print("{command} - {description}".format(command=instance.command_name.ljust(20),
|
|
42
|
+
description=instance.description))
|
|
43
|
+
print(f"-" * 22)
|
|
44
|
+
print(f"{Colors.ENDC}")
|
|
45
|
+
print(" ") # Переводим каретку, хоть и нет явного, все же перевод будет осуществлен
|
|
46
|
+
|
|
47
|
+
for command in instance._children:
|
|
48
|
+
if isinstance(command, Command):
|
|
49
|
+
if command.description:
|
|
50
|
+
for (i, string) in enumerate(split_str(' '.join(command.description.split()), int(columns) - 23)):
|
|
51
|
+
if i == 0:
|
|
52
|
+
print("{command} - {description}".format(command=command.command_name.ljust(20),
|
|
53
|
+
description=string))
|
|
54
|
+
else:
|
|
55
|
+
print(' ' * 22, string.ljust(int(columns) - 23))
|
|
56
|
+
else:
|
|
57
|
+
print("{command} - {description}".format(command=command.command_name.ljust(20),
|
|
58
|
+
description='<Description not available>'))
|
|
59
|
+
|
|
60
|
+
for i, argument in enumerate(command.arguments):
|
|
61
|
+
arg = "{s1}{s2}{s3}{s4}{s5}".format(
|
|
62
|
+
s1='[' if argument.required else '',
|
|
63
|
+
s2=argument.argument,
|
|
64
|
+
s3=', ' if argument.short else '',
|
|
65
|
+
s4=argument.short if argument.short else '',
|
|
66
|
+
s5=']' if argument.required else ''
|
|
67
|
+
)
|
|
68
|
+
print("%s%s%s" % (
|
|
69
|
+
" ".ljust(1),
|
|
70
|
+
arg.ljust(20),
|
|
71
|
+
argument.description.ljust(int(columns) - 23)
|
|
72
|
+
))
|
|
73
|
+
|
|
74
|
+
if isinstance(command, Group):
|
|
75
|
+
if command.description:
|
|
76
|
+
for (i, string) in enumerate(split_str(' '.join(command.description.split()), int(columns) - 23)):
|
|
77
|
+
if i == 0:
|
|
78
|
+
print("{command} - {description}".format(command=command.command_name.ljust(20),
|
|
79
|
+
description=string))
|
|
80
|
+
else:
|
|
81
|
+
print(' ' * 22, string.ljust(int(columns) - 23))
|
|
82
|
+
else:
|
|
83
|
+
print("{command} - {description}".format(command=command.command_name.ljust(20),
|
|
84
|
+
description='<Description not available>'))
|
|
85
|
+
|
|
86
|
+
print(" ") # Переводим каретку, хоть и нет явного, все же перевод будет осуществлен
|
|
87
|
+
else:
|
|
88
|
+
if instance.description is not None:
|
|
89
|
+
doc = instance.description
|
|
90
|
+
elif instance.handler.__doc__ is not None:
|
|
91
|
+
doc = instance.handler.__doc__
|
|
92
|
+
else:
|
|
93
|
+
doc = '<Description not available>'
|
|
94
|
+
print("{heads}{command}{heade}\n\n{description}".format(
|
|
95
|
+
heads=Colors.HEADER,
|
|
96
|
+
heade=Colors.ENDC,
|
|
97
|
+
command=instance.command_name.ljust(20),
|
|
98
|
+
description=doc))
|
|
99
|
+
print(f"\n\n- %s\n" % 'Arguments')
|
|
100
|
+
for i, argument in enumerate(instance.arguments):
|
|
101
|
+
arg = "{s1}{s2}{s3}{s4}{s5}".format(
|
|
102
|
+
s1='[' if argument.required else '',
|
|
103
|
+
s2=argument.argument,
|
|
104
|
+
s3=', ' if argument.short else '',
|
|
105
|
+
s4=argument.short if argument.short else '',
|
|
106
|
+
s5=']' if argument.required else ''
|
|
107
|
+
)
|
|
108
|
+
print("%s%s" % (
|
|
109
|
+
arg.ljust(20),
|
|
110
|
+
argument.description.ljust(int(columns) - 23)
|
|
111
|
+
))
|
|
112
|
+
|
|
113
|
+
print(f"\n")
|
|
114
|
+
return True
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class Command(CliCommand):
|
|
118
|
+
"""
|
|
119
|
+
Класс для обработки консольных команд
|
|
120
|
+
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
def __init__(self, key: str | list[str] | None,
|
|
124
|
+
function_name: typing.Optional[str] = None,
|
|
125
|
+
handler: Callable[..., Any] | None = None,
|
|
126
|
+
description: str = '',
|
|
127
|
+
command_name: typing.Optional[str] = None):
|
|
128
|
+
"""
|
|
129
|
+
Конструктор консольной команды
|
|
130
|
+
|
|
131
|
+
:param key: идентификатор консольной команды
|
|
132
|
+
:param function_name:
|
|
133
|
+
:param handler: Обработчик консольной команды
|
|
134
|
+
:param description: Текстовое описание команды
|
|
135
|
+
:param command_name: название консольной команды
|
|
136
|
+
"""
|
|
137
|
+
if not key:
|
|
138
|
+
raise Exception('`key` not specified')
|
|
139
|
+
self._key = key if isinstance(key, list) else [key]
|
|
140
|
+
self._function_name = function_name
|
|
141
|
+
self._handler = handler
|
|
142
|
+
self._description = description
|
|
143
|
+
self._parent = None
|
|
144
|
+
self._command_name = command_name if command_name else '-'.join(self._key)
|
|
145
|
+
self._arguments = []
|
|
146
|
+
|
|
147
|
+
def __repr__(self):
|
|
148
|
+
return "Command(%s)" % ', '.join([
|
|
149
|
+
'key=%s' % '/'.join(self.key),
|
|
150
|
+
'function_name=%s' % self.function_name,
|
|
151
|
+
'handler=%s' % "{}.{}".format(str(self.handler.__module__), str(self.handler.__name__)),
|
|
152
|
+
'command_name=%s' % self.command_name,
|
|
153
|
+
])
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def function_name(self) -> str:
|
|
157
|
+
return self._function_name or ""
|
|
158
|
+
|
|
159
|
+
@function_name.setter
|
|
160
|
+
def function_name(self, function_name) -> None:
|
|
161
|
+
self._function_name = function_name
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def handler(self) -> Callable[..., Any]:
|
|
165
|
+
if self._handler is None:
|
|
166
|
+
raise RuntimeError("Command handler is not configured")
|
|
167
|
+
return self._handler
|
|
168
|
+
|
|
169
|
+
@handler.setter
|
|
170
|
+
def handler(self, handler):
|
|
171
|
+
self._handler = handler
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def description(self) -> str:
|
|
175
|
+
return self._description
|
|
176
|
+
|
|
177
|
+
@description.setter
|
|
178
|
+
def description(self, description) -> None:
|
|
179
|
+
self._description = description
|
|
180
|
+
|
|
181
|
+
@property
|
|
182
|
+
def command_name(self) -> str:
|
|
183
|
+
return self._command_name
|
|
184
|
+
|
|
185
|
+
@command_name.setter
|
|
186
|
+
def command_name(self, command_name) -> None:
|
|
187
|
+
self._command_name = command_name
|
|
188
|
+
|
|
189
|
+
@property
|
|
190
|
+
def arguments(self) -> list[Argument]:
|
|
191
|
+
return self._arguments
|
|
192
|
+
|
|
193
|
+
@arguments.setter
|
|
194
|
+
def arguments(self, arguments) -> None:
|
|
195
|
+
self._arguments = arguments
|
|
196
|
+
|
|
197
|
+
def add_argument(self, argument: Argument) -> None:
|
|
198
|
+
"""
|
|
199
|
+
Добавляет аргемент к консольной команде
|
|
200
|
+
|
|
201
|
+
:param argument: объект класса Argument
|
|
202
|
+
:return:
|
|
203
|
+
"""
|
|
204
|
+
self._arguments.append(argument)
|
|
205
|
+
|
|
206
|
+
def add_arguments(self, arguments: list[Argument]) -> None:
|
|
207
|
+
"""
|
|
208
|
+
Добавляет список аргументов к консольной команде
|
|
209
|
+
|
|
210
|
+
:param arguments: список объектов класса Argument
|
|
211
|
+
:return:
|
|
212
|
+
"""
|
|
213
|
+
for argument in arguments:
|
|
214
|
+
self._arguments.append(argument)
|
|
215
|
+
|
|
216
|
+
def execute(self, *args, **_kwargs):
|
|
217
|
+
"""
|
|
218
|
+
Запускает обработку консольной команды
|
|
219
|
+
|
|
220
|
+
"""
|
|
221
|
+
handler = self.handler
|
|
222
|
+
handler_obj = cast(Any, handler)
|
|
223
|
+
if hasattr(handler_obj, 'arguments'):
|
|
224
|
+
arguments = handler_obj.arguments
|
|
225
|
+
else:
|
|
226
|
+
arguments = []
|
|
227
|
+
# args, kwargs = argsparse(arguments, args)
|
|
228
|
+
args, kwargs = parse_arguments(arguments, args)
|
|
229
|
+
kwargs.update(_kwargs)
|
|
230
|
+
params = {}
|
|
231
|
+
if hasattr(handler_obj, 'arguments'):
|
|
232
|
+
for argument in handler_obj.arguments:
|
|
233
|
+
params.update({argument.dest: kwargs.get(argument.dest, argument.default)})
|
|
234
|
+
result = handler(*args, **params)
|
|
235
|
+
return result
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class Argument:
|
|
239
|
+
"""
|
|
240
|
+
Клас обработки декоратов для установки возможных значений к консольным командам
|
|
241
|
+
|
|
242
|
+
"""
|
|
243
|
+
|
|
244
|
+
_dest = None
|
|
245
|
+
_argument = None
|
|
246
|
+
_short = None
|
|
247
|
+
_required = False
|
|
248
|
+
_description = ''
|
|
249
|
+
_default = None
|
|
250
|
+
_multiple = False
|
|
251
|
+
_flag_value = False
|
|
252
|
+
_nargs = 0
|
|
253
|
+
_prompt = None
|
|
254
|
+
_value = None
|
|
255
|
+
_hide = None
|
|
256
|
+
|
|
257
|
+
def __init__(self,
|
|
258
|
+
argument: typing.Optional[str] = None,
|
|
259
|
+
short: typing.Optional[str] = None,
|
|
260
|
+
dest: typing.Optional[str] = None,
|
|
261
|
+
required: bool = False,
|
|
262
|
+
description: str = '',
|
|
263
|
+
default: Any = None,
|
|
264
|
+
multiple: bool = False,
|
|
265
|
+
flag_value: bool = False,
|
|
266
|
+
nargs: int = 0,
|
|
267
|
+
prompt: typing.Optional[str] = None,
|
|
268
|
+
hide: typing.Optional[bool] = None
|
|
269
|
+
):
|
|
270
|
+
"""
|
|
271
|
+
Конструктор объекта аргумента
|
|
272
|
+
|
|
273
|
+
:param argument: строка параметр аргумента для консольной команды
|
|
274
|
+
:param short: краткий параметр аргумента консольной команды
|
|
275
|
+
:param dest: название аргумента, которому будет передо значение аргумента из консольной команды
|
|
276
|
+
:param required: Истина, если аргумент обязательный
|
|
277
|
+
:param description: Описание аргумента
|
|
278
|
+
:param default: Значение по умолчанию
|
|
279
|
+
:param multiple: Если Истина то аргумент можно указать несколько раз в одной консольной команде
|
|
280
|
+
:param flag_value: Если Истина то наличие аргумента устанавливает параметр в Истину, а отсутствие означает Лож
|
|
281
|
+
:param nargs: Количество значений аргумента
|
|
282
|
+
:param prompt: Если указан, то в консоли при выполнении команды будет задан вопрос с текстом из параметра и
|
|
283
|
+
команда примет введенный пользователем аргумент в качестве значения
|
|
284
|
+
:param hide: Установите если значение аргумента должно быть скрыто
|
|
285
|
+
"""
|
|
286
|
+
self._dest = dest
|
|
287
|
+
self._argument = argument
|
|
288
|
+
self._short = short
|
|
289
|
+
self._required = required
|
|
290
|
+
self._description = description
|
|
291
|
+
self._default = default
|
|
292
|
+
self._value = default
|
|
293
|
+
self._multiple = multiple
|
|
294
|
+
self._flag_value = flag_value
|
|
295
|
+
self._nargs = nargs
|
|
296
|
+
self._prompt = prompt
|
|
297
|
+
self._hide = hide
|
|
298
|
+
|
|
299
|
+
def __repr__(self):
|
|
300
|
+
return "Argument(%s)" % ', '.join([
|
|
301
|
+
'argument=%s' % self.argument,
|
|
302
|
+
'short=%s' % self.short,
|
|
303
|
+
'dest=%s' % self.dest,
|
|
304
|
+
'required=%s' % self.required,
|
|
305
|
+
'default=%s' % self.default,
|
|
306
|
+
'multiple=%s' % self.multiple,
|
|
307
|
+
'flag_value=%s' % self.flag_value,
|
|
308
|
+
'nargs=%s' % self.nargs,
|
|
309
|
+
# 'prompt=%s' % self.prompt,
|
|
310
|
+
'hide=%s' % self.hide,
|
|
311
|
+
])
|
|
312
|
+
|
|
313
|
+
@property
|
|
314
|
+
def value(self) -> Any:
|
|
315
|
+
return self._value
|
|
316
|
+
|
|
317
|
+
@value.setter
|
|
318
|
+
def value(self, value) -> None:
|
|
319
|
+
self._value = value
|
|
320
|
+
|
|
321
|
+
@property
|
|
322
|
+
def dest(self) -> str:
|
|
323
|
+
arg = self._argument or self._short or ""
|
|
324
|
+
dest = arg.replace("-", "")
|
|
325
|
+
return self._dest or dest
|
|
326
|
+
|
|
327
|
+
@dest.setter
|
|
328
|
+
def dest(self, dest) -> None:
|
|
329
|
+
if dest:
|
|
330
|
+
self._dest = dest
|
|
331
|
+
|
|
332
|
+
@property
|
|
333
|
+
def argument(self) -> str:
|
|
334
|
+
return self._argument or ""
|
|
335
|
+
|
|
336
|
+
@argument.setter
|
|
337
|
+
def argument(self, argument: str) -> None:
|
|
338
|
+
if not argument:
|
|
339
|
+
return
|
|
340
|
+
if argument[0:2] == '--':
|
|
341
|
+
dest = argument[2:]
|
|
342
|
+
elif argument[0:1] == '-':
|
|
343
|
+
dest = argument[1:]
|
|
344
|
+
else:
|
|
345
|
+
raise Exception("Argument %s must start with -- or -" % argument)
|
|
346
|
+
self.dest = dest
|
|
347
|
+
self._argument = argument
|
|
348
|
+
|
|
349
|
+
@property
|
|
350
|
+
def short(self) -> str:
|
|
351
|
+
return self._short or ""
|
|
352
|
+
|
|
353
|
+
@short.setter
|
|
354
|
+
def short(self, short: str | None) -> None:
|
|
355
|
+
self._short = short
|
|
356
|
+
|
|
357
|
+
@property
|
|
358
|
+
def prompt(self) -> str | None:
|
|
359
|
+
return self._prompt
|
|
360
|
+
|
|
361
|
+
@prompt.setter
|
|
362
|
+
def prompt(self, prompt: str | None) -> None:
|
|
363
|
+
if prompt and self._nargs <= 0:
|
|
364
|
+
self._nargs = 1
|
|
365
|
+
self._prompt = prompt
|
|
366
|
+
|
|
367
|
+
@property
|
|
368
|
+
def required(self) -> bool:
|
|
369
|
+
return self._required
|
|
370
|
+
|
|
371
|
+
@required.setter
|
|
372
|
+
def required(self, required: bool) -> None:
|
|
373
|
+
self._required = required
|
|
374
|
+
|
|
375
|
+
@property
|
|
376
|
+
def description(self) -> str:
|
|
377
|
+
return self._description
|
|
378
|
+
|
|
379
|
+
@description.setter
|
|
380
|
+
def description(self, description) -> None:
|
|
381
|
+
self._description = description
|
|
382
|
+
|
|
383
|
+
@property
|
|
384
|
+
def default(self):
|
|
385
|
+
return self._default
|
|
386
|
+
|
|
387
|
+
@default.setter
|
|
388
|
+
def default(self, default) -> None:
|
|
389
|
+
self._default = default
|
|
390
|
+
|
|
391
|
+
@property
|
|
392
|
+
def multiple(self) -> bool:
|
|
393
|
+
return self._multiple if self._multiple else False
|
|
394
|
+
|
|
395
|
+
@multiple.setter
|
|
396
|
+
def multiple(self, multiple: bool) -> None:
|
|
397
|
+
self._multiple = multiple
|
|
398
|
+
|
|
399
|
+
@property
|
|
400
|
+
def hide(self):
|
|
401
|
+
return self._hide
|
|
402
|
+
|
|
403
|
+
@hide.setter
|
|
404
|
+
def hide(self, hide) -> None:
|
|
405
|
+
if hide:
|
|
406
|
+
self.nargs = 1
|
|
407
|
+
self._hide = hide
|
|
408
|
+
|
|
409
|
+
@property
|
|
410
|
+
def flag_value(self):
|
|
411
|
+
return True if self._flag_value else False
|
|
412
|
+
|
|
413
|
+
@flag_value.setter
|
|
414
|
+
def flag_value(self, flag_value) -> None:
|
|
415
|
+
self._flag_value = flag_value
|
|
416
|
+
|
|
417
|
+
@property
|
|
418
|
+
def nargs(self) -> int:
|
|
419
|
+
return self._nargs
|
|
420
|
+
|
|
421
|
+
@nargs.setter
|
|
422
|
+
def nargs(self, nargs: int) -> None:
|
|
423
|
+
self._nargs = nargs
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
class Group(CliCommand):
|
|
427
|
+
"""
|
|
428
|
+
Класс для работы с группами консольных команд
|
|
429
|
+
|
|
430
|
+
"""
|
|
431
|
+
|
|
432
|
+
_commands: Dict[str, Any] = {}
|
|
433
|
+
|
|
434
|
+
def __init__(self,
|
|
435
|
+
key: str | list[str] | None = None,
|
|
436
|
+
function_name: str | None = None,
|
|
437
|
+
handler: Callable[..., Any] | None = None,
|
|
438
|
+
description: str = '',
|
|
439
|
+
command_name: typing.Optional[str] = None
|
|
440
|
+
):
|
|
441
|
+
"""
|
|
442
|
+
Конструктор объекта группы
|
|
443
|
+
|
|
444
|
+
:param key: идентификатор группы
|
|
445
|
+
:param function_name:
|
|
446
|
+
:param handler: обработчик группы
|
|
447
|
+
:param description: описание группы
|
|
448
|
+
:param command_name: название команды группы
|
|
449
|
+
"""
|
|
450
|
+
key_parts = key if isinstance(key, list) else ([key] if key else [])
|
|
451
|
+
self._children: list[Command | Group] = []
|
|
452
|
+
self._key = key_parts
|
|
453
|
+
self._function_name = function_name
|
|
454
|
+
self._handler = handler
|
|
455
|
+
self._description = description
|
|
456
|
+
self._parent = None
|
|
457
|
+
self._command_name = command_name if command_name else '-'.join(key_parts)
|
|
458
|
+
self._arguments: list[Argument] = []
|
|
459
|
+
self._children_by_command: dict[str, Command | Group] = {}
|
|
460
|
+
|
|
461
|
+
def __repr__(self):
|
|
462
|
+
return "Group(%s)" % ', '.join([
|
|
463
|
+
'key=%s' % '/'.join(self.key),
|
|
464
|
+
'function_name=%s' % self.function_name,
|
|
465
|
+
'handler=%s' % "{}.{}".format(str(self.handler.__module__), str(self.handler.__name__)),
|
|
466
|
+
'command_name=%s' % self.command_name,
|
|
467
|
+
'arguments=%s' % self.arguments,
|
|
468
|
+
'arguments=%s' % cast(Any, self.handler).arguments if hasattr(self.handler, 'arguments') else '[]',
|
|
469
|
+
])
|
|
470
|
+
|
|
471
|
+
@property
|
|
472
|
+
def function_name(self) -> str:
|
|
473
|
+
return self._function_name or ""
|
|
474
|
+
|
|
475
|
+
@function_name.setter
|
|
476
|
+
def function_name(self, function_name) -> None:
|
|
477
|
+
self._function_name = function_name
|
|
478
|
+
|
|
479
|
+
@property
|
|
480
|
+
def handler(self) -> Callable[..., Any]:
|
|
481
|
+
if self._handler is None:
|
|
482
|
+
raise RuntimeError("Group handler is not configured")
|
|
483
|
+
return self._handler
|
|
484
|
+
|
|
485
|
+
@handler.setter
|
|
486
|
+
def handler(self, handler):
|
|
487
|
+
self._handler = handler
|
|
488
|
+
|
|
489
|
+
@property
|
|
490
|
+
def arguments(self) -> list[Argument]:
|
|
491
|
+
return self._arguments
|
|
492
|
+
|
|
493
|
+
@arguments.setter
|
|
494
|
+
def arguments(self, arguments) -> None:
|
|
495
|
+
self._arguments = arguments
|
|
496
|
+
|
|
497
|
+
@property
|
|
498
|
+
def description(self) -> str:
|
|
499
|
+
return self._description
|
|
500
|
+
|
|
501
|
+
@description.setter
|
|
502
|
+
def description(self, description) -> None:
|
|
503
|
+
self._description = description
|
|
504
|
+
|
|
505
|
+
@property
|
|
506
|
+
def command_name(self) -> str:
|
|
507
|
+
return self._command_name
|
|
508
|
+
|
|
509
|
+
@command_name.setter
|
|
510
|
+
def command_name(self, command_name) -> None:
|
|
511
|
+
self._command_name = command_name
|
|
512
|
+
|
|
513
|
+
def add(self, command: Command | Group) -> None:
|
|
514
|
+
"""
|
|
515
|
+
Добавляет к группе новую команду или другую группу
|
|
516
|
+
|
|
517
|
+
:param command: объект группы или команды
|
|
518
|
+
:return:
|
|
519
|
+
"""
|
|
520
|
+
self._children.append(command)
|
|
521
|
+
self._children_by_command[command.command_name] = command
|
|
522
|
+
command.parent = self
|
|
523
|
+
|
|
524
|
+
def remove(self, command: Command | Group) -> None:
|
|
525
|
+
"""
|
|
526
|
+
Удаляет из группы команду или другую группу
|
|
527
|
+
|
|
528
|
+
:param command: объект группы или команды
|
|
529
|
+
:return:
|
|
530
|
+
"""
|
|
531
|
+
self._children.remove(command)
|
|
532
|
+
self._children_by_command.pop(command.command_name, None)
|
|
533
|
+
command.parent = None
|
|
534
|
+
|
|
535
|
+
def execute(self, *args, **_kwargs):
|
|
536
|
+
"""
|
|
537
|
+
Запускает выполнение обработчика группы
|
|
538
|
+
|
|
539
|
+
:param args:
|
|
540
|
+
:param _kwargs:
|
|
541
|
+
:return:
|
|
542
|
+
"""
|
|
543
|
+
if hasattr(self.handler, 'arguments'):
|
|
544
|
+
arguments = cast(Any, self.handler).arguments
|
|
545
|
+
else:
|
|
546
|
+
arguments = []
|
|
547
|
+
if isinstance(args, str):
|
|
548
|
+
args = [args]
|
|
549
|
+
# args, kwargs = argsparse(arguments, args)
|
|
550
|
+
args, kwargs = parse_arguments(arguments, args)
|
|
551
|
+
kwargs.update(_kwargs)
|
|
552
|
+
if len(args) >= 1 and isinstance(args[0], str) and args[0].startswith('-'):
|
|
553
|
+
raise ValueError(f"Invalid argument: {args[0]}")
|
|
554
|
+
params = {}
|
|
555
|
+
if hasattr(self.handler, 'arguments'):
|
|
556
|
+
for argument in cast(Any, self.handler).arguments:
|
|
557
|
+
params.update({argument.dest: kwargs.get(argument.dest, argument.default)})
|
|
558
|
+
result = self.handler(*args, **params)
|
|
559
|
+
if len(args) >= 1:
|
|
560
|
+
command = self._children_by_command.get(args[0])
|
|
561
|
+
if command:
|
|
562
|
+
return command.execute(*args[1:], **kwargs)
|
|
563
|
+
print(f"\n")
|
|
564
|
+
|
|
565
|
+
if len(args[0]) > 0:
|
|
566
|
+
raise Exception("Command `%s` not found" % args[0])
|
|
567
|
+
return result
|
|
568
|
+
|
|
569
|
+
def help(self, *args, **kwargs):
|
|
570
|
+
"""
|
|
571
|
+
Устанавливает команду вывода помощи для подсказок по группе
|
|
572
|
+
|
|
573
|
+
:param args:
|
|
574
|
+
:param kwargs:
|
|
575
|
+
:return:
|
|
576
|
+
"""
|
|
577
|
+
self._help(args[:-1], self)
|
|
578
|
+
|
|
579
|
+
def _help(self, args, command):
|
|
580
|
+
if len(args) > 0:
|
|
581
|
+
args, _command = args[1:] if len(args[1:]) > 0 else [], args[0:1][0] if len(args[0:1]) > 0 else None
|
|
582
|
+
if hasattr(command, '_children_by_command') and _command in command._children_by_command:
|
|
583
|
+
self._help(args, command._children_by_command[_command])
|
|
584
|
+
else:
|
|
585
|
+
return print_help(command)
|
|
586
|
+
|
|
587
|
+
def _default_func_prop(self, func):
|
|
588
|
+
if not hasattr(func, 'arguments'):
|
|
589
|
+
setattr(func, 'arguments', [])
|
|
590
|
+
if not hasattr(func, 'command'):
|
|
591
|
+
setattr(func, 'command', None)
|
|
592
|
+
if not hasattr(func, 'group'):
|
|
593
|
+
setattr(func, 'group', None)
|
|
594
|
+
|
|
595
|
+
def command(self, command_name: typing.Union[str, None] = None, description: typing.Union[str, None] = "-"):
|
|
596
|
+
"""
|
|
597
|
+
Декоратор устанавливающий функцию как обработчик команды
|
|
598
|
+
|
|
599
|
+
:param command_name: Название команды
|
|
600
|
+
:param description: Описание
|
|
601
|
+
:return:
|
|
602
|
+
"""
|
|
603
|
+
|
|
604
|
+
def decorator(func):
|
|
605
|
+
self._default_func_prop(func)
|
|
606
|
+
func_obj = cast(Any, func)
|
|
607
|
+
key = [func.__name__] + self.key
|
|
608
|
+
_command = Command(key)
|
|
609
|
+
_command.command_name = command_name if command_name else func.__name__
|
|
610
|
+
_command.description = description if description is not None else func.__doc__
|
|
611
|
+
_command.function_name = func.__name__
|
|
612
|
+
_command.handler = func
|
|
613
|
+
_command.add_arguments(func_obj.arguments)
|
|
614
|
+
self.add(_command)
|
|
615
|
+
# _command = commandFactory.update(key, _command)
|
|
616
|
+
setattr(func, 'command', _command)
|
|
617
|
+
|
|
618
|
+
@wraps(func)
|
|
619
|
+
def wrapper(*args, **kwargs):
|
|
620
|
+
return func(*args, **kwargs)
|
|
621
|
+
|
|
622
|
+
wrapper.__name__ = func.__name__
|
|
623
|
+
wrapper.__doc__ = func.__doc__
|
|
624
|
+
return wrapper
|
|
625
|
+
|
|
626
|
+
return decorator
|
|
627
|
+
|
|
628
|
+
def group(self, description=None, command_name=None):
|
|
629
|
+
"""
|
|
630
|
+
Декоратор устанавливающий функцию как обработчик группы команд
|
|
631
|
+
|
|
632
|
+
:param description: описание
|
|
633
|
+
:param command_name: название команды
|
|
634
|
+
:return:
|
|
635
|
+
"""
|
|
636
|
+
|
|
637
|
+
def decorator(func):
|
|
638
|
+
self._default_func_prop(func)
|
|
639
|
+
func_obj = cast(Any, func)
|
|
640
|
+
key = [func.__name__] + self.key
|
|
641
|
+
_group = Group(key)
|
|
642
|
+
_group.command_name = command_name if command_name else func.__name__
|
|
643
|
+
_group.function_name = func.__name__
|
|
644
|
+
_group.description = description if description is not None else func.__doc__
|
|
645
|
+
_group.handler = func
|
|
646
|
+
setattr(func_obj, 'group', _group)
|
|
647
|
+
self.add(_group)
|
|
648
|
+
|
|
649
|
+
# key = ['help'] + _group.key
|
|
650
|
+
key = _group.key + ['help']
|
|
651
|
+
_command = Command(key)
|
|
652
|
+
_command.function_name = _group.help.__name__
|
|
653
|
+
_command.handler = _group.help
|
|
654
|
+
_command.command_name = 'help'
|
|
655
|
+
_command.description = "Command list for %s" % _group.command_name
|
|
656
|
+
|
|
657
|
+
_group.add(_command)
|
|
658
|
+
|
|
659
|
+
@wraps(func)
|
|
660
|
+
def wrapper(*args, **kwargs):
|
|
661
|
+
return func(*args, **kwargs)
|
|
662
|
+
|
|
663
|
+
wrapper.__name__ = func.__name__
|
|
664
|
+
wrapper.__doc__ = func.__doc__
|
|
665
|
+
return _group
|
|
666
|
+
|
|
667
|
+
return decorator
|
|
668
|
+
|
|
669
|
+
def argument(self, argument, short=None, description='', required=False, default=None,
|
|
670
|
+
multiple=False, flag_value=False, dest=None, nargs=0, prompt=None, hide=False):
|
|
671
|
+
"""
|
|
672
|
+
Декоратор устанавливающий к функции определенной декоратором группы или команды новые аргументы
|
|
673
|
+
|
|
674
|
+
:param argument: строка параметр аргумента для консольной команды
|
|
675
|
+
:param short: краткий параметр аргумента консольной команды
|
|
676
|
+
:param dest: название аргумента, которому будет передо значение аргумента из консольной команды
|
|
677
|
+
:param required: Истина, если аргумент обязательный
|
|
678
|
+
:param description: Описание аргумента
|
|
679
|
+
:param default: Значение по умолчанию
|
|
680
|
+
:param multiple: Если Истина то аргумент можно указать несколько раз в одной консольной команде
|
|
681
|
+
:param flag_value: Если Истина то наличие аргумента устанавливает параметр в Истину, а отсутствие означает Лож
|
|
682
|
+
:param nargs: Количество значений аргумента
|
|
683
|
+
:param prompt: Если указан, то в консоли при выполнении команды будет задан вопрос с текстом из параметра и
|
|
684
|
+
команда примет введенный пользователем аргумент в качестве значения
|
|
685
|
+
:param hide: Установите если значение аргумента должно быть скрыто
|
|
686
|
+
:return:
|
|
687
|
+
"""
|
|
688
|
+
|
|
689
|
+
def decorator(func):
|
|
690
|
+
self._default_func_prop(func)
|
|
691
|
+
func_obj = cast(Any, func)
|
|
692
|
+
_argument = Argument(
|
|
693
|
+
argument=argument,
|
|
694
|
+
short=short,
|
|
695
|
+
description=description,
|
|
696
|
+
required=required,
|
|
697
|
+
default=False if flag_value is True and default is None else default,
|
|
698
|
+
multiple=multiple,
|
|
699
|
+
dest=dest,
|
|
700
|
+
flag_value=flag_value,
|
|
701
|
+
nargs=nargs,
|
|
702
|
+
prompt=prompt,
|
|
703
|
+
hide=hide
|
|
704
|
+
)
|
|
705
|
+
# _argument.argument = argument
|
|
706
|
+
# _argument.short = short
|
|
707
|
+
# _argument.description = description
|
|
708
|
+
# _argument.required = required
|
|
709
|
+
# _argument.default = default
|
|
710
|
+
# _argument.multiple = multiple
|
|
711
|
+
# _argument.dest = dest
|
|
712
|
+
# _argument.flag_value = flag_value
|
|
713
|
+
# _argument.nargs = nargs
|
|
714
|
+
# _argument.prompt = prompt
|
|
715
|
+
# _argument.hide = hide
|
|
716
|
+
|
|
717
|
+
if _argument.required and _argument.nargs <= 0:
|
|
718
|
+
_argument.nargs = 1
|
|
719
|
+
|
|
720
|
+
func_obj.arguments.append(_argument)
|
|
721
|
+
self.arguments = getattr(func_obj, 'arguments')
|
|
722
|
+
|
|
723
|
+
@wraps(func)
|
|
724
|
+
def wrapper(*args, **kwargs):
|
|
725
|
+
# signature = inspect.signature(func)
|
|
726
|
+
# params = signature.parameters
|
|
727
|
+
# must_have_kwargs = {}
|
|
728
|
+
# for name, param in params.items():
|
|
729
|
+
# must_have_kwargs[name] = param.default
|
|
730
|
+
# if len(must_have_kwargs) != len(kwargs):
|
|
731
|
+
# raise Exception(locale('Количество переданных параметров отличается от количества запрошенных'))
|
|
732
|
+
return func(*args, **kwargs)
|
|
733
|
+
|
|
734
|
+
wrapper.__name__ = func.__name__
|
|
735
|
+
wrapper.__doc__ = func.__doc__
|
|
736
|
+
return wrapper
|
|
737
|
+
|
|
738
|
+
return decorator
|
|
739
|
+
|
|
740
|
+
def flag(self, argument, short=None, description='', dest=None, default=False, flag_value=True):
|
|
741
|
+
"""
|
|
742
|
+
Декоратор устанавливающий к функции определенной как декоратором группы или команды новый лаг
|
|
743
|
+
|
|
744
|
+
:param argument: строка параметр аргумента для консольной команды
|
|
745
|
+
:param short: краткий параметр аргумента консольной команды
|
|
746
|
+
:param dest: название аргумента, которому будет передо значение аргумента из консольной команды
|
|
747
|
+
:param description: Описание аргумента
|
|
748
|
+
:param default: Значение по умолчанию
|
|
749
|
+
:param flag_value: Если Истина то наличие аргумента устанавливает параметр в Истину, а отсутствие означает Лож
|
|
750
|
+
:return:
|
|
751
|
+
"""
|
|
752
|
+
|
|
753
|
+
def decorator(func):
|
|
754
|
+
self._default_func_prop(func)
|
|
755
|
+
func_obj = cast(Any, func)
|
|
756
|
+
_argument = Argument()
|
|
757
|
+
_argument.argument = argument
|
|
758
|
+
_argument.short = short
|
|
759
|
+
_argument.description = description
|
|
760
|
+
_argument.required = False
|
|
761
|
+
_argument.default = default
|
|
762
|
+
_argument.multiple = False
|
|
763
|
+
_argument.dest = dest
|
|
764
|
+
_argument.flag_value = flag_value
|
|
765
|
+
_argument.nargs = 0
|
|
766
|
+
_argument.prompt = None
|
|
767
|
+
func_obj.arguments.append(_argument)
|
|
768
|
+
self.arguments = func_obj.arguments
|
|
769
|
+
|
|
770
|
+
@wraps(func)
|
|
771
|
+
def wrapper(*args, **kwargs):
|
|
772
|
+
return func(*args, **kwargs)
|
|
773
|
+
|
|
774
|
+
wrapper.__name__ = func.__name__
|
|
775
|
+
wrapper.__doc__ = func.__doc__
|
|
776
|
+
return wrapper
|
|
777
|
+
|
|
778
|
+
return decorator
|