Fix H405 (multi line docstring) warnings (horizon)

H405: multi line docstring summary not separated with an empty line

Partial-Bug: #1696996
Change-Id: I58f71206def1a25f3eda04b9297f1aa7d3caa646
This commit is contained in:
Akihiro Motoki 2017-06-09 16:02:30 +00:00
parent 8ecb9c141b
commit 95629a337e
35 changed files with 480 additions and 358 deletions

View File

@ -471,14 +471,13 @@ class Dashboard(Registry, HorizonComponent):
self._panel_groups = None
def get_panel(self, panel):
"""Returns the specified :class:`~horizon.Panel` instance registered
with this dashboard.
"""
"""Returns the Panel instance registered with this dashboard."""
return self._registered(panel)
def get_panels(self):
"""Returns the :class:`~horizon.Panel` instances registered with this
dashboard in order, without any panel groupings.
"""Returns the Panel instances registered with this dashboard in order.
Panel grouping information is not included.
"""
all_panels = []
panel_groups = self.get_panel_groups()
@ -487,8 +486,9 @@ class Dashboard(Registry, HorizonComponent):
return all_panels
def get_panel_group(self, slug):
"""Returns the specified :class:~horizon.PanelGroup
or None if not registered
"""Returns the specified :class:~horizon.PanelGroup.
Returns None if not registered.
"""
return self._panel_groups.get(slug)
@ -1006,8 +1006,10 @@ class Site(Registry, HorizonComponent):
class HorizonSite(Site):
"""A singleton implementation of Site such that all dealings with horizon
get the same instance no matter what. There can be only one.
"""A singleton implementation of Site.
All dealings with horizon get the same instance no matter what.
There can be only one.
"""
_instance = None

View File

@ -119,8 +119,11 @@ class ResourceBrowser(html.HTMLElement):
% (attr_name, self.__class__.__name__))
def set_tables(self, tables):
"""Sets the table instances on the browser from a dictionary mapping
table names to table instances (as constructed by MultiTableView).
"""Sets the table instances on the browser.
``tables`` argument specifies tables to be set.
It is a dictionary mapping table names to table instances
(as constructed by MultiTableView).
"""
self.navigation_table = tables[self.navigation_table_class._meta.name]
self.content_table = tables[self.content_table_class._meta.name]

View File

@ -19,8 +19,7 @@ from django.contrib.staticfiles.finders import AppDirectoriesFinder
class HorizonStaticFinder(AppDirectoriesFinder):
"""A static files finder that also looks into the directory of each panel.
"""
"""Static files finder that also looks into the directory of each panel."""
def __init__(self, app_names=None, *args, **kwargs):
super(HorizonStaticFinder, self).__init__(*args, **kwargs)

View File

@ -45,8 +45,10 @@ class HorizonException(Exception):
class Http302(HorizonException):
"""Error class which can be raised from within a handler to cause an
early bailout and redirect at the middleware level.
"""Exception used to redirect at the middleware level.
This error class which can be raised from within a handler to cause
an early bailout and redirect at the middleware level.
"""
status_code = 302
@ -56,7 +58,9 @@ class Http302(HorizonException):
class NotAuthorized(HorizonException):
"""Raised whenever a user attempts to access a resource which they do not
"""User tries to access a resource without sufficient permissions.
Raised whenever a user attempts to access a resource which they do not
have permission-based access to (such as when failing the
:func:`~horizon.decorators.require_perms` decorator).
@ -68,8 +72,7 @@ class NotAuthorized(HorizonException):
class NotAuthenticated(HorizonException):
"""Raised when a user is trying to make requests and they are not logged
in.
"""Raised when a user is trying to make requests and they are not logged in.
The included :class:`~horizon.middleware.HorizonMiddleware` catches
``NotAuthenticated`` and handles it gracefully by displaying an error
@ -99,8 +102,9 @@ class RecoverableError(HorizonException):
class ServiceCatalogException(HorizonException):
"""Raised when a requested service is not available in the
``ServiceCatalog`` returned by Keystone.
"""A requested service is not available in the ``ServiceCatalog``.
``ServiceCatalog`` is fetched from Keystone.
"""
def __init__(self, service_name):
message = 'Invalid service catalog service: %s' % service_name
@ -109,9 +113,7 @@ class ServiceCatalogException(HorizonException):
@six.python_2_unicode_compatible
class AlreadyExists(HorizonException):
"""Exception to be raised when trying to create an API resource which
already exists.
"""
"""API resources tried to create already exists."""
def __init__(self, name, resource_type):
self.attrs = {"name": name, "resource": resource_type}
self.msg = _('A %(resource)s with the name "%(name)s" already exists.')
@ -125,8 +127,10 @@ class AlreadyExists(HorizonException):
@six.python_2_unicode_compatible
class GetFileError(HorizonException):
"""Exception to be raised when the value of get_file did not start with
https:// or http://
"""Exception to be raised when the value of get_file is not expected.
The expected values start with https:// or http://.
Otherwise this exception will be raised.
"""
def __init__(self, name, resource_type):
self.attrs = {"name": name, "resource": resource_type}
@ -159,7 +163,9 @@ class WorkflowError(HorizonException):
class WorkflowValidationError(HorizonException):
"""Exception raised during workflow validation if required data is missing,
"""Exception raised during workflow validation.
It is raised if required data is missing,
or existing data is not valid.
"""
pass
@ -171,7 +177,9 @@ class MessageFailure(HorizonException):
class HandledException(HorizonException):
"""Used internally to track exceptions that have gone through
"""Used internally to track exceptions that are already handled.
It is used to track exceptions that have gone through
:func:`horizon.exceptions.handle` more than once.
"""
def __init__(self, wrapped):
@ -192,8 +200,10 @@ def error_color(msg):
def check_message(keywords, message):
"""Checks an exception for given keywords and raises a new ``ActionError``
with the desired message if the keywords are found. This allows selective
"""Checks an exception for given keywords and raises an error if found.
It raises a new ``ActionError`` with the desired message if the
keywords are found. This allows selective
control over API error messages.
"""
exc_type, exc_value, exc_traceback = sys.exc_info()

View File

@ -30,16 +30,15 @@ class SelfHandlingMixin(object):
class SelfHandlingForm(SelfHandlingMixin, forms.Form):
"""A base :class:`Form <django:django.forms.Form>` class which includes
processing logic in its subclasses.
"""
"""A base Form class which includes processing logic in its subclasses."""
required_css_class = 'required'
def api_error(self, message):
"""Adds an error to the form's error dictionary after validation
based on problems reported via the API. This is useful when you
wish for API errors to appear as errors on the form rather than
using the messages framework.
"""Adds an error to the form's error dictionary.
It can be used after validation based on problems reported via the API.
This is useful when you wish for API errors to appear as errors on the
form rather than using the messages framework.
"""
self.add_error(NON_FIELD_ERRORS, message)

View File

@ -40,6 +40,7 @@ IPv6 = 2
class IPField(fields.Field):
"""Form field for entering IP/range values, with validation.
Supports IPv4/IPv6 in the format:
.. xxx.xxx.xxx.xxx
.. xxx.xxx.xxx.xxx/zz
@ -157,9 +158,10 @@ class MACAddressField(fields.Field):
class SelectWidget(widgets.Select):
"""Customizable select widget, that allows to render
data-xxx attributes from choices. This widget also
allows user to specify additional html attributes
"""Customizable select widget.
It allows to render data-xxx attributes from choices.
This widget also allows user to specify additional html attributes
for choices.
.. attribute:: data_attrs
@ -301,7 +303,9 @@ class ThemableSelectWidget(SelectWidget):
class DynamicSelectWidget(SelectWidget):
"""A subclass of the ``Select`` widget which renders extra attributes for
"""``Select`` widget to handle dynamic changes to the available choices.
A subclass of the ``Select`` widget which renders extra attributes for
use in callbacks to handle dynamic changes to the available choices.
"""
_data_add_url_attr = "data-add-item-url"
@ -335,8 +339,7 @@ class ThemableChoiceField(fields.ChoiceField):
class DynamicChoiceField(fields.ChoiceField):
"""A subclass of ``ChoiceField`` with additional properties that make
dynamically updating its elements easier.
"""ChoiceField that make dynamically updating its elements easier.
Notably, the field declaration takes an extra argument, ``add_item_link``
which may be a string or callable defining the URL that should be used
@ -370,8 +373,9 @@ class ThemableDynamicTypedChoiceField(ThemableDynamicChoiceField,
class ThemableCheckboxInput(widgets.CheckboxInput):
"""A subclass of the ``Checkbox`` widget which renders extra markup to
allow a custom checkbox experience.
"""Checkbox widget which renders extra markup.
It is used to allow a custom checkbox experience.
"""
def render(self, name, value, attrs=None):
label_for = attrs.get('id', '')
@ -411,10 +415,12 @@ class ThemableCheckboxSelectMultiple(widgets.CheckboxSelectMultiple):
class ExternalFileField(fields.FileField):
"""A special flavor of FileField which is meant to be used in cases when
instead of uploading file to Django it should be uploaded to some external
location, while the form validation is done as usual. Should be paired
with ExternalUploadMeta metaclass embedded into the Form class.
"""Special FileField to upload file to some external location.
This is a special flavor of FileField which is meant to be used in cases
when instead of uploading file to Django it should be uploaded to some
external location, while the form validation is done as usual. It should be
paired with ExternalUploadMeta metaclass embedded into the Form class.
"""
def __init__(self, *args, **kwargs):
super(ExternalFileField, self).__init__(*args, **kwargs)
@ -422,9 +428,11 @@ class ExternalFileField(fields.FileField):
class ExternalUploadMeta(forms.DeclarativeFieldsMetaclass):
"""Set this class as the metaclass of a form that contains
ExternalFileField in order to process ExternalFileField fields in a
specific way. A hidden CharField twin of FieldField is created which
"""Metaclass to process ExternalFileField fields in a specific way.
Set this class as the metaclass of a form that contains ExternalFileField
in order to process ExternalFileField fields in a specific way.
A hidden CharField twin of FieldField is created which
contains just the filename (if any file was selected on browser side) and
a special `clean` method for FileField is defined which extracts just file
name. This allows to avoid actual file upload to Django server, yet

View File

@ -27,7 +27,9 @@ ADD_TO_FIELD_HEADER = "HTTP_X_HORIZON_ADD_TO_FIELD"
class ModalBackdropMixin(object):
"""This mixin class is to be used for together with ModalFormView and
"""Mixin class to allow ModalFormView and WorkflowView together.
This mixin class is to be used for together with ModalFormView and
WorkflowView classes to augment them with modal_backdrop context data.
.. attribute: modal_backdrop (optional)
@ -78,8 +80,10 @@ class ModalFormMixin(ModalBackdropMixin):
class ModalFormView(ModalFormMixin, views.HorizonFormView):
"""The main view class from which all views which handle forms in Horizon
should inherit. It takes care of all details with processing
"""The main view class for all views which handle forms in Horizon.
All view which handles forms in Horiozn should inherit this class.
It takes care of all details with processing
:class:`~horizon.forms.base.SelfHandlingForm` classes, and modal concerns
when the associated template inherits from
`horizon/common/_modal_form.html`.
@ -148,16 +152,20 @@ class ModalFormView(ModalFormMixin, views.HorizonFormView):
return self.cancel_url or self.success_url
def get_object_id(self, obj):
"""For dynamic insertion of resources created in modals, this method
returns the id of the created object. Defaults to returning the ``id``
attribute.
"""Returns the ID of the created object.
For dynamic insertion of resources created in modals,
this method returns the id of the created object.
Defaults to returning the ``id`` attribute.
"""
return obj.id
def get_object_display(self, obj):
"""For dynamic insertion of resources created in modals, this method
returns the display name of the created object. Defaults to returning
the ``name`` attribute.
"""Returns the display name of the created object.
For dynamic insertion of resources created in modals,
this method returns the display name of the created object.
Defaults to returning the ``name`` attribute.
"""
return obj.name

View File

@ -111,8 +111,10 @@ class HorizonMiddleware(object):
timezone.activate(tz)
def process_exception(self, request, exception):
"""Catches internal Horizon exception classes such as NotAuthorized,
NotFound and Http302 and handles them gracefully.
"""Catches internal Horizon exception classes.
Exception classes such as NotAuthorized, NotFound and Http302
are caught and handles them gracefully.
"""
if isinstance(exception, (exceptions.NotAuthorized,
exceptions.NotAuthenticated)):
@ -155,8 +157,9 @@ class HorizonMiddleware(object):
dst[header] = src[header]
def process_response(self, request, response):
"""Convert HttpResponseRedirect to HttpResponse if request is via ajax
to allow ajax request to redirect url
"""Convert HttpResponseRedirect to HttpResponse if request is via ajax.
This is to allow ajax request to redirect url.
"""
if request.is_ajax() and hasattr(request, 'horizon'):
queued_msgs = request.horizon['async_messages']

View File

@ -42,8 +42,8 @@ STRING_SEPARATOR = "__"
class BaseActionMetaClass(type):
"""Metaclass for adding all actions options from inheritance tree
to action.
"""Metaclass for adding all actions options from inheritance tree to action.
This way actions can inherit from each other but still use
the class attributes DSL. Meaning, all attributes of Actions are
defined as class attributes, but in the background, it will be used as
@ -99,6 +99,7 @@ class BaseAction(html.HTMLElement):
def data_type_matched(self, datum):
"""Method to see if the action is allowed for a certain type of data.
Only affects mixed data type tables.
"""
if datum:
@ -148,13 +149,15 @@ class BaseAction(html.HTMLElement):
pass
def get_default_classes(self):
"""Returns a list of the default classes for the action. Defaults to
``["btn", "btn-default", "btn-sm"]``.
"""Returns a list of the default classes for the action.
Defaults to ``["btn", "btn-default", "btn-sm"]``.
"""
return getattr(settings, "ACTION_CSS_CLASSES", ACTION_CSS_CLASSES)
def get_default_attrs(self):
"""Returns a list of the default HTML attributes for the action.
Defaults to returning an ``id`` attribute with the value
``{{ table.name }}__action_{{ action.name }}__{{ creation counter }}``.
"""
@ -527,9 +530,7 @@ class FilterAction(BaseAction):
return data
def is_api_filter(self, filter_field):
"""Determine if the given filter field should be used as an
API filter.
"""
"""Determine if agiven filter field should be used as an API filter."""
if self.filter_type == 'server':
for choice in self.filter_choices:
if (choice[0] == filter_field and len(choice) > 2 and
@ -538,8 +539,9 @@ class FilterAction(BaseAction):
return False
def get_select_options(self):
"""Provide the value, string, and help_text (if applicable)
for the template to render.
"""Provide the value, string, and help_text for the template to render.
help_text is returned if applicable.
"""
if self.filter_choices:
return [choice[:4] for choice in self.filter_choices
@ -579,8 +581,7 @@ class FixedFilterAction(FilterAction):
return self.categories[filter_string]
def get_fixed_buttons(self):
"""Returns a list of dictionaries describing the fixed buttons
to use for filtering.
"""Returns a list of dict describing fixed buttons used for filtering.
Each list item should be a dict with the following keys:
@ -605,9 +606,9 @@ class FixedFilterAction(FilterAction):
class BatchAction(Action):
"""A table action which takes batch action on one or more
objects. This action should not require user input on a
per-object basis.
"""A table action which takes batch action on one or more objects.
This action should not require user input on a per-object basis.
.. attribute:: name
@ -723,8 +724,9 @@ class BatchAction(Action):
return action
def action(self, request, datum_id):
"""Required. Accepts a single object id and performs the specific
action.
"""Accepts a single object id and performs the specific action.
This method is required.
Return values are discarded, errors raised are caught and logged.
"""

View File

@ -363,8 +363,9 @@ class Column(html.HTMLElement):
return '<%s: %s>' % (self.__class__.__name__, self.name)
def allowed(self, request):
"""Determine whether processing/displaying the column is allowed
for the current request.
"""Determine whether processing/displaying the column is allowed.
It is determined based on the current request.
"""
if not self.policy_rules:
return True
@ -376,9 +377,10 @@ class Column(html.HTMLElement):
return True
def get_raw_data(self, datum):
"""Returns the raw data for this column, before any filters or
formatting are applied to it. This is useful when doing calculations
on data in the table.
"""Returns the raw data for this column.
No filters or formatting are applied to the returned data.
This is useful when doing calculations on data in the table.
"""
# Callable transformations
if callable(self.transform):
@ -397,8 +399,7 @@ class Column(html.HTMLElement):
return data
def get_data(self, datum):
"""Returns the final display data for this column from the given
inputs.
"""Returns the final display data for this column from the given inputs.
The return value will be either the attribute specified for this column
or the return value of the attr:`~horizon.tables.Column.transform`
@ -471,8 +472,10 @@ class Column(html.HTMLElement):
return attrs
def get_summation(self):
"""Returns the summary value for the data in this column if a
valid summation method is specified for it. Otherwise returns ``None``.
"""Returns the summary value for the data in this column.
It returns the summary value if a valid summation method is
specified for it. Otherwise returns ``None``.
"""
if self.summation not in self.summation_methods:
return None
@ -577,11 +580,14 @@ class Row(html.HTMLElement):
self.cells = []
def load_cells(self, datum=None):
"""Load the row's data (either provided at initialization or as an
argument to this function), initialize all the cells contained
by this row, and set the appropriate row properties which require
"""Load the row's data and initialize all the cells in the row.
It also set the appropriate row properties which require
the row's data to be determined.
The row's data is provided either at initialization or as an
argument to this function.
This function is called automatically by
:meth:`~horizon.tables.Row.__init__` if the ``datum`` argument is
provided. However, by not providing the data during initialization
@ -665,14 +671,17 @@ class Row(html.HTMLElement):
return "%s?%s" % (table_url, params)
def can_be_selected(self, datum):
"""By default if multiselect enabled return True. You can remove the
checkbox after an ajax update here if required.
"""Determines whether the row can be selected.
By default if multiselect enabled return True.
You can remove the checkbox after an ajax update here if required.
"""
return True
def get_data(self, request, obj_id):
"""Fetches the updated data for the row based on the object id
passed in. Must be implemented by a subclass to allow AJAX updating.
"""Fetches the updated data for the row based on the given object ID.
Must be implemented by a subclass to allow AJAX updating.
"""
return {}
@ -1322,9 +1331,11 @@ class DataTable(object):
return str(slugify(self._meta.name))
def get_filter_string(self):
"""Get the filter string value. For 'server' type filters this is
saved in the session so that it gets persisted across table loads.
For other filter types this is obtained from the POST dict.
"""Get the filter string value.
For 'server' type filters this is saved in the session so that
it gets persisted across table loads. For other filter types
this is obtained from the POST dict.
"""
filter_action = self._meta._filter_action
param_name = filter_action.get_param_name()
@ -1336,8 +1347,9 @@ class DataTable(object):
return filter_string
def get_filter_field(self):
"""Get the filter field value used for 'server' type filters. This
is the value from the filter action's list of filter choices.
"""Get the filter field value used for 'server' type filters.
This is the value from the filter action's list of filter choices.
"""
filter_action = self._meta._filter_action
param_name = '%s_field' % filter_action.get_param_name()
@ -1406,15 +1418,19 @@ class DataTable(object):
return self._no_data_message
def get_filter_first_message(self):
"""Return the message to be displayed when the user needs to provide
first a search criteria before loading any data.
"""Return the message to be displayed first in the filter.
when the user needs to provide a search criteria first
before loading any data.
"""
return self._filter_first_message
def get_object_by_id(self, lookup):
"""Returns the data object from the table's dataset which matches
the ``lookup`` parameter specified. An error will be raised if
the match is not a single data object.
"""Returns the data object whose ID matches ``loopup`` parameter.
The data object is looked up from the table's dataset and
the data which matches the ``lookup`` parameter specified.
An error will be raised if the match is not a single data object.
We will convert the object id and ``lookup`` to unicode before
comparison.
@ -1445,8 +1461,9 @@ class DataTable(object):
@property
def has_actions(self):
"""Boolean. Indicates whether there are any available actions on this
table.
"""Indicates whether there are any available actions on this table.
Returns a boolean value.
"""
if not self.base_actions:
return False
@ -1454,8 +1471,9 @@ class DataTable(object):
@property
def needs_form_wrapper(self):
"""Boolean. Indicates whether this table should be rendered wrapped in
a ``<form>`` tag or not.
"""Returns if this table should be rendered wrapped in a ``<form>`` tag.
Returns a boolean value.
"""
# If needs_form_wrapper is explicitly set, defer to that.
if self._needs_form_wrapper is not None:
@ -1532,8 +1550,10 @@ class DataTable(object):
return table_actions_template.render(context)
def render_row_actions(self, datum, row=False):
"""Renders the actions specified in ``Meta.row_actions`` using the
current row data. If `row` is True, the actions are rendered in a row
"""Renders the actions specified in ``Meta.row_actions``.
The actions are rendered using the current row data.
If `row` is True, the actions are rendered in a row
of buttons. Otherwise they are rendered in a dropdown box.
"""
if row:
@ -1550,8 +1570,9 @@ class DataTable(object):
@staticmethod
def parse_action(action_string):
"""Parses the ``action`` parameter (a string) sent back with the
POST data. By default this parses a string formatted as
"""Parses the ``action_string`` parameter sent back with the POST data.
By default this parses a string formatted as
``{{ table_name }}__{{ action_name }}__{{ row_id }}`` and returns
each of the pieces. The ``row_id`` is optional.
"""
@ -1568,10 +1589,10 @@ class DataTable(object):
return table, action, object_id
def take_action(self, action_name, obj_id=None, obj_ids=None):
"""Locates the appropriate action and routes the object
data to it. The action should return an HTTP redirect
if successful, or a value which evaluates to ``False``
if unsuccessful.
"""Locates the appropriate action and routes the object data to it.
The action should return an HTTP redirect if successful,
or a value which evaluates to ``False`` if unsuccessful.
"""
# See if we have a list of ids
obj_ids = obj_ids or self.request.POST.getlist('object_ids')
@ -1616,8 +1637,10 @@ class DataTable(object):
return table, action, obj_id
def maybe_preempt(self):
"""Determine whether the request should be handled by a preemptive
action on this table or by an AJAX row update before loading any data.
"""Determine whether the request should be handled in earlier phase.
It determines the request should be handled by a preemptive action
on this table or by an AJAX row update before loading any data.
"""
request = self.request
table_name, action_name, obj_id = self.check_handler(request)
@ -1705,8 +1728,7 @@ class DataTable(object):
return HttpResponse(status=error.status_code)
def inline_update_action(self, request, datum, cell, obj_id, cell_name):
"""Handling update by POST of the cell.
"""
"""Handling update by POST of the cell."""
new_cell_value = request.POST.get(
cell_name + '__' + obj_id, None)
if issubclass(cell.column.form_field.__class__,
@ -1742,7 +1764,9 @@ class DataTable(object):
content_type="application/json")
def maybe_handle(self):
"""Determine whether the request should be handled by any action on
"""Handles table actions if needed.
It determines whether the request should be handled by any action on
this table after data has been loaded.
"""
request = self.request
@ -1756,8 +1780,10 @@ class DataTable(object):
return None
def sanitize_id(self, obj_id):
"""Override to modify an incoming obj_id to match existing
API data types or modify the format.
"""Override to modify an incoming obj_id to match existing API.
It is used to modify an incoming obj_id (used in Horizon)
to the data type or format expected by the API.
"""
return obj_id
@ -1787,8 +1813,10 @@ class DataTable(object):
return getattr(datum, display_key, None)
def has_prev_data(self):
"""Returns a boolean value indicating whether there is previous data
available to this table from the source (generally an API).
"""Returns a boolean value indicating whether there is previous data.
Returns True if there is previous data available to this table
from the source (generally an API).
The method is largely meant for internal use, but if you want to
override it to provide custom behavior you can do so at your own risk.
@ -1796,8 +1824,10 @@ class DataTable(object):
return self._meta.has_prev_data
def has_more_data(self):
"""Returns a boolean value indicating whether there is more data
available to this table from the source (generally an API).
"""Returns a boolean value indicating whether there is more data.
Returns True if there is more data available to this table
from the source (generally an API).
The method is largely meant for internal use, but if you want to
override it to provide custom behavior you can do so at your own risk.
@ -1805,35 +1835,35 @@ class DataTable(object):
return self._meta.has_more_data
def get_prev_marker(self):
"""Returns the identifier for the first object in the current data set
for APIs that use marker/limit-based paging.
"""Returns the identifier for the first object in the current data set.
The return value will be used as marker/limit-based paging in the API.
"""
return http.urlquote_plus(self.get_object_id(self.data[0])) \
if self.data else ''
def get_marker(self):
"""Returns the identifier for the last object in the current data set
for APIs that use marker/limit-based paging.
"""Returns the identifier for the last object in the current data set.
The return value will be used as marker/limit-based paging in the API.
"""
return http.urlquote_plus(self.get_object_id(self.data[-1])) \
if self.data else ''
def get_prev_pagination_string(self):
"""Returns the query parameter string to paginate this table
to the previous page.
"""
"""Returns the query parameter string to paginate to the prev page."""
return "=".join([self._meta.prev_pagination_param,
self.get_prev_marker()])
def get_pagination_string(self):
"""Returns the query parameter string to paginate this table
to the next page.
"""
"""Returns the query parameter string to paginate to the next page."""
return "=".join([self._meta.pagination_param, self.get_marker()])
def calculate_row_status(self, statuses):
"""Returns a boolean value determining the overall row status
based on the dictionary of column name to status mappings passed in.
"""Returns a boolean value determining the overall row status.
It is detremined based on the dictionary of column name
to status mappings passed in.
By default, it uses the following logic:
@ -1858,9 +1888,10 @@ class DataTable(object):
return True
def get_row_status_class(self, status):
"""Returns a css class name determined by the status value. This class
name is used to indicate the status of the rows in the table if
any ``status_columns`` have been specified.
"""Returns a css class name determined by the status value.
This class name is used to indicate the status of the rows in the table
if any ``status_columns`` have been specified.
"""
if status is True:
return "status_up"

View File

@ -158,8 +158,9 @@ class MultiTableMixin(object):
return filter_info
def handle_server_filter(self, request, table=None):
"""Update the table server filter information in the session and
determine if the filter has been changed.
"""Update the table server filter information in the session.
Returns True if the filter has been changed.
"""
if not table:
table = self.get_table()
@ -172,9 +173,10 @@ class MultiTableMixin(object):
return filter_info['changed']
def update_server_filter_action(self, request, table=None):
"""Update the table server side filter action based on the current
filter. The filter info may be stored in the session and this will
restore it.
"""Update the table server side filter action.
It is done based on the current filter. The filter info may be stored
in the session and this will restore it.
"""
if not table:
table = self.get_table()
@ -187,8 +189,10 @@ class MultiTableMixin(object):
class MultiTableView(MultiTableMixin, views.HorizonTemplateView):
"""A class-based generic view to handle the display and processing of
multiple :class:`~horizon.tables.DataTable` classes in a single view.
"""Generic view to handle multiple DataTable classes in a single view.
Each DataTable class must be a :class:`~horizon.tables.DataTable` class
or its subclass.
Three steps are required to use this view: set the ``table_classes``
attribute with a tuple of the desired
@ -309,8 +313,7 @@ class DataTableView(MultiTableView):
class MixedDataTableView(DataTableView):
"""A class-based generic view to handle DataTable with mixed data
types.
"""A class-based generic view to handle DataTable with mixed data types.
Basic usage is the same as DataTableView.

View File

@ -30,8 +30,7 @@ CSS_DISABLED_TAB_CLASSES = ["disabled"]
class TabGroup(html.HTMLElement):
"""A container class which knows how to manage and render
:class:`~horizon.tabs.Tab` objects.
"""A container class which knows how to manage and render Tab objects.
.. attribute:: slug
@ -128,21 +127,26 @@ class TabGroup(html.HTMLElement):
exceptions.handle(self.request)
def get_id(self):
"""Returns the id for this tab group. Defaults to the value of the tab
"""Returns the id for this tab group.
Defaults to the value of the tab
group's :attr:`horizon.tabs.Tab.slug`.
"""
return self.slug
def get_default_classes(self):
"""Returns a list of the default classes for the tab group. Defaults to
``["nav", "nav-tabs", "ajax-tabs"]``.
"""Returns a list of the default classes for the tab group.
Defaults to ``["nav", "nav-tabs", "ajax-tabs"]``.
"""
default_classes = super(TabGroup, self).get_default_classes()
default_classes.extend(CSS_TAB_GROUP_CLASSES)
return default_classes
def tabs_not_available(self):
"""In the event that no tabs are either allowed or enabled, this method
"""The fallback handler if no tabs are either allowed or enabled.
In the event that no tabs are either allowed or enabled, this method
is the fallback handler. By default it's a no-op, but it exists
to make redirecting or raising exceptions possible for subclasses.
"""
@ -214,8 +218,7 @@ class TabGroup(html.HTMLElement):
class Tab(html.HTMLElement):
"""A reusable interface for constructing a tab within a
:class:`~horizon.tabs.TabGroup`.
"""A reusable interface for constructing a tab within a TabGroup.
.. attribute:: name
@ -301,9 +304,10 @@ class Tab(html.HTMLElement):
return getattr(self, "_data", None) is not None
def render(self):
"""Renders the tab to HTML using the
"""Renders the tab to HTML.
:meth:`~horizon.tabs.Tab.get_context_data` method and
the :meth:`~horizon.tabs.Tab.get_template_name` method.
the :meth:`~horizon.tabs.Tab.get_template_name` method are called.
If :attr:`~horizon.tabs.Tab.preload` is ``False`` and ``force_load``
is not ``True``, or
@ -323,8 +327,9 @@ class Tab(html.HTMLElement):
return render_to_string(self.get_template_name(self.request), context)
def get_id(self):
"""Returns the id for this tab. Defaults to
``"{{ tab_group.slug }}__{{ tab.slug }}"``.
"""Returns the id for this tab.
Defaults to ``"{{ tab_group.slug }}__{{ tab.slug }}"``.
"""
return SEPARATOR.join([self.tab_group.slug, self.slug])
@ -332,9 +337,10 @@ class Tab(html.HTMLElement):
return "=".join((self.tab_group.param_name, self.get_id()))
def get_default_classes(self):
"""Returns a list of the default classes for the tab. Defaults to
and empty list (``[]``), however additional classes may be added
depending on the state of the tab as follows:
"""Returns a list of the default classes for the tab.
Defaults to and empty list (``[]``), however additional classes may
be added depending on the state of the tab as follows:
If the tab is the active tab for the tab group, in which
the class ``"active"`` will be added.
@ -362,14 +368,17 @@ class Tab(html.HTMLElement):
return self.template_name
def get_context_data(self, request, **kwargs):
"""This method should return a dictionary of context data used to
render the tab. Required.
"""Return a dictionary of context data used to render the tab.
Required.
"""
return kwargs
def enabled(self, request):
"""Determines whether or not the tab should be accessible
(e.g. be rendered into the HTML on load and respond to a click event).
"""Determines whether or not the tab should be accessible.
For example, the tab should be rendered into the HTML
on load and respond to a click event.
If a tab returns ``False`` from ``enabled`` it will ignore the value
of ``preload`` and only render the HTML of the tab after being clicked.
@ -400,8 +409,7 @@ class Tab(html.HTMLElement):
class TableTab(Tab):
"""A :class:`~horizon.tabs.Tab` class which knows how to deal with
:class:`~horizon.tables.DataTable` classes rendered inside of it.
"""A Tab class which knows how to deal with DataTable classes inside of it.
This distinct class is required due to the complexity involved in handling
both dynamic tab loading, dynamic table updating and table actions all
@ -432,8 +440,9 @@ class TableTab(Tab):
self._table_data_loaded = False
def load_table_data(self):
"""Calls the ``get_{{ table_name }}_data`` methods for each table class
and sets the data on the tables.
"""Calls the ``get_{{ table_name }}_data`` methods for each table class.
When returning, the loaded data is set on the tables.
"""
# We only want the data to be loaded once, so we track if we have...
if not self._table_data_loaded:
@ -456,8 +465,10 @@ class TableTab(Tab):
self._table_data_loaded = True
def get_context_data(self, request, **kwargs):
"""Adds a ``{{ table_name }}_table`` item to the context for each table
in the :attr:`~horizon.tabs.TableTab.table_classes` attribute.
"""Adds a ``{{ table_name }}_table`` item to the context for each table.
The target tables are specified by
the :attr:`~horizon.tabs.TableTab.table_classes` attribute.
If only one table class is provided, a shortcut ``table`` context
variable is also added containing the single table.

View File

@ -19,8 +19,7 @@ from horizon import views
class TabView(views.HorizonTemplateView):
"""A generic class-based view for displaying a
:class:`horizon.tabs.TabGroup`.
"""A generic view for displaying a :class:`horizon.tabs.TabGroup`.
This view handles selecting specific tabs and deals with AJAX requests
gracefully.
@ -57,8 +56,9 @@ class TabView(views.HorizonTemplateView):
return context
def handle_tabbed_response(self, tab_group, context):
"""Sends back an AJAX-appropriate response for the tab group if
required, otherwise renders the response as normal.
"""Sends back an AJAX-appropriate response for the tab group if needed.
Otherwise renders the response as normal.
"""
if self.request.is_ajax():
if tab_group.selected:
@ -79,10 +79,11 @@ class TabbedTableView(tables.MultiTableMixin, TabView):
self._table_dict = {}
def load_tabs(self):
"""Loads the tab group, and compiles the table instances for each
table attached to any :class:`horizon.tabs.TableTab` instances on
the tab group. This step is necessary before processing any
tab or table actions.
"""Loads the tab group.
It compiles the table instances for each table attached to
any :class:`horizon.tabs.TableTab` instances on the tab group.
This step is necessary before processing any tab or table actions.
"""
tab_group = self.get_tabs(self.request, **self.kwargs)
tabs = tab_group.get_tabs()
@ -99,10 +100,12 @@ class TabbedTableView(tables.MultiTableMixin, TabView):
return {}
def handle_table(self, table_dict):
"""For the given dict containing a ``DataTable`` and a ``TableTab``
"""Loads the table data based on a given table_dict and handles them.
For the given dict containing a ``DataTable`` and a ``TableTab``
instance, it loads the table data for that tab and calls the
table's :meth:`~horizon.tables.DataTable.maybe_handle` method. The
return value will be the result of ``maybe_handle``.
table's :meth:`~horizon.tables.DataTable.maybe_handle` method.
The return value will be the result of ``maybe_handle``.
"""
table = table_dict['table']
tab = table_dict['tab']

View File

@ -25,12 +25,13 @@ register = template.Library()
@receiver(post_compress)
def update_angular_template_hash(sender, **kwargs):
"""Listen for compress events. If the angular templates
have been re-compressed, also clear them from the Django
cache backend. This is important to allow deployers to
change a template file, re-compress, and not accidentally
serve the old Django cached version of that content to
clients.
"""Listen for compress events.
If the angular templates have been re-compressed, also clear them
from the Django cache backend. This is important to allow
deployers to change a template file, re-compress, and not
accidentally serve the old Django cached version of that content
to clients.
"""
context = kwargs['context'] # context the compressor is working with
compressed = context['compressed'] # the compressed content
@ -55,9 +56,11 @@ def update_angular_template_hash(sender, **kwargs):
@register.filter(name='angular_escapes')
def angular_escapes(value):
"""Djangos 'escapejs' is too aggressive and inserts unicode. Provide
a basic filter to allow angular template content to be used within
javascript strings.
"""Provide a basic filter to allow angular template content for Angular.
Djangos 'escapejs' is too aggressive and inserts unicode.
It provide a basic filter to allow angular template content to be used
within javascript strings.
Args:
value: a string
@ -75,10 +78,11 @@ def angular_escapes(value):
@register.inclusion_tag('angular/angular_templates.html', takes_context=True)
def angular_templates(context):
"""For all static HTML templates, generate a dictionary of template
contents. If the template has been overridden by a theme, load the
override contents instead of the original HTML file. One use for
this is to pre-populate the angular template cache.
"""Generate a dictionary of template contents for all static HTML templates.
If the template has been overridden by a theme, load the
override contents instead of the original HTML file.
One use for this is to pre-populate the angular template cache.
Args:
context: the context of the current Django template

View File

@ -24,6 +24,7 @@ register = template.Library()
@register.inclusion_tag('bootstrap/progress_bar.html')
def bs_progress_bar(*args, **kwargs):
"""A Standard Bootstrap Progress Bar.
http://getbootstrap.com/components/#progress
param args (Array of Numbers: 0-100): Percent of Progress Bars

View File

@ -21,6 +21,7 @@ register = template.Library()
takes_context=True)
def breadcrumb_nav(context):
"""A logic heavy function for automagically creating a breadcrumb.
It uses the dashboard, panel group(if it exists), then current panel.
Can also use a "custom_breadcrumb" context item to add extra items.
"""

View File

@ -43,9 +43,7 @@ class MinifiedNode(Node):
@register.filter
def has_permissions(user, component):
"""Checks if the given user meets the permissions requirements for
the component.
"""
"""Checks if the given user meets the permissions requirements."""
return user.has_perms(getattr(component, 'permissions', set()))
@ -188,7 +186,9 @@ class JSTemplateNode(template.Node):
@register.tag
def jstemplate(parser, token):
"""Replaces ``[[[`` and ``]]]`` with ``{{{`` and ``}}}``,
"""Templatetag to handle any of the Mustache-based templates.
Replaces ``[[[`` and ``]]]`` with ``{{{`` and ``}}}``,
``[[`` and ``]]`` with ``{{`` and ``}}`` and
``[%`` and ``%]`` with ``{%`` and ``%}`` to avoid conflicts
with Django's template engine when using any of the Mustache-based

View File

@ -31,9 +31,7 @@ register = template.Library()
class ParseDateNode(template.Node):
def render(self, datestring):
"""Parses a date-like input string into a timezone aware Python
datetime.
"""
"""Parses a date-like string into a timezone aware Python datetime."""
formats = ["%Y-%m-%dT%H:%M:%S.%f", "%Y-%m-%d %H:%M:%S.%f",
"%Y-%m-%dT%H:%M:%S", "%Y-%m-%d %H:%M:%S"]
if datestring:

View File

@ -115,8 +115,7 @@ class RequestFactoryWithMessages(RequestFactory):
@unittest.skipIf(os.environ.get('SKIP_UNITTESTS', False),
"The SKIP_UNITTESTS env variable is set.")
class TestCase(django_test.TestCase):
"""Specialized base test case class for Horizon which gives access to
numerous additional features:
"""Base test case class for Horizon with numerous additional features.
* The ``mox`` mocking framework via ``self.mox``.
* A ``RequestFactory`` class which supports Django's ``contrib.messages``
@ -176,15 +175,17 @@ class TestCase(django_test.TestCase):
self.assertNotRegex(text, unexpected_regexp, msg)
def assertNoMessages(self, response=None):
"""Asserts that no messages have been attached by the
``contrib.messages`` framework.
"""Asserts no messages have been attached by the messages framework.
The expected messages framework is ``django.contrib.messages``.
"""
self.assertMessageCount(response, success=0, warn=0, info=0, error=0)
def assertMessageCount(self, response=None, **kwargs):
"""Asserts that the specified number of messages have been attached
for various message types. Usage would look like
``self.assertMessageCount(success=1)``.
"""Asserts that the expected number of messages have been attached.
The expected number of messages can be specified per message type.
Usage would look like ``self.assertMessageCount(success=1)``.
"""
temp_req = self.client.request(**{'wsgi.input': None})
temp_req.COOKIES = self.client.cookies
@ -255,9 +256,9 @@ class SeleniumTestCase(LiveServerTestCase):
class JasmineTests(SeleniumTestCase):
"""Helper class which allows you to create a simple Jasmine test running
through Selenium
"""Helper class which allows you to create a simple Jasmine test.
Jasmine tests are run through Selenium.
To run a jasmine test suite, create a class which extends JasmineTests in
the :file:`horizon/test/jasmine/jasmine_tests.py` and define two class
attributes

View File

@ -21,10 +21,12 @@ from horizon.test import helpers
class HackingTestCase(helpers.TestCase):
"""This class tests the hacking checks in horizon.hacking.checks by passing
strings to the check methods like the pep8/flake8 parser would. The parser
loops over each line in the file and then passes the parameters to the
check method. The parameter names in the check method dictate what type of
"""This class tests the hacking checks in horizon.hacking.checks.
Each check passes passing strings to the check methods like the
pep8/flake8 parser would. The parser loops over each line in the
file and then passes the parameters to the check method.
The parameter names in the check method dictate what type of
object is passed to the check method. The parameter types are:
logical_line

View File

@ -1,4 +1,4 @@
# -*- encoding: UTF-8 -*-
# -*- encoding: utf-8 -*-
# Copyright 2015, Rackspace, US, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -44,17 +44,14 @@ class ExtractAngularTestCase(test.TestCase):
messages)
def test_attr_value(self):
"""We should not translate tags that have translate as the value of an
attribute.
"""
"""Should not translate tags with translate as the value of an attr."""
buf = StringIO('<html><div id="translate">hello world!</div></html>')
messages = list(extract_angular(buf, [], [], {}))
self.assertEqual([], messages)
def test_attr_value_plus_directive(self):
"""Unless they also have a translate directive.
"""
"""Unless they also have a translate directive."""
buf = StringIO(
'<html><div id="translate" translate>hello world!</div></html>')

View File

@ -118,10 +118,11 @@ class BaseHorizonTests(test.TestCase):
dash.register(panel)
def _reload_urls(self):
"""Clears out the URL caches, reloads the root urls module, and
re-triggers the autodiscovery mechanism for Horizon. Allows URLs
to be re-calculated after registering new dashboards. Useful
only for testing and should never be used on a live site.
"""Clears out the URL caches, and reloads the root urls module.
It re-triggers the autodiscovery mechanism for Horizon.
Allows URLs to be re-calculated after registering new dashboards.
Useful only for testing and should never be used on a live site.
"""
urlresolvers.clear_url_caches()
moves.reload_module(import_module(settings.ROOT_URLCONF))
@ -371,8 +372,9 @@ class GetUserHomeTests(BaseHorizonTests):
class CustomPanelTests(BaseHorizonTests):
"""Test customization of dashboards and panels
using 'customization_module' to HORIZON_CONFIG.
"""Test customization of dashboards and panels.
This tests customization using 'customization_module' to HORIZON_CONFIG.
"""
def setUp(self):
@ -407,8 +409,9 @@ class CustomPanelTests(BaseHorizonTests):
class CustomPermissionsTests(BaseHorizonTests):
"""Test customization of permissions on panels
using 'customization_module' to HORIZON_CONFIG.
"""Test customization of permissions on panels.
This tests customization using 'customization_module' to HORIZON_CONFIG.
"""
def setUp(self):

View File

@ -71,50 +71,42 @@ class FinderTests(unittest.TestCase):
# discover_files()
#
def test_find_all(self):
"""Find all files
"""
"""Find all files"""
files = fd.discover_files(base_path)
self.assertEqual(len(files), 18)
def test_find_a(self):
"""Find all files in folder `a`
"""
"""Find all files in folder `a`"""
files = fd.discover_files(base_path, sub_path='a')
self.assertEqual(len(files), 8)
def test_find_b(self):
"""Find all files in folder `b`
"""
"""Find all files in folder `b`"""
files = fd.discover_files(base_path, sub_path='b')
self.assertEqual(len(files), 10)
def test_find_all_js(self):
"""Find all files with extension of `.js`
"""
"""Find all files with extension of `.js`"""
files = fd.discover_files(base_path, ext='.js')
self.assertEqual(len(files), 14)
def test_find_all_html(self):
"""Find all files with extension of `.html`
"""
"""Find all files with extension of `.html`"""
files = fd.discover_files(base_path, ext='.html')
self.assertEqual(len(files), 2)
def test_find_all_js_in_a(self):
"""Find all files with extension of `.js` in folder a
"""
"""Find all files with extension of `.js` in folder a"""
files = fd.discover_files(base_path, sub_path='b', ext='.js')
self.assertEqual(len(files), 7)
def test_find_all_html_in_a(self):
"""Find all files with extension of `.html` in folder a
"""
"""Find all files with extension of `.html` in folder a"""
files = fd.discover_files(base_path, sub_path='b', ext='.html')
self.assertEqual(len(files), 1)
def test_find_all_file_trim_base_path(self):
"""Find all files in folder, trim base path
"""
"""Find all files in folder, trim base path"""
files = fd.discover_files(base_path, sub_path='a', trim_base_path=True)
self.assertTrue(files[0].startswith('a/'))
@ -125,8 +117,7 @@ class FinderTests(unittest.TestCase):
# sort_js_files()
#
def test_sort_js_files(self):
"""Sort all JavaScript files
"""
"""Sort all JavaScript files"""
files = fd.discover_files(base_path, ext='.js')
sources, mocks, specs = fd.sort_js_files(files)
@ -156,8 +147,7 @@ class FinderTests(unittest.TestCase):
# discover_static_files()
#
def test_discover_all_static_files(self):
"""Find all static files
"""
"""Find all static files"""
sources, mocks, specs, templates = fd.discover_static_files(base_path)
self.assertEqual(len(sources), 6)
self.assertEqual(len(mocks), 2)
@ -189,8 +179,7 @@ class FinderTests(unittest.TestCase):
# populate_horizon_config()
#
def test_populate_horizon_config(self):
"""Populate horizon config
"""
"""Populate horizon config"""
horizon_config = {}
fd.populate_horizon_config(horizon_config, base_path)

View File

@ -12,9 +12,7 @@
class ObjDictWrapper(dict):
"""ObjDictWrapper is a container that provides both dictionary-like and
object-like attribute access.
"""
"""Wrapper that provides both dict- and object-like attribute access."""
def __getattr__(self, item):
if item in self:
return self[item]

View File

@ -109,8 +109,7 @@ class WebElementWrapper(WrapperFindOverride, webelement.WebElement):
class WebDriverWrapper(WrapperFindOverride, WebDriver):
"""Wrapper for webdriver to return WebElementWrapper on find_element.
"""
"""Wrapper for webdriver to return WebElementWrapper on find_element."""
def reload_request(self, locator, index):
try:
# element was found out via find_elements

View File

@ -87,9 +87,11 @@ def offline_context():
# A piece of middleware that stores the theme cookie value into
# local thread storage so the template loader can access it
class ThemeMiddleware(object):
"""The Theme Middleware component. The custom template loaders
don't have access to the request object, so we need to store
the Cookie's theme value for use later in the Django chain.
"""The Theme Middleware component.
The custom template loaders
don't have access to the request object, so we need to store
the Cookie's theme value for use later in the Django chain.
"""
def process_request(self, request):
@ -111,8 +113,10 @@ class ThemeMiddleware(object):
class ThemeTemplateLoader(tLoaderCls):
"""Themes can contain template overrides, so we need to check the
theme directory first, before loading any of the standard templates.
"""Theme-aware template loader.
Themes can contain template overrides, so we need to check the
theme directory first, before loading any of the standard templates.
"""
is_usable = True

View File

@ -1,4 +1,4 @@
# -*- encoding: UTF-8 -*-
# -*- encoding: utf-8 -*-
# Copyright 2015, Rackspace, US, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -144,9 +144,11 @@ class AngularGettextHTMLParser(html_parser.HTMLParser):
def extract_angular(fileobj, keywords, comment_tags, options):
"""Extract messages from angular template (HTML) files that use the
"""Extract messages from angular template (HTML) files.
It extract messages from angular template (HTML) files that use
angular-gettext translate directive as per
https://angular-gettext.rocketeer.be/ .
https://angular-gettext.rocketeer.be/
:param fileobj: the file-like object the messages should be extracted
from

View File

@ -23,8 +23,7 @@ SPEC_EXT = '.spec.js'
def discover_files(base_path, sub_path='', ext='', trim_base_path=False):
"""Discovers all files with certain extension in given paths.
"""
"""Discovers all files with certain extension in given paths."""
file_list = []
for root, dirs, files in walk(path.join(base_path, sub_path)):
if trim_base_path:
@ -36,8 +35,10 @@ def discover_files(base_path, sub_path='', ext='', trim_base_path=False):
def sort_js_files(js_files):
"""Sorts JavaScript files in `js_files` into source files, mock files
and spec files based on file extension.
"""Sorts JavaScript files in `js_files`.
It sorts JavaScript files in a given `js_files`
into source files, mock files and spec files based on file extension.
Output:
@ -72,8 +73,10 @@ def sort_js_files(js_files):
def discover_static_files(base_path, sub_path=''):
"""Discovers static files in given paths, returning JavaScript sources,
mocks, specs and HTML templates, all grouped in lists.
"""Discovers static files in given paths.
It returns JavaScript sources, mocks, specs and HTML templates,
all grouped in lists.
"""
js_files = discover_files(base_path, sub_path=sub_path,
ext='.js', trim_base_path=True)
@ -105,8 +108,7 @@ def populate_horizon_config(horizon_config, base_path,
def _log(file_list, list_name, in_path):
"""Logs result at debug level
"""
"""Logs result at debug level"""
file_names = '\n'.join(file_list)
LOG.debug("\nDiscovered %(size)d %(name)s file(s) in %(path)s:\n"
"%(files)s\n",

View File

@ -30,9 +30,8 @@ def replace_underscores(string):
@register.filter
def parse_isotime(timestr, default=None):
"""This duplicates oslo timeutils parse_isotime but with a
@register.filter annotation and a silent fallback on error.
"""
# This duplicates oslo timeutils parse_isotime but with a
# @register.filter annotation and a silent fallback on error.
try:
return iso8601.parse_date(timestr)
except (iso8601.ParseError, TypeError):
@ -41,8 +40,10 @@ def parse_isotime(timestr, default=None):
@register.filter
def timesince_or_never(dt, default=None):
"""Call the Django ``timesince`` filter, but return the string
*default* if *dt* is not a valid ``date`` or ``datetime`` object.
"""Call the Django ``timesince`` filter or a given default string.
It returns the string *default* if *dt* is not a valid ``date``
or ``datetime`` object.
When *default* is None, "Never" is returned.
"""
if default is None:

View File

@ -68,6 +68,7 @@ def logout_with_message(request, msg, redirect=True, status='success'):
def get_config_value(request, key, default, search_in_settings=True):
"""Retrieves the value of `key` from configuration in the following order:
- from the session; if not found there then
- from cookies; if not found there then
- from the settings file if `search_in_settings` is True,
@ -92,8 +93,7 @@ def get_config_value(request, key, default, search_in_settings=True):
def save_config_value(request, response, key, value):
"""Sets value of key `key` to `value` in both session and cookies.
"""
"""Sets value of key `key` to `value` in both session and cookies."""
request.session[key] = value
response.set_cookie(key, value, expires=one_year_from_now())
return response
@ -126,14 +126,18 @@ def natural_sort(attr):
def get_keys(tuple_of_tuples):
"""Processes a tuple of 2-element tuples and returns a tuple containing
"""Returns a tuple containing first component of each tuple.
It processes a tuple of 2-element tuples and returns a tuple containing
first component of each tuple.
"""
return tuple([t[0] for t in tuple_of_tuples])
def value_for_key(tuple_of_tuples, key):
"""Processes a tuple of 2-element tuples and returns the value
"""Returns a value containing to the given key.
It processes a tuple of 2-element tuples and returns the value
corresponding to the given key. If no value is found, the key is returned.
"""
for t in tuple_of_tuples:
@ -144,7 +148,9 @@ def value_for_key(tuple_of_tuples, key):
def next_key(tuple_of_tuples, key):
"""Processes a tuple of 2-element tuples and returns the key which comes
"""Returns the key which comes after the given key.
It processes a tuple of 2-element tuples and returns the key which comes
after the given key.
"""
for i, t in enumerate(tuple_of_tuples):
@ -156,7 +162,9 @@ def next_key(tuple_of_tuples, key):
def previous_key(tuple_of_tuples, key):
"""Processes a tuple of 2-element tuples and returns the key which comes
"""Returns the key which comes before the give key.
It Processes a tuple of 2-element tuples and returns the key which comes
before the given key.
"""
for i, t in enumerate(tuple_of_tuples):
@ -168,8 +176,9 @@ def previous_key(tuple_of_tuples, key):
def format_value(value):
"""Returns the given value rounded to one decimal place if it is a
decimal, or integer if it is an integer.
"""Returns the given value rounded to one decimal place if deciaml.
Returns the integer if an integer is given.
"""
value = decimal.Decimal(str(value))
if int(value) == value:

View File

@ -22,21 +22,21 @@ class HTMLElement(object):
self.classes = getattr(self, "classes", [])
def get_default_classes(self):
"""Returns an iterable of default classes which should be combined with
any other declared classes.
"""Returns an iterable of default classes.
They will be combined with any other declared classes.
"""
return []
def get_default_attrs(self):
"""Returns a dict of default attributes which should be combined with
other declared attributes.
"""Returns a dict of default attributes.
They will be combined with other declared attributes.
"""
return {}
def get_final_attrs(self, classes=True):
"""Returns a dict containing the final attributes of this element
which will be rendered.
"""
"""Returns a dict containing the final attributes to be rendered."""
final_attrs = copy.copy(self.get_default_attrs())
final_attrs.update(self.attrs)
if classes:
@ -57,14 +57,18 @@ class HTMLElement(object):
@property
def attr_string(self):
"""Returns a flattened string of HTML attributes based on the
"""Returns a flattened string of HTML attributes.
HTML attributes are flattened based on the
``attrs`` dict provided to the class.
"""
return flatatt(self.get_final_attrs())
@property
def attr_string_nc(self):
"""Returns a flattened string of HTML attributes based on the
"""Returns a flattened string of HTML attributes.
HTML attributes are flattened based on the
``attrs`` dict provided to the class.
"""
return flatatt(self.get_final_attrs(False))

View File

@ -33,9 +33,7 @@ ureg = pint.UnitRegistry()
def is_supported(unit):
"""Returns a bool indicating whether the unit specified is supported by
this module.
"""
"""Returns a bool indicating whether the unit specified is supported."""
return unit in functions.get_keys(INFORMATION_UNITS) + TIME_UNITS
@ -56,10 +54,11 @@ def is_larger(unit_1, unit_2):
def convert(value, source_unit, target_unit, fmt=False):
"""Converts value from source_unit to target_unit. Returns a tuple
containing the converted value and target_unit. Having fmt set to True
causes the value to be formatted to 1 decimal digit if it's a decimal or
be formatted as integer if it's an integer.
"""Converts value from source_unit to target_unit.
Returns a tuple containing the converted value and target_unit.
Having fmt set to True causes the value to be formatted to 1 decimal digit
if it's a decimal or be formatted as integer if it's an integer.
E.g:
@ -82,6 +81,7 @@ def convert(value, source_unit, target_unit, fmt=False):
def normalize(value, unit):
"""Converts the value so that it belongs to some expected range.
Returns the new value and new unit.
E.g:

View File

@ -44,8 +44,10 @@ class PageTitleMixin(object):
page_title = ""
def render_context_with_title(self, context):
"""This function takes in a context dict and uses it to render the
page_title variable, it then appends this title to the context using
"""Render a page title and insert it into the context.
This function takes in a context dict and uses it to render the
page_title variable. It then appends this title to the context using
the 'page_title' key. If there is already a page_title key defined in
context received then this function will do nothing.
"""
@ -59,8 +61,10 @@ class PageTitleMixin(object):
return context
def render_to_response(self, context):
"""This is an override of the default render_to_response function that
exists in the django generic views, this is here to inject the
"""render_to_response() with a page title.
This is an override of the default render_to_response function that
exists in the django generic views. This is here to inject the
page title into the context before the main template is rendered.
"""
@ -105,7 +109,9 @@ class APIView(HorizonTemplateView):
"""
def get_data(self, request, context, *args, **kwargs):
"""This method should handle any necessary API calls, update the
"""Load necessary API data into the context.
This method should handle any necessary API calls, update the
context object, and return the context object at the end.
"""
return context

View File

@ -79,11 +79,11 @@ class ActionMetaclass(forms.forms.DeclarativeFieldsMetaclass):
@six.python_2_unicode_compatible
@six.add_metaclass(ActionMetaclass)
class Action(forms.Form):
"""An ``Action`` represents an atomic logical interaction you can have with
the system. This is easier to understand with a conceptual example: in the
context of a "launch instance" workflow, actions would include "naming
the instance", "selecting an image", and ultimately "launching the
instance".
"""An ``Action`` represents an atomic logical interaction with the system.
This is easier to understand with a conceptual example: in the context of
a "launch instance" workflow, actions would include "naming the instance",
"selecting an image", and ultimately "launching the instance".
Because ``Actions`` are always interactive, they always provide form
controls, and thus inherit from Django's ``Form`` class. However, they
@ -192,9 +192,10 @@ class Action(forms.Form):
self.errors[NON_FIELD_ERRORS] = self.error_class([message])
def handle(self, request, context):
"""Handles any requisite processing for this action. The method should
return either ``None`` or a dictionary of data to be passed to
:meth:`~horizon.workflows.Step.contribute`.
"""Handles any requisite processing for this action.
The method should return either ``None`` or a dictionary of data
to be passed to :meth:`~horizon.workflows.Step.contribute`.
Returns ``None`` by default, effectively making it a no-op.
"""
@ -216,8 +217,9 @@ class MembershipAction(Action):
@six.python_2_unicode_compatible
class Step(object):
"""A step is a wrapper around an action which defines its context in a
workflow. It knows about details such as:
"""A wrapper around an action which defines its context in a workflow.
It knows about details such as:
* The workflow's context data (data passed from step to step).
@ -399,8 +401,9 @@ class Step(object):
return self._action
def prepare_action_context(self, request, context):
"""Allows for customization of how the workflow context is passed to
the action; this is the reverse of what "contribute" does to make the
"""Hook to customize how the workflow context is passed to the action.
This is the reverse of what "contribute" does to make the
action outputs sane for the workflow. Changes to the context are not
saved globally here. They are localized to the action.
@ -430,8 +433,9 @@ class Step(object):
return True
def contribute(self, data, context):
"""Adds the data listed in ``contributes`` to the workflow's shared
context. By default, the context is simply updated with all the data
"""Adds the data listed in ``contributes`` to the workflow's context.
By default, the context is simply updated with all the data
returned by the action.
Note that even if the value of one of the ``contributes`` keys is
@ -524,9 +528,10 @@ class UpdateMembersStep(Step):
@six.python_2_unicode_compatible
@six.add_metaclass(WorkflowMetaclass)
class Workflow(html.HTMLElement):
"""A Workflow is a collection of Steps. Its interface is very
straightforward, but it is responsible for handling some very
important tasks such as:
"""A Workflow is a collection of Steps.
Its interface is very straightforward, but it is responsible for handling
some very important tasks such as:
* Handling the injection, removal, and ordering of arbitrary steps.
@ -777,8 +782,7 @@ class Workflow(html.HTMLElement):
@classmethod
def unregister(cls, step_class):
"""Unregisters a :class:`~horizon.workflows.Step` from the workflow.
"""
"""Unregisters a :class:`~horizon.workflows.Step` from the workflow."""
try:
cls._cls_registry.remove(step_class)
except KeyError:
@ -786,14 +790,17 @@ class Workflow(html.HTMLElement):
return cls._unregister(step_class)
def validate(self, context):
"""Hook for custom context data validation. Should return a boolean
value or raise :class:`~horizon.exceptions.WorkflowValidationError`.
"""Hook for custom context data validation.
Should return a booleanvalue or
raise :class:`~horizon.exceptions.WorkflowValidationError`.
"""
return True
def is_valid(self):
"""Verified that all required data is present in the context and
calls the ``validate`` method to allow for finer-grained checks
"""Verifies that all required data is present in the context.
It also calls the ``validate`` method to allow for finer-grained checks
on the context data.
"""
missing = self.depends_on - set(self.context.keys())
@ -814,10 +821,12 @@ class Workflow(html.HTMLElement):
return self.validate(self.context)
def finalize(self):
"""Finalizes a workflow by running through all the actions in order
and calling their ``handle`` methods. Returns ``True`` on full success,
or ``False`` for a partial success, e.g. there were non-critical
errors. (If it failed completely the function wouldn't return.)
"""Finalizes a workflow by running through all the actions.
It runs all the actions in order and calling their ``handle`` methods.
Returns ``True`` on full success, or ``False`` for a partial success,
e.g. there were non-critical errors.
(If it failed completely the function wouldn't return.)
"""
partial = False
for step in self.steps:
@ -837,16 +846,18 @@ class Workflow(html.HTMLElement):
return not partial
def handle(self, request, context):
"""Handles any final processing for this workflow. Should return a
boolean value indicating success.
"""Handles any final processing for this workflow.
Should return a boolean value indicating success.
"""
return True
def get_success_url(self):
"""Returns a URL to redirect the user to upon completion. By default it
will attempt to parse a ``success_url`` attribute on the workflow,
which can take the form of a reversible URL pattern name, or a
standard HTTP URL.
"""Returns a URL to redirect the user to upon completion.
By default it will attempt to parse a ``success_url`` attribute on the
workflow, which can take the form of a reversible URL pattern name,
or a standard HTTP URL.
"""
try:
return urlresolvers.reverse(self.success_url)
@ -854,8 +865,10 @@ class Workflow(html.HTMLElement):
return self.success_url
def format_status_message(self, message):
"""Hook to allow customization of the message returned to the user
upon successful or unsuccessful completion of the workflow.
"""Hook to allow customization of the message returned to the user.
This is called upon both successful or unsuccessful completion of
the workflow.
By default it simply inserts the workflow's name into the message
string.
@ -894,10 +907,13 @@ class Workflow(html.HTMLElement):
return self.request.get_full_path().partition('?')[0]
def add_error_to_step(self, message, slug):
"""Adds an error to the workflow's Step with the
specified slug based on API issues. This is useful
when you wish for API errors to appear as errors on
the form rather than using the messages framework.
"""Adds an error message to the workflow's Step.
This is useful when you wish for API errors to appear as errors
on the form rather than using the messages framework.
The workflow's Step is specified by its slug.
"""
step = self.get_step(slug)
if step:

View File

@ -29,8 +29,7 @@ from horizon import messages
class WorkflowView(hz_views.ModalBackdropMixin, generic.TemplateView):
"""A generic class-based view which handles the intricacies of workflow
processing with minimal user configuration.
"""A generic view which handles the intricacies of workflow processing.
.. attribute:: workflow_class
@ -67,8 +66,10 @@ class WorkflowView(hz_views.ModalBackdropMixin, generic.TemplateView):
"on %s." % self.__class__.__name__)
def get_initial(self):
"""Returns initial data for the workflow. Defaults to using the GET
parameters to allow pre-seeding of the workflow context values.
"""Returns initial data for the workflow.
Defaults to using the GET parameters
to allow pre-seeding of the workflow context values.
"""
return copy.copy(self.request.GET)
@ -102,8 +103,10 @@ class WorkflowView(hz_views.ModalBackdropMixin, generic.TemplateView):
return context
def get_layout(self):
"""returns classes for the workflow element in template based on
the workflow characteristics
"""Returns classes for the workflow element in template.
The returned classes are determied based on
the workflow characteristics.
"""
if self.request.is_ajax():
layout = ['modal', ]