From c4f4bb212886704cf507ff8a7dd205db144f3e72 Mon Sep 17 00:00:00 2001 From: Vassilii Khachaturov Date: Thu, 12 Sep 2013 17:56:50 +0000 Subject: [PATCH] 7061: Add User.progress context manager reapply from trunk svn: r23094 --- gramps/cli/test/user_test.py | 39 ++++++++++++++++++++++++++++++++++-- gramps/gen/user.py | 30 +++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/gramps/cli/test/user_test.py b/gramps/cli/test/user_test.py index a02a751bc..6157c176e 100644 --- a/gramps/cli/test/user_test.py +++ b/gramps/cli/test/user_test.py @@ -32,9 +32,9 @@ import sys try: if sys.version_info < (3,3): - from mock import Mock, NonCallableMock + from mock import Mock, patch else: - from unittest.mock import Mock, NonCallableMock + from unittest.mock import Mock, patch MOCKING = True @@ -151,5 +151,40 @@ class TestUser_quiet(unittest.TestCase): assert len(self.user._fileout.method_calls ) == 0, list(self.user._fileout.method_calls) +@unittest.skipUnless(MOCKING, "Requires unittest.mock to run") +class TestUser_progress(unittest.TestCase): + _progress_begin_step_end = \ + TestUser_quiet.test_progress_can_begin_step_end.__func__ + + def setUp(self): + self.user = user.User() + self.user._fileout = Mock(spec=sys.stderr) + + def test_can_step_using_with(self): + # Collect baseline output from the old-style interface (begin/step/end) + self._progress_begin_step_end() + self.expected_output = list(self.user._fileout.method_calls) + self.user._fileout.reset_mock() + self.assertTrue( + len(self.user._fileout.method_calls) == 0, + list(self.user._fileout.method_calls)) + + with self.user.progress("Foo", "Bar", 0) as step: + for i in range(10): + step() + + # Output using `with' differs from one with `progress_...' + self.assertEqual(self.expected_output, + list(self.user._fileout.method_calls)) + + def test_ends_progress_upon_exception_in_with(self): + with patch('gramps.cli.user.User.end_progress') as MockEP: + try: + with self.user.progress("Foo", "Bar", 0) as step: + raise Exception() + except Exception: + pass + self.assertTrue(MockEP.called) + if __name__ == "__main__": unittest.main() diff --git a/gramps/gen/user.py b/gramps/gen/user.py index cb6975bb1..70b18b764 100644 --- a/gramps/gen/user.py +++ b/gramps/gen/user.py @@ -21,6 +21,7 @@ # import sys +from contextlib import contextmanager """ The User class provides basic interaction with the user. @@ -40,6 +41,8 @@ class User(): def begin_progress(self, title, message, steps): """ Start showing a progress indicator to the user. + + Don't use this method directly, use progress instead. @param title: the title of the progress meter @type title: str @@ -56,6 +59,8 @@ class User(): def step_progress(self): """ Advance the progress meter. + + Don't use this method directly, use progress instead. """ pass @@ -80,8 +85,33 @@ class User(): def end_progress(self): """ Stop showing the progress indicator to the user. + + Don't use this method directly, use progress instead. """ pass + + # Context-manager wrapper of the begin/step/end_progress above + @contextmanager + def progress(self, *args, **kwargs): + """ + Preferred form of context reporting. + + Parameters: same as for begin_progress. + + Usage example (see gramps/cli/test/user_test.py): + with self.user.progress("Foo", "Bar", 0) as step: + for i in range(10): + step() + + Ensures end_progress will be called even if an exception was thrown. + """ + self.begin_progress(*args, **kwargs) + try: + yield self.step_progress + except: + raise + finally: + self.end_progress() def prompt(self, title, message, accept_label, reject_label): """