Update common docstrings to match guidelines

Per http://docs.openstack.org/developer/hacking/
and http://www.python.org/dev/peps/pep-0257/

Without extra blank line in multi-line docstrings
based on http://lists.openstack.org/pipermail/openstack-dev/2014-February/028156.html

Blueprint: reduce-flake8-ignored-rules

Change-Id: Ic970fff3b66c357170f45e817d080fdb37c63d6e
This commit is contained in:
Ziad Sawalha 2014-02-14 01:38:42 -06:00 committed by Ziad Sawalha
parent ad2d94980a
commit c1273677f9
7 changed files with 120 additions and 131 deletions

View File

@ -20,9 +20,7 @@ SECTIONS = (PARAMETERS, RESOURCE_REGISTRY) = \
def parse(env_str):
'''
Takes a string and returns a dict containing the parsed structure.
'''
"""Takes a string and returns a dict containing the parsed structure."""
if env_str is None:
return {}
@ -45,9 +43,7 @@ def parse(env_str):
def default_for_missing(env):
'''
Checks a parsed environment for missing sections.
'''
"""Checks a parsed environment for missing sections."""
for param in SECTIONS:
if param not in env:
env[param] = {}

View File

@ -11,6 +11,8 @@
# License for the specific language governing permissions and limitations
# under the License.
"""Keystone Client functionality for use by resources."""
from collections import namedtuple
import json
import uuid
@ -40,13 +42,14 @@ cfg.CONF.register_opts(keystone_opts)
class KeystoneClientV3(object):
"""
Wrap keystone client so we can encapsulate logic used in resources
"""Wrap keystone client so we can encapsulate logic used in resources.
Note this is intended to be initialized from a resource on a per-session
basis, so the session context is passed in on initialization
Also note that a copy of this is created every resource as self.keystone()
via the code in engine/client.py, so there should not be any need to
directly instantiate instances of this class inside resources themselves
directly instantiate instances of this class inside resources themselves.
"""
def __init__(self, context):
@ -217,13 +220,14 @@ class KeystoneClientV3(object):
return getattr(cfg.CONF.clients, option)
def create_trust_context(self):
"""
Create a trust using the trustor identity in the current context,
with the trustee as the heat service user and return a context
containing the new trust_id.
"""Create a trust using the trustor identity in the current context.
The trust is created with the trustee as the heat service user.
If the current context already contains a trust_id, we do nothing
and return the current context.
Returns a context containing the new trust_id.
"""
if self.context.trust_id:
return self.context
@ -248,9 +252,7 @@ class KeystoneClientV3(object):
return trust_context
def delete_trust(self, trust_id):
"""
Delete the specified trust.
"""
"""Delete the specified trust."""
try:
self.client.trusts.delete(trust_id)
except kc_exception.NotFound:
@ -274,11 +276,13 @@ class KeystoneClientV3(object):
return stack_user_role[0].id
def create_stack_user(self, username, password=''):
"""
Create a user defined as part of a stack, either via template
or created internally by a resource. This user will be added to
the heat_stack_user_role as defined in the config
Returns the keystone ID of the resulting user
"""Create a user defined as part of a stack.
The user is defined either via template or created internally by a
resource. This user will be added to the heat_stack_user_role as
defined in the config.
Returns the keystone ID of the resulting user.
"""
# FIXME(shardy): There's duplicated logic between here and
# create_stack_domain user, but this function is expected to
@ -307,12 +311,14 @@ class KeystoneClientV3(object):
return user.id
def create_stack_domain_user(self, username, project_id, password=None):
"""
Create a user defined as part of a stack, either via template
or created internally by a resource. This user will be added to
the heat_stack_user_role as defined in the config, and created in
the specified project (which is expected to be in the stack_domain.
Returns the keystone ID of the resulting user
"""Create a domain user defined as part of a stack.
The user is defined either via template or created internally by a
resource. This user will be added to the heat_stack_user_role as
defined in the config, and created in the specified project (which is
expected to be in the stack_domain).
Returns the keystone ID of the resulting user.
"""
if not self.stack_domain_id:
# FIXME(shardy): Legacy fallback for folks using old heat.conf
@ -320,7 +326,6 @@ class KeystoneClientV3(object):
logger.warning(_('Falling back to legacy non-domain user create, '
'configure domain in heat.conf'))
return self.create_stack_user(username=username, password=password)
# We add the new user to a special keystone role
# This role is designed to allow easier differentiation of the
# heat-generated "stack users" which will generally have credentials
@ -347,7 +352,7 @@ class KeystoneClientV3(object):
return user.id
def _check_stack_domain_user(self, user_id, project_id, action):
# Sanity check that domain/project is correct
"""Sanity check that domain/project is correct."""
user = self.domain_admin_client.users.get(user_id)
if user.domain_id != self.stack_domain_id:
raise ValueError(_('User %s in invalid domain') % action)
@ -375,7 +380,7 @@ class KeystoneClientV3(object):
pass
def create_stack_domain_project(self, stack_id):
'''Creates a project in the heat stack-user domain.'''
"""Create a project in the heat stack-user domain."""
if not self.stack_domain_id:
# FIXME(shardy): Legacy fallback for folks using old heat.conf
# files which lack domain configuration
@ -405,7 +410,7 @@ class KeystoneClientV3(object):
pass
def _find_ec2_keypair(self, access, user_id=None):
'''Lookup an ec2 keypair by access ID.'''
"""Lookup an ec2 keypair by access ID."""
# FIXME(shardy): add filtering for user_id when keystoneclient
# extensible-crud-manager-operations bp lands
credentials = self.client.credentials.list()
@ -418,7 +423,7 @@ class KeystoneClientV3(object):
def delete_ec2_keypair(self, credential_id=None, access=None,
user_id=None):
'''Delete credential containing ec2 keypair.'''
"""Delete credential containing ec2 keypair."""
if credential_id:
try:
self.client.credentials.delete(credential_id)
@ -432,7 +437,7 @@ class KeystoneClientV3(object):
raise ValueError("Must specify either credential_id or access")
def get_ec2_keypair(self, credential_id=None, access=None, user_id=None):
'''Get an ec2 keypair via v3/credentials, by id or access.'''
"""Get an ec2 keypair via v3/credentials, by id or access."""
# Note v3/credentials does not support filtering by access
# because it's stored in the credential blob, so we expect
# all resources to pass credential_id except where backwards
@ -533,10 +538,13 @@ class KeystoneClientV3(object):
class KeystoneClient(object):
"""
"""Keystone Auth Client.
Delay choosing the backend client module until the client's class
needs to be initialized.
"""
def __new__(cls, context):
if cfg.CONF.keystone_backend == _default_keystone_backend:
return KeystoneClientV3(context)

View File

@ -29,11 +29,12 @@ class HeatIdentifier(collections.Mapping):
path_re = re.compile(r'stacks/([^/]+)/([^/]+)(.*)')
def __init__(self, tenant, stack_name, stack_id, path=''):
'''
Initialise a HeatIdentifier from a Tenant ID, Stack name, Stack ID
"""Initialise a HeatIdentifier.
Identifier is initialized from a Tenant ID, Stack name, Stack ID
and optional path. If a path is supplied and it does not begin with
"/", a "/" will be prepended.
'''
"""
if path and not path.startswith('/'):
path = '/' + path
@ -49,9 +50,7 @@ class HeatIdentifier(collections.Mapping):
@classmethod
def from_arn(cls, arn):
'''
Return a new HeatIdentifier generated by parsing the supplied ARN.
'''
"""Generate a new HeatIdentifier by parsing the supplied ARN."""
fields = arn.split(':')
if len(fields) < 6 or fields[0].lower() != 'arn':
raise ValueError(_('"%s" is not a valid ARN') % arn)
@ -69,10 +68,10 @@ class HeatIdentifier(collections.Mapping):
@classmethod
def from_arn_url(cls, url):
'''
Return a new HeatIdentifier generated by parsing the supplied URL
The URL is expected to contain a valid arn as part of the path
'''
"""Generate a new HeatIdentifier by parsing the supplied URL.
The URL is expected to contain a valid arn as part of the path.
"""
# Sanity check the URL
urlp = urlparse.urlparse(url)
if (urlp.scheme not in ('http', 'https') or
@ -90,68 +89,65 @@ class HeatIdentifier(collections.Mapping):
return cls.from_arn(arn)
def arn(self):
'''
Return an ARN of the form:
"""Return as an ARN.
Returned in the form:
arn:openstack:heat::<tenant>:stacks/<stack_name>/<stack_id><path>
'''
"""
return 'arn:openstack:heat::%s:%s' % (urlparse.quote(self.tenant, ''),
self._tenant_path())
def arn_url_path(self):
'''
Return an ARN quoted correctly for use in a URL
'''
"""Return an ARN quoted correctly for use in a URL."""
return '/' + urlparse.quote(self.arn(), '')
def url_path(self):
'''
Return a URL-encoded path segment of a URL in the form:
"""Return a URL-encoded path segment of a URL.
Returned in the form:
<tenant>/stacks/<stack_name>/<stack_id><path>
'''
"""
return '/'.join((urlparse.quote(self.tenant, ''), self._tenant_path()))
def _tenant_path(self):
'''
Return a URL-encoded path segment of a URL within a particular tenant,
in the form:
"""URL-encoded path segment of a URL within a particular tenant.
Returned in the form:
stacks/<stack_name>/<stack_id><path>
'''
"""
return 'stacks/%s%s' % (self.stack_path(),
urlparse.quote(strutils.safe_encode(
self.path)))
def stack_path(self):
'''
Return a URL-encoded path segment of a URL,
in the form:
"""Return a URL-encoded path segment of a URL without a tenant.
Returned in the form:
<stack_name>/<stack_id>
'''
"""
return '%s/%s' % (urlparse.quote(self.stack_name, ''),
urlparse.quote(self.stack_id, ''))
def _path_components(self):
'''Return a list of the path components.'''
"""Return a list of the path components."""
return self.path.lstrip('/').split('/')
def __getattr__(self, attr):
'''
Return one of the components of the identity when accessed as an
attribute.
'''
"""Return a component of the identity when accessed as an attribute."""
if attr not in self.FIELDS:
raise AttributeError(_('Unknown attribute "%s"') % attr)
return self.identity[attr]
def __getitem__(self, key):
'''Return one of the components of the identity.'''
"""Return one of the components of the identity."""
if key not in self.FIELDS:
raise KeyError(_('Unknown attribute "%s"') % key)
return self.identity[key]
def __len__(self):
'''Return the number of components in an identity.'''
"""Return the number of components in an identity."""
return len(self.FIELDS)
def __contains__(self, key):
@ -165,16 +161,18 @@ class HeatIdentifier(collections.Mapping):
class ResourceIdentifier(HeatIdentifier):
'''An identifier for a resource.'''
"""An identifier for a resource."""
RESOURCE_NAME = 'resource_name'
def __init__(self, tenant, stack_name, stack_id, path,
resource_name=None):
'''
Return a new Resource identifier based on the identifier components of
"""Initialise a new Resource identifier.
The identifier is based on the identifier components of
the owning stack and the resource name.
'''
"""
if resource_name is not None:
if '/' in resource_name:
raise ValueError(_('Resource name may not contain "/"'))
@ -185,10 +183,7 @@ class ResourceIdentifier(HeatIdentifier):
path)
def __getattr__(self, attr):
'''
Return one of the components of the identity when accessed as an
attribute.
'''
"""Return a component of the identity when accessed as an attribute."""
if attr == self.RESOURCE_NAME:
return self._path_components()[-1]
@ -196,24 +191,24 @@ class ResourceIdentifier(HeatIdentifier):
return HeatIdentifier.__getattr__(self, attr)
def stack(self):
'''
Return a HeatIdentifier for the owning stack
'''
"""Return a HeatIdentifier for the owning stack."""
return HeatIdentifier(self.tenant, self.stack_name, self.stack_id,
'/'.join(self._path_components()[:-2]))
class EventIdentifier(HeatIdentifier):
'''An identifier for an event.'''
"""An identifier for an event."""
(RESOURCE_NAME, EVENT_ID) = (ResourceIdentifier.RESOURCE_NAME, 'event_id')
def __init__(self, tenant, stack_name, stack_id, path,
event_id=None):
'''
Return a new Event identifier based on the identifier components of
"""Initialise a new Event identifier based on components.
The identifier is based on the identifier components of
the associated resource and the event ID.
'''
"""
if event_id is not None:
path = '/'.join([path.rstrip('/'), 'events', event_id])
super(EventIdentifier, self).__init__(tenant,
@ -222,10 +217,7 @@ class EventIdentifier(HeatIdentifier):
path)
def __getattr__(self, attr):
'''
Return one of the components of the identity when accessed as an
attribute.
'''
"""Return a component of the identity when accessed as an attribute."""
if attr == self.RESOURCE_NAME:
return getattr(self.resource(), attr)
@ -235,14 +227,10 @@ class EventIdentifier(HeatIdentifier):
return HeatIdentifier.__getattr__(self, attr)
def resource(self):
'''
Return a HeatIdentifier for the owning resource
'''
"""Return a HeatIdentifier for the owning resource."""
return ResourceIdentifier(self.tenant, self.stack_name, self.stack_id,
'/'.join(self._path_components()[:-2]))
def stack(self):
'''
Return a HeatIdentifier for the owning stack
'''
"""Return a HeatIdentifier for the owning stack."""
return self.resource().stack()

View File

@ -11,14 +11,13 @@
# License for the specific language governing permissions and limitations
# under the License.
'''
Utilities to dynamically load plugin modules.
"""Utilities to dynamically load plugin modules.
Modules imported this way remain accessible to static imports, regardless of
the order in which they are imported. For modules that are not part of an
existing package tree, use create_subpackage() to dynamically create a package
for them before loading them.
'''
"""
import pkgutil
import sys
@ -31,19 +30,18 @@ logger = logging.getLogger(__name__)
def _module_name(*components):
'''Assemble a fully-qualified module name from its components.'''
"""Assemble a fully-qualified module name from its components."""
return '.'.join(components)
def create_subpackage(path, parent_package_name, subpackage_name="plugins"):
'''
Dynamically create a package into which to load plugins.
"""Dynamically create a package into which to load plugins.
This allows us to not include an __init__.py in the plugins directory. We
must still create a package for plugins to go in, otherwise we get warning
messages during import. This also provides a convenient place to store the
path(s) to the plugins directory.
'''
"""
package_name = _module_name(parent_package_name, subpackage_name)
package = types.ModuleType(package_name)
@ -54,10 +52,12 @@ def create_subpackage(path, parent_package_name, subpackage_name="plugins"):
def _import_module(importer, module_name, package):
'''
Import a module dynamically into the specified package, given its name and
PEP302 Importer object (which knows the path to look in).
'''
"""Import a module dynamically into a package.
:param importer: PEP302 Importer object (which knows the path to look in).
:param module_name: the name of the module to import.
:param package: the package to import the module into.
"""
# Duplicate copies of modules are bad, so check if this has already been
# imported statically
@ -80,7 +80,7 @@ def _import_module(importer, module_name, package):
def load_modules(package, ignore_error=False):
'''Dynamically load all modules from a given package.'''
"""Dynamically load all modules from a given package."""
path = package.__path__
pkg_prefix = package.__name__ + '.'

View File

@ -11,10 +11,10 @@
# License for the specific language governing permissions and limitations
# under the License.
'''
Utilities for creating short ID strings based on a random UUID. The IDs
each comprise 12 (lower-case) alphanumeric characters.
'''
"""Utilities for creating short ID strings based on a random UUID.
The IDs each comprise 12 (lower-case) alphanumeric characters.
"""
import base64
import uuid
@ -23,21 +23,21 @@ from six.moves import xrange
def _to_byte_string(value, num_bits):
'''
Convert an integer to a big-endian string of bytes, with any padding
required added at the end (i.e. after the least-significant bit).
'''
"""Convert an integer to a big-endian string of bytes with padding.
Padding is added at the end (i.e. after the least-significant bit) if
required.
"""
shifts = xrange(num_bits - 8, -8, -8)
byte_at = lambda off: (value >> off if off >= 0 else value << -off) & 0xff
return ''.join(chr(byte_at(offset)) for offset in shifts)
def get_id(source_uuid):
'''
Derive a short (12 character) id from a random UUID.
"""Derive a short (12 character) id from a random UUID.
The supplied UUID must be a version 4 UUID object.
'''
"""
if isinstance(source_uuid, basestring):
source_uuid = uuid.UUID(source_uuid)
if source_uuid.version != 4:
@ -53,7 +53,5 @@ def get_id(source_uuid):
def generate_id():
'''
Generate a short (12 character), random id.
'''
"""Generate a short (12 character), random id."""
return get_id(uuid.uuid4())

View File

@ -62,11 +62,11 @@ def simple_parse(tmpl_str):
def parse(tmpl_str):
'''
Takes a string and returns a dict containing the parsed structure.
"""Takes a string and returns a dict containing the parsed structure.
This includes determination of whether the string is using the
JSON or YAML format.
'''
"""
if len(tmpl_str) > cfg.CONF.max_template_size:
msg = (_('Template exceeds maximum allowed size (%s bytes)') %
cfg.CONF.max_template_size)
@ -84,9 +84,11 @@ def parse(tmpl_str):
def convert_json_to_yaml(json_str):
'''Convert a string containing the AWS JSON template format
to an equivalent string containing the Heat YAML format.
'''
"""Convert AWS JSON template format to Heat YAML format.
:param json_str: a string containing the AWS JSON template format.
:returns: the equivalent string containing the Heat YAML format.
"""
# Replace AWS format version with Heat format version
json_str = re.sub('"AWSTemplateFormatVersion"\s*:\s*"[^"]+"\s*,',

View File

@ -11,9 +11,7 @@
# License for the specific language governing permissions and limitations
# under the License.
'''
Utility for fetching a resource (e.g. a template) from a URL.
'''
"""Utility for fetching a resource (e.g. a template) from a URL."""
from oslo.config import cfg
import requests
@ -35,14 +33,13 @@ class URLFetchError(exception.Error, IOError):
def get(url, allowed_schemes=('http', 'https')):
'''
Get the data at the specifier URL.
"""Get the data at the specified URL.
The URL must use the http: or https: schemes.
The file: scheme is also supported if you override
the allowed_schemes argument.
Raise an IOError if getting the data fails.
'''
"""
logger.info(_('Fetching data from %s') % url)
components = urllib.parse.urlparse(url)