Commits

Ali Gholami Rudi  committed fd5278f

Adding ProgressBar
Changing rename to use TaskHandle

  • Participants
  • Parent commits 34d0173

Comments (0)

Files changed (10)

File docs/dev/contributing.txt

 -----------
 
 Always use the latest version of files from rope Subversion repository
-from https://svn.sourceforge.net/svnroot/rope
+from https://rope.svn.sourceforge.net/svnroot/rope
 
 * Submit patches in the format returned by the ``svn diff`` command.
 * Follow :PEP:`8`.

File docs/dev/issues.txt

       def get_job_sets(self):
           pass
 
-      def get_percent_done(self):
-          pass
-
       def add_observer(self):
           pass
 
       def finished_job(self):
           """Raises `TaskInterruptedError` if this task was interrupted"""
 
+      def get_percent_done(self):
+          pass
+
 
 It requires adding an argument to all refactorings and other time
 consuming tasks and changing them to use this object.

File docs/dev/workingon.txt

 Stoppable Refactorings
 ======================
 
-- Better property handling
-- Fixing `History.get_prev_contents()` when called in observers
-- Adding `TaskHandle`
+- Adding a ProgressBar
+- Using ProgressBar in unit-test view
+- Adding `StoppableTaskRunner`
+- Stoppable rename refactoring
 
-* Adding a ui progress
-* Using progress in unit-test view
+* Using in refactorings: move, inline, change signature,
+  introduce_factory, rename/move current module, find occurrences
 * Document refactorings and task handles
 
 * Validating callinfo

File rope/base/oi/staticoi.py

 def _analyze_node(pycore, pydefined, should_analyze):
     if should_analyze is not None and not should_analyze(pydefined):
         return
+#    if hasattr(pydefined, 'get_name'):
+#        print pydefined.get_name()
     visitor = SOIVisitor(pycore, pydefined, should_analyze)
     for child in pydefined.get_ast().getChildNodes():
         compiler.walk(child, visitor)

File rope/refactor/__init__.py

         self.name = name
         self.stopped = False
         self.job_sets = []
+        self.observers = []
 
     def is_stopped(self):
         return self.stopped
 
     def stop(self):
         self.stopped = True
+        self._inform_observers()
 
-    def get_percent_done(self):
-        return 0
-
-    def create_job_set(self, name='JobSet', count=1):
+    def create_job_set(self, name='JobSet', count=None):
         result = JobSet(self, name=name, count=count)
         self.job_sets.append(result)
+        self._inform_observers()
         return result
 
     def get_job_sets(self):
         return self.job_sets
 
+    def add_observer(self, observer):
+        self.observers.append(observer)
+
+    def _inform_observers(self):
+        for observer in list(self.observers):
+            observer()
+
 
 class JobSet(object):
 
         self.handle = handle
         self.name = name
         self.count = count
+        self.done = 0
+        self.job_name = None
 
     def started_job(self, name):
         self.check_status()
+        self.job_name = name
+        self.handle._inform_observers()
+
+    def finished_job(self):
+        self.handle._inform_observers()
+        self.job_name = None
+        self.done += 1
+
+    def check_status(self):
+        if self.handle.is_stopped():
+            raise exceptions.InterruptedTaskError()
+
+    def get_active_job_name(self):
+        return self.job_name
+
+    def get_percent_done(self):
+        if self.count is not None:
+            percent = self.done * 100 / self.count
+            return min(percent, 100)
+
+    def get_name(self):
+        return self.name
+
+
+class NullJobSet(object):
+
+    def started_job(self, name):
+        pass
 
     def finished_job(self):
         pass
 
     def check_status(self):
-        if self.handle.is_stopped():
-            raise exceptions.InterruptedTaskError()
+        pass

File rope/refactor/rename.py

+import rope.refactor
 from rope.base import exceptions, codeanalyze, pyobjects, pynames
 from rope.base.change import ChangeSet, ChangeContents, MoveResource
 from rope.refactor import occurrences, sourceutils
         return self.old_name
 
     def get_changes(self, new_name, in_file=False, in_hierarchy=False,
-                    unsure=False):
+                    unsure=False, task_handle=None):
         """Get the changes needed for this refactoring
 
         :parameters:
                             (self.old_name, new_name))
         finder = occurrences.FilteredFinder(self.pycore, self.old_name,
                                             old_pynames, unsure=unsure)
+        job_set = self._create_job_set(task_handle, len(files))
         for file_ in files:
+            job_set.started_job('Working on <%s>' % file_.path)
             new_content = rename_in_module(finder, new_name, resource=file_)
             if new_content is not None:
                 changes.add_change(ChangeContents(file_, new_content))
-
+            job_set.finished_job()
         if self._is_renaming_a_module():
             changes.add_change(self._rename_module(old_pynames[0].get_object(),
                                                    new_name))
         return changes
 
+    def _create_job_set(self, task_handle, count):
+        if task_handle is not None:
+            job_set = task_handle.create_job_set('Collecting Changes', count)
+        else:
+            job_set = rope.refactor.NullJobSet()
+        return job_set
+
     def _is_renaming_a_function_local_name(self):
         module, lineno = self.old_pyname.get_definition_location()
         if lineno is None:

File rope/ui/refactor.py

 import Tkinter
+import threading
 
 import rope.refactor.change_signature
 import rope.refactor.encapsulate_field
 from rope.ui.menubar import MenuAddress
 from rope.ui.uihelpers import (TreeViewHandle, TreeView,
                                DescriptionList, EnhancedListHandle,
-                               VolatileList, EnhancedList)
+                               VolatileList, EnhancedList, ProgressBar)
 
 
 class PreviewAndCommitChanges(object):
     def _cancel(self, event=None):
         self.toplevel.destroy()
 
+    def _get_changes(self):
+        runner = StoppableTaskRunner(self._calculate_changes, self.title)
+        return runner()
+
+
+class StoppableTaskRunner(object):
+
+    def __init__(self, task, title='Task'):
+        """Task is a function that takes a `TaskHandle`"""
+        self.task = task
+        self.title = title
+
+    def __call__(self):
+        handle = rope.refactor.TaskHandle(self.title)
+        toplevel = Tkinter.Toplevel()
+        toplevel.title('Performing Task ' + self.title)
+        frame = Tkinter.Frame(toplevel)
+        progress = ProgressBar(frame)
+        def update_progress():
+            job_sets = handle.get_job_sets()
+            if job_sets:
+                job_set = job_sets[0]
+                text = ''
+                if job_set.get_name() is not None:
+                    text += job_set.get_name()
+                if job_set.get_active_job_name() is not None:
+                    text += ' : ' + job_set.get_active_job_name()
+                progress.set_text(text)
+                percent = job_set.get_percent_done()
+                if percent is not None:
+                    progress.set_done_percent(percent)
+        handle.add_observer(update_progress)
+        class Calculate(object):
+
+            def __init__(self, task):
+                self.task = task
+                self.result = None
+                self.interrupted = False
+
+            def __call__(self):
+                try:
+                    try:
+                        self.result = self.task(handle)
+                    except exceptions.InterruptedTaskError:
+                        self.interrupted = True
+                finally:
+                    toplevel.quit()
+        
+        calculate = Calculate(self.task)
+        def stop(event=None):
+            handle.stop()
+        frame.grid(row=0)
+        stop_button = Tkinter.Button(toplevel, text='Stop', command=stop)
+        toplevel.bind('<Control-g>', stop)
+        toplevel.bind('<Escape>', stop)
+        stop_button.grid(row=1)
+
+        thread = threading.Thread(target=calculate)
+        thread.start()
+        toplevel.focus_set()
+        toplevel.grab_set()
+        toplevel.mainloop()
+        toplevel.destroy()
+        if calculate.interrupted:
+            raise exceptions.InterruptedTaskError(
+                'Task <%s> was interrupted' % self.title)
+        return calculate.result
+
 
 class RenameDialog(RefactoringDialog):
 
         self.renamer = rope.refactor.rename.Rename(
             context.project, resource, offset)
 
-    def _get_changes(self):
+    def _calculate_changes(self, handle=None):
         new_name = self.new_name_entry.get()
-        return self.renamer.get_changes(new_name, in_file=self.is_local,
-                                        in_hierarchy=self.in_hierarchy.get(),
-                                        unsure=self.unsure.get())
+        return self.renamer.get_changes(
+            new_name, in_file=self.is_local,
+            in_hierarchy=self.in_hierarchy.get(),
+            unsure=self.unsure.get(), task_handle=handle)
 
     def _get_dialog_frame(self):
         frame = Tkinter.Frame(self.toplevel)

File rope/ui/testview.py

 from SimpleXMLRPCServer import SimpleXMLRPCServer
 
 import rope.base.project
-from rope.ui.uihelpers import DescriptionList
+from rope.ui.uihelpers import DescriptionList, ProgressBar
 import rope.ui.runtest
 
 
 class GUITestResult(object):
 
     def __init__(self, gui_runner):
+        self.gui_runner = gui_runner
         self.count = -1
         self.run_count = 0
-        self.label = gui_runner.test_name
-        self.color = 'green'
-        self.gui_runner = gui_runner
-        self.canvas = canvas = gui_runner.canvas
-        canvas.create_rectangle(0, 0, canvas['width'], canvas['height'], fill='')
-        canvas.create_rectangle(0, 0, 0, canvas['height'],
-                                fill=self.color, outline=self.color)
+        self.progress = gui_runner.progress
+        self.progress.set_color('green')
 
     def start_test(self, test_name):
-        self.label['text'] = test_name
+        self.progress.set_text(test_name)
         return True
 
     def add_success(self, test_name):
         return True
 
     def add_error(self, test_name, error):
-        self.gui_runner.add_failure(test_name, error)
-        self.gui_runner.description_list.add_entry(test_name)
-        self.color = 'red'
+        self.progress.set_color('red')
         return True
 
     def add_failure(self, test_name, error):
-        self.gui_runner.add_failure(test_name, error)
-        self.gui_runner.description_list.add_entry(test_name)
-        self.color = 'red'
+        self.progress.set_color('red')
         return True
 
     def set_test_count(self, count):
 
     def stop_test(self, test_name):
         self.run_count += 1
-        self.label['text'] = ('ran %d of %d' % (self.run_count, self.count))
-        self._draw_shape()
+        self.progress.set_text('ran %d of %d' % (self.run_count, self.count))
+        self.progress.set_done_percent(self.run_count * 100 / self.count)
         return True
 
-    def _draw_shape(self):
-        width = int(self.canvas['width']) * self.run_count / self.count
-        self.canvas.create_rectangle(0, 0, width, self.canvas['height'], fill=self.color)
-
     def _is_finished(self):
         return self.run_count == self.count
 
         label = Tkinter.Label(self.toplevel,
                               text='Running Unit Tests in <%s>' % resource.path)
         label.grid(row=0)
-        self.test_name = Tkinter.Label(self.toplevel, width=80)
-        self.test_name.grid(row=1)
-        self.canvas = Tkinter.Canvas(self.toplevel, height=20)
-        self.canvas.grid(row=2)
+        progress_frame = Tkinter.Frame(self.toplevel)
+        self.progress = ProgressBar(progress_frame)
+        progress_frame.grid(row=1)
 
         self.result = GUITestResult(self)
         self.failures = {}

File rope/ui/uihelpers.py

 
     def add_entry(self, entry):
         self.list.add_entry(entry)
+
+
+class ProgressBar(object):
+
+    def __init__(self, parent):
+        self.text = Tkinter.Label(parent, width=80, justify=Tkinter.LEFT)
+        self.canvas = canvas = Tkinter.Canvas(parent, height=20)
+        self.color = 'blue'
+        self.percent = 0
+        canvas.create_rectangle(0, 0, canvas['width'], canvas['height'], fill='')
+        canvas.create_rectangle(0, 0, 0, canvas['height'],
+                                fill=self.color, outline=self.color)
+        self.text.grid(row=0)
+        self.canvas.grid(row=1)
+
+    def set_done_percent(self, percent):
+        self.percent = percent
+        self._draw_shape()
+
+    def set_color(self, color):
+        self.color = color
+        self._draw_shape()
+
+    def set_text(self, text):
+        self.text['text'] = text
+
+    def _draw_shape(self):
+        width = int(self.canvas['width']) * self.percent / 100
+        self.canvas.create_rectangle(0, 0, width, self.canvas['height'],
+                                     fill=self.color)

File ropetest/refactor/__init__.py

                           self.mod.read())
 
 
+class _MockTaskObserver(object):
+
+    def __init__(self):
+        self.called = 0
+
+    def __call__(self):
+        self.called += 1
+
 class TaskHandleTest(unittest.TestCase):
 
     def test_trivial_case(self):
         handle.stop()
         self.assertTrue(handle.is_stopped())
 
-    def test_zero_get_percent_done(self):
-        handle = rope.refactor.TaskHandle()
-        self.assertFalse(0, handle.get_percent_done())
-
     def test_job_sets(self):
         handle = rope.refactor.TaskHandle()
         jobs = handle.create_job_set()
         handle.stop()
         jobs.started_job('job1')
 
+    def test_calling_the_observer_after_stopping(self):
+        handle = rope.refactor.TaskHandle()
+        observer = _MockTaskObserver()
+        handle.add_observer(observer)
+        handle.stop()
+        self.assertEquals(1, observer.called)
+
+    def test_calling_the_observer_after_creating_job_sets(self):
+        handle = rope.refactor.TaskHandle()
+        observer = _MockTaskObserver()
+        handle.add_observer(observer)
+        jobs = handle.create_job_set()
+        self.assertEquals(1, observer.called)
+
+    def test_calling_the_observer_when_starting_and_finishing_jobs(self):
+        handle = rope.refactor.TaskHandle()
+        observer = _MockTaskObserver()
+        handle.add_observer(observer)
+        jobs = handle.create_job_set(name='test job set', count=1)
+        jobs.started_job('job1')
+        jobs.finished_job()
+        self.assertEquals(3, observer.called)
+
+    def test_job_set_get_percent_done(self):
+        handle = rope.refactor.TaskHandle()
+        jobs = handle.create_job_set(name='test job set', count=2)
+        self.assertEquals(0, jobs.get_percent_done())
+        jobs.started_job('job1')
+        jobs.finished_job()
+        self.assertEquals(50, jobs.get_percent_done())
+        jobs.started_job('job2')
+        jobs.finished_job()
+        self.assertEquals(100, jobs.get_percent_done())
+
+    def test_getting_job_name(self):
+        handle = rope.refactor.TaskHandle()
+        jobs = handle.create_job_set(name='test job set', count=1)
+        self.assertEquals('test job set', jobs.get_name())
+        self.assertEquals(None, jobs.get_active_job_name())
+        jobs.started_job('job1')
+        self.assertEquals('job1', jobs.get_active_job_name())
+
 
 def suite():
     result = unittest.TestSuite()