diff --git a/aiorest_ws/__init__.py b/aiorest_ws/__init__.py index 7a24d8f..a44de1d 100644 --- a/aiorest_ws/__init__.py +++ b/aiorest_ws/__init__.py @@ -11,6 +11,6 @@ __version__ = '1.1.0' __author__ = 'Valeryi Savich' __license__ = 'BSD' -__copyright__ = 'Copyright (c) 2015 by Valeryi Savich' +__copyright__ = 'Copyright (c) 2016 by Valeryi Savich' VERSION = __version__ diff --git a/aiorest_ws/abstract.py b/aiorest_ws/abstract.py index 9eb990b..e020334 100644 --- a/aiorest_ws/abstract.py +++ b/aiorest_ws/abstract.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - Abstract classes for future implementation. +Abstract classes for future implementation. """ from abc import ABCMeta, abstractmethod @@ -11,13 +11,14 @@ class AbstractEndpoint(metaclass=ABCMeta): - """Base class for endpoints.""" - + """ + Base class for endpoints. + """ path = None # URL, used for get access to API - handler = None # class/function for processing request - methods = [] # list of supported methods (GET, POST, etc.) - name = None # short name for route - _pattern = None # pattern, which using for checking path on compatible + handler = None # Class/function for processing request + methods = [] # List of supported methods (GET, POST, etc.) + name = None # Short name for route + _pattern = None # Pattern, which using for checking path on compatible def __init__(self, path, handler, methods, name): self.path = path @@ -30,7 +31,8 @@ def __init__(self, path, handler, methods, name): @abstractmethod def match(self, path): - """Checking path on compatible. + """ + Checking path on compatible. :param path: URL, which used for get access to API. """ @@ -38,7 +40,9 @@ def match(self, path): class AbstractRouter(metaclass=ABCMeta): - """Base class for routers.""" + """ + Base class for routers. + """ _middlewares = [] def __init__(self, *args, **kwargs): @@ -47,12 +51,15 @@ def __init__(self, *args, **kwargs): @property def middlewares(self): - """Get list of used middlewares.""" + """ + Get list of used middlewares. + """ return self._middlewares @abstractmethod def process_request(self, request): - """Handling received request from user. + """ + Handling received request from user. :param request: request from user. """ @@ -60,10 +67,13 @@ def process_request(self, request): class AbstractMiddleware(metaclass=ABCMeta): - """Base class for middlewares.""" + """ + Base class for middlewares. + """ @abstractmethod def process_request(self, request, handler): - """Processing request before calling handler. + """ + Processing request before calling handler. :param request: instance of Request class. :param handler: view, invoked later for the request. @@ -72,10 +82,13 @@ def process_request(self, request, handler): class AbstractPermission(metaclass=ABCMeta): - """Base class for permissions.""" + """ + Base class for permissions. + """ @staticmethod def check(request, handler): - """Check permission method. + """ + Check permission method. :param request: instance of Request class. :param handler: view, invoked later for the request. diff --git a/aiorest_ws/app.py b/aiorest_ws/app.py index 1c39b2b..21dcb19 100644 --- a/aiorest_ws/app.py +++ b/aiorest_ws/app.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - This module implements the central application object. +This module implements the central application object. """ import asyncio import ssl @@ -16,7 +16,9 @@ class Application(object): - """Main application of aiorest-ws framework.""" + """ + Main application of aiorest-ws framework. + """ _factory = RequestHandlerFactory _protocol = RequestHandlerProtocol @@ -25,7 +27,9 @@ class Application(object): _middlewares = [] def __init__(self, *args, **options): - """Initialization of Application instance.""" + """ + Initialization of Application instance. + """ super(Application, self).__init__() self.factory = options.get('factory') self.protocol = options.get('protocol') @@ -38,12 +42,15 @@ def __init__(self, *args, **options): @property def factory(self): - """Get factory class.""" + """ + Get factory class. + """ return self._factory @factory.setter def factory(self, factory): - """Set factory class. + """ + Set factory class. :param factory: subclass of RequestHandlerFactory. """ @@ -53,17 +60,22 @@ def factory(self, factory): @property def middlewares(self): - """Get list of used middlewares.""" + """ + Get list of used middlewares. + """ return self._middlewares @property def protocol(self): - """Get protocol class.""" + """ + Get protocol class. + """ return self._protocol @protocol.setter def protocol(self, protocol): - """Set protocol class. + """ + Set protocol class. :param factory: subclass of RequestHandlerProtocol. """ @@ -73,12 +85,15 @@ def protocol(self, protocol): @property def certificate(self): - """Get filepath to certificate.""" + """ + Get filepath to certificate. + """ return self._certificate @certificate.setter def certificate(self, certificate): - """Setter for certificate. + """ + Setter for certificate. :param certificate: path to certificate file. """ @@ -86,12 +101,15 @@ def certificate(self, certificate): @property def key(self): - """Get private key for certificate.""" + """ + Get private key for certificate. + """ return self._key @key.setter def key(self, key): - """Set private key for certificate. + """ + Set private key for certificate. :param key: private key for certificate. """ @@ -99,20 +117,22 @@ def key(self, key): @property def url(self): - """Get url to WebSocket REST API.""" - if self.isSecure: - url = "wss://{0}:{1}/{2}" - else: - url = "ws://{0}:{1}/{2}" - return url + """ + Get url to WebSocket REST API. + """ + return "wss://{0}:{1}/{2}" if self.isSecure else "ws://{0}:{1}/{2}" @property def isSecure(self): - """Property, which help us to understand, use SSL or not.""" + """ + Property, which help us to understand, use SSL or not. + """ return self.certificate and self.key def _get_ssl_context(self): - """Generating SSL context for asyncio loop.""" + """ + Generating SSL context for asyncio loop. + """ if self.isSecure: ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) ssl_context.load_cert_chain(self.certificate, self.key) @@ -121,7 +141,9 @@ def _get_ssl_context(self): return ssl_context def _init_factory(self, url, **options): - """Create a factory instance.""" + """ + Create a factory instance. + """ debug = options.get('debug', False) factory = self.factory(url, debug=debug) @@ -129,14 +151,18 @@ def _init_factory(self, url, **options): return factory def _enable_compressing(self, factory, **options): - """Set compression message for factory, if defined.""" + """ + Set compression message for factory, if defined. + """ compress = options.get('compress', False) if compress: factory.setProtocolOptions(perMessageCompressionAccept=accept) def _set_factory_router(self, factory, **options): - """Set users router for factory, if defined.""" + """ + Set users router for factory, if defined. + """ router = options.get('router', None) assert router, "Argument `router` must be defined for Application." @@ -144,7 +170,9 @@ def _set_factory_router(self, factory, **options): factory.router._middlewares = self.middlewares def _init_urlconf(self, factory, url, **options): - """Initialize urlconf thread variable.""" + """ + Initialize urlconf thread variable. + """ data = { 'path': url, 'urls': factory.router._urls, @@ -153,7 +181,9 @@ def _init_urlconf(self, factory, url, **options): set_urlconf(data) def generate_factory(self, url, **options): - """Create and initialize factory instance.""" + """ + Create and initialize factory instance. + """ factory = self._init_factory(url, **options) self._enable_compressing(factory, **options) self._set_factory_router(factory, **options) @@ -161,11 +191,14 @@ def generate_factory(self, url, **options): return factory def generate_url(self, host, port, path=''): - """Generate URL to application.""" + """ + Generate URL to application. + """ return self.url.format(host, port, path) def run(self, **options): - """Create and start web server with some IP and PORT. + """ + Create and start web server with some IP and PORT. :param options: parameters, which can be used for configuration of the Application. diff --git a/aiorest_ws/auth/exceptions.py b/aiorest_ws/auth/exceptions.py index f02c592..366c650 100644 --- a/aiorest_ws/auth/exceptions.py +++ b/aiorest_ws/auth/exceptions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - Exception classes for authentication. +Exception classes for authentication. """ from aiorest_ws.exceptions import BaseAPIException diff --git a/aiorest_ws/auth/permissions.py b/aiorest_ws/auth/permissions.py index 7ba2dd8..8d09604 100644 --- a/aiorest_ws/auth/permissions.py +++ b/aiorest_ws/auth/permissions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - Permission classes for authentication. +Permission classes for authentication. """ from aiorest_ws.abstract import AbstractPermission @@ -8,10 +8,13 @@ class IsAuthenticated(AbstractPermission): - """Permissions used for checking authenticated users.""" + """ + Permissions used for checking authenticated users. + """ @staticmethod def check(request, handler): - """Check permission method. + """ + Check permission method. :param request: instance of Request class. :param handler: view, invoked later for the request. diff --git a/aiorest_ws/auth/token/backends.py b/aiorest_ws/auth/token/backends.py index 64a63fe..4c982eb 100644 --- a/aiorest_ws/auth/token/backends.py +++ b/aiorest_ws/auth/token/backends.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - Storage backends for save/get credentials. +Storage backends for save/get credentials. """ from aiorest_ws.auth.token.utils import SQL_CREATE_TOKEN_TABLE, \ SQL_TOKEN_GET, SQL_TOKEN_GET_BY_TOKEN_USERNAME, SQL_TOKEN_ADD, \ @@ -15,7 +15,9 @@ class InMemoryTokenBackend(BaseStorageBackend): - """In memory backend (based on SQLite) for token.""" + """ + In memory backend (based on SQLite) for token. + """ def __init__(self): super(InMemoryTokenBackend, self).__init__() @@ -29,7 +31,8 @@ def __create_models(self): self.db_manager.execute_script(SQL_CREATE_TOKEN_TABLE) def get(self, token): - """Get token from the storage. + """ + Get token from the storage. :param token: token as string. """ @@ -42,7 +45,8 @@ def get(self, token): return token_object def get_token_by_username(self, token_name, username): - """Get token from the storage by token_name and username. + """ + Get token from the storage by token_name and username. :param token: token as string. """ @@ -57,7 +61,8 @@ def get_token_by_username(self, token_name, username): return token_object def save(self, token_name, token, expired=None, user_id=None): - """Save token in the storage. + """ + Save token in the storage. :param user: instance of User class. :param token: token as string. diff --git a/aiorest_ws/auth/token/exceptions.py b/aiorest_ws/auth/token/exceptions.py index 8e42fe8..b53524f 100644 --- a/aiorest_ws/auth/token/exceptions.py +++ b/aiorest_ws/auth/token/exceptions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - Exception classes for token authentication. +Exception classes for token authentication. """ from aiorest_ws.exceptions import BaseAPIException diff --git a/aiorest_ws/auth/token/managers.py b/aiorest_ws/auth/token/managers.py index b41e341..9c56eab 100644 --- a/aiorest_ws/auth/token/managers.py +++ b/aiorest_ws/auth/token/managers.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - Token managers, proposed for generating/validating tokens. +Token managers, proposed for generating/validating tokens. """ import hashlib import hmac @@ -15,7 +15,8 @@ class JSONWebTokenManager(object): - """JSON Web Token (or shortly JWT) manager for the aiorest-ws library. + """ + JSON Web Token (or shortly JWT) manager for the aiorest-ws library. This manager written under inspire of the articles below: https://scotch.io/tutorials/the-anatomy-of-a-json-web-token @@ -32,7 +33,8 @@ class JSONWebTokenManager(object): RESERVED_NAMES = ('iss', 'sub', 'aud', 'exp', 'nbf', 'ait', 'jti') def _encode_data(self, data): - """Encode passed data to base64. + """ + Encode passed data to base64. :param data: dictionary object. """ @@ -40,7 +42,8 @@ def _encode_data(self, data): return b64encode(data).decode('utf-8') def _decode_data(self, data): - """Decode passed data to JSON. + """ + Decode passed data to JSON. :param data: dictionary object. """ @@ -48,12 +51,15 @@ def _decode_data(self, data): return json.loads(data) def _generate_header(self): - """Generate header for the token.""" + """ + Generate header for the token. + """ header = self._encode_data({"typ": "JWT", "alg": self.HASH_ALGORITHM}) return header def _generate_payload(self, data): - """Generate payload for the token. + """ + Generate payload for the token. :param data: dictionary object. """ @@ -61,7 +67,8 @@ def _generate_payload(self, data): return payload def _generate_signature(self, header, payload): - """Generate signature for the token. + """ + Generate signature for the token. :param header: token header. :param payload: token payload. @@ -76,11 +83,14 @@ def _generate_signature(self, header, payload): return signature def _used_reserved_keys(self, data): - """Get set of used reserved keys.""" + """ + Get set of used reserved keys. + """ return set(data.keys()) & set(self.RESERVED_NAMES) def _check_token_timestamp(self, token, key): - """Check token timestamp. + """ + Check token timestamp. :param token: dictionary object. :param key: field of token as a string. @@ -91,7 +101,8 @@ def _check_token_timestamp(self, token, key): return False def _is_invalid_signature(self, header, payload, token_signature): - """Validate token by signature. + """ + Validate token by signature. :param header: header of token. :param payload: payload of token. @@ -103,38 +114,42 @@ def _is_invalid_signature(self, header, payload, token_signature): return False def _is_not_be_accepted(self, token): - """Check for token is can be accepted or not. + """ + Check for token is can be accepted or not. :param token: dictionary object. """ return self._check_token_timestamp(token, 'nbf') def _is_expired_token(self, token): - """Check for token expired or not. + """ + Check for token expired or not. :param token: dictionary object. """ return self._check_token_timestamp(token, 'exp') def set_reserved_attribute(self, token, attribute, value): - """Set for token reserved attribute. + """ + Set for token reserved attribute. :param token: dictionary object. :param attribute: updated reserved field of JSON Web Token. :param value: initialized value. """ if attribute in self.RESERVED_NAMES and value: - # if user define "exp" or "nbf" argument, than calculate timestamp + # If user define "exp" or "nbf" argument, than calculate timestamp if attribute in ['exp', 'nbf']: current_time_in_seconds = int(time.time()) expired_timestamp = current_time_in_seconds + value token.update({attribute: expired_timestamp}) - # for any other JSON Web Token attributes just set value + # For any other JSON Web Token attributes just set value else: token[attribute] = value def generate(self, data, *args, **kwargs): - """Generate token. + """ + Generate token. :param data: dictionary, which will be stored inside token. :param args: tuple of arguments. @@ -152,7 +167,8 @@ def generate(self, data, *args, **kwargs): return token def verify(self, token): - """Verify passed token. + """ + Verify passed token. :param token: validated token (as `header.payload.signature`). """ diff --git a/aiorest_ws/auth/token/middlewares.py b/aiorest_ws/auth/token/middlewares.py index 4e5e432..ff4e0ba 100644 --- a/aiorest_ws/auth/token/middlewares.py +++ b/aiorest_ws/auth/token/middlewares.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - Middlewares for authentication issues. +Middlewares for authentication issues. """ from aiorest_ws.abstract import AbstractMiddleware from aiorest_ws.auth.user.models import UserSQLiteModel @@ -14,17 +14,21 @@ class BaseTokenMiddleware(AbstractMiddleware): - """Base token middleware class.""" + """ + Base token middleware class. + """ def init_credentials(self, request): - """Getting credentials (user, keys, tokens) from database/cache/etc. + """ + Getting credentials (user, keys, tokens) from database/cache/etc. :param request: instance of Request class. """ pass def authenticate(self, request, handler): - """Authenticate user. + """ + Authenticate user. :param request: instance of Request class. :param handler: view, invoked later for the request. @@ -32,7 +36,8 @@ def authenticate(self, request, handler): pass def process_request(self, request, handler): - """Processing request before calling handler. + """ + Processing request before calling handler. :param request: instance of Request class. :param handler: view, invoked later for the request. @@ -42,7 +47,9 @@ def process_request(self, request, handler): class JSONWebTokenMiddleware(BaseTokenMiddleware): - """The JSON Web Token middleware class.""" + """ + The JSON Web Token middleware class. + """ storage_backend = InMemoryTokenBackend manager = JSONWebTokenManager user_model = UserSQLiteModel @@ -54,7 +61,8 @@ def __init__(self): self.manager = self.manager() def get_user_by_token(self, token): - """Get user from the database by passed token. + """ + Get user from the database by passed token. :param token: token as string. """ @@ -66,7 +74,8 @@ def get_user_by_token(self, token): return user def init_credentials(self, request): - """Getting credentials (user, keys, tokens) from database/cache/etc. + """ + Getting credentials (user, keys, tokens) from database/cache/etc. :param request: instance of Request class. """ @@ -83,7 +92,8 @@ def init_credentials(self, request): add_property(request, 'token_payload', token_payload) def authenticate(self, request, view): - """Authenticate user. + """ + Authenticate user. NOTE: Authentication applied for the views, which set `auth_required` attribute to `True` value. diff --git a/aiorest_ws/auth/token/utils.py b/aiorest_ws/auth/token/utils.py index 7cf7026..a54d69b 100644 --- a/aiorest_ws/auth/token/utils.py +++ b/aiorest_ws/auth/token/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - Functions and constants, which can be used for work with Token models. +Functions and constants, which can be used for work with Token models. """ SQL_CREATE_TOKEN_TABLE = """ diff --git a/aiorest_ws/auth/user/abstractions.py b/aiorest_ws/auth/user/abstractions.py index 4637ac0..fa867ae 100644 --- a/aiorest_ws/auth/user/abstractions.py +++ b/aiorest_ws/auth/user/abstractions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - User abstractions for authentication. +User abstractions for authentication. """ from aiorest_ws.auth.user.utils import generate_password_hash @@ -8,7 +8,9 @@ class AbstractUser(object): - """Abstract class, which describe generic user.""" + """ + Abstract class, which describe generic user. + """ def __init__(self, *args, **kwargs): super(AbstractUser, self).__init__() self.first_name = kwargs.get('first_name', '') @@ -16,7 +18,9 @@ def __init__(self, *args, **kwargs): self._change_user_credentials(*args, **kwargs) def _change_user_credentials(self, *args, **kwargs): - """Change user credentials, based on the passed args and kwargs.""" + """ + Change user credentials, based on the passed args and kwargs. + """ self._is_active = kwargs.get('is_active', True) self._is_superuser = kwargs.get('is_superuser', False) self._is_staff = kwargs.get('is_staff', False) @@ -28,17 +32,22 @@ def _change_user_credentials(self, *args, **kwargs): self._is_anonymous = True def get_fullname(self): - """Get fullname of user.""" + """ + Get fullname of user. + """ return u"{0} {1}".format(self.first_name, self.last_name).strip() @property def is_active(self): - """Get is_active status of user.""" + """ + Get is_active status of user. + """ return self._is_active @is_active.setter def is_active(self, is_active): - """Set is_active status of user. + """ + Set is_active status of user. :param is_active: boolean value of is_active. """ @@ -46,26 +55,36 @@ def is_active(self, is_active): @property def is_superuser(self): - """Get is_superuser status of user.""" + """ + Get is_superuser status of user. + """ return self._is_superuser @property def is_staff(self): - """Get is_staff status of user.""" + """ + Get is_staff status of user. + """ return self._is_staff @property def is_user(self): - """Get is_user status of user.""" + """ + Get is_user status of user. + """ return self._is_user @property def is_anonymous(self): - """Get is_anonymous status of user.""" + """ + Get is_anonymous status of user. + """ return self._is_anonymous def is_authenticated(self): - """Check that this user is authenticated.""" + """ + Check that this user is authenticated. + """ if self.is_anonymous: return False else: @@ -73,7 +92,9 @@ def is_authenticated(self): class User(AbstractUser): - """Default class, which describe current user.""" + """ + Default class, which describe current user. + """ def __init__(self, *args, **kwargs): super(User, self).__init__(*args, **kwargs) self._id = kwargs.get('id', None) @@ -84,17 +105,22 @@ def __init__(self, *args, **kwargs): @property def id(self): - """Get users ID.""" + """ + Get users ID. + """ return self._id @property def username(self): - """Get username.""" + """ + Get username. + """ return self._username @username.setter def username(self, username): - """Set username. + """ + Set username. :param username: new username as a string. """ @@ -102,12 +128,15 @@ def username(self, username): @property def password(self): - """Get password.""" + """ + Get password. + """ return self._password @password.setter def password(self, password): - """Set new password for user. + """ + Set new password for user. :param password: password as a string. """ @@ -115,12 +144,15 @@ def password(self, password): @property def email(self): - """Get email.""" + """ + Get email. + """ return self._email @email.setter def email(self, email): - """Set email for user. + """ + Set email for user. :param email: email as a string. """ @@ -128,26 +160,31 @@ def email(self, email): @property def permissions(self): - """Get list of permissions.""" + """ + Get list of permissions. + """ return self._permissions @permissions.setter def permissions(self, permissions): - """Set permissions for user. + """ + Set permissions for user. :param permissions: list of permissions. """ self._permissions = permissions def check_password(self, password): - """Check for a valid password has taken. + """ + Check for a valid password has taken. :param password: password as a string. """ return self.password == generate_password_hash(password) def has_permission(self, obj=None): - """Check that user have a some permission. + """ + Check that user have a some permission. :param obj: permissions object derived from the AbstractPermission. """ diff --git a/aiorest_ws/auth/user/exceptions.py b/aiorest_ws/auth/user/exceptions.py index 900c516..f70aa5b 100644 --- a/aiorest_ws/auth/user/exceptions.py +++ b/aiorest_ws/auth/user/exceptions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - Exceptions for user management. +Exceptions for user management. """ from aiorest_ws.exceptions import BaseAPIException diff --git a/aiorest_ws/auth/user/models.py b/aiorest_ws/auth/user/models.py index 49f5ee3..31a9052 100644 --- a/aiorest_ws/auth/user/models.py +++ b/aiorest_ws/auth/user/models.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - User model for authentication. +User model for authentication. """ from sqlite3 import OperationalError @@ -21,7 +21,9 @@ class UserSQLiteModel(User): - """SQLite user model.""" + """ + SQLite user model. + """ db_manager = SQLiteManager def __init__(self): @@ -37,18 +39,21 @@ def __init__(self): self.__create_models() def __create_models(self): - """Create user model and append foreign key into token table.""" + """ + Create user model and append foreign key into token table. + """ try: self.db_manager.execute_script(SQL_CREATE_USER_TABLE) self.db_manager.execute_script(SQL_CREATE_TOKEN_FOREIGN_KEY) # This exception taken only in the case, when `user_id` foreign # keys already created. We didn't have any opportunity to check - # existing column via SQL, because SQL syntax of SQLite is reduced. + # existing column via SQL, because SQL syntax of SQLite is reduced except OperationalError: pass def __user_defined_fields(self, init_data): - """Define fields in which changed data by the user. + """ + Define fields in which changed data by the user. :param init_data: data, taken from the user. """ @@ -62,16 +67,21 @@ def __user_defined_fields(self, init_data): @property def fields(self): - """Get list of fields with primary key.""" + """ + Get list of fields with primary key. + """ return USER_MODEL_FIELDS @property def fields_without_pk(self): - """Get list of fields without primary key.""" + """ + Get list of fields without primary key. + """ return USER_MODEL_FIELDS_WITHOUT_PK def create_user(self, *args, **kwargs): - """Create user in the database. + """ + Create user in the database. :param args: tuple of arguments. :param kwargs: dictionary, where key is filled field of user model. @@ -102,16 +112,15 @@ def create_user(self, *args, **kwargs): logger.error(exc) def update_user(self, *args, **kwargs): - """Update user row in the database. + """ + Update user row in the database. :param args: tuple of arguments. :param kwargs: dictionary, where key is updated field of user model. """ username = kwargs.pop('username', None) if not username: - raise SearchCriteriaRequired( - "Username for WHEN statement is required." - ) + raise SearchCriteriaRequired("Username for WHEN statement is required.") # NOQA if len(kwargs) < 1: raise NotEnoughArguments() @@ -128,7 +137,8 @@ def update_user(self, *args, **kwargs): logger.error(exc) def get_user_by_username(self, username, with_id=False): - """Get user by his username from the database. + """ + Get user by his username from the database. :param username: username as a string. :param with_id: boolean flag, which means necessity to append to the @@ -140,9 +150,7 @@ def get_user_by_username(self, username, with_id=False): else: sql = SQL_USER_GET_BY_USERNAME - user_row = self.db_manager.execute_sql( - sql, (username, ) - ).fetchone() + user_row = self.db_manager.execute_sql(sql, (username, )).fetchone() # NOQA if user_row: user_data = convert_user_raw_data_to_dict(user_row, with_id) else: @@ -153,7 +161,8 @@ def get_user_by_username(self, username, with_id=False): return User(**user_data) def get_user_by_token(self, token): - """Get user object from the database, based on the his token. + """ + Get user object from the database, based on the his token. :param token: passed token as a dictionary object. """ diff --git a/aiorest_ws/auth/user/utils.py b/aiorest_ws/auth/user/utils.py index 985722e..01da6c5 100644 --- a/aiorest_ws/auth/user/utils.py +++ b/aiorest_ws/auth/user/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - Functions and constants, which can be used for work with User models. +Functions and constants, which can be used for work with User models. """ from aiorest_ws.db.utils import convert_db_row_to_dict from hashlib import sha256 @@ -61,8 +61,8 @@ def construct_update_sql(**parameters): - """Create update SQL query for SQLite based on the data, provided by - the user. + """ + Create update SQL query for SQLite based on the data, provided by the user. :param parameters: dictionary, where key is updated field of user model. """ @@ -81,7 +81,8 @@ def construct_update_sql(**parameters): def convert_user_raw_data_to_dict(user_raw_data, with_id=False): - """Convert database row to the dictionary object. + """ + Convert database row to the dictionary object. :param user_raw_data: database row. :param with_id: boolean flag, which means necessity to append to the result @@ -100,7 +101,8 @@ def convert_user_raw_data_to_dict(user_raw_data, with_id=False): def generate_password_hash(password): - """Generate SHA256 hash for password. + """ + Generate SHA256 hash for password. :param password: password as a string. """ diff --git a/aiorest_ws/command_line.py b/aiorest_ws/command_line.py index 30f5bea..be6b4be 100644 --- a/aiorest_ws/command_line.py +++ b/aiorest_ws/command_line.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - Custom functions and classes, which help working with the command line. +Custom functions and classes, which help working with the command line. """ from argparse import ArgumentParser @@ -8,11 +8,14 @@ class CommandLine(object): - """Wrapper over ArgumentParser class for working with command line.""" + """ + Wrapper over ArgumentParser class for working with command line. + """ options = ArgumentParser() def define(self, name, default=None, help=None, type=None): - """Defines an option in the global namespace. + """ + Defines an option in the global namespace. Note: already defined argument or option has been ignored and not appended again! @@ -28,5 +31,7 @@ def define(self, name, default=None, help=None, type=None): type=type) def parse_command_line(self): - """Parse options from the command line.""" + """ + Parse options from the command line. + """ return self.options.parse_args() diff --git a/aiorest_ws/conf/__init__.py b/aiorest_ws/conf/__init__.py index 0fecfa4..bc8123e 100644 --- a/aiorest_ws/conf/__init__.py +++ b/aiorest_ws/conf/__init__.py @@ -21,14 +21,16 @@ class Settings(object): - """Settings class of application.""" + """ + Settings class of application. + """ def __init__(self): super(Settings, self).__init__() - # Parse default settings and append to object. + # Parse default settings and append to object self._setup(global_settings) - # After that take the user defined settings and make merge. + # After that take the user defined settings and make merge user_settings = os.environ.get(ENVIRONMENT_VARIABLE, None) if user_settings: user_settings = importlib.import_module(user_settings) @@ -59,12 +61,12 @@ def _create_database_managers(self): ) manager_cls = db_settings.get('manager') or 'SQLiteManager' # User defined manager_cls as string means that necessary to - # extract class and create one instance of this. + # extract class and create one instance of this if type(manager_cls) is str: manager_instance = getattr( managers_module, manager_cls )(**kwargs) - # In any other cases in can be already instantiated manager. + # In any other cases in can be already instantiated manager else: manager_instance = manager_cls db_settings['manager'] = manager_instance @@ -75,7 +77,7 @@ class UserSettingsHolder(Settings): Holder for user configured settings. """ # SETTINGS_MODULE doesn't make much sense in the manually configured - # (standalone) case. + # (standalone) case SETTINGS_MODULE = None def __init__(self, default_settings): diff --git a/aiorest_ws/conf/global_settings.py b/aiorest_ws/conf/global_settings.py index 32c7886..68347be 100644 --- a/aiorest_ws/conf/global_settings.py +++ b/aiorest_ws/conf/global_settings.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - Default settings for configuration behaviour of aiorest-ws framework. +Default settings for configuration behaviour of aiorest-ws framework. """ # ----------------------------------------------- @@ -20,10 +20,10 @@ # Local time zone for this installation. All choices can be found here: # https://en.wikipedia.org/wiki/List_of_tz_zones_by_name (although not all # systems may support all possibilities). When USE_TZ is True, this is -# interpreted as the default user time zone. +# interpreted as the default user time zone TIME_ZONE = 'America/Chicago' -# If you set this to True, Django will use timezone-aware datetimes. +# If you set this to True, Django will use timezone-aware datetimes USE_TZ = False # Encoding charset for string, files, etc. @@ -62,7 +62,7 @@ # ----------------------------------------------- # List of middleware classes, which assigned for the main router. All # middlewares will be used in the order of enumeration. Keep in mind, when -# use this feature. +# use this feature MIDDLEWARE_CLASSES = () # ----------------------------------------------- diff --git a/aiorest_ws/db/backends/sqlite3/constants.py b/aiorest_ws/db/backends/sqlite3/constants.py index b1e3ebc..127eb19 100644 --- a/aiorest_ws/db/backends/sqlite3/constants.py +++ b/aiorest_ws/db/backends/sqlite3/constants.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - Constants which can be used for work with SQLite3 +Constants which can be used for work with SQLite3. """ __all__ = ('IN_MEMORY', ) diff --git a/aiorest_ws/db/backends/sqlite3/managers.py b/aiorest_ws/db/backends/sqlite3/managers.py index 7f0b21d..241a4b6 100644 --- a/aiorest_ws/db/backends/sqlite3/managers.py +++ b/aiorest_ws/db/backends/sqlite3/managers.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - Manager classes and functions, which help for work with databases via SQL. +Manager classes and functions, which help for work with databases via SQL. """ __all__ = ('SQLiteManager', ) @@ -15,7 +15,8 @@ def __init__(self, *args, **kwargs): self.connection = sqlite3.connect(db_path) def execute_sql(self, sql, parameters=()): - """Executes a SQL statement. + """ + Executes a SQL statement. :param sql: SQL statement as string. :param parameters: tuple with arguments. @@ -23,7 +24,8 @@ def execute_sql(self, sql, parameters=()): return self.connection.execute(sql, parameters) def execute_sql_and_fetchone(self, sql, parameters=()): - """Executes a SQL statement with fetching one row from result. + """ + Executes a SQL statement with fetching one row from result. :param sql: SQL statement as string. :param parameters: tuple with arguments. @@ -31,7 +33,8 @@ def execute_sql_and_fetchone(self, sql, parameters=()): return self.execute_sql(sql, parameters).fetchone() def execute_sql_from_file(self, filepath): - """Executes a SQL statement, which was taken from the file. + """ + Executes a SQL statement, which was taken from the file. :param filepath: path to file. """ @@ -41,7 +44,8 @@ def execute_sql_from_file(self, filepath): return result def execute_script(self, sql): - """Execute a SQL statement. This method recommended to use, when + """ + Execute a SQL statement. This method recommended to use, when required to execute few SQL queries independent and in parallel. :param sql: SQL statement as string. diff --git a/aiorest_ws/db/orm/abstract.py b/aiorest_ws/db/orm/abstract.py index cd09a2f..f980321 100644 --- a/aiorest_ws/db/orm/abstract.py +++ b/aiorest_ws/db/orm/abstract.py @@ -138,7 +138,7 @@ def bind(self, field_name, parent): self.source = field_name # `self.source_attrs` is a list of attributes that need to be looked - # up when serializing the instance, or populating the validated data. + # up when serializing the instance, or populating the validated data if self.source == '*': self.source_attrs = [] else: diff --git a/aiorest_ws/db/orm/django/field_mapping.py b/aiorest_ws/db/orm/django/field_mapping.py index ef85662..aef73f2 100644 --- a/aiorest_ws/db/orm/django/field_mapping.py +++ b/aiorest_ws/db/orm/django/field_mapping.py @@ -43,7 +43,7 @@ def get_field_kwargs(field_name, model_field): validator_kwarg = list(model_field.validators) # The following will only be used by ModelField classes. - # Gets removed for everything else. + # Gets removed for everything else kwargs['model_field'] = model_field if model_field.verbose_name and needs_label(model_field.verbose_name, field_name): # NOQA @@ -62,7 +62,7 @@ def get_field_kwargs(field_name, model_field): if isinstance(model_field, models.AutoField) or not model_field.editable: # If this field is read-only, then return early. - # Further keyword arguments are not valid. + # Further keyword arguments are not valid kwargs['read_only'] = True return kwargs @@ -94,7 +94,7 @@ def get_field_kwargs(field_name, model_field): if model_field.choices: # If this model field contains choices, then return early. - # Further keyword arguments are not valid. + # Further keyword arguments are not valid kwargs['choices'] = model_field.choices return kwargs @@ -106,7 +106,7 @@ def get_field_kwargs(field_name, model_field): ] # Ensure that max_length is passed explicitly as a keyword arg, - # rather than as a validator. + # rather than as a validator max_length = getattr(model_field, 'max_length', None) if max_length is not None and (isinstance(model_field, models.CharField) or isinstance(model_field, models.TextField)): @@ -117,7 +117,7 @@ def get_field_kwargs(field_name, model_field): ] # Ensure that min_length is passed explicitly as a keyword arg, - # rather than as a validator. + # rather than as a validator min_length = next(( validator.limit_value for validator in validator_kwarg if isinstance(validator, MinLengthValidator) @@ -130,7 +130,7 @@ def get_field_kwargs(field_name, model_field): ] # Ensure that max_value is passed explicitly as a keyword arg, - # rather than as a validator. + # rather than as a validator max_value = next(( validator.limit_value for validator in validator_kwarg if isinstance(validator, MaxValueValidator) @@ -143,7 +143,7 @@ def get_field_kwargs(field_name, model_field): ] # Ensure that max_value is passed explicitly as a keyword arg, - # rather than as a validator. + # rather than as a validator min_value = next(( validator.limit_value for validator in validator_kwarg if isinstance(validator, MinValueValidator) @@ -156,7 +156,7 @@ def get_field_kwargs(field_name, model_field): ] # URLField does not need to include the URLValidator argument, - # as it is explicitly added in. + # as it is explicitly added in if isinstance(model_field, models.URLField): validator_kwarg = [ validator for validator in validator_kwarg @@ -164,7 +164,7 @@ def get_field_kwargs(field_name, model_field): ] # EmailField does not need to include the validate_email argument, - # as it is explicitly added in. + # as it is explicitly added in if isinstance(model_field, models.EmailField): validator_kwarg = [ validator for validator in validator_kwarg @@ -194,7 +194,8 @@ def get_field_kwargs(field_name, model_field): } validator = UniqueValidator( queryset=model_field.model._default_manager, - message=unique_error_message) + message=unique_error_message + ) validator_kwarg.append(validator) if validator_kwarg: @@ -238,7 +239,7 @@ def get_relation_kwargs(field_name, relation_info): kwargs.pop('queryset', None) if kwargs.get('read_only', False): # If this field is read-only, then return early. - # No further keyword arguments are valid. + # No further keyword arguments are valid return kwargs if model_field.has_default() or model_field.blank or model_field.null: diff --git a/aiorest_ws/db/orm/django/fields.py b/aiorest_ws/db/orm/django/fields.py index 1edac0d..bc94b70 100644 --- a/aiorest_ws/db/orm/django/fields.py +++ b/aiorest_ws/db/orm/django/fields.py @@ -156,16 +156,12 @@ class ModelField(fields.ModelField): def __init__(self, model_field, **kwargs): # The `max_length` option is supported by Django's base `Field` class, - # so we'd better support it here. + # so we'd better support it here max_length = kwargs.pop('max_length', None) super(ModelField, self).__init__(model_field, **kwargs) if max_length is not None: - message = self.error_messages['max_length'].format( - max_length=max_length - ) - self.validators.append( - MaxLengthValidator(max_length, message=message) - ) + message = self.error_messages['max_length'].format(max_length=max_length) # NOQA + self.validators.append(MaxLengthValidator(max_length, message=message)) # NOQA def to_internal_value(self, data): rel = get_remote_field(self.model_field, default=None) @@ -289,9 +285,7 @@ def __init__(self, protocol='both', **kwargs): self.protocol = protocol.lower() self.unpack_ipv4 = (self.protocol == 'both') super(IPAddressField, self).__init__(**kwargs) - validators, error_message = ip_address_validators( - protocol, self.unpack_ipv4 - ) + validators, error_message = ip_address_validators(protocol, self.unpack_ipv4) # NOQA self.validators.extend(validators) def to_internal_value(self, data): @@ -316,7 +310,7 @@ class FilePathField(ChoiceField): def __init__(self, path, match=None, recursive=False, allow_files=True, allow_folders=False, required=None, **kwargs): # Defer to Django's FilePathField implementation to get the - # valid set of choices. + # valid set of choices field = DjangoFilePathField( path, match=match, recursive=recursive, allow_files=allow_files, allow_folders=allow_folders, required=required @@ -345,7 +339,7 @@ def __init__(self, *args, **kwargs): def to_internal_value(self, data): try: - # `UploadedFile` objects should have name and size attributes. + # `UploadedFile` objects should have name and size attributes file_name = data.name file_size = data.size except AttributeError: @@ -370,7 +364,7 @@ def to_representation(self, value): if use_url: if not getattr(value, 'url', None): - # If the file has not been saved it may not have a URL. + # If the file has not been saved it may not have a URL return None url = value.url return url @@ -392,7 +386,7 @@ def __init__(self, *args, **kwargs): def to_internal_value(self, data): # Image validation is a bit grungy, so we'll just outright # defer to Django's implementation so we don't need to - # consider it, or treat PIL as a test dependency. + # consider it, or treat PIL as a test dependency file_object = super(ImageField, self).to_internal_value(data) django_field = self._DjangoImageField() django_field.error_messages = self.error_messages diff --git a/aiorest_ws/db/orm/django/model_meta.py b/aiorest_ws/db/orm/django/model_meta.py index 7640afa..97bf048 100644 --- a/aiorest_ws/db/orm/django/model_meta.py +++ b/aiorest_ws/db/orm/django/model_meta.py @@ -22,7 +22,7 @@ def _get_pk(opts): rel = get_remote_field(pk) while rel and rel.parent_link: - # If model is a child via multi-table inheritance, use parent's pk. + # If model is a child via multi-table inheritance, use parent's pk pk = get_related_model(pk)._meta.pk rel = get_remote_field(pk) @@ -75,9 +75,7 @@ def _get_forward_relationships(opts): to_many=True, # many-to-many do not have to_fields to_field=None, - has_through_model=( - not get_remote_field(field).through._meta.auto_created - ) + has_through_model=not get_remote_field(field).through._meta.auto_created # NOQA ) return forward_relations diff --git a/aiorest_ws/db/orm/django/relations.py b/aiorest_ws/db/orm/django/relations.py index 5c6dbd4..edbfe69 100644 --- a/aiorest_ws/db/orm/django/relations.py +++ b/aiorest_ws/db/orm/django/relations.py @@ -61,7 +61,7 @@ def get_queryset(self): def get_attribute(self, instance): if self.use_pk_only_optimization() and self.source_attrs: # Optimized case, return a mock object only containing the - # pk attribute. + # pk attribute try: instance = get_attribute(instance, self.source_attrs[:-1]) value = instance.serializable_value(self.source_attrs[-1]) @@ -80,7 +80,7 @@ def get_choices(self, cutoff=None): queryset = self.get_queryset() if queryset is None: # Ensure that field.choices returns something sensible - # even when accessed with a read-only field. + # even when accessed with a read-only field return {} if cutoff is not None: diff --git a/aiorest_ws/db/orm/django/serializers.py b/aiorest_ws/db/orm/django/serializers.py index 0150420..8981fad 100644 --- a/aiorest_ws/db/orm/django/serializers.py +++ b/aiorest_ws/db/orm/django/serializers.py @@ -77,8 +77,7 @@ def run_validation(self, data=empty): try: self.run_validators(value) value = self.validate(value) - assert value is not None, '.validate() should return the ' \ - 'validated data' + assert value is not None, '.validate() should return the validated data' # NOQA except (ValidationError, DjangoValidationError) as exc: raise ValidationError(detail=get_validation_error_detail(exc)) @@ -172,7 +171,7 @@ def create(self, validated_data): # Remove many-to-many relationships from validated_data. # They are not valid arguments to the default `.create()` method, - # as they require that the instance has already been saved. + # as they require that the instance has already been saved info = model_meta.get_field_info(ModelClass) many_to_many = {} for field_name, relation_info in info.relations.items(): @@ -199,7 +198,7 @@ def create(self, validated_data): ) raise TypeError(msg) - # Save many-to-many relationships after the instance is created. + # Save many-to-many relationships after the instance is created if many_to_many: for field_name, value in many_to_many.items(): field = getattr(instance, field_name) @@ -214,7 +213,7 @@ def update(self, instance, validated_data): # Simply set each attribute on the instance, and then save it. # Note that unlike `.create()` we don't need to treat many-to-many # relationships as being a special case. During updates we already - # have an instance pk for the relationships to be associated with. + # have an instance pk for the relationships to be associated with for attr, value in validated_data.items(): setattr(instance, attr, value) instance.save() @@ -267,7 +266,7 @@ def _get_unique_constraint_names(self, model, model_fields, field_names): unique_constraint_names = set() for model_field in model_fields.values(): - # Include each of the `unique_for_*` field names. + # Include each of the `unique_for_*` field names unique_constraint_names |= { model_field.unique_for_date, model_field.unique_for_month, model_field.unique_for_year @@ -350,10 +349,10 @@ def build_standard_field(self, *args, **kwargs): if 'choices' in field_kwargs: # Fields with choices get coerced into `ChoiceField` - # instead of using their regular typed field. + # instead of using their regular typed field field_class = self.serializer_choice_field # Some model fields may introduce kwargs that would not be valid - # for the choice field. We need to strip these out. + # for the choice field. We need to strip these out valid_kwargs = { 'read_only', 'write_only', 'required', 'default', 'initial', 'source', @@ -368,16 +367,16 @@ def build_standard_field(self, *args, **kwargs): if not issubclass(field_class, ModelField): # `model_field` is only valid for the fallback case of # `ModelField`, which is used when no other typed field - # matched to the model field. + # matched to the model field field_kwargs.pop('model_field', None) if not issubclass(field_class, CharField) and not issubclass(field_class, ChoiceField): # NOQA - # `allow_blank` is only valid for textual fields. + # `allow_blank` is only valid for textual fields field_kwargs.pop('allow_blank', None) if postgres_fields and isinstance(model_field, postgres_fields.ArrayField): # NOQA # Populate the `child` argument on `ListField` instances generated - # for the PostgreSQL specific `ArrayField`. + # for the PostgreSQL specific `ArrayField` child_model_field = model_field.base_field child_field_class, child_field_kwargs = self.build_standard_field( 'child', child_model_field @@ -399,7 +398,7 @@ def build_relational_field(self, *args, **kwargs): field_kwargs['slug_field'] = to_field field_class = self.serializer_related_to_field - # `view_name` is only valid for hyperlinked relationships. + # `view_name` is only valid for hyperlinked relationships if not issubclass(field_class, HyperlinkedRelatedField): field_kwargs.pop('view_name', None) diff --git a/aiorest_ws/db/orm/django/validators.py b/aiorest_ws/db/orm/django/validators.py index e7ea780..2ed4cf3 100644 --- a/aiorest_ws/db/orm/django/validators.py +++ b/aiorest_ws/db/orm/django/validators.py @@ -13,9 +13,7 @@ from aiorest_ws.db.orm.exceptions import ValidationError from aiorest_ws.utils.representation import smart_repr -__all__ = [ - 'qs_exists', 'qs_filter', 'UniqueValidator' -] +__all__ = ('qs_exists', 'qs_filter', 'UniqueValidator', ) def qs_exists(queryset): @@ -50,9 +48,9 @@ def set_context(self, serializer_field): prior to the validation call being made. """ # Determine the underlying model field name. This may not be the - # same as the serializer field name if `source=<>` is set. + # same as the serializer field name if `source=<>` is set self.field_name = serializer_field.source_attrs[-1] - # Determine the existing instance, if this is an update operation. + # Determine the existing instance, if this is an update operation self.instance = getattr(serializer_field.parent, 'instance', None) def filter_queryset(self, value, queryset): diff --git a/aiorest_ws/db/orm/fields.py b/aiorest_ws/db/orm/fields.py index 2332083..b3b6732 100644 --- a/aiorest_ws/db/orm/fields.py +++ b/aiorest_ws/db/orm/fields.py @@ -41,7 +41,7 @@ class IntegerField(AbstractField): u"to {min_value}.", 'max_string_length': u"String value too large." } - MAX_STRING_LENGTH = 1000 # protect against extremely string inputs. + MAX_STRING_LENGTH = 1000 # protect against extremely string inputs re_decimal = re.compile(r'\.0*$') # allow to use .0 at the end of number def __init__(self, **kwargs): @@ -49,19 +49,11 @@ def __init__(self, **kwargs): self.min_value = kwargs.pop('min_value', None) super(IntegerField, self).__init__(**kwargs) if self.max_value is not None: - message = self.error_messages['max_value'].format( - max_value=self.max_value - ) - self.validators.append( - validators.MaxValueValidator(self.max_value, message=message) - ) + message = self.error_messages['max_value'].format(max_value=self.max_value) # NOQA + self.validators.append(validators.MaxValueValidator(self.max_value, message=message)) # NOQA if self.min_value is not None: - message = self.error_messages['min_value'].format( - min_value=self.min_value - ) - self.validators.append( - validators.MinValueValidator(self.min_value, message=message) - ) + message = self.error_messages['min_value'].format(min_value=self.min_value) # NOQA + self.validators.append(validators.MinValueValidator(self.min_value, message=message)) # NOQA def to_internal_value(self, data): if isinstance(data, str) and len(data) > self.MAX_STRING_LENGTH: @@ -193,7 +185,7 @@ def __init__(self, **kwargs): def run_validation(self, data=empty): # Test for the empty string here so that it does not get validated, # and so that subclasses do not need to handle it explicitly inside - # the `to_internal_value()` method. + # the `to_internal_value()` method if data == '' or (self.trim_whitespace and str(data).strip() == ''): if not self.allow_blank: self.raise_error('blank') @@ -219,7 +211,7 @@ def __init__(self, choices, **kwargs): # Map the string representation of choices to the underlying value. # Allows us to deal with eg. integer choices while supporting either - # integer or string input, but still get the correct datatype out. + # integer or string input, but still get the correct datatype out self.choice_strings_to_values = { str(key): key for key in self.choices.keys() } @@ -251,7 +243,7 @@ class FloatField(AbstractField): u"to {min_value}.", 'max_string_length': u"String value too large." } - MAX_STRING_LENGTH = 1000 # protect against extremely string inputs. + MAX_STRING_LENGTH = 1000 # protect against extremely string inputs def __init__(self, **kwargs): self.max_value = kwargs.pop('max_value', None) @@ -371,7 +363,7 @@ def to_representation(self, value): # Applying a `TimeField` to a datetime value is almost always not a # sensible thing to do, as it means naively dropping any explicit or - # implicit timezone info. + # implicit timezone info assert not isinstance(value, datetime.datetime), ( 'Expected a `time`, but got a `datetime`. Refusing to coerce, ' 'as this may mean losing timezone information. Use a custom ' @@ -401,7 +393,7 @@ class DecimalField(AbstractField): u"decimal point.", 'max_string_length': u"String value too large." } - MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs. + MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs def __init__(self, max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None, **kwargs): @@ -583,7 +575,7 @@ def to_representation(self, value): # Applying a `DateField` to a datetime value is almost always not a # sensible thing to do, as it means naively dropping any explicit or - # implicit timezone info. + # implicit timezone info assert not isinstance(value, datetime.datetime), ( 'Expected a `date`, but got a `datetime`. Refusing to coerce, ' 'as this may mean losing timezone information. Use a custom ' @@ -618,7 +610,6 @@ def __init__(self, format=empty, input_formats=None, default_timezone=None, def enforce_timezone(self, value): """ - When `self.timezone` is `None`, always return naive datetimes. When `self.timezone` is not `None`, always return aware datetimes. """ @@ -726,8 +717,8 @@ def __init__(self, **kwargs): super(HiddenField, self).__init__(**kwargs) def get_value(self, dictionary): - # We always use the default value for `HiddenField`. - # User input is never provided or accepted. + # We always use the default value for `HiddenField` and user input + # is never provided or accepted return empty def to_internal_value(self, data): @@ -768,6 +759,7 @@ class ReadOnlyField(AbstractField): If the field is a method with no parameters, the method will be called and it's return value used as the representation. For example, the following would call `get_expiry_date()` on the object: + class ExampleSerializer(Serializer): expiry_date = ReadOnlyField(source='get_expiry_date') """ @@ -890,12 +882,15 @@ def to_representation(self, value): class HStoreField(DictField): """ - HStore field for PostgreSQL + HStore field for PostgreSQL. """ child = CharField(allow_blank=True) class JSONField(AbstractField): + """ + Special kind of field which is storing data in JSON format. + """ default_error_messages = { 'invalid': u"Value must be valid JSON." } @@ -939,7 +934,7 @@ def to_internal_value(self, data): def get_attribute(self, obj): # We pass the object instance onto `to_representation`, - # not just the field attribute. + # not just the field attribute return obj def to_representation(self, obj): @@ -951,12 +946,12 @@ class SerializerMethodField(AbstractField): A read-only field that get its representation from calling a method on the parent serializer class. The method called will be of the form "get_{field_name}", and should take a single argument, which is the - object being serialized. - For example: + object being serialized. For example: + class ExampleSerializer(ModelSerializer): extra_info = SerializerMethodField() def get_extra_info(self, obj): - return ... # Calculate some data to return. + return ... # Calculate some data to return """ def __init__(self, method_name=None, **kwargs): self.method_name = method_name @@ -976,7 +971,7 @@ def bind(self, field_name, parent): (self.method_name, field_name, parent.__class__.__name__) ) - # The method name should default to `get_{field_name}`. + # The method name should default to `get_{field_name}` if self.method_name is None: self.method_name = default_method_name diff --git a/aiorest_ws/db/orm/relations.py b/aiorest_ws/db/orm/relations.py index ce32f33..ab99f70 100644 --- a/aiorest_ws/db/orm/relations.py +++ b/aiorest_ws/db/orm/relations.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- """ -Module, which provide classes and function for related and nested field. +Module which provide classes and function for related and nested field. NOTE: Don't forget to override `to_internal_value()`, `to_representation()` -and some specific methods for all specified classes for you own purposes. +and other specific methods for all specified classes for you own purposes. For your own ORM support necessary to implement classes accordingly to the next inheritance tree (don't look onto the classes, which aren't inherited @@ -35,7 +35,7 @@ ) # We assume that 'validators' are intended for the child serializer, -# rather than the parent serializer. +# rather than the parent serializer MANY_RELATION_KWARGS = ( 'read_only', 'write_only', 'required', 'default', 'initial', 'source', 'label', 'help_text', 'style', 'error_messages', 'allow_empty' @@ -92,7 +92,7 @@ def __init__(self, **kwargs): def __new__(cls, *args, **kwargs): # We override this method in order to automagically create - # `ManyRelatedField` classes instead when `many=True` is set. + # `ManyRelatedField` classes instead when `many=True` is set if kwargs.pop('many', False): return cls.many_init(*args, **kwargs) return super(RelatedField, cls).__new__(cls, *args, **kwargs) @@ -106,6 +106,7 @@ def many_init(cls, *args, **kwargs): Note that we're over-cautious in passing most arguments to both parent and child classes in order to try to cover the general case. If you're overriding this method you'll probably want something much simpler, eg: + @classmethod def many_init(cls, *args, **kwargs): kwargs['child'] = cls() @@ -266,14 +267,14 @@ def __init__(self, view_name=None, **kwargs): # We include this simply for dependency injection in tests. # We can't add it as a class attributes or it would expect an # implicit `self` argument to be passed. - # Set the `reverse` attribute of this class. + # Set the `reverse` attribute of this class self.reverse = reverse super(HyperlinkedRelatedField, self).__init__(**kwargs) def use_pk_only_optimization(self): # Must return boolean value for equal operation between - # self.lookup_field attribute and defined model PK + # self.lookup_field attribute and defined model PK. # For example, for Django Framework it can be: # return self.lookup_field == 'pk' raise NotImplementedError("`use_pk_only_optimization()` must be " @@ -308,7 +309,7 @@ def get_url(self, obj, view_name): May raise a `NoReverseMatch` if the `view_name` and `lookup_field` attributes are not configured to correctly match the URL conf. """ - # Unsaved objects will not yet have a valid URL. + # Unsaved objects will not yet have a valid URL if not self.is_saved_in_database(obj): return None @@ -341,7 +342,7 @@ def to_internal_value(self, data): return self.get_object(match.view_name, match.args, match.kwargs) def to_representation(self, value): - # Return the hyperlink, or error if incorrectly configured. + # Return the hyperlink, or error if incorrectly configured try: url = self.get_url(value, self.view_name) except NoReverseMatch: @@ -381,7 +382,7 @@ def __init__(self, view_name=None, **kwargs): def use_pk_only_optimization(self): # We have the complete object instance already. We don't need - # to run the 'only get the pk for this relationship' code. + # to run the 'only get the pk for this relationship' code return False diff --git a/aiorest_ws/db/orm/serializers.py b/aiorest_ws/db/orm/serializers.py index d0f4a93..877dab7 100644 --- a/aiorest_ws/db/orm/serializers.py +++ b/aiorest_ws/db/orm/serializers.py @@ -76,7 +76,7 @@ def __init__(self, instance=None, data=empty, **kwargs): def __new__(cls, *args, **kwargs): # We override this method in order to automatically create - # `ListSerializer` classes instead when `many=True` is set. + # `ListSerializer` classes instead when `many=True` is set if kwargs.pop('many', False): return cls.many_init(*args, **kwargs) return super(BaseSerializer, cls).__new__(cls, *args, **kwargs) @@ -201,14 +201,10 @@ def save(self, **kwargs): if self.instance is not None: self.instance = self.update(self.instance, validated_data) - assert self.instance is not None, ( - '`update()` did not return an object instance.' - ) + assert self.instance is not None, '`update()` did not return an object instance.' # NOQA else: self.instance = self.create(validated_data) - assert self.instance is not None, ( - '`create()` did not return an object instance.' - ) + assert self.instance is not None, '`create()` did not return an object instance.' # NOQA return self.instance @@ -251,7 +247,7 @@ def _get_declared_fields(cls, bases, attrs): # If this class is subclass of another Serializer, then add that # Serializer's fields. Note that we loop over the bases in *reverse*. - # This is necessary in order to maintain the correct order of fields. + # This is necessary in order to maintain the correct order of fields for base in reversed(bases): if hasattr(base, '_declared_fields'): fields = list(base._declared_fields.items()) + fields @@ -275,7 +271,7 @@ def fields(self): """ # `fields` is evaluated lazily. We do this to ensure that we don't # have issues importing modules that use ModelSerializers as fields, - # even if app-loading stage has not yet run. + # even if app-loading stage has not yet run if not hasattr(self, '_fields'): self._fields = BindingDict(self) for key, value in self.get_fields().items(): @@ -302,14 +298,14 @@ def get_fields(self): """ # Every new serializer is created with a clone of the field instances. # This allows users to dynamically modify the fields on a serializer - # instance without affecting every other serializer class. + # instance without affecting every other serializer class return copy.deepcopy(self._declared_fields) def get_validators(self): """ Returns a list of validator callables. """ - # Used by the lazily-evaluated `validators` property. + # Used by the lazily-evaluated `validators` property meta = getattr(self, 'Meta', None) validators = getattr(meta, 'validators', None) return validators[:] if validators else [] @@ -334,7 +330,7 @@ def get_value(self, dictionary): def run_validation(self, data=empty): """ - Validate passed data + Validate passed data. """ raise NotImplementedError('`run_validation()` must be implemented.') @@ -390,7 +386,7 @@ def to_representation(self, instance): if attribute is None: # We skip `to_representation` for `None` values so that - # fields do not have to explicitly deal with that case. + # fields do not have to explicitly deal with that case ret[field.field_name] = None else: ret[field.field_name] = field.to_representation(attribute) @@ -586,9 +582,7 @@ def to_representation(self, data): """ # Dealing with nested relationships, data can be a Manager, # so, first get a queryset from the Manager if needed - return [ - self.child.to_representation(item) for item in data - ] + return [self.child.to_representation(item) for item in data] def validate(self, attrs): return attrs @@ -603,9 +597,7 @@ def update(self, instance, validated_data): ) def create(self, validated_data): - return [ - self.child.create(attrs) for attrs in validated_data - ] + return [self.child.create(attrs) for attrs in validated_data] def save(self, **kwargs): """ @@ -628,14 +620,10 @@ def save(self, **kwargs): if self.instance is not None: self.instance = self.update(self.instance, validated_data) - assert self.instance is not None, ( - '`update()` did not return an object instance.' - ) + assert self.instance is not None, '`update()` did not return an object instance.' # NOQA else: self.instance = self.create(validated_data) - assert self.instance is not None, ( - '`create()` did not return an object instance.' - ) + assert self.instance is not None, '`create()` did not return an object instance.' # NOQA return self.instance @@ -690,7 +678,7 @@ def get_field_info(self, model): """ raise NotImplementedError('`get_field_info()` must be implemented.') - # Default `create` and `update` behavior... + # Default `create` and `update` behavior def create(self, validated_data): """ Create object in the database, with the passed `validated_data`. @@ -723,9 +711,7 @@ def get_fields(self): ) if self.is_abstract_model(self.Meta.model): - raise ValueError( - 'Cannot use ModelSerializer with Abstract Models.' - ) + raise ValueError('Cannot use ModelSerializer with Abstract Models.') # NOQA declared_fields = copy.deepcopy(self._declared_fields) model = getattr(self.Meta, 'model') @@ -735,7 +721,7 @@ def get_fields(self): assert depth >= 0, "'depth' may not be negative." assert depth <= 10, "'depth' may not be greater than 10." - # Retrieve metadata about fields & relationships on the model class. + # Retrieve metadata about fields & relationships on the model class info = self.get_field_info(model) field_names = self.get_field_names(declared_fields, info) @@ -746,16 +732,16 @@ def get_fields(self): field_names, declared_fields, extra_kwargs ) - # Determine the fields that should be included on the serializer. + # Determine the fields that should be included on the serializer fields = OrderedDict() for field_name in field_names: - # If the field is explicitly declared on the class then use that. + # If the field is explicitly declared on the class then use that if field_name in declared_fields: fields[field_name] = declared_fields[field_name] continue - # Determine the serializer field class and keyword arguments. + # Determine the serializer field class and keyword arguments field_class, field_kwargs = self.build_field( field_name, info, model, depth ) @@ -766,10 +752,10 @@ def get_fields(self): field_kwargs, extra_field_kwargs ) - # Create the serializer field. + # Create the serializer field fields[field_name] = field_class(**field_kwargs) - # Add in any hidden fields. + # Add in any hidden fields fields.update(hidden_fields) return fields @@ -808,11 +794,11 @@ def get_field_names(self, declared_fields, info): if fields is not None: # Ensure that all declared fields have also been included in the - # `Meta.fields` option. + # `Meta.fields` option # Do not require any fields that are declared a parent class, # in order to allow serializer subclasses to only include - # a subset of fields. + # a subset of fields required_field_names = set(declared_fields) for cls in self.__class__.__bases__: _declared_fields = getattr(cls, '_declared_fields', []) @@ -830,11 +816,11 @@ def get_field_names(self, declared_fields, info): return fields - # Use the default set of field names if `Meta.fields` is not specified. + # Use the default set of field names if `Meta.fields` is not specified fields = self.get_default_field_names(declared_fields, info) if exclude is not None: - # If `Meta.exclude` is included, then remove those fields. + # If `Meta.exclude` is included, then remove those fields for field_name in exclude: assert field_name in fields, ( "The field '{field_name}' was included on serializer " @@ -922,7 +908,7 @@ def include_extra_kwargs(self, kwargs, extra_kwargs): kwargs.pop('required') if extra_kwargs.get('read_only', kwargs.get('read_only', False)): - # Read only fields should always omit the 'required' argument. + # Read only fields should always omit the 'required' argument extra_kwargs.pop('required', None) kwargs.update(extra_kwargs) @@ -960,13 +946,13 @@ def get_uniqueness_extra_kwargs(self, field_names, declared_fields, # Determine if we need any additional `HiddenField` or extra keyword # arguments to deal with `unique_for` dates that are required to - # be in the input data in order to validate it. + # be in the input data in order to validate it unique_constraint_names = self._get_unique_constraint_names( model, model_fields, field_names ) # Include each of "unique multiple columns" field names, - # so long as all the field names are included on the serializer. + # so long as all the field names are included on the serializer unique_constraint_names |= self._get_unique_together_constraints( model, model_fields, field_names ) @@ -974,12 +960,12 @@ def get_uniqueness_extra_kwargs(self, field_names, declared_fields, # Now we have all the field names that have uniqueness constraints # applied, we can add the extra 'required=...' or 'default=...' # arguments that are appropriate to these fields, or add a - # `HiddenField` for it. + # `HiddenField` for it hidden_fields = {} uniqueness_extra_kwargs = {} for unique_constraint_name in unique_constraint_names: - # Get the model field that is referred too. + # Get the model field that is referred too unique_constraint_field = self._get_unique_field( model, unique_constraint_name ) @@ -989,22 +975,17 @@ def get_uniqueness_extra_kwargs(self, field_names, declared_fields, if unique_constraint_name in model_fields: # The corresponding field is present in the serializer if default is empty: - uniqueness_extra_kwargs[unique_constraint_name] = { - 'required': True - } + field_kwargs = {'required': True} else: - uniqueness_extra_kwargs[unique_constraint_name] = { - 'default': default - } + field_kwargs = {'default': default} + uniqueness_extra_kwargs[unique_constraint_name] = field_kwargs elif default is not empty: # The corresponding field is not present in the, # serializer. We have a default to use for it, so - # add in a hidden field that populates it. - hidden_fields[unique_constraint_name] = HiddenField( - default=default - ) + # add in a hidden field that populates it + hidden_fields[unique_constraint_name] = HiddenField(default=default) # NOQA - # Update `extra_kwargs` with any new options. + # Update `extra_kwargs` with any new options for key, value in uniqueness_extra_kwargs.items(): if key in extra_kwargs: extra_kwargs[key].update(value) @@ -1036,7 +1017,7 @@ def _get_model_fields(self, field_names, declared_fields, extra_kwargs): if '.' in source or source == '*': # Model fields will always have a simple source mapping, - # they can't be nested attribute lookups. + # they can't be nested attribute lookups continue self._bind_field(model, source, model_fields) diff --git a/aiorest_ws/db/orm/sqlalchemy/exceptions.py b/aiorest_ws/db/orm/sqlalchemy/exceptions.py index 2bd239d..f578108 100644 --- a/aiorest_ws/db/orm/sqlalchemy/exceptions.py +++ b/aiorest_ws/db/orm/sqlalchemy/exceptions.py @@ -1,4 +1,7 @@ # -*- coding: utf-8 -*- +""" +Exception classes for work with model serializers, fields and SQLAlchemy ORM. +""" from aiorest_ws.exceptions import BaseAPIException diff --git a/aiorest_ws/db/orm/sqlalchemy/field_mapping.py b/aiorest_ws/db/orm/sqlalchemy/field_mapping.py index a052922..097fa2e 100644 --- a/aiorest_ws/db/orm/sqlalchemy/field_mapping.py +++ b/aiorest_ws/db/orm/sqlalchemy/field_mapping.py @@ -43,12 +43,12 @@ def get_field_kwargs(field_name, model_field, model_class): validator_kwarg = [] # The following will only be used by ModelField classes. - # Gets removed for everything else. + # Gets removed for everything else kwargs['model_field'] = model_field if model_field.primary_key: # If this field is read-only, then return early. - # Further keyword arguments are not valid. + # Further keyword arguments are not valid kwargs['read_only'] = True return kwargs @@ -63,12 +63,12 @@ def get_field_kwargs(field_name, model_field, model_class): if isinstance(model_field.type, types.Enum): # If this model field contains choices, then return early. - # Further keyword arguments are not valid. + # Further keyword arguments are not valid kwargs['choices'] = model_field.type.enums return kwargs # Ensure that max_length is passed explicitly as a keyword arg, rather - # than as a validator. + # than as a validator max_length = getattr(model_field.type, 'length', None) if max_length is not None and isinstance(model_field.type, STRING_TYPES): kwargs['max_length'] = max_length @@ -123,7 +123,7 @@ def get_relation_kwargs(field_name, relation_info): if kwargs.get('read_only', False): # If this field is read-only, then return early. - # No further keyword arguments are valid. + # No further keyword arguments are valid return kwargs return kwargs diff --git a/aiorest_ws/db/orm/sqlalchemy/fields.py b/aiorest_ws/db/orm/sqlalchemy/fields.py index 920b32d..c379639 100644 --- a/aiorest_ws/db/orm/sqlalchemy/fields.py +++ b/aiorest_ws/db/orm/sqlalchemy/fields.py @@ -95,7 +95,7 @@ class ModelField(fields.ModelField): This is used by `ModelSerializer` when dealing with custom model fields, that do not have a serializer field to be mapped to. - For more details, I recommend to read the next page in SQLAlchemy docs: + For more details recommended to read the next page in SQLAlchemy docs: http://docs.sqlalchemy.org/en/latest/core/type_api.html """ def __init__(self, model_field, **kwargs): @@ -121,7 +121,7 @@ def to_internal_value(self, data): return data def to_representation(self, obj): - # Custom implementation of representation of data for a user + # Custom implementation of representation of data if self.type_has_custom_behaviour('process_result_value'): return self.field_type.process_result_value(obj, self.dialect) diff --git a/aiorest_ws/db/orm/sqlalchemy/mixins.py b/aiorest_ws/db/orm/sqlalchemy/mixins.py index 93bb620..35723b8 100644 --- a/aiorest_ws/db/orm/sqlalchemy/mixins.py +++ b/aiorest_ws/db/orm/sqlalchemy/mixins.py @@ -1,4 +1,8 @@ # -*- coding: utf-8 -*- +""" +This module provides special classes (mixins) which used for getting data from +SQLAlchemy ORM when work with a model serializers or fields. +""" from aiorest_ws.conf import settings from aiorest_ws.db.orm.sqlalchemy.model_meta import model_pk @@ -7,7 +11,10 @@ class ORMSessionMixin(object): - + """ + Special wrapper around SQLALchemy querysets, when user specified them in + for fields, serializers and etcetera. + """ def _get_session(self): return settings.SQLALCHEMY_SESSION() @@ -28,7 +35,9 @@ def get_queryset(self): class SQLAlchemyMixin(object): - + """ + Class which provide opportunity to get primary key from the passed object. + """ def _get_filter_args(self, query, data): mapper = query._bind_mapper() model = mapper.class_ diff --git a/aiorest_ws/db/orm/sqlalchemy/relations.py b/aiorest_ws/db/orm/sqlalchemy/relations.py index 12d9567..31340f0 100644 --- a/aiorest_ws/db/orm/sqlalchemy/relations.py +++ b/aiorest_ws/db/orm/sqlalchemy/relations.py @@ -55,7 +55,7 @@ def __deepcopy__(self, memo): def get_attribute(self, instance): if self.use_pk_only_optimization() and self.source_attrs: # Optimized case, return a mock object only containing the - # pk attribute. + # pk attribute try: instance = get_attribute(instance, self.source_attrs[:-1]) value = getattr(instance, self.source_attrs[-1]) @@ -63,7 +63,7 @@ def get_attribute(self, instance): except AttributeError: pass - # Standard case, return the object instance. + # Standard case, return the object instance return get_attribute(instance, self.source_attrs) diff --git a/aiorest_ws/db/orm/sqlalchemy/serializers.py b/aiorest_ws/db/orm/sqlalchemy/serializers.py index a22c957..72344bc 100644 --- a/aiorest_ws/db/orm/sqlalchemy/serializers.py +++ b/aiorest_ws/db/orm/sqlalchemy/serializers.py @@ -68,8 +68,7 @@ def run_validation(self, data=empty): try: self.run_validators(value) value = self.validate(value) - assert value is not None, '.validate() should return the ' \ - 'validated data' + assert value is not None, '.validate() should return the validated data' # NOQA except (ValidationError, AssertionError) as exc: raise ValidationError(detail=get_validation_error_detail(exc)) @@ -153,7 +152,7 @@ def get_field_info(self, model): """ return model_meta.get_field_info(model) - # Default `create` and `update` behavior... + # Default `create` and `update` behavior def create(self, validated_data): """ We have a bit of extra checking around this in order to provide @@ -185,14 +184,14 @@ def create(self, validated_data): ModelClass = self.Meta.model # Remove relationship instances from validated_data. # They are not valid arguments to the default `.create()` method, - # as they require that the instance has already been saved. + # as they require that the instance has already been saved relations = model_meta.get_relations_data(ModelClass, validated_data) session = settings.SQLALCHEMY_SESSION(expire_on_commit=False) try: instance = ModelClass(**validated_data) # After creating instance of ModelClass, just append all data, - # which are removed earlier. + # which are removed earlier if relations: session.enable_relationship_loading(instance) for field_name, value in relations.items(): @@ -230,7 +229,7 @@ def update(self, instance, validated_data): # Generate filter for getting existing object once more. Earlier, # We generate query to the database, because we can get relationship # objects, which attached to the existing object. But can't use this - # instance further. To avoid issues with it, just get object once more. + # instance further. To avoid issues with it, just get object once more filter_args = ( getattr(ModelClass, field) == getattr(instance, field) for field in model_meta.model_pk(ModelClass) @@ -239,7 +238,7 @@ def update(self, instance, validated_data): # Simply set each attribute on the instance, and then save it. # Note that unlike `.create()` we don't need to treat many-to-many # relationships as being a special case. During updates we already - # have an instance pk for the relationships to be associated with. + # have an instance pk for the relationships to be associated with session = settings.SQLALCHEMY_SESSION(expire_on_commit=False) try: instance = session.query(ModelClass).filter(*filter_args).first() @@ -274,8 +273,7 @@ def run_validation(self, data=empty): try: self.run_validators(value) value = self.validate(value) - assert value is not None, '.validate() should return the ' \ - 'validated data' + assert value is not None, '.validate() should return the validated data' # NOQA except (ValidationError, AssertionError) as exc: raise ValidationError(detail=get_validation_error_detail(exc)) @@ -316,7 +314,7 @@ def _get_model_fields(self, field_names, declared_fields, extra_kwargs): if '.' in source or source == '*': # Model fields will always have a simple source mapping, - # they can't be nested attribute lookups. + # they can't be nested attribute lookups continue self._bind_field(model, source, model_fields) @@ -387,18 +385,14 @@ def build_field(self, field_name, info, model_class, nested_depth): """ if field_name in info.fields_and_pk: model_field = info.fields_and_pk[field_name] - return self.build_standard_field( - field_name, model_field, model_class - ) + return self.build_standard_field(field_name, model_field, model_class) # NOQA elif field_name in info.relations: relation_info = info.relations[field_name] if not nested_depth: return self.build_relational_field(field_name, relation_info) else: - return self.build_nested_field( - field_name, relation_info, nested_depth - ) + return self.build_nested_field(field_name, relation_info, nested_depth) # NOQA elif hasattr(model_class, field_name): return self.build_property_field(field_name, model_class) @@ -420,7 +414,7 @@ def build_standard_field(self, *args, **kwargs): if 'choices' in field_kwargs: # Fields with choices get coerced into `ChoiceField` - # instead of using their regular typed field. + # instead of using their regular typed field field_class = self.serializer_choice_field # Some model fields may introduce kwargs that would not be valid # for the choice field. We need to strip these out. @@ -439,17 +433,17 @@ def build_standard_field(self, *args, **kwargs): if not issubclass(field_class, ModelField): # `model_field` is only valid for the fallback case of # `ModelField`, which is used when no other typed field - # matched to the model field. + # matched to the model field field_kwargs.pop('model_field', None) if not issubclass(field_class, CharField) and \ not issubclass(field_class, EnumField): - # `allow_blank` is only valid for textual fields. + # `allow_blank` is only valid for textual fields field_kwargs.pop('allow_blank', None) if isinstance(model_field.type, postgresql.ARRAY): # Populate the `child` argument on `ListField` instances generated - # for the PostgreSQL specific `ArrayField`. + # for the PostgreSQL specific `ArrayField` child_field_name = '%s.%s' % (field_name, 'child') child_model_field = model_field.type.item_type # Because our model_field, which passed to the build_standard_field @@ -476,10 +470,10 @@ def build_relational_field(self, *args, **kwargs): field_kwargs['many'] = True # `to_field` is only valid for slug fields, which aren't - # supported by SQLAlchemy ORM by default. + # supported by SQLAlchemy ORM by default field_kwargs.pop('to_field', None) - # `view_name` is only valid for hyperlinked relationships. + # `view_name` is only valid for hyperlinked relationships if not issubclass(field_class, HyperlinkedRelatedField): field_kwargs.pop('view_name', None) diff --git a/aiorest_ws/db/orm/sqlalchemy/validators.py b/aiorest_ws/db/orm/sqlalchemy/validators.py index 0a6e5dc..b77ea17 100644 --- a/aiorest_ws/db/orm/sqlalchemy/validators.py +++ b/aiorest_ws/db/orm/sqlalchemy/validators.py @@ -7,9 +7,7 @@ from aiorest_ws.db.orm.validators import BaseValidator, \ BaseUniqueFieldValidator -__all__ = ( - 'ORMFieldValidator', 'UniqueORMValidator', -) +__all__ = ('ORMFieldValidator', 'UniqueORMValidator', ) class ORMFieldValidator(BaseValidator): diff --git a/aiorest_ws/db/orm/validators.py b/aiorest_ws/db/orm/validators.py index ca54081..c8b76f8 100644 --- a/aiorest_ws/db/orm/validators.py +++ b/aiorest_ws/db/orm/validators.py @@ -44,6 +44,9 @@ def __eq__(self, other): class MaxValueValidator(BaseValidator): + """ + Validator for checking maximal value of input number. + """ message = u"Ensure this value is less than or equal to {max_value}." def __init__(self, max_value, *args, **kwargs): @@ -59,6 +62,9 @@ def __call__(self, value): class MinValueValidator(BaseValidator): + """ + Validator for checking minimal value of input number. + """ message = u"Ensure this value is greater than or equal to {min_value}." def __init__(self, min_value, *args, **kwargs): @@ -74,6 +80,9 @@ def __call__(self, value): class MaxLengthValidator(BaseValidator): + """ + Validator for checking maximum length of input string. + """ message = u"Ensure that this value has no more {max_length} characters." def __init__(self, max_length, *args, **kwargs): @@ -90,6 +99,9 @@ def __call__(self, value): class MinLengthValidator(BaseValidator): + """ + Validator for checking minimum length of input string. + """ message = u"Ensure that this value has minimum {min_length} characters." def __init__(self, min_length, *args, **kwargs): @@ -106,6 +118,9 @@ def __call__(self, value): class EnumValidator(BaseValidator): + """ + Validator for checking input keys of passed Enum. + """ message = u"Ensure that passed value is one of the allowable: {key_list}." def __init__(self, enum, *args, **kwargs): diff --git a/aiorest_ws/db/utils.py b/aiorest_ws/db/utils.py index e8ddc16..0100482 100644 --- a/aiorest_ws/db/utils.py +++ b/aiorest_ws/db/utils.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- """ - Functions for processing data from raw SQL queries. +Functions for processing data from raw SQL queries. """ __all__ = ('convert_db_row_to_dict', ) def convert_db_row_to_dict(row, mapped_fields): - """Convert row of database to dictionary. + """ + Convert row of database to dictionary. :param row: row of database. :param mapped_fields: list of tuple, which means field names. diff --git a/aiorest_ws/decorators.py b/aiorest_ws/decorators.py index 3768e3d..8969a30 100644 --- a/aiorest_ws/decorators.py +++ b/aiorest_ws/decorators.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - Decorators and wrappers, used for routing issues. +Decorators and wrappers, used for routing issues. """ from aiorest_ws.validators import MethodValidator from aiorest_ws.views import MethodBasedView @@ -9,7 +9,8 @@ def endpoint(path, methods, name=None, **attrs): - """Decorator function, which turn handler into MethodBasedView class. + """ + Decorator function, which turn handler into MethodBasedView class. :param path: URL, used for get access to APIs. :param methods: acceptable method name or list of methods. diff --git a/aiorest_ws/endpoints.py b/aiorest_ws/endpoints.py index cee5585..c3f46d5 100644 --- a/aiorest_ws/endpoints.py +++ b/aiorest_ws/endpoints.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - Endpoint classes for aiorest-ws router. +Endpoint classes for aiorest-ws router. """ from aiorest_ws.abstract import AbstractEndpoint @@ -10,7 +10,8 @@ class PlainEndpoint(AbstractEndpoint): def match(self, path): - """Checking path on compatible. + """ + Checking path on compatible. :param path: URL, which used for get access to API. """ @@ -27,12 +28,13 @@ def __init__(self, path, methods, handler, name, pattern): self._pattern = pattern def match(self, path): - """Checking path on compatible. + """ + Checking path on compatible. :param path: URL, which used for get access to API. """ match_result = self._pattern.match(path) - # if comparing has successful, then return list of parsed values + # If comparing has successful, then return list of parsed values if match_result: match_result = match_result.groups() return match_result diff --git a/aiorest_ws/exceptions.py b/aiorest_ws/exceptions.py index abedc74..6dfaba2 100644 --- a/aiorest_ws/exceptions.py +++ b/aiorest_ws/exceptions.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ - Handled exceptions raised by aiorest-ws framework, which inspired under - Django REST framework. +Handled exceptions raised by aiorest-ws framework, which inspired under +Django REST framework. """ from aiorest_ws.status import WS_PROTOCOL_ERROR, WS_DATA_CANNOT_ACCEPT from aiorest_ws.utils.encoding import force_text @@ -24,7 +24,8 @@ class ImproperlyConfigured(Exception): class BaseAPIException(Exception): - """Base class for aiorest-ws framework exceptions. + """ + Base class for aiorest-ws framework exceptions. All subclasses should provide `.status_code` and `.default_detail` properties. @@ -33,7 +34,8 @@ class BaseAPIException(Exception): default_detail = u"A server error occurred." def __init__(self, detail=None): - """Create an instance of exception with users detail information if + """ + Create an instance of exception with users detail information if it is passed. :param detail: users detail information (string). diff --git a/aiorest_ws/log.py b/aiorest_ws/log.py index 244cd6f..2b5b909 100644 --- a/aiorest_ws/log.py +++ b/aiorest_ws/log.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - Logging tool for aiorest-ws framework. +Logging tool for aiorest-ws framework. """ import logging import logging.config diff --git a/aiorest_ws/parsers.py b/aiorest_ws/parsers.py index 7efb5dc..30dd193 100644 --- a/aiorest_ws/parsers.py +++ b/aiorest_ws/parsers.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - URL parsers, which help to define, with which endpoint router works. +URL parsers, which help to define, with which endpoint router works. """ import re @@ -17,12 +17,13 @@ class URLParser(object): - """Parser over endpoints paths, which returns one of the most suitable + """ + Parser over endpoints paths, which returns one of the most suitable instances of route classes. """ - def define_route(self, path, handler, methods, name=None): - """Define a router as instance of BaseRoute subclass, which passed + """ + Define a router as instance of BaseRoute subclass, which passed from register method in RestWSRouter. :param path: URL, which used to get access to API. @@ -36,7 +37,7 @@ def define_route(self, path, handler, methods, name=None): if all(symbol not in path for symbol in ['{', '}']): return PlainEndpoint(path, handler, methods, name) - # try to processing as a dynamic path + # Try to processing as a dynamic path pattern = '' for part in DYNAMIC_PARAMETER.split(path): match = VALID_DYNAMIC_PARAMETER.match(part) @@ -45,13 +46,11 @@ def define_route(self, path, handler, methods, name=None): continue if any(symbol in part for symbol in ['{', '}']): - raise EndpointValueError("Invalid {} part of {} path" - .format(part, path)) + raise EndpointValueError("Invalid {} part of {} path".format(part, path)) # NOQA pattern += re.escape(part) try: compiled = re.compile("^{}$".format(pattern)) except re.error as exc: - raise EndpointValueError("Bad pattern '{}': {}" - .format(pattern, exc)) + raise EndpointValueError("Bad pattern '{}': {}".format(pattern, exc)) # NOQA return DynamicEndpoint(path, handler, methods, name, compiled) diff --git a/aiorest_ws/renderers.py b/aiorest_ws/renderers.py index 7d46539..7092703 100644 --- a/aiorest_ws/renderers.py +++ b/aiorest_ws/renderers.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - Serializers for generated responses by the server. +Serializers for generated responses by the server. """ import json @@ -11,7 +11,7 @@ WRONG_UNICODE_SYMBOLS from aiorest_ws.utils.xmlutils import SimpleXMLGenerator -__all__ = ('BaseRenderer', 'JSONRenderer', 'XMLRenderer',) +__all__ = ('BaseRenderer', 'JSONRenderer', 'XMLRenderer', ) class BaseRenderer(object): @@ -20,7 +20,8 @@ class BaseRenderer(object): charset = 'utf-8' def render(self, data): - """Render input data into another format. + """ + Render input data into another format. :param data: dictionary object. """ @@ -30,16 +31,17 @@ def render(self, data): class JSONRenderer(BaseRenderer): format = 'json' - # don't set a charset because JSON is a binary encoding, that can be + # Don't set a charset because JSON is a binary encoding, that can be # encoded as utf-8, utf-16 or utf-32. - # for more details see: http://www.ietf.org/rfc/rfc4627.txt + # For more details see: http://www.ietf.org/rfc/rfc4627.txt # and Armin Ronacher's article http://goo.gl/MExCKv charset = None ensure_ascii = not settings.UNICODE_JSON compact = settings.COMPACT_JSON def render(self, data): - """Render input data into JSON. + """ + Render input data into JSON. :param data: dictionary or list object (response). """ @@ -50,10 +52,10 @@ def render(self, data): data, ensure_ascii=self.ensure_ascii, separators=separators ) - # unicode symbols \u2028 and \u2029 are invisible in JSON and + # Unicode symbols \u2028 and \u2029 are invisible in JSON and # make output are invalid. To avoid this situations, necessary # replace this symbols. - # for more information read this article: http://goo.gl/ImC89E + # For more information read this article: http://goo.gl/ImC89E for wrong_symbol, expected in WRONG_UNICODE_SYMBOLS: render = render.replace(wrong_symbol, expected) @@ -69,7 +71,8 @@ class XMLRenderer(BaseRenderer): xml_generator = SimpleXMLGenerator def render(self, data): - """Render input data into XML. + """ + Render input data into XML. :param data: dictionary or list object (response). """ diff --git a/aiorest_ws/request.py b/aiorest_ws/request.py index a7536dc..69718cc 100644 --- a/aiorest_ws/request.py +++ b/aiorest_ws/request.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - Classes and function for creating and processing requests from user. +Classes and function for creating and processing requests from user. """ import asyncio import json @@ -18,38 +18,42 @@ class RequestHandlerProtocol(WebSocketServerProtocol): - """REST WebSocket protocol instance, creating for every client connection. + """ + REST WebSocket protocol instance, creating for every client connection. This protocol describe how to process network events (users requests to APIs) asynchronously. """ def _decode_message(self, payload, isBinary=False): - """Decoding input message to Request object. + """ + Decoding input message to Request object. :param payload: input message. :param isBinary: boolean value, means that received data had a binary format. """ - # message was taken in base64 + # Message was taken in base64 if isBinary: payload = b64decode(payload) input_data = json.loads(payload.decode('utf-8')) return Request(**input_data) def _encode_message(self, response, isBinary=False): - """Encoding output message. + """ + Encoding output message. :param response: output message. :param isBinary: boolean value, means that received data had a binary format. """ - # encode additionally to base64 if necessary + # Encode additionally to base64 if necessary if isBinary: response = b64encode(response) return response @asyncio.coroutine def onMessage(self, payload, isBinary): - """Handler, called for every message which was sent from the some user. + """ + Handler, called for every message which was sent from the some user. :param payload: input message. :param isBinary: boolean value, means that received data had a binary @@ -62,7 +66,8 @@ def onMessage(self, payload, isBinary): class RequestHandlerFactory(WebSocketServerFactory): - """REST WebSocket server factory, which instantiates client connections. + """ + REST WebSocket server factory, which instantiates client connections. NOTE: Persistent configuration information is not saved in the instantiated protocol. For such cases kept data in a Factory classes, databases, etc. @@ -73,12 +78,15 @@ def __init__(self, *args, **kwargs): @property def router(self): - """Get router instance.""" + """ + Get router instance. + """ return self._router @router.setter def router(self, router): - """Set router instance. + """ + Set router instance. :param router: instance of class, which derived from AbstractRouter. """ diff --git a/aiorest_ws/routers.py b/aiorest_ws/routers.py index da19ede..57747fd 100644 --- a/aiorest_ws/routers.py +++ b/aiorest_ws/routers.py @@ -1,15 +1,15 @@ # -*- coding: utf-8 -*- """ - This modules provide a functions and classes, which every developer - can used for determine URL for their APIs. +This modules provide a functions and classes, which every developer can use +for determine URL for their APIs. - For example, we can use this features something like this: +For example, we can use this features something like this: - router = SimpleRouter() - router.register('user/info', info_handler, methods='GET') - router.register('user/register', register_handler, methods='POST') - router.register('user/profile/{user_name}', user_handler, - methods=['GET', 'PUT']) + router = SimpleRouter() + router.register('user/info', info_handler, methods='GET') + router.register('user/register', register_handler, methods='POST') + router.register('user/profile/{user_name}', user_handler, + methods=['GET', 'PUT']) """ from aiorest_ws.abstract import AbstractEndpoint, AbstractRouter from aiorest_ws.exceptions import BaseAPIException, EndpointValueError, \ @@ -24,12 +24,15 @@ class SimpleRouter(AbstractRouter): - """Default router class, used for working with REST over WebSockets.""" + """ + Default router class, used for working with REST over WebSockets. + """ args_validator = RouteArgumentsValidator() url_parser = URLParser() def _correct_path(self, path): - """Convert path to valid value. + """ + Convert path to valid value. :param path: URL, which used to get access to API. """ @@ -39,7 +42,8 @@ def _correct_path(self, path): return path def register(self, path, handler, methods, name=None): - """Add new endpoint to the router. + """ + Add new endpoint to the router. :param path: URL, which used to get access to API. :param handler: inherited class from the MethodBasedView, which used @@ -55,7 +59,8 @@ def register(self, path, handler, methods, name=None): self._register_url(route) def register_endpoint(self, endpoint): - """Add new endpoint to the router. + """ + Add new endpoint to the router. :param endpoint: function with @endpoint decorator, which used for processing request. @@ -64,7 +69,8 @@ def register_endpoint(self, endpoint): self.register(path, handler, methods, name) def extract_url(self, request): - """Extracting URL parameter for request. + """ + Extracting URL parameter for request. :param request: request from the user. """ @@ -73,7 +79,8 @@ def extract_url(self, request): return self._correct_path(request.url) def search_handler(self, request, url): - """Searching handler by URL. + """ + Searching handler by URL. :param request: request from user. :param url: path to the registered endpoint. @@ -81,9 +88,9 @@ def search_handler(self, request, url): args = () kwargs = {} handler = None - # iterate over the all endpoints + # Iterate over the all endpoints for route in self._urls: - # find match between endpoint and request URL + # Find match between endpoint and request URL match = route.match(url) if match is not None: handler = route.handler() @@ -95,7 +102,8 @@ def search_handler(self, request, url): return handler, args, kwargs def process_request(self, request): - """Handle received request from user. + """ + Handle received request from user. :param request: request from user. """ @@ -110,13 +118,13 @@ def process_request(self, request): url = self.extract_url(request) handler, args, kwargs = self.search_handler(request, url) - # invoke handler for request + # Invoke handler for request if handler: for middleware in self.middlewares: middleware.process_request(request, handler) - # search serializer for response + # Search serializer for response format = request.get_argument('format') serializer = handler.get_renderer(format, *args, **kwargs) @@ -132,7 +140,8 @@ def process_request(self, request): return serializer.render(response.content) def _register_url(self, route): - """Register new endpoint. + """ + Register new endpoint. :param route: instance of class, inherited from AbstractEndpoint. """ @@ -150,9 +159,10 @@ def _register_url(self, route): self._urls.append(route) def include(self, router): - """Appending endpoints from another router to self. + """ + Appending endpoints from another router to self. - :param router: instance of subclass, derived from AbstractRouter + :param router: instance of subclass, derived from AbstractRouter. """ if not issubclass(type(router), (AbstractRouter, )): raise TypeError(u"Passed router must be inherited from the " diff --git a/aiorest_ws/status.py b/aiorest_ws/status.py index 3d25de0..0fa7057 100644 --- a/aiorest_ws/status.py +++ b/aiorest_ws/status.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- """ - WebSocket status codes and functions for work with them. +WebSocket status codes and functions for work with them. - For more details check the link below: - https://tools.ietf.org/html/rfc6455 +For more details check the link below: + https://tools.ietf.org/html/rfc6455 """ __all__ = ( 'WS_NORMAL', 'WS_GOING_AWAY', 'WS_PROTOCOL_ERROR', @@ -12,7 +12,6 @@ 'WS_MESSAGE_VIOLATE_POLICY', 'WS_MESSAGE_TOO_BIG', 'WS_SERVER_DIDNT_RETURN_EXTENSIONS', 'WS_UNEXPECTED_CONDITION', 'WS_FAILURE_TLS', - 'is_not_used', 'is_reserved', 'is_library', 'is_private', ) @@ -33,7 +32,8 @@ def is_not_used(code): - """Checking code, that is unused. + """ + Checking code, that is unused. :param code: integer value. """ @@ -41,7 +41,8 @@ def is_not_used(code): def is_reserved(code): - """Checking code, that is reserved. + """ + Checking code, that is reserved. :param code: integer value. """ @@ -49,7 +50,8 @@ def is_reserved(code): def is_library(code): - """Checking code, that is value, used by libraries. + """ + Checking code, that is value, used by libraries. :param code: integer value. """ @@ -57,7 +59,8 @@ def is_library(code): def is_private(code): - """Checking code, that is private code. + """ + Checking code, that is private code. :param code: integer value. """ diff --git a/aiorest_ws/storages/backends.py b/aiorest_ws/storages/backends.py index 370c1bb..5156950 100644 --- a/aiorest_ws/storages/backends.py +++ b/aiorest_ws/storages/backends.py @@ -1,16 +1,22 @@ # -*- coding: utf-8 -*- """ - Backend classes for storages. +Backend classes for storages. """ __all__ = ('BaseStorageBackend', ) class BaseStorageBackend(object): - """Base interface for storage.""" + """ + Base interface for storage. + """ def get(self, *args, **kwargs): - """Get object from the storage.""" + """ + Get object from the storage. + """ pass def save(self, *args, **kwargs): - """Save object in the storage.""" + """ + Save object in the storage. + """ pass diff --git a/aiorest_ws/test/utils.py b/aiorest_ws/test/utils.py index efc1981..7533b70 100644 --- a/aiorest_ws/test/utils.py +++ b/aiorest_ws/test/utils.py @@ -113,7 +113,7 @@ def save_options(self, test_func): if test_func._overridden_settings is None: test_func._overridden_settings = self.options else: - # Duplicate dict to prevent subclasses from altering their parent. + # Duplicate dict to prevent subclasses from altering their parent test_func._overridden_settings = dict( test_func._overridden_settings, **self.options ) diff --git a/aiorest_ws/urls/base.py b/aiorest_ws/urls/base.py index 1eb567d..ae7c5f1 100644 --- a/aiorest_ws/urls/base.py +++ b/aiorest_ws/urls/base.py @@ -10,7 +10,7 @@ def set_urlconf(urlconf_data): """ - Set the _urlconf for the current thread + Set the _urlconf for the current thread. """ _urlconfs['data'] = urlconf_data diff --git a/aiorest_ws/utils/date/dateparse.py b/aiorest_ws/utils/date/dateparse.py index 7e3743a..c90c5d5 100644 --- a/aiorest_ws/utils/date/dateparse.py +++ b/aiorest_ws/utils/date/dateparse.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - Functions to parse datetime objects. +Functions to parse datetime objects. """ # We're using regular expressions rather than time.strptime because: # - They provide both validation and parsing. @@ -59,7 +59,8 @@ def parse_date(value): - """Parses a string and return a datetime.date. + """ + Parses a string and return a datetime.date. Raises ValueError if the input is well formatted but not a valid date. Returns None if the input isn't well formatted. @@ -71,7 +72,8 @@ def parse_date(value): def parse_time(value): - """Parses a string and return a datetime.time. + """ + Parses a string and return a datetime.time. This function doesn't support time zone offsets. @@ -89,7 +91,8 @@ def parse_time(value): def parse_datetime(value): - """Parses a string and return a datetime.datetime. + """ + Parses a string and return a datetime.datetime. This function supports time zone offsets. When the input contains one, the output uses a timezone with a fixed offset from UTC. @@ -155,7 +158,8 @@ def parse_timedelta(value): def parse_duration(value): - """Parses a duration string and returns a datetime.timedelta. + """ + Parses a duration string and returns a datetime.timedelta. The preferred format for durations in Django is '%d %H:%M:%S.%f'. Also supports ISO 8601 representation. """ diff --git a/aiorest_ws/utils/date/formatters.py b/aiorest_ws/utils/date/formatters.py index 4c0caa8..49527bb 100644 --- a/aiorest_ws/utils/date/formatters.py +++ b/aiorest_ws/utils/date/formatters.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -__all__ = ( - 'iso8601_repr', -) +__all__ = ('iso8601_repr', ) def iso8601_repr(timedelta, format=None): diff --git a/aiorest_ws/utils/date/humanize_datetime.py b/aiorest_ws/utils/date/humanize_datetime.py index e053db1..784089e 100644 --- a/aiorest_ws/utils/date/humanize_datetime.py +++ b/aiorest_ws/utils/date/humanize_datetime.py @@ -41,7 +41,7 @@ def time_formats(formats): def humanize_strptime(format_string): # Note that we're missing some of the locale specific mappings that - # don't really make sense. + # don't really make sense mapping = { "%Y": "YYYY", "%y": "YY", @@ -50,7 +50,7 @@ def humanize_strptime(format_string): "%B": "[January-December]", "%d": "DD", "%H": "hh", - "%I": "hh", # Requires '%p' to differentiate from '%H'. + "%I": "hh", # Requires '%p' to differentiate from '%H' "%M": "mm", "%S": "ss", "%f": "uuuuuu", diff --git a/aiorest_ws/utils/date/timezone.py b/aiorest_ws/utils/date/timezone.py index 1dd6992..5731c47 100644 --- a/aiorest_ws/utils/date/timezone.py +++ b/aiorest_ws/utils/date/timezone.py @@ -147,7 +147,7 @@ def _isdst(self, dt): exc.__traceback__ = sys.exc_info()[2] raise exc_value -utc = pytz.utc if pytz else UTC() # UTC time zone as a tzinfo instance. +utc = pytz.utc if pytz else UTC() # UTC time zone as a tzinfo instance def get_fixed_timezone(offset): @@ -163,7 +163,7 @@ def get_fixed_timezone(offset): # In order to avoid accessing settings at compile time, -# wrap the logic in a function and cache the result. +# wrap the logic in a function and cache the result def get_default_timezone(): """ Returns the default time zone as a tzinfo instance. @@ -173,7 +173,7 @@ def get_default_timezone(): if isinstance(settings.TIME_ZONE, str) and pytz is not None: return pytz.timezone(settings.TIME_ZONE) else: - # This relies on os.environ['TZ'] being set to settings.TIME_ZONE. + # This relies on os.environ['TZ'] being set to settings.TIME_ZONE return LocalTimezone() @@ -199,10 +199,10 @@ def _get_timezone_name(timezone): Returns the name of ``timezone``. """ try: - # for pytz timezones + # For pytz timezones return timezone.zone except AttributeError: - # for regular tzinfo objects + # For regular tzinfo objects return timezone.tzname(None) @@ -216,10 +216,10 @@ def localtime(value, timezone=None): if timezone is None: timezone = get_current_timezone() # If `value` is naive, astimezone() will raise a ValueError, - # so we don't need to perform a redundant check. + # so we don't need to perform a redundant check value = value.astimezone(timezone) if hasattr(timezone, 'normalize'): - # This method is available for pytz time zones. + # This method is available for pytz time zones value = timezone.normalize(value) return value @@ -229,14 +229,14 @@ def now(): Returns an aware or naive datetime.datetime, depending on settings.USE_TZ. """ if settings.USE_TZ: - # timeit shows that datetime.now(tz=utc) is 24% slower + # Timeit shows that datetime.now(tz=utc) is 24% slower return datetime.utcnow().replace(tzinfo=utc) else: return datetime.now() # By design, these four functions don't perform any checks on their arguments. -# The caller should ensure that they don't receive an invalid value like None. +# The caller should ensure that they don't receive an invalid value like None def is_aware(value): """ @@ -271,10 +271,10 @@ def make_aware(value, timezone=None, is_dst=None): if timezone is None: timezone = get_current_timezone() if hasattr(timezone, 'localize'): - # This method is available for pytz time zones. + # This method is available for pytz time zones return timezone.localize(value, is_dst=is_dst) else: - # Check that we won't overwrite the timezone of an aware datetime. + # Check that we won't overwrite the timezone of an aware datetime if is_aware(value): raise ValueError( "make_aware expects a naive datetime, got %s" % value) @@ -289,9 +289,9 @@ def make_naive(value, timezone=None): if timezone is None: timezone = get_current_timezone() # If `value` is naive, astimezone() will raise a ValueError, - # so we don't need to perform a redundant check. + # so we don't need to perform a redundant check value = value.astimezone(timezone) if hasattr(timezone, 'normalize'): - # This method is available for pytz time zones. + # This method is available for pytz time zones value = timezone.normalize(value) return value.replace(tzinfo=None) diff --git a/aiorest_ws/utils/encoding.py b/aiorest_ws/utils/encoding.py index 9616a3a..fcca607 100644 --- a/aiorest_ws/utils/encoding.py +++ b/aiorest_ws/utils/encoding.py @@ -19,7 +19,8 @@ def is_protected_type(obj): - """Determine if the object instance is of a protected type. + """ + Determine if the object instance is of a protected type. Objects of protected types are preserved as-is when passed to force_text(strings_only=True). """ @@ -32,7 +33,7 @@ def force_text(s, encoding='utf-8', strings_only=False, errors='strict'): strings, rather than kept as lazy objects. If strings_only is True, don't convert (some) non-string-like objects. """ - # Handle the common case first for performance reasons. + # Handle the common case first for performance reasons if issubclass(type(s), str): return s if strings_only and is_protected_type(s): @@ -46,14 +47,14 @@ def force_text(s, encoding='utf-8', strings_only=False, errors='strict'): else: # Note: We use .decode() here, instead of six.text_type(s, # encoding, errors), so that if s is a SafeBytes, it ends up - # being a SafeText at the end. + # being a SafeText at the end s = s.decode(encoding, errors) except UnicodeDecodeError: # If we get to here, the caller has passed in an Exception # subclass populated with non-ASCII bytestring data without a # working unicode method. Try to handle this without raising a # further exception by individually forcing the exception args - # to unicode. + # to unicode s = ' '.join(force_text(arg, encoding, strings_only, errors) for arg in s) return s diff --git a/aiorest_ws/utils/field_mapping.py b/aiorest_ws/utils/field_mapping.py index dc2c942..27b428a 100644 --- a/aiorest_ws/utils/field_mapping.py +++ b/aiorest_ws/utils/field_mapping.py @@ -23,7 +23,7 @@ def __init__(self, mapping): def __getitem__(self, key): if hasattr(key, '_proxy_class'): # Deal with proxy classes. Ie. BoundField behaves as if it - # is a Field instance when using ClassLookupDict. + # is a Field instance when using ClassLookupDict base_class = key._proxy_class else: base_class = key.__class__ diff --git a/aiorest_ws/utils/fields.py b/aiorest_ws/utils/fields.py index dfba96d..9e878ca 100644 --- a/aiorest_ws/utils/fields.py +++ b/aiorest_ws/utils/fields.py @@ -45,7 +45,7 @@ def get_attribute(instance, attrs): """ for attr in attrs: if instance is None: - # Break out early if we get `None` at any point in a nested lookup. + # Break out early if we get `None` at any point in a nested lookup return None if isinstance(instance, collections.Mapping): @@ -59,7 +59,7 @@ def get_attribute(instance, attrs): except (AttributeError, KeyError) as exc: # If we raised an Attribute or KeyError here it'd get treated # as an omitted field in `Field.get_attribute()`. Instead we - # raise a ValueError to ensure the exception is not masked. + # raise a ValueError to ensure the exception is not masked raise ValueError( 'Exception raised in callable attribute "{0}"; ' 'original exception was: {1}'.format(attr, exc) @@ -102,15 +102,15 @@ def to_choices_dict(choices): ret = collections.OrderedDict() for choice in choices: if (not isinstance(choice, (list, tuple))): - # single choice + # Single choice ret[choice] = choice else: key, value = choice if isinstance(value, (list, tuple)): - # grouped choices (category, sub choices) + # Grouped choices (category, sub choices) ret[key] = to_choices_dict(value) else: - # paired choice (key, display value) + # Paired choice (key, display value) ret[key] = value return ret @@ -124,10 +124,10 @@ def flatten_choices_dict(choices): ret = collections.OrderedDict() for key, value in choices.items(): if isinstance(value, dict): - # grouped choices (category, sub choices) + # Grouped choices (category, sub choices) for sub_key, sub_value in value.items(): ret[sub_key] = sub_value else: - # choice (key, display value) + # Choice (key, display value) ret[key] = value return ret diff --git a/aiorest_ws/utils/modify.py b/aiorest_ws/utils/modify.py index ae961b6..31ec93d 100644 --- a/aiorest_ws/utils/modify.py +++ b/aiorest_ws/utils/modify.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- """ - Functions and decorators used for modify classes on the fly. +Functions and decorators used for modify classes on the fly. """ __all__ = ('add_property', ) def add_property(instance, field_name, value): - """Append property to the current instance of class. + """ + Append property to the current instance of class. :param instance: modified object. :param field_name: attribute name in class. diff --git a/aiorest_ws/utils/representation.py b/aiorest_ws/utils/representation.py index 325d28d..3daa710 100644 --- a/aiorest_ws/utils/representation.py +++ b/aiorest_ws/utils/representation.py @@ -60,15 +60,13 @@ def serializer_repr(serializer, indent, force_many=None): elif hasattr(field, 'child'): ret += list_repr(field, indent + 1) elif hasattr(field, 'child_relation'): - ret += field_repr(field.child_relation, - force_many=field.child_relation) + ret += field_repr(field.child_relation, force_many=field.child_relation) # NOQA else: ret += field_repr(field) if serializer.validators: ret += '\n' + indent_str + 'class Meta:' - ret += '\n' + indent_str + ' validators = ' + \ - smart_repr(serializer.validators) + ret += '\n' + indent_str + ' validators = ' + smart_repr(serializer.validators) # NOQA return ret diff --git a/aiorest_ws/utils/serializer_helpers.py b/aiorest_ws/utils/serializer_helpers.py index 7d4f6f9..afb3f16 100644 --- a/aiorest_ws/utils/serializer_helpers.py +++ b/aiorest_ws/utils/serializer_helpers.py @@ -33,7 +33,7 @@ def __repr__(self): def __reduce__(self): # Pickling these objects will drop the .serializer backlink, - # but preserve the raw data. + # but preserve the raw data return dict, (dict(self),) diff --git a/aiorest_ws/utils/validators.py b/aiorest_ws/utils/validators.py index 22c30f7..637e5dc 100644 --- a/aiorest_ws/utils/validators.py +++ b/aiorest_ws/utils/validators.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ - This module contains function, which using at validators.py for type - checking issues. +This module contains function, which using at validators.py for type +checking issues. """ from inspect import isclass @@ -9,7 +9,9 @@ def to_str(obj): - """Custom convert object to string representation.""" + """ + Custom convert object to string representation. + """ if isinstance(obj, (list, tuple)): string = "/".join([item.__name__ for item in obj]) else: @@ -18,9 +20,7 @@ def to_str(obj): def get_object_type(value): - """Getting object type.""" - if not isclass(value): - obj_type = type(value) - else: - obj_type = value - return obj_type + """ + Getting object type. + """ + return type(value) if not isclass(value) else value diff --git a/aiorest_ws/utils/websocket.py b/aiorest_ws/utils/websocket.py index ff90819..38ffcb0 100644 --- a/aiorest_ws/utils/websocket.py +++ b/aiorest_ws/utils/websocket.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ - This module contains classes and functions, used for configuration - issues with websockets. +This module contains classes and functions, used for configuration +issues with websockets. """ from autobahn.websocket.compress import PerMessageDeflateOffer, \ PerMessageDeflateOfferAccept @@ -10,7 +10,8 @@ def deflate_offer_accept(offers): - """Function to accept offers from the client. + """ + Function to accept offers from the client. :param offers: iterable object (list, tuple), where every object is instance of PerMessageDeflateOffer. diff --git a/aiorest_ws/utils/xmlutils.py b/aiorest_ws/utils/xmlutils.py index 2967bfa..bf9c2b0 100644 --- a/aiorest_ws/utils/xmlutils.py +++ b/aiorest_ws/utils/xmlutils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - XML classes and functions, used for serializing and de-serializing. +XML classes and functions, used for serializing and de-serializing. """ from xml.sax.saxutils import XMLGenerator @@ -8,7 +8,9 @@ class SimpleXMLGenerator(XMLGenerator): - """XML generator for input data.""" + """ + XML generator for input data. + """ root_name = 'root' item_tag_name = 'list-item' @@ -16,7 +18,8 @@ def __init__(self, stream, encoding='utf-8'): super(SimpleXMLGenerator, self).__init__(stream, encoding=encoding) def parse(self, data): - """Convert data to XML. + """ + Convert data to XML. :param data: input data. """ @@ -27,14 +30,16 @@ def parse(self, data): self.endDocument() def to_str(self, value): - """Encode value for the string. + """ + Encode value for the string. :param value: value, which will have converted to the string. """ return str(value).encode(self._encoding) def to_xml(self, xml, data): - """Convert Python object to XML. + """ + Convert Python object to XML. :param xml: XML object. :param data: Python's object, which will have been converted. diff --git a/aiorest_ws/validators.py b/aiorest_ws/validators.py index b7daa6b..8088d76 100644 --- a/aiorest_ws/validators.py +++ b/aiorest_ws/validators.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - Validator classes and functions for checking passed arguments. +Validator classes and functions for checking passed arguments. """ import inspect @@ -17,17 +17,24 @@ class BaseValidator(object): - """Base class for validators.""" + """ + Base class for validators. + """ def validate(self, *args, **kwargs): - """Validating passed arguments and kwargs.""" + """ + Validating passed arguments and kwargs. + """ pass class EndpointNameValidator(BaseValidator): - """Validator for endpoint name argument.""" + """ + Validator for endpoint name argument. + """ def _check_name(self, name): - """Validate passed name variable. + """ + Validate passed name variable. :param name: the base to use for the URL names that are created. """ @@ -36,7 +43,8 @@ def _check_name(self, name): u'be string type') def validate(self, name): - """Validate passed name variable. + """ + Validate passed name variable. :param name: the base to use for the URL names that are created. """ @@ -44,9 +52,12 @@ def validate(self, name): class HandlerValidator(BaseValidator): - """Validator for handler argument.""" + """ + Validator for handler argument. + """ def _check_handler(self, handler): - """Validate passed handler for requests. + """ + Validate passed handler for requests. :param handler: class or function (wrapped into MethodBasedView), which used for generating response. @@ -59,7 +70,8 @@ def _check_handler(self, handler): raise InvalidHandler() def validate(self, handler): - """Validate passed handler for requests. + """ + Validate passed handler for requests. :param handler: class or function, used for generating response. """ @@ -67,11 +79,14 @@ def validate(self, handler): class MethodValidator(BaseValidator): - """Validator for methods argument.""" + """ + Validator for methods argument. + """ supported_methods_types = (list, str) def _check_methods(self, methods): - """Validate passed methods variable. + """ + Validate passed methods variable. :param methods: list of methods or string with concrete method name. """ @@ -88,33 +103,41 @@ def validate(self, methods): class PathValidator(BaseValidator): - """Validator for path argument.""" + """ + Validator for path argument. + """ def _check_path(self, path): - """Validate passed path for endpoint. + """ + Validate passed path for endpoint. - :param path: path to endpoint (string) + :param path: path to endpoint (string). """ if not path.startswith('/'): raise InvalidPathArgument(u"Path should be started with `/` " u"symbol") def validate(self, path): - """Validate passed path for endpoint. + """ + Validate passed path for endpoint. - :param path: path to endpoint (string) + :param path: path to endpoint (string). """ self._check_path(path) class RouteArgumentsValidator(BaseValidator): - """Validator for arguments of RestWSRouter.""" + """ + Validator for arguments of RestWSRouter. + """ path_validator = PathValidator() handler_validator = HandlerValidator() methods_validator = MethodValidator() endpoint_name_validator = EndpointNameValidator() def validate(self, path, handler, methods, name): - """Validating passed arguments and kwargs.""" + """ + Validating passed arguments and kwargs. + """ self.path_validator.validate(path) self.handler_validator.validate(handler) self.methods_validator.validate(methods) @@ -122,7 +145,8 @@ def validate(self, path, handler, methods, name): def check_and_set_subclass(instance, attribute, value, subclasses): - """Validate subclass of passed value on supported type and set him for + """ + Validate subclass of passed value on supported type and set him for instance of some class. :param instance: object, for with necessary make check and sets value. diff --git a/aiorest_ws/views.py b/aiorest_ws/views.py index 53f52f1..f885386 100644 --- a/aiorest_ws/views.py +++ b/aiorest_ws/views.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ - This module provide a function and class-based views and can be used - with aiorest-ws routers. +This module provide a function and class-based views and can be used +with aiorest-ws routers. """ from aiorest_ws.exceptions import IncorrectMethodNameType, \ InvalidRenderer, NotSpecifiedHandler, NotSpecifiedMethodName @@ -14,7 +14,9 @@ class View(object): - """Subclass for implementing method-based views.""" + """ + Subclass for implementing method-based views. + """ @classmethod def as_view(cls, name, *class_args, **class_kwargs): """Converts the class into an actual view function that can be used @@ -24,47 +26,50 @@ def as_view(cls, name, *class_args, **class_kwargs): class MethodViewMeta(type): - """Metaclass, which helps to define list of supported methods in + """ + Metaclass, which helps to define list of supported methods in class-based views. """ def __new__(cls, name, bases, attrs): obj = type.__new__(cls, name, bases, attrs) - # if not defined 'method' attribute, then make and append him to + # If not defined 'method' attribute, then make and append him to # our class-based view if 'methods' not in attrs: methods = set(obj.methods if hasattr(obj, 'methods') else []) for key in attrs: if key in http_methods: methods.add(key.lower()) - # this action necessary for appending list of supported methods + # This action necessary for appending list of supported methods obj.methods = sorted(methods) return obj class MethodBasedView(View, metaclass=MethodViewMeta): - """Method-based view for aiorest-ws framework.""" + """ + Method-based view for aiorest-ws framework. + """ renderers = () def dispatch(self, request, *args, **kwargs): - """Search the most suitable handler for request. + """ + Search the most suitable handler for request. :param request: passed request from user. """ method = request.method - # invoked, when user not specified method in query (e.c. get, post) + # Invoked, when user not specified method in query (e.c. get, post) if not method: raise NotSpecifiedMethodName() - # invoked, when user specified method name as not a string + # Invoked, when user specified method name as not a string if not isinstance(method, str): raise IncorrectMethodNameType() - # trying to find the most suitable handler - # for that what we are doing: - # 1) make string in lowercase (e.c. 'GET' -> 'get') - # 2) look into class and try to get handler with this name - # 3) if extracting is successful, then invoke handler with arguments + # Trying to find the most suitable handler. For that what we are doing: + # 1) Make string in lowercase (e.c. 'GET' -> 'get') + # 2) Look into class and try to get handler with this name + # 3) If extracting is successful, then invoke handler with arguments method = method.lower().strip() handler = getattr(self, method, None) if not handler: @@ -72,7 +77,8 @@ def dispatch(self, request, *args, **kwargs): return handler(request, *args, **kwargs) def get_renderer(self, preferred_format, *args, **kwargs): - """Get serialize class, which using to converting response to + """ + Get serialize class, which using to converting response to some users format. :param preferred_format: string, which means serializing response to @@ -82,11 +88,11 @@ def get_renderer(self, preferred_format, *args, **kwargs): if type(self.renderers) not in (list, tuple): raise InvalidRenderer() - # by default we are take first serializer from list/tuple + # By default we are take first serializer from list/tuple renderer = self.renderers[0] if preferred_format: - # try to find suitable serializer + # Try to find suitable serializer for renderer_class in self.renderers: if renderer_class.format == preferred_format: renderer = renderer_class diff --git a/aiorest_ws/wrappers.py b/aiorest_ws/wrappers.py index 80c18e5..284c653 100644 --- a/aiorest_ws/wrappers.py +++ b/aiorest_ws/wrappers.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - Wrappers, similar on HTTP requests/responses. +Wrappers, similar on HTTP requests/responses. """ from aiorest_ws.utils.modify import add_property @@ -21,30 +21,41 @@ def __init__(self, *args, **kwargs): @property def method(self): - """Get method type, which defined by the user.""" + """ + Get method type, which defined by the user. + """ return self._method @property def url(self): - """Get the APIs url, from which expected response.""" + """ + Get the APIs url, from which expected response. + """ return self._url @property def args(self): - """Get dictionary of arguments, defined by the user.""" + """ + Get dictionary of arguments, defined by the user. + """ return self._args @property def event_name(self): - """Get event name, which used by the client for processing response.""" + """ + Get event name, which used by the client for processing response. + """ return self._event_name def to_representation(self): - """Serialize request object to dictionary object.""" + """ + Serialize request object to dictionary object. + """ return {'event_name': self.event_name} def get_argument(self, name): - """Extracting argument from the request. + """ + Extracting argument from the request. :param name: name of extracted argument in dictionary. """ @@ -59,18 +70,26 @@ def __init__(self): @property def content(self): - """Get content of response.""" + """ + Get content of response. + """ return self._content @content.setter def content(self, value): - """Set content for the response.""" + """ + Set content for the response. + """ self._content['data'] = value def wrap_exception(self, exception): - """Set content of response, when taken exception.""" + """ + Set content of response, when taken exception. + """ self._content = {'detail': exception.detail} def append_request(self, request): - """Add to the response object serialized request.""" + """ + Add to the response object serialized request. + """ self._content.update(request.to_representation())