From 52d78121b1c6499b1a477c8f1f3a69cd896c6c55 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Sat, 5 Aug 2023 22:14:43 +0100 Subject: [PATCH 01/11] env var override support - fixes #370, fixes #612, fixes #619, fixes #1318 - closes #950, closes #1061 --- tqdm/std.py | 234 +++++++++++++++++++++++++------------------------- tqdm/utils.py | 58 ++++++++++++- 2 files changed, 175 insertions(+), 117 deletions(-) diff --git a/tqdm/std.py b/tqdm/std.py index 1163137e9..fa9777a7e 100644 --- a/tqdm/std.py +++ b/tqdm/std.py @@ -19,7 +19,8 @@ from ._monitor import TMonitor from .utils import ( CallbackIOWrapper, Comparable, DisableOnWriteError, FormatReplace, SimpleTextIOWrapper, - _is_ascii, _screen_shape_wrapper, _supports_unicode, _term_move_up, disp_len, disp_trim) + _is_ascii, _screen_shape_wrapper, _supports_unicode, _term_move_up, disp_len, disp_trim, + envwrap) __author__ = "https://github.com/tqdm/tqdm#contributions" __all__ = ['tqdm', 'trange', @@ -246,6 +247,120 @@ class tqdm(Comparable): Decorate an iterable object, returning an iterator which acts exactly like the original iterable, but prints a dynamically updating progressbar every time a value is requested. + + Parameters + ---------- + iterable : iterable, optional + Iterable to decorate with a progressbar. + Leave blank to manually manage the updates. + desc : str, optional + Prefix for the progressbar. + total : int or float, optional + The number of expected iterations. If unspecified, + len(iterable) is used if possible. If float("inf") or as a last + resort, only basic progress statistics are displayed + (no ETA, no progressbar). + If `gui` is True and this parameter needs subsequent updating, + specify an initial arbitrary large positive number, + e.g. 9e9. + leave : bool, optional + If [default: True], keeps all traces of the progressbar + upon termination of iteration. + If `None`, will leave only if `position` is `0`. + file : `io.TextIOWrapper` or `io.StringIO`, optional + Specifies where to output the progress messages + (default: sys.stderr). Uses `file.write(str)` and `file.flush()` + methods. For encoding, see `write_bytes`. + ncols : int, optional + The width of the entire output message. If specified, + dynamically resizes the progressbar to stay within this bound. + If unspecified, attempts to use environment width. The + fallback is a meter width of 10 and no limit for the counter and + statistics. If 0, will not print any meter (only stats). + mininterval : float, optional + Minimum progress display update interval [default: 0.1] seconds. + maxinterval : float, optional + Maximum progress display update interval [default: 10] seconds. + Automatically adjusts `miniters` to correspond to `mininterval` + after long display update lag. Only works if `dynamic_miniters` + or monitor thread is enabled. + miniters : int or float, optional + Minimum progress display update interval, in iterations. + If 0 and `dynamic_miniters`, will automatically adjust to equal + `mininterval` (more CPU efficient, good for tight loops). + If > 0, will skip display of specified number of iterations. + Tweak this and `mininterval` to get very efficient loops. + If your progress is erratic with both fast and slow iterations + (network, skipping items, etc) you should set miniters=1. + ascii : bool or str, optional + If unspecified or False, use unicode (smooth blocks) to fill + the meter. The fallback is to use ASCII characters " 123456789#". + disable : bool, optional + Whether to disable the entire progressbar wrapper + [default: False]. If set to None, disable on non-TTY. + unit : str, optional + String that will be used to define the unit of each iteration + [default: it]. + unit_scale : bool or int or float, optional + If 1 or True, the number of iterations will be reduced/scaled + automatically and a metric prefix following the + International System of Units standard will be added + (kilo, mega, etc.) [default: False]. If any other non-zero + number, will scale `total` and `n`. + dynamic_ncols : bool, optional + If set, constantly alters `ncols` and `nrows` to the + environment (allowing for window resizes) [default: False]. + smoothing : float, optional + Exponential moving average smoothing factor for speed estimates + (ignored in GUI mode). Ranges from 0 (average speed) to 1 + (current/instantaneous speed) [default: 0.3]. + bar_format : str, optional + Specify a custom bar string formatting. May impact performance. + [default: '{l_bar}{bar}{r_bar}'], where + l_bar='{desc}: {percentage:3.0f}%|' and + r_bar='| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, ' + '{rate_fmt}{postfix}]' + Possible vars: l_bar, bar, r_bar, n, n_fmt, total, total_fmt, + percentage, elapsed, elapsed_s, ncols, nrows, desc, unit, + rate, rate_fmt, rate_noinv, rate_noinv_fmt, + rate_inv, rate_inv_fmt, postfix, unit_divisor, + remaining, remaining_s, eta. + Note that a trailing ": " is automatically removed after {desc} + if the latter is empty. + initial : int or float, optional + The initial counter value. Useful when restarting a progress + bar [default: 0]. If using float, consider specifying `{n:.3f}` + or similar in `bar_format`, or specifying `unit_scale`. + position : int, optional + Specify the line offset to print this bar (starting from 0) + Automatic if unspecified. + Useful to manage multiple bars at once (eg, from threads). + postfix : dict or *, optional + Specify additional stats to display at the end of the bar. + Calls `set_postfix(**postfix)` if possible (dict). + unit_divisor : float, optional + [default: 1000], ignored unless `unit_scale` is True. + write_bytes : bool, optional + Whether to write bytes. If (default: False) will write unicode. + lock_args : tuple, optional + Passed to `refresh` for intermediate output + (initialisation, iterating, and updating). + nrows : int, optional + The screen height. If specified, hides nested bars outside this + bound. If unspecified, attempts to use environment height. + The fallback is 20. + colour : str, optional + Bar colour (e.g. 'green', '#00ff00'). + delay : float, optional + Don't display until [default: 0] seconds have elapsed. + gui : bool, optional + WARNING: internal parameter - do not use. + Use tqdm.gui.tqdm(...) instead. If set, will attempt to use + matplotlib animations for a graphical output [default: False]. + + Returns + ------- + out : decorated iterator. """ monitor_interval = 10 # set to 0 to disable the thread @@ -834,6 +949,7 @@ def wrapper(*args, **kwargs): elif _Rolling_and_Expanding is not None: _Rolling_and_Expanding.progress_apply = inner_generator() + @envwrap("TQDM_", is_method=True) def __init__(self, iterable=None, desc=None, total=None, leave=True, file=None, ncols=None, mininterval=0.1, maxinterval=10.0, miniters=None, ascii=None, disable=False, unit='it', unit_scale=False, @@ -841,121 +957,7 @@ def __init__(self, iterable=None, desc=None, total=None, leave=True, file=None, position=None, postfix=None, unit_divisor=1000, write_bytes=False, lock_args=None, nrows=None, colour=None, delay=0, gui=False, **kwargs): - """ - Parameters - ---------- - iterable : iterable, optional - Iterable to decorate with a progressbar. - Leave blank to manually manage the updates. - desc : str, optional - Prefix for the progressbar. - total : int or float, optional - The number of expected iterations. If unspecified, - len(iterable) is used if possible. If float("inf") or as a last - resort, only basic progress statistics are displayed - (no ETA, no progressbar). - If `gui` is True and this parameter needs subsequent updating, - specify an initial arbitrary large positive number, - e.g. 9e9. - leave : bool, optional - If [default: True], keeps all traces of the progressbar - upon termination of iteration. - If `None`, will leave only if `position` is `0`. - file : `io.TextIOWrapper` or `io.StringIO`, optional - Specifies where to output the progress messages - (default: sys.stderr). Uses `file.write(str)` and `file.flush()` - methods. For encoding, see `write_bytes`. - ncols : int, optional - The width of the entire output message. If specified, - dynamically resizes the progressbar to stay within this bound. - If unspecified, attempts to use environment width. The - fallback is a meter width of 10 and no limit for the counter and - statistics. If 0, will not print any meter (only stats). - mininterval : float, optional - Minimum progress display update interval [default: 0.1] seconds. - maxinterval : float, optional - Maximum progress display update interval [default: 10] seconds. - Automatically adjusts `miniters` to correspond to `mininterval` - after long display update lag. Only works if `dynamic_miniters` - or monitor thread is enabled. - miniters : int or float, optional - Minimum progress display update interval, in iterations. - If 0 and `dynamic_miniters`, will automatically adjust to equal - `mininterval` (more CPU efficient, good for tight loops). - If > 0, will skip display of specified number of iterations. - Tweak this and `mininterval` to get very efficient loops. - If your progress is erratic with both fast and slow iterations - (network, skipping items, etc) you should set miniters=1. - ascii : bool or str, optional - If unspecified or False, use unicode (smooth blocks) to fill - the meter. The fallback is to use ASCII characters " 123456789#". - disable : bool, optional - Whether to disable the entire progressbar wrapper - [default: False]. If set to None, disable on non-TTY. - unit : str, optional - String that will be used to define the unit of each iteration - [default: it]. - unit_scale : bool or int or float, optional - If 1 or True, the number of iterations will be reduced/scaled - automatically and a metric prefix following the - International System of Units standard will be added - (kilo, mega, etc.) [default: False]. If any other non-zero - number, will scale `total` and `n`. - dynamic_ncols : bool, optional - If set, constantly alters `ncols` and `nrows` to the - environment (allowing for window resizes) [default: False]. - smoothing : float, optional - Exponential moving average smoothing factor for speed estimates - (ignored in GUI mode). Ranges from 0 (average speed) to 1 - (current/instantaneous speed) [default: 0.3]. - bar_format : str, optional - Specify a custom bar string formatting. May impact performance. - [default: '{l_bar}{bar}{r_bar}'], where - l_bar='{desc}: {percentage:3.0f}%|' and - r_bar='| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, ' - '{rate_fmt}{postfix}]' - Possible vars: l_bar, bar, r_bar, n, n_fmt, total, total_fmt, - percentage, elapsed, elapsed_s, ncols, nrows, desc, unit, - rate, rate_fmt, rate_noinv, rate_noinv_fmt, - rate_inv, rate_inv_fmt, postfix, unit_divisor, - remaining, remaining_s, eta. - Note that a trailing ": " is automatically removed after {desc} - if the latter is empty. - initial : int or float, optional - The initial counter value. Useful when restarting a progress - bar [default: 0]. If using float, consider specifying `{n:.3f}` - or similar in `bar_format`, or specifying `unit_scale`. - position : int, optional - Specify the line offset to print this bar (starting from 0) - Automatic if unspecified. - Useful to manage multiple bars at once (eg, from threads). - postfix : dict or *, optional - Specify additional stats to display at the end of the bar. - Calls `set_postfix(**postfix)` if possible (dict). - unit_divisor : float, optional - [default: 1000], ignored unless `unit_scale` is True. - write_bytes : bool, optional - Whether to write bytes. If (default: False) will write unicode. - lock_args : tuple, optional - Passed to `refresh` for intermediate output - (initialisation, iterating, and updating). - nrows : int, optional - The screen height. If specified, hides nested bars outside this - bound. If unspecified, attempts to use environment height. - The fallback is 20. - colour : str, optional - Bar colour (e.g. 'green', '#00ff00'). - delay : float, optional - Don't display until [default: 0] seconds have elapsed. - gui : bool, optional - WARNING: internal parameter - do not use. - Use tqdm.gui.tqdm(...) instead. If set, will attempt to use - matplotlib animations for a graphical output [default: False]. - - Returns - ------- - out : decorated iterator. - """ + """see tqdm.tqdm for arguments""" if file is None: file = sys.stderr diff --git a/tqdm/utils.py b/tqdm/utils.py index c5d3a6e32..93f635ad2 100644 --- a/tqdm/utils.py +++ b/tqdm/utils.py @@ -4,7 +4,9 @@ import os import re import sys -from functools import wraps +from ast import literal_eval as safe_eval +from functools import partial, partialmethod, wraps +from inspect import signature # TODO consider using wcswidth third-party package for 0-width characters from unicodedata import east_asian_width from warnings import warn @@ -30,6 +32,60 @@ colorama.init() +def envwrap(prefix, case_sensitive=False, literal_eval=False, is_method=False): + """ + Override parameter defaults via `os.environ[prefix + param_name]`. + Precedence (highest first): + - call (`foo(a=3)`) + - environ (`FOO_A=2`) + - signature (`def foo(a=1)`) + + Parameters + ---------- + prefix : str + Env var prefix, e.g. "FOO_" + case_sensitive : bool, optional + If (default: False), treat env var "FOO_Some_ARG" as "FOO_some_arg". + literal_eval : bool, optional + Whether to `ast.literal_eval` the detected env var overrides. + Otherwise if (default: False), infer types from function signature. + is_method : bool, optional + Whether to use `functools.partialmethod`. If (default: False) use `functools.partial`. + + Examples: + ``` + $ cat foo.py + from tqdm.utils import envwrap + @envwrap("FOO_") + def test(a=1, b=2, c=3): + print(f"received: a={a}, b={b}, c={c}") + + $ FOO_A=42 FOO_C=1337 python -c 'import foo; foo.test(c=99)' + received: a=42, b=2, c=99 + ``` + """ + i = len(prefix) + overrides = {k[i:] if case_sensitive else k[i:].lower(): v + for k, v in os.environ.items() if k.startswith(prefix)} + part = partialmethod if is_method else partial + + def wrap(func): + if literal_eval: + return part(func, **{k: safe_eval(v) for k, v in overrides.items()}) + # use `func` signature to infer env override `type` (fallback to `str`) + params = signature(func).parameters + for k in overrides: + param = params[k] + if param.annotation is not param.empty: + typ = param.annotation + # TODO: parse type in {Union, Any, Optional, ...} + else: + typ = str if param.default is None else type(param.default) + overrides[k] = typ(overrides[k]) + return part(func, **overrides) + return wrap + + class FormatReplace(object): """ >>> a = FormatReplace('something') From 783bd6e07009bfe873701abd96671df561bdbba2 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Sat, 5 Aug 2023 22:22:32 +0100 Subject: [PATCH 02/11] ignore unknown env overrides --- tqdm/utils.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tqdm/utils.py b/tqdm/utils.py index 93f635ad2..b2ab9738b 100644 --- a/tqdm/utils.py +++ b/tqdm/utils.py @@ -75,13 +75,14 @@ def wrap(func): # use `func` signature to infer env override `type` (fallback to `str`) params = signature(func).parameters for k in overrides: - param = params[k] - if param.annotation is not param.empty: - typ = param.annotation - # TODO: parse type in {Union, Any, Optional, ...} - else: - typ = str if param.default is None else type(param.default) - overrides[k] = typ(overrides[k]) + param = params.get(k, None) + if param is not None: + if param.annotation is not param.empty: + typ = param.annotation + # TODO: parse type in {Union, Any, Optional, ...} + else: + typ = str if param.default is None else type(param.default) + overrides[k] = typ(overrides[k]) return part(func, **overrides) return wrap From 17482374a5dbcab53ce3696a462e6c659314cd92 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Sat, 5 Aug 2023 22:15:53 +0100 Subject: [PATCH 03/11] cli: fix & update completion --- .meta/mkcompletion.py | 13 ++++----- tqdm/cli.py | 66 +++++++++++++++++++++---------------------- tqdm/tqdm.1 | 6 ++-- 3 files changed, 40 insertions(+), 45 deletions(-) diff --git a/.meta/mkcompletion.py b/.meta/mkcompletion.py index 387bd3075..130c2f803 100644 --- a/.meta/mkcompletion.py +++ b/.meta/mkcompletion.py @@ -3,10 +3,9 @@ """ import re import sys -from io import open as io_open -from os import path +from pathlib import Path -sys.path.insert(0, path.dirname(path.dirname(__file__))) +sys.path.insert(0, str(Path(__file__).parent.parent)) import tqdm # NOQA import tqdm.cli # NOQA @@ -27,13 +26,12 @@ def doc2opt(doc, user_input=True): # CLI options options = {'-h', '--help', '-v', '--version'} options_input = set() -for doc in (tqdm.tqdm.__init__.__doc__, tqdm.cli.CLI_EXTRA_DOC): +for doc in (tqdm.tqdm.__doc__, tqdm.cli.CLI_EXTRA_DOC): options.update(doc2opt(doc, user_input=False)) options_input.update(doc2opt(doc, user_input=True)) options.difference_update('--' + i for i in ('name',) + tqdm.cli.UNSUPPORTED_OPTS) options_input &= options options_input -= {"--log"} # manually dealt with -src_dir = path.abspath(path.dirname(__file__)) completion = u"""\ #!/usr/bin/env bash _tqdm(){{ @@ -58,6 +56,5 @@ def doc2opt(doc, user_input=True): """.format(opts=' '.join(sorted(options)), opts_manual='|'.join(sorted(options_input))) if __name__ == "__main__": - fncompletion = path.join(path.dirname(src_dir), 'tqdm', 'completion.sh') - with io_open(fncompletion, mode='w', encoding='utf-8') as fd: - fd.write(completion) + (Path(__file__).resolve().parent.parent / 'tqdm' / 'completion.sh').write_text( + completion, encoding='utf-8') diff --git a/tqdm/cli.py b/tqdm/cli.py index 444262831..1223d4977 100644 --- a/tqdm/cli.py +++ b/tqdm/cli.py @@ -98,7 +98,7 @@ def posix_pipe(fin, fout, delim=b'\\n', buf_size=256, # ((opt, type), ... ) -RE_OPTS = re.compile(r'\n {8}(\S+)\s{2,}:\s*([^,]+)') +RE_OPTS = re.compile(r'\n {4}(\S+)\s{2,}:\s*([^,]+)') # better split method assuming no positional args RE_SHLEX = re.compile(r'\s*(? Date: Sat, 5 Aug 2023 22:56:07 +0100 Subject: [PATCH 04/11] fix & update API docs --- .meta/.readme.rst | 4 +++- .meta/mkdocs.py | 23 ++++++++++------------- README.rst | 4 +++- tqdm/std.py | 2 +- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/.meta/.readme.rst b/.meta/.readme.rst index 1dc2bbb92..d5ab4012c 100644 --- a/.meta/.readme.rst +++ b/.meta/.readme.rst @@ -345,12 +345,14 @@ Documentation class tqdm(): """{DOC_tqdm}""" + @envwrap("TQDM_", is_method=True) # override defaults via env vars def __init__(self, iterable=None, desc=None, total=None, leave=True, file=None, ncols=None, mininterval=0.1, maxinterval=10.0, miniters=None, ascii=None, disable=False, unit='it', unit_scale=False, dynamic_ncols=False, smoothing=0.3, bar_format=None, initial=0, position=None, - postfix=None, unit_divisor=1000): + postfix=None, unit_divisor=1000, write_bytes=False, + lock_args=None, nrows=None, colour=None, delay=0): Parameters ~~~~~~~~~~ diff --git a/.meta/mkdocs.py b/.meta/mkdocs.py index 6fdc0bad0..4c0d6ba20 100644 --- a/.meta/mkdocs.py +++ b/.meta/mkdocs.py @@ -2,11 +2,10 @@ Auto-generate README.rst from .meta/.readme.rst and docstrings. """ import sys -from io import open as io_open -from os import path +from pathlib import Path from textwrap import dedent -sys.path.insert(0, path.dirname(path.dirname(__file__))) +sys.path.insert(0, str(Path(__file__).parent.parent)) import tqdm # NOQA import tqdm.cli # NOQA @@ -42,13 +41,13 @@ def doc2rst(doc, arglist=True, raw=False): return doc -src_dir = path.abspath(path.dirname(__file__)) -README_rst = path.join(src_dir, '.readme.rst') -with io_open(README_rst, mode='r', encoding='utf-8') as fd: - README_rst = fd.read() -DOC_tqdm = doc2rst(tqdm.tqdm.__doc__, False).replace('\n', '\n ') -DOC_tqdm_init = doc2rst(tqdm.tqdm.__init__.__doc__) -DOC_tqdm_init_args = DOC_tqdm_init.partition(doc2rst(HEAD_ARGS))[-1].replace('\n ', '\n ') +src_dir = Path(__file__).parent.resolve() +README_rst = (src_dir / '.readme.rst').read_text("utf-8") +class_doc, init_doc = tqdm.tqdm.__doc__.split('\n\n', 1) +DOC_tqdm = doc2rst(class_doc + '\n', False).replace('\n', '\n ') +DOC_tqdm_init = doc2rst('\n' + init_doc) +DOC_tqdm_init_args = DOC_tqdm_init.partition(doc2rst(HEAD_ARGS))[-1].replace( + '\n ', '\n ').replace('\n ', '\n ') DOC_tqdm_init_args, _, DOC_tqdm_init_rets = DOC_tqdm_init_args.partition(doc2rst(HEAD_RETS)) DOC_cli = doc2rst(tqdm.cli.CLI_EXTRA_DOC).partition(doc2rst(HEAD_CLI))[-1] DOC_tqdm_tqdm = {} @@ -70,6 +69,4 @@ def doc2rst(doc, arglist=True, raw=False): README_rst = README_rst.replace('{DOC_tqdm.tqdm.%s}' % k, v) if __name__ == "__main__": - fndoc = path.join(path.dirname(src_dir), 'README.rst') - with io_open(fndoc, mode='w', encoding='utf-8') as fd: - fd.write(README_rst) + (src_dir.parent / 'README.rst').write_text(README_rst, encoding='utf-8') diff --git a/README.rst b/README.rst index d5588e917..87288a5a2 100644 --- a/README.rst +++ b/README.rst @@ -349,12 +349,14 @@ Documentation progressbar every time a value is requested. """ + @envwrap("TQDM_", is_method=True) # override defaults via env vars def __init__(self, iterable=None, desc=None, total=None, leave=True, file=None, ncols=None, mininterval=0.1, maxinterval=10.0, miniters=None, ascii=None, disable=False, unit='it', unit_scale=False, dynamic_ncols=False, smoothing=0.3, bar_format=None, initial=0, position=None, - postfix=None, unit_divisor=1000): + postfix=None, unit_divisor=1000, write_bytes=False, + lock_args=None, nrows=None, colour=None, delay=0): Parameters ~~~~~~~~~~ diff --git a/tqdm/std.py b/tqdm/std.py index fa9777a7e..218a561f6 100644 --- a/tqdm/std.py +++ b/tqdm/std.py @@ -949,7 +949,7 @@ def wrapper(*args, **kwargs): elif _Rolling_and_Expanding is not None: _Rolling_and_Expanding.progress_apply = inner_generator() - @envwrap("TQDM_", is_method=True) + @envwrap("TQDM_", is_method=True) # override defaults via env vars def __init__(self, iterable=None, desc=None, total=None, leave=True, file=None, ncols=None, mininterval=0.1, maxinterval=10.0, miniters=None, ascii=None, disable=False, unit='it', unit_scale=False, From 7faf97e97842f4f9e5fb1a069c24e9db0063b3f5 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Tue, 8 Aug 2023 02:03:30 +0100 Subject: [PATCH 05/11] replace os.path => pathlib.Path --- .meta/mksnap.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/.meta/mksnap.py b/.meta/mksnap.py index 5a65d3b7a..29f15acd7 100644 --- a/.meta/mksnap.py +++ b/.meta/mksnap.py @@ -3,14 +3,12 @@ Auto-generate snapcraft.yaml. """ import sys -from io import open as io_open -from os import path +from pathlib import Path from subprocess import check_output # nosec -sys.path.insert(1, path.dirname(path.dirname(__file__))) +sys.path.insert(1, str(Path(__file__).parent.parent)) import tqdm # NOQA -src_dir = path.abspath(path.dirname(__file__)) snap_yml = r"""name: tqdm summary: A fast, extensible CLI progress bar description: | @@ -65,9 +63,8 @@ command: bin/tqdm completer: completion.sh """.format(version=tqdm.__version__, commit=check_output([ - 'git', 'describe', '--always']).decode('U8').strip()) # nosec -fname = path.join(path.dirname(src_dir), 'snapcraft.yaml') + 'git', 'describe', '--always']).decode('utf-8').strip()) # nosec if __name__ == "__main__": - with io_open(fname, mode='w', encoding='utf-8') as fd: - fd.write(snap_yml.decode('U8') if hasattr(snap_yml, 'decode') else snap_yml) + (Path(__file__).resolve().parent.parent / 'snapcraft.yaml').write_text( + snap_yml.decode('utf-8') if hasattr(snap_yml, 'decode') else snap_yml, encoding='utf-8') From 38593d8b004ba51c772015d0594b1c706eac86ce Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Tue, 8 Aug 2023 11:12:32 +0100 Subject: [PATCH 06/11] ci: release with bot account again - thanks to https://github.com/cli/cli/issues/6680 --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9aae1d14a..56b122907 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -153,7 +153,7 @@ jobs: tag="${GITHUB_REF#refs/tags/}" gh release create --title "tqdm $tag stable" --draft --notes "$changelog" "$tag" dist/${{ steps.dist.outputs.whl }} dist/${{ steps.dist.outputs.whl_asc }} env: - GH_TOKEN: ${{ github.token }} + GH_TOKEN: ${{ secrets.GH_TOKEN }} - uses: snapcore/action-build@v1 id: snap_build - if: github.event_name == 'push' && steps.collect_assets.outputs.snap_channel From 6feef44607c8baf850712d6d5c54797bbcdee9d5 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Tue, 8 Aug 2023 12:12:57 +0100 Subject: [PATCH 07/11] docs: fix image hosting --- .meta/.readme.rst | 20 ++++++++++---------- DEMO.ipynb | 8 ++++---- README.rst | 20 ++++++++++---------- tqdm/contrib/discord.py | 2 +- tqdm/contrib/slack.py | 2 +- tqdm/contrib/telegram.py | 2 +- 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/.meta/.readme.rst b/.meta/.readme.rst index d5ab4012c..1d3fa6aee 100644 --- a/.meta/.readme.rst +++ b/.meta/.readme.rst @@ -1214,13 +1214,13 @@ Citation information: |DOI| |README-Hits| (Since 19 May 2016) -.. |Logo| image:: https://img.tqdm.ml/logo.gif -.. |Screenshot| image:: https://img.tqdm.ml/tqdm.gif -.. |Video| image:: https://img.tqdm.ml/video.jpg +.. |Logo| image:: https://tqdm.github.io/img/logo.gif +.. |Screenshot| image:: https://tqdm.github.io/img/tqdm.gif +.. |Video| image:: https://tqdm.github.io/img/video.jpg :target: https://tqdm.github.io/video -.. |Slides| image:: https://img.tqdm.ml/slides.jpg +.. |Slides| image:: https://tqdm.github.io/img/slides.jpg :target: https://tqdm.github.io/PyData2019/slides.html -.. |Merch| image:: https://img.tqdm.ml/merch.jpg +.. |Merch| image:: https://tqdm.github.io/img/merch.jpg :target: https://tqdm.github.io/merch .. |Build-Status| image:: https://img.shields.io/github/actions/workflow/status/tqdm/tqdm/test.yml?branch=master&label=tqdm&logo=GitHub :target: https://github.com/tqdm/tqdm/actions/workflows/test.yml @@ -1276,8 +1276,8 @@ Citation information: |DOI| :target: https://doi.org/10.5281/zenodo.595120 .. |binder-demo| image:: https://mybinder.org/badge_logo.svg :target: https://mybinder.org/v2/gh/tqdm/tqdm/master?filepath=DEMO.ipynb -.. |Screenshot-Jupyter1| image:: https://img.tqdm.ml/jupyter-1.gif -.. |Screenshot-Jupyter2| image:: https://img.tqdm.ml/jupyter-2.gif -.. |Screenshot-Jupyter3| image:: https://img.tqdm.ml/jupyter-3.gif -.. |README-Hits| image:: https://caspersci.uk.to/cgi-bin/hits.cgi?q=tqdm&style=social&r=https://github.com/tqdm/tqdm&l=https://img.tqdm.ml/favicon.png&f=https://img.tqdm.ml/logo.gif - :target: https://caspersci.uk.to/cgi-bin/hits.cgi?q=tqdm&a=plot&r=https://github.com/tqdm/tqdm&l=https://img.tqdm.ml/favicon.png&f=https://img.tqdm.ml/logo.gif&style=social +.. |Screenshot-Jupyter1| image:: https://tqdm.github.io/img/jupyter-1.gif +.. |Screenshot-Jupyter2| image:: https://tqdm.github.io/img/jupyter-2.gif +.. |Screenshot-Jupyter3| image:: https://tqdm.github.io/img/jupyter-3.gif +.. |README-Hits| image:: https://caspersci.uk.to/cgi-bin/hits.cgi?q=tqdm&style=social&r=https://github.com/tqdm/tqdm&l=https://tqdm.github.io/img/favicon.png&f=https://tqdm.github.io/img/logo.gif + :target: https://caspersci.uk.to/cgi-bin/hits.cgi?q=tqdm&a=plot&r=https://github.com/tqdm/tqdm&l=https://tqdm.github.io/img/favicon.png&f=https://tqdm.github.io/img/logo.gif&style=social diff --git a/DEMO.ipynb b/DEMO.ipynb index 3dabe4507..8048cbc49 100644 --- a/DEMO.ipynb +++ b/DEMO.ipynb @@ -5,7 +5,7 @@ "metadata": {}, "source": [ "

tqdm

\n", - "\n", + "\n", "\n", "[![Py-Versions](https://img.shields.io/pypi/pyversions/tqdm.svg?logo=python&logoColor=white)](https://pypi.org/project/tqdm)|[![Versions](https://img.shields.io/pypi/v/tqdm.svg)](https://tqdm.github.io/releases)|[![Conda-Forge-Status](https://img.shields.io/conda/v/conda-forge/tqdm.svg?label=conda-forge&logo=conda-forge)](https://anaconda.org/conda-forge/tqdm)|[![Docker](https://img.shields.io/badge/docker-pull-blue.svg?logo=docker&logoColor=white)](https://hub.docker.com/r/tqdm/tqdm)|[![Snapcraft](https://img.shields.io/badge/snap-install-82BEA0.svg?logo=snapcraft)](https://snapcraft.io/tqdm)\n", "-|-|-|-|-\n", @@ -58,7 +58,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "![Screenshot](https://img.tqdm.ml/tqdm.gif)|[![Video](https://img.tqdm.ml/video.jpg)](https://tqdm.github.io/video) [![Slides](https://img.tqdm.ml/slides.jpg)](https://tqdm.github.io/PyData2019/slides.html) [![Merch](https://img.tqdm.ml/merch.jpg)](https://tqdm.github.io/merch)\n", + "![Screenshot](https://tqdm.github.io/img/tqdm.gif)|[![Video](https://tqdm.github.io/img/video.jpg)](https://tqdm.github.io/video) [![Slides](https://tqdm.github.io/img/slides.jpg)](https://tqdm.github.io/PyData2019/slides.html) [![Merch](https://tqdm.github.io/img/merch.jpg)](https://tqdm.github.io/merch)\n", "-|-\n", "\n", "It can also be executed as a module with pipes:" @@ -737,7 +737,7 @@ "bars and colour hints (blue: normal, green: completed, red:\n", "error/interrupt, light blue: no ETA); as demonstrated below.\n", "\n", - "![Screenshot-Jupyter3](https://img.tqdm.ml/jupyter-3.gif)\n", + "![Screenshot-Jupyter3](https://tqdm.github.io/img/jupyter-3.gif)\n", "\n", "The `notebook` version supports percentage or pixels for overall width\n", "(e.g.: `ncols='100%'` or `ncols='480px'`).\n", @@ -843,7 +843,7 @@ "specify any file-like object using the `file` argument. For example,\n", "this can be used to redirect the messages writing to a log file or class.\n", "\n", - "[![README-Hits](https://caspersci.uk.to/cgi-bin/hits.cgi?q=tqdm&style=social&r=https://github.com/tqdm/tqdm&l=https://img.tqdm.ml/favicon.png&f=https://img.tqdm.ml/logo.gif)](https://caspersci.uk.to/cgi-bin/hits.cgi?q=tqdm&a=plot&r=https://github.com/tqdm/tqdm&l=https://img.tqdm.ml/favicon.png&f=https://img.tqdm.ml/logo.gif&style=social)|(Since 19 May 2016)\n", + "[![README-Hits](https://caspersci.uk.to/cgi-bin/hits.cgi?q=tqdm&style=social&r=https://github.com/tqdm/tqdm&l=https://tqdm.github.io/img/favicon.png&f=https://tqdm.github.io/img/logo.gif)](https://caspersci.uk.to/cgi-bin/hits.cgi?q=tqdm&a=plot&r=https://github.com/tqdm/tqdm&l=https://tqdm.github.io/img/favicon.png&f=https://tqdm.github.io/img/logo.gif&style=social)|(Since 19 May 2016)\n", "-|-" ] }, diff --git a/README.rst b/README.rst index 87288a5a2..fe582ce9c 100644 --- a/README.rst +++ b/README.rst @@ -1431,13 +1431,13 @@ Citation information: |DOI| |README-Hits| (Since 19 May 2016) -.. |Logo| image:: https://img.tqdm.ml/logo.gif -.. |Screenshot| image:: https://img.tqdm.ml/tqdm.gif -.. |Video| image:: https://img.tqdm.ml/video.jpg +.. |Logo| image:: https://tqdm.github.io/img/logo.gif +.. |Screenshot| image:: https://tqdm.github.io/img/tqdm.gif +.. |Video| image:: https://tqdm.github.io/img/video.jpg :target: https://tqdm.github.io/video -.. |Slides| image:: https://img.tqdm.ml/slides.jpg +.. |Slides| image:: https://tqdm.github.io/img/slides.jpg :target: https://tqdm.github.io/PyData2019/slides.html -.. |Merch| image:: https://img.tqdm.ml/merch.jpg +.. |Merch| image:: https://tqdm.github.io/img/merch.jpg :target: https://tqdm.github.io/merch .. |Build-Status| image:: https://img.shields.io/github/actions/workflow/status/tqdm/tqdm/test.yml?branch=master&label=tqdm&logo=GitHub :target: https://github.com/tqdm/tqdm/actions/workflows/test.yml @@ -1493,8 +1493,8 @@ Citation information: |DOI| :target: https://doi.org/10.5281/zenodo.595120 .. |binder-demo| image:: https://mybinder.org/badge_logo.svg :target: https://mybinder.org/v2/gh/tqdm/tqdm/master?filepath=DEMO.ipynb -.. |Screenshot-Jupyter1| image:: https://img.tqdm.ml/jupyter-1.gif -.. |Screenshot-Jupyter2| image:: https://img.tqdm.ml/jupyter-2.gif -.. |Screenshot-Jupyter3| image:: https://img.tqdm.ml/jupyter-3.gif -.. |README-Hits| image:: https://caspersci.uk.to/cgi-bin/hits.cgi?q=tqdm&style=social&r=https://github.com/tqdm/tqdm&l=https://img.tqdm.ml/favicon.png&f=https://img.tqdm.ml/logo.gif - :target: https://caspersci.uk.to/cgi-bin/hits.cgi?q=tqdm&a=plot&r=https://github.com/tqdm/tqdm&l=https://img.tqdm.ml/favicon.png&f=https://img.tqdm.ml/logo.gif&style=social +.. |Screenshot-Jupyter1| image:: https://tqdm.github.io/img/jupyter-1.gif +.. |Screenshot-Jupyter2| image:: https://tqdm.github.io/img/jupyter-2.gif +.. |Screenshot-Jupyter3| image:: https://tqdm.github.io/img/jupyter-3.gif +.. |README-Hits| image:: https://caspersci.uk.to/cgi-bin/hits.cgi?q=tqdm&style=social&r=https://github.com/tqdm/tqdm&l=https://tqdm.github.io/img/favicon.png&f=https://tqdm.github.io/img/logo.gif + :target: https://caspersci.uk.to/cgi-bin/hits.cgi?q=tqdm&a=plot&r=https://github.com/tqdm/tqdm&l=https://tqdm.github.io/img/favicon.png&f=https://tqdm.github.io/img/logo.gif&style=social diff --git a/tqdm/contrib/discord.py b/tqdm/contrib/discord.py index b8366fb66..1e4130828 100644 --- a/tqdm/contrib/discord.py +++ b/tqdm/contrib/discord.py @@ -6,7 +6,7 @@ >>> for i in trange(10, token='{token}', channel_id='{channel_id}'): ... ... -![screenshot](https://img.tqdm.ml/screenshot-discord.png) +![screenshot](https://tqdm.github.io/img/screenshot-discord.png) """ import logging from os import getenv diff --git a/tqdm/contrib/slack.py b/tqdm/contrib/slack.py index c8fe713a7..d4c850ca4 100644 --- a/tqdm/contrib/slack.py +++ b/tqdm/contrib/slack.py @@ -6,7 +6,7 @@ >>> for i in trange(10, token='{token}', channel='{channel}'): ... ... -![screenshot](https://img.tqdm.ml/screenshot-slack.png) +![screenshot](https://tqdm.github.io/img/screenshot-slack.png) """ import logging from os import getenv diff --git a/tqdm/contrib/telegram.py b/tqdm/contrib/telegram.py index b9a589b59..cbeadf20f 100644 --- a/tqdm/contrib/telegram.py +++ b/tqdm/contrib/telegram.py @@ -6,7 +6,7 @@ >>> for i in trange(10, token='{token}', chat_id='{chat_id}'): ... ... -![screenshot](https://img.tqdm.ml/screenshot-telegram.gif) +![screenshot](https://tqdm.github.io/img/screenshot-telegram.gif) """ from os import getenv from warnings import warn From 1411a0fd6a350a7e712358fa943f71005852e23e Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Tue, 8 Aug 2023 20:42:14 +0100 Subject: [PATCH 08/11] utils.envwrap: fix unused var, add tests --- tests/tests_utils.py | 25 +++++++++++++++++++++++++ tqdm/utils.py | 7 ++++--- 2 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 tests/tests_utils.py diff --git a/tests/tests_utils.py b/tests/tests_utils.py new file mode 100644 index 000000000..c174b8253 --- /dev/null +++ b/tests/tests_utils.py @@ -0,0 +1,25 @@ +from tqdm.utils import envwrap + + +def test_envwrap(monkeypatch): + """Test envwrap overrides""" + env_a = 42 + env_c = 1337 + monkeypatch.setenv('FUNC_A', str(env_a)) + monkeypatch.setenv('FUNC_TyPe_HiNt', str(env_c)) + + @envwrap("FUNC_") + def func(a=1, b=2, type_hint: int = None): + return a, b, type_hint + + assert (env_a, 2, 1337) == func(), "expected env override" + assert (99, 2, 1337) == func(a=99), "expected manual override" + + env_liTeral = 3.14159 + monkeypatch.setenv('FUNC_liTeral', str(env_liTeral)) + + @envwrap("FUNC_", literal_eval=True, case_sensitive=True) + def another_func(liTeral=1): + return liTeral + + assert env_liTeral == another_func() diff --git a/tqdm/utils.py b/tqdm/utils.py index b2ab9738b..c42a5d875 100644 --- a/tqdm/utils.py +++ b/tqdm/utils.py @@ -52,7 +52,8 @@ def envwrap(prefix, case_sensitive=False, literal_eval=False, is_method=False): is_method : bool, optional Whether to use `functools.partialmethod`. If (default: False) use `functools.partial`. - Examples: + Examples + -------- ``` $ cat foo.py from tqdm.utils import envwrap @@ -70,10 +71,10 @@ def test(a=1, b=2, c=3): part = partialmethod if is_method else partial def wrap(func): + params = signature(func).parameters if literal_eval: - return part(func, **{k: safe_eval(v) for k, v in overrides.items()}) + return part(func, **{k: safe_eval(v) for k, v in overrides.items() if k in params}) # use `func` signature to infer env override `type` (fallback to `str`) - params = signature(func).parameters for k in overrides: param = params.get(k, None) if param is not None: From fff3bf5f3ead77e493cad1ce29d8adf1b72da33c Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Tue, 8 Aug 2023 20:51:35 +0100 Subject: [PATCH 09/11] docs: mention envwrap --- .meta/.readme.rst | 13 +++++++++---- README.rst | 13 +++++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/.meta/.readme.rst b/.meta/.readme.rst index 1d3fa6aee..2e3588ea4 100644 --- a/.meta/.readme.rst +++ b/.meta/.readme.rst @@ -332,6 +332,10 @@ of a neat one-line progress bar. buffering. - `No intermediate output in docker-compose `__: use ``docker-compose run`` instead of ``docker-compose up`` and ``tty: true``. +- Overriding defaults via environment variables: + e.g. in CI jobs, ``export TQDM_MININTERVAL=5`` to avoid log spam. + This override logic is handled by the ``tqdm.utils.envwrap`` decorator + (useful independent of ``tqdm``). If you come across any other difficulties, browse and file |GitHub-Issues|. @@ -1185,16 +1189,17 @@ are: ==================== ======================================================== ==== ================================ Name ID SLoC Notes ==================== ======================================================== ==== ================================ -Casper da Costa-Luis `casperdcl `__ ~78% primary maintainer |Gift-Casper| -Stephen Larroque `lrq3000 `__ ~10% team member -Martin Zugnoni `martinzugnoni `__ ~4% +Casper da Costa-Luis `casperdcl `__ ~80% primary maintainer |Gift-Casper| +Stephen Larroque `lrq3000 `__ ~9% team member +Martin Zugnoni `martinzugnoni `__ ~3% Daniel Ecer `de-code `__ ~2% Richard Sheridan `richardsheridan `__ ~1% Guangshuo Chen `chengs `__ ~1% +Helio Machado `0x2b3bfa0 `__ ~1% Kyle Altendorf `altendky `__ <1% +Noam Yorav-Raphael `noamraph `__ <1% original author Matthew Stevens `mjstevens777 `__ <1% Hadrien Mary `hadim `__ <1% team member -Noam Yorav-Raphael `noamraph `__ <1% original author Mikhail Korobov `kmike `__ <1% team member ==================== ======================================================== ==== ================================ diff --git a/README.rst b/README.rst index fe582ce9c..1ab09a3e6 100644 --- a/README.rst +++ b/README.rst @@ -332,6 +332,10 @@ of a neat one-line progress bar. buffering. - `No intermediate output in docker-compose `__: use ``docker-compose run`` instead of ``docker-compose up`` and ``tty: true``. +- Overriding defaults via environment variables: + e.g. in CI jobs, ``export TQDM_MININTERVAL=5`` to avoid log spam. + This override logic is handled by the ``tqdm.utils.envwrap`` decorator + (useful independent of ``tqdm``). If you come across any other difficulties, browse and file |GitHub-Issues|. @@ -1402,16 +1406,17 @@ are: ==================== ======================================================== ==== ================================ Name ID SLoC Notes ==================== ======================================================== ==== ================================ -Casper da Costa-Luis `casperdcl `__ ~78% primary maintainer |Gift-Casper| -Stephen Larroque `lrq3000 `__ ~10% team member -Martin Zugnoni `martinzugnoni `__ ~4% +Casper da Costa-Luis `casperdcl `__ ~80% primary maintainer |Gift-Casper| +Stephen Larroque `lrq3000 `__ ~9% team member +Martin Zugnoni `martinzugnoni `__ ~3% Daniel Ecer `de-code `__ ~2% Richard Sheridan `richardsheridan `__ ~1% Guangshuo Chen `chengs `__ ~1% +Helio Machado `0x2b3bfa0 `__ ~1% Kyle Altendorf `altendky `__ <1% +Noam Yorav-Raphael `noamraph `__ <1% original author Matthew Stevens `mjstevens777 `__ <1% Hadrien Mary `hadim `__ <1% team member -Noam Yorav-Raphael `noamraph `__ <1% original author Mikhail Korobov `kmike `__ <1% team member ==================== ======================================================== ==== ================================ From 84546d6be86810915b868078efb4d426dbac9d6c Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Tue, 8 Aug 2023 21:02:39 +0100 Subject: [PATCH 10/11] fix edge cases & windows tests --- tests/tests_utils.py | 28 ++++++++++++++++++++++------ tqdm/utils.py | 22 +++++++++++----------- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/tests/tests_utils.py b/tests/tests_utils.py index c174b8253..5fb20dcea 100644 --- a/tests/tests_utils.py +++ b/tests/tests_utils.py @@ -1,4 +1,6 @@ -from tqdm.utils import envwrap +from pytest import mark + +from tqdm.utils import IS_WIN, envwrap def test_envwrap(monkeypatch): @@ -7,6 +9,7 @@ def test_envwrap(monkeypatch): env_c = 1337 monkeypatch.setenv('FUNC_A', str(env_a)) monkeypatch.setenv('FUNC_TyPe_HiNt', str(env_c)) + monkeypatch.setenv('FUNC_Unused', "x") @envwrap("FUNC_") def func(a=1, b=2, type_hint: int = None): @@ -15,11 +18,24 @@ def func(a=1, b=2, type_hint: int = None): assert (env_a, 2, 1337) == func(), "expected env override" assert (99, 2, 1337) == func(a=99), "expected manual override" - env_liTeral = 3.14159 - monkeypatch.setenv('FUNC_liTeral', str(env_liTeral)) + env_literal = 3.14159 + monkeypatch.setenv('FUNC_literal', str(env_literal)) + + @envwrap("FUNC_", literal_eval=True) + def another_func(literal="some_string"): + return literal + + assert env_literal == another_func() + + +@mark.skipif(IS_WIN, reason="no lowercase environ on Windows") +def test_envwrap_case(monkeypatch): + """Test envwrap case-sensitive overrides""" + env_liTeRaL = 3.14159 + monkeypatch.setenv('FUNC_liTeRaL', str(env_liTeRaL)) @envwrap("FUNC_", literal_eval=True, case_sensitive=True) - def another_func(liTeral=1): - return liTeral + def func(liTeRaL="some_string"): + return liTeRaL - assert env_liTeral == another_func() + assert env_liTeRaL == func() diff --git a/tqdm/utils.py b/tqdm/utils.py index c42a5d875..249a73960 100644 --- a/tqdm/utils.py +++ b/tqdm/utils.py @@ -66,24 +66,24 @@ def test(a=1, b=2, c=3): ``` """ i = len(prefix) - overrides = {k[i:] if case_sensitive else k[i:].lower(): v - for k, v in os.environ.items() if k.startswith(prefix)} + env_overrides = {k[i:] if case_sensitive else k[i:].lower(): v + for k, v in os.environ.items() if k.startswith(prefix)} part = partialmethod if is_method else partial def wrap(func): params = signature(func).parameters + overrides = {k: v for k, v in env_overrides.items() if k in params} if literal_eval: - return part(func, **{k: safe_eval(v) for k, v in overrides.items() if k in params}) + return part(func, **{k: safe_eval(v) for k, v in overrides.items()}) # use `func` signature to infer env override `type` (fallback to `str`) for k in overrides: - param = params.get(k, None) - if param is not None: - if param.annotation is not param.empty: - typ = param.annotation - # TODO: parse type in {Union, Any, Optional, ...} - else: - typ = str if param.default is None else type(param.default) - overrides[k] = typ(overrides[k]) + param = params[k] + if param.annotation is not param.empty: + typ = param.annotation + # TODO: parse type in {Union, Any, Optional, ...} + else: + typ = str if param.default is None else type(param.default) + overrides[k] = typ(overrides[k]) return part(func, **overrides) return wrap From d434a3c44e90712795a2c7820fd1ced8bcc526b1 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Wed, 9 Aug 2023 00:14:50 +0100 Subject: [PATCH 11/11] tests & docs: minor tidy --- .github/workflows/check.yml | 19 ++++++++++++------- .meta/.readme.rst | 2 +- README.rst | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 815b0bd34..2236d7703 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -16,22 +16,25 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - with: - fetch-depth: 0 - uses: actions/setup-python@v4 + with: + python-version: '3.x' - run: pip install -U tox - run: tox env: TOXENV: ${{ matrix.TOXENV }} asvfull: - if: (github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')) || github.event_name == 'schedule' + if: (github.event_name == 'push' && startsWith(github.ref, 'refs/tags')) || github.event_name == 'schedule' name: Benchmark (Full) runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: fetch-depth: 0 + token: ${{ secrets.GH_TOKEN || github.token }} - uses: actions/setup-python@v4 + with: + python-version: '3.x' - name: Install run: | pip install -U wheel @@ -58,7 +61,7 @@ jobs: GIT_AUTHOR_NAME: ${{ github.actor }} GIT_AUTHOR_EMAIL: ${{ github.actor }}@users.noreply.github.com testasv: - if: github.event.ref != 'refs/heads/master' && ! startsWith(github.event.ref, 'refs/tags') + if: github.ref != 'refs/heads/master' && ! startsWith(github.ref, 'refs/tags') name: Benchmark (Branch) runs-on: ubuntu-latest steps: @@ -66,6 +69,8 @@ jobs: with: fetch-depth: 0 - uses: actions/setup-python@v4 + with: + python-version: '3.x' - name: Install run: | pip install -U wheel @@ -82,6 +87,6 @@ jobs: - name: Benchmark run: | asv continuous --interleave-processes --only-changed -f 1.25 master HEAD - CHANGES="$(asv compare --only-changed -f 1.25 master HEAD)" - echo "$CHANGES" - [ -z "$CHANGES" ] || exit 1 + CHANGES=$(asv compare --only-changed -f 1.25 master HEAD) + echo "$CHANGES" >> "$GITHUB_STEP_SUMMARY" + test -z "$CHANGES" || exit 1 diff --git a/.meta/.readme.rst b/.meta/.readme.rst index 2e3588ea4..4e325bcd2 100644 --- a/.meta/.readme.rst +++ b/.meta/.readme.rst @@ -255,7 +255,7 @@ This can be beautified further: .. code:: sh - $ BYTES="$(du -sb docs/ | cut -f1)" + $ BYTES=$(du -sb docs/ | cut -f1) $ tar -cf - docs/ \ | tqdm --bytes --total "$BYTES" --desc Processing | gzip \ | tqdm --bytes --total "$BYTES" --desc Compressed --position 1 \ diff --git a/README.rst b/README.rst index 1ab09a3e6..defa9ad35 100644 --- a/README.rst +++ b/README.rst @@ -255,7 +255,7 @@ This can be beautified further: .. code:: sh - $ BYTES="$(du -sb docs/ | cut -f1)" + $ BYTES=$(du -sb docs/ | cut -f1) $ tar -cf - docs/ \ | tqdm --bytes --total "$BYTES" --desc Processing | gzip \ | tqdm --bytes --total "$BYTES" --desc Compressed --position 1 \