Docstrings should document parameters return values

Partial-Bug: #1374202

Documents what the function parameters are, what
is the type of the parameters passed what return
values are, how it is used and what they should
provide for it when using a method/class or deriving
from an existing class.

Change-Id: Ie81b3a446c9fee2dad9411efa28dad8d455b06ba
This commit is contained in:
Vilobh Meshram
2015-02-03 01:53:32 +00:00
parent 19f9674877
commit b7d59ec748
9 changed files with 200 additions and 48 deletions

View File

@@ -175,7 +175,11 @@ class Failure(object):
and self.traceback_str == other.traceback_str)
def matches(self, other):
"""Checks if another object is equivalent to this object."""
"""Checks if another object is equivalent to this object.
:returns: checks if another object is equivalent to this object
:rtype: boolean
"""
if not isinstance(other, Failure):
return False
if self.exc_info is None or other.exc_info is None:

View File

@@ -76,13 +76,23 @@ class FSM(object):
@property
def current_state(self):
"""Return the current state name.
:returns: current state name
:rtype: string
"""
if self._current is not None:
return self._current.name
return None
@property
def terminated(self):
"""Returns whether the state machine is in a terminal state."""
"""Returns whether the state machine is in a terminal state.
:returns: whether the state machine is in
terminal state or not
:rtype: boolean
"""
if self._current is None:
return False
return self._states[self._current.name]['terminal']
@@ -90,11 +100,16 @@ class FSM(object):
def add_state(self, state, terminal=False, on_enter=None, on_exit=None):
"""Adds a given state to the state machine.
The on_enter and on_exit callbacks, if provided will be expected to
take two positional parameters, these being the state being exited (for
on_exit) or the state being entered (for on_enter) and a second
parameter which is the event that is being processed that caused the
state transition.
:param on_enter: callback, if provided will be expected to take
two positional parameters, these being state being
entered and the second parameter is the event that is
being processed that caused the state transition
:param on_exit: callback, if provided will be expected to take
two positional parameters, these being state being
entered and the second parameter is the event that is
being processed that caused the state transition
:param state: state being entered or exited
:type state: string
"""
if self.frozen:
raise FrozenMachine()
@@ -117,22 +132,22 @@ class FSM(object):
def add_reaction(self, state, event, reaction, *args, **kwargs):
"""Adds a reaction that may get triggered by the given event & state.
:param state: the last stable state expressed
:type state: string
:param event: event that caused the transition
:param args: non-keyworded arguments
:type args: list
:param kwargs: key-value pair arguments
:type kwargs: dictionary
Reaction callbacks may (depending on how the state machine is ran) be
used after an event is processed (and a transition occurs) to cause the
machine to react to the newly arrived at stable state.
These callbacks are expected to accept three default positional
parameters (although more can be passed in via *args and **kwargs,
these will automatically get provided to the callback when it is
activated *ontop* of the three default). The three default parameters
are the last stable state, the new stable state and the event that
caused the transition to this new stable state to be arrived at.
The expected result of a callback is expected to be a new event that
the callback wants the state machine to react to. This new event
may (depending on how the state machine is ran) get processed (and
this process typically repeats) until the state machine reaches a
terminal state.
used after an event is processed (and a transition occurs) to cause
the machine to react to the newly arrived at stable state. The
expected result of a callback is expected to be a
new event that the callback wants the state machine to react to.
This new event may (depending on how the state machine is ran) get
processed (and this process typically repeats) until the state
machine reaches a terminal state.
"""
if self.frozen:
raise FrozenMachine()
@@ -148,7 +163,12 @@ class FSM(object):
" already defined" % (state, event))
def add_transition(self, start, end, event):
"""Adds an allowed transition from start -> end for the given event."""
"""Adds an allowed transition from start -> end for the given event.
:param start: start of the transition
:param end: end of the transition
:param event: event that caused the transition
"""
if self.frozen:
raise FrozenMachine()
if start not in self._states:
@@ -164,7 +184,10 @@ class FSM(object):
self._states[start]['on_exit'])
def process_event(self, event):
"""Trigger a state change in response to the provided event."""
"""Trigger a state change in response to the provided event.
:param event: event to be processed to cause a potential transition
"""
current = self._current
if current is None:
raise NotInitialized("Can only process events after"
@@ -256,7 +279,14 @@ class FSM(object):
event = cb(old_state, new_state, event, *args, **kwargs)
def __contains__(self, state):
"""Returns if this state exists in the machines known states."""
"""Returns if this state exists in the machines known states.
:param state: input state
:type state: string
:returns: checks whether the state exists in the machine
known states
:rtype: boolean
"""
return state in self._states
def freeze(self):
@@ -270,7 +300,11 @@ class FSM(object):
@property
def events(self):
"""Returns how many events exist."""
"""Returns how many events exist.
:returns: how many events exist
:rtype: number
"""
c = 0
for state in six.iterkeys(self._states):
c += len(self._transitions[state])

View File

@@ -54,6 +54,11 @@ class _Gatherer(object):
self._stats = ExecutorStatistics()
def _capture_stats(self, watch, fut):
"""Capture statistics
:param watch: stopwatch object
:param fut: future object
"""
watch.stop()
with self._stats_lock:
# Use a new collection and lock so that all mutations are seen as
@@ -79,6 +84,7 @@ class _Gatherer(object):
cancelled=cancelled)
def submit(self, fn, *args, **kwargs):
"""Submit work to be executed and capture statistics."""
watch = timing.StopWatch()
if self._start_before_submit:
watch.start()
@@ -301,7 +307,13 @@ class GreenThreadPoolExecutor(_futures.Executor):
return self._gatherer.statistics
def submit(self, fn, *args, **kwargs):
"""Submit some work to be executed (and gather statistics)."""
"""Submit some work to be executed (and gather statistics).
:param args: non-keyworded arguments
:type args: list
:param kwargs: key-value arguments
:type kwargs: dictionary
"""
with self._shutdown_lock:
if self._shutdown:
raise RuntimeError('Can not schedule new futures'
@@ -316,6 +328,12 @@ class GreenThreadPoolExecutor(_futures.Executor):
return f
def _spin_up(self, work):
"""Spin up a greenworker if less than max_workers.
:param work: work to be given to the greenworker
:returns: whether a green worker was spun up or not
:rtype: boolean
"""
alive = self._pool.running() + self._pool.waiting()
if alive < self._max_workers:
self._pool.spawn_n(_GreenWorker(self, work, self._delayed_work))
@@ -350,28 +368,46 @@ class ExecutorStatistics(object):
@property
def failures(self):
"""How many submissions ended up raising exceptions."""
"""How many submissions ended up raising exceptions.
:returns: how many submissions ended up raising exceptions
:rtype: number
"""
return self._failures
@property
def executed(self):
"""How many submissions were executed (failed or not)."""
"""How many submissions were executed (failed or not).
:returns: how many submissions were executed
:rtype: number
"""
return self._executed
@property
def runtime(self):
"""Total runtime of all submissions executed (failed or not)."""
"""Total runtime of all submissions executed (failed or not).
:returns: total runtime of all submissions executed
:rtype: number
"""
return self._runtime
@property
def cancelled(self):
"""How many submissions were cancelled before executing."""
"""How many submissions were cancelled before executing.
:returns: how many submissions were cancelled before executing
:rtype: number
"""
return self._cancelled
@property
def average_runtime(self):
"""The average runtime of all submissions executed.
:returns: average runtime of all submissions executed
:rtype: number
:raises: ZeroDivisionError when no executions have occurred.
"""
return self._runtime / self._executed

View File

@@ -49,10 +49,11 @@ class Latch(object):
def wait(self, timeout=None):
"""Waits until the latch is released.
NOTE(harlowja): if a timeout is provided this function will wait
until that timeout expires, if the latch has been released before the
timeout expires then this will return True, otherwise it will
return False.
:param timeout: wait until the timeout expires
:type timeout: number
:returns: true if the latch has been released before the
timeout expires otherwise false
:rtype: boolean
"""
watch = tt.StopWatch(duration=timeout)
watch.start()

View File

@@ -29,6 +29,18 @@ class _Listener(object):
"""Internal helper that represents a notification listener/target."""
def __init__(self, callback, args=None, kwargs=None, details_filter=None):
"""Initialize members
:param callback: callback function
:param details_filter: a callback that will be called before the
actual callback that can be used to discard
the event (thus avoiding the invocation of
the actual callback)
:param args: non-keyworded arguments
:type args: list
:param kwargs: key-value pair arguments
:type kwargs: dictionary
"""
self._callback = callback
self._details_filter = details_filter
if not args:
@@ -64,6 +76,13 @@ class _Listener(object):
return "<%s>" % repr_msg
def is_equivalent(self, callback, details_filter=None):
"""Check if the callback is same
:param callback: callback used for comparison
:param details_filter: callback used for comparison
:returns: false if not the same callback, otherwise true
:rtype: boolean
"""
if not reflection.is_same_callback(self._callback, callback):
return False
if details_filter is not None:
@@ -105,14 +124,22 @@ class Notifier(object):
self._listeners = collections.defaultdict(list)
def __len__(self):
"""Returns how many callbacks are registered."""
"""Returns how many callbacks are registered.
:returns: count of how many callbacks are registered
:rtype: number
"""
count = 0
for (_event_type, listeners) in six.iteritems(self._listeners):
count += len(listeners)
return count
def is_registered(self, event_type, callback, details_filter=None):
"""Check if a callback is registered."""
"""Check if a callback is registered.
:returns: checks if the callback is registered
:rtype: boolean
"""
for listener in self._listeners.get(event_type, []):
if listener.is_equivalent(callback, details_filter=details_filter):
return True
@@ -134,7 +161,8 @@ class Notifier(object):
:param event_type: event type that occurred
:param details: additional event details *dictionary* passed to
callback keyword argument with the same name.
callback keyword argument with the same name
:type details: dictionary
"""
if not self.can_trigger_notification(event_type):
LOG.debug("Event type '%s' is not allowed to trigger"
@@ -165,6 +193,13 @@ class Notifier(object):
:meth:`.notify` method (if a details filter callback is provided then
the target callback will *only* be triggered if the details filter
callback returns a truthy value).
:param event_type: event type input
:param callback: function callback to be registered.
:param args: non-keyworded arguments
:type args: list
:param kwargs: key-value pair arguments
:type kwargs: dictionary
"""
if not six.callable(callback):
raise ValueError("Event callback must be callable")
@@ -189,7 +224,10 @@ class Notifier(object):
details_filter=details_filter))
def deregister(self, event_type, callback, details_filter=None):
"""Remove a single listener bound to event ``event_type``."""
"""Remove a single listener bound to event ``event_type``.
:param event_type: deregister listener bound to event_type
"""
if event_type not in self._listeners:
return False
for i, listener in enumerate(self._listeners.get(event_type, [])):
@@ -199,7 +237,10 @@ class Notifier(object):
return False
def deregister_event(self, event_type):
"""Remove a group of listeners bound to event ``event_type``."""
"""Remove a group of listeners bound to event ``event_type``.
:param event_type: deregister listeners bound to event_type
"""
return len(self._listeners.pop(event_type, []))
def copy(self):
@@ -220,7 +261,12 @@ class Notifier(object):
return True
def can_trigger_notification(self, event_type):
"""Checks if the event can trigger a notification."""
"""Checks if the event can trigger a notification.
:param event_type: event that needs to be verified
:returns: whether the event can trigger a notification
:rtype: boolean
"""
if event_type in self._DISALLOWED_NOTIFICATION_EVENTS:
return False
else:
@@ -251,7 +297,12 @@ class RestrictedNotifier(Notifier):
yield event_type
def can_be_registered(self, event_type):
"""Checks if the event can be registered/subscribed to."""
"""Checks if the event can be registered/subscribed to.
:param event_type: event that needs to be verified
:returns: whether the event can be registered/subscribed to
:rtype: boolean
"""
return (event_type in self._watchable_events or
(event_type == self.ANY and self._allow_any))

View File

@@ -39,7 +39,12 @@ _PERIODIC_ATTRS = tuple([
def periodic(spacing, run_immediately=True):
"""Tags a method/function as wanting/able to execute periodically."""
"""Tags a method/function as wanting/able to execute periodically.
:param run_immediately: option to specify whether to run
immediately or not
:type run_immediately: boolean
"""
if spacing <= 0:
raise ValueError("Periodicity/spacing must be greater than"

View File

@@ -49,8 +49,15 @@ class PleasantTable(object):
@classmethod
def _size_selector(cls, possible_sizes):
# The number two is used so that the edges of a column have spaces
# around them (instead of being right next to a column separator).
"""Select the maximum size, utility function for adding borders.
The number two is used so that the edges of a column have spaces
around them (instead of being right next to a column separator).
:param possible_sizes: possible sizes available
:returns: maximum size
:rtype: number
"""
try:
return max(x + 2 for x in possible_sizes)
except ValueError:

View File

@@ -250,8 +250,10 @@ class StopWatch(object):
:param return_none: when ``True`` instead of raising a ``RuntimeError``
when no duration has been set this call will
return ``None`` instead.
return ``None`` instead
:type return_none: boolean
:returns: how many seconds left until the watch expires
:rtype: number
"""
if self._state != self._STARTED:
raise RuntimeError("Can not get the leftover time of a stopwatch"
@@ -265,7 +267,11 @@ class StopWatch(object):
return max(0.0, self._duration - self.elapsed())
def expired(self):
"""Returns if the watch has expired (ie, duration provided elapsed)."""
"""Returns if the watch has expired (ie, duration provided elapsed).
:returns: if the watch has expired
:rtype: boolean
"""
if self._state is None:
raise RuntimeError("Can not check if a stopwatch has expired"
" if it has not been started/stopped")

View File

@@ -96,6 +96,9 @@ class Node(object):
This will search not only this node but also any children nodes and
finally if nothing is found then None is returned instead of a node
object.
:param item: item to lookup.
:returns: the node for an item if it exists in this node
"""
for n in self.dfs_iter(include_self=True):
if n.item == item:
@@ -103,7 +106,12 @@ class Node(object):
return None
def __contains__(self, item):
"""Returns if this item exists in this node or this nodes children."""
"""Returns whether item exists in this node or this nodes children.
:returns: if the item exists in this node or nodes children,
true if the item exists, false otherwise
:rtype: boolean
"""
return self.find(item) is not None
def __getitem__(self, index):