diff --git a/py_tools_ds/processing/progress_mon.py b/py_tools_ds/processing/progress_mon.py index 62ce5ee46df236bf076708d59c36c427a28c9499..2df428ca63fc271d4a8471a2c6bbde12d423e5d4 100644 --- a/py_tools_ds/processing/progress_mon.py +++ b/py_tools_ds/processing/progress_mon.py @@ -99,6 +99,8 @@ class ProgressBar(object): self.use_as_cb = use_as_callback self.out = out + self._percdone = list(range(10, 110, 10)) + def print_progress(self, percent): """Print progress. @@ -108,11 +110,14 @@ class ProgressBar(object): :return: """ if self.Timer.timed_out: - self.out.flush() + if self.out is not None: + self.out.flush() if self.use_as_cb: # raise a KeyBoardInterrupt instead of a TimeOutError # as this is catchable by gdal.GetLastException() raise KeyboardInterrupt() + else: + raise TimeoutError(f'No progress for {self.timeout} seconds.') formatStr = "{0:." + str(self.decimals) + "f}" percents = formatStr.format(percent) @@ -120,17 +125,30 @@ class ProgressBar(object): # bar = '█' * filledLength + '-' * (barLength - filledLength) # this is not compatible to shell console bar = '=' * filledLength + '-' * (self.barLength - filledLength) - # reset the cursor to the beginning of the line and allows to write over what was previously on the line - self.out.write('\r') + if self.out is not None: + # reset the cursor to the beginning of the line and allows to write over what was previously on the line + self.out.write('\r') - # [%s/%s] numberDone - suffix = self.suffix if not self.show_elapsed else '%s => %s' % (self.suffix, self.Timer.elapsed) - self.out.write('%s |%s| %s%s %s' % (self.prefix, bar, percents, '%', suffix)) + # [%s/%s] numberDone + suffix = self.suffix if not self.show_elapsed else '%s => %s' % (self.suffix, self.Timer.elapsed) + self.out.write('%s |%s| %s%s %s' % (self.prefix, bar, percents, '%', suffix)) - if percent >= 100.: - self.out.write('\n') + if percent >= 100.: + self.out.write('\n') - self.out.flush() + self.out.flush() + + else: + # in some environments, sys.stderr can also be None + # pydocs: usually Windows GUI apps that aren’t connected to a console and Python apps started with pythonw + try: + percnext = self._percdone[0] + if percent >= percnext: + print(f'{percents} %') + self._percdone.pop(0) + + except IndexError: # pragma: no cover + pass def __call__(self, percent01, message, user_data): """Allow ProgressBar instances to be callable and thus to be used as callback function, e.g., for GDAL. diff --git a/tests/test_processing/__init__.py b/tests/test_processing/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/test_processing/test_progress_mon.py b/tests/test_processing/test_progress_mon.py new file mode 100644 index 0000000000000000000000000000000000000000..0589d2f6e4c23587649963ad166fcab9c272176e --- /dev/null +++ b/tests/test_processing/test_progress_mon.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# py_tools_ds - A collection of geospatial data analysis tools that simplify standard +# operations when handling geospatial raster and vector data as well as projections. +# +# Copyright (C) 2016-2021 +# - Daniel Scheffler (GFZ Potsdam, daniel.scheffler@gfz-potsdam.de) +# - Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences Potsdam, +# Germany (https://www.gfz-potsdam.de/) +# +# This software was developed within the context of the GeoMultiSens project funded +# by the German Federal Ministry of Education and Research +# (project grant code: 01 IS 14 010 A-C). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +test_progress_mon +----------------- + +Tests for `py_tools_ds.processing.progress_mon` module. +""" + +import unittest +from time import sleep + +from py_tools_ds.processing.progress_mon import ProgressBar + + +class Test_ProgressBar(unittest.TestCase): + def test_print_progress(self): + bar = ProgressBar(prefix='\tprogress:') + for i in range(100): + bar.print_progress((i + 1) / 100 * 100) + + def test_timeout(self): + with self.assertRaises(TimeoutError): + bar = ProgressBar(prefix='\tprogress:', timeout=0.1) + for i in range(5): + bar.print_progress((i + 1) / 5 * 100) + sleep(0.2) + + def test_stderr_is_none(self): + bar = ProgressBar(prefix='\tprogress:', out=None) + for i in range(73): + bar.print_progress((i + 1) / 73 * 100) + + +if __name__ == '__main__': + unittest.main()