From dc5c33a9e52a062cd6742986f780a48ee2d6b383 Mon Sep 17 00:00:00 2001 From: Dirk Mueller Date: Sun, 4 Aug 2013 23:10:16 +0200 Subject: [PATCH] Fix and enable Gating on H404 Enable gating on the Hacking H404 check - docstring should start with a summary. Change-Id: I80612a15bd11f689e9e9f4dc2ff812138630ddbd --- keystoneclient/base.py | 26 ++++++---------- keystoneclient/contrib/ec2/utils.py | 26 ++++++++-------- keystoneclient/exceptions.py | 47 +++++++++++------------------ keystoneclient/shell.py | 8 ++--- keystoneclient/utils.py | 13 ++++---- keystoneclient/v2_0/ec2.py | 16 +++++----- keystoneclient/v2_0/roles.py | 12 ++------ keystoneclient/v2_0/tenants.py | 16 +++------- keystoneclient/v2_0/users.py | 30 +++++------------- tests/fakes.py | 8 ++--- tests/test_ec2utils.py | 19 +++++------- tests/test_keyring.py | 3 +- tox.ini | 2 +- 13 files changed, 81 insertions(+), 145 deletions(-) diff --git a/keystoneclient/base.py b/keystoneclient/base.py index efc8bd013..961c9dff8 100644 --- a/keystoneclient/base.py +++ b/keystoneclient/base.py @@ -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 diff --git a/keystoneclient/contrib/ec2/utils.py b/keystoneclient/contrib/ec2/utils.py index a73bef3ce..29a01f5bf 100644 --- a/keystoneclient/contrib/ec2/utils.py +++ b/keystoneclient/contrib/ec2/utils.py @@ -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 diff --git a/keystoneclient/exceptions.py b/keystoneclient/exceptions.py index d3a7c3212..4c33346a9 100644 --- a/keystoneclient/exceptions.py +++ b/keystoneclient/exceptions.py @@ -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:: diff --git a/keystoneclient/shell.py b/keystoneclient/shell.py index a0c27f8dc..c59dc68a8 100644 --- a/keystoneclient/shell.py +++ b/keystoneclient/shell.py @@ -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='', nargs='?', help='Display help for ') 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() diff --git a/keystoneclient/utils.py b/keystoneclient/utils.py index f09e76e37..12d9eeac4 100644 --- a/keystoneclient/utils.py +++ b/keystoneclient/utils.py @@ -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 diff --git a/keystoneclient/v2_0/ec2.py b/keystoneclient/v2_0/ec2.py index e0f48829c..51c6de613 100644 --- a/keystoneclient/v2_0/ec2.py +++ b/keystoneclient/v2_0/ec2.py @@ -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))) diff --git a/keystoneclient/v2_0/roles.py b/keystoneclient/v2_0/roles.py index a74021115..2549bd025 100644 --- a/keystoneclient/v2_0/roles.py +++ b/keystoneclient/v2_0/roles.py @@ -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): diff --git a/keystoneclient/v2_0/tenants.py b/keystoneclient/v2_0/tenants.py index cecec830e..6eac981e3 100644 --- a/keystoneclient/v2_0/tenants.py +++ b/keystoneclient/v2_0/tenants.py @@ -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): diff --git a/keystoneclient/v2_0/users.py b/keystoneclient/v2_0/users.py index 227ebee86..f6d0a0bd6 100644 --- a/keystoneclient/v2_0/users.py +++ b/keystoneclient/v2_0/users.py @@ -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` """ diff --git a/tests/fakes.py b/tests/fakes.py index de7ecffaa..57391f03f 100644 --- a/tests/fakes.py +++ b/tests/fakes.py @@ -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." % diff --git a/tests/test_ec2utils.py b/tests/test_ec2utils.py index 7f35898cb..a9ef36a54 100644 --- a/tests/test_ec2utils.py +++ b/tests/test_ec2utils.py @@ -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' diff --git a/tests/test_keyring.py b/tests/test_keyring.py index cc15b13d5..4b647db6f 100644 --- a/tests/test_keyring.py +++ b/tests/test_keyring.py @@ -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, diff --git a/tox.ini b/tox.ini index a79e8fecd..505437162 100644 --- a/tox.ini +++ b/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