From afce46dd07af1584cb110be21f02cbf7890e7ba2 Mon Sep 17 00:00:00 2001 From: Sai Rahul Poruri Date: Fri, 8 Jan 2021 16:58:31 +0000 Subject: [PATCH 1/3] DEV : Use pyface.undo imports in apptools.undo instead of simply raising a deprecation warning and redefining the modules/classes Note that the tests have been removed as this module is now simply a stub, exposing the functionality in pyface.undo, and will be removed in the future modified: apptools/undo/abstract_command.py modified: apptools/undo/action/abstract_command_stack_action.py modified: apptools/undo/action/api.py modified: apptools/undo/action/command_action.py modified: apptools/undo/action/redo_action.py modified: apptools/undo/action/undo_action.py modified: apptools/undo/api.py modified: apptools/undo/command_stack.py modified: apptools/undo/i_command.py modified: apptools/undo/i_command_stack.py modified: apptools/undo/i_undo_manager.py deleted: apptools/undo/tests/__init__.py deleted: apptools/undo/tests/test_command_stack.py deleted: apptools/undo/tests/testing_commands.py modified: apptools/undo/undo_manager.py --- apptools/undo/abstract_command.py | 63 +--- .../action/abstract_command_stack_action.py | 72 +--- apptools/undo/action/api.py | 8 +- apptools/undo/action/command_action.py | 45 +-- apptools/undo/action/redo_action.py | 36 +- apptools/undo/action/undo_action.py | 34 +- apptools/undo/api.py | 14 +- apptools/undo/command_stack.py | 307 +----------------- apptools/undo/i_command.py | 52 +-- apptools/undo/i_command_stack.py | 78 +---- apptools/undo/i_undo_manager.py | 50 +-- apptools/undo/tests/__init__.py | 9 - apptools/undo/tests/test_command_stack.py | 164 ---------- apptools/undo/tests/testing_commands.py | 33 -- apptools/undo/undo_manager.py | 109 +------ 15 files changed, 26 insertions(+), 1048 deletions(-) delete mode 100644 apptools/undo/tests/__init__.py delete mode 100644 apptools/undo/tests/test_command_stack.py delete mode 100644 apptools/undo/tests/testing_commands.py diff --git a/apptools/undo/abstract_command.py b/apptools/undo/abstract_command.py index c428ce42a..ae8a5d085 100644 --- a/apptools/undo/abstract_command.py +++ b/apptools/undo/abstract_command.py @@ -12,65 +12,4 @@ # Description: # ------------------------------------------------------------------------------ -# Enthought library imports. -from traits.api import Any, HasTraits, Str, provides - -# Local imports. -from .i_command import ICommand - - -@provides(ICommand) -class AbstractCommand(HasTraits): - """The AbstractCommand class is an abstract base class that implements the - ICommand interface. - """ - - #### 'ICommand' interface ################################################# - - # This is the data on which the command operates. - data = Any - - # This is the name of the command as it will appear in any GUI element. It - # may include '&' which will be automatically removed whenever it is - # inappropriate. - name = Str - - ########################################################################### - # 'ICommand' interface. - ########################################################################### - - def do(self): - """This is called by the command stack to do the command and to return - any value. The command must save any state necessary for the 'redo()' - and 'undo()' methods to work. The class's __init__() must also ensure - that deep copies of any arguments are made if appropriate. It is - guaranteed that this will only ever be called once and that it will be - called before any call to 'redo()' or 'undo()'. - """ - - raise NotImplementedError - - def merge(self, other): - """This is called by the command stack to try and merge another - command with this one. True is returned if the commands were merged. - 'other' is the command that is about to be executed. If the commands - are merged then 'other' will discarded and not placed on the command - stack. A subsequent undo or redo of this modified command must have - the same effect as the two original commands. - """ - - # By default merges never happen. - return False - - def redo(self): - """This is called by the command stack to redo the command. Any - returned value will replace the value that the command stack references - from the original call to 'do()' or previous call to 'redo()'. - """ - - raise NotImplementedError - - def undo(self): - """ This is called by the command stack to undo the command. """ - - raise NotImplementedError +from pyface.undo.api import AbstractCommand diff --git a/apptools/undo/action/abstract_command_stack_action.py b/apptools/undo/action/abstract_command_stack_action.py index 46f30e830..ce1d87edb 100644 --- a/apptools/undo/action/abstract_command_stack_action.py +++ b/apptools/undo/action/abstract_command_stack_action.py @@ -12,72 +12,6 @@ # Description: # ------------------------------------------------------------------------------ -# Enthought library imports. -from pyface.action.api import Action -from traits.api import Instance - -# Local library imports -from ..i_undo_manager import IUndoManager - - -class AbstractCommandStackAction(Action): - """The abstract base class for all actions that operate on a command - stack. - """ - - #### 'AbstractCommandStackAction' interface ############################### - - # The undo manager. - undo_manager = Instance(IUndoManager) - - ########################################################################### - # 'object' interface. - ########################################################################### - - def __init__(self, **traits): - """ Initialise the instance. """ - - super(AbstractCommandStackAction, self).__init__(**traits) - - self.undo_manager.on_trait_event( - self._on_stack_updated, "stack_updated" - ) - - # Update the action to initialise it. - self._update_action() - - ########################################################################### - # 'Action' interface. - ########################################################################### - - def destroy(self): - """Called when the action is no longer required. - - By default this method does nothing, but this would be a great place to - unhook trait listeners etc. - - """ - - self.undo_manager.on_trait_event( - self._on_stack_updated, "stack_updated", remove=True - ) - - ########################################################################### - # Protected interface. - ########################################################################### - - def _update_action(self): - """ Update the state of the action. """ - - raise NotImplementedError - - ########################################################################### - # Private interface. - ########################################################################### - - def _on_stack_updated(self, stack): - """ Handle changes to the state of a command stack. """ - - # Ignore unless it is the active stack. - if stack is self.undo_manager.active_stack: - self._update_action() +from pyface.undo.action.abstract_command_stack_action import ( + AbstractCommandStackAction +) diff --git a/apptools/undo/action/api.py b/apptools/undo/action/api.py index 48d52295c..bbf9ef2bb 100644 --- a/apptools/undo/action/api.py +++ b/apptools/undo/action/api.py @@ -12,6 +12,8 @@ # Description: # ------------------------------------------------------------------------------ -from .command_action import CommandAction -from .redo_action import RedoAction -from .undo_action import UndoAction +from pyface.undo.action.command_action import ( + CommandAction, + RedoAction, + UndoAction, +) diff --git a/apptools/undo/action/command_action.py b/apptools/undo/action/command_action.py index 8684ca212..df3fda843 100644 --- a/apptools/undo/action/command_action.py +++ b/apptools/undo/action/command_action.py @@ -12,47 +12,4 @@ # Description: # ------------------------------------------------------------------------------ -# Enthought library imports. -from pyface.action.api import Action -from traits.api import Any, Callable, Instance -from ..i_command_stack import ICommandStack - - -class CommandAction(Action): - """The CommandAction class is an Action class that wraps undo/redo - commands. It is only useful for commands that do not take any arguments or - return any result. - """ - - #### 'CommandAction' interface ############################################ - - # The command to create when the action is performed. - command = Callable - - # The command stack onto which the command will be pushed when the action - # is performed. - command_stack = Instance(ICommandStack) - - # This is the data on which the command operates. - data = Any - - ########################################################################### - # 'Action' interface. - ########################################################################### - - def perform(self, event): - """This is reimplemented to push a new command instance onto the - command stack. - """ - - self.command_stack.push(self.command(data=self.data)) - - def _name_default(self): - """ This gets the action name from the command. """ - - if self.command: - name = self.command().name - else: - name = "" - - return name +from pyface.undo.action.api import CommandAction diff --git a/apptools/undo/action/redo_action.py b/apptools/undo/action/redo_action.py index 4cb426e5e..32c83ab88 100644 --- a/apptools/undo/action/redo_action.py +++ b/apptools/undo/action/redo_action.py @@ -12,38 +12,4 @@ # Description: # ------------------------------------------------------------------------------ -# Local imports. -from .abstract_command_stack_action import AbstractCommandStackAction - - -class RedoAction(AbstractCommandStackAction): - """An action that redos the last command undone of the active command - stack. - """ - - ########################################################################### - # 'Action' interface. - ########################################################################### - - def perform(self, event): - """ Perform the action. """ - - self.undo_manager.redo() - - ########################################################################### - # 'AbstractUndoAction' interface. - ########################################################################### - - def _update_action(self): - """ Update the state of the action. """ - - name = self.undo_manager.redo_name - - if name: - name = "&Redo " + name - self.enabled = True - else: - name = "&Redo" - self.enabled = False - - self.name = name +from pyface.undo.action.api import RedoAction diff --git a/apptools/undo/action/undo_action.py b/apptools/undo/action/undo_action.py index b6a273996..96f7de47b 100644 --- a/apptools/undo/action/undo_action.py +++ b/apptools/undo/action/undo_action.py @@ -12,36 +12,4 @@ # Description: # ------------------------------------------------------------------------------ -# Local imports. -from .abstract_command_stack_action import AbstractCommandStackAction - - -class UndoAction(AbstractCommandStackAction): - """ An action that undos the last command of the active command stack. """ - - ########################################################################### - # 'Action' interface. - ########################################################################### - - def perform(self, event): - """ Perform the action. """ - - self.undo_manager.undo() - - ########################################################################### - # 'AbstractUndoAction' interface. - ########################################################################### - - def _update_action(self): - """ Update the state of the action. """ - - name = self.undo_manager.undo_name - - if name: - name = "&Undo " + name - self.enabled = True - else: - name = "&Undo" - self.enabled = False - - self.name = name +from pyface.undo.action.api import UndoAction diff --git a/apptools/undo/api.py b/apptools/undo/api.py index ce6fb87f4..cc33f3fe1 100644 --- a/apptools/undo/api.py +++ b/apptools/undo/api.py @@ -12,9 +12,11 @@ # Description: # ------------------------------------------------------------------------------ -from .abstract_command import AbstractCommand -from .command_stack import CommandStack -from .i_command import ICommand -from .i_command_stack import ICommandStack -from .i_undo_manager import IUndoManager -from .undo_manager import UndoManager +from pyface.undo.api import ( + AbstractCommand, + CommandStack, + ICommand, + ICommandStack, + IUndoManager, + UndoManager, +) \ No newline at end of file diff --git a/apptools/undo/command_stack.py b/apptools/undo/command_stack.py index ca1cacc93..1baf0ce15 100644 --- a/apptools/undo/command_stack.py +++ b/apptools/undo/command_stack.py @@ -12,308 +12,5 @@ # Description: # ------------------------------------------------------------------------------ -# Enthought library imports. -from traits.api import ( - Bool, - HasTraits, - Instance, - Int, - List, - Property, - Str, - provides, -) - -# Local imports. -from .abstract_command import AbstractCommand -from .i_command import ICommand -from .i_command_stack import ICommandStack -from .i_undo_manager import IUndoManager - - -class _StackEntry(HasTraits): - """ The _StackEntry class is a single entry on a command stack. """ - - #### '_StackEntry' interface ############################################## - - # Set if the entry corresponds to a clean point on the stack. - clean = Bool(False) - - # The command instance. - command = Instance(ICommand) - - # The sequence number of the entry. - sequence_nr = Int - - -class _MacroCommand(AbstractCommand): - """ The _MacroCommand class is an internal command that handles macros. """ - - #### '_MacroCommand' interface ############################################ - - # The commands that make up this macro. - macro_commands = List(Instance(ICommand)) - - ########################################################################### - # 'ICommand' interface. - ########################################################################### - - def do(self): - """ Invoke the command. """ - - # This is a dummy. - return None - - def merge(self, other): - """ Try and merge a command. """ - - if len(self.macro_commands) == 0: - merged = False - else: - merged = self.macro_commands[-1].merge(other) - - return merged - - def redo(self): - """ Redo the sub-commands. """ - - for cmd in self.macro_commands: - cmd.redo() - - # Macros cannot return values. - return None - - def undo(self): - """ Undo the sub-commands. """ - - for cmd in self.macro_commands: - cmd.undo() - - -@provides(ICommandStack) -class CommandStack(HasTraits): - """The CommandStack class is the default implementation of the - ICommandStack interface. - """ - - #### 'ICommandStack' interface ############################################ - - # This is the clean state of the stack. Its value changes as commands are - # undone and redone. It can also be explicity set to mark the current - # stack position as being clean (when the data is saved to disk for - # example). - clean = Property(Bool) - - # This is the name of the command that can be redone. It will be empty if - # there is no command that can be redone. It is maintained by the undo - # stack. - redo_name = Property(Str) - - # This is the undo manager that manages this stack. - undo_manager = Instance(IUndoManager) - - # This is the name of the command that can be undone. It will be empty if - # there is no command that can be undone. It is maintained by the undo - # stack. - undo_name = Property(Str) - - #### Private interface #################################################### - - # The current index into the stack (ie. the last command that was done). - _index = Int(-1) - - # The current macro stack. - _macro_stack = List(Instance(_MacroCommand)) - - # The stack itself. - _stack = List(Instance(_StackEntry)) - - ########################################################################### - # 'ICommandStack' interface. - ########################################################################### - - def begin_macro(self, name): - """This begins a macro by creating an empty command with the given - 'name'. All subsequent calls to 'push()' create commands that will be - children of the empty command until the next call to 'end_macro()'. - Macros may be nested. The stack is disabled (ie. nothing can be undone - or redone) while a macro is being created (ie. while there is an - outstanding 'end_macro()' call). - """ - - command = _MacroCommand(name=name) - self.push(command) - self._macro_stack.append(command) - - def clear(self): - """This clears the stack, without undoing or redoing any commands, and - leaves the stack in a clean state. It is typically used when all - changes to the data have been abandoned. - """ - - self._index = -1 - self._stack = [] - self._macro_stack = [] - - self.undo_manager.stack_updated = self - - def end_macro(self): - """ This ends a macro. """ - - try: - self._macro_stack.pop() - except IndexError: - pass - - def push(self, command): - """This executes a command and saves it on the command stack so that - it can be subsequently undone and redone. 'command' is an instance - that implements the ICommand interface. Its 'do()' method is called - to execute the command. If any value is returned by 'do()' then it is - returned by 'push()'. - """ - - # See if the command can be merged with the previous one. - if len(self._macro_stack) == 0: - if self._index >= 0: - merged = self._stack[self._index].command.merge(command) - else: - merged = False - else: - merged = self._macro_stack[-1].merge(command) - - # Increment the global sequence number. - if not merged: - self.undo_manager.sequence_nr += 1 - - # Execute the command. - result = command.do() - - # Do nothing more if the command was merged. - if merged: - return result - - # Only update the command stack if there is no current macro. - if len(self._macro_stack) == 0: - # Remove everything on the stack after the last command that was - # done. - self._index += 1 - del self._stack[self._index:] - - # Create a new stack entry and add it to the stack. - entry = _StackEntry( - command=command, sequence_nr=self.undo_manager.sequence_nr - ) - - self._stack.append(entry) - self.undo_manager.stack_updated = self - else: - # Add the command to the parent macro command. - self._macro_stack[-1].macro_commands.append(command) - - return result - - def redo(self, sequence_nr=0): - """If 'sequence_nr' is 0 then the last command that was undone is - redone and any result returned. Otherwise commands are redone up to - and including the given 'sequence_nr' and any result of the last of - these is returned. - """ - - # Make sure a redo is valid in the current context. - if self.redo_name == "": - return None - - if sequence_nr == 0: - result = self._redo_one() - else: - result = None - - while self._index + 1 < len(self._stack): - if self._stack[self._index + 1].sequence_nr > sequence_nr: - break - - result = self._redo_one() - - self.undo_manager.stack_updated = self - - return result - - def undo(self, sequence_nr=0): - """If 'sequence_nr' is 0 then the last command is undone. Otherwise - commands are undone up to and including the given 'sequence_nr'. - """ - - # Make sure an undo is valid in the current context. - if self.undo_name == "": - return - - if sequence_nr == 0: - self._undo_one() - else: - while self._index >= 0: - if self._stack[self._index].sequence_nr <= sequence_nr: - break - - self._undo_one() - - self.undo_manager.stack_updated = self - - ########################################################################### - # Private interface. - ########################################################################### - - def _redo_one(self): - """ Redo the command at the current index and return the result. """ - - self._index += 1 - entry = self._stack[self._index] - - return entry.command.redo() - - def _undo_one(self): - """ Undo the command at the current index. """ - - entry = self._stack[self._index] - self._index -= 1 - - entry.command.undo() - - def _get_clean(self): - """ Get the clean state of the stack. """ - - if self._index >= 0: - clean = self._stack[self._index].clean - else: - clean = True - - return clean - - def _set_clean(self, clean): - """ Set the clean state of the stack. """ - - if self._index >= 0: - self._stack[self._index].clean = clean - - def _get_redo_name(self): - """ Get the name of the redo command, if any. """ - - redo_name = "" - - if len(self._macro_stack) == 0 and self._index + 1 < len(self._stack): - redo_name = self._stack[self._index + 1].command.name.replace( - "&", "" - ) - - return redo_name - - def _get_undo_name(self): - """ Get the name of the undo command, if any. """ - - undo_name = "" - - if len(self._macro_stack) == 0 and self._index >= 0: - command = self._stack[self._index].command - undo_name = command.name.replace("&", "") - - return undo_name +from pyface.undo.api import CommandStack +from pyface.undo.command_stack import _MacroCommand, _StackEntry diff --git a/apptools/undo/i_command.py b/apptools/undo/i_command.py index ca7c2217b..e659f9413 100644 --- a/apptools/undo/i_command.py +++ b/apptools/undo/i_command.py @@ -12,54 +12,4 @@ # Description: # ------------------------------------------------------------------------------ - -# Enthought library imports. -from traits.api import Any, Interface, Str - - -class ICommand(Interface): - """The command interface. The state of the data can be changed by passing - an instance that implements this interface to the 'push()' method of a - command stack along with any arguments. - """ - - #### 'ICommand' interface ################################################# - - # This is the data on which the command operates. - data = Any - - # This is the name of the command as it will appear in any GUI element. It - # may include '&' which will be automatically removed whenever it is - # inappropriate. - name = Str - - ########################################################################### - # 'ICommand' interface. - ########################################################################### - - def do(self): - """This is called by the command stack to do the command and to return - any value. The command must save any state necessary for the 'redo()' - and 'undo()' methods to work. The class's __init__() must also ensure - that deep copies of any arguments are made if appropriate. It is - guaranteed that this will only ever be called once and that it will be - called before any call to 'redo()' or 'undo()'. - """ - - def merge(self, other): - """This is called by the command stack to try and merge another - command with this one. True is returned if the commands were merged. - 'other' is the command that is about to be executed. If the commands - are merged then 'other' will discarded and not placed on the command - stack. A subsequent undo or redo of this modified command must have - the same effect as the two original commands. - """ - - def redo(self): - """This is called by the command stack to redo the command. Any - returned value will replace the value that the command stack references - from the original call to 'do()' or previous call to 'redo()'. - """ - - def undo(self): - """ This is called by the command stack to undo the command. """ +from pyface.undo.api import ICommand diff --git a/apptools/undo/i_command_stack.py b/apptools/undo/i_command_stack.py index 2c923fc5f..cb3cd4deb 100644 --- a/apptools/undo/i_command_stack.py +++ b/apptools/undo/i_command_stack.py @@ -12,80 +12,4 @@ # Description: # ------------------------------------------------------------------------------ -# Enthought library imports. -from traits.api import Bool, Instance, Interface, Str - -# Local imports. -from .i_undo_manager import IUndoManager - - -class ICommandStack(Interface): - """The command stack interface. A command stack is responsible for - managing the changes to a data model and recording those changes so that - they can be undone or redone. - """ - - #### 'ICommandStack' interface ############################################ - - # This is the clean state of the stack. Its value changes as commands are - # undone and redone. It can also be explicity set to mark the current - # stack position as being clean (when the data is saved to disk for - # example). - clean = Bool - - # This is the name of the command that can be redone. It will be empty if - # there is no command that can be redone. It is maintained by the undo - # stack. - redo_name = Str - - # This is the undo manager that manages this stack. - undo_manager = Instance(IUndoManager) - - # This is the name of the command that can be undone. It will be empty if - # there is no command that can be undone. It is maintained by the undo - # stack. - undo_name = Str - - ########################################################################### - # 'ICommandStack' interface. - ########################################################################### - - def begin_macro(self, name): - """This begins a macro by creating an empty command with the given - 'name'. The commands passed to all subsequent calls to 'push()' will - be contained in the macro until the next call to 'end_macro()'. Macros - may be nested. The stack is disabled (ie. nothing can be undone or - redone) while a macro is being created (ie. while there is an - outstanding 'end_macro()' call). - """ - - def clear(self): - """This clears the stack, without undoing or redoing any commands, and - leaves the stack in a clean state. It is typically used when all - changes to the data have been abandoned. - """ - - def end_macro(self): - """ This ends a macro. """ - - def push(self, command): - """This executes a command and saves it on the command stack so that - it can be subsequently undone and redone. 'command' is an instance - that implements the ICommand interface. Its 'do()' method is called - to execute the command. If any value is returned by 'do()' then it is - returned by 'push()'. The command stack will keep a reference to the - result so that it can recognise it as an argument to a subsequent - command (which allows a script to properly save a result needed later). - """ - - def redo(self, sequence_nr=0): - """If 'sequence_nr' is 0 then the last command that was undone is - redone and any result returned. Otherwise commands are redone up to - and including the given 'sequence_nr' and any result of the last of - these is returned. - """ - - def undo(self, sequence_nr=0): - """If 'sequence_nr' is 0 then the last command is undone. Otherwise - commands are undone up to and including the given 'sequence_nr'. - """ +from pyface.undo.api import ICommandStack diff --git a/apptools/undo/i_undo_manager.py b/apptools/undo/i_undo_manager.py index ff15e7052..780a55516 100644 --- a/apptools/undo/i_undo_manager.py +++ b/apptools/undo/i_undo_manager.py @@ -12,52 +12,4 @@ # Description: # ------------------------------------------------------------------------------ -# Enthought library imports. -from traits.api import Bool, Event, Instance, Int, Interface, Str - - -class IUndoManager(Interface): - """The undo manager interface. An undo manager is responsible for one or - more command stacks. Typically an application would have a single undo - manager. - """ - - #### 'IUndoManager' interface ############################################# - - # This is the currently active command stack and may be None. Typically it - # is set when some sort of editor becomes active. - active_stack = Instance("apptools.undo.api.ICommandStack") - - # This reflects the clean state of the currently active command stack. It - # is intended to support a "document modified" indicator in the GUI. It is - # maintained by the undo manager. - active_stack_clean = Bool - - # This is the name of the command that can be redone. It will be empty if - # there is no command that can be redone. It is maintained by the undo - # manager. - redo_name = Str - - # This is the sequence number of the next command to be performed. It is - # incremented immediately before a command is invoked (by its 'do()' - # method). - sequence_nr = Int - - # This event is fired when the index of a command stack changes. Note that - # it may not be the active stack. - stack_updated = Event(Instance("apptools.undo.api.ICommandStack")) - - # This is the name of the command that can be undone. It will be empty if - # there is no command that can be undone. It is maintained by the undo - # manager. - undo_name = Str - - ########################################################################### - # 'IUndoManager' interface. - ########################################################################### - - def redo(self): - """ Redo the last undone command of the active command stack. """ - - def undo(self): - """ Undo the last command of the active command stack. """ +from pyface.undo.api import IUndoManager diff --git a/apptools/undo/tests/__init__.py b/apptools/undo/tests/__init__.py deleted file mode 100644 index 26ce110f3..000000000 --- a/apptools/undo/tests/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# (C) Copyright 2005-2021 Enthought, Inc., Austin, TX -# All rights reserved. -# -# This software is provided without warranty under the terms of the BSD -# license included in LICENSE.txt and may be redistributed only under -# the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! diff --git a/apptools/undo/tests/test_command_stack.py b/apptools/undo/tests/test_command_stack.py deleted file mode 100644 index 163c52599..000000000 --- a/apptools/undo/tests/test_command_stack.py +++ /dev/null @@ -1,164 +0,0 @@ -# (C) Copyright 2005-2021 Enthought, Inc., Austin, TX -# All rights reserved. -# -# This software is provided without warranty under the terms of the BSD -# license included in LICENSE.txt and may be redistributed only under -# the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! - -from contextlib import contextmanager -import unittest - -from apptools.undo.api import CommandStack, UndoManager -from apptools.undo.tests.testing_commands import SimpleCommand, UnnamedCommand - - -class TestCommandStack(unittest.TestCase): - def setUp(self): - self.stack = CommandStack() - undo_manager = UndoManager() - self.stack.undo_manager = undo_manager - - self.command = SimpleCommand() - - # Command pushing tests --------------------------------------------------- - - def test_empty_command_stack(self): - with self.assert_n_commands_pushed(self.stack, 0): - pass - - def test_1_command_pushed(self): - with self.assert_n_commands_pushed(self.stack, 1): - self.stack.push(self.command) - - def test_n_command_pushed(self): - n = 4 - with self.assert_n_commands_pushed(self.stack, n): - for i in range(n): - self.stack.push(self.command) - - # Undo/Redo tests --------------------------------------------------------- - - def test_undo_1_command(self): - with self.assert_n_commands_pushed_and_undone(self.stack, 1): - self.stack.push(self.command) - self.assertEqual(self.stack.undo_name, self.command.name) - self.stack.undo() - - def test_undo_n_command(self): - n = 4 - with self.assert_n_commands_pushed_and_undone(self.stack, n): - for i in range(n): - self.stack.push(self.command) - - for i in range(n): - self.stack.undo() - - def test_undo_unnamed_command(self): - unnamed_command = UnnamedCommand() - with self.assert_n_commands_pushed(self.stack, 1): - self.stack.push(unnamed_command) - - # But the command cannot be undone because it has no name - self.assertEqual(self.stack.undo_name, "") - # This is a no-op - self.stack.undo() - - def test_undo_redo_1_command(self): - with self.assert_n_commands_pushed(self.stack, 1): - self.stack.push(self.command) - self.stack.undo() - self.stack.redo() - - # Macro tests ------------------------------------------------------------- - - def test_define_macro(self): - with self.assert_n_commands_pushed(self.stack, 1): - add_macro(self.stack, num_commands=2) - - def test_undo_macro(self): - with self.assert_n_commands_pushed_and_undone(self.stack, 1): - # The 2 pushes are viewed as 1 command - add_macro(self.stack, num_commands=2) - self.stack.undo() - - # Cleanliness tests ------------------------------------------------------- - - def test_empty_stack_is_clean(self): - self.assertTrue(self.stack.clean) - - def test_non_empty_stack_is_dirty(self): - self.stack.push(self.command) - self.assertFalse(self.stack.clean) - - def test_make_clean(self): - # This makes it dirty by default - self.stack.push(self.command) - # Make the current tip of the stack clean - self.stack.clean = True - self.assertTrue(self.stack.clean) - - def test_make_dirty(self): - # Start from a clean state: - self.stack.push(self.command) - self.stack.clean = True - - self.stack.clean = False - self.assertFalse(self.stack.clean) - - def test_save_push_undo_is_clean(self): - self.stack.push(self.command) - - self.stack.clean = True - self.stack.push(self.command) - self.stack.undo() - self.assertTrue(self.stack.clean) - - def test_save_push_save_undo_is_clean(self): - self.stack.push(self.command) - - self.stack.clean = True - self.stack.push(self.command) - self.stack.clean = True - self.stack.undo() - self.assertTrue(self.stack.clean) - - def test_push_undo_save_redo_is_dirty(self): - self.stack.push(self.command) - self.stack.undo() - self.stack.clean = True - self.stack.redo() - self.assertFalse(self.stack.clean) - - # Assertion helpers ------------------------------------------------------- - - @contextmanager - def assert_n_commands_pushed(self, stack, n): - current_length = len(stack._stack) - yield - # N commands have been pushed... - self.assertEqual(len(stack._stack), current_length + n) - # ... and the state is at the tip of the stack... - self.assertEqual(stack._index, current_length + n - 1) - - @contextmanager - def assert_n_commands_pushed_and_undone(self, stack, n): - current_length = len(stack._stack) - yield - # N commands have been pushed and then reverted. The stack still - # contains the commands... - self.assertEqual(len(stack._stack), n) - # ... but we are back to the initial (clean) state - self.assertEqual(stack._index, current_length - 1) - - -def add_macro(stack, num_commands=2): - command = SimpleCommand() - stack.begin_macro("Increment n times") - try: - for i in range(num_commands): - stack.push(command) - finally: - stack.end_macro() diff --git a/apptools/undo/tests/testing_commands.py b/apptools/undo/tests/testing_commands.py deleted file mode 100644 index 53a8794ea..000000000 --- a/apptools/undo/tests/testing_commands.py +++ /dev/null @@ -1,33 +0,0 @@ -# (C) Copyright 2005-2021 Enthought, Inc., Austin, TX -# All rights reserved. -# -# This software is provided without warranty under the terms of the BSD -# license included in LICENSE.txt and may be redistributed only under -# the conditions described in the aforementioned license. The license -# is also available online at http://www.enthought.com/licenses/BSD.txt -# -# Thanks for using Enthought open source! - -from traits.api import Int -from apptools.undo.api import AbstractCommand - - -class SimpleCommand(AbstractCommand): - """ Simplest command possible operating on an integer. """ - - name = "Increment by 1" - - data = Int - - def do(self): - self.redo() - - def redo(self): - self.data += 1 - - def undo(self): - self.data -= 1 - - -class UnnamedCommand(SimpleCommand): - name = "" diff --git a/apptools/undo/undo_manager.py b/apptools/undo/undo_manager.py index ed6aa5740..67f3fd7df 100644 --- a/apptools/undo/undo_manager.py +++ b/apptools/undo/undo_manager.py @@ -12,111 +12,4 @@ # Description: # ------------------------------------------------------------------------------ -# Enthought library imports. -from traits.api import ( - Bool, - Event, - HasTraits, - Instance, - Int, - Property, - Str, - provides, -) - -# Local imports. -from .i_undo_manager import IUndoManager - - -@provides(IUndoManager) -class UndoManager(HasTraits): - """The UndoManager class is the default implementation of the - IUndoManager interface. - """ - - #### 'IUndoManager' interface ############################################# - - # This is the currently active command stack and may be None. Typically it - # is set when some sort of editor becomes active. - active_stack = Instance("apptools.undo.api.ICommandStack") - - # This reflects the clean state of the currently active command stack. It - # is intended to support a "document modified" indicator in the GUI. It is - # maintained by the undo manager. - active_stack_clean = Property(Bool) - - # This is the name of the command that can be redone. It will be empty if - # there is no command that can be redone. It is maintained by the undo - # manager. - redo_name = Property(Str) - - # This is the sequence number of the next command to be performed. It is - # incremented immediately before a command is invoked (by its 'do()' - # method). - sequence_nr = Int - - # This event is fired when the index of a command stack changes. The value - # of the event is the stack that has changed. Note that it may not be the - # active stack. - stack_updated = Event - - # This is the name of the command that can be undone. It will be empty if - # there is no command that can be undone. It is maintained by the undo - # manager. - undo_name = Property(Str) - - ########################################################################### - # 'IUndoManager' interface. - ########################################################################### - - def redo(self): - """ Redo the last undone command of the active command stack. """ - - if self.active_stack is not None: - self.active_stack.redo() - - def undo(self): - """ Undo the last command of the active command stack. """ - - if self.active_stack is not None: - self.active_stack.undo() - - ########################################################################### - # Private interface. - ########################################################################### - - def _active_stack_changed(self, new): - """ Handle a different stack becoming active. """ - - # Pretend that the stack contents have changed. - self.stack_updated = new - - def _get_active_stack_clean(self): - """ Get the current clean state. """ - - if self.active_stack is None: - active_stack_clean = True - else: - active_stack_clean = self.active_stack.clean - - return active_stack_clean - - def _get_redo_name(self): - """ Get the current redo name. """ - - if self.active_stack is None: - redo_name = "" - else: - redo_name = self.active_stack.redo_name - - return redo_name - - def _get_undo_name(self): - """ Get the current undo name. """ - - if self.active_stack is None: - undo_name = "" - else: - undo_name = self.active_stack.undo_name - - return undo_name +from pyface.undo.api import UndoManager From 0d8fa04bce5cb4867e3da798f137e55be2a78515 Mon Sep 17 00:00:00 2001 From: Sai Rahul Poruri Date: Fri, 8 Jan 2021 17:03:21 +0000 Subject: [PATCH 2/3] DOC : Add changelog entry new file: docs/releases/upcoming/272.deprecation.rst --- docs/releases/upcoming/272.deprecation.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/releases/upcoming/272.deprecation.rst diff --git a/docs/releases/upcoming/272.deprecation.rst b/docs/releases/upcoming/272.deprecation.rst new file mode 100644 index 000000000..1b1e2d4af --- /dev/null +++ b/docs/releases/upcoming/272.deprecation.rst @@ -0,0 +1 @@ +Import from pyface.undo.* instead of redefining classes in apptools.undo.* (#272) \ No newline at end of file From b5b61cf7c69d71ce8072af9e85ce132d8324feba Mon Sep 17 00:00:00 2001 From: Sai Rahul Poruri Date: Fri, 8 Jan 2021 17:14:59 +0000 Subject: [PATCH 3/3] FIX : Flake8 Ignore F401 errors anywhere in apptools.undo.* modified: apptools/undo/api.py modified: setup.cfg --- apptools/undo/api.py | 2 +- setup.cfg | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/apptools/undo/api.py b/apptools/undo/api.py index cc33f3fe1..036cc9812 100644 --- a/apptools/undo/api.py +++ b/apptools/undo/api.py @@ -19,4 +19,4 @@ ICommandStack, IUndoManager, UndoManager, -) \ No newline at end of file +) diff --git a/setup.cfg b/setup.cfg index 3c236f95f..c0076014c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,6 +5,4 @@ per-file-ignores = */__init__.py:F401, # ignore undo related errors because it has been copied to pyface and will # soon be deprecated. See enthought/apptools#243 - */undo/*:H101, - apptools/undo/api.py:F401,H101, - apptools/undo/action/api.py:F401,H101 + */undo/*:F401,H101