From 7de6b4a3173dd70c89d38d3851be9c7dceae4ab7 Mon Sep 17 00:00:00 2001 From: AnonymouX47 Date: Fri, 24 Jun 2022 13:47:40 +0100 Subject: [PATCH] [lib,test] Added `ImageIterator.loop_no` property - Add: Added `loop_no` and `_loop_no` to `ImageIterator` to expose iteration repeat countdown. - Add: Added tests for `loop_no`. - Change: `.tui.render.render_frames()` now uses `loop_no` instead of directly accessing the underlying generator's locals. --- CHANGELOG.md | 1 + term_image/image/common.py | 18 ++++++++++++++---- term_image/tui/render.py | 2 +- tests/test_image_iterator.py | 22 ++++++++++++++++++++++ 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7faacb5..5436db8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [lib] `term_image.TermImageWarning`; pacage specific warning category ([#50]). - [lib] `term_image.set_query_timeout()` to set global query timeout ([3b658f3]). - [lib] Auto background color i.e using the terminal's default BG color for transparent images ([#54]) +- [lib] `ImageIterator.loop_no` property. - [cli] `--style` command-line option for render style selection ([#37]). - [cli] `kitty` render style choice for the `--style` command-line option ([#39]). - [cli] `--force-style` to bypass render style support checks ([#44]). diff --git a/term_image/image/common.py b/term_image/image/common.py index 5acc7c4a..fb232a16 100644 --- a/term_image/image/common.py +++ b/term_image/image/common.py @@ -2004,6 +2004,7 @@ def __init__( self._cached = ( cached if isinstance(cached, bool) else image.n_frames <= cached ) and repeat != 1 + self._loop_no = None self._animator = image._renderer( self._animate, alpha, fmt, style_args, check_size=False ) @@ -2033,11 +2034,20 @@ def __next__(self) -> None: raise def __repr__(self) -> None: - return "{}(image={!r}, repeat={}, format={!r}, cached={})".format( + return "{}(image={!r}, repeat={}, format={!r}, cached={}, loop_no={})".format( type(self).__name__, *self.__dict__.values(), ) + loop_no = property( + lambda self: self._loop_no, + doc="""Iteration repeat countdown + + Changes on the first iteration of each loop, except for infinite iteration + where it's always ``-1``. + """, + ) + def close(self) -> None: """Closes the iterator and releases resources used. @@ -2097,7 +2107,7 @@ def _animate( self._img = img # For cleanup image = self._image cached = self._cached - repeat = self._repeat + self._loop_no = repeat = self._repeat if cached: cache = [(None,) * 2] * image.n_frames @@ -2119,7 +2129,7 @@ def _animate( except EOFError: image._seek_position = n = 0 if repeat > 0: # Avoid infinitely large negative numbers - repeat -= 1 + self._loop_no = repeat = repeat - 1 if cached: break continue @@ -2161,7 +2171,7 @@ def _animate( image._seek_position = n = 0 if repeat > 0: # Avoid infinitely large negative numbers - repeat -= 1 + self._loop_no = repeat = repeat - 1 # For consistency in behaviour if img is image._source: diff --git a/term_image/tui/render.py b/term_image/tui/render.py index 5d8db24b..919bdc75 100644 --- a/term_image/tui/render.py +++ b/term_image/tui/render.py @@ -321,7 +321,7 @@ def render_frames( output.put( ( next(animator), - animator._animator.gi_frame.f_locals["repeat"], + animator._loop_no, image.tell(), size, image.rendered_size, diff --git a/tests/test_image_iterator.py b/tests/test_image_iterator.py index e202fbcb..bdbfcc92 100644 --- a/tests/test_image_iterator.py +++ b/tests/test_image_iterator.py @@ -228,6 +228,28 @@ def test_formatting(): assert next(image_it).partition("\n")[0] == " " * (_size[0] + 2) +def test_loop_no(): + for cached in (False, True): + image_it = ImageIterator(gif_image, 2, cached=cached) + assert image_it.loop_no is None + + next(image_it) + assert image_it.loop_no == 2 + for _ in range(gif_image.n_frames - 1): + next(image_it) + assert image_it.loop_no == 2 + + next(image_it) + assert image_it.loop_no == 1 + for _ in range(gif_image.n_frames - 1): + next(image_it) + assert image_it.loop_no == 1 + + with pytest.raises(StopIteration): + next(image_it) + assert image_it.loop_no == 0 + + def test_close(): image_it = ImageIterator(gif_image, 1) next(image_it)