diff --git a/horizon/base.py b/horizon/base.py index 0a4ec13efd..187186fc24 100644 --- a/horizon/base.py +++ b/horizon/base.py @@ -471,14 +471,13 @@ class Dashboard(Registry, HorizonComponent): self._panel_groups = None def get_panel(self, panel): - """Returns the specified :class:`~horizon.Panel` instance registered - with this dashboard. - """ + """Returns the Panel instance registered with this dashboard.""" return self._registered(panel) def get_panels(self): - """Returns the :class:`~horizon.Panel` instances registered with this - dashboard in order, without any panel groupings. + """Returns the Panel instances registered with this dashboard in order. + + Panel grouping information is not included. """ all_panels = [] panel_groups = self.get_panel_groups() @@ -487,8 +486,9 @@ class Dashboard(Registry, HorizonComponent): return all_panels def get_panel_group(self, slug): - """Returns the specified :class:~horizon.PanelGroup - or None if not registered + """Returns the specified :class:~horizon.PanelGroup. + + Returns None if not registered. """ return self._panel_groups.get(slug) @@ -1006,8 +1006,10 @@ class Site(Registry, HorizonComponent): class HorizonSite(Site): - """A singleton implementation of Site such that all dealings with horizon - get the same instance no matter what. There can be only one. + """A singleton implementation of Site. + + All dealings with horizon get the same instance no matter what. + There can be only one. """ _instance = None diff --git a/horizon/browsers/base.py b/horizon/browsers/base.py index 2aea8fb8b1..5a28c6797f 100644 --- a/horizon/browsers/base.py +++ b/horizon/browsers/base.py @@ -119,8 +119,11 @@ class ResourceBrowser(html.HTMLElement): % (attr_name, self.__class__.__name__)) def set_tables(self, tables): - """Sets the table instances on the browser from a dictionary mapping - table names to table instances (as constructed by MultiTableView). + """Sets the table instances on the browser. + + ``tables`` argument specifies tables to be set. + It is a dictionary mapping table names to table instances + (as constructed by MultiTableView). """ self.navigation_table = tables[self.navigation_table_class._meta.name] self.content_table = tables[self.content_table_class._meta.name] diff --git a/horizon/contrib/staticfiles/finders.py b/horizon/contrib/staticfiles/finders.py index 6ea9dc39d6..0b147b71bc 100644 --- a/horizon/contrib/staticfiles/finders.py +++ b/horizon/contrib/staticfiles/finders.py @@ -19,8 +19,7 @@ from django.contrib.staticfiles.finders import AppDirectoriesFinder class HorizonStaticFinder(AppDirectoriesFinder): - """A static files finder that also looks into the directory of each panel. - """ + """Static files finder that also looks into the directory of each panel.""" def __init__(self, app_names=None, *args, **kwargs): super(HorizonStaticFinder, self).__init__(*args, **kwargs) diff --git a/horizon/exceptions.py b/horizon/exceptions.py index 3d2ad78696..a8eb154d1c 100644 --- a/horizon/exceptions.py +++ b/horizon/exceptions.py @@ -45,8 +45,10 @@ class HorizonException(Exception): class Http302(HorizonException): - """Error class which can be raised from within a handler to cause an - early bailout and redirect at the middleware level. + """Exception used to redirect at the middleware level. + + This error class which can be raised from within a handler to cause + an early bailout and redirect at the middleware level. """ status_code = 302 @@ -56,7 +58,9 @@ class Http302(HorizonException): class NotAuthorized(HorizonException): - """Raised whenever a user attempts to access a resource which they do not + """User tries to access a resource without sufficient permissions. + + Raised whenever a user attempts to access a resource which they do not have permission-based access to (such as when failing the :func:`~horizon.decorators.require_perms` decorator). @@ -68,8 +72,7 @@ class NotAuthorized(HorizonException): class NotAuthenticated(HorizonException): - """Raised when a user is trying to make requests and they are not logged - in. + """Raised when a user is trying to make requests and they are not logged in. The included :class:`~horizon.middleware.HorizonMiddleware` catches ``NotAuthenticated`` and handles it gracefully by displaying an error @@ -99,8 +102,9 @@ class RecoverableError(HorizonException): class ServiceCatalogException(HorizonException): - """Raised when a requested service is not available in the - ``ServiceCatalog`` returned by Keystone. + """A requested service is not available in the ``ServiceCatalog``. + + ``ServiceCatalog`` is fetched from Keystone. """ def __init__(self, service_name): message = 'Invalid service catalog service: %s' % service_name @@ -109,9 +113,7 @@ class ServiceCatalogException(HorizonException): @six.python_2_unicode_compatible class AlreadyExists(HorizonException): - """Exception to be raised when trying to create an API resource which - already exists. - """ + """API resources tried to create already exists.""" def __init__(self, name, resource_type): self.attrs = {"name": name, "resource": resource_type} self.msg = _('A %(resource)s with the name "%(name)s" already exists.') @@ -125,8 +127,10 @@ class AlreadyExists(HorizonException): @six.python_2_unicode_compatible class GetFileError(HorizonException): - """Exception to be raised when the value of get_file did not start with - https:// or http:// + """Exception to be raised when the value of get_file is not expected. + + The expected values start with https:// or http://. + Otherwise this exception will be raised. """ def __init__(self, name, resource_type): self.attrs = {"name": name, "resource": resource_type} @@ -159,7 +163,9 @@ class WorkflowError(HorizonException): class WorkflowValidationError(HorizonException): - """Exception raised during workflow validation if required data is missing, + """Exception raised during workflow validation. + + It is raised if required data is missing, or existing data is not valid. """ pass @@ -171,7 +177,9 @@ class MessageFailure(HorizonException): class HandledException(HorizonException): - """Used internally to track exceptions that have gone through + """Used internally to track exceptions that are already handled. + + It is used to track exceptions that have gone through :func:`horizon.exceptions.handle` more than once. """ def __init__(self, wrapped): @@ -192,8 +200,10 @@ def error_color(msg): def check_message(keywords, message): - """Checks an exception for given keywords and raises a new ``ActionError`` - with the desired message if the keywords are found. This allows selective + """Checks an exception for given keywords and raises an error if found. + + It raises a new ``ActionError`` with the desired message if the + keywords are found. This allows selective control over API error messages. """ exc_type, exc_value, exc_traceback = sys.exc_info() diff --git a/horizon/forms/base.py b/horizon/forms/base.py index 8704142db0..bd78ad3f9f 100644 --- a/horizon/forms/base.py +++ b/horizon/forms/base.py @@ -30,16 +30,15 @@ class SelfHandlingMixin(object): class SelfHandlingForm(SelfHandlingMixin, forms.Form): - """A base :class:`Form ` class which includes - processing logic in its subclasses. - """ + """A base Form class which includes processing logic in its subclasses.""" required_css_class = 'required' def api_error(self, message): - """Adds an error to the form's error dictionary after validation - based on problems reported via the API. This is useful when you - wish for API errors to appear as errors on the form rather than - using the messages framework. + """Adds an error to the form's error dictionary. + + It can be used after validation based on problems reported via the API. + This is useful when you wish for API errors to appear as errors on the + form rather than using the messages framework. """ self.add_error(NON_FIELD_ERRORS, message) diff --git a/horizon/forms/fields.py b/horizon/forms/fields.py index 7192f5a7f0..dcf351e0d5 100644 --- a/horizon/forms/fields.py +++ b/horizon/forms/fields.py @@ -40,6 +40,7 @@ IPv6 = 2 class IPField(fields.Field): """Form field for entering IP/range values, with validation. + Supports IPv4/IPv6 in the format: .. xxx.xxx.xxx.xxx .. xxx.xxx.xxx.xxx/zz @@ -157,9 +158,10 @@ class MACAddressField(fields.Field): class SelectWidget(widgets.Select): - """Customizable select widget, that allows to render - data-xxx attributes from choices. This widget also - allows user to specify additional html attributes + """Customizable select widget. + + It allows to render data-xxx attributes from choices. + This widget also allows user to specify additional html attributes for choices. .. attribute:: data_attrs @@ -301,7 +303,9 @@ class ThemableSelectWidget(SelectWidget): class DynamicSelectWidget(SelectWidget): - """A subclass of the ``Select`` widget which renders extra attributes for + """``Select`` widget to handle dynamic changes to the available choices. + + A subclass of the ``Select`` widget which renders extra attributes for use in callbacks to handle dynamic changes to the available choices. """ _data_add_url_attr = "data-add-item-url" @@ -335,8 +339,7 @@ class ThemableChoiceField(fields.ChoiceField): class DynamicChoiceField(fields.ChoiceField): - """A subclass of ``ChoiceField`` with additional properties that make - dynamically updating its elements easier. + """ChoiceField that make dynamically updating its elements easier. Notably, the field declaration takes an extra argument, ``add_item_link`` which may be a string or callable defining the URL that should be used @@ -370,8 +373,9 @@ class ThemableDynamicTypedChoiceField(ThemableDynamicChoiceField, class ThemableCheckboxInput(widgets.CheckboxInput): - """A subclass of the ``Checkbox`` widget which renders extra markup to - allow a custom checkbox experience. + """Checkbox widget which renders extra markup. + + It is used to allow a custom checkbox experience. """ def render(self, name, value, attrs=None): label_for = attrs.get('id', '') @@ -411,10 +415,12 @@ class ThemableCheckboxSelectMultiple(widgets.CheckboxSelectMultiple): class ExternalFileField(fields.FileField): - """A special flavor of FileField which is meant to be used in cases when - instead of uploading file to Django it should be uploaded to some external - location, while the form validation is done as usual. Should be paired - with ExternalUploadMeta metaclass embedded into the Form class. + """Special FileField to upload file to some external location. + + This is a special flavor of FileField which is meant to be used in cases + when instead of uploading file to Django it should be uploaded to some + external location, while the form validation is done as usual. It should be + paired with ExternalUploadMeta metaclass embedded into the Form class. """ def __init__(self, *args, **kwargs): super(ExternalFileField, self).__init__(*args, **kwargs) @@ -422,9 +428,11 @@ class ExternalFileField(fields.FileField): class ExternalUploadMeta(forms.DeclarativeFieldsMetaclass): - """Set this class as the metaclass of a form that contains - ExternalFileField in order to process ExternalFileField fields in a - specific way. A hidden CharField twin of FieldField is created which + """Metaclass to process ExternalFileField fields in a specific way. + + Set this class as the metaclass of a form that contains ExternalFileField + in order to process ExternalFileField fields in a specific way. + A hidden CharField twin of FieldField is created which contains just the filename (if any file was selected on browser side) and a special `clean` method for FileField is defined which extracts just file name. This allows to avoid actual file upload to Django server, yet diff --git a/horizon/forms/views.py b/horizon/forms/views.py index 0f7316cdc4..ebb05291fd 100644 --- a/horizon/forms/views.py +++ b/horizon/forms/views.py @@ -27,7 +27,9 @@ ADD_TO_FIELD_HEADER = "HTTP_X_HORIZON_ADD_TO_FIELD" class ModalBackdropMixin(object): - """This mixin class is to be used for together with ModalFormView and + """Mixin class to allow ModalFormView and WorkflowView together. + + This mixin class is to be used for together with ModalFormView and WorkflowView classes to augment them with modal_backdrop context data. .. attribute: modal_backdrop (optional) @@ -78,8 +80,10 @@ class ModalFormMixin(ModalBackdropMixin): class ModalFormView(ModalFormMixin, views.HorizonFormView): - """The main view class from which all views which handle forms in Horizon - should inherit. It takes care of all details with processing + """The main view class for all views which handle forms in Horizon. + + All view which handles forms in Horiozn should inherit this class. + It takes care of all details with processing :class:`~horizon.forms.base.SelfHandlingForm` classes, and modal concerns when the associated template inherits from `horizon/common/_modal_form.html`. @@ -148,16 +152,20 @@ class ModalFormView(ModalFormMixin, views.HorizonFormView): return self.cancel_url or self.success_url def get_object_id(self, obj): - """For dynamic insertion of resources created in modals, this method - returns the id of the created object. Defaults to returning the ``id`` - attribute. + """Returns the ID of the created object. + + For dynamic insertion of resources created in modals, + this method returns the id of the created object. + Defaults to returning the ``id`` attribute. """ return obj.id def get_object_display(self, obj): - """For dynamic insertion of resources created in modals, this method - returns the display name of the created object. Defaults to returning - the ``name`` attribute. + """Returns the display name of the created object. + + For dynamic insertion of resources created in modals, + this method returns the display name of the created object. + Defaults to returning the ``name`` attribute. """ return obj.name diff --git a/horizon/middleware/base.py b/horizon/middleware/base.py index 7ae8f9abc3..8f28fe89c1 100644 --- a/horizon/middleware/base.py +++ b/horizon/middleware/base.py @@ -111,8 +111,10 @@ class HorizonMiddleware(object): timezone.activate(tz) def process_exception(self, request, exception): - """Catches internal Horizon exception classes such as NotAuthorized, - NotFound and Http302 and handles them gracefully. + """Catches internal Horizon exception classes. + + Exception classes such as NotAuthorized, NotFound and Http302 + are caught and handles them gracefully. """ if isinstance(exception, (exceptions.NotAuthorized, exceptions.NotAuthenticated)): @@ -155,8 +157,9 @@ class HorizonMiddleware(object): dst[header] = src[header] def process_response(self, request, response): - """Convert HttpResponseRedirect to HttpResponse if request is via ajax - to allow ajax request to redirect url + """Convert HttpResponseRedirect to HttpResponse if request is via ajax. + + This is to allow ajax request to redirect url. """ if request.is_ajax() and hasattr(request, 'horizon'): queued_msgs = request.horizon['async_messages'] diff --git a/horizon/tables/actions.py b/horizon/tables/actions.py index 4dde2115c0..d47463057b 100644 --- a/horizon/tables/actions.py +++ b/horizon/tables/actions.py @@ -42,8 +42,8 @@ STRING_SEPARATOR = "__" class BaseActionMetaClass(type): - """Metaclass for adding all actions options from inheritance tree - to action. + """Metaclass for adding all actions options from inheritance tree to action. + This way actions can inherit from each other but still use the class attributes DSL. Meaning, all attributes of Actions are defined as class attributes, but in the background, it will be used as @@ -99,6 +99,7 @@ class BaseAction(html.HTMLElement): def data_type_matched(self, datum): """Method to see if the action is allowed for a certain type of data. + Only affects mixed data type tables. """ if datum: @@ -148,13 +149,15 @@ class BaseAction(html.HTMLElement): pass def get_default_classes(self): - """Returns a list of the default classes for the action. Defaults to - ``["btn", "btn-default", "btn-sm"]``. + """Returns a list of the default classes for the action. + + Defaults to ``["btn", "btn-default", "btn-sm"]``. """ return getattr(settings, "ACTION_CSS_CLASSES", ACTION_CSS_CLASSES) def get_default_attrs(self): """Returns a list of the default HTML attributes for the action. + Defaults to returning an ``id`` attribute with the value ``{{ table.name }}__action_{{ action.name }}__{{ creation counter }}``. """ @@ -527,9 +530,7 @@ class FilterAction(BaseAction): return data def is_api_filter(self, filter_field): - """Determine if the given filter field should be used as an - API filter. - """ + """Determine if agiven filter field should be used as an API filter.""" if self.filter_type == 'server': for choice in self.filter_choices: if (choice[0] == filter_field and len(choice) > 2 and @@ -538,8 +539,9 @@ class FilterAction(BaseAction): return False def get_select_options(self): - """Provide the value, string, and help_text (if applicable) - for the template to render. + """Provide the value, string, and help_text for the template to render. + + help_text is returned if applicable. """ if self.filter_choices: return [choice[:4] for choice in self.filter_choices @@ -579,8 +581,7 @@ class FixedFilterAction(FilterAction): return self.categories[filter_string] def get_fixed_buttons(self): - """Returns a list of dictionaries describing the fixed buttons - to use for filtering. + """Returns a list of dict describing fixed buttons used for filtering. Each list item should be a dict with the following keys: @@ -605,9 +606,9 @@ class FixedFilterAction(FilterAction): class BatchAction(Action): - """A table action which takes batch action on one or more - objects. This action should not require user input on a - per-object basis. + """A table action which takes batch action on one or more objects. + + This action should not require user input on a per-object basis. .. attribute:: name @@ -723,8 +724,9 @@ class BatchAction(Action): return action def action(self, request, datum_id): - """Required. Accepts a single object id and performs the specific - action. + """Accepts a single object id and performs the specific action. + + This method is required. Return values are discarded, errors raised are caught and logged. """ diff --git a/horizon/tables/base.py b/horizon/tables/base.py index 3c6ad36b66..92a4a22e14 100644 --- a/horizon/tables/base.py +++ b/horizon/tables/base.py @@ -363,8 +363,9 @@ class Column(html.HTMLElement): return '<%s: %s>' % (self.__class__.__name__, self.name) def allowed(self, request): - """Determine whether processing/displaying the column is allowed - for the current request. + """Determine whether processing/displaying the column is allowed. + + It is determined based on the current request. """ if not self.policy_rules: return True @@ -376,9 +377,10 @@ class Column(html.HTMLElement): return True def get_raw_data(self, datum): - """Returns the raw data for this column, before any filters or - formatting are applied to it. This is useful when doing calculations - on data in the table. + """Returns the raw data for this column. + + No filters or formatting are applied to the returned data. + This is useful when doing calculations on data in the table. """ # Callable transformations if callable(self.transform): @@ -397,8 +399,7 @@ class Column(html.HTMLElement): return data def get_data(self, datum): - """Returns the final display data for this column from the given - inputs. + """Returns the final display data for this column from the given inputs. The return value will be either the attribute specified for this column or the return value of the attr:`~horizon.tables.Column.transform` @@ -471,8 +472,10 @@ class Column(html.HTMLElement): return attrs def get_summation(self): - """Returns the summary value for the data in this column if a - valid summation method is specified for it. Otherwise returns ``None``. + """Returns the summary value for the data in this column. + + It returns the summary value if a valid summation method is + specified for it. Otherwise returns ``None``. """ if self.summation not in self.summation_methods: return None @@ -577,11 +580,14 @@ class Row(html.HTMLElement): self.cells = [] def load_cells(self, datum=None): - """Load the row's data (either provided at initialization or as an - argument to this function), initialize all the cells contained - by this row, and set the appropriate row properties which require + """Load the row's data and initialize all the cells in the row. + + It also set the appropriate row properties which require the row's data to be determined. + The row's data is provided either at initialization or as an + argument to this function. + This function is called automatically by :meth:`~horizon.tables.Row.__init__` if the ``datum`` argument is provided. However, by not providing the data during initialization @@ -665,14 +671,17 @@ class Row(html.HTMLElement): return "%s?%s" % (table_url, params) def can_be_selected(self, datum): - """By default if multiselect enabled return True. You can remove the - checkbox after an ajax update here if required. + """Determines whether the row can be selected. + + By default if multiselect enabled return True. + You can remove the checkbox after an ajax update here if required. """ return True def get_data(self, request, obj_id): - """Fetches the updated data for the row based on the object id - passed in. Must be implemented by a subclass to allow AJAX updating. + """Fetches the updated data for the row based on the given object ID. + + Must be implemented by a subclass to allow AJAX updating. """ return {} @@ -1322,9 +1331,11 @@ class DataTable(object): return str(slugify(self._meta.name)) def get_filter_string(self): - """Get the filter string value. For 'server' type filters this is - saved in the session so that it gets persisted across table loads. - For other filter types this is obtained from the POST dict. + """Get the filter string value. + + For 'server' type filters this is saved in the session so that + it gets persisted across table loads. For other filter types + this is obtained from the POST dict. """ filter_action = self._meta._filter_action param_name = filter_action.get_param_name() @@ -1336,8 +1347,9 @@ class DataTable(object): return filter_string def get_filter_field(self): - """Get the filter field value used for 'server' type filters. This - is the value from the filter action's list of filter choices. + """Get the filter field value used for 'server' type filters. + + This is the value from the filter action's list of filter choices. """ filter_action = self._meta._filter_action param_name = '%s_field' % filter_action.get_param_name() @@ -1406,15 +1418,19 @@ class DataTable(object): return self._no_data_message def get_filter_first_message(self): - """Return the message to be displayed when the user needs to provide - first a search criteria before loading any data. + """Return the message to be displayed first in the filter. + + when the user needs to provide a search criteria first + before loading any data. """ return self._filter_first_message def get_object_by_id(self, lookup): - """Returns the data object from the table's dataset which matches - the ``lookup`` parameter specified. An error will be raised if - the match is not a single data object. + """Returns the data object whose ID matches ``loopup`` parameter. + + The data object is looked up from the table's dataset and + the data which matches the ``lookup`` parameter specified. + An error will be raised if the match is not a single data object. We will convert the object id and ``lookup`` to unicode before comparison. @@ -1445,8 +1461,9 @@ class DataTable(object): @property def has_actions(self): - """Boolean. Indicates whether there are any available actions on this - table. + """Indicates whether there are any available actions on this table. + + Returns a boolean value. """ if not self.base_actions: return False @@ -1454,8 +1471,9 @@ class DataTable(object): @property def needs_form_wrapper(self): - """Boolean. Indicates whether this table should be rendered wrapped in - a ``
`` tag or not. + """Returns if this table should be rendered wrapped in a ```` tag. + + Returns a boolean value. """ # If needs_form_wrapper is explicitly set, defer to that. if self._needs_form_wrapper is not None: @@ -1532,8 +1550,10 @@ class DataTable(object): return table_actions_template.render(context) def render_row_actions(self, datum, row=False): - """Renders the actions specified in ``Meta.row_actions`` using the - current row data. If `row` is True, the actions are rendered in a row + """Renders the actions specified in ``Meta.row_actions``. + + The actions are rendered using the current row data. + If `row` is True, the actions are rendered in a row of buttons. Otherwise they are rendered in a dropdown box. """ if row: @@ -1550,8 +1570,9 @@ class DataTable(object): @staticmethod def parse_action(action_string): - """Parses the ``action`` parameter (a string) sent back with the - POST data. By default this parses a string formatted as + """Parses the ``action_string`` parameter sent back with the POST data. + + By default this parses a string formatted as ``{{ table_name }}__{{ action_name }}__{{ row_id }}`` and returns each of the pieces. The ``row_id`` is optional. """ @@ -1568,10 +1589,10 @@ class DataTable(object): return table, action, object_id def take_action(self, action_name, obj_id=None, obj_ids=None): - """Locates the appropriate action and routes the object - data to it. The action should return an HTTP redirect - if successful, or a value which evaluates to ``False`` - if unsuccessful. + """Locates the appropriate action and routes the object data to it. + + The action should return an HTTP redirect if successful, + or a value which evaluates to ``False`` if unsuccessful. """ # See if we have a list of ids obj_ids = obj_ids or self.request.POST.getlist('object_ids') @@ -1616,8 +1637,10 @@ class DataTable(object): return table, action, obj_id def maybe_preempt(self): - """Determine whether the request should be handled by a preemptive - action on this table or by an AJAX row update before loading any data. + """Determine whether the request should be handled in earlier phase. + + It determines the request should be handled by a preemptive action + on this table or by an AJAX row update before loading any data. """ request = self.request table_name, action_name, obj_id = self.check_handler(request) @@ -1705,8 +1728,7 @@ class DataTable(object): return HttpResponse(status=error.status_code) def inline_update_action(self, request, datum, cell, obj_id, cell_name): - """Handling update by POST of the cell. - """ + """Handling update by POST of the cell.""" new_cell_value = request.POST.get( cell_name + '__' + obj_id, None) if issubclass(cell.column.form_field.__class__, @@ -1742,7 +1764,9 @@ class DataTable(object): content_type="application/json") def maybe_handle(self): - """Determine whether the request should be handled by any action on + """Handles table actions if needed. + + It determines whether the request should be handled by any action on this table after data has been loaded. """ request = self.request @@ -1756,8 +1780,10 @@ class DataTable(object): return None def sanitize_id(self, obj_id): - """Override to modify an incoming obj_id to match existing - API data types or modify the format. + """Override to modify an incoming obj_id to match existing API. + + It is used to modify an incoming obj_id (used in Horizon) + to the data type or format expected by the API. """ return obj_id @@ -1787,8 +1813,10 @@ class DataTable(object): return getattr(datum, display_key, None) def has_prev_data(self): - """Returns a boolean value indicating whether there is previous data - available to this table from the source (generally an API). + """Returns a boolean value indicating whether there is previous data. + + Returns True if there is previous data available to this table + from the source (generally an API). The method is largely meant for internal use, but if you want to override it to provide custom behavior you can do so at your own risk. @@ -1796,8 +1824,10 @@ class DataTable(object): return self._meta.has_prev_data def has_more_data(self): - """Returns a boolean value indicating whether there is more data - available to this table from the source (generally an API). + """Returns a boolean value indicating whether there is more data. + + Returns True if there is more data available to this table + from the source (generally an API). The method is largely meant for internal use, but if you want to override it to provide custom behavior you can do so at your own risk. @@ -1805,35 +1835,35 @@ class DataTable(object): return self._meta.has_more_data def get_prev_marker(self): - """Returns the identifier for the first object in the current data set - for APIs that use marker/limit-based paging. + """Returns the identifier for the first object in the current data set. + + The return value will be used as marker/limit-based paging in the API. """ return http.urlquote_plus(self.get_object_id(self.data[0])) \ if self.data else '' def get_marker(self): - """Returns the identifier for the last object in the current data set - for APIs that use marker/limit-based paging. + """Returns the identifier for the last object in the current data set. + + The return value will be used as marker/limit-based paging in the API. """ return http.urlquote_plus(self.get_object_id(self.data[-1])) \ if self.data else '' def get_prev_pagination_string(self): - """Returns the query parameter string to paginate this table - to the previous page. - """ + """Returns the query parameter string to paginate to the prev page.""" return "=".join([self._meta.prev_pagination_param, self.get_prev_marker()]) def get_pagination_string(self): - """Returns the query parameter string to paginate this table - to the next page. - """ + """Returns the query parameter string to paginate to the next page.""" return "=".join([self._meta.pagination_param, self.get_marker()]) def calculate_row_status(self, statuses): - """Returns a boolean value determining the overall row status - based on the dictionary of column name to status mappings passed in. + """Returns a boolean value determining the overall row status. + + It is detremined based on the dictionary of column name + to status mappings passed in. By default, it uses the following logic: @@ -1858,9 +1888,10 @@ class DataTable(object): return True def get_row_status_class(self, status): - """Returns a css class name determined by the status value. This class - name is used to indicate the status of the rows in the table if - any ``status_columns`` have been specified. + """Returns a css class name determined by the status value. + + This class name is used to indicate the status of the rows in the table + if any ``status_columns`` have been specified. """ if status is True: return "status_up" diff --git a/horizon/tables/views.py b/horizon/tables/views.py index 3077ddca70..62d2cf3c72 100644 --- a/horizon/tables/views.py +++ b/horizon/tables/views.py @@ -158,8 +158,9 @@ class MultiTableMixin(object): return filter_info def handle_server_filter(self, request, table=None): - """Update the table server filter information in the session and - determine if the filter has been changed. + """Update the table server filter information in the session. + + Returns True if the filter has been changed. """ if not table: table = self.get_table() @@ -172,9 +173,10 @@ class MultiTableMixin(object): return filter_info['changed'] def update_server_filter_action(self, request, table=None): - """Update the table server side filter action based on the current - filter. The filter info may be stored in the session and this will - restore it. + """Update the table server side filter action. + + It is done based on the current filter. The filter info may be stored + in the session and this will restore it. """ if not table: table = self.get_table() @@ -187,8 +189,10 @@ class MultiTableMixin(object): class MultiTableView(MultiTableMixin, views.HorizonTemplateView): - """A class-based generic view to handle the display and processing of - multiple :class:`~horizon.tables.DataTable` classes in a single view. + """Generic view to handle multiple DataTable classes in a single view. + + Each DataTable class must be a :class:`~horizon.tables.DataTable` class + or its subclass. Three steps are required to use this view: set the ``table_classes`` attribute with a tuple of the desired @@ -309,8 +313,7 @@ class DataTableView(MultiTableView): class MixedDataTableView(DataTableView): - """A class-based generic view to handle DataTable with mixed data - types. + """A class-based generic view to handle DataTable with mixed data types. Basic usage is the same as DataTableView. diff --git a/horizon/tabs/base.py b/horizon/tabs/base.py index 921ec9129c..d39fdee95c 100644 --- a/horizon/tabs/base.py +++ b/horizon/tabs/base.py @@ -30,8 +30,7 @@ CSS_DISABLED_TAB_CLASSES = ["disabled"] class TabGroup(html.HTMLElement): - """A container class which knows how to manage and render - :class:`~horizon.tabs.Tab` objects. + """A container class which knows how to manage and render Tab objects. .. attribute:: slug @@ -128,21 +127,26 @@ class TabGroup(html.HTMLElement): exceptions.handle(self.request) def get_id(self): - """Returns the id for this tab group. Defaults to the value of the tab + """Returns the id for this tab group. + + Defaults to the value of the tab group's :attr:`horizon.tabs.Tab.slug`. """ return self.slug def get_default_classes(self): - """Returns a list of the default classes for the tab group. Defaults to - ``["nav", "nav-tabs", "ajax-tabs"]``. + """Returns a list of the default classes for the tab group. + + Defaults to ``["nav", "nav-tabs", "ajax-tabs"]``. """ default_classes = super(TabGroup, self).get_default_classes() default_classes.extend(CSS_TAB_GROUP_CLASSES) return default_classes def tabs_not_available(self): - """In the event that no tabs are either allowed or enabled, this method + """The fallback handler if no tabs are either allowed or enabled. + + In the event that no tabs are either allowed or enabled, this method is the fallback handler. By default it's a no-op, but it exists to make redirecting or raising exceptions possible for subclasses. """ @@ -214,8 +218,7 @@ class TabGroup(html.HTMLElement): class Tab(html.HTMLElement): - """A reusable interface for constructing a tab within a - :class:`~horizon.tabs.TabGroup`. + """A reusable interface for constructing a tab within a TabGroup. .. attribute:: name @@ -301,9 +304,10 @@ class Tab(html.HTMLElement): return getattr(self, "_data", None) is not None def render(self): - """Renders the tab to HTML using the + """Renders the tab to HTML. + :meth:`~horizon.tabs.Tab.get_context_data` method and - the :meth:`~horizon.tabs.Tab.get_template_name` method. + the :meth:`~horizon.tabs.Tab.get_template_name` method are called. If :attr:`~horizon.tabs.Tab.preload` is ``False`` and ``force_load`` is not ``True``, or @@ -323,8 +327,9 @@ class Tab(html.HTMLElement): return render_to_string(self.get_template_name(self.request), context) def get_id(self): - """Returns the id for this tab. Defaults to - ``"{{ tab_group.slug }}__{{ tab.slug }}"``. + """Returns the id for this tab. + + Defaults to ``"{{ tab_group.slug }}__{{ tab.slug }}"``. """ return SEPARATOR.join([self.tab_group.slug, self.slug]) @@ -332,9 +337,10 @@ class Tab(html.HTMLElement): return "=".join((self.tab_group.param_name, self.get_id())) def get_default_classes(self): - """Returns a list of the default classes for the tab. Defaults to - and empty list (``[]``), however additional classes may be added - depending on the state of the tab as follows: + """Returns a list of the default classes for the tab. + + Defaults to and empty list (``[]``), however additional classes may + be added depending on the state of the tab as follows: If the tab is the active tab for the tab group, in which the class ``"active"`` will be added. @@ -362,14 +368,17 @@ class Tab(html.HTMLElement): return self.template_name def get_context_data(self, request, **kwargs): - """This method should return a dictionary of context data used to - render the tab. Required. + """Return a dictionary of context data used to render the tab. + + Required. """ return kwargs def enabled(self, request): - """Determines whether or not the tab should be accessible - (e.g. be rendered into the HTML on load and respond to a click event). + """Determines whether or not the tab should be accessible. + + For example, the tab should be rendered into the HTML + on load and respond to a click event. If a tab returns ``False`` from ``enabled`` it will ignore the value of ``preload`` and only render the HTML of the tab after being clicked. @@ -400,8 +409,7 @@ class Tab(html.HTMLElement): class TableTab(Tab): - """A :class:`~horizon.tabs.Tab` class which knows how to deal with - :class:`~horizon.tables.DataTable` classes rendered inside of it. + """A Tab class which knows how to deal with DataTable classes inside of it. This distinct class is required due to the complexity involved in handling both dynamic tab loading, dynamic table updating and table actions all @@ -432,8 +440,9 @@ class TableTab(Tab): self._table_data_loaded = False def load_table_data(self): - """Calls the ``get_{{ table_name }}_data`` methods for each table class - and sets the data on the tables. + """Calls the ``get_{{ table_name }}_data`` methods for each table class. + + When returning, the loaded data is set on the tables. """ # We only want the data to be loaded once, so we track if we have... if not self._table_data_loaded: @@ -456,8 +465,10 @@ class TableTab(Tab): self._table_data_loaded = True def get_context_data(self, request, **kwargs): - """Adds a ``{{ table_name }}_table`` item to the context for each table - in the :attr:`~horizon.tabs.TableTab.table_classes` attribute. + """Adds a ``{{ table_name }}_table`` item to the context for each table. + + The target tables are specified by + the :attr:`~horizon.tabs.TableTab.table_classes` attribute. If only one table class is provided, a shortcut ``table`` context variable is also added containing the single table. diff --git a/horizon/tabs/views.py b/horizon/tabs/views.py index e2456494f4..a5be35b63b 100644 --- a/horizon/tabs/views.py +++ b/horizon/tabs/views.py @@ -19,8 +19,7 @@ from horizon import views class TabView(views.HorizonTemplateView): - """A generic class-based view for displaying a - :class:`horizon.tabs.TabGroup`. + """A generic view for displaying a :class:`horizon.tabs.TabGroup`. This view handles selecting specific tabs and deals with AJAX requests gracefully. @@ -57,8 +56,9 @@ class TabView(views.HorizonTemplateView): return context def handle_tabbed_response(self, tab_group, context): - """Sends back an AJAX-appropriate response for the tab group if - required, otherwise renders the response as normal. + """Sends back an AJAX-appropriate response for the tab group if needed. + + Otherwise renders the response as normal. """ if self.request.is_ajax(): if tab_group.selected: @@ -79,10 +79,11 @@ class TabbedTableView(tables.MultiTableMixin, TabView): self._table_dict = {} def load_tabs(self): - """Loads the tab group, and compiles the table instances for each - table attached to any :class:`horizon.tabs.TableTab` instances on - the tab group. This step is necessary before processing any - tab or table actions. + """Loads the tab group. + + It compiles the table instances for each table attached to + any :class:`horizon.tabs.TableTab` instances on the tab group. + This step is necessary before processing any tab or table actions. """ tab_group = self.get_tabs(self.request, **self.kwargs) tabs = tab_group.get_tabs() @@ -99,10 +100,12 @@ class TabbedTableView(tables.MultiTableMixin, TabView): return {} def handle_table(self, table_dict): - """For the given dict containing a ``DataTable`` and a ``TableTab`` + """Loads the table data based on a given table_dict and handles them. + + For the given dict containing a ``DataTable`` and a ``TableTab`` instance, it loads the table data for that tab and calls the - table's :meth:`~horizon.tables.DataTable.maybe_handle` method. The - return value will be the result of ``maybe_handle``. + table's :meth:`~horizon.tables.DataTable.maybe_handle` method. + The return value will be the result of ``maybe_handle``. """ table = table_dict['table'] tab = table_dict['tab'] diff --git a/horizon/templatetags/angular.py b/horizon/templatetags/angular.py index b86e2bc7f6..34bb435960 100644 --- a/horizon/templatetags/angular.py +++ b/horizon/templatetags/angular.py @@ -25,12 +25,13 @@ register = template.Library() @receiver(post_compress) def update_angular_template_hash(sender, **kwargs): - """Listen for compress events. If the angular templates - have been re-compressed, also clear them from the Django - cache backend. This is important to allow deployers to - change a template file, re-compress, and not accidentally - serve the old Django cached version of that content to - clients. + """Listen for compress events. + + If the angular templates have been re-compressed, also clear them + from the Django cache backend. This is important to allow + deployers to change a template file, re-compress, and not + accidentally serve the old Django cached version of that content + to clients. """ context = kwargs['context'] # context the compressor is working with compressed = context['compressed'] # the compressed content @@ -55,9 +56,11 @@ def update_angular_template_hash(sender, **kwargs): @register.filter(name='angular_escapes') def angular_escapes(value): - """Djangos 'escapejs' is too aggressive and inserts unicode. Provide - a basic filter to allow angular template content to be used within - javascript strings. + """Provide a basic filter to allow angular template content for Angular. + + Djangos 'escapejs' is too aggressive and inserts unicode. + It provide a basic filter to allow angular template content to be used + within javascript strings. Args: value: a string @@ -75,10 +78,11 @@ def angular_escapes(value): @register.inclusion_tag('angular/angular_templates.html', takes_context=True) def angular_templates(context): - """For all static HTML templates, generate a dictionary of template - contents. If the template has been overridden by a theme, load the - override contents instead of the original HTML file. One use for - this is to pre-populate the angular template cache. + """Generate a dictionary of template contents for all static HTML templates. + + If the template has been overridden by a theme, load the + override contents instead of the original HTML file. + One use for this is to pre-populate the angular template cache. Args: context: the context of the current Django template diff --git a/horizon/templatetags/bootstrap.py b/horizon/templatetags/bootstrap.py index 311a3da0c1..e4a0148ac8 100644 --- a/horizon/templatetags/bootstrap.py +++ b/horizon/templatetags/bootstrap.py @@ -24,6 +24,7 @@ register = template.Library() @register.inclusion_tag('bootstrap/progress_bar.html') def bs_progress_bar(*args, **kwargs): """A Standard Bootstrap Progress Bar. + http://getbootstrap.com/components/#progress param args (Array of Numbers: 0-100): Percent of Progress Bars diff --git a/horizon/templatetags/breadcrumb_nav.py b/horizon/templatetags/breadcrumb_nav.py index ef05723015..27a9df236d 100644 --- a/horizon/templatetags/breadcrumb_nav.py +++ b/horizon/templatetags/breadcrumb_nav.py @@ -21,6 +21,7 @@ register = template.Library() takes_context=True) def breadcrumb_nav(context): """A logic heavy function for automagically creating a breadcrumb. + It uses the dashboard, panel group(if it exists), then current panel. Can also use a "custom_breadcrumb" context item to add extra items. """ diff --git a/horizon/templatetags/horizon.py b/horizon/templatetags/horizon.py index 2225665f1c..5721569c6d 100644 --- a/horizon/templatetags/horizon.py +++ b/horizon/templatetags/horizon.py @@ -43,9 +43,7 @@ class MinifiedNode(Node): @register.filter def has_permissions(user, component): - """Checks if the given user meets the permissions requirements for - the component. - """ + """Checks if the given user meets the permissions requirements.""" return user.has_perms(getattr(component, 'permissions', set())) @@ -188,7 +186,9 @@ class JSTemplateNode(template.Node): @register.tag def jstemplate(parser, token): - """Replaces ``[[[`` and ``]]]`` with ``{{{`` and ``}}}``, + """Templatetag to handle any of the Mustache-based templates. + + Replaces ``[[[`` and ``]]]`` with ``{{{`` and ``}}}``, ``[[`` and ``]]`` with ``{{`` and ``}}`` and ``[%`` and ``%]`` with ``{%`` and ``%}`` to avoid conflicts with Django's template engine when using any of the Mustache-based diff --git a/horizon/templatetags/parse_date.py b/horizon/templatetags/parse_date.py index f2a6d7fae6..9af229f16f 100644 --- a/horizon/templatetags/parse_date.py +++ b/horizon/templatetags/parse_date.py @@ -31,9 +31,7 @@ register = template.Library() class ParseDateNode(template.Node): def render(self, datestring): - """Parses a date-like input string into a timezone aware Python - datetime. - """ + """Parses a date-like string into a timezone aware Python datetime.""" formats = ["%Y-%m-%dT%H:%M:%S.%f", "%Y-%m-%d %H:%M:%S.%f", "%Y-%m-%dT%H:%M:%S", "%Y-%m-%d %H:%M:%S"] if datestring: diff --git a/horizon/test/helpers.py b/horizon/test/helpers.py index b3b0dd4e1a..98ac493e8f 100644 --- a/horizon/test/helpers.py +++ b/horizon/test/helpers.py @@ -115,8 +115,7 @@ class RequestFactoryWithMessages(RequestFactory): @unittest.skipIf(os.environ.get('SKIP_UNITTESTS', False), "The SKIP_UNITTESTS env variable is set.") class TestCase(django_test.TestCase): - """Specialized base test case class for Horizon which gives access to - numerous additional features: + """Base test case class for Horizon with numerous additional features. * The ``mox`` mocking framework via ``self.mox``. * A ``RequestFactory`` class which supports Django's ``contrib.messages`` @@ -176,15 +175,17 @@ class TestCase(django_test.TestCase): self.assertNotRegex(text, unexpected_regexp, msg) def assertNoMessages(self, response=None): - """Asserts that no messages have been attached by the - ``contrib.messages`` framework. + """Asserts no messages have been attached by the messages framework. + + The expected messages framework is ``django.contrib.messages``. """ self.assertMessageCount(response, success=0, warn=0, info=0, error=0) def assertMessageCount(self, response=None, **kwargs): - """Asserts that the specified number of messages have been attached - for various message types. Usage would look like - ``self.assertMessageCount(success=1)``. + """Asserts that the expected number of messages have been attached. + + The expected number of messages can be specified per message type. + Usage would look like ``self.assertMessageCount(success=1)``. """ temp_req = self.client.request(**{'wsgi.input': None}) temp_req.COOKIES = self.client.cookies @@ -255,9 +256,9 @@ class SeleniumTestCase(LiveServerTestCase): class JasmineTests(SeleniumTestCase): - """Helper class which allows you to create a simple Jasmine test running - through Selenium + """Helper class which allows you to create a simple Jasmine test. + Jasmine tests are run through Selenium. To run a jasmine test suite, create a class which extends JasmineTests in the :file:`horizon/test/jasmine/jasmine_tests.py` and define two class attributes diff --git a/horizon/test/test_hacking.py b/horizon/test/test_hacking.py index 451b80ac79..e6ec669bde 100644 --- a/horizon/test/test_hacking.py +++ b/horizon/test/test_hacking.py @@ -21,10 +21,12 @@ from horizon.test import helpers class HackingTestCase(helpers.TestCase): - """This class tests the hacking checks in horizon.hacking.checks by passing - strings to the check methods like the pep8/flake8 parser would. The parser - loops over each line in the file and then passes the parameters to the - check method. The parameter names in the check method dictate what type of + """This class tests the hacking checks in horizon.hacking.checks. + + Each check passes passing strings to the check methods like the + pep8/flake8 parser would. The parser loops over each line in the + file and then passes the parameters to the check method. + The parameter names in the check method dictate what type of object is passed to the check method. The parameter types are: logical_line diff --git a/horizon/test/tests/babel_extract_angular.py b/horizon/test/tests/babel_extract_angular.py index 4c712b96e5..f09b62879e 100644 --- a/horizon/test/tests/babel_extract_angular.py +++ b/horizon/test/tests/babel_extract_angular.py @@ -1,4 +1,4 @@ -# -*- encoding: UTF-8 -*- +# -*- encoding: utf-8 -*- # Copyright 2015, Rackspace, US, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -44,17 +44,14 @@ class ExtractAngularTestCase(test.TestCase): messages) def test_attr_value(self): - """We should not translate tags that have translate as the value of an - attribute. - """ + """Should not translate tags with translate as the value of an attr.""" buf = StringIO('
hello world!
') messages = list(extract_angular(buf, [], [], {})) self.assertEqual([], messages) def test_attr_value_plus_directive(self): - """Unless they also have a translate directive. - """ + """Unless they also have a translate directive.""" buf = StringIO( '
hello world!
') diff --git a/horizon/test/tests/base.py b/horizon/test/tests/base.py index 997806d247..94e612f35d 100644 --- a/horizon/test/tests/base.py +++ b/horizon/test/tests/base.py @@ -118,10 +118,11 @@ class BaseHorizonTests(test.TestCase): dash.register(panel) def _reload_urls(self): - """Clears out the URL caches, reloads the root urls module, and - re-triggers the autodiscovery mechanism for Horizon. Allows URLs - to be re-calculated after registering new dashboards. Useful - only for testing and should never be used on a live site. + """Clears out the URL caches, and reloads the root urls module. + + It re-triggers the autodiscovery mechanism for Horizon. + Allows URLs to be re-calculated after registering new dashboards. + Useful only for testing and should never be used on a live site. """ urlresolvers.clear_url_caches() moves.reload_module(import_module(settings.ROOT_URLCONF)) @@ -371,8 +372,9 @@ class GetUserHomeTests(BaseHorizonTests): class CustomPanelTests(BaseHorizonTests): - """Test customization of dashboards and panels - using 'customization_module' to HORIZON_CONFIG. + """Test customization of dashboards and panels. + + This tests customization using 'customization_module' to HORIZON_CONFIG. """ def setUp(self): @@ -407,8 +409,9 @@ class CustomPanelTests(BaseHorizonTests): class CustomPermissionsTests(BaseHorizonTests): - """Test customization of permissions on panels - using 'customization_module' to HORIZON_CONFIG. + """Test customization of permissions on panels. + + This tests customization using 'customization_module' to HORIZON_CONFIG. """ def setUp(self): diff --git a/horizon/test/tests/test_file_discovery.py b/horizon/test/tests/test_file_discovery.py index 0d569008bb..3fb0055651 100644 --- a/horizon/test/tests/test_file_discovery.py +++ b/horizon/test/tests/test_file_discovery.py @@ -71,50 +71,42 @@ class FinderTests(unittest.TestCase): # discover_files() # def test_find_all(self): - """Find all files - """ + """Find all files""" files = fd.discover_files(base_path) self.assertEqual(len(files), 18) def test_find_a(self): - """Find all files in folder `a` - """ + """Find all files in folder `a`""" files = fd.discover_files(base_path, sub_path='a') self.assertEqual(len(files), 8) def test_find_b(self): - """Find all files in folder `b` - """ + """Find all files in folder `b`""" files = fd.discover_files(base_path, sub_path='b') self.assertEqual(len(files), 10) def test_find_all_js(self): - """Find all files with extension of `.js` - """ + """Find all files with extension of `.js`""" files = fd.discover_files(base_path, ext='.js') self.assertEqual(len(files), 14) def test_find_all_html(self): - """Find all files with extension of `.html` - """ + """Find all files with extension of `.html`""" files = fd.discover_files(base_path, ext='.html') self.assertEqual(len(files), 2) def test_find_all_js_in_a(self): - """Find all files with extension of `.js` in folder a - """ + """Find all files with extension of `.js` in folder a""" files = fd.discover_files(base_path, sub_path='b', ext='.js') self.assertEqual(len(files), 7) def test_find_all_html_in_a(self): - """Find all files with extension of `.html` in folder a - """ + """Find all files with extension of `.html` in folder a""" files = fd.discover_files(base_path, sub_path='b', ext='.html') self.assertEqual(len(files), 1) def test_find_all_file_trim_base_path(self): - """Find all files in folder, trim base path - """ + """Find all files in folder, trim base path""" files = fd.discover_files(base_path, sub_path='a', trim_base_path=True) self.assertTrue(files[0].startswith('a/')) @@ -125,8 +117,7 @@ class FinderTests(unittest.TestCase): # sort_js_files() # def test_sort_js_files(self): - """Sort all JavaScript files - """ + """Sort all JavaScript files""" files = fd.discover_files(base_path, ext='.js') sources, mocks, specs = fd.sort_js_files(files) @@ -156,8 +147,7 @@ class FinderTests(unittest.TestCase): # discover_static_files() # def test_discover_all_static_files(self): - """Find all static files - """ + """Find all static files""" sources, mocks, specs, templates = fd.discover_static_files(base_path) self.assertEqual(len(sources), 6) self.assertEqual(len(mocks), 2) @@ -189,8 +179,7 @@ class FinderTests(unittest.TestCase): # populate_horizon_config() # def test_populate_horizon_config(self): - """Populate horizon config - """ + """Populate horizon config""" horizon_config = {} fd.populate_horizon_config(horizon_config, base_path) diff --git a/horizon/test/utils.py b/horizon/test/utils.py index ea2b254ee7..9a5dd34a06 100644 --- a/horizon/test/utils.py +++ b/horizon/test/utils.py @@ -12,9 +12,7 @@ class ObjDictWrapper(dict): - """ObjDictWrapper is a container that provides both dictionary-like and - object-like attribute access. - """ + """Wrapper that provides both dict- and object-like attribute access.""" def __getattr__(self, item): if item in self: return self[item] diff --git a/horizon/test/webdriver.py b/horizon/test/webdriver.py index 9503aecd76..d235249999 100644 --- a/horizon/test/webdriver.py +++ b/horizon/test/webdriver.py @@ -109,8 +109,7 @@ class WebElementWrapper(WrapperFindOverride, webelement.WebElement): class WebDriverWrapper(WrapperFindOverride, WebDriver): - """Wrapper for webdriver to return WebElementWrapper on find_element. - """ + """Wrapper for webdriver to return WebElementWrapper on find_element.""" def reload_request(self, locator, index): try: # element was found out via find_elements diff --git a/horizon/themes.py b/horizon/themes.py index be5b37086d..e03e917911 100644 --- a/horizon/themes.py +++ b/horizon/themes.py @@ -87,9 +87,11 @@ def offline_context(): # A piece of middleware that stores the theme cookie value into # local thread storage so the template loader can access it class ThemeMiddleware(object): - """The Theme Middleware component. The custom template loaders - don't have access to the request object, so we need to store - the Cookie's theme value for use later in the Django chain. + """The Theme Middleware component. + + The custom template loaders + don't have access to the request object, so we need to store + the Cookie's theme value for use later in the Django chain. """ def process_request(self, request): @@ -111,8 +113,10 @@ class ThemeMiddleware(object): class ThemeTemplateLoader(tLoaderCls): - """Themes can contain template overrides, so we need to check the - theme directory first, before loading any of the standard templates. + """Theme-aware template loader. + + Themes can contain template overrides, so we need to check the + theme directory first, before loading any of the standard templates. """ is_usable = True diff --git a/horizon/utils/babel_extract_angular.py b/horizon/utils/babel_extract_angular.py index 37c1eb9aec..ff517cedca 100644 --- a/horizon/utils/babel_extract_angular.py +++ b/horizon/utils/babel_extract_angular.py @@ -1,4 +1,4 @@ -# -*- encoding: UTF-8 -*- +# -*- encoding: utf-8 -*- # Copyright 2015, Rackspace, US, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -144,9 +144,11 @@ class AngularGettextHTMLParser(html_parser.HTMLParser): def extract_angular(fileobj, keywords, comment_tags, options): - """Extract messages from angular template (HTML) files that use the + """Extract messages from angular template (HTML) files. + + It extract messages from angular template (HTML) files that use angular-gettext translate directive as per - https://angular-gettext.rocketeer.be/ . + https://angular-gettext.rocketeer.be/ :param fileobj: the file-like object the messages should be extracted from diff --git a/horizon/utils/file_discovery.py b/horizon/utils/file_discovery.py index 03227030ce..40477f9d88 100644 --- a/horizon/utils/file_discovery.py +++ b/horizon/utils/file_discovery.py @@ -23,8 +23,7 @@ SPEC_EXT = '.spec.js' def discover_files(base_path, sub_path='', ext='', trim_base_path=False): - """Discovers all files with certain extension in given paths. - """ + """Discovers all files with certain extension in given paths.""" file_list = [] for root, dirs, files in walk(path.join(base_path, sub_path)): if trim_base_path: @@ -36,8 +35,10 @@ def discover_files(base_path, sub_path='', ext='', trim_base_path=False): def sort_js_files(js_files): - """Sorts JavaScript files in `js_files` into source files, mock files - and spec files based on file extension. + """Sorts JavaScript files in `js_files`. + + It sorts JavaScript files in a given `js_files` + into source files, mock files and spec files based on file extension. Output: @@ -72,8 +73,10 @@ def sort_js_files(js_files): def discover_static_files(base_path, sub_path=''): - """Discovers static files in given paths, returning JavaScript sources, - mocks, specs and HTML templates, all grouped in lists. + """Discovers static files in given paths. + + It returns JavaScript sources, mocks, specs and HTML templates, + all grouped in lists. """ js_files = discover_files(base_path, sub_path=sub_path, ext='.js', trim_base_path=True) @@ -105,8 +108,7 @@ def populate_horizon_config(horizon_config, base_path, def _log(file_list, list_name, in_path): - """Logs result at debug level - """ + """Logs result at debug level""" file_names = '\n'.join(file_list) LOG.debug("\nDiscovered %(size)d %(name)s file(s) in %(path)s:\n" "%(files)s\n", diff --git a/horizon/utils/filters.py b/horizon/utils/filters.py index 61a390fcd3..c2578615f9 100644 --- a/horizon/utils/filters.py +++ b/horizon/utils/filters.py @@ -30,9 +30,8 @@ def replace_underscores(string): @register.filter def parse_isotime(timestr, default=None): - """This duplicates oslo timeutils parse_isotime but with a - @register.filter annotation and a silent fallback on error. - """ + # This duplicates oslo timeutils parse_isotime but with a + # @register.filter annotation and a silent fallback on error. try: return iso8601.parse_date(timestr) except (iso8601.ParseError, TypeError): @@ -41,8 +40,10 @@ def parse_isotime(timestr, default=None): @register.filter def timesince_or_never(dt, default=None): - """Call the Django ``timesince`` filter, but return the string - *default* if *dt* is not a valid ``date`` or ``datetime`` object. + """Call the Django ``timesince`` filter or a given default string. + + It returns the string *default* if *dt* is not a valid ``date`` + or ``datetime`` object. When *default* is None, "Never" is returned. """ if default is None: diff --git a/horizon/utils/functions.py b/horizon/utils/functions.py index d4d1168534..a5514fdeec 100644 --- a/horizon/utils/functions.py +++ b/horizon/utils/functions.py @@ -68,6 +68,7 @@ def logout_with_message(request, msg, redirect=True, status='success'): def get_config_value(request, key, default, search_in_settings=True): """Retrieves the value of `key` from configuration in the following order: + - from the session; if not found there then - from cookies; if not found there then - from the settings file if `search_in_settings` is True, @@ -92,8 +93,7 @@ def get_config_value(request, key, default, search_in_settings=True): def save_config_value(request, response, key, value): - """Sets value of key `key` to `value` in both session and cookies. - """ + """Sets value of key `key` to `value` in both session and cookies.""" request.session[key] = value response.set_cookie(key, value, expires=one_year_from_now()) return response @@ -126,14 +126,18 @@ def natural_sort(attr): def get_keys(tuple_of_tuples): - """Processes a tuple of 2-element tuples and returns a tuple containing + """Returns a tuple containing first component of each tuple. + + It processes a tuple of 2-element tuples and returns a tuple containing first component of each tuple. """ return tuple([t[0] for t in tuple_of_tuples]) def value_for_key(tuple_of_tuples, key): - """Processes a tuple of 2-element tuples and returns the value + """Returns a value containing to the given key. + + It processes a tuple of 2-element tuples and returns the value corresponding to the given key. If no value is found, the key is returned. """ for t in tuple_of_tuples: @@ -144,7 +148,9 @@ def value_for_key(tuple_of_tuples, key): def next_key(tuple_of_tuples, key): - """Processes a tuple of 2-element tuples and returns the key which comes + """Returns the key which comes after the given key. + + It processes a tuple of 2-element tuples and returns the key which comes after the given key. """ for i, t in enumerate(tuple_of_tuples): @@ -156,7 +162,9 @@ def next_key(tuple_of_tuples, key): def previous_key(tuple_of_tuples, key): - """Processes a tuple of 2-element tuples and returns the key which comes + """Returns the key which comes before the give key. + + It Processes a tuple of 2-element tuples and returns the key which comes before the given key. """ for i, t in enumerate(tuple_of_tuples): @@ -168,8 +176,9 @@ def previous_key(tuple_of_tuples, key): def format_value(value): - """Returns the given value rounded to one decimal place if it is a - decimal, or integer if it is an integer. + """Returns the given value rounded to one decimal place if deciaml. + + Returns the integer if an integer is given. """ value = decimal.Decimal(str(value)) if int(value) == value: diff --git a/horizon/utils/html.py b/horizon/utils/html.py index 3311c16db0..71180c0432 100644 --- a/horizon/utils/html.py +++ b/horizon/utils/html.py @@ -22,21 +22,21 @@ class HTMLElement(object): self.classes = getattr(self, "classes", []) def get_default_classes(self): - """Returns an iterable of default classes which should be combined with - any other declared classes. + """Returns an iterable of default classes. + + They will be combined with any other declared classes. """ return [] def get_default_attrs(self): - """Returns a dict of default attributes which should be combined with - other declared attributes. + """Returns a dict of default attributes. + + They will be combined with other declared attributes. """ return {} def get_final_attrs(self, classes=True): - """Returns a dict containing the final attributes of this element - which will be rendered. - """ + """Returns a dict containing the final attributes to be rendered.""" final_attrs = copy.copy(self.get_default_attrs()) final_attrs.update(self.attrs) if classes: @@ -57,14 +57,18 @@ class HTMLElement(object): @property def attr_string(self): - """Returns a flattened string of HTML attributes based on the + """Returns a flattened string of HTML attributes. + + HTML attributes are flattened based on the ``attrs`` dict provided to the class. """ return flatatt(self.get_final_attrs()) @property def attr_string_nc(self): - """Returns a flattened string of HTML attributes based on the + """Returns a flattened string of HTML attributes. + + HTML attributes are flattened based on the ``attrs`` dict provided to the class. """ return flatatt(self.get_final_attrs(False)) diff --git a/horizon/utils/units.py b/horizon/utils/units.py index e579c5f892..99b33458d8 100644 --- a/horizon/utils/units.py +++ b/horizon/utils/units.py @@ -33,9 +33,7 @@ ureg = pint.UnitRegistry() def is_supported(unit): - """Returns a bool indicating whether the unit specified is supported by - this module. - """ + """Returns a bool indicating whether the unit specified is supported.""" return unit in functions.get_keys(INFORMATION_UNITS) + TIME_UNITS @@ -56,10 +54,11 @@ def is_larger(unit_1, unit_2): def convert(value, source_unit, target_unit, fmt=False): - """Converts value from source_unit to target_unit. Returns a tuple - containing the converted value and target_unit. Having fmt set to True - causes the value to be formatted to 1 decimal digit if it's a decimal or - be formatted as integer if it's an integer. + """Converts value from source_unit to target_unit. + + Returns a tuple containing the converted value and target_unit. + Having fmt set to True causes the value to be formatted to 1 decimal digit + if it's a decimal or be formatted as integer if it's an integer. E.g: @@ -82,6 +81,7 @@ def convert(value, source_unit, target_unit, fmt=False): def normalize(value, unit): """Converts the value so that it belongs to some expected range. + Returns the new value and new unit. E.g: diff --git a/horizon/views.py b/horizon/views.py index a77cb34b88..19b73cd3a5 100644 --- a/horizon/views.py +++ b/horizon/views.py @@ -44,8 +44,10 @@ class PageTitleMixin(object): page_title = "" def render_context_with_title(self, context): - """This function takes in a context dict and uses it to render the - page_title variable, it then appends this title to the context using + """Render a page title and insert it into the context. + + This function takes in a context dict and uses it to render the + page_title variable. It then appends this title to the context using the 'page_title' key. If there is already a page_title key defined in context received then this function will do nothing. """ @@ -59,8 +61,10 @@ class PageTitleMixin(object): return context def render_to_response(self, context): - """This is an override of the default render_to_response function that - exists in the django generic views, this is here to inject the + """render_to_response() with a page title. + + This is an override of the default render_to_response function that + exists in the django generic views. This is here to inject the page title into the context before the main template is rendered. """ @@ -105,7 +109,9 @@ class APIView(HorizonTemplateView): """ def get_data(self, request, context, *args, **kwargs): - """This method should handle any necessary API calls, update the + """Load necessary API data into the context. + + This method should handle any necessary API calls, update the context object, and return the context object at the end. """ return context diff --git a/horizon/workflows/base.py b/horizon/workflows/base.py index 9546a4feac..2b2fa293d3 100644 --- a/horizon/workflows/base.py +++ b/horizon/workflows/base.py @@ -79,11 +79,11 @@ class ActionMetaclass(forms.forms.DeclarativeFieldsMetaclass): @six.python_2_unicode_compatible @six.add_metaclass(ActionMetaclass) class Action(forms.Form): - """An ``Action`` represents an atomic logical interaction you can have with - the system. This is easier to understand with a conceptual example: in the - context of a "launch instance" workflow, actions would include "naming - the instance", "selecting an image", and ultimately "launching the - instance". + """An ``Action`` represents an atomic logical interaction with the system. + + This is easier to understand with a conceptual example: in the context of + a "launch instance" workflow, actions would include "naming the instance", + "selecting an image", and ultimately "launching the instance". Because ``Actions`` are always interactive, they always provide form controls, and thus inherit from Django's ``Form`` class. However, they @@ -192,9 +192,10 @@ class Action(forms.Form): self.errors[NON_FIELD_ERRORS] = self.error_class([message]) def handle(self, request, context): - """Handles any requisite processing for this action. The method should - return either ``None`` or a dictionary of data to be passed to - :meth:`~horizon.workflows.Step.contribute`. + """Handles any requisite processing for this action. + + The method should return either ``None`` or a dictionary of data + to be passed to :meth:`~horizon.workflows.Step.contribute`. Returns ``None`` by default, effectively making it a no-op. """ @@ -216,8 +217,9 @@ class MembershipAction(Action): @six.python_2_unicode_compatible class Step(object): - """A step is a wrapper around an action which defines its context in a - workflow. It knows about details such as: + """A wrapper around an action which defines its context in a workflow. + + It knows about details such as: * The workflow's context data (data passed from step to step). @@ -399,8 +401,9 @@ class Step(object): return self._action def prepare_action_context(self, request, context): - """Allows for customization of how the workflow context is passed to - the action; this is the reverse of what "contribute" does to make the + """Hook to customize how the workflow context is passed to the action. + + This is the reverse of what "contribute" does to make the action outputs sane for the workflow. Changes to the context are not saved globally here. They are localized to the action. @@ -430,8 +433,9 @@ class Step(object): return True def contribute(self, data, context): - """Adds the data listed in ``contributes`` to the workflow's shared - context. By default, the context is simply updated with all the data + """Adds the data listed in ``contributes`` to the workflow's context. + + By default, the context is simply updated with all the data returned by the action. Note that even if the value of one of the ``contributes`` keys is @@ -524,9 +528,10 @@ class UpdateMembersStep(Step): @six.python_2_unicode_compatible @six.add_metaclass(WorkflowMetaclass) class Workflow(html.HTMLElement): - """A Workflow is a collection of Steps. Its interface is very - straightforward, but it is responsible for handling some very - important tasks such as: + """A Workflow is a collection of Steps. + + Its interface is very straightforward, but it is responsible for handling + some very important tasks such as: * Handling the injection, removal, and ordering of arbitrary steps. @@ -777,8 +782,7 @@ class Workflow(html.HTMLElement): @classmethod def unregister(cls, step_class): - """Unregisters a :class:`~horizon.workflows.Step` from the workflow. - """ + """Unregisters a :class:`~horizon.workflows.Step` from the workflow.""" try: cls._cls_registry.remove(step_class) except KeyError: @@ -786,14 +790,17 @@ class Workflow(html.HTMLElement): return cls._unregister(step_class) def validate(self, context): - """Hook for custom context data validation. Should return a boolean - value or raise :class:`~horizon.exceptions.WorkflowValidationError`. + """Hook for custom context data validation. + + Should return a booleanvalue or + raise :class:`~horizon.exceptions.WorkflowValidationError`. """ return True def is_valid(self): - """Verified that all required data is present in the context and - calls the ``validate`` method to allow for finer-grained checks + """Verifies that all required data is present in the context. + + It also calls the ``validate`` method to allow for finer-grained checks on the context data. """ missing = self.depends_on - set(self.context.keys()) @@ -814,10 +821,12 @@ class Workflow(html.HTMLElement): return self.validate(self.context) def finalize(self): - """Finalizes a workflow by running through all the actions in order - and calling their ``handle`` methods. Returns ``True`` on full success, - or ``False`` for a partial success, e.g. there were non-critical - errors. (If it failed completely the function wouldn't return.) + """Finalizes a workflow by running through all the actions. + + It runs all the actions in order and calling their ``handle`` methods. + Returns ``True`` on full success, or ``False`` for a partial success, + e.g. there were non-critical errors. + (If it failed completely the function wouldn't return.) """ partial = False for step in self.steps: @@ -837,16 +846,18 @@ class Workflow(html.HTMLElement): return not partial def handle(self, request, context): - """Handles any final processing for this workflow. Should return a - boolean value indicating success. + """Handles any final processing for this workflow. + + Should return a boolean value indicating success. """ return True def get_success_url(self): - """Returns a URL to redirect the user to upon completion. By default it - will attempt to parse a ``success_url`` attribute on the workflow, - which can take the form of a reversible URL pattern name, or a - standard HTTP URL. + """Returns a URL to redirect the user to upon completion. + + By default it will attempt to parse a ``success_url`` attribute on the + workflow, which can take the form of a reversible URL pattern name, + or a standard HTTP URL. """ try: return urlresolvers.reverse(self.success_url) @@ -854,8 +865,10 @@ class Workflow(html.HTMLElement): return self.success_url def format_status_message(self, message): - """Hook to allow customization of the message returned to the user - upon successful or unsuccessful completion of the workflow. + """Hook to allow customization of the message returned to the user. + + This is called upon both successful or unsuccessful completion of + the workflow. By default it simply inserts the workflow's name into the message string. @@ -894,10 +907,13 @@ class Workflow(html.HTMLElement): return self.request.get_full_path().partition('?')[0] def add_error_to_step(self, message, slug): - """Adds an error to the workflow's Step with the - specified slug based on API issues. This is useful - when you wish for API errors to appear as errors on - the form rather than using the messages framework. + """Adds an error message to the workflow's Step. + + This is useful when you wish for API errors to appear as errors + on the form rather than using the messages framework. + + The workflow's Step is specified by its slug. + """ step = self.get_step(slug) if step: diff --git a/horizon/workflows/views.py b/horizon/workflows/views.py index 6053b9f5e0..0ce15a8f10 100644 --- a/horizon/workflows/views.py +++ b/horizon/workflows/views.py @@ -29,8 +29,7 @@ from horizon import messages class WorkflowView(hz_views.ModalBackdropMixin, generic.TemplateView): - """A generic class-based view which handles the intricacies of workflow - processing with minimal user configuration. + """A generic view which handles the intricacies of workflow processing. .. attribute:: workflow_class @@ -67,8 +66,10 @@ class WorkflowView(hz_views.ModalBackdropMixin, generic.TemplateView): "on %s." % self.__class__.__name__) def get_initial(self): - """Returns initial data for the workflow. Defaults to using the GET - parameters to allow pre-seeding of the workflow context values. + """Returns initial data for the workflow. + + Defaults to using the GET parameters + to allow pre-seeding of the workflow context values. """ return copy.copy(self.request.GET) @@ -102,8 +103,10 @@ class WorkflowView(hz_views.ModalBackdropMixin, generic.TemplateView): return context def get_layout(self): - """returns classes for the workflow element in template based on - the workflow characteristics + """Returns classes for the workflow element in template. + + The returned classes are determied based on + the workflow characteristics. """ if self.request.is_ajax(): layout = ['modal', ]