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:
parent
ad2d94980a
commit
c1273677f9
|
@ -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] = {}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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__ + '.'
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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*,',
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue