From 9537f523512eed8603dc304d8502762caaa733cf Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Thu, 25 Sep 2014 14:55:28 -0700 Subject: [PATCH] Document more function/class/method params To help fill out the docs start adding on docstrings that document what a parameter is and what its meaning and usage is to help users better understand the parameter. Part of ongoing bug 1374202 Change-Id: I9f9a83cfb763a0a05d22efca4e0f80627ba8ca8f --- taskflow/atom.py | 20 +++++++---- taskflow/exceptions.py | 39 ++++++++++++++++++---- taskflow/retry.py | 76 ++++++++++++++++++++++-------------------- taskflow/task.py | 30 ++++++++++------- 4 files changed, 103 insertions(+), 62 deletions(-) diff --git a/taskflow/atom.py b/taskflow/atom.py index d93ff57a..b3b7ea9c 100644 --- a/taskflow/atom.py +++ b/taskflow/atom.py @@ -125,7 +125,7 @@ class Atom(object): with this atom. It can be useful in resuming older versions of atoms. Standard major, minor versioning concepts should apply. - :ivar save_as: An *immutable* output ``resource`` name dict this atom + :ivar save_as: An *immutable* output ``resource`` name dictionary this atom produces that other atoms may depend on this atom providing. The format is output index (or key when a dictionary is returned from the execute method) to stored argument @@ -136,11 +136,19 @@ class Atom(object): the names that this atom expects (in a way this is like remapping a namespace of another atom into the namespace of this atom). - :ivar inject: An *immutable* input_name => value dictionary which specifies - any initial inputs that should be automatically injected into - the atoms scope before the atom execution commences (this - allows for providing atom *local* values that do not need to - be provided by other atoms). + :param name: Meaningful name for this atom, should be something that is + distinguishable and understandable for notification, + debugging, storing and any other similar purposes. + :param provides: A set, string or list of items that + this will be providing (or could provide) to others, used + to correlate and associate the thing/s this atom + produces, if it produces anything at all. + :param inject: An *immutable* input_name => value dictionary which + specifies any initial inputs that should be automatically + injected into the atoms scope before the atom execution + commences (this allows for providing atom *local* values that + do not need to be provided by other atoms/dependents). + :ivar inject: See parameter ``inject``. """ def __init__(self, name=None, provides=None, inject=None): diff --git a/taskflow/exceptions.py b/taskflow/exceptions.py index 21bf35ee..1bdb0f48 100644 --- a/taskflow/exceptions.py +++ b/taskflow/exceptions.py @@ -25,6 +25,14 @@ class TaskFlowException(Exception): NOTE(harlowja): in later versions of python we can likely remove the need to have a cause here as PY3+ have implemented PEP 3134 which handles chaining in a much more elegant manner. + + :param message: the exception message, typically some string that is + useful for consumers to view when debugging or analyzing + failures. + :param cause: the cause of the exception being raised, when provided this + should itself be an exception instance, this is useful for + creating a chain of exceptions for versions of python where + this is not yet implemented/supported natively. """ def __init__(self, message, cause=None): super(TaskFlowException, self).__init__(message) @@ -99,7 +107,16 @@ class DependencyFailure(TaskFlowException): class MissingDependencies(DependencyFailure): - """Raised when a entity has dependencies that can not be satisfied.""" + """Raised when a entity has dependencies that can not be satisfied. + + :param who: the entity that caused the missing dependency to be triggered. + :param requirements: the dependency which were not satisfied. + + Further arguments are interpreted as for in + :py:class:`~taskflow.exceptions.TaskFlowException`. + """ + + #: Exception message template used when creating an actual message. MESSAGE_TPL = ("%(who)s requires %(requirements)s but no other entity" " produces said requirements") @@ -147,6 +164,9 @@ class WrappedFailure(Exception): See the failure class documentation for a more comprehensive set of reasons why this object *may* be reraised instead of the original exception. + + :param causes: the :py:class:`~taskflow.utils.misc.Failure` objects that + caused this this exception to be raised. """ def __init__(self, causes): @@ -168,12 +188,14 @@ class WrappedFailure(Exception): return len(self._causes) def check(self, *exc_classes): - """Check if any of exc_classes caused (part of) the failure. + """Check if any of exception classes caused the failure/s. - Arguments of this method can be exception types or type names - (strings). If any of wrapped failures were caused by exception - of given type, the corresponding argument is returned. Else, - None is returned. + :param exc_classes: exception types/exception type names to + search for. + + If any of the contained failures were caused by an exception of a + given type, the corresponding argument that matched is returned. If + not then none is returned. """ if not exc_classes: return None @@ -189,7 +211,10 @@ class WrappedFailure(Exception): def exception_message(exc): - """Return the string representation of exception.""" + """Return the string representation of exception. + + :param exc: exception object to get a string representation of. + """ # NOTE(imelnikov): Dealing with non-ascii data in python is difficult: # https://bugs.launchpad.net/taskflow/+bug/1275895 # https://bugs.launchpad.net/taskflow/+bug/1276053 diff --git a/taskflow/retry.py b/taskflow/retry.py index 425c8ea6..edfc4d18 100644 --- a/taskflow/retry.py +++ b/taskflow/retry.py @@ -33,43 +33,19 @@ RETRY = "RETRY" @six.add_metaclass(abc.ABCMeta) -class Decider(object): - """A class/mixin object that can decide how to resolve execution failures. - - A decider may be executed multiple times on subflow or other atom - failure and it is expected to make a decision about what should be done - to resolve the failure (retry, revert to the previous retry, revert - the whole flow, etc.). - """ - - @abc.abstractmethod - def on_failure(self, history, *args, **kwargs): - """On failure makes a decision about the future. - - This method will typically use information about prior failures (if - this historical failure information is not available or was not - persisted this history will be empty). - - Returns retry action constant: - - * ``RETRY`` when subflow must be reverted and restarted again (maybe - with new parameters). - * ``REVERT`` when this subflow must be completely reverted and parent - subflow should make a decision about the flow execution. - * ``REVERT_ALL`` in a case when the whole flow must be reverted and - marked as ``FAILURE``. - """ - - -@six.add_metaclass(abc.ABCMeta) -class Retry(atom.Atom, Decider): +class Retry(atom.Atom): """A class that can decide how to resolve execution failures. This abstract base class is used to inherit from and provide different strategies that will be activated upon execution failures. Since a retry - object is an atom it may also provide execute and revert methods to alter - the inputs of connected atoms (depending on the desired strategy to be - used this can be quite useful). + object is an atom it may also provide :meth:`.execute` and + :meth:`.revert` methods to alter the inputs of connected atoms (depending + on the desired strategy to be used this can be quite useful). + + NOTE(harlowja): the :meth:`.execute` and :meth:`.revert` and + :meth:`.on_failure` will automatically be given a ``history`` parameter, + which contains information about the past decisions and outcomes + that have occurred (if available). """ default_provides = None @@ -92,11 +68,11 @@ class Retry(atom.Atom, Decider): @abc.abstractmethod def execute(self, history, *args, **kwargs): - """Executes the given retry atom. + """Executes the given retry. This execution activates a given retry which will typically produce data required to start or restart a connected component using - previously provided values and a history of prior failures from + previously provided values and a ``history`` of prior failures from previous runs. The historical data can be analyzed to alter the resolution strategy that this retry controller will use. @@ -105,12 +81,15 @@ class Retry(atom.Atom, Decider): saved to the history of the retry atom automatically, that is a list of tuples (result, failures) are persisted where failures is a dictionary of failures indexed by task names and the result is the execution - result returned by this retry controller during that failure resolution + result returned by this retry during that failure resolution attempt. + + :param args: positional arguments that retry requires to execute. + :param kwargs: any keyword arguments that retry requires to execute. """ def revert(self, history, *args, **kwargs): - """Reverts this retry using the given context. + """Reverts this retry. On revert call all results that had been provided by previous tries and all errors caused during reversion are provided. This method @@ -118,6 +97,29 @@ class Retry(atom.Atom, Decider): retry (that is to say that the controller has ran out of resolution options and has either given up resolution or has failed to handle a execution failure). + + :param args: positional arguments that the retry required to execute. + :param kwargs: any keyword arguments that the retry required to + execute. + """ + + @abc.abstractmethod + def on_failure(self, history, *args, **kwargs): + """Makes a decision about the future. + + This method will typically use information about prior failures (if + this historical failure information is not available or was not + persisted the provided history will be empty). + + Returns a retry constant (one of): + + * ``RETRY``: when the controlling flow must be reverted and restarted + again (for example with new parameters). + * ``REVERT``: when this controlling flow must be completely reverted + and the parent flow (if any) should make a decision about further + flow execution. + * ``REVERT_ALL``: when this controlling flow and the parent + flow (if any) must be reverted and marked as a ``FAILURE``. """ diff --git a/taskflow/task.py b/taskflow/task.py index cd470e72..62fd7314 100644 --- a/taskflow/task.py +++ b/taskflow/task.py @@ -52,7 +52,7 @@ class BaseTask(atom.Atom): A common pattern for initializing the state of the system prior to running tasks is to define some code in a base class that all your - tasks inherit from. In that class, you can define a pre_execute + tasks inherit from. In that class, you can define a ``pre_execute`` method and it will always be invoked just prior to your tasks running. """ @@ -72,6 +72,9 @@ class BaseTask(atom.Atom): happens in a different python process or on a remote machine) and so that the result can be transmitted to other tasks (which may be local or remote). + + :param args: positional arguments that task requires to execute. + :param kwargs: any keyword arguments that task requires to execute. """ def post_execute(self): @@ -79,7 +82,7 @@ class BaseTask(atom.Atom): A common pattern for cleaning up global state of the system after the execution of tasks is to define some code in a base class that all your - tasks inherit from. In that class, you can define a post_execute + tasks inherit from. In that class, you can define a ``post_execute`` method and it will always be invoked just after your tasks execute, regardless of whether they succeded or not. @@ -90,7 +93,7 @@ class BaseTask(atom.Atom): def pre_revert(self): """Code to be run prior to reverting the task. - This works the same as pre_execute, but for the revert phase. + This works the same as :meth:`.pre_execute`, but for the revert phase. """ def revert(self, *args, **kwargs): @@ -98,26 +101,29 @@ class BaseTask(atom.Atom): This method should undo any side-effects caused by previous execution of the task using the result of the :py:meth:`execute` method and - information on failure which triggered reversion of the flow. + information on the failure which triggered reversion of the flow the + task is contained in (if applicable). - NOTE(harlowja): The ``**kwargs`` which are passed into the - :py:meth:`execute` method will also be passed into this method. The - ``**kwargs`` key ``'result'`` will contain the :py:meth:`execute` - result (if any) and the ``**kwargs`` key ``'flow_failures'`` will - contain the failure information. + :param args: positional arguments that the task required to execute. + :param kwargs: any keyword arguments that the task required to + execute; the special key ``'result'`` will contain + the :py:meth:`execute` result (if any) and + the ``**kwargs`` key ``'flow_failures'`` will contain + any failure information. """ def post_revert(self): """Code to be run after reverting the task. - This works the same as post_execute, but for the revert phase. + This works the same as :meth:`.post_execute`, but for the revert phase. """ def update_progress(self, progress, **kwargs): """Update task progress and notify all registered listeners. - :param progress: task progress float value between 0 and 1 - :param kwargs: task specific progress information + :param progress: task progress float value between 0.0 and 1.0 + :param kwargs: any keyword arguments that are tied to the specific + progress value. """ if progress > 1.0: LOG.warn("Progress must be <= 1.0, clamping to upper bound")