From 1da038c1cf883e0eff03c19c23daa992447644a8 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Fri, 19 Jan 2018 13:00:42 +0000 Subject: [PATCH] CLI improvements - add `--log` - fix `--unit_scale` - make `--bytes` more intelligent (closes #503) - update tests & documentation --- tqdm.1 | 10 ++++++++- tqdm/_main.py | 45 +++++++++++++++++++++++++++++++++++++--- tqdm/tests/tests_main.py | 9 ++++---- 3 files changed, 56 insertions(+), 8 deletions(-) diff --git a/tqdm.1 b/tqdm.1 index 18484167..7da9a4b8 100644 --- a/tqdm.1 +++ b/tqdm.1 @@ -215,7 +215,15 @@ specified. .TP .B \-\-bytes=\f[I]bytes\f[] bool, optional. -If true, will count bytes and ignore \f[C]delim\f[]. +If true, will count bytes, ignore \f[C]delim\f[], and default +\f[C]unit_scale\f[] to True, \f[C]unit_divisor\f[] to 1024, and +\f[C]unit\f[] to \[aq]B\[aq]. +.RS +.RE +.TP +.B \-\-log=\f[I]log\f[] +str, optional. +CRITICAL|FATAL|ERROR|WARN(ING)|[default: \[aq]INFO\[aq]]|DEBUG|NOTSET. .RS .RE .SH AUTHORS diff --git a/tqdm/_main.py b/tqdm/_main.py index 3e9646b9..9c9273f6 100644 --- a/tqdm/_main.py +++ b/tqdm/_main.py @@ -2,10 +2,21 @@ from ._tqdm import tqdm, TqdmTypeError, TqdmKeyError from ._version import __version__ # NOQA import sys import re +import logging __all__ = ["main"] def cast(val, typ): + log = logging.getLogger(__name__) + log.debug((val, typ)) + if " or " in typ: + for t in typ.split(" or "): + try: + return cast(val, t) + except TqdmTypeError: + pass + raise TqdmTypeError(val + ' : ' + typ) + # sys.stderr.write('\ndebug | `val:type`: `' + val + ':' + typ + '`.\n') if typ == 'bool': if (val == 'True') or (val == ''): @@ -76,7 +87,7 @@ def posix_pipe(fin, fout, delim='\n', buf_size=256, # ((opt, type), ... ) -RE_OPTS = re.compile(r'\n {8}(\S+)\s{2,}:\s*([^\s,]+)') +RE_OPTS = re.compile(r'\n {8}(\S+)\s{2,}:\s*([^,]+)') # better split method assuming no positional args RE_SHLEX = re.compile(r'\s*--?([^\s=]+)(?:\s*|=|$)') @@ -87,6 +98,8 @@ UNSUPPORTED_OPTS = ('iterable', 'gui', 'out', 'file') CLI_EXTRA_DOC = r""" Extra CLI Options ----------------- + name : type, optional + TODO: find out why this is needed. delim : chr, optional Delimiting character [default: '\n']. Use '\0' for null. N.B.: on Windows systems, Python converts '\n' to '\r\n'. @@ -94,7 +107,10 @@ CLI_EXTRA_DOC = r""" String buffer size in bytes [default: 256] used when `delim` is specified. bytes : bool, optional - If true, will count bytes and ignore `delim`. + If true, will count bytes, ignore `delim`, and default + `unit_scale` to True, `unit_divisor` to 1024, and `unit` to 'B'. + log : str, optional + CRITICAL|FATAL|ERROR|WARN(ING)|[default: 'INFO']|DEBUG|NOTSET. """ @@ -104,13 +120,27 @@ def main(fp=sys.stderr): --------- fp : file-like object for tqdm """ + try: + log = sys.argv.index('--log') + except ValueError: + logLevel = 'INFO' + else: + # sys.argv.pop(log) + # logLevel = sys.argv.pop(log) + logLevel = sys.argv[log + 1] + logging.basicConfig(level=getattr(logging, logLevel)) + log = logging.getLogger(__name__) + d = tqdm.__init__.__doc__ + CLI_EXTRA_DOC opt_types = dict(RE_OPTS.findall(d)) + # opt_types['delim'] = 'chr' for o in UNSUPPORTED_OPTS: opt_types.pop(o) + log.debug(sorted(opt_types.items())) + # d = RE_OPTS.sub(r' --\1=<\1> : \2', d) split = RE_OPTS.split(d) opt_types_desc = zip(split[1::3], split[2::3], split[3::3]) @@ -137,6 +167,9 @@ Options: argv = RE_SHLEX.split(' '.join(["tqdm"] + sys.argv[1:])) opts = dict(zip(argv[1::2], argv[2::2])) + log.debug(opts) + opts.pop('log', True) + tqdm_args = {'file': fp} try: for (o, v) in opts.items(): @@ -144,7 +177,7 @@ Options: tqdm_args[o] = cast(v, opt_types[o]) except KeyError as e: raise TqdmKeyError(str(e)) - # fp.write('\ndebug | args: ' + str(tqdm_args) + '\n') + log.debug('args:' + str(tqdm_args)) except: fp.write('\nError:\nUsage:\n tqdm [--help | options]\n') for i in sys.stdin: @@ -155,13 +188,19 @@ Options: delim = tqdm_args.pop('delim', '\n') delim_per_char = tqdm_args.pop('bytes', False) if delim_per_char: + tqdm_args.setdefault('unit', 'B') + tqdm_args.setdefault('unit_scale', True) + tqdm_args.setdefault('unit_divisor', 1024) + log.debug(tqdm_args) with tqdm(**tqdm_args) as t: posix_pipe(sys.stdin, sys.stdout, '', buf_size, t.update) elif delim == '\n': + log.debug(tqdm_args) for i in tqdm(sys.stdin, **tqdm_args): sys.stdout.write(i) else: + log.debug(tqdm_args) with tqdm(**tqdm_args) as t: posix_pipe(sys.stdin, sys.stdout, delim, buf_size, t.update) diff --git a/tqdm/tests/tests_main.py b/tqdm/tests/tests_main.py index 32dc8205..0781c242 100644 --- a/tqdm/tests/tests_main.py +++ b/tqdm/tests/tests_main.py @@ -2,7 +2,8 @@ import sys import subprocess from tqdm import main, TqdmKeyError, TqdmTypeError -from tests_tqdm import with_setup, pretest, posttest, _range, closing, UnicodeIO +from tests_tqdm import with_setup, pretest, posttest, _range, closing, \ + UnicodeIO, StringIO def _sh(*cmd, **kwargs): @@ -28,7 +29,7 @@ def test_main(): # semi-fake test which gets coverage: _SYS = sys.stdin, sys.argv - with closing(UnicodeIO()) as sys.stdin: + with closing(StringIO()) as sys.stdin: sys.argv = ['', '--desc', 'Test CLI delims', '--ascii', 'True', '--delim', r'\0', '--buf_size', '64'] sys.stdin.write('\0'.join(map(str, _range(int(1e3))))) @@ -42,10 +43,10 @@ def test_main(): import tqdm.__main__ # NOQA IN_DATA = '\0'.join(IN_DATA_LIST) - with closing(UnicodeIO()) as sys.stdin: + with closing(StringIO()) as sys.stdin: sys.stdin.write(IN_DATA) sys.stdin.seek(0) - sys.argv = ['', '--ascii', '--bytes'] + sys.argv = ['', '--ascii', '--bytes', '--unit_scale', 'False'] with closing(UnicodeIO()) as fp: main(fp=fp) assert (str(len(IN_DATA)) in fp.getvalue())