Gate on H4xx docstrings for pep8
In an effort to help horizon more friendly to OpenStack hacking guidelines, we now gate on H40* violations. Change-Id: Id07294543660368d2f7f5ac363710176ab23b874 Signed-off-by: Paul Belanger <paul.belanger@polybeacon.com>
This commit is contained in:
parent
94129baebb
commit
da8c69afa6
@ -159,7 +159,7 @@ class Registry(object):
|
||||
|
||||
|
||||
class Panel(HorizonComponent):
|
||||
""" A base class for defining Horizon dashboard panels.
|
||||
"""A base class for defining Horizon dashboard panels.
|
||||
|
||||
All Horizon dashboard panels should extend from this class. It provides
|
||||
the appropriate hooks for automatically constructing URLconfs, and
|
||||
@ -214,7 +214,7 @@ class Panel(HorizonComponent):
|
||||
return "<Panel: %s>" % self.slug
|
||||
|
||||
def get_absolute_url(self):
|
||||
""" Returns the default URL for this panel.
|
||||
"""Returns the default URL for this panel.
|
||||
|
||||
The default URL is defined as the URL pattern with ``name="index"`` in
|
||||
the URLconf for this panel.
|
||||
@ -243,7 +243,7 @@ class Panel(HorizonComponent):
|
||||
|
||||
|
||||
class PanelGroup(object):
|
||||
""" A container for a set of :class:`~horizon.Panel` classes.
|
||||
"""A container for a set of :class:`~horizon.Panel` classes.
|
||||
|
||||
When iterated, it will yield each of the ``Panel`` instances it
|
||||
contains.
|
||||
@ -286,7 +286,7 @@ class PanelGroup(object):
|
||||
|
||||
|
||||
class Dashboard(Registry, HorizonComponent):
|
||||
""" A base class for defining Horizon dashboards.
|
||||
"""A base class for defining Horizon dashboards.
|
||||
|
||||
All Horizon dashboards should extend from this base class. It provides the
|
||||
appropriate hooks for automatic discovery of :class:`~horizon.Panel`
|
||||
@ -390,15 +390,13 @@ class Dashboard(Registry, HorizonComponent):
|
||||
self._panel_groups = None
|
||||
|
||||
def get_panel(self, panel):
|
||||
"""
|
||||
Returns the specified :class:`~horizon.Panel` instance registered
|
||||
"""Returns the specified :class:`~horizon.Panel` instance registered
|
||||
with this dashboard.
|
||||
"""
|
||||
return self._registered(panel)
|
||||
|
||||
def get_panels(self):
|
||||
"""
|
||||
Returns the :class:`~horizon.Panel` instances registered with this
|
||||
"""Returns the :class:`~horizon.Panel` instances registered with this
|
||||
dashboard in order, without any panel groupings.
|
||||
"""
|
||||
all_panels = []
|
||||
@ -432,7 +430,7 @@ class Dashboard(Registry, HorizonComponent):
|
||||
return SortedDict(panel_groups)
|
||||
|
||||
def get_absolute_url(self):
|
||||
""" Returns the default URL for this dashboard.
|
||||
"""Returns the default URL for this dashboard.
|
||||
|
||||
The default URL is defined as the URL pattern with ``name="index"``
|
||||
in the URLconf for the :class:`~horizon.Panel` specified by
|
||||
@ -478,7 +476,7 @@ class Dashboard(Registry, HorizonComponent):
|
||||
return urlpatterns, self.slug, self.slug
|
||||
|
||||
def _autodiscover(self):
|
||||
""" Discovers panels to register from the current dashboard module. """
|
||||
"""Discovers panels to register from the current dashboard module."""
|
||||
if getattr(self, "_autodiscover_complete", False):
|
||||
return
|
||||
|
||||
@ -520,7 +518,7 @@ class Dashboard(Registry, HorizonComponent):
|
||||
|
||||
@classmethod
|
||||
def register(cls, panel):
|
||||
""" Registers a :class:`~horizon.Panel` with this dashboard. """
|
||||
"""Registers a :class:`~horizon.Panel` with this dashboard."""
|
||||
panel_class = Horizon.register_panel(cls, panel)
|
||||
# Support template loading from panel template directories.
|
||||
panel_mod = import_module(panel.__module__)
|
||||
@ -533,7 +531,7 @@ class Dashboard(Registry, HorizonComponent):
|
||||
|
||||
@classmethod
|
||||
def unregister(cls, panel):
|
||||
""" Unregisters a :class:`~horizon.Panel` from this dashboard. """
|
||||
"""Unregisters a :class:`~horizon.Panel` from this dashboard."""
|
||||
success = Horizon.unregister_panel(cls, panel)
|
||||
if success:
|
||||
# Remove the panel's template directory.
|
||||
@ -578,7 +576,7 @@ class LazyURLPattern(SimpleLazyObject):
|
||||
|
||||
|
||||
class Site(Registry, HorizonComponent):
|
||||
""" The overarching class which encompasses all dashboards and panels. """
|
||||
"""The overarching class which encompasses all dashboards and panels."""
|
||||
|
||||
# Required for registry
|
||||
_registerable_class = Dashboard
|
||||
@ -604,11 +602,11 @@ class Site(Registry, HorizonComponent):
|
||||
return self._conf['default_dashboard']
|
||||
|
||||
def register(self, dashboard):
|
||||
""" Registers a :class:`~horizon.Dashboard` with Horizon."""
|
||||
"""Registers a :class:`~horizon.Dashboard` with Horizon."""
|
||||
return self._register(dashboard)
|
||||
|
||||
def unregister(self, dashboard):
|
||||
""" Unregisters a :class:`~horizon.Dashboard` from Horizon. """
|
||||
"""Unregisters a :class:`~horizon.Dashboard` from Horizon."""
|
||||
return self._unregister(dashboard)
|
||||
|
||||
def registered(self, dashboard):
|
||||
@ -626,11 +624,11 @@ class Site(Registry, HorizonComponent):
|
||||
return dash_instance._unregister(panel)
|
||||
|
||||
def get_dashboard(self, dashboard):
|
||||
""" Returns the specified :class:`~horizon.Dashboard` instance. """
|
||||
"""Returns the specified :class:`~horizon.Dashboard` instance."""
|
||||
return self._registered(dashboard)
|
||||
|
||||
def get_dashboards(self):
|
||||
""" Returns an ordered tuple of :class:`~horizon.Dashboard` modules.
|
||||
"""Returns an ordered tuple of :class:`~horizon.Dashboard` modules.
|
||||
|
||||
Orders dashboards according to the ``"dashboards"`` key in
|
||||
``HORIZON_CONFIG`` or else returns all registered dashboards
|
||||
@ -658,7 +656,7 @@ class Site(Registry, HorizonComponent):
|
||||
return dashboards
|
||||
|
||||
def get_default_dashboard(self):
|
||||
""" Returns the default :class:`~horizon.Dashboard` instance.
|
||||
"""Returns the default :class:`~horizon.Dashboard` instance.
|
||||
|
||||
If ``"default_dashboard"`` is specified in ``HORIZON_CONFIG``
|
||||
then that dashboard will be returned. If not, the first dashboard
|
||||
@ -672,7 +670,7 @@ class Site(Registry, HorizonComponent):
|
||||
raise NotRegistered("No dashboard modules have been registered.")
|
||||
|
||||
def get_user_home(self, user):
|
||||
""" Returns the default URL for a particular user.
|
||||
"""Returns the default URL for a particular user.
|
||||
|
||||
This method can be used to customize where a user is sent when
|
||||
they log in, etc. By default it returns the value of
|
||||
@ -710,7 +708,7 @@ class Site(Registry, HorizonComponent):
|
||||
return self.get_absolute_url()
|
||||
|
||||
def get_absolute_url(self):
|
||||
""" Returns the default URL for Horizon's URLconf.
|
||||
"""Returns the default URL for Horizon's URLconf.
|
||||
|
||||
The default URL is determined by calling
|
||||
:meth:`~horizon.Dashboard.get_absolute_url`
|
||||
@ -721,7 +719,7 @@ class Site(Registry, HorizonComponent):
|
||||
|
||||
@property
|
||||
def _lazy_urls(self):
|
||||
""" Lazy loading for URL patterns.
|
||||
"""Lazy loading for URL patterns.
|
||||
|
||||
This method avoids problems associated with attempting to evaluate
|
||||
the the URLconf before the settings module has been loaded.
|
||||
@ -732,7 +730,7 @@ class Site(Registry, HorizonComponent):
|
||||
return LazyURLPattern(url_patterns), self.namespace, self.slug
|
||||
|
||||
def _urls(self):
|
||||
""" Constructs the URLconf for Horizon from registered Dashboards. """
|
||||
"""Constructs the URLconf for Horizon from registered Dashboards."""
|
||||
urlpatterns = self._get_default_urlpatterns()
|
||||
self._autodiscover()
|
||||
|
||||
@ -764,7 +762,7 @@ class Site(Registry, HorizonComponent):
|
||||
return urlpatterns, self.namespace, self.slug
|
||||
|
||||
def _autodiscover(self):
|
||||
""" Discovers modules to register from ``settings.INSTALLED_APPS``.
|
||||
"""Discovers modules to register from ``settings.INSTALLED_APPS``.
|
||||
|
||||
This makes sure that the appropriate modules get imported to register
|
||||
themselves with Horizon.
|
||||
@ -787,8 +785,7 @@ class Site(Registry, HorizonComponent):
|
||||
|
||||
|
||||
class HorizonSite(Site):
|
||||
"""
|
||||
A singleton implementation of Site such that all dealings with horizon
|
||||
"""A singleton implementation of Site such that all dealings with horizon
|
||||
get the same instance no matter what. There can be only one.
|
||||
"""
|
||||
_instance = None
|
||||
|
@ -121,9 +121,8 @@ 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 from 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]
|
||||
|
@ -41,7 +41,7 @@ class Breadcrumb(html.HTMLElement):
|
||||
return self._subfolders
|
||||
|
||||
def render(self):
|
||||
""" Renders the table using the template from the table options. """
|
||||
"""Renders the table using the template from the table options."""
|
||||
breadcrumb_template = template.loader.get_template(self.template)
|
||||
extra_context = {"breadcrumb": self}
|
||||
context = template.RequestContext(self.request, extra_context)
|
||||
|
@ -25,7 +25,7 @@ from horizon import conf
|
||||
|
||||
|
||||
def horizon(request):
|
||||
""" The main Horizon context processor. Required for Horizon to function.
|
||||
"""The main Horizon context processor. Required for Horizon to function.
|
||||
|
||||
It adds the Horizon config to the context as well as setting the names
|
||||
``True`` and ``False`` in the context to their boolean equivalents
|
||||
|
@ -28,7 +28,7 @@ from django.utils.translation import ugettext_lazy as _ # noqa
|
||||
|
||||
|
||||
def _current_component(view_func, dashboard=None, panel=None):
|
||||
""" Sets the currently-active dashboard and/or panel on the request. """
|
||||
"""Sets the currently-active dashboard and/or panel on the request."""
|
||||
@functools.wraps(view_func, assigned=available_attrs(view_func))
|
||||
def dec(request, *args, **kwargs):
|
||||
if dashboard:
|
||||
@ -40,7 +40,7 @@ def _current_component(view_func, dashboard=None, panel=None):
|
||||
|
||||
|
||||
def require_auth(view_func):
|
||||
""" Performs user authentication check.
|
||||
"""Performs user authentication check.
|
||||
|
||||
Similar to Django's `login_required` decorator, except that this throws
|
||||
:exc:`~horizon.exceptions.NotAuthenticated` exception if the user is not
|
||||
@ -57,7 +57,7 @@ def require_auth(view_func):
|
||||
|
||||
|
||||
def require_perms(view_func, required):
|
||||
""" Enforces permission-based access controls.
|
||||
"""Enforces permission-based access controls.
|
||||
|
||||
:param list required: A tuple of permission names, all of which the request
|
||||
user must possess in order access the decorated view.
|
||||
|
@ -36,15 +36,14 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HorizonReporterFilter(SafeExceptionReporterFilter):
|
||||
""" Error report filter that's always active, even in DEBUG mode. """
|
||||
"""Error report filter that's always active, even in DEBUG mode."""
|
||||
def is_active(self, request):
|
||||
return True
|
||||
|
||||
# TODO(gabriel): This bugfix is cribbed from Django's code. When 1.4.1
|
||||
# is available we can remove this code.
|
||||
def get_traceback_frame_variables(self, request, tb_frame):
|
||||
"""
|
||||
Replaces the values of variables marked as sensitive with
|
||||
"""Replaces the values of variables marked as sensitive with
|
||||
stars (*********).
|
||||
"""
|
||||
# Loop through the frame's callers to see if the sensitive_variables
|
||||
@ -93,13 +92,12 @@ class HorizonReporterFilter(SafeExceptionReporterFilter):
|
||||
|
||||
|
||||
class HorizonException(Exception):
|
||||
""" Base exception class for distinguishing our own exception classes. """
|
||||
"""Base exception class for distinguishing our own exception classes."""
|
||||
pass
|
||||
|
||||
|
||||
class Http302(HorizonException):
|
||||
"""
|
||||
Error class which can be raised from within a handler to cause an
|
||||
"""Error class which can be raised from within a handler to cause an
|
||||
early bailout and redirect at the middleware level.
|
||||
"""
|
||||
status_code = 302
|
||||
@ -110,8 +108,7 @@ class Http302(HorizonException):
|
||||
|
||||
|
||||
class NotAuthorized(HorizonException):
|
||||
"""
|
||||
Raised whenever a user attempts to access a resource which they do not
|
||||
"""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).
|
||||
|
||||
@ -123,8 +120,8 @@ 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
|
||||
@ -134,19 +131,18 @@ class NotAuthenticated(HorizonException):
|
||||
|
||||
|
||||
class NotFound(HorizonException):
|
||||
""" Generic error to replace all "Not Found"-type API errors. """
|
||||
"""Generic error to replace all "Not Found"-type API errors."""
|
||||
status_code = 404
|
||||
|
||||
|
||||
class RecoverableError(HorizonException):
|
||||
""" Generic error to replace any "Recoverable"-type API errors. """
|
||||
"""Generic error to replace any "Recoverable"-type API errors."""
|
||||
status_code = 100 # HTTP status code "Continue"
|
||||
|
||||
|
||||
class ServiceCatalogException(HorizonException):
|
||||
"""
|
||||
Raised when a requested service is not available in the ``ServiceCatalog``
|
||||
returned by Keystone.
|
||||
"""Raised when a requested service is not available in the
|
||||
``ServiceCatalog`` returned by Keystone.
|
||||
"""
|
||||
def __init__(self, service_name):
|
||||
message = 'Invalid service catalog service: %s' % service_name
|
||||
@ -154,8 +150,7 @@ class ServiceCatalogException(HorizonException):
|
||||
|
||||
|
||||
class AlreadyExists(HorizonException):
|
||||
"""
|
||||
Exception to be raised when trying to create an API resource which
|
||||
"""Exception to be raised when trying to create an API resource which
|
||||
already exists.
|
||||
"""
|
||||
def __init__(self, name, resource_type):
|
||||
@ -173,21 +168,19 @@ class AlreadyExists(HorizonException):
|
||||
|
||||
|
||||
class WorkflowError(HorizonException):
|
||||
""" Exception to be raised when something goes wrong in a workflow. """
|
||||
"""Exception to be raised when something goes wrong in a workflow."""
|
||||
pass
|
||||
|
||||
|
||||
class WorkflowValidationError(HorizonException):
|
||||
"""
|
||||
Exception raised during workflow validation if required data is missing,
|
||||
"""Exception raised during workflow validation if required data is missing,
|
||||
or existing data is not valid.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class HandledException(HorizonException):
|
||||
"""
|
||||
Used internally to track exceptions that have gone through
|
||||
"""Used internally to track exceptions that have gone through
|
||||
:func:`horizon.exceptions.handle` more than once.
|
||||
"""
|
||||
def __init__(self, wrapped):
|
||||
@ -205,8 +198,7 @@ def error_color(msg):
|
||||
|
||||
|
||||
def check_message(keywords, message):
|
||||
"""
|
||||
Checks an exception for given keywords and raises a new ``ActionError``
|
||||
"""Checks an exception for given keywords and raises a new ``ActionError``
|
||||
with the desired message if the keywords are found. This allows selective
|
||||
control over API error messages.
|
||||
"""
|
||||
@ -218,7 +210,7 @@ def check_message(keywords, message):
|
||||
|
||||
def handle(request, message=None, redirect=None, ignore=False,
|
||||
escalate=False, log_level=None, force_log=None):
|
||||
""" Centralized error handling for Horizon.
|
||||
"""Centralized error handling for Horizon.
|
||||
|
||||
Because Horizon consumes so many different APIs with completely
|
||||
different ``Exception`` types, it's necessary to have a centralized
|
||||
|
@ -32,15 +32,13 @@ class SelfHandlingMixin(object):
|
||||
|
||||
|
||||
class SelfHandlingForm(SelfHandlingMixin, forms.Form):
|
||||
"""
|
||||
A base :class:`Form <django:django.forms.Form>` class which includes
|
||||
"""A base :class:`Form <django:django.forms.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
|
||||
"""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.
|
||||
@ -49,7 +47,7 @@ class SelfHandlingForm(SelfHandlingMixin, forms.Form):
|
||||
|
||||
|
||||
class DateForm(forms.Form):
|
||||
""" A simple form for selecting a range of time. """
|
||||
"""A simple form for selecting a range of time."""
|
||||
start = forms.DateField(input_formats=("%Y-%m-%d",))
|
||||
end = forms.DateField(input_formats=("%Y-%m-%d",))
|
||||
|
||||
|
@ -20,9 +20,8 @@ from django.forms import widgets
|
||||
|
||||
|
||||
class DynamicSelectWidget(widgets.Select):
|
||||
"""
|
||||
A subclass of the ``Select`` widget which renders extra attributes for use
|
||||
in callbacks 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"
|
||||
|
||||
@ -46,8 +45,7 @@ class DynamicSelectWidget(widgets.Select):
|
||||
|
||||
|
||||
class DynamicChoiceField(fields.ChoiceField):
|
||||
"""
|
||||
A subclass of ``ChoiceField`` with additional properties that make
|
||||
"""A subclass of ``ChoiceField`` with additional properties that make
|
||||
dynamically updating its elements easier.
|
||||
|
||||
Notably, the field declaration takes an extra argument, ``add_item_link``
|
||||
@ -67,5 +65,5 @@ class DynamicChoiceField(fields.ChoiceField):
|
||||
|
||||
|
||||
class DynamicTypedChoiceField(DynamicChoiceField, fields.TypedChoiceField):
|
||||
""" Simple mix of ``DynamicChoiceField`` and ``TypedChoiceField``. """
|
||||
"""Simple mix of ``DynamicChoiceField`` and ``TypedChoiceField``."""
|
||||
pass
|
||||
|
@ -49,8 +49,7 @@ class ModalFormMixin(object):
|
||||
|
||||
|
||||
class ModalFormView(ModalFormMixin, generic.FormView):
|
||||
"""
|
||||
The main view class from which all views which handle forms in Horizon
|
||||
"""The main view class from which all views which handle forms in Horizon
|
||||
should inherit. It takes care of all details with processing
|
||||
:class:`~horizon.forms.base.SelfHandlingForm` classes, and modal concerns
|
||||
when the associated template inherits from
|
||||
@ -65,25 +64,21 @@ class ModalFormView(ModalFormMixin, generic.FormView):
|
||||
"""
|
||||
|
||||
def get_object_id(self, obj):
|
||||
"""
|
||||
For dynamic insertion of resources created in modals, this method
|
||||
"""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
|
||||
"""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
|
||||
|
||||
def get_form(self, form_class):
|
||||
"""
|
||||
Returns an instance of the form to be used in this view.
|
||||
"""
|
||||
"""Returns an instance of the form to be used in this view."""
|
||||
return form_class(self.request, **self.get_form_kwargs())
|
||||
|
||||
def form_valid(self, form):
|
||||
|
@ -26,9 +26,7 @@ from django.utils.safestring import SafeData # noqa
|
||||
|
||||
|
||||
def add_message(request, level, message, extra_tags='', fail_silently=False):
|
||||
"""
|
||||
Attempts to add a message to the request using the 'messages' app.
|
||||
"""
|
||||
"""Attempts to add a message to the request using the 'messages' app."""
|
||||
if request.is_ajax():
|
||||
tag = constants.DEFAULT_TAGS[level]
|
||||
# if message is marked as safe, pass "safe" tag as extra_tags so that
|
||||
@ -44,40 +42,30 @@ def add_message(request, level, message, extra_tags='', fail_silently=False):
|
||||
|
||||
|
||||
def debug(request, message, extra_tags='', fail_silently=False):
|
||||
"""
|
||||
Adds a message with the ``DEBUG`` level.
|
||||
"""
|
||||
"""Adds a message with the ``DEBUG`` level."""
|
||||
add_message(request, constants.DEBUG, message, extra_tags=extra_tags,
|
||||
fail_silently=fail_silently)
|
||||
|
||||
|
||||
def info(request, message, extra_tags='', fail_silently=False):
|
||||
"""
|
||||
Adds a message with the ``INFO`` level.
|
||||
"""
|
||||
"""Adds a message with the ``INFO`` level."""
|
||||
add_message(request, constants.INFO, message, extra_tags=extra_tags,
|
||||
fail_silently=fail_silently)
|
||||
|
||||
|
||||
def success(request, message, extra_tags='', fail_silently=False):
|
||||
"""
|
||||
Adds a message with the ``SUCCESS`` level.
|
||||
"""
|
||||
"""Adds a message with the ``SUCCESS`` level."""
|
||||
add_message(request, constants.SUCCESS, message, extra_tags=extra_tags,
|
||||
fail_silently=fail_silently)
|
||||
|
||||
|
||||
def warning(request, message, extra_tags='', fail_silently=False):
|
||||
"""
|
||||
Adds a message with the ``WARNING`` level.
|
||||
"""
|
||||
"""Adds a message with the ``WARNING`` level."""
|
||||
add_message(request, constants.WARNING, message, extra_tags=extra_tags,
|
||||
fail_silently=fail_silently)
|
||||
|
||||
|
||||
def error(request, message, extra_tags='', fail_silently=False):
|
||||
"""
|
||||
Adds a message with the ``ERROR`` level.
|
||||
"""
|
||||
"""Adds a message with the ``ERROR`` level."""
|
||||
add_message(request, constants.ERROR, message, extra_tags=extra_tags,
|
||||
fail_silently=fail_silently)
|
||||
|
@ -43,12 +43,12 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HorizonMiddleware(object):
|
||||
""" The main Horizon middleware class. Required for use of Horizon. """
|
||||
"""The main Horizon middleware class. Required for use of Horizon."""
|
||||
|
||||
logout_reason = None
|
||||
|
||||
def process_request(self, request):
|
||||
""" Adds data necessary for Horizon to function to the request. """
|
||||
"""Adds data necessary for Horizon to function to the request."""
|
||||
# Activate timezone handling
|
||||
tz = request.session.get('django_timezone')
|
||||
if tz:
|
||||
@ -75,8 +75,7 @@ class HorizonMiddleware(object):
|
||||
request.session['last_activity'] = timestamp
|
||||
|
||||
def process_exception(self, request, exception):
|
||||
"""
|
||||
Catches internal Horizon exception classes such as NotAuthorized,
|
||||
"""Catches internal Horizon exception classes such as NotAuthorized,
|
||||
NotFound and Http302 and handles them gracefully.
|
||||
"""
|
||||
if isinstance(exception, (exceptions.NotAuthorized,
|
||||
@ -108,8 +107,7 @@ class HorizonMiddleware(object):
|
||||
return shortcuts.redirect(exception.location)
|
||||
|
||||
def process_response(self, request, response):
|
||||
"""
|
||||
Convert HttpResponseRedirect to HttpResponse if request is via ajax
|
||||
"""Convert HttpResponseRedirect to HttpResponse if request is via ajax
|
||||
to allow ajax request to redirect url
|
||||
"""
|
||||
if request.is_ajax() and hasattr(request, 'horizon'):
|
||||
|
@ -39,7 +39,7 @@ STRING_SEPARATOR = "__"
|
||||
|
||||
|
||||
class BaseAction(html.HTMLElement):
|
||||
""" Common base class for all ``Action`` classes. """
|
||||
"""Common base class for all ``Action`` classes."""
|
||||
table = None
|
||||
handles_multiple = False
|
||||
requires_input = False
|
||||
@ -51,8 +51,8 @@ class BaseAction(html.HTMLElement):
|
||||
self.datum = datum
|
||||
|
||||
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.
|
||||
"""Method to see if the action is allowed for a certain type of data.
|
||||
Only affects mixed data type tables.
|
||||
"""
|
||||
if datum:
|
||||
action_data_types = getattr(self, "allowed_data_types", [])
|
||||
@ -66,7 +66,7 @@ class BaseAction(html.HTMLElement):
|
||||
return True
|
||||
|
||||
def get_policy_target(self, request, datum):
|
||||
""" Provide the target for a policy request.
|
||||
"""Provide the target for a policy request.
|
||||
|
||||
This method is meant to be overridden to return target details when
|
||||
one of the policy checks requires them. E.g., {"user_id": datum.id}
|
||||
@ -74,7 +74,7 @@ class BaseAction(html.HTMLElement):
|
||||
return {}
|
||||
|
||||
def allowed(self, request, datum):
|
||||
""" Determine whether this action is allowed for the current request.
|
||||
"""Determine whether this action is allowed for the current request.
|
||||
|
||||
This method is meant to be overridden with more specific checks.
|
||||
"""
|
||||
@ -90,7 +90,7 @@ class BaseAction(html.HTMLElement):
|
||||
return self.allowed(request, datum)
|
||||
|
||||
def update(self, request, datum):
|
||||
""" Allows per-action customization based on current conditions.
|
||||
"""Allows per-action customization based on current conditions.
|
||||
|
||||
This is particularly useful when you wish to create a "toggle"
|
||||
action that will be rendered differently based on the value of an
|
||||
@ -101,16 +101,14 @@ class BaseAction(html.HTMLElement):
|
||||
pass
|
||||
|
||||
def get_default_classes(self):
|
||||
"""
|
||||
Returns a list of the default classes for the action. Defaults to
|
||||
"""Returns a list of the default classes for the action. Defaults to
|
||||
``["btn", "btn-small"]``.
|
||||
"""
|
||||
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
|
||||
"""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 }}``.
|
||||
"""
|
||||
if self.datum is not None:
|
||||
@ -126,7 +124,7 @@ class BaseAction(html.HTMLElement):
|
||||
|
||||
|
||||
class Action(BaseAction):
|
||||
""" Represents an action which can be taken on this table's data.
|
||||
"""Represents an action which can be taken on this table's data.
|
||||
|
||||
.. attribute:: name
|
||||
|
||||
@ -265,7 +263,7 @@ class Action(BaseAction):
|
||||
self.multiple = new.instancemethod(multiple, self)
|
||||
|
||||
def get_param_name(self):
|
||||
""" Returns the full POST parameter name for this action.
|
||||
"""Returns the full POST parameter name for this action.
|
||||
|
||||
Defaults to
|
||||
``{{ table.name }}__{{ action.name }}``.
|
||||
@ -274,7 +272,7 @@ class Action(BaseAction):
|
||||
|
||||
|
||||
class LinkAction(BaseAction):
|
||||
""" A table action which is simply a link rather than a form POST.
|
||||
"""A table action which is simply a link rather than a form POST.
|
||||
|
||||
.. attribute:: name
|
||||
|
||||
@ -319,7 +317,7 @@ class LinkAction(BaseAction):
|
||||
self.attrs.update(attrs)
|
||||
|
||||
def get_link_url(self, datum=None):
|
||||
""" Returns the final URL based on the value of ``url``.
|
||||
"""Returns the final URL based on the value of ``url``.
|
||||
|
||||
If ``url`` is callable it will call the function.
|
||||
If not, it will then try to call ``reverse`` on ``url``.
|
||||
@ -346,7 +344,7 @@ class LinkAction(BaseAction):
|
||||
|
||||
|
||||
class FilterAction(BaseAction):
|
||||
""" A base class representing a filter action for a table.
|
||||
"""A base class representing a filter action for a table.
|
||||
|
||||
.. attribute:: name
|
||||
|
||||
@ -389,7 +387,7 @@ class FilterAction(BaseAction):
|
||||
self.param_name = param_name or 'q'
|
||||
|
||||
def get_param_name(self):
|
||||
""" Returns the full query parameter name for this action.
|
||||
"""Returns the full query parameter name for this action.
|
||||
|
||||
Defaults to
|
||||
``{{ table.name }}__{{ action.name }}__{{ action.param_name }}``.
|
||||
@ -424,7 +422,7 @@ class FilterAction(BaseAction):
|
||||
return filtered_data
|
||||
|
||||
def filter(self, table, data, filter_string):
|
||||
""" Provides the actual filtering logic.
|
||||
"""Provides the actual filtering logic.
|
||||
|
||||
This method must be overridden by subclasses and return
|
||||
the filtered data.
|
||||
@ -434,8 +432,7 @@ class FilterAction(BaseAction):
|
||||
|
||||
|
||||
class FixedFilterAction(FilterAction):
|
||||
""" A filter action with fixed buttons.
|
||||
"""
|
||||
"""A filter action with fixed buttons."""
|
||||
filter_type = 'fixed'
|
||||
needs_preloading = True
|
||||
|
||||
@ -480,9 +477,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
|
||||
|
||||
@ -543,8 +540,7 @@ class BatchAction(Action):
|
||||
return super(BatchAction, self)._allowed(request, datum)
|
||||
|
||||
def _conjugate(self, items=None, past=False):
|
||||
"""
|
||||
Builds combinations like 'Delete Object' and 'Deleted
|
||||
"""Builds combinations like 'Delete Object' and 'Deleted
|
||||
Objects' based on the number of items and `past` flag.
|
||||
"""
|
||||
action_type = "past" if past else "present"
|
||||
@ -565,8 +561,8 @@ class BatchAction(Action):
|
||||
return msgstr % {'action': action, 'data_type': data_type}
|
||||
|
||||
def action(self, request, datum_id):
|
||||
"""
|
||||
Required. Accepts a single object id and performs the specific action.
|
||||
"""Required. Accepts a single object id and performs the specific
|
||||
action.
|
||||
|
||||
Return values are discarded, errors raised are caught and logged.
|
||||
"""
|
||||
@ -574,17 +570,13 @@ class BatchAction(Action):
|
||||
'BatchAction: %s' % self.data_type_singular)
|
||||
|
||||
def update(self, request, datum):
|
||||
"""
|
||||
Switches the action verbose name, if needed
|
||||
"""
|
||||
"""Switches the action verbose name, if needed."""
|
||||
if getattr(self, 'action_present', False):
|
||||
self.verbose_name = self._conjugate()
|
||||
self.verbose_name_plural = self._conjugate('plural')
|
||||
|
||||
def get_success_url(self, request=None):
|
||||
"""
|
||||
Returns the URL to redirect to after a successful action.
|
||||
"""
|
||||
"""Returns the URL to redirect to after a successful action."""
|
||||
if self.success_url:
|
||||
return self.success_url
|
||||
return request.get_full_path()
|
||||
|
@ -49,7 +49,7 @@ STRING_SEPARATOR = "__"
|
||||
|
||||
|
||||
class Column(html.HTMLElement):
|
||||
""" A class which represents a single column in a :class:`.DataTable`.
|
||||
"""A class which represents a single column in a :class:`.DataTable`.
|
||||
|
||||
.. attribute:: transform
|
||||
|
||||
@ -270,10 +270,9 @@ class Column(html.HTMLElement):
|
||||
return '<%s: %s>' % (self.__class__.__name__, self.name)
|
||||
|
||||
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, before any filters or
|
||||
formatting are applied to it. This is useful when doing calculations
|
||||
on data in the table.
|
||||
"""
|
||||
# Callable transformations
|
||||
if callable(self.transform):
|
||||
@ -295,8 +294,8 @@ 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`
|
||||
@ -329,7 +328,7 @@ class Column(html.HTMLElement):
|
||||
return self.table._data_cache[self][datum_id]
|
||||
|
||||
def get_link_url(self, datum):
|
||||
""" Returns the final value for the column's ``link`` property.
|
||||
"""Returns the final value for the column's ``link`` property.
|
||||
|
||||
If ``allowed_data_types`` of this column is not empty and the datum
|
||||
has an assigned type, check if the datum's type is in the
|
||||
@ -355,8 +354,7 @@ class Column(html.HTMLElement):
|
||||
return self.link
|
||||
|
||||
def get_summation(self):
|
||||
"""
|
||||
Returns the summary value for the data in this column if a
|
||||
"""Returns the summary value for the data in this column if a
|
||||
valid summation method is specified for it. Otherwise returns ``None``.
|
||||
"""
|
||||
if self.summation not in self.summation_methods:
|
||||
@ -376,7 +374,7 @@ class Column(html.HTMLElement):
|
||||
|
||||
|
||||
class Row(html.HTMLElement):
|
||||
""" Represents a row in the table.
|
||||
"""Represents a row in the table.
|
||||
|
||||
When iterated, the ``Row`` instance will yield each of its cells.
|
||||
|
||||
@ -444,8 +442,7 @@ class Row(html.HTMLElement):
|
||||
self.cells = []
|
||||
|
||||
def load_cells(self, datum=None):
|
||||
"""
|
||||
Load the row's data (either provided at initialization or as an
|
||||
"""Load the row's data (either provided at initialization or as an
|
||||
argument to this function), initiailize all the cells contained
|
||||
by this row, and set the appropriate row properties which require
|
||||
the row's data to be determined.
|
||||
@ -526,7 +523,7 @@ class Row(html.HTMLElement):
|
||||
{"row": self})
|
||||
|
||||
def get_cells(self):
|
||||
""" Returns the bound cells for this row in order. """
|
||||
"""Returns the bound cells for this row in order."""
|
||||
return self.cells.values()
|
||||
|
||||
def get_ajax_update_url(self):
|
||||
@ -537,8 +534,7 @@ class Row(html.HTMLElement):
|
||||
return "%s?%s" % (table_url, params)
|
||||
|
||||
def get_data(self, request, obj_id):
|
||||
"""
|
||||
Fetches the updated data for the row based on the object 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.
|
||||
"""
|
||||
raise NotImplementedError("You must define a get_data method on %s"
|
||||
@ -546,7 +542,7 @@ class Row(html.HTMLElement):
|
||||
|
||||
|
||||
class Cell(html.HTMLElement):
|
||||
""" Represents a single cell in the table. """
|
||||
"""Represents a single cell in the table."""
|
||||
def __init__(self, datum, data, column, row, attrs=None, classes=None):
|
||||
self.classes = classes or getattr(self, "classes", [])
|
||||
super(Cell, self).__init__()
|
||||
@ -565,8 +561,7 @@ class Cell(html.HTMLElement):
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
"""
|
||||
Returns a formatted version of the data for final output.
|
||||
"""Returns a formatted version of the data for final output.
|
||||
|
||||
This takes into consideration the
|
||||
:attr:`~horizon.tables.Column.link`` and
|
||||
@ -602,7 +597,7 @@ class Cell(html.HTMLElement):
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
""" Gets the status for the column based on the cell's data. """
|
||||
"""Gets the status for the column based on the cell's data."""
|
||||
# Deal with status column mechanics based in this cell's data
|
||||
if hasattr(self, '_status'):
|
||||
return self._status
|
||||
@ -619,7 +614,7 @@ class Cell(html.HTMLElement):
|
||||
return self._status
|
||||
|
||||
def get_status_class(self, status):
|
||||
""" Returns a css class name determined by the status value. """
|
||||
"""Returns a css class name determined by the status value."""
|
||||
if status is True:
|
||||
return "status_up"
|
||||
elif status is False:
|
||||
@ -628,7 +623,7 @@ class Cell(html.HTMLElement):
|
||||
return "status_unknown"
|
||||
|
||||
def get_default_classes(self):
|
||||
""" Returns a flattened string of the cell's CSS classes. """
|
||||
"""Returns a flattened string of the cell's CSS classes."""
|
||||
if not self.url:
|
||||
self.column.classes = [cls for cls in self.column.classes
|
||||
if cls != "anchor"]
|
||||
@ -640,7 +635,7 @@ class Cell(html.HTMLElement):
|
||||
|
||||
|
||||
class DataTableOptions(object):
|
||||
""" Contains options for :class:`.DataTable` objects.
|
||||
"""Contains options for :class:`.DataTable` objects.
|
||||
|
||||
.. attribute:: name
|
||||
|
||||
@ -823,7 +818,7 @@ class DataTableOptions(object):
|
||||
|
||||
|
||||
class DataTableMetaclass(type):
|
||||
""" Metaclass to add options to DataTable class and collect columns. """
|
||||
"""Metaclass to add options to DataTable class and collect columns."""
|
||||
def __new__(mcs, name, bases, attrs):
|
||||
# Process options from Meta
|
||||
class_name = name
|
||||
@ -896,7 +891,7 @@ class DataTableMetaclass(type):
|
||||
|
||||
|
||||
class DataTable(object):
|
||||
""" A class which defines a table with all data and associated actions.
|
||||
"""A class which defines a table with all data and associated actions.
|
||||
|
||||
.. attribute:: name
|
||||
|
||||
@ -1017,14 +1012,14 @@ class DataTable(object):
|
||||
return False
|
||||
|
||||
def render(self):
|
||||
""" Renders the table using the template from the table options. """
|
||||
"""Renders the table using the template from the table options."""
|
||||
table_template = template.loader.get_template(self._meta.template)
|
||||
extra_context = {self._meta.context_var_name: self}
|
||||
context = template.RequestContext(self.request, extra_context)
|
||||
return table_template.render(context)
|
||||
|
||||
def get_absolute_url(self):
|
||||
""" Returns the canonical URL for this table.
|
||||
"""Returns the canonical URL for this table.
|
||||
|
||||
This is used for the POST action attribute on the form element
|
||||
wrapping the table. In many cases it is also useful for redirecting
|
||||
@ -1037,12 +1032,11 @@ class DataTable(object):
|
||||
return self.request.get_full_path().partition('?')[0]
|
||||
|
||||
def get_empty_message(self):
|
||||
""" Returns the message to be displayed when there is no data. """
|
||||
"""Returns the message to be displayed when there is no data."""
|
||||
return self._no_data_message
|
||||
|
||||
def get_object_by_id(self, lookup):
|
||||
"""
|
||||
Returns the data object from the table's dataset which matches
|
||||
"""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.
|
||||
|
||||
@ -1071,8 +1065,7 @@ class DataTable(object):
|
||||
|
||||
@property
|
||||
def has_actions(self):
|
||||
"""
|
||||
Boolean. Indicates whether there are any available actions on this
|
||||
"""Boolean. Indicates whether there are any available actions on this
|
||||
table.
|
||||
"""
|
||||
if not self.base_actions:
|
||||
@ -1081,8 +1074,7 @@ class DataTable(object):
|
||||
|
||||
@property
|
||||
def needs_form_wrapper(self):
|
||||
"""
|
||||
Boolean. Indicates whather this table should be rendered wrapped in
|
||||
"""Boolean. Indicates whather this table should be rendered wrapped in
|
||||
a ``<form>`` tag or not.
|
||||
"""
|
||||
# If needs_form_wrapper is explicitly set, defer to that.
|
||||
@ -1092,14 +1084,14 @@ class DataTable(object):
|
||||
return self.has_actions
|
||||
|
||||
def get_table_actions(self):
|
||||
""" Returns a list of the action instances for this table. """
|
||||
"""Returns a list of the action instances for this table."""
|
||||
bound_actions = [self.base_actions[action.name] for
|
||||
action in self._meta.table_actions]
|
||||
return [action for action in bound_actions if
|
||||
self._filter_action(action, self.request)]
|
||||
|
||||
def get_row_actions(self, datum):
|
||||
""" Returns a list of the action instances for a specific row. """
|
||||
"""Returns a list of the action instances for a specific row."""
|
||||
bound_actions = []
|
||||
for action in self._meta.row_actions:
|
||||
# Copy to allow modifying properties per row
|
||||
@ -1120,7 +1112,7 @@ class DataTable(object):
|
||||
return bound_actions
|
||||
|
||||
def render_table_actions(self):
|
||||
""" Renders the actions specified in ``Meta.table_actions``. """
|
||||
"""Renders the actions specified in ``Meta.table_actions``."""
|
||||
template_path = self._meta.table_actions_template
|
||||
table_actions_template = template.loader.get_template(template_path)
|
||||
bound_actions = self.get_table_actions()
|
||||
@ -1132,9 +1124,9 @@ class DataTable(object):
|
||||
return table_actions_template.render(context)
|
||||
|
||||
def render_row_actions(self, datum):
|
||||
"""Renders the actions specified in ``Meta.row_actions`` using the
|
||||
current row data.
|
||||
"""
|
||||
Renders the actions specified in ``Meta.row_actions`` using the
|
||||
current row data. """
|
||||
template_path = self._meta.row_actions_template
|
||||
row_actions_template = template.loader.get_template(template_path)
|
||||
bound_actions = self.get_row_actions(datum)
|
||||
@ -1145,8 +1137,7 @@ class DataTable(object):
|
||||
|
||||
@staticmethod
|
||||
def parse_action(action_string):
|
||||
"""
|
||||
Parses the ``action`` parameter (a string) sent back with the
|
||||
"""Parses the ``action`` parameter (a string) 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.
|
||||
@ -1163,8 +1154,7 @@ 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
|
||||
"""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.
|
||||
@ -1200,7 +1190,7 @@ class DataTable(object):
|
||||
|
||||
@classmethod
|
||||
def check_handler(cls, request):
|
||||
""" Determine whether the request should be handled by this table. """
|
||||
"""Determine whether the request should be handled by this table."""
|
||||
if request.method == "POST" and "action" in request.POST:
|
||||
table, action, obj_id = cls.parse_action(request.POST["action"])
|
||||
elif "table" in request.GET and "action" in request.GET:
|
||||
@ -1212,9 +1202,8 @@ 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 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)
|
||||
@ -1247,9 +1236,8 @@ class DataTable(object):
|
||||
return None
|
||||
|
||||
def maybe_handle(self):
|
||||
"""
|
||||
Determine whether the request should be handled by any action on this
|
||||
table after data has been loaded.
|
||||
"""Determine whether the request should be handled by any action on
|
||||
this table after data has been loaded.
|
||||
"""
|
||||
request = self.request
|
||||
table_name, action_name, obj_id = self.check_handler(request)
|
||||
@ -1262,13 +1250,13 @@ class DataTable(object):
|
||||
return None
|
||||
|
||||
def sanitize_id(self, obj_id):
|
||||
""" Override to modify an incoming obj_id to match existing
|
||||
"""Override to modify an incoming obj_id to match existing
|
||||
API data types or modify the format.
|
||||
"""
|
||||
return obj_id
|
||||
|
||||
def get_object_id(self, datum):
|
||||
""" Returns the identifier for the object this row will represent.
|
||||
"""Returns the identifier for the object this row will represent.
|
||||
|
||||
By default this returns an ``id`` attribute on the given object,
|
||||
but this can be overridden to return other values.
|
||||
@ -1281,7 +1269,7 @@ class DataTable(object):
|
||||
return datum.id
|
||||
|
||||
def get_object_display(self, datum):
|
||||
""" Returns a display name that identifies this object.
|
||||
"""Returns a display name that identifies this object.
|
||||
|
||||
By default, this returns a ``name`` attribute from the given object,
|
||||
but this can be overriden to return other values.
|
||||
@ -1291,8 +1279,7 @@ class DataTable(object):
|
||||
return None
|
||||
|
||||
def has_more_data(self):
|
||||
"""
|
||||
Returns a boolean value indicating whether there is more data
|
||||
"""Returns a boolean value indicating whether 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
|
||||
@ -1301,19 +1288,17 @@ class DataTable(object):
|
||||
return self._meta.has_more_data
|
||||
|
||||
def get_marker(self):
|
||||
"""
|
||||
Returns the identifier for the last object in the current data set
|
||||
"""Returns the identifier for the last object in the current data set
|
||||
for APIs that use marker/limit-based paging.
|
||||
"""
|
||||
return http.urlquote_plus(self.get_object_id(self.data[-1]))
|
||||
|
||||
def get_pagination_string(self):
|
||||
""" Returns the query parameter string to paginate this table. """
|
||||
"""Returns the query parameter string to paginate this table."""
|
||||
return "=".join([self._meta.pagination_param, self.get_marker()])
|
||||
|
||||
def calculate_row_status(self, statuses):
|
||||
"""
|
||||
Returns a boolean value determining the overall row status
|
||||
"""Returns a boolean value determining the overall row status
|
||||
based on the dictionary of column name to status mappings passed in.
|
||||
|
||||
By default, it uses the following logic:
|
||||
@ -1339,8 +1324,7 @@ class DataTable(object):
|
||||
return True
|
||||
|
||||
def get_row_status_class(self, status):
|
||||
"""
|
||||
Returns a css class name determined by the status value. This class
|
||||
"""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.
|
||||
"""
|
||||
@ -1352,11 +1336,11 @@ class DataTable(object):
|
||||
return "status_unknown"
|
||||
|
||||
def get_columns(self):
|
||||
""" Returns this table's columns including auto-generated ones."""
|
||||
"""Returns this table's columns including auto-generated ones."""
|
||||
return self.columns.values()
|
||||
|
||||
def get_rows(self):
|
||||
""" Return the row data for this table broken out by columns. """
|
||||
"""Return the row data for this table broken out by columns."""
|
||||
rows = []
|
||||
try:
|
||||
for datum in self.filtered_data:
|
||||
|
@ -22,7 +22,7 @@ from horizon.templatetags.horizon import has_permissions # noqa
|
||||
|
||||
|
||||
class MultiTableMixin(object):
|
||||
""" A generic mixin which provides methods for handling DataTables. """
|
||||
"""A generic mixin which provides methods for handling DataTables."""
|
||||
data_method_pattern = "get_%s_data"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -123,8 +123,7 @@ class MultiTableMixin(object):
|
||||
|
||||
|
||||
class MultiTableView(MultiTableMixin, generic.TemplateView):
|
||||
"""
|
||||
A class-based generic view to handle the display and processing of
|
||||
"""A class-based generic view to handle the display and processing of
|
||||
multiple :class:`~horizon.tables.DataTable` classes in a single view.
|
||||
|
||||
Three steps are required to use this view: set the ``table_classes``
|
||||
@ -164,7 +163,7 @@ class MultiTableView(MultiTableMixin, generic.TemplateView):
|
||||
|
||||
|
||||
class DataTableView(MultiTableView):
|
||||
""" A class-based generic view to handle basic DataTable processing.
|
||||
"""A class-based generic view to handle basic DataTable processing.
|
||||
|
||||
Three steps are required to use this view: set the ``table_class``
|
||||
attribute with the desired :class:`~horizon.tables.DataTable` class;
|
||||
@ -211,7 +210,7 @@ class DataTableView(MultiTableView):
|
||||
|
||||
|
||||
class MixedDataTableView(DataTableView):
|
||||
""" A class-based generic view to handle DataTable with mixed data
|
||||
"""A class-based generic view to handle DataTable with mixed data
|
||||
types.
|
||||
|
||||
Basic usage is the same as DataTableView.
|
||||
|
@ -30,8 +30,7 @@ CSS_DISABLED_TAB_CLASSES = ["disabled"]
|
||||
|
||||
|
||||
class TabGroup(html.HTMLElement):
|
||||
"""
|
||||
A container class which knows how to manage and render
|
||||
"""A container class which knows how to manage and render
|
||||
:class:`~horizon.tabs.Tab` objects.
|
||||
|
||||
.. attribute:: slug
|
||||
@ -110,9 +109,7 @@ class TabGroup(html.HTMLElement):
|
||||
return "<%s: %s>" % (self.__class__.__name__, self.slug)
|
||||
|
||||
def load_tab_data(self):
|
||||
"""
|
||||
Preload all data that for the tabs that will be displayed.
|
||||
"""
|
||||
"""Preload all data that for the tabs that will be displayed."""
|
||||
for tab in self._tabs.values():
|
||||
if tab.load and not tab.data_loaded:
|
||||
try:
|
||||
@ -122,15 +119,13 @@ 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
|
||||
"""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()
|
||||
@ -138,8 +133,7 @@ class TabGroup(html.HTMLElement):
|
||||
return default_classes
|
||||
|
||||
def tabs_not_available(self):
|
||||
"""
|
||||
In the event that no tabs are either allowed or enabled, this method
|
||||
"""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.
|
||||
"""
|
||||
@ -169,15 +163,15 @@ class TabGroup(html.HTMLElement):
|
||||
return marked_active
|
||||
|
||||
def render(self):
|
||||
""" Renders the HTML output for this tab group. """
|
||||
"""Renders the HTML output for this tab group."""
|
||||
return render_to_string(self.template_name, {"tab_group": self})
|
||||
|
||||
def get_tabs(self):
|
||||
""" Returns a list of the allowed tabs for this tab group. """
|
||||
"""Returns a list of the allowed tabs for this tab group."""
|
||||
return filter(lambda tab: tab._allowed, self._tabs.values())
|
||||
|
||||
def get_tab(self, tab_name, allow_disabled=False):
|
||||
""" Returns a specific tab from this tab group.
|
||||
"""Returns a specific tab from this tab group.
|
||||
|
||||
If the tab is not allowed or not enabled this method returns ``None``.
|
||||
|
||||
@ -193,7 +187,7 @@ class TabGroup(html.HTMLElement):
|
||||
return filter(lambda t: self.get_tab(t.slug), self._tabs.values())
|
||||
|
||||
def get_selected_tab(self):
|
||||
""" Returns the tab specific by the GET request parameter.
|
||||
"""Returns the tab specific by the GET request parameter.
|
||||
|
||||
In the event that there is no GET request parameter, the value
|
||||
of the query parameter is invalid, or the tab is not allowed/enabled,
|
||||
@ -208,8 +202,7 @@ class TabGroup(html.HTMLElement):
|
||||
|
||||
|
||||
class Tab(html.HTMLElement):
|
||||
"""
|
||||
A reusable interface for constructing a tab within a
|
||||
"""A reusable interface for constructing a tab within a
|
||||
:class:`~horizon.tabs.TabGroup`.
|
||||
|
||||
.. attribute:: name
|
||||
@ -265,7 +258,7 @@ class Tab(html.HTMLElement):
|
||||
return "<%s: %s>" % (self.__class__.__name__, self.slug)
|
||||
|
||||
def is_active(self):
|
||||
""" Method to access whether or not this tab is the active tab. """
|
||||
"""Method to access whether or not this tab is the active tab."""
|
||||
if self._active is None:
|
||||
self.tab_group._set_active_tab()
|
||||
return self._active
|
||||
@ -286,8 +279,7 @@ 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 using the
|
||||
:meth:`~horizon.tabs.Tab.get_context_data` method and
|
||||
the :meth:`~horizon.tabs.Tab.get_template_name` method.
|
||||
|
||||
@ -309,8 +301,7 @@ 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
|
||||
"""Returns the id for this tab. Defaults to
|
||||
``"{{ tab_group.slug }}__{{ tab.slug }}"``.
|
||||
"""
|
||||
return SEPARATOR.join([self.tab_group.slug, self.slug])
|
||||
@ -319,8 +310,7 @@ 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
|
||||
"""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:
|
||||
|
||||
@ -338,8 +328,7 @@ class Tab(html.HTMLElement):
|
||||
return default_classes
|
||||
|
||||
def get_template_name(self, request):
|
||||
"""
|
||||
Returns the name of the template to be used for rendering this tab.
|
||||
"""Returns the name of the template to be used for rendering this tab.
|
||||
|
||||
By default it returns the value of the ``template_name`` attribute
|
||||
on the ``Tab`` class.
|
||||
@ -351,16 +340,14 @@ class Tab(html.HTMLElement):
|
||||
return self.template_name
|
||||
|
||||
def get_context_data(self, request):
|
||||
"""
|
||||
This method should return a dictionary of context data used to render
|
||||
the tab. Required.
|
||||
"""This method should return a dictionary of context data used to
|
||||
render the tab. Required.
|
||||
"""
|
||||
raise NotImplementedError("%s needs to define a get_context_data "
|
||||
"method." % self.__class__.__name__)
|
||||
|
||||
def enabled(self, request):
|
||||
"""
|
||||
Determines whether or not the tab should be accessible
|
||||
"""Determines whether or not the tab should be accessible
|
||||
(e.g. 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
|
||||
@ -371,8 +358,7 @@ class Tab(html.HTMLElement):
|
||||
return True
|
||||
|
||||
def allowed(self, request):
|
||||
"""
|
||||
Determines whether or not the tab is displayed.
|
||||
"""Determines whether or not the tab is displayed.
|
||||
|
||||
Tab instances can override this method to specify conditions under
|
||||
which this tab should not be shown at all by returning ``False``.
|
||||
@ -382,8 +368,7 @@ class Tab(html.HTMLElement):
|
||||
return True
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
Handles POST data sent to a tab.
|
||||
"""Handles POST data sent to a tab.
|
||||
|
||||
Tab instances can override this method to have tab-specific POST logic
|
||||
without polluting the TabView code.
|
||||
@ -394,8 +379,7 @@ class Tab(html.HTMLElement):
|
||||
|
||||
|
||||
class TableTab(Tab):
|
||||
"""
|
||||
A :class:`~horizon.tabs.Tab` class which knows how to deal with
|
||||
"""A :class:`~horizon.tabs.Tab` class which knows how to deal with
|
||||
:class:`~horizon.tables.DataTable` classes rendered inside of it.
|
||||
|
||||
This distinct class is required due to the complexity involved in handling
|
||||
@ -427,8 +411,7 @@ class TableTab(Tab):
|
||||
self._table_data_loaded = False
|
||||
|
||||
def load_table_data(self):
|
||||
"""
|
||||
Calls the ``get_{{ table_name }}_data`` methods for each table class
|
||||
"""Calls the ``get_{{ table_name }}_data`` methods for each table class
|
||||
and sets the data on the tables.
|
||||
"""
|
||||
# We only want the data to be loaded once, so we track if we have...
|
||||
@ -448,8 +431,7 @@ class TableTab(Tab):
|
||||
self._table_data_loaded = True
|
||||
|
||||
def get_context_data(self, request):
|
||||
"""
|
||||
Adds a ``{{ table_name }}_table`` item to the context for each table
|
||||
"""Adds a ``{{ table_name }}_table`` item to the context for each table
|
||||
in the :attr:`~horizon.tabs.TableTab.table_classes` attribute.
|
||||
|
||||
If only one table class is provided, a shortcut ``table`` context
|
||||
|
@ -19,8 +19,8 @@ from horizon.tabs.base import TableTab # noqa
|
||||
|
||||
|
||||
class TabView(generic.TemplateView):
|
||||
"""
|
||||
A generic class-based view for displaying a :class:`horizon.tabs.TabGroup`.
|
||||
"""A generic class-based view for displaying a
|
||||
:class:`horizon.tabs.TabGroup`.
|
||||
|
||||
This view handles selecting specific tabs and deals with AJAX requests
|
||||
gracefully.
|
||||
@ -39,13 +39,13 @@ class TabView(generic.TemplateView):
|
||||
"on %s." % self.__class__.__name__)
|
||||
|
||||
def get_tabs(self, request, **kwargs):
|
||||
""" Returns the initialized tab group for this view. """
|
||||
"""Returns the initialized tab group for this view."""
|
||||
if self._tab_group is None:
|
||||
self._tab_group = self.tab_group_class(request, **kwargs)
|
||||
return self._tab_group
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
""" Adds the ``tab_group`` variable to the context data. """
|
||||
"""Adds the ``tab_group`` variable to the context data."""
|
||||
context = super(TabView, self).get_context_data(**kwargs)
|
||||
try:
|
||||
tab_group = self.get_tabs(self.request, **kwargs)
|
||||
@ -57,8 +57,7 @@ class TabView(generic.TemplateView):
|
||||
return context
|
||||
|
||||
def handle_tabbed_response(self, tab_group, context):
|
||||
"""
|
||||
Sends back an AJAX-appropriate response for the tab group if
|
||||
"""Sends back an AJAX-appropriate response for the tab group if
|
||||
required, otherwise renders the response as normal.
|
||||
"""
|
||||
if self.request.is_ajax():
|
||||
@ -90,8 +89,7 @@ class TabbedTableView(tables.MultiTableMixin, TabView):
|
||||
self._table_dict = {}
|
||||
|
||||
def load_tabs(self):
|
||||
"""
|
||||
Loads the tab group, and compiles the table instances for each
|
||||
"""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.
|
||||
@ -105,14 +103,13 @@ class TabbedTableView(tables.MultiTableMixin, TabView):
|
||||
'tab': tab}
|
||||
|
||||
def get_tables(self):
|
||||
""" A no-op on this class. Tables are handled at the tab level. """
|
||||
"""A no-op on this class. Tables are handled at the tab level."""
|
||||
# Override the base class implementation so that the MultiTableMixin
|
||||
# doesn't freak out. We do the processing at the TableTab level.
|
||||
return {}
|
||||
|
||||
def handle_table(self, table_dict):
|
||||
"""
|
||||
For the given dict containing a ``DataTable`` and a ``TableTab``
|
||||
"""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``.
|
||||
|
@ -30,8 +30,7 @@ register = template.Library()
|
||||
|
||||
@register.filter
|
||||
def has_permissions(user, component):
|
||||
"""
|
||||
Checks if the given user meets the permissions requirements for
|
||||
"""Checks if the given user meets the permissions requirements for
|
||||
the component.
|
||||
"""
|
||||
return user.has_perms(getattr(component, 'permissions', set()))
|
||||
@ -45,7 +44,7 @@ def has_permissions_on_list(components, user):
|
||||
|
||||
@register.inclusion_tag('horizon/_nav_list.html', takes_context=True)
|
||||
def horizon_main_nav(context):
|
||||
""" Generates top-level dashboard navigation entries. """
|
||||
"""Generates top-level dashboard navigation entries."""
|
||||
if 'request' not in context:
|
||||
return {}
|
||||
current_dashboard = context['request'].horizon.get('dashboard', None)
|
||||
@ -63,7 +62,7 @@ def horizon_main_nav(context):
|
||||
|
||||
@register.inclusion_tag('horizon/_subnav_list.html', takes_context=True)
|
||||
def horizon_dashboard_nav(context):
|
||||
""" Generates sub-navigation entries for the current dashboard. """
|
||||
"""Generates sub-navigation entries for the current dashboard."""
|
||||
if 'request' not in context:
|
||||
return {}
|
||||
dashboard = context['request'].horizon['dashboard']
|
||||
@ -97,7 +96,7 @@ def quota(val, units=None):
|
||||
|
||||
|
||||
class JSTemplateNode(template.Node):
|
||||
""" Helper node for the ``jstemplate`` template tag. """
|
||||
"""Helper node for the ``jstemplate`` template tag."""
|
||||
def __init__(self, nodelist):
|
||||
self.nodelist = nodelist
|
||||
|
||||
@ -111,8 +110,7 @@ class JSTemplateNode(template.Node):
|
||||
|
||||
@register.tag
|
||||
def jstemplate(parser, token):
|
||||
"""
|
||||
Replaces ``[[[`` and ``]]]`` with ``{{{`` and ``}}}``,
|
||||
"""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
|
||||
|
@ -32,8 +32,8 @@ 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 input 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"]
|
||||
|
@ -73,8 +73,9 @@ def mbformat(mb):
|
||||
|
||||
@register.filter(name='mb_float_format')
|
||||
def mb_float_format(mb):
|
||||
""" Takes a size value in mb, and prints returns the data in a
|
||||
saner unit. """
|
||||
"""Takes a size value in mb, and prints returns the data in a
|
||||
saner unit.
|
||||
"""
|
||||
if not mb:
|
||||
return 0
|
||||
return filesizeformat(mb * 1024 * 1024, float_format)
|
||||
|
@ -75,8 +75,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
|
||||
"""Specialized base test case class for Horizon which gives access to
|
||||
numerous additional features:
|
||||
|
||||
* The ``mox`` mocking framework via ``self.mox``.
|
||||
@ -115,15 +114,13 @@ class TestCase(django_test.TestCase):
|
||||
del self.user._perm_cache
|
||||
|
||||
def assertNoMessages(self, response=None):
|
||||
"""
|
||||
Asserts that no messages have been attached by the ``contrib.messages``
|
||||
framework.
|
||||
"""Asserts that no messages have been attached by the
|
||||
``contrib.messages`` framework.
|
||||
"""
|
||||
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
|
||||
"""Asserts that the specified number of messages have been attached
|
||||
for various message types. Usage would look like
|
||||
``self.assertMessageCount(success=1)``.
|
||||
"""
|
||||
|
@ -102,12 +102,11 @@ class BaseHorizonTests(test.TestCase):
|
||||
dash.register(panel)
|
||||
|
||||
def _reload_urls(self):
|
||||
'''
|
||||
Clears out the URL caches, reloads the root urls module, and
|
||||
"""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.
|
||||
'''
|
||||
"""
|
||||
urlresolvers.clear_url_caches()
|
||||
reload(import_module(settings.ROOT_URLCONF))
|
||||
base.Horizon._urls()
|
||||
@ -116,7 +115,7 @@ class BaseHorizonTests(test.TestCase):
|
||||
class HorizonTests(BaseHorizonTests):
|
||||
|
||||
def test_registry(self):
|
||||
""" Verify registration and autodiscovery work correctly.
|
||||
"""Verify registration and autodiscovery work correctly.
|
||||
|
||||
Please note that this implicitly tests that autodiscovery works
|
||||
by virtue of the fact that the dashboards listed in
|
||||
@ -210,12 +209,12 @@ class HorizonTests(BaseHorizonTests):
|
||||
reversed(urlpatterns)
|
||||
|
||||
def test_horizon_test_isolation_1(self):
|
||||
""" Isolation Test Part 1: sets a value. """
|
||||
"""Isolation Test Part 1: sets a value."""
|
||||
cats = horizon.get_dashboard("cats")
|
||||
cats.evil = True
|
||||
|
||||
def test_horizon_test_isolation_2(self):
|
||||
""" Isolation Test Part 2: The value set in part 1 should be gone. """
|
||||
"""Isolation Test Part 2: The value set in part 1 should be gone."""
|
||||
cats = horizon.get_dashboard("cats")
|
||||
self.assertFalse(hasattr(cats, "evil"))
|
||||
|
||||
@ -299,7 +298,7 @@ class HorizonTests(BaseHorizonTests):
|
||||
|
||||
class CustomPanelTests(BaseHorizonTests):
|
||||
|
||||
""" Test customization of dashboards and panels
|
||||
"""Test customization of dashboards and panels
|
||||
using 'customization_module' to HORIZON_CONFIG.
|
||||
"""
|
||||
|
||||
@ -334,7 +333,7 @@ class CustomPanelTests(BaseHorizonTests):
|
||||
|
||||
class CustomPermissionsTests(BaseHorizonTests):
|
||||
|
||||
""" Test customization of permissions on panels
|
||||
"""Test customization of permissions on panels
|
||||
using 'customization_module' to HORIZON_CONFIG.
|
||||
"""
|
||||
|
||||
|
@ -190,7 +190,7 @@ class NoActionsTable(tables.DataTable):
|
||||
|
||||
class DataTableTests(test.TestCase):
|
||||
def test_table_instantiation(self):
|
||||
""" Tests everything that happens when the table is instantiated. """
|
||||
"""Tests everything that happens when the table is instantiated."""
|
||||
self.table = MyTable(self.request, TEST_DATA)
|
||||
# Properties defined on the table
|
||||
self.assertEqual(self.table.data, TEST_DATA)
|
||||
|
@ -29,22 +29,22 @@ from horizon.test import helpers as test
|
||||
|
||||
|
||||
def single_line(text):
|
||||
''' Quick utility to make comparing template output easier. '''
|
||||
"""Quick utility to make comparing template output easier."""
|
||||
return re.sub(' +',
|
||||
' ',
|
||||
normalize_newlines(text).replace('\n', '')).strip()
|
||||
|
||||
|
||||
class TemplateTagTests(test.TestCase):
|
||||
'''Test Custom Template Tag'''
|
||||
"""Test Custom Template Tag."""
|
||||
def render_template_tag(self, tag_name, tag_require=''):
|
||||
'''Render a Custom Template Tag to string'''
|
||||
"""Render a Custom Template Tag to string."""
|
||||
template = Template("{%% load %s %%}{%% %s %%}"
|
||||
% (tag_require, tag_name))
|
||||
return template.render(Context())
|
||||
|
||||
def test_site_branding_tag(self):
|
||||
'''Test if site_branding tag renders the correct setting'''
|
||||
"""Test if site_branding tag renders the correct setting."""
|
||||
rendered_str = self.render_template_tag("site_branding", "branding")
|
||||
self.assertEqual(settings.SITE_BRANDING, rendered_str.strip(),
|
||||
"tag site_branding renders %s" % rendered_str.strip())
|
||||
|
@ -27,8 +27,7 @@ IPv6 = 2
|
||||
|
||||
|
||||
class IPField(forms.Field):
|
||||
"""
|
||||
Form field for entering IP/range values, with validation.
|
||||
"""Form field for entering IP/range values, with validation.
|
||||
Supports IPv4/IPv6 in the format:
|
||||
.. xxx.xxx.xxx.xxx
|
||||
.. xxx.xxx.xxx.xxx/zz
|
||||
@ -102,9 +101,7 @@ class IPField(forms.Field):
|
||||
|
||||
|
||||
class MultiIPField(IPField):
|
||||
"""
|
||||
Extends IPField to allow comma-separated lists of addresses
|
||||
"""
|
||||
"""Extends IPField to allow comma-separated lists of addresses."""
|
||||
def validate(self, value):
|
||||
self.addresses = []
|
||||
if value:
|
||||
@ -121,8 +118,7 @@ class MultiIPField(IPField):
|
||||
|
||||
|
||||
class SelectWidget(widgets.Select):
|
||||
"""
|
||||
Customizable select widget, that allows to render
|
||||
"""Customizable select widget, that allows to render
|
||||
data-xxx attributes from choices.
|
||||
|
||||
.. attribute:: data_attrs
|
||||
|
@ -29,8 +29,7 @@ def replace_underscores(string):
|
||||
|
||||
@register.filter
|
||||
def parse_isotime(timestr):
|
||||
"""
|
||||
This duplicates oslo timeutils parse_isotime but with a
|
||||
"""This duplicates oslo timeutils parse_isotime but with a
|
||||
@register.filter annotation.
|
||||
"""
|
||||
try:
|
||||
|
@ -16,28 +16,25 @@ from django.forms.util import flatatt # noqa
|
||||
|
||||
|
||||
class HTMLElement(object):
|
||||
""" A generic base class that gracefully handles html-style attributes. """
|
||||
"""A generic base class that gracefully handles html-style attributes."""
|
||||
def __init__(self):
|
||||
self.attrs = getattr(self, "attrs", {})
|
||||
self.classes = getattr(self, "classes", [])
|
||||
|
||||
def get_default_classes(self):
|
||||
"""
|
||||
Returns an iterable of default classes which should be combined with
|
||||
"""Returns an iterable of default classes which should be combined with
|
||||
any other declared classes.
|
||||
"""
|
||||
return []
|
||||
|
||||
def get_default_attrs(self):
|
||||
"""
|
||||
Returns a dict of default attributes which should be combined with
|
||||
"""Returns a dict of default attributes which should be combined with
|
||||
other declared attributes.
|
||||
"""
|
||||
return {}
|
||||
|
||||
def get_final_attrs(self):
|
||||
"""
|
||||
Returns a dict containing the final attributes of this element
|
||||
"""Returns a dict containing the final attributes of this element
|
||||
which will be rendered.
|
||||
"""
|
||||
final_attrs = copy.copy(self.get_default_attrs())
|
||||
@ -53,16 +50,13 @@ 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 based on the
|
||||
``attrs`` dict provided to the class.
|
||||
"""
|
||||
return flatatt(self.get_final_attrs())
|
||||
|
||||
@property
|
||||
def class_string(self):
|
||||
"""
|
||||
Returns a list of class name of HTML Element in string
|
||||
"""
|
||||
"""Returns a list of class name of HTML Element in string."""
|
||||
classes_str = " ".join(self.classes)
|
||||
return classes_str
|
||||
|
@ -39,7 +39,7 @@ def password_validator_msg():
|
||||
|
||||
|
||||
def validate_port_or_colon_separated_port_range(port_range):
|
||||
"""accepts a port number or a single-colon separated range"""
|
||||
"""Accepts a port number or a single-colon separated range."""
|
||||
if port_range.count(':') > 1:
|
||||
raise ValidationError(_("One colon allowed in port range"))
|
||||
ports = port_range.split(':')
|
||||
|
@ -22,12 +22,12 @@ from horizon import exceptions
|
||||
|
||||
|
||||
def user_home(request):
|
||||
""" Reversible named view to direct a user to the appropriate homepage. """
|
||||
"""Reversible named view to direct a user to the appropriate homepage."""
|
||||
return shortcuts.redirect(horizon.get_user_home(request.user))
|
||||
|
||||
|
||||
class APIView(generic.TemplateView):
|
||||
""" A quick class-based view for putting API data into a template.
|
||||
"""A quick class-based view for putting API data into a template.
|
||||
|
||||
Subclasses must define one method, ``get_data``, and a template name
|
||||
via the ``template_name`` attribute on the class.
|
||||
@ -37,8 +37,7 @@ class APIView(generic.TemplateView):
|
||||
caught.
|
||||
"""
|
||||
def get_data(self, request, context, *args, **kwargs):
|
||||
"""
|
||||
This method should handle any necessary API calls, update the
|
||||
"""This method should handle any necessary API calls, update the
|
||||
context object, and return the context object at the end.
|
||||
"""
|
||||
raise NotImplementedError("You must define a get_data method "
|
||||
|
@ -76,8 +76,7 @@ class ActionMetaclass(forms.forms.DeclarativeFieldsMetaclass):
|
||||
|
||||
|
||||
class Action(forms.Form):
|
||||
"""
|
||||
An ``Action`` represents an atomic logical interaction you can have with
|
||||
"""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
|
||||
@ -154,7 +153,7 @@ class Action(forms.Form):
|
||||
bound_field.choices = meth(request, context)
|
||||
|
||||
def get_help_text(self, extra_context=None):
|
||||
""" Returns the help text for this step. """
|
||||
"""Returns the help text for this step."""
|
||||
text = ""
|
||||
extra_context = extra_context or {}
|
||||
if self.help_text_template:
|
||||
@ -166,14 +165,11 @@ class Action(forms.Form):
|
||||
return safe(text)
|
||||
|
||||
def add_error(self, message):
|
||||
"""
|
||||
Adds an error to the Action's Step based on API issues.
|
||||
"""
|
||||
"""Adds an error to the Action's Step based on API issues."""
|
||||
self._get_errors()[NON_FIELD_ERRORS] = self.error_class([message])
|
||||
|
||||
def handle(self, request, context):
|
||||
"""
|
||||
Handles any requisite processing for this action. The method should
|
||||
"""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`.
|
||||
|
||||
@ -183,8 +179,7 @@ class Action(forms.Form):
|
||||
|
||||
|
||||
class MembershipAction(Action):
|
||||
"""
|
||||
An action that allows a user to add/remove members from a group.
|
||||
"""An action that allows a user to add/remove members from a group.
|
||||
|
||||
Extend the Action class with additional helper method for membership
|
||||
management.
|
||||
@ -197,8 +192,7 @@ class MembershipAction(Action):
|
||||
|
||||
|
||||
class Step(object):
|
||||
"""
|
||||
A step is a wrapper around an action which defines its context in a
|
||||
"""A step is 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).
|
||||
@ -380,9 +374,8 @@ 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
|
||||
"""Allows for customization of 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.
|
||||
|
||||
@ -391,7 +384,7 @@ class Step(object):
|
||||
return context
|
||||
|
||||
def get_id(self):
|
||||
""" Returns the ID for this step. Suitable for use in HTML markup. """
|
||||
"""Returns the ID for this step. Suitable for use in HTML markup."""
|
||||
return "%s__%s" % (self.workflow.slug, self.slug)
|
||||
|
||||
def _verify_contributions(self, context):
|
||||
@ -412,8 +405,7 @@ class Step(object):
|
||||
return True
|
||||
|
||||
def contribute(self, data, context):
|
||||
"""
|
||||
Adds the data listed in ``contributes`` to the workflow's shared
|
||||
"""Adds the data listed in ``contributes`` to the workflow's shared
|
||||
context. By default, the context is simply updated with all the data
|
||||
returned by the action.
|
||||
|
||||
@ -427,7 +419,7 @@ class Step(object):
|
||||
return context
|
||||
|
||||
def render(self):
|
||||
""" Renders the step. """
|
||||
"""Renders the step."""
|
||||
step_template = template.loader.get_template(self.template_name)
|
||||
extra_context = {"form": self.action,
|
||||
"step": self}
|
||||
@ -435,21 +427,17 @@ class Step(object):
|
||||
return step_template.render(context)
|
||||
|
||||
def get_help_text(self):
|
||||
""" Returns the help text for this step. """
|
||||
"""Returns the help text for this step."""
|
||||
text = linebreaks(force_unicode(self.help_text))
|
||||
text += self.action.get_help_text()
|
||||
return safe(text)
|
||||
|
||||
def add_error(self, message):
|
||||
"""
|
||||
Adds an error to the Step based on API issues.
|
||||
"""
|
||||
"""Adds an error to the Step based on API issues."""
|
||||
self.action.add_error(message)
|
||||
|
||||
def has_required_fields(self):
|
||||
"""
|
||||
Returns True if action contains any required fields
|
||||
"""
|
||||
"""Returns True if action contains any required fields."""
|
||||
for key in self.contributes:
|
||||
field = self.action.fields.get(key, None)
|
||||
if (field and field.required):
|
||||
@ -503,8 +491,7 @@ class UpdateMembersStep(Step):
|
||||
|
||||
|
||||
class Workflow(html.HTMLElement):
|
||||
"""
|
||||
A Workflow is a collection of Steps. Its interface is very
|
||||
"""A Workflow is a collection of Steps. Its interface is very
|
||||
straightforward, but it is responsible for handling some very
|
||||
important tasks such as:
|
||||
|
||||
@ -665,7 +652,7 @@ class Workflow(html.HTMLElement):
|
||||
return self._ordered_steps
|
||||
|
||||
def get_step(self, slug):
|
||||
""" Returns the instantiated step matching the given slug. """
|
||||
"""Returns the instantiated step matching the given slug."""
|
||||
for step in self.steps:
|
||||
if step.slug == slug:
|
||||
return step
|
||||
@ -705,8 +692,7 @@ class Workflow(html.HTMLElement):
|
||||
return steps
|
||||
|
||||
def get_entry_point(self):
|
||||
"""
|
||||
Returns the slug of the step which the workflow should begin on.
|
||||
"""Returns the slug of the step which the workflow should begin on.
|
||||
|
||||
This method takes into account both already-available data and errors
|
||||
within the steps.
|
||||
@ -736,7 +722,7 @@ class Workflow(html.HTMLElement):
|
||||
|
||||
@classmethod
|
||||
def register(cls, step_class):
|
||||
""" Registers a :class:`~horizon.workflows.Step` with the workflow. """
|
||||
"""Registers a :class:`~horizon.workflows.Step` with the workflow."""
|
||||
if not inspect.isclass(step_class):
|
||||
raise ValueError('Only classes may be registered.')
|
||||
elif not issubclass(step_class, cls._registerable_class):
|
||||
@ -750,8 +736,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)
|
||||
@ -760,15 +745,13 @@ class Workflow(html.HTMLElement):
|
||||
return cls._unregister(step_class)
|
||||
|
||||
def validate(self, context):
|
||||
"""
|
||||
Hook for custom context data validation. Should return a boolean
|
||||
"""Hook for custom context data validation. Should return a boolean
|
||||
value or raise :class:`~horizon.exceptions.WorkflowValidationError`.
|
||||
"""
|
||||
return True
|
||||
|
||||
def is_valid(self):
|
||||
"""
|
||||
Verified that all required data is present in the context and
|
||||
"""Verified that all required data is present in the context and
|
||||
calls the ``validate`` method to allow for finer-grained checks
|
||||
on the context data.
|
||||
"""
|
||||
@ -790,8 +773,7 @@ class Workflow(html.HTMLElement):
|
||||
return self.validate(self.context)
|
||||
|
||||
def finalize(self):
|
||||
"""
|
||||
Finalizes a workflow by running through all the actions in order
|
||||
"""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.)
|
||||
@ -814,15 +796,13 @@ 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
|
||||
"""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.
|
||||
@ -833,8 +813,7 @@ 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
|
||||
"""Hook to allow customization of the message returned to the user
|
||||
upon successful or unsuccessful completion of the workflow.
|
||||
|
||||
By default it simply inserts the workflow's name into the message
|
||||
@ -846,7 +825,7 @@ class Workflow(html.HTMLElement):
|
||||
return message
|
||||
|
||||
def render(self):
|
||||
""" Renders the workflow. """
|
||||
"""Renders the workflow."""
|
||||
workflow_template = template.loader.get_template(self.template_name)
|
||||
extra_context = {"workflow": self}
|
||||
if self.request.is_ajax():
|
||||
@ -855,7 +834,7 @@ class Workflow(html.HTMLElement):
|
||||
return workflow_template.render(context)
|
||||
|
||||
def get_absolute_url(self):
|
||||
""" Returns the canonical URL for this workflow.
|
||||
"""Returns the canonical URL for this workflow.
|
||||
|
||||
This is used for the POST action attribute on the form element
|
||||
wrapping the workflow.
|
||||
@ -867,8 +846,7 @@ 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
|
||||
"""Adds an error to the workflow's Step with the
|
||||
specifed 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.
|
||||
|
@ -27,8 +27,7 @@ from horizon import messages
|
||||
|
||||
|
||||
class WorkflowView(generic.TemplateView):
|
||||
"""
|
||||
A generic class-based view which handles the intricacies of workflow
|
||||
"""A generic class-based view which handles the intricacies of workflow
|
||||
processing with minimal user configuration.
|
||||
|
||||
.. attribute:: workflow_class
|
||||
@ -65,14 +64,13 @@ class WorkflowView(generic.TemplateView):
|
||||
"on %s." % self.__class__.__name__)
|
||||
|
||||
def get_initial(self):
|
||||
"""
|
||||
Returns initial data for the workflow. Defaults to using the GET
|
||||
"""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)
|
||||
|
||||
def get_workflow(self):
|
||||
""" Returns the instanciated workflow class. """
|
||||
"""Returns the instanciated workflow class."""
|
||||
extra_context = self.get_initial()
|
||||
entry_point = self.request.GET.get("step", None)
|
||||
workflow = self.workflow_class(self.request,
|
||||
@ -81,8 +79,7 @@ class WorkflowView(generic.TemplateView):
|
||||
return workflow
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""
|
||||
Returns the template context, including the workflow class.
|
||||
"""Returns the template context, including the workflow class.
|
||||
|
||||
This method should be overridden in subclasses to provide additional
|
||||
context data to the template.
|
||||
@ -99,7 +96,7 @@ class WorkflowView(generic.TemplateView):
|
||||
return context
|
||||
|
||||
def get_template_names(self):
|
||||
""" Returns the template name to use for this request. """
|
||||
"""Returns the template name to use for this request."""
|
||||
if self.request.is_ajax():
|
||||
template = self.ajax_template_name
|
||||
else:
|
||||
@ -122,13 +119,13 @@ class WorkflowView(generic.TemplateView):
|
||||
workflow.add_error_to_step(error_msg, step)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
""" Handler for HTTP GET requests. """
|
||||
"""Handler for HTTP GET requests."""
|
||||
context = self.get_context_data(**kwargs)
|
||||
self.set_workflow_step_errors(context)
|
||||
return self.render_to_response(context)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
""" Handler for HTTP POST requests. """
|
||||
"""Handler for HTTP POST requests."""
|
||||
context = self.get_context_data(**kwargs)
|
||||
workflow = context[self.context_object_name]
|
||||
if workflow.is_valid():
|
||||
|
@ -34,7 +34,7 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class APIVersionManager(object):
|
||||
""" Object to store and manage API versioning data and utility methods. """
|
||||
"""Object to store and manage API versioning data and utility methods."""
|
||||
|
||||
SETTINGS_KEY = "OPENSTACK_API_VERSIONS"
|
||||
|
||||
@ -67,10 +67,10 @@ class APIVersionManager(object):
|
||||
|
||||
|
||||
class APIResourceWrapper(object):
|
||||
""" Simple wrapper for api objects
|
||||
"""Simple wrapper for api objects.
|
||||
|
||||
Define _attrs on the child class and pass in the
|
||||
api object as the only argument to the constructor
|
||||
Define _attrs on the child class and pass in the
|
||||
api object as the only argument to the constructor
|
||||
"""
|
||||
_attrs = []
|
||||
|
||||
@ -97,14 +97,14 @@ class APIResourceWrapper(object):
|
||||
|
||||
|
||||
class APIDictWrapper(object):
|
||||
""" Simple wrapper for api dictionaries
|
||||
"""Simple wrapper for api dictionaries
|
||||
|
||||
Some api calls return dictionaries. This class provides identical
|
||||
behavior as APIResourceWrapper, except that it will also behave as a
|
||||
dictionary, in addition to attribute accesses.
|
||||
Some api calls return dictionaries. This class provides identical
|
||||
behavior as APIResourceWrapper, except that it will also behave as a
|
||||
dictionary, in addition to attribute accesses.
|
||||
|
||||
Attribute access is the preferred method of access, to be
|
||||
consistent with api resource objects from novaclient.
|
||||
Attribute access is the preferred method of access, to be
|
||||
consistent with api resource objects from novaclient.
|
||||
"""
|
||||
def __init__(self, apidict):
|
||||
self._apidict = apidict
|
||||
@ -146,8 +146,7 @@ class Quota(object):
|
||||
|
||||
|
||||
class QuotaSet(Sequence):
|
||||
"""
|
||||
Wrapper for client QuotaSet objects which turns the individual quotas
|
||||
"""Wrapper for client QuotaSet objects which turns the individual quotas
|
||||
into Quota objects for easier handling/iteration.
|
||||
|
||||
`QuotaSet` objects support a mix of `list` and `dict` methods; you can use
|
||||
@ -177,8 +176,9 @@ class QuotaSet(Sequence):
|
||||
return self.items[index]
|
||||
|
||||
def __add__(self, other):
|
||||
'''Merge another QuotaSet into this one. Existing quotas are
|
||||
not overriden.'''
|
||||
"""Merge another QuotaSet into this one. Existing quotas are
|
||||
not overriden.
|
||||
"""
|
||||
if not isinstance(other, QuotaSet):
|
||||
msg = "Can only add QuotaSet to QuotaSet, " \
|
||||
"but received %s instead" % type(other)
|
||||
|
@ -25,8 +25,7 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def is_iterable(var):
|
||||
""" Return True if the given is list or tuple.
|
||||
"""
|
||||
"""Return True if the given is list or tuple."""
|
||||
|
||||
return (isinstance(var, (list, tuple)) or
|
||||
issubclass(var.__class__, (list, tuple)))
|
||||
@ -34,7 +33,7 @@ def is_iterable(var):
|
||||
|
||||
def make_query(user_id=None, tenant_id=None, resource_id=None,
|
||||
user_ids=None, tenant_ids=None, resource_ids=None):
|
||||
""" Returns query built form given parameters.
|
||||
"""Returns query built form given parameters.
|
||||
|
||||
This query can be then used for querying resources, meters and
|
||||
statistics.
|
||||
@ -71,15 +70,13 @@ def make_query(user_id=None, tenant_id=None, resource_id=None,
|
||||
|
||||
|
||||
class Meter(base.APIResourceWrapper):
|
||||
""" Represents one Ceilometer meter.
|
||||
"""
|
||||
"""Represents one Ceilometer meter."""
|
||||
_attrs = ['name', 'type', 'unit', 'resource_id', 'user_id',
|
||||
'project_id']
|
||||
|
||||
|
||||
class Resource(base.APIResourceWrapper):
|
||||
""" Represents one Ceilometer resource.
|
||||
"""
|
||||
"""Represents one Ceilometer resource."""
|
||||
_attrs = ['resource_id', 'source', 'user_id', 'project_id', 'metadata',
|
||||
'links']
|
||||
|
||||
@ -140,7 +137,7 @@ class Resource(base.APIResourceWrapper):
|
||||
|
||||
|
||||
class ResourceAggregate(Resource):
|
||||
""" Represents aggregate of more resources together.
|
||||
"""Represents aggregate of more resources together.
|
||||
|
||||
Aggregate of resources can be obtain by specifing
|
||||
multiple ids in one parameter or by not specifying
|
||||
@ -194,8 +191,7 @@ class ResourceAggregate(Resource):
|
||||
|
||||
|
||||
class Sample(base.APIResourceWrapper):
|
||||
""" Represents one Ceilometer sample.
|
||||
"""
|
||||
"""Represents one Ceilometer sample."""
|
||||
|
||||
_attrs = ['counter_name', 'user_id', 'resource_id', 'timestamp',
|
||||
'resource_metadata', 'source', 'counter_unit', 'counter_volume',
|
||||
@ -215,8 +211,7 @@ class Sample(base.APIResourceWrapper):
|
||||
|
||||
|
||||
class Statistic(base.APIResourceWrapper):
|
||||
""" Represents one Ceilometer statistic.
|
||||
"""
|
||||
"""Represents one Ceilometer statistic."""
|
||||
|
||||
_attrs = ['period', 'period_start', 'period_end',
|
||||
'count', 'min', 'max', 'sum', 'avg',
|
||||
@ -224,11 +219,11 @@ class Statistic(base.APIResourceWrapper):
|
||||
|
||||
|
||||
class GlobalDiskUsage(base.APIResourceWrapper):
|
||||
""" Represents collection of resources with statistic of defined meters.
|
||||
"""Represents collection of resources with statistic of defined meters.
|
||||
|
||||
Resources are filtered either by given default query or by
|
||||
meters in python. It's preferred to use default_query as it is more
|
||||
effective.
|
||||
Resources are filtered either by given default query or by
|
||||
meters in python. It's preferred to use default_query as it is more
|
||||
effective.
|
||||
"""
|
||||
|
||||
_attrs = ["id", "tenant", "user", "resource", "disk_read_bytes",
|
||||
@ -247,11 +242,11 @@ class GlobalDiskUsage(base.APIResourceWrapper):
|
||||
|
||||
|
||||
class GlobalNetworkTrafficUsage(base.APIResourceWrapper):
|
||||
""" Represents collection of resources with statistic of defined meters.
|
||||
"""Represents collection of resources with statistic of defined meters.
|
||||
|
||||
Resources are filtered either by given default query or by
|
||||
meters in python. It's preferred to use default_query as it is more
|
||||
effective.
|
||||
Resources are filtered either by given default query or by
|
||||
meters in python. It's preferred to use default_query as it is more
|
||||
effective.
|
||||
"""
|
||||
|
||||
_attrs = ["id", "tenant", "user", "resource", "network_incoming_bytes",
|
||||
@ -270,11 +265,11 @@ class GlobalNetworkTrafficUsage(base.APIResourceWrapper):
|
||||
|
||||
|
||||
class GlobalNetworkUsage(base.APIResourceWrapper):
|
||||
""" Represents collection of resources with statistic of defined meters.
|
||||
"""Represents collection of resources with statistic of defined meters.
|
||||
|
||||
Resources are filtered either by given default query or by
|
||||
meters in python. It's preferred to use default_query as it is more
|
||||
effective.
|
||||
Resources are filtered either by given default query or by
|
||||
meters in python. It's preferred to use default_query as it is more
|
||||
effective.
|
||||
"""
|
||||
|
||||
_attrs = ["id", "tenant", "user", "resource", "network", "network_create",
|
||||
@ -294,11 +289,11 @@ class GlobalNetworkUsage(base.APIResourceWrapper):
|
||||
|
||||
|
||||
class GlobalObjectStoreUsage(base.APIResourceWrapper):
|
||||
""" Represents collection of resources with statistic of defined meters.
|
||||
"""Represents collection of resources with statistic of defined meters.
|
||||
|
||||
Resources are filtered either by given default query or by
|
||||
meters in python. It's preferred to use default_query as it is more
|
||||
effective.
|
||||
Resources are filtered either by given default query or by
|
||||
meters in python. It's preferred to use default_query as it is more
|
||||
effective.
|
||||
"""
|
||||
|
||||
_attrs = ["id", "tenant", "user", "resource", "storage_objects",
|
||||
@ -317,8 +312,7 @@ class GlobalObjectStoreUsage(base.APIResourceWrapper):
|
||||
|
||||
|
||||
def ceilometerclient(request):
|
||||
""" Initialization of Ceilometer client.
|
||||
"""
|
||||
"""Initialization of Ceilometer client."""
|
||||
|
||||
endpoint = base.url_for(request, 'metering')
|
||||
insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
|
||||
@ -359,8 +353,8 @@ def statistic_list(request, meter_name, query=None, period=None):
|
||||
|
||||
|
||||
class ThreadedUpdateResourceWithStatistics(threading.Thread):
|
||||
""" Multithread wrapper for update_with_statistics method of
|
||||
resource_usage.
|
||||
"""Multithread wrapper for update_with_statistics method of
|
||||
resource_usage.
|
||||
|
||||
A join logic is placed in process_list class method. All resources
|
||||
will have its statistics attribute filled in separate threads.
|
||||
@ -426,7 +420,7 @@ class ThreadedUpdateResourceWithStatistics(threading.Thread):
|
||||
|
||||
|
||||
class CeilometerUsage(object):
|
||||
""" Represents wrapper of any Ceilometer queries.
|
||||
"""Represents wrapper of any Ceilometer queries.
|
||||
|
||||
One instance of this class should be shared between resources
|
||||
as this class provides a place where users and tenants are
|
||||
@ -448,7 +442,7 @@ class CeilometerUsage(object):
|
||||
self._tenants = {}
|
||||
|
||||
def get_user(self, user_id):
|
||||
""" Returns user fetched form API
|
||||
"""Returns user fetched form API
|
||||
|
||||
Caching the result, so it doesn't contact API twice with the
|
||||
same query
|
||||
@ -462,7 +456,7 @@ class CeilometerUsage(object):
|
||||
return user
|
||||
|
||||
def preload_all_users(self):
|
||||
""" Preloads all users into dictionary.
|
||||
"""Preloads all users into dictionary.
|
||||
|
||||
It's more effective to preload all users, rather the fetching many
|
||||
users by separate API get calls.
|
||||
@ -475,7 +469,7 @@ class CeilometerUsage(object):
|
||||
self._users[u.id] = u
|
||||
|
||||
def get_tenant(self, tenant_id):
|
||||
""" Returns tenant fetched form API
|
||||
"""Returns tenant fetched form API.
|
||||
|
||||
Caching the result, so it doesn't contact API twice with the
|
||||
same query
|
||||
@ -489,7 +483,7 @@ class CeilometerUsage(object):
|
||||
return tenant
|
||||
|
||||
def preload_all_tenants(self):
|
||||
""" Preloads all teannts into dictionary.
|
||||
"""Preloads all teannts into dictionary.
|
||||
|
||||
It's more effective to preload all tenants, rather the fetching many
|
||||
tenants by separate API get calls.
|
||||
@ -504,7 +498,7 @@ class CeilometerUsage(object):
|
||||
def global_data_get(self, used_cls=None, query=None,
|
||||
with_statistics=False, additional_query=None,
|
||||
with_users_and_tenants=True):
|
||||
""" Obtaining a resources for table view.
|
||||
"""Obtaining a resources for table view.
|
||||
|
||||
It obtains resources with statistics data according to declaration
|
||||
in used_cls class.
|
||||
@ -534,10 +528,9 @@ class CeilometerUsage(object):
|
||||
filter_func = None
|
||||
|
||||
def filter_resources(resource):
|
||||
""" Method for filtering resources by theirs links.rel attr.
|
||||
"""Method for filtering resources by theirs links.rel attr.
|
||||
|
||||
The links.rel attributes contains all meters the resource
|
||||
have.
|
||||
The links.rel attributes contains all meters the resource have.
|
||||
"""
|
||||
for link in resource.links:
|
||||
if link['rel'] in used_cls.meters:
|
||||
@ -567,8 +560,7 @@ class CeilometerUsage(object):
|
||||
|
||||
def global_disk_usage(self, query=None, with_statistics=False,
|
||||
additional_query=None):
|
||||
""" Wrapper for specific call of global_data_get.
|
||||
"""
|
||||
"""Wrapper for specific call of global_data_get."""
|
||||
|
||||
return self.global_data_get(used_cls=GlobalDiskUsage,
|
||||
query=query,
|
||||
@ -577,7 +569,7 @@ class CeilometerUsage(object):
|
||||
|
||||
def global_network_traffic_usage(self, query=None, with_statistics=False,
|
||||
additional_query=None):
|
||||
""" Wrapper for specific call of global_data_get.
|
||||
"""Wrapper for specific call of global_data_get.
|
||||
"""
|
||||
|
||||
return self.global_data_get(used_cls=GlobalNetworkTrafficUsage,
|
||||
@ -587,7 +579,7 @@ class CeilometerUsage(object):
|
||||
|
||||
def global_network_usage(self, query=None, with_statistics=False,
|
||||
additional_query=None):
|
||||
""" Wrapper for specific call of global_data_get.
|
||||
"""Wrapper for specific call of global_data_get.
|
||||
"""
|
||||
|
||||
return self.global_data_get(used_cls=GlobalNetworkUsage,
|
||||
@ -597,7 +589,7 @@ class CeilometerUsage(object):
|
||||
|
||||
def global_object_store_usage(self, query=None, with_statistics=False,
|
||||
additional_query=None):
|
||||
""" Wrapper for specific call of global_data_get.
|
||||
"""Wrapper for specific call of global_data_get.
|
||||
"""
|
||||
# TODO(lsmola) This call uses all tenants now. When ajax pagination
|
||||
# is added, it needs to obtain only paginated tenants.
|
||||
@ -609,7 +601,7 @@ class CeilometerUsage(object):
|
||||
additional_query=additional_query)
|
||||
|
||||
def query_from_object_id(self, object_id):
|
||||
""" Obtaining a query from resource id.
|
||||
"""Obtaining a query from resource id.
|
||||
|
||||
Query can be then used to identify a resource in resources or meters
|
||||
API calls. ID is being built in the Resource initializer, or returned
|
||||
@ -625,7 +617,7 @@ class CeilometerUsage(object):
|
||||
|
||||
def update_with_statistics(self, resource, meter_names=None, period=None,
|
||||
stats_attr=None, additional_query=None):
|
||||
""" Adding statistical data into one Resource or ResourceAggregate.
|
||||
"""Adding statistical data into one Resource or ResourceAggregate.
|
||||
|
||||
It adds each statistic of each meter_names into the resource
|
||||
attributes. Attribute name is the meter name with replaced '.' to '_'.
|
||||
@ -684,7 +676,7 @@ class CeilometerUsage(object):
|
||||
|
||||
def resources(self, query=None, filter_func=None,
|
||||
with_users_and_tenants=False):
|
||||
""" Obtaining resources with the query or filter_func.
|
||||
"""Obtaining resources with the query or filter_func.
|
||||
|
||||
Obtains resources and also fetch tenants and users associated
|
||||
with those resources if with_users_and_tenants flag is true.
|
||||
@ -712,7 +704,7 @@ class CeilometerUsage(object):
|
||||
period=None, filter_func=None,
|
||||
stats_attr=None, additional_query=None,
|
||||
with_users_and_tenants=False):
|
||||
""" Obtaining resources with statistics data inside.
|
||||
"""Obtaining resources with statistics data inside.
|
||||
|
||||
:Parameters:
|
||||
- `query`: Query for fetching the Ceilometer Resources.
|
||||
@ -745,7 +737,7 @@ class CeilometerUsage(object):
|
||||
return resources
|
||||
|
||||
def resource_aggregates(self, queries=None):
|
||||
""" Obtaining resource aggregates with queries.
|
||||
"""Obtaining resource aggregates with queries.
|
||||
|
||||
Representing a resource aggregate by query is a most general way
|
||||
how to obtain a resource aggregates.
|
||||
@ -764,7 +756,7 @@ class CeilometerUsage(object):
|
||||
def resource_aggregates_with_statistics(self, queries=None,
|
||||
meter_names=None, period=None, filter_func=None, stats_attr=None,
|
||||
additional_query=None):
|
||||
""" Obtaining resource aggregates with statistics data inside.
|
||||
"""Obtaining resource aggregates with statistics data inside.
|
||||
|
||||
:Parameters:
|
||||
- `queries`: Dictionary of named queries that defines a bulk of
|
||||
|
@ -68,8 +68,7 @@ def cinderclient(request):
|
||||
|
||||
|
||||
def volume_list(request, search_opts=None):
|
||||
"""
|
||||
To see all volumes in the cloud as an admin you can pass in a special
|
||||
"""To see all volumes in the cloud as an admin you can pass in a special
|
||||
search option: {'all_tenants': 1}
|
||||
"""
|
||||
c_client = cinderclient(request)
|
||||
@ -181,8 +180,7 @@ def list_extensions(request):
|
||||
|
||||
@memoized
|
||||
def extension_supported(request, extension_name):
|
||||
"""
|
||||
This method will determine if Cinder supports a given extension name.
|
||||
"""This method will determine if Cinder supports a given extension name.
|
||||
"""
|
||||
extensions = list_extensions(request)
|
||||
for extension in extensions:
|
||||
|
@ -24,7 +24,7 @@ neutronclient = neutron.neutronclient
|
||||
|
||||
|
||||
class Rule(neutron.NeutronAPIDictWrapper):
|
||||
"""Wrapper for neutron firewall rule"""
|
||||
"""Wrapper for neutron firewall rule."""
|
||||
|
||||
def get_dict(self):
|
||||
rule_dict = self._apidict
|
||||
@ -33,7 +33,7 @@ class Rule(neutron.NeutronAPIDictWrapper):
|
||||
|
||||
|
||||
class Policy(neutron.NeutronAPIDictWrapper):
|
||||
"""Wrapper for neutron firewall policy"""
|
||||
"""Wrapper for neutron firewall policy."""
|
||||
|
||||
def get_dict(self):
|
||||
policy_dict = self._apidict
|
||||
@ -42,7 +42,7 @@ class Policy(neutron.NeutronAPIDictWrapper):
|
||||
|
||||
|
||||
class Firewall(neutron.NeutronAPIDictWrapper):
|
||||
"""Wrapper for neutron firewall"""
|
||||
"""Wrapper for neutron firewall."""
|
||||
|
||||
def get_dict(self):
|
||||
firewall_dict = self._apidict
|
||||
|
@ -51,8 +51,7 @@ def image_delete(request, image_id):
|
||||
|
||||
|
||||
def image_get(request, image_id):
|
||||
"""
|
||||
Returns an Image object populated with metadata for image
|
||||
"""Returns an Image object populated with metadata for image
|
||||
with supplied identifier.
|
||||
"""
|
||||
return glanceclient(request).images.get(image_id)
|
||||
|
@ -74,7 +74,7 @@ except ImportError:
|
||||
|
||||
|
||||
class Service(base.APIDictWrapper):
|
||||
""" Wrapper for a dict based on the service data from keystone. """
|
||||
"""Wrapper for a dict based on the service data from keystone."""
|
||||
_attrs = ['id', 'type', 'name']
|
||||
|
||||
def __init__(self, service, region, *args, **kwargs):
|
||||
@ -216,8 +216,7 @@ def tenant_create(request, name, description=None, enabled=None, domain=None):
|
||||
|
||||
|
||||
def get_default_domain(request):
|
||||
"""
|
||||
Gets the default domain object to use when creating Identity object.
|
||||
"""Gets the default domain object to use when creating Identity object.
|
||||
Returns the domain context if is set, otherwise return the domain
|
||||
of the logon user.
|
||||
"""
|
||||
@ -482,7 +481,7 @@ def role_delete(request, role_id):
|
||||
|
||||
|
||||
def role_list(request):
|
||||
""" Returns a global list of available roles. """
|
||||
"""Returns a global list of available roles."""
|
||||
return keystoneclient(request, admin=True).roles.list()
|
||||
|
||||
|
||||
@ -496,7 +495,7 @@ def roles_for_user(request, user, project):
|
||||
|
||||
def add_tenant_user_role(request, project=None, user=None, role=None,
|
||||
group=None, domain=None):
|
||||
""" Adds a role for a user on a tenant. """
|
||||
"""Adds a role for a user on a tenant."""
|
||||
manager = keystoneclient(request, admin=True).roles
|
||||
if VERSIONS.active < 3:
|
||||
return manager.add_user_role(user, role, project)
|
||||
@ -507,7 +506,7 @@ def add_tenant_user_role(request, project=None, user=None, role=None,
|
||||
|
||||
def remove_tenant_user_role(request, project=None, user=None, role=None,
|
||||
group=None, domain=None):
|
||||
""" Removes a given single role for a user from a tenant. """
|
||||
"""Removes a given single role for a user from a tenant."""
|
||||
manager = keystoneclient(request, admin=True).roles
|
||||
if VERSIONS.active < 3:
|
||||
return manager.remove_user_role(user, role, project)
|
||||
@ -517,7 +516,7 @@ def remove_tenant_user_role(request, project=None, user=None, role=None,
|
||||
|
||||
|
||||
def remove_tenant_user(request, project=None, user=None, domain=None):
|
||||
""" Removes all roles from a user on a tenant, removing them from it. """
|
||||
"""Removes all roles from a user on a tenant, removing them from it."""
|
||||
client = keystoneclient(request, admin=True)
|
||||
roles = client.roles.roles_for_user(user, project)
|
||||
for role in roles:
|
||||
@ -531,22 +530,22 @@ def roles_for_group(request, group, domain=None, project=None):
|
||||
|
||||
|
||||
def add_group_role(request, role, group, domain=None, project=None):
|
||||
""" Adds a role for a group on a domain or project ."""
|
||||
"""Adds a role for a group on a domain or project."""
|
||||
manager = keystoneclient(request, admin=True).roles
|
||||
return manager.grant(role=role, group=group, domain=domain,
|
||||
project=project)
|
||||
|
||||
|
||||
def remove_group_role(request, role, group, domain=None, project=None):
|
||||
""" Removes a given single role for a group from a domain or project. """
|
||||
"""Removes a given single role for a group from a domain or project."""
|
||||
manager = keystoneclient(request, admin=True).roles
|
||||
return manager.revoke(role=role, group=group, project=project,
|
||||
domain=domain)
|
||||
|
||||
|
||||
def remove_group_roles(request, group, domain=None, project=None):
|
||||
""" Removes all roles from a group on a domain or project,
|
||||
removing them from it.
|
||||
"""Removes all roles from a group on a domain or project,
|
||||
removing them from it.
|
||||
"""
|
||||
client = keystoneclient(request, admin=True)
|
||||
roles = client.roles.list(group=group, domain=domain, project=project)
|
||||
@ -556,8 +555,7 @@ def remove_group_roles(request, group, domain=None, project=None):
|
||||
|
||||
|
||||
def get_default_role(request):
|
||||
"""
|
||||
Gets the default role object from Keystone and saves it as a global
|
||||
"""Gets the default role object from Keystone and saves it as a global
|
||||
since this is configured in settings and should not change from request
|
||||
to request. Supports lookup by name or id.
|
||||
"""
|
||||
|
@ -22,14 +22,14 @@ neutronclient = neutron.neutronclient
|
||||
|
||||
|
||||
class Vip(neutron.NeutronAPIDictWrapper):
|
||||
"""Wrapper for neutron load balancer vip"""
|
||||
"""Wrapper for neutron load balancer vip."""
|
||||
|
||||
def __init__(self, apiresource):
|
||||
super(Vip, self).__init__(apiresource)
|
||||
|
||||
|
||||
class Pool(neutron.NeutronAPIDictWrapper):
|
||||
"""Wrapper for neutron load balancer pool"""
|
||||
"""Wrapper for neutron load balancer pool."""
|
||||
|
||||
def __init__(self, apiresource):
|
||||
if 'provider' not in apiresource:
|
||||
@ -75,7 +75,7 @@ class Pool(neutron.NeutronAPIDictWrapper):
|
||||
|
||||
|
||||
class Member(neutron.NeutronAPIDictWrapper):
|
||||
"""Wrapper for neutron load balancer member"""
|
||||
"""Wrapper for neutron load balancer member."""
|
||||
|
||||
def __init__(self, apiresource):
|
||||
super(Member, self).__init__(apiresource)
|
||||
@ -104,14 +104,14 @@ class Member(neutron.NeutronAPIDictWrapper):
|
||||
|
||||
|
||||
class PoolStats(neutron.NeutronAPIDictWrapper):
|
||||
"""Wrapper for neutron load balancer pool stats"""
|
||||
"""Wrapper for neutron load balancer pool stats."""
|
||||
|
||||
def __init__(self, apiresource):
|
||||
super(PoolStats, self).__init__(apiresource)
|
||||
|
||||
|
||||
class PoolMonitor(neutron.NeutronAPIDictWrapper):
|
||||
"""Wrapper for neutron load balancer pool health monitor"""
|
||||
"""Wrapper for neutron load balancer pool health monitor."""
|
||||
|
||||
def __init__(self, apiresource):
|
||||
super(PoolMonitor, self).__init__(apiresource)
|
||||
|
@ -62,7 +62,7 @@ class NeutronAPIDictWrapper(base.APIDictWrapper):
|
||||
|
||||
|
||||
class Agent(NeutronAPIDictWrapper):
|
||||
"""Wrapper for neutron agents"""
|
||||
"""Wrapper for neutron agents."""
|
||||
|
||||
def __init__(self, apiresource):
|
||||
apiresource['admin_state'] = \
|
||||
@ -71,7 +71,7 @@ class Agent(NeutronAPIDictWrapper):
|
||||
|
||||
|
||||
class Network(NeutronAPIDictWrapper):
|
||||
"""Wrapper for neutron Networks"""
|
||||
"""Wrapper for neutron Networks."""
|
||||
|
||||
def __init__(self, apiresource):
|
||||
apiresource['admin_state'] = \
|
||||
@ -84,7 +84,7 @@ class Network(NeutronAPIDictWrapper):
|
||||
|
||||
|
||||
class Subnet(NeutronAPIDictWrapper):
|
||||
"""Wrapper for neutron subnets"""
|
||||
"""Wrapper for neutron subnets."""
|
||||
|
||||
def __init__(self, apiresource):
|
||||
apiresource['ipver_str'] = get_ipver_str(apiresource['ip_version'])
|
||||
@ -92,7 +92,7 @@ class Subnet(NeutronAPIDictWrapper):
|
||||
|
||||
|
||||
class Port(NeutronAPIDictWrapper):
|
||||
"""Wrapper for neutron ports"""
|
||||
"""Wrapper for neutron ports."""
|
||||
|
||||
def __init__(self, apiresource):
|
||||
apiresource['admin_state'] = \
|
||||
@ -110,7 +110,7 @@ class Profile(NeutronAPIDictWrapper):
|
||||
|
||||
|
||||
class Router(NeutronAPIDictWrapper):
|
||||
"""Wrapper for neutron routers"""
|
||||
"""Wrapper for neutron routers."""
|
||||
|
||||
def __init__(self, apiresource):
|
||||
#apiresource['admin_state'] = \
|
||||
@ -412,7 +412,7 @@ class FloatingIpManager(network_base.FloatingIpManager):
|
||||
|
||||
|
||||
def get_ipver_str(ip_version):
|
||||
"""Convert an ip version number to a human-friendly string"""
|
||||
"""Convert an ip version number to a human-friendly string."""
|
||||
return IP_VERSION_DICT.get(ip_version, '')
|
||||
|
||||
|
||||
@ -476,8 +476,7 @@ def network_get(request, network_id, expand_subnet=True, **params):
|
||||
|
||||
|
||||
def network_create(request, **kwargs):
|
||||
"""
|
||||
Create a subnet on a specified network.
|
||||
"""Create a subnet on a specified network.
|
||||
:param request: request context
|
||||
:param tenant_id: (optional) tenant id of the network created
|
||||
:param name: (optional) name of the network created
|
||||
@ -519,8 +518,7 @@ def subnet_get(request, subnet_id, **params):
|
||||
|
||||
|
||||
def subnet_create(request, network_id, cidr, ip_version, **kwargs):
|
||||
"""
|
||||
Create a subnet on a specified network.
|
||||
"""Create a subnet on a specified network.
|
||||
:param request: request context
|
||||
:param network_id: network id a subnet is created on
|
||||
:param cidr: subnet IP address range
|
||||
@ -567,8 +565,7 @@ def port_get(request, port_id, **params):
|
||||
|
||||
|
||||
def port_create(request, network_id, **kwargs):
|
||||
"""
|
||||
Create a port on a specified network.
|
||||
"""Create a port on a specified network.
|
||||
:param request: request context
|
||||
:param network_id: network id a subnet is created on
|
||||
:param device_id: (optional) device id attached to the port
|
||||
|
@ -166,7 +166,7 @@ class SecurityGroup(base.APIResourceWrapper):
|
||||
|
||||
|
||||
class SecurityGroupRule(base.APIResourceWrapper):
|
||||
""" Wrapper for individual rules in a SecurityGroup. """
|
||||
"""Wrapper for individual rules in a SecurityGroup."""
|
||||
_attrs = ['id', 'ip_protocol', 'from_port', 'to_port', 'ip_range', 'group']
|
||||
|
||||
def __unicode__(self):
|
||||
@ -686,8 +686,7 @@ def list_extensions(request):
|
||||
|
||||
@memoized
|
||||
def extension_supported(extension_name, request):
|
||||
"""
|
||||
this method will determine if nova supports a given extension name.
|
||||
"""this method will determine if nova supports a given extension name.
|
||||
example values for the extension_name include AdminActions, ConsoleOutput,
|
||||
etc.
|
||||
"""
|
||||
|
@ -75,7 +75,7 @@ class PseudoFolder(base.APIDictWrapper):
|
||||
|
||||
|
||||
def _objectify(items, container_name):
|
||||
""" Splits a listing of objects into their appropriate wrapper classes. """
|
||||
"""Splits a listing of objects into their appropriate wrapper classes."""
|
||||
objects = []
|
||||
|
||||
# Deal with objects and object pseudo-folders first, save subdirs for later
|
||||
|
@ -30,7 +30,7 @@ except ImportError:
|
||||
|
||||
|
||||
class TokenAuth(object):
|
||||
"""Simple Token Authentication handler for trove api"""
|
||||
"""Simple Token Authentication handler for trove api."""
|
||||
|
||||
def __init__(self, client, auth_strategy, auth_url, username, password,
|
||||
tenant, region, service_type, service_name, service_url):
|
||||
|
@ -25,7 +25,7 @@ from django.conf import settings # noqa
|
||||
|
||||
|
||||
def openstack(request):
|
||||
""" Context processor necessary for OpenStack Dashboard functionality.
|
||||
"""Context processor necessary for OpenStack Dashboard functionality.
|
||||
|
||||
The following variables are added to the request context:
|
||||
|
||||
|
@ -98,7 +98,7 @@ class DomainFilterAction(tables.FilterAction):
|
||||
return multidomain_support
|
||||
|
||||
def filter(self, table, domains, filter_string):
|
||||
""" Naive case-insensitive search """
|
||||
"""Naive case-insensitive search."""
|
||||
q = filter_string.lower()
|
||||
|
||||
def comp(domain):
|
||||
|
@ -72,7 +72,7 @@ class ModifyAccess(tables.LinkAction):
|
||||
|
||||
class FlavorFilterAction(tables.FilterAction):
|
||||
def filter(self, table, flavors, filter_string):
|
||||
""" Really naive case-insensitive search. """
|
||||
"""Really naive case-insensitive search."""
|
||||
q = filter_string.lower()
|
||||
|
||||
def comp(flavor):
|
||||
|
@ -85,7 +85,7 @@ class ManageUsersLink(tables.LinkAction):
|
||||
|
||||
class GroupFilterAction(tables.FilterAction):
|
||||
def filter(self, table, groups, filter_string):
|
||||
""" Naive case-insensitive search """
|
||||
"""Naive case-insensitive search."""
|
||||
q = filter_string.lower()
|
||||
|
||||
def comp(group):
|
||||
@ -112,7 +112,7 @@ class GroupsTable(tables.DataTable):
|
||||
|
||||
class UserFilterAction(tables.FilterAction):
|
||||
def filter(self, table, users, filter_string):
|
||||
""" Naive case-insensitive search """
|
||||
"""Naive case-insensitive search."""
|
||||
q = filter_string.lower()
|
||||
return [user for user in users
|
||||
if q in user.name.lower()
|
||||
|
@ -68,5 +68,5 @@ class UpdateView(views.UpdateView):
|
||||
|
||||
|
||||
class DetailView(views.DetailView):
|
||||
""" Admin placeholder for image detail view. """
|
||||
"""Admin placeholder for image detail view."""
|
||||
pass
|
||||
|
@ -60,7 +60,7 @@ class AdminUpdateRow(project_tables.UpdateRow):
|
||||
|
||||
class AdminInstanceFilterAction(tables.FilterAction):
|
||||
def filter(self, table, instances, filter_string):
|
||||
""" Naive case-insensitive search. """
|
||||
"""Naive case-insensitive search."""
|
||||
q = filter_string.lower()
|
||||
return [instance for instance in instances
|
||||
if q in instance.name.lower()]
|
||||
|
@ -84,7 +84,7 @@ class GlobalDiskUsageTab(tabs.TableTab):
|
||||
preload = False
|
||||
|
||||
def get_global_disk_usage_data(self):
|
||||
""" Disk usage table data aggregated by project """
|
||||
"""Disk usage table data aggregated by project."""
|
||||
request = self.tab_group.request
|
||||
return list_of_resource_aggregates(request,
|
||||
ceilometer.GlobalDiskUsage.meters)
|
||||
@ -214,7 +214,7 @@ class GlobalStatsTab(tabs.Tab):
|
||||
meter_titles[name] = hint
|
||||
|
||||
class MetersWrap(object):
|
||||
""" A quick wrapper for meter and associated titles. """
|
||||
"""A quick wrapper for meter and associated titles."""
|
||||
def __init__(self, meter, meter_titles):
|
||||
self.name = meter
|
||||
self.title = meter_titles.get(meter, "")
|
||||
|
@ -174,7 +174,7 @@ class SamplesView(TemplateView):
|
||||
query = []
|
||||
|
||||
def filter_by_meter_name(resource):
|
||||
""" Function for filtering of the list of resources.
|
||||
"""Function for filtering of the list of resources.
|
||||
|
||||
Will pick the right resources according to currently selected
|
||||
meter.
|
||||
|
@ -109,7 +109,7 @@ class DeleteTenantsAction(tables.DeleteAction):
|
||||
|
||||
class TenantFilterAction(tables.FilterAction):
|
||||
def filter(self, table, tenants, filter_string):
|
||||
""" Really naive case-insensitive search. """
|
||||
"""Really naive case-insensitive search."""
|
||||
# FIXME(gabriel): This should be smarter. Written for demo purposes.
|
||||
q = filter_string.lower()
|
||||
|
||||
|
@ -57,7 +57,7 @@ class DeleteRolesAction(tables.DeleteAction):
|
||||
|
||||
class RoleFilterAction(tables.FilterAction):
|
||||
def filter(self, table, roles, filter_string):
|
||||
""" Naive case-insensitive search """
|
||||
"""Naive case-insensitive search."""
|
||||
q = filter_string.lower()
|
||||
return [role for role in roles
|
||||
if q in role.name.lower()]
|
||||
|
@ -115,7 +115,7 @@ class DeleteUsersAction(tables.DeleteAction):
|
||||
|
||||
class UserFilterAction(tables.FilterAction):
|
||||
def filter(self, table, users, filter_string):
|
||||
""" Naive case-insensitive search """
|
||||
"""Naive case-insensitive search."""
|
||||
q = filter_string.lower()
|
||||
return [user for user in users
|
||||
if q in user.name.lower()
|
||||
|
@ -36,7 +36,7 @@ class DeleteVolumeType(tables.DeleteAction):
|
||||
class VolumesFilterAction(tables.FilterAction):
|
||||
|
||||
def filter(self, table, volumes, filter_string):
|
||||
""" Naive case-insensitive search. """
|
||||
"""Naive case-insensitive search."""
|
||||
q = filter_string.lower()
|
||||
return [volume for volume in volumes
|
||||
if q in volume.display_name.lower()]
|
||||
|
@ -47,8 +47,7 @@ class DeleteContainer(tables.DeleteAction):
|
||||
api.swift.swift_delete_container(request, obj_id)
|
||||
|
||||
def get_success_url(self, request=None):
|
||||
"""
|
||||
Returns the URL to redirect to after a successful action.
|
||||
"""Returns the URL to redirect to after a successful action.
|
||||
"""
|
||||
current_container = self.table.kwargs.get("container_name", None)
|
||||
|
||||
|
@ -61,7 +61,7 @@ class ContainerView(browsers.ResourceBrowserView):
|
||||
|
||||
@cached_property
|
||||
def objects(self):
|
||||
""" Returns a list of objects given the subfolder's path.
|
||||
"""Returns a list of objects given the subfolder's path.
|
||||
|
||||
The path is from the kwargs of the request.
|
||||
"""
|
||||
@ -93,13 +93,13 @@ class ContainerView(browsers.ResourceBrowserView):
|
||||
return getattr(item, "content_type", None) == content_type
|
||||
|
||||
def get_objects_data(self):
|
||||
""" Returns a list of objects within the current folder. """
|
||||
"""Returns a list of objects within the current folder."""
|
||||
filtered_objects = [item for item in self.objects
|
||||
if not self.is_subdir(item)]
|
||||
return filtered_objects
|
||||
|
||||
def get_subfolders_data(self):
|
||||
""" Returns a list of subfolders within the current folder. """
|
||||
"""Returns a list of subfolders within the current folder."""
|
||||
filtered_objects = [item for item in self.objects
|
||||
if self.is_subdir(item)]
|
||||
return filtered_objects
|
||||
|
@ -65,8 +65,7 @@ class SetInstanceDetails(workflows.Step):
|
||||
|
||||
|
||||
class AddDatabasesAction(workflows.Action):
|
||||
"""
|
||||
Initialize the database with users/databases. This tab will honor
|
||||
"""Initialize the database with users/databases. This tab will honor
|
||||
the settings which should be a list of permissions required:
|
||||
|
||||
* TROVE_ADD_USER_PERMS = []
|
||||
|
@ -44,8 +44,7 @@ IMAGES_INDEX_URL = reverse('horizon:project:images_and_snapshots:index')
|
||||
|
||||
class CreateImageFormTests(test.TestCase):
|
||||
def test_no_location_or_file(self):
|
||||
"""
|
||||
The form will not be valid if both copy_from and image_file are not
|
||||
"""The form will not be valid if both copy_from and image_file are not
|
||||
provided.
|
||||
"""
|
||||
post = {
|
||||
@ -62,8 +61,7 @@ class CreateImageFormTests(test.TestCase):
|
||||
|
||||
@override_settings(HORIZON_IMAGES_ALLOW_UPLOAD=False)
|
||||
def test_image_upload_disabled(self):
|
||||
"""
|
||||
If HORIZON_IMAGES_ALLOW_UPLOAD is false, the image_file field widget
|
||||
"""If HORIZON_IMAGES_ALLOW_UPLOAD is false, the image_file field widget
|
||||
will be a HiddenInput widget instead of a FileInput widget.
|
||||
"""
|
||||
form = forms.CreateImageForm({})
|
||||
|
@ -18,8 +18,7 @@ from openstack_dashboard.api import glance
|
||||
|
||||
|
||||
def get_available_images(request, project_id=None, images_cache=None):
|
||||
"""
|
||||
Returns a list of images that are public or owned by the given
|
||||
"""Returns a list of images that are public or owned by the given
|
||||
project_id. If project_id is not specified, only public images
|
||||
are returned.
|
||||
|
||||
|
@ -548,7 +548,7 @@ TASK_DISPLAY_CHOICES = (
|
||||
class InstancesFilterAction(tables.FilterAction):
|
||||
|
||||
def filter(self, table, instances, filter_string):
|
||||
""" Naive case-insensitive search. """
|
||||
"""Naive case-insensitive search."""
|
||||
q = filter_string.lower()
|
||||
return [instance for instance in instances
|
||||
if q in instance.name.lower()]
|
||||
|
@ -217,7 +217,8 @@ class SetInstanceDetailsAction(workflows.Action):
|
||||
"""By default, returns the available flavors, sorted by RAM
|
||||
usage (ascending).
|
||||
Override these behaviours with a CREATE_INSTANCE_FLAVOR_SORT dict
|
||||
in local_settings.py."""
|
||||
in local_settings.py.
|
||||
"""
|
||||
try:
|
||||
flavors = api.nova.flavor_list(request)
|
||||
flavor_sort = getattr(settings, 'CREATE_INSTANCE_FLAVOR_SORT', {})
|
||||
|
@ -368,7 +368,7 @@ class CreateNetwork(workflows.Workflow):
|
||||
return False
|
||||
|
||||
def _delete_network(self, request, network):
|
||||
"""Delete the created network when subnet creation failed"""
|
||||
"""Delete the created network when subnet creation failed."""
|
||||
try:
|
||||
api.neutron.network_delete(request, network.id)
|
||||
msg = _('Delete the created network "%s" '
|
||||
|
@ -31,8 +31,7 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RuleCIDRField(fields.IPField):
|
||||
"""
|
||||
Extends IPField to allow ('any','external') keywords and requires CIDR
|
||||
"""Extends IPField to allow ('any','external') keywords and requires CIDR
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs['mask'] = True
|
||||
|
@ -29,9 +29,7 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def exception_to_validation_msg(e):
|
||||
'''
|
||||
Extracts a validation message to display to the user.
|
||||
'''
|
||||
"""Extracts a validation message to display to the user."""
|
||||
try:
|
||||
error = json.loads(str(e))
|
||||
# NOTE(jianingy): if no message exists, we just return 'None'
|
||||
|
@ -140,9 +140,7 @@ def get_resource_status(status):
|
||||
|
||||
|
||||
def get_resource_image(status, type):
|
||||
'''
|
||||
Sets the image url and in_progress action sw based on status.
|
||||
'''
|
||||
"""Sets the image url and in_progress action sw based on status."""
|
||||
resource_type = get_resource_type(type)
|
||||
resource_status = get_resource_status(status)
|
||||
resource_state = resource_type + "_" + resource_status
|
||||
|
@ -132,8 +132,9 @@ def get_attachment_name(request, attachment):
|
||||
|
||||
|
||||
class AttachmentColumn(tables.Column):
|
||||
"""
|
||||
Customized column class that does complex processing on the attachments
|
||||
"""Customized column class.
|
||||
|
||||
So it that does complex processing on the attachments
|
||||
for a volume instance.
|
||||
"""
|
||||
def get_raw_data(self, volume):
|
||||
@ -184,7 +185,7 @@ class VolumesTableBase(tables.DataTable):
|
||||
class VolumesFilterAction(tables.FilterAction):
|
||||
|
||||
def filter(self, table, volumes, filter_string):
|
||||
""" Naive case-insensitive search. """
|
||||
"""Naive case-insensitive search."""
|
||||
q = filter_string.lower()
|
||||
return [volume for volume in volumes
|
||||
if q in volume.display_name.lower()]
|
||||
@ -230,8 +231,7 @@ class DetachVolume(tables.BatchAction):
|
||||
|
||||
|
||||
class AttachedInstanceColumn(tables.Column):
|
||||
"""
|
||||
Customized column class that does complex processing on the attachments
|
||||
"""Customized column class that does complex processing on the attachments
|
||||
for a volume instance.
|
||||
"""
|
||||
def get_raw_data(self, attachment):
|
||||
|
@ -46,7 +46,7 @@ def get_tenant_choices(request):
|
||||
|
||||
class CreateNetworkProfile(forms.SelfHandlingForm):
|
||||
|
||||
""" Create Network Profile form."""
|
||||
"""Create Network Profile form."""
|
||||
|
||||
name = forms.CharField(max_length=255,
|
||||
label=_("Name"),
|
||||
@ -115,7 +115,7 @@ class CreateNetworkProfile(forms.SelfHandlingForm):
|
||||
|
||||
class UpdateNetworkProfile(forms.SelfHandlingForm):
|
||||
|
||||
""" Update Network Profile form."""
|
||||
"""Update Network Profile form."""
|
||||
|
||||
profile_id = forms.CharField(label=_("ID"),
|
||||
widget=forms.HiddenInput())
|
||||
|
@ -60,7 +60,8 @@ def reset():
|
||||
|
||||
|
||||
def check(actions, request, target={}):
|
||||
"""
|
||||
"""Check user permission.
|
||||
|
||||
Check if the user has permission to the action according
|
||||
to policy setting.
|
||||
|
||||
|
@ -27,7 +27,7 @@ from openstack_dashboard.test import helpers as test
|
||||
|
||||
|
||||
class APIResource(api_base.APIResourceWrapper):
|
||||
""" Simple APIResource for testing """
|
||||
"""Simple APIResource for testing."""
|
||||
_attrs = ['foo', 'bar', 'baz']
|
||||
|
||||
@staticmethod
|
||||
@ -44,7 +44,7 @@ class APIResource(api_base.APIResourceWrapper):
|
||||
|
||||
|
||||
class APIDict(api_base.APIDictWrapper):
|
||||
""" Simple APIDict for testing """
|
||||
"""Simple APIDict for testing."""
|
||||
_attrs = ['foo', 'bar', 'baz']
|
||||
|
||||
@staticmethod
|
||||
@ -116,7 +116,7 @@ class APIDictWrapperTests(test.TestCase):
|
||||
|
||||
|
||||
class ApiHelperTests(test.TestCase):
|
||||
""" Tests for functions that don't use one of the api objects """
|
||||
"""Tests for functions that don't use one of the api objects."""
|
||||
|
||||
def test_url_for(self):
|
||||
url = api_base.url_for(self.request, 'image')
|
||||
|
@ -50,8 +50,7 @@ class RoleAPITests(test.APITestCase):
|
||||
self.roles = self.roles.list()
|
||||
|
||||
def test_remove_tenant_user(self):
|
||||
"""
|
||||
Tests api.keystone.remove_tenant_user
|
||||
"""Tests api.keystone.remove_tenant_user
|
||||
|
||||
Verifies that remove_tenant_user is called with the right arguments
|
||||
after iterating the user's roles.
|
||||
|
@ -108,8 +108,7 @@ class RequestFactoryWithMessages(RequestFactory):
|
||||
@unittest.skipIf(os.environ.get('SKIP_UNITTESTS', False),
|
||||
"The SKIP_UNITTESTS env variable is set.")
|
||||
class TestCase(horizon_helpers.TestCase):
|
||||
"""
|
||||
Specialized base test case class for Horizon which gives access to
|
||||
"""Specialized base test case class for Horizon which gives access to
|
||||
numerous additional features:
|
||||
|
||||
* A full suite of test data through various attached objects and
|
||||
@ -180,8 +179,7 @@ class TestCase(horizon_helpers.TestCase):
|
||||
utils.get_user = get_user
|
||||
|
||||
def assertRedirectsNoFollow(self, response, expected_url):
|
||||
"""
|
||||
Asserts that the given response issued a 302 redirect without
|
||||
"""Asserts that the given response issued a 302 redirect without
|
||||
processing the view which is redirected to.
|
||||
"""
|
||||
assert (response.status_code / 100 == 3), \
|
||||
@ -191,8 +189,7 @@ class TestCase(horizon_helpers.TestCase):
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def assertNoFormErrors(self, response, context_name="form"):
|
||||
"""
|
||||
Asserts that the response either does not contain a form in its
|
||||
"""Asserts that the response either does not contain a form in its
|
||||
context, or that if it does, that form has no errors.
|
||||
"""
|
||||
context = getattr(response, "context", {})
|
||||
@ -204,8 +201,7 @@ class TestCase(horizon_helpers.TestCase):
|
||||
|
||||
def assertFormErrors(self, response, count=0, message=None,
|
||||
context_name="form"):
|
||||
"""
|
||||
Asserts that the response does contain a form in its
|
||||
"""Asserts that the response does contain a form in its
|
||||
context, and that form has errors, if count were given,
|
||||
it must match the exact numbers of errors
|
||||
"""
|
||||
@ -226,8 +222,7 @@ class TestCase(horizon_helpers.TestCase):
|
||||
|
||||
|
||||
class BaseAdminViewTests(TestCase):
|
||||
"""
|
||||
A ``TestCase`` subclass which sets an active user with the "admin" role
|
||||
"""A ``TestCase`` subclass which sets an active user with the "admin" role
|
||||
for testing admin-only views and functionality.
|
||||
"""
|
||||
def setActiveUser(self, *args, **kwargs):
|
||||
@ -248,8 +243,7 @@ class BaseAdminViewTests(TestCase):
|
||||
|
||||
|
||||
class APITestCase(TestCase):
|
||||
"""
|
||||
The ``APITestCase`` class is for use with tests which deal with the
|
||||
"""The ``APITestCase`` class is for use with tests which deal with the
|
||||
underlying clients rather than stubbing out the
|
||||
openstack_dashboard.api.* methods.
|
||||
"""
|
||||
@ -258,8 +252,7 @@ class APITestCase(TestCase):
|
||||
utils.patch_middleware_get_user()
|
||||
|
||||
def fake_keystoneclient(request, admin=False):
|
||||
"""
|
||||
Wrapper function which returns the stub keystoneclient. Only
|
||||
"""Wrapper function which returns the stub keystoneclient. Only
|
||||
necessary because the function takes too many arguments to
|
||||
conveniently be a lambda.
|
||||
"""
|
||||
@ -414,8 +407,7 @@ class SeleniumTestCase(horizon_helpers.SeleniumTestCase):
|
||||
|
||||
|
||||
class SeleniumAdminTestCase(SeleniumTestCase):
|
||||
"""
|
||||
A ``TestCase`` subclass which sets an active user with the "admin" role
|
||||
"""A ``TestCase`` subclass which sets an active user with the "admin" role
|
||||
for testing admin-only views and functionality.
|
||||
"""
|
||||
def setActiveUser(self, *args, **kwargs):
|
||||
|
@ -53,8 +53,7 @@ def load_test_data(load_onto=None):
|
||||
|
||||
|
||||
class TestData(object):
|
||||
"""
|
||||
Holder object for test data. Any functions passed to the init method
|
||||
"""Holder object for test data. Any functions passed to the init method
|
||||
will be called with the ``TestData`` object as their only argument. They
|
||||
can then load data onto the object as desired.
|
||||
|
||||
@ -79,7 +78,7 @@ class TestData(object):
|
||||
|
||||
|
||||
class TestDataContainer(object):
|
||||
""" A container for test data objects.
|
||||
"""A container for test data objects.
|
||||
|
||||
The behavior of this class is meant to mimic a "manager" class, which
|
||||
has convenient shortcuts for common actions like "list", "filter", "get",
|
||||
@ -89,7 +88,7 @@ class TestDataContainer(object):
|
||||
self._objects = []
|
||||
|
||||
def add(self, *args):
|
||||
""" Add a new object to this container.
|
||||
"""Add a new object to this container.
|
||||
|
||||
Generally this method should only be used during data loading, since
|
||||
adding data during a test can affect the results of other tests.
|
||||
@ -99,12 +98,11 @@ class TestDataContainer(object):
|
||||
self._objects.append(obj)
|
||||
|
||||
def list(self):
|
||||
""" Returns a list of all objects in this container. """
|
||||
"""Returns a list of all objects in this container."""
|
||||
return self._objects
|
||||
|
||||
def filter(self, filtered=None, **kwargs):
|
||||
"""
|
||||
Returns objects in this container whose attributes match the given
|
||||
"""Returns objects in this container whose attributes match the given
|
||||
keyword arguments.
|
||||
"""
|
||||
if filtered is None:
|
||||
@ -121,8 +119,7 @@ class TestDataContainer(object):
|
||||
return self.filter(filtered=filter(get_match, filtered), **kwargs)
|
||||
|
||||
def get(self, **kwargs):
|
||||
"""
|
||||
Returns the single object in this container whose attributes match
|
||||
"""Returns the single object in this container whose attributes match
|
||||
the given keyword arguments. An error will be raised if the arguments
|
||||
provided don't return exactly one match.
|
||||
"""
|
||||
@ -135,7 +132,7 @@ class TestDataContainer(object):
|
||||
return matches.pop()
|
||||
|
||||
def first(self):
|
||||
""" Returns the first object from this container. """
|
||||
"""Returns the first object from this container."""
|
||||
return self._objects[0]
|
||||
|
||||
def count(self):
|
||||
|
@ -23,7 +23,7 @@ from openstack_dashboard.test import helpers as test
|
||||
|
||||
|
||||
class ErrorPageTests(test.TestCase):
|
||||
""" Tests for error pages """
|
||||
"""Tests for error pages."""
|
||||
urls = 'openstack_dashboard.test.error_pages_urls'
|
||||
|
||||
def test_500_error(self):
|
||||
|
@ -25,7 +25,7 @@ class FakeUser(object):
|
||||
|
||||
|
||||
class TemplateRenderTest(test.TestCase):
|
||||
""" Tests for templates render """
|
||||
"""Tests for templates render."""
|
||||
|
||||
def test_openrc_html_escape(self):
|
||||
context = {
|
||||
|
@ -275,8 +275,7 @@ class ProjectUsage(BaseUsage):
|
||||
|
||||
class CsvDataMixin(object):
|
||||
|
||||
"""
|
||||
CSV data Mixin - provides handling for CSV data
|
||||
"""CSV data Mixin - provides handling for CSV data.
|
||||
|
||||
.. attribute:: columns
|
||||
|
||||
@ -318,10 +317,7 @@ class CsvDataMixin(object):
|
||||
|
||||
class BaseCsvResponse(CsvDataMixin, HttpResponse):
|
||||
|
||||
"""
|
||||
Base CSV response class. Provides handling of CSV data.
|
||||
|
||||
"""
|
||||
"""Base CSV response class. Provides handling of CSV data."""
|
||||
|
||||
def __init__(self, request, template, context, content_type, **kwargs):
|
||||
super(BaseCsvResponse, self).__init__()
|
||||
@ -358,8 +354,7 @@ if VERSION >= (1, 5, 0):
|
||||
|
||||
class BaseCsvStreamingResponse(CsvDataMixin, StreamingHttpResponse):
|
||||
|
||||
"""
|
||||
Base CSV Streaming class. Provides streaming response for CSV data.
|
||||
"""Base CSV Streaming class. Provides streaming response for CSV data.
|
||||
"""
|
||||
|
||||
def __init__(self, request, template, context, content_type, **kwargs):
|
||||
|
@ -60,7 +60,7 @@ QUOTA_FIELDS = NOVA_QUOTA_FIELDS + CINDER_QUOTA_FIELDS + NEUTRON_QUOTA_FIELDS
|
||||
|
||||
|
||||
class QuotaUsage(dict):
|
||||
""" Tracks quota limit, used, and available for a given set of quotas."""
|
||||
"""Tracks quota limit, used, and available for a given set of quotas."""
|
||||
|
||||
def __init__(self):
|
||||
self.usages = defaultdict(dict)
|
||||
@ -77,7 +77,7 @@ class QuotaUsage(dict):
|
||||
return repr(dict(self.usages))
|
||||
|
||||
def add_quota(self, quota):
|
||||
""" Adds an internal tracking reference for the given quota. """
|
||||
"""Adds an internal tracking reference for the given quota."""
|
||||
if quota.limit is None or quota.limit == -1:
|
||||
# Handle "unlimited" quotas.
|
||||
self.usages[quota.name]['quota'] = float("inf")
|
||||
@ -86,7 +86,7 @@ class QuotaUsage(dict):
|
||||
self.usages[quota.name]['quota'] = int(quota.limit)
|
||||
|
||||
def tally(self, name, value):
|
||||
""" Adds to the "used" metric for the given quota. """
|
||||
"""Adds to the "used" metric for the given quota."""
|
||||
value = value or 0 # Protection against None.
|
||||
# Start at 0 if this is the first value.
|
||||
if 'used' not in self.usages[name]:
|
||||
@ -96,7 +96,7 @@ class QuotaUsage(dict):
|
||||
self.update_available(name)
|
||||
|
||||
def update_available(self, name):
|
||||
""" Updates the "available" metric for the given quota. """
|
||||
"""Updates the "available" metric for the given quota."""
|
||||
available = self.usages[name]['quota'] - self.usages[name]['used']
|
||||
if available < 0:
|
||||
available = 0
|
||||
|
3
tox.ini
3
tox.ini
@ -34,8 +34,7 @@ builtins = _
|
||||
exclude = .venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,panel_template,dash_template,local_settings.py
|
||||
# E127 continuation line over-indented for visual indent
|
||||
# E128 continuation line under-indented for visual indent
|
||||
# H4xx docstrings
|
||||
# H701 empty localization string
|
||||
# H702 Formatting operation should be outside of localization method call
|
||||
# H803 git commit title should not end with period (disabled on purpose, see bug #1236621)
|
||||
ignore = E127,E128,H4,H701,H702,H803
|
||||
ignore = E127,E128,H701,H702,H803
|
||||
|
Loading…
x
Reference in New Issue
Block a user