cli2 5.2.2__tar.gz → 6.0.0rc4__tar.gz
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.
- cli2-6.0.0rc4/PKG-INFO +34 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/__init__.py +16 -4
- cli2-6.0.0rc4/cli2/asyncio.py +65 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/cli.py +76 -49
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/configuration.py +6 -3
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/display.py +15 -4
- cli2-6.0.0rc4/cli2/examples/traceback_demo.py +205 -0
- cli2-6.0.0rc4/cli2/exceptions.py +28 -0
- cli2-6.0.0rc4/cli2/file.py +241 -0
- cli2-6.0.0rc4/cli2/find.py +146 -0
- cli2-6.0.0rc4/cli2/flow2.py +12 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/interactive.py +33 -17
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/log.py +15 -1
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/proc.py +14 -74
- cli2-5.2.2/cli2/asyncio.py → cli2-6.0.0rc4/cli2/queue.py +12 -41
- cli2-6.0.0rc4/cli2/template2.py +13 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/theme.py +8 -7
- cli2-6.0.0rc4/cli2/traceback.py +454 -0
- cli2-6.0.0rc4/cli2.egg-info/PKG-INFO +34 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2.egg-info/SOURCES.txt +9 -25
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2.egg-info/entry_points.txt +8 -2
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2.egg-info/requires.txt +2 -0
- cli2-6.0.0rc4/pyproject.toml +3 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/setup.py +11 -3
- cli2-5.2.2/PKG-INFO +0 -69
- cli2-5.2.2/README.rst +0 -31
- cli2-5.2.2/classifiers.txt +0 -10
- cli2-5.2.2/cli2.egg-info/PKG-INFO +0 -69
- cli2-5.2.2/tests/test_ansible.py +0 -222
- cli2-5.2.2/tests/test_ansible_variables.py +0 -46
- cli2-5.2.2/tests/test_asyncio.py +0 -53
- cli2-5.2.2/tests/test_cli.py +0 -85
- cli2-5.2.2/tests/test_client.py +0 -1358
- cli2-5.2.2/tests/test_client_test.py +0 -21
- cli2-5.2.2/tests/test_command.py +0 -724
- cli2-5.2.2/tests/test_configuration.py +0 -80
- cli2-5.2.2/tests/test_decorators.py +0 -114
- cli2-5.2.2/tests/test_display.py +0 -72
- cli2-5.2.2/tests/test_entry_point.py +0 -16
- cli2-5.2.2/tests/test_group.py +0 -255
- cli2-5.2.2/tests/test_inject.py +0 -120
- cli2-5.2.2/tests/test_interactive.py +0 -50
- cli2-5.2.2/tests/test_lock.py +0 -44
- cli2-5.2.2/tests/test_log.py +0 -80
- cli2-5.2.2/tests/test_mask.py +0 -30
- cli2-5.2.2/tests/test_node.py +0 -67
- cli2-5.2.2/tests/test_notlevenshtein.py +0 -46
- cli2-5.2.2/tests/test_proc.py +0 -150
- cli2-5.2.2/tests/test_prompt2.py +0 -210
- cli2-5.2.2/tests/test_restful.py +0 -95
- cli2-5.2.2/tests/test_table.py +0 -130
- {cli2-5.2.2 → cli2-6.0.0rc4}/MANIFEST.in +0 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/cli2.py +0 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/colors.py +0 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/decorators.py +0 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/examples/__init__.py +0 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/examples/conf.py +0 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/examples/example.py +0 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/examples/example_obj.py +0 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/examples/nesting.py +0 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/examples/obj.py +0 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/examples/obj2.py +0 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/examples/test.py +0 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/lock.py +0 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/mask.py +0 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/node.py +0 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/notlevenshtein.py +0 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/sphinx.py +0 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/table.py +0 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2/test.py +0 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2.egg-info/dependency_links.txt +0 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/cli2.egg-info/top_level.txt +0 -0
- {cli2-5.2.2 → cli2-6.0.0rc4}/setup.cfg +0 -0
cli2-6.0.0rc4/PKG-INFO
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cli2
|
|
3
|
+
Version: 6.0.0rc4
|
|
4
|
+
Home-page: https://yourlabs.io/oss/cli2
|
|
5
|
+
Author: James Pic
|
|
6
|
+
Author-email: jamespic@gmail.com
|
|
7
|
+
License: MIT
|
|
8
|
+
Keywords: cli
|
|
9
|
+
Requires-Python: >=3.6
|
|
10
|
+
Requires-Dist: docstring_parser
|
|
11
|
+
Requires-Dist: pyyaml
|
|
12
|
+
Requires-Dist: pygments
|
|
13
|
+
Requires-Dist: structlog
|
|
14
|
+
Requires-Dist: aiofiles
|
|
15
|
+
Provides-Extra: httpx
|
|
16
|
+
Requires-Dist: chttpx; extra == "httpx"
|
|
17
|
+
Provides-Extra: ansible
|
|
18
|
+
Requires-Dist: cansible; extra == "ansible"
|
|
19
|
+
Provides-Extra: test
|
|
20
|
+
Requires-Dist: freezegun; extra == "test"
|
|
21
|
+
Requires-Dist: pytest; extra == "test"
|
|
22
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
23
|
+
Requires-Dist: pytest-mock; extra == "test"
|
|
24
|
+
Requires-Dist: pytest-asyncio; extra == "test"
|
|
25
|
+
Requires-Dist: pytest-httpx; extra == "test"
|
|
26
|
+
Requires-Dist: pytest-env; extra == "test"
|
|
27
|
+
Dynamic: author
|
|
28
|
+
Dynamic: author-email
|
|
29
|
+
Dynamic: home-page
|
|
30
|
+
Dynamic: keywords
|
|
31
|
+
Dynamic: license
|
|
32
|
+
Dynamic: provides-extra
|
|
33
|
+
Dynamic: requires-dist
|
|
34
|
+
Dynamic: requires-python
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
# flake8: noqa
|
|
2
|
+
|
|
3
|
+
from .configuration import Configuration, cfg
|
|
4
|
+
cfg.defaults['CLI2_TRACEBACK_DISABLE'] = ''
|
|
5
|
+
|
|
2
6
|
from .cli import (
|
|
3
7
|
cmd,
|
|
4
8
|
arg,
|
|
@@ -11,13 +15,12 @@ from .cli import (
|
|
|
11
15
|
Cli2Error,
|
|
12
16
|
Cli2ValueError,
|
|
13
17
|
)
|
|
14
|
-
from .asyncio import async_resolve,
|
|
18
|
+
from .asyncio import async_resolve, files_read
|
|
19
|
+
from .queue import Queue
|
|
15
20
|
from .colors import colors as c
|
|
16
21
|
from .theme import theme, t
|
|
17
|
-
|
|
18
|
-
from .configuration import Configuration, cfg
|
|
19
22
|
from .display import diff, diff_data, render, print, highlight, yaml_highlight
|
|
20
|
-
from .interactive import choice, editor
|
|
23
|
+
from .interactive import confirm, choice, editor
|
|
21
24
|
try:
|
|
22
25
|
import fcntl
|
|
23
26
|
except ImportError:
|
|
@@ -29,9 +32,17 @@ from .log import configure, log, parse
|
|
|
29
32
|
from .mask import Mask
|
|
30
33
|
from .notlevenshtein import closest, closest_path
|
|
31
34
|
from .proc import Proc
|
|
35
|
+
from .find import Find
|
|
32
36
|
from .table import Table
|
|
33
37
|
|
|
34
38
|
|
|
39
|
+
import os
|
|
40
|
+
|
|
41
|
+
if not bool(cfg['CLI2_TRACEBACK_DISABLE']):
|
|
42
|
+
from .traceback import enable
|
|
43
|
+
enable()
|
|
44
|
+
|
|
45
|
+
|
|
35
46
|
def which(cmd):
|
|
36
47
|
""" Wrapper around shutil.which, and also check for ~/.local/bin. """
|
|
37
48
|
import shutil
|
|
@@ -39,6 +50,7 @@ def which(cmd):
|
|
|
39
50
|
if path:
|
|
40
51
|
return path
|
|
41
52
|
|
|
53
|
+
from pathlib import Path
|
|
42
54
|
path = Path(os.getenv('HOME')) / '.local/bin' / cmd
|
|
43
55
|
if path.exists():
|
|
44
56
|
return str(path)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import aiofiles
|
|
2
|
+
import inspect
|
|
3
|
+
from . import display
|
|
4
|
+
from .queue import Queue
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def async_iter(obj):
|
|
8
|
+
""" Check if an object is an async iterable. """
|
|
9
|
+
return inspect.isasyncgen(obj) or hasattr(obj, '__aiter__')
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def async_resolve(result, output=False):
|
|
13
|
+
"""
|
|
14
|
+
Recursively resolve awaitables and async iterables.
|
|
15
|
+
|
|
16
|
+
:param result: The awaitable or async iterable to resolve
|
|
17
|
+
:param output: If True, print results as they are resolved. If False,
|
|
18
|
+
collect results.
|
|
19
|
+
|
|
20
|
+
:return: The resolved value(s). If output is True, returns None. If output
|
|
21
|
+
is False, returns a list of resolved values from async iterables.
|
|
22
|
+
"""
|
|
23
|
+
while inspect.iscoroutine(result):
|
|
24
|
+
result = await result
|
|
25
|
+
|
|
26
|
+
if async_iter(result):
|
|
27
|
+
results = []
|
|
28
|
+
async for _ in result:
|
|
29
|
+
if output:
|
|
30
|
+
if (
|
|
31
|
+
not inspect.iscoroutine(_)
|
|
32
|
+
and not inspect.isasyncgen(_)
|
|
33
|
+
):
|
|
34
|
+
display.print(_)
|
|
35
|
+
else:
|
|
36
|
+
await async_resolve(_, output=output)
|
|
37
|
+
else:
|
|
38
|
+
results.append(await async_resolve(_))
|
|
39
|
+
return None if output else results
|
|
40
|
+
return result
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
async def files_read(paths, num_workers=None, mode='r', silent=False):
|
|
44
|
+
"""
|
|
45
|
+
Read a list of files asynchronously with anyio.
|
|
46
|
+
|
|
47
|
+
:param paths: File paths to read.
|
|
48
|
+
:param num_workers: Number of workers, cpucount*2 by default.
|
|
49
|
+
:return: Dict of path=content
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
result = dict()
|
|
53
|
+
|
|
54
|
+
async def file_read(path):
|
|
55
|
+
try:
|
|
56
|
+
async with aiofiles.open(str(path), mode) as f:
|
|
57
|
+
result[path] = await f.read()
|
|
58
|
+
except: # noqa
|
|
59
|
+
if not silent:
|
|
60
|
+
raise
|
|
61
|
+
|
|
62
|
+
queue = Queue(num_workers=num_workers)
|
|
63
|
+
await queue.run(*[file_read(path) for path in paths])
|
|
64
|
+
|
|
65
|
+
return {key: result[key] for key in sorted(result)}
|
|
@@ -10,12 +10,10 @@ import textwrap
|
|
|
10
10
|
from docstring_parser import parse
|
|
11
11
|
|
|
12
12
|
from . import display
|
|
13
|
-
from .asyncio import async_resolve
|
|
14
13
|
from .colors import colors
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
pass
|
|
14
|
+
from .asyncio import async_resolve
|
|
15
|
+
from .theme import t
|
|
16
|
+
from .exceptions import Cli2Error, NotFoundError
|
|
19
17
|
|
|
20
18
|
|
|
21
19
|
class Cli2ValueError(Cli2Error):
|
|
@@ -115,17 +113,30 @@ class EntryPoint:
|
|
|
115
113
|
sys.exit(self.exit_code)
|
|
116
114
|
|
|
117
115
|
def print(self, *args, sep=' ', end='\n', file=None, color=None):
|
|
118
|
-
if args and args[0].lower() in
|
|
116
|
+
if args and args[0].lower() in t.__dict__ and not color:
|
|
117
|
+
color_name = args[0]
|
|
118
|
+
args = args[1:]
|
|
119
|
+
color = getattr(t, color_name.lower())
|
|
120
|
+
if color_name.lower() != color_name:
|
|
121
|
+
color = getattr(color, 'bold')
|
|
122
|
+
elif args and args[0].lower() in colors.__dict__ and not color:
|
|
123
|
+
# backward compatibility
|
|
119
124
|
color = args[0]
|
|
120
125
|
args = args[1:]
|
|
121
126
|
if color.lower() != color:
|
|
122
127
|
color = color.lower() + 'bold'
|
|
123
128
|
color = getattr(colors, color)
|
|
129
|
+
elif color:
|
|
130
|
+
color = getattr(t, color)
|
|
124
131
|
|
|
125
132
|
msg = sep.join(map(str, args))
|
|
126
133
|
|
|
127
134
|
if color:
|
|
128
|
-
|
|
135
|
+
if isinstance(color, str):
|
|
136
|
+
# backward compatibility
|
|
137
|
+
msg = color + msg + colors.reset
|
|
138
|
+
else:
|
|
139
|
+
msg = color(msg)
|
|
129
140
|
|
|
130
141
|
print(msg, end=end, file=file or self.outfile, flush=True)
|
|
131
142
|
|
|
@@ -164,7 +175,7 @@ class Group(EntryPoint, dict):
|
|
|
164
175
|
self.doc = textwrap.dedent(doc).strip()
|
|
165
176
|
else:
|
|
166
177
|
self.doc = inspect.getdoc(self)
|
|
167
|
-
self.color = color or
|
|
178
|
+
self.color = color or 'green'
|
|
168
179
|
self.posix = posix
|
|
169
180
|
self.parent = None
|
|
170
181
|
self.cmdclass = cmdclass or Command
|
|
@@ -247,7 +258,7 @@ class Group(EntryPoint, dict):
|
|
|
247
258
|
return ''
|
|
248
259
|
|
|
249
260
|
if error:
|
|
250
|
-
self.print('RED', 'ERROR: ' +
|
|
261
|
+
self.print('RED', 'ERROR: ' + t.reset + error, end='\n\n')
|
|
251
262
|
|
|
252
263
|
self.print('ORANGE', 'SYNOPSYS')
|
|
253
264
|
chain = []
|
|
@@ -271,7 +282,7 @@ class Group(EntryPoint, dict):
|
|
|
271
282
|
table = Table(*[
|
|
272
283
|
(
|
|
273
284
|
(
|
|
274
|
-
getattr(
|
|
285
|
+
getattr(t, command.color, command.color),
|
|
275
286
|
name,
|
|
276
287
|
),
|
|
277
288
|
command.help(short=True),
|
|
@@ -283,6 +294,11 @@ class Group(EntryPoint, dict):
|
|
|
283
294
|
help.cli2 = dict(color='green')
|
|
284
295
|
|
|
285
296
|
def load(self, obj):
|
|
297
|
+
if loader := getattr(obj, 'cli2_load', None):
|
|
298
|
+
loading = getattr(obj, '_cli2_loading', False)
|
|
299
|
+
if not loading:
|
|
300
|
+
obj._cli2_loading = True
|
|
301
|
+
return loader(self)
|
|
286
302
|
if isinstance(obj, type):
|
|
287
303
|
return self.load_cls(obj)
|
|
288
304
|
return self.load_obj(obj)
|
|
@@ -365,8 +381,11 @@ class Command(EntryPoint, dict):
|
|
|
365
381
|
|
|
366
382
|
def __new__(cls, target, *args, **kwargs):
|
|
367
383
|
overrides = getattr(target, 'cli2', {})
|
|
368
|
-
|
|
369
|
-
|
|
384
|
+
other_cls = overrides.get('cls', cls)
|
|
385
|
+
if other_cls:
|
|
386
|
+
cls = other_cls
|
|
387
|
+
result = super().__new__(cls, *args, **kwargs)
|
|
388
|
+
return result
|
|
370
389
|
|
|
371
390
|
def __init__(self, target, name=None, color=None, doc=None, posix=False,
|
|
372
391
|
help_hack=True, outfile=None, log=True, overrides=None):
|
|
@@ -483,8 +502,8 @@ class Command(EntryPoint, dict):
|
|
|
483
502
|
def cmd(cls, *args, **kwargs):
|
|
484
503
|
def override(target):
|
|
485
504
|
overrides = getattr(target, 'cli2', {})
|
|
486
|
-
overrides.update(kwargs)
|
|
487
505
|
overrides['cls'] = cls
|
|
506
|
+
overrides.update(kwargs)
|
|
488
507
|
target.cli2 = overrides
|
|
489
508
|
|
|
490
509
|
if len(args) == 1 and not kwargs:
|
|
@@ -516,7 +535,7 @@ class Command(EntryPoint, dict):
|
|
|
516
535
|
)
|
|
517
536
|
|
|
518
537
|
if error:
|
|
519
|
-
self.print('RED', 'ERROR: ' +
|
|
538
|
+
self.print('RED', 'ERROR: ' + t.reset + error, end='\n\n')
|
|
520
539
|
|
|
521
540
|
self.print('ORANGE', 'SYNOPSYS')
|
|
522
541
|
chain = []
|
|
@@ -640,7 +659,13 @@ class Command(EntryPoint, dict):
|
|
|
640
659
|
finally:
|
|
641
660
|
self.post_result = asyncio.run(async_resolve(self.post_call()))
|
|
642
661
|
|
|
643
|
-
|
|
662
|
+
try:
|
|
663
|
+
error = self.parse(*argv)
|
|
664
|
+
except Cli2ValueError as exc:
|
|
665
|
+
return self.help(error=exc.args[0])
|
|
666
|
+
except Exception as exc:
|
|
667
|
+
return self.handle_exception(exc)
|
|
668
|
+
|
|
644
669
|
if error:
|
|
645
670
|
self.exit_code = 1
|
|
646
671
|
return self.help(error=error)
|
|
@@ -666,11 +691,27 @@ class Command(EntryPoint, dict):
|
|
|
666
691
|
self.post_result = self.post_call()
|
|
667
692
|
|
|
668
693
|
def handle_exception(self, exc):
|
|
694
|
+
if isinstance(exc, NotFoundError):
|
|
695
|
+
print(t.red.bold(exc.title) + f': {exc.name}\n')
|
|
696
|
+
if exc.available:
|
|
697
|
+
print(t.green.bold('AVAILABLE') + ':')
|
|
698
|
+
display.print(exc.available)
|
|
699
|
+
return
|
|
700
|
+
elif exc.searched:
|
|
701
|
+
print(t.green.bold('SEARCHED') + ':')
|
|
702
|
+
display.print(exc.searched)
|
|
703
|
+
return
|
|
669
704
|
raise exc
|
|
670
705
|
|
|
671
706
|
async def async_call(self, *argv):
|
|
672
707
|
""" Call with async stuff in single event loop """
|
|
673
|
-
|
|
708
|
+
try:
|
|
709
|
+
error = self.parse(*argv)
|
|
710
|
+
except Cli2ValueError as exc:
|
|
711
|
+
return self.help(error=exc.args[0])
|
|
712
|
+
except Exception as exc:
|
|
713
|
+
return self.handle_exception(exc)
|
|
714
|
+
|
|
674
715
|
if error:
|
|
675
716
|
self.exit_code = 1
|
|
676
717
|
return self.help(error=error)
|
|
@@ -816,6 +857,9 @@ class Command(EntryPoint, dict):
|
|
|
816
857
|
"""
|
|
817
858
|
pass
|
|
818
859
|
|
|
860
|
+
def __repr__(self):
|
|
861
|
+
return f'{type(self).__name__}(name={self.name})'
|
|
862
|
+
|
|
819
863
|
|
|
820
864
|
class Argument:
|
|
821
865
|
"""
|
|
@@ -923,25 +967,20 @@ class Argument:
|
|
|
923
967
|
|
|
924
968
|
def __str__(self):
|
|
925
969
|
if self.alias:
|
|
926
|
-
out = '[' +
|
|
927
|
-
out += colors.reset
|
|
970
|
+
out = '[' + t.orange(self.alias[-1])
|
|
928
971
|
|
|
929
972
|
if self.type != bool:
|
|
930
|
-
out += '=' +
|
|
931
|
-
out += colors.reset
|
|
973
|
+
out += '=' + t.green(self.param.name.upper())
|
|
932
974
|
|
|
933
975
|
if self.negates:
|
|
934
|
-
out += '|' +
|
|
935
|
-
out += colors.reset
|
|
976
|
+
out += '|' + t.orange(self.negates[-1])
|
|
936
977
|
|
|
937
978
|
out += ']'
|
|
938
979
|
return out
|
|
939
980
|
elif self.param.kind == self.param.VAR_POSITIONAL:
|
|
940
981
|
return (
|
|
941
982
|
'['
|
|
942
|
-
+
|
|
943
|
-
+ self.param.name.upper()
|
|
944
|
-
+ colors.reset
|
|
983
|
+
+ t.green(self.param.name.upper())
|
|
945
984
|
+ ']...'
|
|
946
985
|
)
|
|
947
986
|
elif self.param.kind == self.param.VAR_KEYWORD:
|
|
@@ -949,27 +988,23 @@ class Argument:
|
|
|
949
988
|
return (
|
|
950
989
|
'['
|
|
951
990
|
+ prefix
|
|
952
|
-
+
|
|
953
|
-
+ self.param.name.upper()
|
|
954
|
-
+ colors.reset
|
|
991
|
+
+ t.green(self.param.name.upper())
|
|
955
992
|
+ '='
|
|
956
|
-
+
|
|
957
|
-
+ 'VALUE'
|
|
958
|
-
+ colors.reset
|
|
993
|
+
+ t.green('VALUE')
|
|
959
994
|
+ ']...'
|
|
960
995
|
)
|
|
961
996
|
else:
|
|
962
|
-
return
|
|
997
|
+
return t.green(self.param.name.upper())
|
|
963
998
|
|
|
964
999
|
def help(self):
|
|
965
1000
|
"""Render help for this argument."""
|
|
966
1001
|
if self.alias:
|
|
967
1002
|
out = ''
|
|
968
1003
|
for alias in self.alias:
|
|
969
|
-
out +=
|
|
1004
|
+
out += t.orange(alias)
|
|
970
1005
|
if self.type != bool:
|
|
971
1006
|
out += '='
|
|
972
|
-
out +=
|
|
1007
|
+
out += str(t.green)
|
|
973
1008
|
if self.type:
|
|
974
1009
|
if isinstance(self.type, str):
|
|
975
1010
|
out += self.type
|
|
@@ -977,17 +1012,16 @@ class Argument:
|
|
|
977
1012
|
out += self.type.__name__
|
|
978
1013
|
else:
|
|
979
1014
|
out += self.param.name.upper()
|
|
980
|
-
out +=
|
|
1015
|
+
out += str(t.reset)
|
|
981
1016
|
out += ' '
|
|
982
1017
|
self.cmd.print(out)
|
|
983
1018
|
else:
|
|
984
|
-
self.cmd.print(
|
|
1019
|
+
self.cmd.print(f'{self}{t.reset}')
|
|
985
1020
|
|
|
986
1021
|
if self.negates:
|
|
987
1022
|
out = ''
|
|
988
1023
|
for negate in self.negates:
|
|
989
|
-
out +=
|
|
990
|
-
out += colors.reset
|
|
1024
|
+
out += t.orange(negate)
|
|
991
1025
|
out += ' '
|
|
992
1026
|
self.cmd.print(out)
|
|
993
1027
|
|
|
@@ -997,9 +1031,7 @@ class Argument:
|
|
|
997
1031
|
):
|
|
998
1032
|
self.cmd.print(
|
|
999
1033
|
'Default: '
|
|
1000
|
-
+
|
|
1001
|
-
+ str(self.default or self.param.default)
|
|
1002
|
-
+ colors.reset
|
|
1034
|
+
+ t.cyan(self.default or self.param.default)
|
|
1003
1035
|
)
|
|
1004
1036
|
|
|
1005
1037
|
if (
|
|
@@ -1009,9 +1041,7 @@ class Argument:
|
|
|
1009
1041
|
):
|
|
1010
1042
|
self.cmd.print(
|
|
1011
1043
|
'Accepted: '
|
|
1012
|
-
+
|
|
1013
|
-
+ 'yes, 1, true, no, 0, false'
|
|
1014
|
-
+ colors.reset
|
|
1044
|
+
+ t.cyan('yes, 1, true, no, 0, false')
|
|
1015
1045
|
)
|
|
1016
1046
|
|
|
1017
1047
|
if self.param.kind == self.param.VAR_KEYWORD:
|
|
@@ -1019,12 +1049,9 @@ class Argument:
|
|
|
1019
1049
|
if self.cmd.posix:
|
|
1020
1050
|
self.cmd.print(
|
|
1021
1051
|
'--'
|
|
1022
|
-
+
|
|
1023
|
-
+ 'something'
|
|
1024
|
-
+ colors.reset
|
|
1052
|
+
+ t.green('something')
|
|
1025
1053
|
+ '='
|
|
1026
|
-
+
|
|
1027
|
-
+ 'somearg'
|
|
1054
|
+
+ t.green('somearg')
|
|
1028
1055
|
)
|
|
1029
1056
|
else:
|
|
1030
1057
|
self.cmd.print('something=somearg')
|
|
@@ -31,8 +31,6 @@ import shlex
|
|
|
31
31
|
import textwrap
|
|
32
32
|
from pathlib import Path
|
|
33
33
|
|
|
34
|
-
from .log import log
|
|
35
|
-
|
|
36
34
|
|
|
37
35
|
class Configuration(dict):
|
|
38
36
|
"""
|
|
@@ -168,7 +166,12 @@ class Configuration(dict):
|
|
|
168
166
|
|
|
169
167
|
if key in self.defaults:
|
|
170
168
|
value = self.defaults[key]
|
|
171
|
-
|
|
169
|
+
try:
|
|
170
|
+
from .log import log
|
|
171
|
+
except ImportError:
|
|
172
|
+
pass
|
|
173
|
+
else:
|
|
174
|
+
log.debug(f'Defaulting {key} to {value}')
|
|
172
175
|
return value
|
|
173
176
|
|
|
174
177
|
prompt = self.questions.get(key, key)
|
|
@@ -5,6 +5,10 @@ Generic pretty display utils.
|
|
|
5
5
|
|
|
6
6
|
By default, we will not color strings in non-interactive ttys, but you can
|
|
7
7
|
force it with :envvar:`FORCE_COLOR`, ie. gitlab-ci etc
|
|
8
|
+
|
|
9
|
+
.. envvar:: CLI2_PYGMENTS_STYLE
|
|
10
|
+
|
|
11
|
+
Pygments style to use when highlighting code, monokai by default.
|
|
8
12
|
"""
|
|
9
13
|
import difflib
|
|
10
14
|
import json
|
|
@@ -15,6 +19,12 @@ import yaml
|
|
|
15
19
|
_print = print
|
|
16
20
|
|
|
17
21
|
|
|
22
|
+
def color_enabled():
|
|
23
|
+
if 'FORCE_COLOR' in os.environ:
|
|
24
|
+
return bool(os.getenv('FORCE_COLOR', ''))
|
|
25
|
+
return sys.stdout.isatty()
|
|
26
|
+
|
|
27
|
+
|
|
18
28
|
def highlight(string, lexer):
|
|
19
29
|
"""
|
|
20
30
|
Use pygments to render a string with a lexer.
|
|
@@ -22,8 +32,7 @@ def highlight(string, lexer):
|
|
|
22
32
|
:param string: String to render
|
|
23
33
|
:param lexer: Lexer name, Yaml, Diff, etc
|
|
24
34
|
"""
|
|
25
|
-
|
|
26
|
-
if not sys.stdout.isatty() and not FORCE_COLOR:
|
|
35
|
+
if not color_enabled():
|
|
27
36
|
return string
|
|
28
37
|
|
|
29
38
|
try:
|
|
@@ -33,9 +42,11 @@ def highlight(string, lexer):
|
|
|
33
42
|
except ImportError:
|
|
34
43
|
return string
|
|
35
44
|
|
|
36
|
-
formatter = pygments.formatters.
|
|
45
|
+
formatter = pygments.formatters.Terminal256Formatter(
|
|
46
|
+
style=os.getenv('CLI2_PYGMENTS_STYLE', 'monokai'),
|
|
47
|
+
)
|
|
37
48
|
lexer = getattr(pygments.lexers, lexer + 'Lexer')()
|
|
38
|
-
return pygments.highlight(string, lexer, formatter)
|
|
49
|
+
return pygments.highlight(string, lexer, formatter).rstrip()
|
|
39
50
|
|
|
40
51
|
|
|
41
52
|
def yaml_dump(data):
|