diff --git a/atpbar/main.py b/atpbar/main.py index 8fe5a88..40f01ca 100644 --- a/atpbar/main.py +++ b/atpbar/main.py @@ -64,6 +64,7 @@ def __init__(self, iterable: Iterable[T], name: str, len_: int): self.name = name self.len_ = len_ self.id_ = uuid.uuid4() + self._done = 0 def __iter__(self) -> Iterator[T]: with fetch_reporter() as reporter: @@ -76,16 +77,31 @@ def __iter__(self) -> Iterator[T]: with self._report_last(): for i, e in enumerate(self.iterable): yield e - self._report_progress(i) + self._done = i + 1 + self._report_progress() else: self.loop_complete = True def _report_start(self) -> None: - report = Report(task_id=self.id_, name=self.name, done=0, total=self.len_) + report = Report( + task_id=self.id_, + name=self.name, + done=0, + total=self.len_, + first=True, + last=self._done == self.len_, + ) self._submit(report) - def _report_progress(self, i: int) -> None: - report = Report(task_id=self.id_, done=(i + 1)) + def _report_progress(self) -> None: + report = Report( + task_id=self.id_, + name=self.name, + done=self._done, + total=self.len_, + first=self._done == 0, + last=self._done == self.len_, + ) self._submit(report) @contextlib.contextmanager @@ -102,7 +118,14 @@ def _report_last(self) -> Iterator[None]: finally: if self.loop_complete: return - report = Report(task_id=self.id_, first=False, last=True) + report = Report( + task_id=self.id_, + name=self.name, + done=self._done, + total=self.len_, + first=False, + last=True, + ) self._submit(report) def _submit(self, report: Report) -> None: diff --git a/atpbar/progress_report/complement.py b/atpbar/progress_report/complement.py deleted file mode 100755 index 13991e1..0000000 --- a/atpbar/progress_report/complement.py +++ /dev/null @@ -1,51 +0,0 @@ -from uuid import UUID - -from .report import Report - - -class ProgressReportComplementer: - '''Complement progress reports - - Complement a progress report with the previous report for the same - task. - - Parameters - ---------- - report : Report - A report must always include `task_id`. The first report for a task must - include `done`, `total`, and 'name'. The `first` and `last` will be - automatically determined if not given. - - ''' - - def __init__(self) -> None: - self.previous_reports = dict[UUID, Report]() - - def __call__(self, report: Report) -> None: - task_id = report['task_id'] - if task_id in self.previous_reports: - self._complement(task_id, report) - self._first(report) - self._last(report) - self._store(task_id, report.copy()) - - def _complement(self, task_id: UUID, report: Report) -> None: - report_copy = report.copy() - report.update(Report(task_id=task_id)) - report.update(self.previous_reports[task_id]) - report.update(report_copy) - - def _first(self, report: Report) -> None: - if 'first' in report: - return - report['first'] = report['done'] == 0 - - def _last(self, report: Report) -> None: - if 'last' in report: - return - report['last'] = report['done'] >= report['total'] - - def _store(self, task_id: UUID, report: Report) -> None: - report.pop('first', None) - report.pop('last', None) - self.previous_reports[task_id] = report diff --git a/atpbar/progress_report/report.py b/atpbar/progress_report/report.py index e59d451..7dc9da1 100644 --- a/atpbar/progress_report/report.py +++ b/atpbar/progress_report/report.py @@ -2,32 +2,24 @@ from uuid import UUID -class Report(TypedDict, total=False): +class Report(TypedDict): '''Progress report Parameters ---------- - task_id : immutable + task_id The unique task ID. - done : int + done The number of the iterations done so far - total : int - The total iterations to be done - name : str - A name of the task. It will be use as the label on the - progress bars. + total + The total iterations to be done + name + A name of the task. It will be use as the label on the progress bars. first : bool - `True` if the first report for the task. If not given, - automatically determined from `done`; `True` if `done` is - 0, `False` otherwise + `True` if the first report for the task. last : bool - `True` if the last report for the task. If not given, - automatically determined from `done` and `total`; `True` - if `done` equals `total`, `False` otherwise + `True` if the last report for the task. - Notes: - ------ - TODO: Use typing.Required for Python 3.11 and above ''' task_id: UUID diff --git a/atpbar/progress_report/reporter.py b/atpbar/progress_report/reporter.py index 90c745a..b9cf2d7 100755 --- a/atpbar/progress_report/reporter.py +++ b/atpbar/progress_report/reporter.py @@ -5,7 +5,6 @@ from atpbar.presentation import create_presentation from atpbar.stream import StreamQueue, StreamRedirection, register_stream_queue -from .complement import ProgressReportComplementer from .pickup import ProgressReportPickup from .report import Report @@ -53,7 +52,6 @@ def __init__(self) -> None: self.stream_queue: StreamQueue = Queue() self.interval = DEFAULT_INTERVAL # [second] self.last_time = dict[UUID, float]() - self.complete_report = ProgressReportComplementer() self.stream_redirection_enabled = True def __repr__(self) -> str: @@ -101,8 +99,6 @@ def report(self, report: Report) -> None: ''' - self.complete_report(report) - if not self._need_to_report(report): return diff --git a/tests/presentation/test_base.py b/tests/presentation/test_base.py index 33aabd1..e16e866 100644 --- a/tests/presentation/test_base.py +++ b/tests/presentation/test_base.py @@ -35,17 +35,17 @@ def test_repr(obj: MockProgressBar) -> None: def test_present(obj: Presentation) -> None: i = uuid.uuid4() j = uuid.uuid4() - obj.present(dict(task_id=i, last=False)) + obj.present(dict(task_id=i, last=False)) # type: ignore assert obj.active() - obj.present(dict(task_id=i, last=False)) + obj.present(dict(task_id=i, last=False)) # type: ignore assert obj.active() - obj.present(dict(task_id=j, last=False)) + obj.present(dict(task_id=j, last=False)) # type: ignore assert obj.active() - obj.present(dict(task_id=j, last=False)) + obj.present(dict(task_id=j, last=False)) # type: ignore assert obj.active() - obj.present(dict(task_id=i, last=True)) + obj.present(dict(task_id=i, last=True)) # type: ignore assert obj.active() - obj.present(dict(task_id=j, last=True)) + obj.present(dict(task_id=j, last=True)) # type: ignore assert not obj.active() diff --git a/tests/progress_report/test_report_complement.py b/tests/progress_report/test_report_complement.py deleted file mode 100644 index 1195f5c..0000000 --- a/tests/progress_report/test_report_complement.py +++ /dev/null @@ -1,136 +0,0 @@ -import uuid - -import pytest - -from atpbar.progress_report import Report -from atpbar.progress_report.complement import ProgressReportComplementer - - -@pytest.fixture() -def obj() -> ProgressReportComplementer: - ret = ProgressReportComplementer() - return ret - - -def test_repr(obj: ProgressReportComplementer) -> None: - repr(obj) - - -def test_complement(obj: ProgressReportComplementer) -> None: - - task_id = uuid.uuid4() - report0 = Report(task_id=task_id, done=0, total=10, name='task1') - expected0 = Report( - task_id=task_id, done=0, total=10, first=True, last=False, name='task1' - ) - obj(report0) - assert expected0 == report0 - - report1 = Report(task_id=task_id, done=1, total=12) - expected1 = Report( - task_id=task_id, done=1, total=12, first=False, last=False, name='task1' - ) - obj(report1) - assert expected0 == report0 - assert expected1 == report1 - - report2 = Report(task_id=task_id, done=2) - expected2 = Report( - task_id=task_id, done=2, total=12, first=False, last=False, name='task1' - ) - obj(report2) - assert expected0 == report0 - assert expected1 == report1 - assert expected2 == report2 - - report3 = Report(task_id=task_id, done=12) - expected3 = Report( - task_id=task_id, done=12, total=12, first=False, last=True, name='task1' - ) - obj(report3) - assert expected0 == report0 - assert expected1 == report1 - assert expected2 == report2 - assert expected3 == report3 - - -def test_volatile_fields(obj: ProgressReportComplementer) -> None: - - task_id = uuid.uuid4() - report0 = Report(task_id=task_id, done=0, total=10, first=True) - expected0 = Report(task_id=task_id, done=0, total=10, first=True, last=False) - obj(report0) - assert expected0 == report0 - - # manually set `first` - report1 = Report(task_id=task_id, done=1, total=12, first=True, last=False) - expected1 = Report(task_id=task_id, done=1, total=12, first=True, last=False) - obj(report1) - assert expected0 == report0 - assert expected1 == report1 - - # doesn't remember previously manually set `first` - report2 = Report(task_id=task_id, done=2) - expected2 = Report(task_id=task_id, done=2, total=12, first=False, last=False) - obj(report2) - assert expected0 == report0 - assert expected1 == report1 - assert expected2 == report2 - - -def test_first(obj: ProgressReportComplementer) -> None: - - task_id_1 = uuid.uuid4() - report0 = Report(task_id=task_id_1, done=0, total=10) - expected0 = Report(task_id=task_id_1, done=0, total=10, first=True, last=False) - obj(report0) - assert expected0 == report0 - - report1 = Report(task_id=task_id_1, done=1, total=12) - expected1 = Report(task_id=task_id_1, done=1, total=12, first=False, last=False) - obj(report1) - assert expected1 == report1 - - report2 = Report(task_id=task_id_1, done=2, first=True) - expected2 = Report(task_id=task_id_1, done=2, total=12, first=True, last=False) - obj(report2) - assert expected2 == report2 - - report3 = Report(task_id=task_id_1, done=3) - expected3 = Report(task_id=task_id_1, done=3, total=12, first=False, last=False) - obj(report3) - assert expected3 == report3 - - # different task_id, both done and total are 0 - task_id_2 = uuid.uuid4() - report4 = Report(task_id=task_id_2, done=0, total=0) - expected4 = Report(task_id=task_id_2, done=0, total=0, first=True, last=True) - obj(report4) - assert expected4 == report4 - - -def test_last(obj: ProgressReportComplementer) -> None: - - task_id_1 = uuid.uuid4() - report0 = Report(task_id=task_id_1, done=0, total=10) - expected0 = Report(task_id=task_id_1, done=0, total=10, first=True, last=False) - obj(report0) - assert expected0 == report0 - - # set `last` manually - report1 = Report(task_id=task_id_1, done=1, total=12, last=True) - expected1 = Report(task_id=task_id_1, done=1, total=12, first=False, last=True) - obj(report1) - assert expected1 == report1 - - task_id_2 = uuid.uuid4() - report2 = Report(task_id=task_id_2, done=0, total=10) - expected2 = Report(task_id=task_id_2, done=0, total=10, first=True, last=False) - obj(report2) - assert expected2 == report2 - - # done = total - report3 = Report(task_id=task_id_2, done=10, total=10) - expected3 = Report(task_id=task_id_2, done=10, total=10, first=False, last=True) - obj(report3) - assert expected3 == report3 diff --git a/tests/progress_report/test_reporter.py b/tests/progress_report/test_reporter.py index 13b177e..1c5a7a0 100644 --- a/tests/progress_report/test_reporter.py +++ b/tests/progress_report/test_reporter.py @@ -46,7 +46,14 @@ def test_report_need_to_report( task_id = uuid.uuid4() mock_time.time.return_value = current_time monkeypatch.setattr(obj, '_need_to_report', mock.Mock(return_value=True)) - report = Report(task_id=task_id, done=0, total=10) + report = Report( + task_id=task_id, + name='', + done=0, + total=10, + first=True, + last=False, + ) obj.report(report) assert [mock.call(report)] == mock_queue.put.call_args_list assert {task_id: current_time} == obj.last_time @@ -62,7 +69,14 @@ def test_report_no_need_to_report( task_id = uuid.uuid4() mock_time.time.return_value = current_time monkeypatch.setattr(obj, '_need_to_report', mock.Mock(return_value=False)) - report = Report(task_id=task_id, done=0, total=10) + report = Report( + task_id=task_id, + name='', + done=0, + total=10, + first=True, + last=False, + ) obj.report(report) assert [] == mock_queue.put.call_args_list assert {} == obj.last_time @@ -71,16 +85,70 @@ def test_report_no_need_to_report( task_id = uuid.uuid4() params = [ - pytest.param(Report(task_id=task_id, first=True, last=False), {}, 10.0, True), - pytest.param(Report(task_id=task_id, first=False, last=True), {}, 10.0, True), pytest.param( - Report(task_id=task_id, first=True, last=False), {task_id: 10.0}, 10.0, True + Report( + task_id=task_id, + name='', + done=0, + total=10, + first=True, + last=False, + ), + {}, + 10.0, + True, ), pytest.param( - Report(task_id=task_id, first=False, last=False), {task_id: 10.0}, 30.0, True + Report( + task_id=task_id, + name='', + done=10, + total=10, + first=False, + last=True, + ), + {}, + 10.0, + True, ), pytest.param( - Report(task_id=task_id, first=False, last=False), {task_id: 10.0}, 15.0, False + Report( + task_id=task_id, + name='', + done=0, + total=10, + first=True, + last=False, + ), + {task_id: 10.0}, + 10.0, + True, + ), + pytest.param( + Report( + task_id=task_id, + name='', + done=5, + total=10, + first=False, + last=False, + ), + {task_id: 10.0}, + 30.0, + True, + ), + pytest.param( + Report( + task_id=task_id, + name='', + done=5, + total=10, + first=False, + last=False, + ), + {task_id: 10.0}, + 15.0, + False, ), ] param_names = 'report, last_time, current_time, expected' diff --git a/tests/stream/output/test_output_stream.py b/tests/stream/output/test_output_stream.py index 8b547fb..5cd72f9 100644 --- a/tests/stream/output/test_output_stream.py +++ b/tests/stream/output/test_output_stream.py @@ -5,7 +5,7 @@ from hypothesis import strategies as st from atpbar.stream import OutputStream, Queue, StreamQueue -from tests.stream.st import st_text +from tests.test_utils.st import st_text def test_type_error() -> None: diff --git a/tests/stream/output/test_print.py b/tests/stream/output/test_print.py index 7cf5cdc..b11af65 100644 --- a/tests/stream/output/test_print.py +++ b/tests/stream/output/test_print.py @@ -7,7 +7,7 @@ from pytest import MonkeyPatch from atpbar.stream import FD, OutputStream -from tests.stream.st import st_text +from tests.test_utils.st import st_text def st_end() -> st.SearchStrategy[str | None]: diff --git a/tests/test_utils/__init__.py b/tests/test_utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/stream/st.py b/tests/test_utils/st.py similarity index 100% rename from tests/stream/st.py rename to tests/test_utils/st.py diff --git a/tests/stream/test_st.py b/tests/test_utils/test_st.py similarity index 100% rename from tests/stream/test_st.py rename to tests/test_utils/test_st.py