Fix and enable Gating on H404
Enable gating on the Hacking H404 check - docstring should start with a summary. Change-Id: I80612a15bd11f689e9e9f4dc2ff812138630ddbd
This commit is contained in:

committed by
Morgan Fainberg

parent
0e11cf03cf
commit
dc5c33a9e5
@@ -34,9 +34,8 @@ except NameError:
|
||||
|
||||
|
||||
def getid(obj):
|
||||
"""
|
||||
Abstracts the common pattern of allowing both an object or an object's ID
|
||||
(UUID) as a parameter when dealing with relationships.
|
||||
"""Abstracts the common pattern of allowing both an object or an object's
|
||||
ID (UUID) as a parameter when dealing with relationships.
|
||||
"""
|
||||
|
||||
# Try to return the object's UUID first, if we have a UUID.
|
||||
@@ -75,9 +74,8 @@ def filter_kwargs(f):
|
||||
|
||||
|
||||
class Manager(object):
|
||||
"""
|
||||
Managers interact with a particular type of API (servers, flavors, images,
|
||||
etc.) and provide CRUD operations for them.
|
||||
"""Managers interact with a particular type of API (servers, flavors,
|
||||
images, etc.) and provide CRUD operations for them.
|
||||
"""
|
||||
resource_class = None
|
||||
|
||||
@@ -138,8 +136,7 @@ class Manager(object):
|
||||
|
||||
|
||||
class ManagerWithFind(Manager):
|
||||
"""
|
||||
Like a `Manager`, but with additional `find()`/`findall()` methods.
|
||||
"""Like a `Manager`, but with additional `find()`/`findall()` methods.
|
||||
"""
|
||||
|
||||
__metaclass__ = abc.ABCMeta
|
||||
@@ -149,8 +146,7 @@ class ManagerWithFind(Manager):
|
||||
pass
|
||||
|
||||
def find(self, **kwargs):
|
||||
"""
|
||||
Find a single item with attributes matching ``**kwargs``.
|
||||
"""Find a single item with attributes matching ``**kwargs``.
|
||||
|
||||
This isn't very efficient: it loads the entire list then filters on
|
||||
the Python side.
|
||||
@@ -167,8 +163,7 @@ class ManagerWithFind(Manager):
|
||||
return rl[0]
|
||||
|
||||
def findall(self, **kwargs):
|
||||
"""
|
||||
Find all items with attributes matching ``**kwargs``.
|
||||
"""Find all items with attributes matching ``**kwargs``.
|
||||
|
||||
This isn't very efficient: it loads the entire list then filters on
|
||||
the Python side.
|
||||
@@ -287,9 +282,7 @@ class CrudManager(Manager):
|
||||
|
||||
@filter_kwargs
|
||||
def find(self, **kwargs):
|
||||
"""
|
||||
Find a single item with attributes matching ``**kwargs``.
|
||||
"""
|
||||
"""Find a single item with attributes matching ``**kwargs``."""
|
||||
url = self.build_url(dict_args_in_out=kwargs)
|
||||
|
||||
rl = self._list(
|
||||
@@ -310,8 +303,7 @@ class CrudManager(Manager):
|
||||
|
||||
|
||||
class Resource(object):
|
||||
"""
|
||||
A resource represents a particular instance of an object (tenant, user,
|
||||
"""A resource represents a particular instance of an object (tenant, user,
|
||||
etc). This is pretty much just a bag for attributes.
|
||||
|
||||
:param manager: Manager object
|
||||
|
@@ -28,10 +28,9 @@ import six
|
||||
|
||||
|
||||
class Ec2Signer(object):
|
||||
"""
|
||||
Utility class which adds allows a request to be signed with an AWS style
|
||||
"""Utility class which adds allows a request to be signed with an AWS style
|
||||
signature, which can then be used for authentication via the keystone ec2
|
||||
authentication extension
|
||||
authentication extension.
|
||||
"""
|
||||
|
||||
def __init__(self, secret_key):
|
||||
@@ -41,9 +40,9 @@ class Ec2Signer(object):
|
||||
self.hmac_256 = hmac.new(self.secret_key, digestmod=hashlib.sha256)
|
||||
|
||||
def _v4_creds(self, credentials):
|
||||
"""
|
||||
Detect if the credentials are for a v4 signed request, since AWS
|
||||
"""Detect if the credentials are for a v4 signed request, since AWS
|
||||
removed the SignatureVersion field from the v4 request spec...
|
||||
|
||||
This expects a dict of the request headers to be passed in the
|
||||
credentials dict, since the recommended way to pass v4 creds is
|
||||
via the 'Authorization' header
|
||||
@@ -127,9 +126,8 @@ class Ec2Signer(object):
|
||||
|
||||
@staticmethod
|
||||
def _canonical_qs(params):
|
||||
"""
|
||||
Construct a sorted, correctly encoded query string as required for
|
||||
_calc_signature_2 and _calc_signature_4
|
||||
"""Construct a sorted, correctly encoded query string as required for
|
||||
_calc_signature_2 and _calc_signature_4.
|
||||
"""
|
||||
keys = params.keys()
|
||||
keys.sort()
|
||||
@@ -164,8 +162,7 @@ class Ec2Signer(object):
|
||||
hashlib.sha256).digest()
|
||||
|
||||
def signature_key(datestamp, region_name, service_name):
|
||||
"""
|
||||
Signature key derivation, see
|
||||
"""Signature key derivation, see
|
||||
http://docs.aws.amazon.com/general/latest/gr/
|
||||
signature-v4-examples.html#signature-v4-examples-python
|
||||
"""
|
||||
@@ -177,8 +174,9 @@ class Ec2Signer(object):
|
||||
return k_signing
|
||||
|
||||
def auth_param(param_name):
|
||||
"""
|
||||
Get specified auth parameter, provided via one of:
|
||||
"""Get specified auth parameter.
|
||||
|
||||
Provided via one of:
|
||||
- the Authorization header
|
||||
- the X-Amz-* query parameters
|
||||
"""
|
||||
@@ -191,8 +189,8 @@ class Ec2Signer(object):
|
||||
return param_str
|
||||
|
||||
def date_param():
|
||||
"""
|
||||
Get the X-Amz-Date' value, which can be either a header or paramter
|
||||
"""Get the X-Amz-Date' value, which can be either a header
|
||||
or parameter.
|
||||
|
||||
Note AWS supports parsing the Date header also, but this is not
|
||||
currently supported here as it will require some format mangling
|
||||
|
@@ -42,9 +42,8 @@ class NoUniqueMatch(Exception):
|
||||
|
||||
|
||||
class ClientException(Exception):
|
||||
"""
|
||||
The base exception class for all exceptions this library raises.
|
||||
"""
|
||||
"""The base exception class for all exceptions this library raises."""
|
||||
|
||||
def __init__(self, code, message=None, details=None):
|
||||
self.code = code
|
||||
self.message = message or self.__class__.message
|
||||
@@ -55,24 +54,21 @@ class ClientException(Exception):
|
||||
|
||||
|
||||
class BadRequest(ClientException):
|
||||
"""
|
||||
HTTP 400 - Bad request: you sent some malformed data.
|
||||
"""
|
||||
"""HTTP 400 - Bad request: you sent some malformed data."""
|
||||
|
||||
http_status = 400
|
||||
message = "Bad request"
|
||||
|
||||
|
||||
class Unauthorized(ClientException):
|
||||
"""
|
||||
HTTP 401 - Unauthorized: bad credentials.
|
||||
"""
|
||||
"""HTTP 401 - Unauthorized: bad credentials."""
|
||||
|
||||
http_status = 401
|
||||
message = "Unauthorized"
|
||||
|
||||
|
||||
class Forbidden(ClientException):
|
||||
"""
|
||||
HTTP 403 - Forbidden: your credentials don't give you access to this
|
||||
"""HTTP 403 - Forbidden: your credentials do not allow access to this
|
||||
resource.
|
||||
"""
|
||||
http_status = 403
|
||||
@@ -80,32 +76,26 @@ class Forbidden(ClientException):
|
||||
|
||||
|
||||
class NotFound(ClientException):
|
||||
"""
|
||||
HTTP 404 - Not found
|
||||
"""
|
||||
"""HTTP 404 - Not found."""
|
||||
http_status = 404
|
||||
message = "Not found"
|
||||
|
||||
|
||||
class MethodNotAllowed(ClientException):
|
||||
"""
|
||||
HTTP 405 - Method not allowed
|
||||
"""
|
||||
"""HTTP 405 - Method not allowed."""
|
||||
http_status = 405
|
||||
message = "Method not allowed"
|
||||
|
||||
|
||||
class Conflict(ClientException):
|
||||
"""
|
||||
HTTP 409 - Conflict
|
||||
"""
|
||||
"""HTTP 409 - Conflict."""
|
||||
http_status = 409
|
||||
message = "Conflict"
|
||||
|
||||
|
||||
class OverLimit(ClientException):
|
||||
"""
|
||||
HTTP 413 - Over limit: you're over the API limits for this time period.
|
||||
"""HTTP 413 - Over limit: you're over the API limits for this time
|
||||
period.
|
||||
"""
|
||||
http_status = 413
|
||||
message = "Over limit"
|
||||
@@ -113,17 +103,15 @@ class OverLimit(ClientException):
|
||||
|
||||
# NotImplemented is a python keyword.
|
||||
class HTTPNotImplemented(ClientException):
|
||||
"""
|
||||
HTTP 501 - Not Implemented: the server does not support this operation.
|
||||
"""HTTP 501 - Not Implemented: the server does not support this
|
||||
operation.
|
||||
"""
|
||||
http_status = 501
|
||||
message = "Not Implemented"
|
||||
|
||||
|
||||
class ServiceUnavailable(ClientException):
|
||||
"""
|
||||
HTTP 503 - Service Unavailable: The server is currently unavailable.
|
||||
"""
|
||||
"""HTTP 503 - Service Unavailable: The server is currently unavailable."""
|
||||
http_status = 503
|
||||
message = "Service Unavailable"
|
||||
|
||||
@@ -146,9 +134,8 @@ _code_map = dict((c.http_status, c) for c in [BadRequest,
|
||||
|
||||
|
||||
def from_response(response, body=None):
|
||||
"""
|
||||
Return an instance of an ClientException or subclass
|
||||
based on an requests response.
|
||||
"""Return an instance of a ClientException or subclass
|
||||
based on a requests response.
|
||||
|
||||
Usage::
|
||||
|
||||
|
@@ -442,8 +442,8 @@ class OpenStackIdentityShell(object):
|
||||
return shell_v2_0.CLIENT_CLASS
|
||||
|
||||
def do_bash_completion(self, args):
|
||||
"""
|
||||
Prints all of the commands and options to stdout.
|
||||
"""Prints all of the commands and options to stdout.
|
||||
|
||||
The keystone.bash_completion script doesn't have to hard code them.
|
||||
"""
|
||||
commands = set()
|
||||
@@ -460,9 +460,7 @@ class OpenStackIdentityShell(object):
|
||||
@utils.arg('command', metavar='<subcommand>', nargs='?',
|
||||
help='Display help for <subcommand>')
|
||||
def do_help(self, args):
|
||||
"""
|
||||
Display help about this program or one of its subcommands.
|
||||
"""
|
||||
"""Display help about this program or one of its subcommands."""
|
||||
if getattr(args, 'command', None):
|
||||
if args.command in self.subcommands:
|
||||
self.subcommands[args.command].print_help()
|
||||
|
@@ -113,10 +113,10 @@ def unauthenticated(f):
|
||||
|
||||
|
||||
def isunauthenticated(f):
|
||||
"""
|
||||
Checks to see if the function is marked as not requiring authentication
|
||||
with the @unauthenticated decorator. Returns True if decorator is
|
||||
set to True, False otherwise.
|
||||
"""Checks to see if the function is marked as not requiring authentication
|
||||
with the @unauthenticated decorator.
|
||||
|
||||
Returns True if decorator is set to True, False otherwise.
|
||||
"""
|
||||
return getattr(f, 'unauthenticated', False)
|
||||
|
||||
@@ -135,9 +135,8 @@ def hash_signed_token(signed_text):
|
||||
|
||||
|
||||
def prompt_for_password():
|
||||
"""
|
||||
Prompt user for password if not provided so the password
|
||||
doesn't show up in the bash history.
|
||||
"""Prompt user for password if not provided so the password
|
||||
doesn't show up in the bash history.
|
||||
"""
|
||||
if not (hasattr(sys.stdin, 'isatty') and sys.stdin.isatty()):
|
||||
# nothing to do
|
||||
|
@@ -28,8 +28,8 @@ class CredentialsManager(base.ManagerWithFind):
|
||||
resource_class = EC2
|
||||
|
||||
def create(self, user_id, tenant_id):
|
||||
"""
|
||||
Create a new access/secret pair for the user/tenant pair
|
||||
"""Create a new access/secret pair for the user/tenant pair.
|
||||
|
||||
:rtype: object of type :class:`EC2`
|
||||
"""
|
||||
|
||||
@@ -39,24 +39,22 @@ class CredentialsManager(base.ManagerWithFind):
|
||||
params, "credential")
|
||||
|
||||
def list(self, user_id):
|
||||
"""
|
||||
Get a list of access/secret pairs for a user_id
|
||||
"""Get a list of access/secret pairs for a user_id.
|
||||
|
||||
:rtype: list of :class:`EC2`
|
||||
"""
|
||||
return self._list("/users/%s/credentials/OS-EC2" % user_id,
|
||||
"credentials")
|
||||
|
||||
def get(self, user_id, access):
|
||||
"""
|
||||
Get the access/secret pair for a given access key
|
||||
"""Get the access/secret pair for a given access key.
|
||||
|
||||
:rtype: object of type :class:`EC2`
|
||||
"""
|
||||
return self._get("/users/%s/credentials/OS-EC2/%s" %
|
||||
(user_id, base.getid(access)), "credential")
|
||||
|
||||
def delete(self, user_id, access):
|
||||
"""
|
||||
Delete an access/secret pair for a user
|
||||
"""
|
||||
"""Delete an access/secret pair for a user."""
|
||||
return self._delete("/users/%s/credentials/OS-EC2/%s" %
|
||||
(user_id, base.getid(access)))
|
||||
|
@@ -34,22 +34,16 @@ class RoleManager(base.ManagerWithFind):
|
||||
return self._get("/OS-KSADM/roles/%s" % base.getid(role), "role")
|
||||
|
||||
def create(self, name):
|
||||
"""
|
||||
Create a role.
|
||||
"""
|
||||
"""Create a role."""
|
||||
params = {"role": {"name": name}}
|
||||
return self._create('/OS-KSADM/roles', params, "role")
|
||||
|
||||
def delete(self, role):
|
||||
"""
|
||||
Delete a role.
|
||||
"""
|
||||
"""Delete a role."""
|
||||
return self._delete("/OS-KSADM/roles/%s" % base.getid(role))
|
||||
|
||||
def list(self):
|
||||
"""
|
||||
List all available roles.
|
||||
"""
|
||||
"""List all available roles."""
|
||||
return self._list("/OS-KSADM/roles", "roles")
|
||||
|
||||
def roles_for_user(self, user, tenant=None):
|
||||
|
@@ -75,10 +75,7 @@ class TenantManager(base.ManagerWithFind):
|
||||
return self._get("/tenants/%s" % tenant_id, "tenant")
|
||||
|
||||
def create(self, tenant_name, description=None, enabled=True, **kwargs):
|
||||
"""
|
||||
Create a new tenant.
|
||||
|
||||
"""
|
||||
"""Create a new tenant."""
|
||||
params = {"tenant": {"name": tenant_name,
|
||||
"description": description,
|
||||
"enabled": enabled}}
|
||||
@@ -91,8 +88,7 @@ class TenantManager(base.ManagerWithFind):
|
||||
return self._create('/tenants', params, "tenant")
|
||||
|
||||
def list(self, limit=None, marker=None):
|
||||
"""
|
||||
Get a list of tenants.
|
||||
"""Get a list of tenants.
|
||||
|
||||
:param integer limit: maximum number to return. (optional)
|
||||
:param string marker: use when specifying a limit and making
|
||||
@@ -125,9 +121,7 @@ class TenantManager(base.ManagerWithFind):
|
||||
|
||||
def update(self, tenant_id, tenant_name=None, description=None,
|
||||
enabled=None, **kwargs):
|
||||
"""
|
||||
Update a tenant with a new name and description.
|
||||
"""
|
||||
"""Update a tenant with a new name and description."""
|
||||
body = {"tenant": {'id': tenant_id}}
|
||||
if tenant_name is not None:
|
||||
body['tenant']['name'] = tenant_name
|
||||
@@ -145,9 +139,7 @@ class TenantManager(base.ManagerWithFind):
|
||||
return self._create("/tenants/%s" % tenant_id, body, "tenant")
|
||||
|
||||
def delete(self, tenant):
|
||||
"""
|
||||
Delete a tenant.
|
||||
"""
|
||||
"""Delete a tenant."""
|
||||
return self._delete("/tenants/%s" % (base.getid(tenant)))
|
||||
|
||||
def list_users(self, tenant):
|
||||
|
@@ -39,8 +39,7 @@ class UserManager(base.ManagerWithFind):
|
||||
return self._get("/users/%s" % base.getid(user), "user")
|
||||
|
||||
def update(self, user, **kwargs):
|
||||
"""
|
||||
Update user data.
|
||||
"""Update user data.
|
||||
|
||||
Supported arguments include ``name``, ``email``, and ``enabled``.
|
||||
"""
|
||||
@@ -52,9 +51,7 @@ class UserManager(base.ManagerWithFind):
|
||||
return self._update(url, params, "user")
|
||||
|
||||
def update_enabled(self, user, enabled):
|
||||
"""
|
||||
Update enabled-ness
|
||||
"""
|
||||
"""Update enabled-ness."""
|
||||
params = {"user": {"id": base.getid(user),
|
||||
"enabled": enabled}}
|
||||
|
||||
@@ -62,9 +59,7 @@ class UserManager(base.ManagerWithFind):
|
||||
"user")
|
||||
|
||||
def update_password(self, user, password):
|
||||
"""
|
||||
Update password
|
||||
"""
|
||||
"""Update password."""
|
||||
params = {"user": {"id": base.getid(user),
|
||||
"password": password}}
|
||||
|
||||
@@ -72,9 +67,7 @@ class UserManager(base.ManagerWithFind):
|
||||
params, "user")
|
||||
|
||||
def update_own_password(self, origpasswd, passwd):
|
||||
"""
|
||||
Update password
|
||||
"""
|
||||
"""Update password."""
|
||||
params = {"user": {"password": passwd,
|
||||
"original_password": origpasswd}}
|
||||
|
||||
@@ -84,9 +77,7 @@ class UserManager(base.ManagerWithFind):
|
||||
management=False)
|
||||
|
||||
def update_tenant(self, user, tenant):
|
||||
"""
|
||||
Update default tenant.
|
||||
"""
|
||||
"""Update default tenant."""
|
||||
params = {"user": {"id": base.getid(user),
|
||||
"tenantId": base.getid(tenant)}}
|
||||
|
||||
@@ -96,9 +87,7 @@ class UserManager(base.ManagerWithFind):
|
||||
params, "user")
|
||||
|
||||
def create(self, name, password, email, tenant_id=None, enabled=True):
|
||||
"""
|
||||
Create a user.
|
||||
"""
|
||||
"""Create a user."""
|
||||
# FIXME(ja): email should be optional, keystone currently requires it
|
||||
params = {"user": {"name": name,
|
||||
"password": password,
|
||||
@@ -108,14 +97,11 @@ class UserManager(base.ManagerWithFind):
|
||||
return self._create('/users', params, "user")
|
||||
|
||||
def delete(self, user):
|
||||
"""
|
||||
Delete a user.
|
||||
"""
|
||||
"""Delete a user."""
|
||||
return self._delete("/users/%s" % base.getid(user))
|
||||
|
||||
def list(self, tenant_id=None, limit=None, marker=None):
|
||||
"""
|
||||
Get a list of users (optionally limited to a tenant)
|
||||
"""Get a list of users (optionally limited to a tenant).
|
||||
|
||||
:rtype: list of :class:`User`
|
||||
"""
|
||||
|
@@ -23,9 +23,7 @@ def assert_has_keys(dict, required=[], optional=[]):
|
||||
class FakeClient(object):
|
||||
|
||||
def assert_called(self, method, url, body=None, pos=-1):
|
||||
"""
|
||||
Assert than an API method was just called.
|
||||
"""
|
||||
"""Assert than an API method was just called."""
|
||||
expected = (method, url)
|
||||
called = self.callstack[pos][0:2]
|
||||
|
||||
@@ -38,9 +36,7 @@ class FakeClient(object):
|
||||
assert self.callstack[pos][2] == body
|
||||
|
||||
def assert_called_anytime(self, method, url, body=None):
|
||||
"""
|
||||
Assert than an API method was called anytime in the test.
|
||||
"""
|
||||
"""Assert than an API method was called anytime in the test."""
|
||||
expected = (method, url)
|
||||
|
||||
assert self.callstack, ("Expected %s %s but no calls were made." %
|
||||
|
@@ -105,8 +105,9 @@ class Ec2SignerTest(testtools.TestCase):
|
||||
self.assertEqual(signature, expected)
|
||||
|
||||
def test_generate_v4(self):
|
||||
"""
|
||||
Test v4 generator with data from AWS docs example, see:
|
||||
"""Test v4 generator with data from AWS docs example.
|
||||
|
||||
see:
|
||||
http://docs.aws.amazon.com/general/latest/gr/
|
||||
sigv4-create-canonical-request.html
|
||||
and
|
||||
@@ -145,9 +146,7 @@ class Ec2SignerTest(testtools.TestCase):
|
||||
self.assertEqual(signature, expected)
|
||||
|
||||
def test_generate_v4_port(self):
|
||||
"""
|
||||
Test v4 generator with host:port format
|
||||
"""
|
||||
"""Test v4 generator with host:port format."""
|
||||
# Create a new signer object with the AWS example key
|
||||
secret = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'
|
||||
signer = Ec2Signer(secret)
|
||||
@@ -181,10 +180,9 @@ class Ec2SignerTest(testtools.TestCase):
|
||||
self.assertEqual(signature, expected)
|
||||
|
||||
def test_generate_v4_port_strip(self):
|
||||
"""
|
||||
Test v4 generator with host:port format, but for an old
|
||||
"""Test v4 generator with host:port format, but for an old
|
||||
(<2.9.3) version of boto, where the port should be stripped
|
||||
to match boto behavior
|
||||
to match boto behavior.
|
||||
"""
|
||||
# Create a new signer object with the AWS example key
|
||||
secret = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'
|
||||
@@ -220,9 +218,8 @@ class Ec2SignerTest(testtools.TestCase):
|
||||
self.assertEqual(expected, signature)
|
||||
|
||||
def test_generate_v4_port_nostrip(self):
|
||||
"""
|
||||
Test v4 generator with host:port format, but for an new
|
||||
(>=2.9.3) version of boto, where the port should not be stripped
|
||||
"""Test v4 generator with host:port format, but for an new
|
||||
(>=2.9.3) version of boto, where the port should not be stripped.
|
||||
"""
|
||||
# Create a new signer object with the AWS example key
|
||||
secret = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'
|
||||
|
@@ -54,8 +54,7 @@ class KeyringTest(utils.TestCase):
|
||||
keyring.set_keyring(MemoryKeyring())
|
||||
|
||||
def test_no_keyring_key(self):
|
||||
"""
|
||||
Ensure that we get no value back if we don't have use_keyring
|
||||
"""Ensure that we get no value back if we don't have use_keyring
|
||||
set in the client.
|
||||
"""
|
||||
cl = httpclient.HTTPClient(username=USERNAME, password=PASSWORD,
|
||||
|
2
tox.ini
2
tox.ini
@@ -34,6 +34,6 @@ downloadcache = ~/cache/pip
|
||||
# H302: import only modules
|
||||
# H304: no relative imports
|
||||
# H404: multi line docstring should start with a summary
|
||||
ignore = F811,F821,F841,H102,H302,H304,H404
|
||||
ignore = F811,F821,F841,H102,H302,H304
|
||||
show-source = True
|
||||
exclude = .venv,.tox,dist,doc,*egg,build
|
||||
|
Reference in New Issue
Block a user