[kupfer: 33/53] Move commandexec.py into kupfer.core
- From: Ulrik Sverdrup <usverdrup src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [kupfer: 33/53] Move commandexec.py into kupfer.core
- Date: Thu, 24 Mar 2011 16:33:19 +0000 (UTC)
commit 316a45e7dc9239af41567195f1731ec40a21f8e5
Author: Ulrik Sverdrup <ulrik sverdrup gmail com>
Date: Thu Mar 24 17:22:38 2011 +0100
Move commandexec.py into kupfer.core
commandexec is deprecated in the Plugin API, but a placeholder module
is left where it once was, so that we are still compatible (a short
while) with old users of commandexec.
kupfer/commandexec.py | 399 +---------------------------------------
kupfer/core/commandexec.py | 398 +++++++++++++++++++++++++++++++++++++++
kupfer/core/data.py | 10 +-
kupfer/plugin/core/internal.py | 2 +-
kupfer/plugin/higherorder.py | 2 +-
kupfer/plugin/triggers.py | 2 +-
6 files changed, 412 insertions(+), 401 deletions(-)
---
diff --git a/kupfer/commandexec.py b/kupfer/commandexec.py
index d492f65..1f59139 100644
--- a/kupfer/commandexec.py
+++ b/kupfer/commandexec.py
@@ -1,398 +1,11 @@
"""
-The main logic for executing constructed commands.
+This file only exists for backwards compatibility!
-A command is normally a tuple of (object, action, indirect object).
-Where, of course, the indirect object is often not needed (in this module we
-then pass None in its stead).
-
-This code was once a shining machine; While adding the "comma trick" and
-support for "multiple dispatch" was easy in the rest of the program, it shed
-its casualties here: While the main process is simple, we deal here with all
-the exceptions that are, at the moment, tacked on.
-
-The ActionExecutionContext (ACE) keeps track of its nested invocation, so that
-we can catch the results of commands executed inside other commands. The
-delegation mechanism allows a user of the ACE to indicate that the result of
-the command should be passed on from the earlier (more nested) invocation.
-
-Multiple dispatch is straightforward if the action implements the multiple
-dispatch protocol. Is the protocol not implemented, the command is simply
-"multiplied out": executed once for each object, or once for each combination
-of object and indirect object.
-
-With multiple command execution (and delegation), we must then process and
-merge multiple return values.
+For new plugins, you probably should not use ``commandexec``
+at all, but instead implement ``Action.wants_context()``
"""
-from __future__ import with_statement
-
-import collections
-import contextlib
-import itertools
-import sys
-
-import gobject
-
-from kupfer import pretty
-from kupfer import task
-from kupfer import uiutils
-from kupfer.objects import OperationError
-from kupfer.obj.objects import SourceLeaf
-from kupfer.obj.sources import MultiSource
-from kupfer.obj.compose import MultipleLeaf
-
-RESULT_NONE, RESULT_OBJECT, RESULT_SOURCE, RESULT_ASYNC = (1, 2, 3, 4)
-RESULTS_SYNC = (RESULT_OBJECT, RESULT_SOURCE)
-
-_MAX_LAST_RESULTS = 10
-
-_action_exec_context = None
-def DefaultActionExecutionContext():
- global _action_exec_context
- if _action_exec_context is None:
- _action_exec_context = ActionExecutionContext()
- return _action_exec_context
-
-class ActionExecutionError (Exception):
- pass
-
-def _get_leaf_members(leaf):
- """
- Return an iterator to members of @leaf, if it is a multiple leaf
- """
- # NOTE : This function duplicates one in core/actionlogic.py
- try:
- return leaf.get_multiple_leaf_representation()
- except AttributeError:
- return (leaf, )
-
-def _is_multiple(leaf):
- return hasattr(leaf, "get_multiple_leaf_representation")
-
-def _wants_context(action):
- return action.wants_context()
-
-def activate_action(context, obj, action, iobj):
- """ Activate @action in simplest manner """
- kwargs = {}
- if _wants_context(action):
- kwargs['ctx'] = context
- if not _is_multiple(obj) and not _is_multiple(iobj):
- return _activate_action_single(obj, action, iobj, kwargs)
- else:
- return _activate_action_multiple(obj, action, iobj, kwargs)
-
-def _activate_action_single(obj, action, iobj, kwargs):
- if action.requires_object():
- ret = action.activate(obj, iobj, **kwargs)
- else:
- ret = action.activate(obj, **kwargs)
- return ret
-
-def _activate_action_multiple(obj, action, iobj, kwargs):
- if not hasattr(action, "activate_multiple"):
- iobjs = (None, ) if iobj is None else _get_leaf_members(iobj)
- return _activate_action_multiple_multiplied(_get_leaf_members(obj),
- action, iobjs, kwargs)
-
- if action.requires_object():
- ret = action.activate_multiple(_get_leaf_members(obj),
- _get_leaf_members(iobj), **kwargs)
- else:
- ret = action.activate_multiple(_get_leaf_members(obj), **kwargs)
- return ret
-
-def _activate_action_multiple_multiplied(objs, action, iobjs, kwargs):
- """
- Multiple dispatch by "mulitplied" invocation of the simple activation
-
- Return an iterable of the return values.
- """
- rets = []
- for L in objs:
- for I in iobjs:
- ret = _activate_action_single(L, action, I, kwargs)
- rets.append(ret)
- ctx = DefaultActionExecutionContext()
- ret = ctx._combine_action_result_multiple(action, rets)
- return ret
-
-def parse_action_result(action, ret):
- """Return result type for @action and return value @ret"""
- def valid_result(ret):
- return ret and (not hasattr(ret, "is_valid") or ret.is_valid())
-
- # handle actions returning "new contexts"
- res = RESULT_NONE
- if action.is_factory() and valid_result(ret):
- res = RESULT_SOURCE
- if action.has_result() and valid_result(ret):
- res = RESULT_OBJECT
- elif action.is_async() and valid_result(ret):
- res = RESULT_ASYNC
- return res
-
-class ExecutionToken (object):
- """
- A token object that an ``Action`` carries with it
- from ``activate``.
-
- Must be used for access to current execution context,
- and to access the environment.
- """
- def __init__(self, aectx, async_token, ui_ctx):
- self._aectx = aectx
- self._token = async_token
- self._ui_ctx = ui_ctx
-
- def register_late_result(self, result_object, show=True):
- self._aectx.register_late_result(self._token, result_object, show=show)
-
- def register_late_error(self, exc_info=None):
- self._aectx.register_late_error(self._token, exc_info)
-
- def delegated_run(self, *objs):
- return self._aectx.run(*objs, delegate=True, ui_ctx=self._ui_ctx)
-
- @property
- def environment(self):
- """This is a property for the current environment,
- acess env variables like this::
-
- ctx.environment.get_timestamp()
-
- Raises RuntimeError when not available.
- """
- if self._ui_ctx is not None:
- return self._ui_ctx
- else:
- raise RuntimeError("Environment Context not available")
-
-class ActionExecutionContext (gobject.GObject, pretty.OutputMixin):
- """
- command-result (result_type, result)
- Emitted when a command is carried out, with its resulting value
- """
- __gtype_name__ = "ActionExecutionContext"
- def __init__(self):
- gobject.GObject.__init__(self)
- self.task_runner = task.TaskRunner(end_on_finish=False)
- self._nest_level = 0
- self._delegate = False
- self._command_counter = itertools.count()
- self.last_command_id = -1
- self.last_command = None
- self.last_executed_command = None
- self.last_results = collections.deque([], _MAX_LAST_RESULTS)
-
- def check_valid(self, obj, action, iobj):
- pass
-
- @contextlib.contextmanager
- def _nesting(self):
- try:
- self._nest_level += 1
- self._delegate = False
- yield
- finally:
- self._nest_level -= 1
-
- def _is_nested(self):
- return self._nest_level
-
- @contextlib.contextmanager
- def _error_conversion(self, *cmdtuple):
- try:
- yield
- except OperationError:
- self._do_error_conversion(cmdtuple, sys.exc_info())
-
- def _do_error_conversion(self, cmdtuple, exc_info):
- if not self.operation_error(exc_info, cmdtuple):
- raise
- etype, value, tb = exc_info
- raise ActionExecutionError, value, tb
-
- def get_async_token(self):
- """Get an action execution for current execution
-
- Return a token for the currently active command execution.
- The token must be used for posting late results or late errors.
- """
- return (self.last_command_id, self.last_executed_command)
-
- def make_execution_token(self, ui_ctx):
- """
- Return an ExecutionToken for @self and @ui_ctx
- """
- return ExecutionToken(self, self.get_async_token(), ui_ctx)
-
- def operation_error(self, exc_info, cmdtuple):
- "Error when executing action. Return True when error was handled"
- if self._is_nested():
- return
- etype, value, tb = exc_info
- obj, action, iobj = cmdtuple
- # TRANS: When an error occurs in an action to be carried out,
- # TRANS: then this is the heading of the error notification
- return uiutils.show_notification(
- _("Could not to carry out '%s'") % action,
- unicode(value), icon_name="kupfer")
-
- def register_late_error(self, token, exc_info=None):
- "Register an error in exc_info. The error must be an OperationError"
- if exc_info is None:
- exc_info = sys.exc_info()
- if isinstance(exc_info, Exception):
- exc_info = (type(exc_info), exc_info, None)
- command_id, cmdtuple = token
- self._do_error_conversion(cmdtuple, exc_info)
-
- def register_late_result(self, token, result, show=True):
- """Register a late result
-
- Result must be a Leaf (as in result object, not factory or async)
-
- If @show, possibly display the result to the user.
- """
- self.output_debug("Late result", repr(result), "for", token)
- command_id, (_ign1, action, _ign2) = token
- if result is None:
- raise ActionExecutionError("Late result from %s was None" % action)
- res_name = unicode(result)
- res_desc = result.get_description()
- if res_desc:
- description = "%s (%s)" % (res_name, res_desc)
- else:
- description = res_name
- uiutils.show_notification(_('"%s" produced a result') % action,
- description)
-
- # If only registration was requsted, remove the command id info
- if not show:
- command_id = -1
- self.emit("late-command-result", command_id, RESULT_OBJECT, result)
- self._append_result(RESULT_OBJECT, result)
-
- def _append_result(self, res_type, result):
- if res_type == RESULT_OBJECT:
- self.last_results.append(result)
-
- def run(self, obj, action, iobj, delegate=False, ui_ctx=None):
- """
- Activate the command (obj, action, iobj), where @iobj may be None
-
- Return a tuple (DESCRIPTION; RESULT)
-
- If a command carries out another command as part of its execution,
- and wishes to delegate to it, pass True for @delegate.
- """
- self.last_command_id = self._command_counter.next()
- self.last_executed_command = (obj, action, iobj)
-
- if not action or not obj:
- raise ActionExecutionError("Primary Object and Action required")
- if iobj is None and action.requires_object():
- raise ActionExecutionError("%s requires indirect object" % action)
-
- # The execution token object for the current invocation
- execution_token = self.make_execution_token(ui_ctx)
- with self._error_conversion(obj, action, iobj):
- with self._nesting():
- ret = activate_action(execution_token, obj, action, iobj)
-
- # remember last command, but not delegated commands.
- if not delegate:
- self.last_command = self.last_executed_command
-
- # Delegated command execution was previously requested: we take
- # the result of the nested execution context
- if self._delegate:
- res, ret = ret
- return self._return_result(res, ret)
-
- res = parse_action_result(action, ret)
- if res == RESULT_ASYNC:
- # Register the task then "clear" the result
- self.output_debug("Registering async task", ret)
- self.task_runner.add_task(ret)
- res, ret = RESULT_NONE, None
-
- # Delegated command execution was requested: we pass
- # through the result of the action to the parent execution context
- if delegate and self._is_nested():
- self._delegate = True
-
- return self._return_result(res, ret)
-
- def _return_result(self, res, ret):
- if not self._is_nested():
- self._append_result(res, ret)
- self.emit("command-result", res, ret)
- return res, ret
-
-
- def _combine_action_result_multiple(self, action, retvals):
- self.output_debug("Combining", repr(action), retvals,
- "delegate=%s" % self._delegate)
-
- def _make_retvalue(res, values):
- "Construct a return value for type res"
- if res == RESULT_SOURCE:
- return values[0] if len(values) == 1 else MultiSource(values)
- if res == RESULT_OBJECT:
- return values[0] if len(values) == 1 else MultipleLeaf(values)
- if res == RESULT_ASYNC:
- # Register all tasks now, and return None upwards
- for task in values:
- self.output_debug("Registering async task", task)
- self.task_runner.add_task(task)
- return None
-
- if not self._delegate:
- values = []
- res = RESULT_NONE
- for ret in retvals:
- res_type = parse_action_result(action, ret)
- if res_type != RESULT_NONE:
- values.append(ret)
- res = res_type
- return _make_retvalue(res, values)
- else:
- # Re-parse result values
- res = RESULT_NONE
- resmap = {}
- for ret in retvals:
- if ret is None:
- continue
- res_type, ret_obj = ret
- if res_type != RESULT_NONE:
- res = res_type
- resmap.setdefault(res_type, []).append(ret_obj)
-
- # register tasks
- tasks = resmap.pop(RESULT_ASYNC, [])
- _make_retvalue(RESULT_ASYNC, tasks)
-
- if len(resmap) == 1:
- # Return the only of the Source or Object case
- key, values = resmap.items()[0]
- return key, _make_retvalue(key, values)
- elif len(resmap) > 1:
- # Put the source in a leaf and return a multiple leaf
- source = _make_retvalue(RESULT_SOURCE, resmap[RESULT_SOURCE])
- objects = resmap[RESULT_OBJECT]
- objects.append(SourceLeaf(source))
- return RESULT_OBJECT, _make_retvalue(RESULT_OBJECT, objects)
- return RESULT_NONE, None
-
+from kupfer.core.commandexec import *
-# Action result type, action result
-gobject.signal_new("command-result", ActionExecutionContext,
- gobject.SIGNAL_RUN_LAST,
- gobject.TYPE_BOOLEAN, (gobject.TYPE_INT, gobject.TYPE_PYOBJECT))
+import warnings
+warnings.warn(FutureWarning("%s is deprecated. See Documentation/PluginAPI.rst" % __name__))
-# Command ID, Action result type, action result
-gobject.signal_new("late-command-result", ActionExecutionContext,
- gobject.SIGNAL_RUN_LAST,
- gobject.TYPE_BOOLEAN, (gobject.TYPE_INT, gobject.gobject.TYPE_INT,
- gobject.TYPE_PYOBJECT))
diff --git a/kupfer/core/commandexec.py b/kupfer/core/commandexec.py
new file mode 100644
index 0000000..d492f65
--- /dev/null
+++ b/kupfer/core/commandexec.py
@@ -0,0 +1,398 @@
+"""
+The main logic for executing constructed commands.
+
+A command is normally a tuple of (object, action, indirect object).
+Where, of course, the indirect object is often not needed (in this module we
+then pass None in its stead).
+
+This code was once a shining machine; While adding the "comma trick" and
+support for "multiple dispatch" was easy in the rest of the program, it shed
+its casualties here: While the main process is simple, we deal here with all
+the exceptions that are, at the moment, tacked on.
+
+The ActionExecutionContext (ACE) keeps track of its nested invocation, so that
+we can catch the results of commands executed inside other commands. The
+delegation mechanism allows a user of the ACE to indicate that the result of
+the command should be passed on from the earlier (more nested) invocation.
+
+Multiple dispatch is straightforward if the action implements the multiple
+dispatch protocol. Is the protocol not implemented, the command is simply
+"multiplied out": executed once for each object, or once for each combination
+of object and indirect object.
+
+With multiple command execution (and delegation), we must then process and
+merge multiple return values.
+"""
+from __future__ import with_statement
+
+import collections
+import contextlib
+import itertools
+import sys
+
+import gobject
+
+from kupfer import pretty
+from kupfer import task
+from kupfer import uiutils
+from kupfer.objects import OperationError
+from kupfer.obj.objects import SourceLeaf
+from kupfer.obj.sources import MultiSource
+from kupfer.obj.compose import MultipleLeaf
+
+RESULT_NONE, RESULT_OBJECT, RESULT_SOURCE, RESULT_ASYNC = (1, 2, 3, 4)
+RESULTS_SYNC = (RESULT_OBJECT, RESULT_SOURCE)
+
+_MAX_LAST_RESULTS = 10
+
+_action_exec_context = None
+def DefaultActionExecutionContext():
+ global _action_exec_context
+ if _action_exec_context is None:
+ _action_exec_context = ActionExecutionContext()
+ return _action_exec_context
+
+class ActionExecutionError (Exception):
+ pass
+
+def _get_leaf_members(leaf):
+ """
+ Return an iterator to members of @leaf, if it is a multiple leaf
+ """
+ # NOTE : This function duplicates one in core/actionlogic.py
+ try:
+ return leaf.get_multiple_leaf_representation()
+ except AttributeError:
+ return (leaf, )
+
+def _is_multiple(leaf):
+ return hasattr(leaf, "get_multiple_leaf_representation")
+
+def _wants_context(action):
+ return action.wants_context()
+
+def activate_action(context, obj, action, iobj):
+ """ Activate @action in simplest manner """
+ kwargs = {}
+ if _wants_context(action):
+ kwargs['ctx'] = context
+ if not _is_multiple(obj) and not _is_multiple(iobj):
+ return _activate_action_single(obj, action, iobj, kwargs)
+ else:
+ return _activate_action_multiple(obj, action, iobj, kwargs)
+
+def _activate_action_single(obj, action, iobj, kwargs):
+ if action.requires_object():
+ ret = action.activate(obj, iobj, **kwargs)
+ else:
+ ret = action.activate(obj, **kwargs)
+ return ret
+
+def _activate_action_multiple(obj, action, iobj, kwargs):
+ if not hasattr(action, "activate_multiple"):
+ iobjs = (None, ) if iobj is None else _get_leaf_members(iobj)
+ return _activate_action_multiple_multiplied(_get_leaf_members(obj),
+ action, iobjs, kwargs)
+
+ if action.requires_object():
+ ret = action.activate_multiple(_get_leaf_members(obj),
+ _get_leaf_members(iobj), **kwargs)
+ else:
+ ret = action.activate_multiple(_get_leaf_members(obj), **kwargs)
+ return ret
+
+def _activate_action_multiple_multiplied(objs, action, iobjs, kwargs):
+ """
+ Multiple dispatch by "mulitplied" invocation of the simple activation
+
+ Return an iterable of the return values.
+ """
+ rets = []
+ for L in objs:
+ for I in iobjs:
+ ret = _activate_action_single(L, action, I, kwargs)
+ rets.append(ret)
+ ctx = DefaultActionExecutionContext()
+ ret = ctx._combine_action_result_multiple(action, rets)
+ return ret
+
+def parse_action_result(action, ret):
+ """Return result type for @action and return value @ret"""
+ def valid_result(ret):
+ return ret and (not hasattr(ret, "is_valid") or ret.is_valid())
+
+ # handle actions returning "new contexts"
+ res = RESULT_NONE
+ if action.is_factory() and valid_result(ret):
+ res = RESULT_SOURCE
+ if action.has_result() and valid_result(ret):
+ res = RESULT_OBJECT
+ elif action.is_async() and valid_result(ret):
+ res = RESULT_ASYNC
+ return res
+
+class ExecutionToken (object):
+ """
+ A token object that an ``Action`` carries with it
+ from ``activate``.
+
+ Must be used for access to current execution context,
+ and to access the environment.
+ """
+ def __init__(self, aectx, async_token, ui_ctx):
+ self._aectx = aectx
+ self._token = async_token
+ self._ui_ctx = ui_ctx
+
+ def register_late_result(self, result_object, show=True):
+ self._aectx.register_late_result(self._token, result_object, show=show)
+
+ def register_late_error(self, exc_info=None):
+ self._aectx.register_late_error(self._token, exc_info)
+
+ def delegated_run(self, *objs):
+ return self._aectx.run(*objs, delegate=True, ui_ctx=self._ui_ctx)
+
+ @property
+ def environment(self):
+ """This is a property for the current environment,
+ acess env variables like this::
+
+ ctx.environment.get_timestamp()
+
+ Raises RuntimeError when not available.
+ """
+ if self._ui_ctx is not None:
+ return self._ui_ctx
+ else:
+ raise RuntimeError("Environment Context not available")
+
+class ActionExecutionContext (gobject.GObject, pretty.OutputMixin):
+ """
+ command-result (result_type, result)
+ Emitted when a command is carried out, with its resulting value
+ """
+ __gtype_name__ = "ActionExecutionContext"
+ def __init__(self):
+ gobject.GObject.__init__(self)
+ self.task_runner = task.TaskRunner(end_on_finish=False)
+ self._nest_level = 0
+ self._delegate = False
+ self._command_counter = itertools.count()
+ self.last_command_id = -1
+ self.last_command = None
+ self.last_executed_command = None
+ self.last_results = collections.deque([], _MAX_LAST_RESULTS)
+
+ def check_valid(self, obj, action, iobj):
+ pass
+
+ @contextlib.contextmanager
+ def _nesting(self):
+ try:
+ self._nest_level += 1
+ self._delegate = False
+ yield
+ finally:
+ self._nest_level -= 1
+
+ def _is_nested(self):
+ return self._nest_level
+
+ @contextlib.contextmanager
+ def _error_conversion(self, *cmdtuple):
+ try:
+ yield
+ except OperationError:
+ self._do_error_conversion(cmdtuple, sys.exc_info())
+
+ def _do_error_conversion(self, cmdtuple, exc_info):
+ if not self.operation_error(exc_info, cmdtuple):
+ raise
+ etype, value, tb = exc_info
+ raise ActionExecutionError, value, tb
+
+ def get_async_token(self):
+ """Get an action execution for current execution
+
+ Return a token for the currently active command execution.
+ The token must be used for posting late results or late errors.
+ """
+ return (self.last_command_id, self.last_executed_command)
+
+ def make_execution_token(self, ui_ctx):
+ """
+ Return an ExecutionToken for @self and @ui_ctx
+ """
+ return ExecutionToken(self, self.get_async_token(), ui_ctx)
+
+ def operation_error(self, exc_info, cmdtuple):
+ "Error when executing action. Return True when error was handled"
+ if self._is_nested():
+ return
+ etype, value, tb = exc_info
+ obj, action, iobj = cmdtuple
+ # TRANS: When an error occurs in an action to be carried out,
+ # TRANS: then this is the heading of the error notification
+ return uiutils.show_notification(
+ _("Could not to carry out '%s'") % action,
+ unicode(value), icon_name="kupfer")
+
+ def register_late_error(self, token, exc_info=None):
+ "Register an error in exc_info. The error must be an OperationError"
+ if exc_info is None:
+ exc_info = sys.exc_info()
+ if isinstance(exc_info, Exception):
+ exc_info = (type(exc_info), exc_info, None)
+ command_id, cmdtuple = token
+ self._do_error_conversion(cmdtuple, exc_info)
+
+ def register_late_result(self, token, result, show=True):
+ """Register a late result
+
+ Result must be a Leaf (as in result object, not factory or async)
+
+ If @show, possibly display the result to the user.
+ """
+ self.output_debug("Late result", repr(result), "for", token)
+ command_id, (_ign1, action, _ign2) = token
+ if result is None:
+ raise ActionExecutionError("Late result from %s was None" % action)
+ res_name = unicode(result)
+ res_desc = result.get_description()
+ if res_desc:
+ description = "%s (%s)" % (res_name, res_desc)
+ else:
+ description = res_name
+ uiutils.show_notification(_('"%s" produced a result') % action,
+ description)
+
+ # If only registration was requsted, remove the command id info
+ if not show:
+ command_id = -1
+ self.emit("late-command-result", command_id, RESULT_OBJECT, result)
+ self._append_result(RESULT_OBJECT, result)
+
+ def _append_result(self, res_type, result):
+ if res_type == RESULT_OBJECT:
+ self.last_results.append(result)
+
+ def run(self, obj, action, iobj, delegate=False, ui_ctx=None):
+ """
+ Activate the command (obj, action, iobj), where @iobj may be None
+
+ Return a tuple (DESCRIPTION; RESULT)
+
+ If a command carries out another command as part of its execution,
+ and wishes to delegate to it, pass True for @delegate.
+ """
+ self.last_command_id = self._command_counter.next()
+ self.last_executed_command = (obj, action, iobj)
+
+ if not action or not obj:
+ raise ActionExecutionError("Primary Object and Action required")
+ if iobj is None and action.requires_object():
+ raise ActionExecutionError("%s requires indirect object" % action)
+
+ # The execution token object for the current invocation
+ execution_token = self.make_execution_token(ui_ctx)
+ with self._error_conversion(obj, action, iobj):
+ with self._nesting():
+ ret = activate_action(execution_token, obj, action, iobj)
+
+ # remember last command, but not delegated commands.
+ if not delegate:
+ self.last_command = self.last_executed_command
+
+ # Delegated command execution was previously requested: we take
+ # the result of the nested execution context
+ if self._delegate:
+ res, ret = ret
+ return self._return_result(res, ret)
+
+ res = parse_action_result(action, ret)
+ if res == RESULT_ASYNC:
+ # Register the task then "clear" the result
+ self.output_debug("Registering async task", ret)
+ self.task_runner.add_task(ret)
+ res, ret = RESULT_NONE, None
+
+ # Delegated command execution was requested: we pass
+ # through the result of the action to the parent execution context
+ if delegate and self._is_nested():
+ self._delegate = True
+
+ return self._return_result(res, ret)
+
+ def _return_result(self, res, ret):
+ if not self._is_nested():
+ self._append_result(res, ret)
+ self.emit("command-result", res, ret)
+ return res, ret
+
+
+ def _combine_action_result_multiple(self, action, retvals):
+ self.output_debug("Combining", repr(action), retvals,
+ "delegate=%s" % self._delegate)
+
+ def _make_retvalue(res, values):
+ "Construct a return value for type res"
+ if res == RESULT_SOURCE:
+ return values[0] if len(values) == 1 else MultiSource(values)
+ if res == RESULT_OBJECT:
+ return values[0] if len(values) == 1 else MultipleLeaf(values)
+ if res == RESULT_ASYNC:
+ # Register all tasks now, and return None upwards
+ for task in values:
+ self.output_debug("Registering async task", task)
+ self.task_runner.add_task(task)
+ return None
+
+ if not self._delegate:
+ values = []
+ res = RESULT_NONE
+ for ret in retvals:
+ res_type = parse_action_result(action, ret)
+ if res_type != RESULT_NONE:
+ values.append(ret)
+ res = res_type
+ return _make_retvalue(res, values)
+ else:
+ # Re-parse result values
+ res = RESULT_NONE
+ resmap = {}
+ for ret in retvals:
+ if ret is None:
+ continue
+ res_type, ret_obj = ret
+ if res_type != RESULT_NONE:
+ res = res_type
+ resmap.setdefault(res_type, []).append(ret_obj)
+
+ # register tasks
+ tasks = resmap.pop(RESULT_ASYNC, [])
+ _make_retvalue(RESULT_ASYNC, tasks)
+
+ if len(resmap) == 1:
+ # Return the only of the Source or Object case
+ key, values = resmap.items()[0]
+ return key, _make_retvalue(key, values)
+ elif len(resmap) > 1:
+ # Put the source in a leaf and return a multiple leaf
+ source = _make_retvalue(RESULT_SOURCE, resmap[RESULT_SOURCE])
+ objects = resmap[RESULT_OBJECT]
+ objects.append(SourceLeaf(source))
+ return RESULT_OBJECT, _make_retvalue(RESULT_OBJECT, objects)
+ return RESULT_NONE, None
+
+
+# Action result type, action result
+gobject.signal_new("command-result", ActionExecutionContext,
+ gobject.SIGNAL_RUN_LAST,
+ gobject.TYPE_BOOLEAN, (gobject.TYPE_INT, gobject.TYPE_PYOBJECT))
+
+# Command ID, Action result type, action result
+gobject.signal_new("late-command-result", ActionExecutionContext,
+ gobject.SIGNAL_RUN_LAST,
+ gobject.TYPE_BOOLEAN, (gobject.TYPE_INT, gobject.gobject.TYPE_INT,
+ gobject.TYPE_PYOBJECT))
diff --git a/kupfer/core/data.py b/kupfer/core/data.py
index 488ee62..96c461e 100644
--- a/kupfer/core/data.py
+++ b/kupfer/core/data.py
@@ -10,14 +10,14 @@ gobject.threads_init()
from kupfer.obj import base, sources, compose
from kupfer import pretty, scheduler
-from kupfer import commandexec
-from kupfer.core import actioncompat
from kupfer import datatools
+from kupfer.core import actioncompat
+from kupfer.core import commandexec
+from kupfer.core import execfile
+from kupfer.core import pluginload
+from kupfer.core import qfurl
from kupfer.core import search, learn
from kupfer.core import settings
-from kupfer.core import qfurl
-from kupfer.core import pluginload
-from kupfer.core import execfile
from kupfer.core.sources import GetSourceController
diff --git a/kupfer/plugin/core/internal.py b/kupfer/plugin/core/internal.py
index 48ed90e..dbc09bf 100644
--- a/kupfer/plugin/core/internal.py
+++ b/kupfer/plugin/core/internal.py
@@ -1,7 +1,7 @@
from kupfer.objects import Source, Leaf
from kupfer.objects import RunnableLeaf
-from kupfer import commandexec
+from kupfer.core import commandexec
__kupfer_sources__ = ("KupferInterals", "CommandResults", )
__author__ = "Ulrik Sverdrup <ulrik sverdrup gmail com>"
diff --git a/kupfer/plugin/higherorder.py b/kupfer/plugin/higherorder.py
index bc22c25..7274857 100644
--- a/kupfer/plugin/higherorder.py
+++ b/kupfer/plugin/higherorder.py
@@ -10,7 +10,7 @@ __author__ = "Ulrik Sverdrup <ulrik sverdrup gmail com>"
from kupfer.objects import Action, Leaf
from kupfer.obj.compose import ComposedLeaf, MultipleLeaf
-from kupfer import commandexec
+from kupfer.core import commandexec
from kupfer import pretty
diff --git a/kupfer/plugin/triggers.py b/kupfer/plugin/triggers.py
index cfae091..b903adb 100644
--- a/kupfer/plugin/triggers.py
+++ b/kupfer/plugin/triggers.py
@@ -21,8 +21,8 @@ from kupfer import task
from kupfer.ui import keybindings
from kupfer.ui import uievents
-from kupfer import commandexec
from kupfer.ui import getkey_dialog
+from kupfer.core import commandexec
# we import the keybinder module for its side-effects --
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]