diff --git a/examples/flask/app.py b/examples/flask/app.py index 6e45a56..eeb532c 100644 --- a/examples/flask/app.py +++ b/examples/flask/app.py @@ -14,16 +14,11 @@ def is_authenticated(self): return True def make_user_key(self, username): - return 'user_{}'.format(username) + return f'user_{username}' def list(self): usernames = self.conn.lrange('users', 0, 100) - users = [] - - for user in usernames: - users.append(self.conn.hgetall(self.make_user_key(user))) - - return users + return [self.conn.hgetall(self.make_user_key(user)) for user in usernames] def detail(self, username): return self.conn.hgetall(self.make_user_key(username)) diff --git a/examples/pyramid/app.py b/examples/pyramid/app.py index 7d4ef1c..10dcde8 100644 --- a/examples/pyramid/app.py +++ b/examples/pyramid/app.py @@ -14,16 +14,11 @@ def is_authenticated(self): return True def make_user_key(self, username): - return 'user_{}'.format(username) + return f'user_{username}' def list(self): usernames = self.conn.lrange('users', 0, 100) - users = [] - - for user in usernames: - users.append(self.conn.hgetall(self.make_user_key(user))) - - return users + return [self.conn.hgetall(self.make_user_key(user)) for user in usernames] def detail(self, username): return self.conn.hgetall(self.make_user_key(username)) diff --git a/restless/dj.py b/restless/dj.py index 2d7a0fc..4f346dc 100644 --- a/restless/dj.py +++ b/restless/dj.py @@ -1,7 +1,7 @@ import six from django.conf import settings -from django.conf.urls import url +from django.urls import re_path from django.core.exceptions import ObjectDoesNotExist from django.core.paginator import Paginator from django.http import HttpResponse, Http404 @@ -60,24 +60,19 @@ def wrap_list_response(self, data): # Because Django. @classmethod - def as_list(self, *args, **kwargs): - return csrf_exempt(super(DjangoResource, self).as_list(*args, **kwargs)) + def as_list(cls, *args, **kwargs): + return csrf_exempt(super(DjangoResource, cls).as_list(*args, **kwargs)) @classmethod - def as_detail(self, *args, **kwargs): - return csrf_exempt(super(DjangoResource, self).as_detail(*args, **kwargs)) + def as_detail(cls, *args, **kwargs): + return csrf_exempt(super(DjangoResource, cls).as_detail(*args, **kwargs)) def is_debug(self): return settings.DEBUG def build_response(self, data, status=OK): - if status == NO_CONTENT: - # Avoid crashing the client when it tries to parse nonexisting JSON. - content_type = 'text/plain' - else: - content_type = 'application/json' - resp = HttpResponse(data, content_type=content_type, status=status) - return resp + content_type = 'text/plain' if status == NO_CONTENT else 'application/json' + return HttpResponse(data, content_type=content_type, status=status) def build_error(self, err): # A bit nicer behavior surrounding things that don't exist. @@ -105,9 +100,7 @@ def build_url_name(cls, name, name_prefix=None): :rtype: string """ if name_prefix is None: - name_prefix = 'api_{}'.format( - cls.__name__.replace('Resource', '').lower() - ) + name_prefix = f"api_{cls.__name__.replace('Resource', '').lower()}" name_prefix = name_prefix.rstrip('_') return '_'.join([name_prefix, name]) @@ -128,6 +121,6 @@ def urls(cls, name_prefix=None): :returns: A list of ``url`` objects for ``include(...)`` """ return [ - url(r'^$', cls.as_list(), name=cls.build_url_name('list', name_prefix)), - url(r'^(?P[\w-]+)/$', cls.as_detail(), name=cls.build_url_name('detail', name_prefix)), + re_path(r'^$', cls.as_list(), name=cls.build_url_name('list', name_prefix)), + re_path(r'^(?P[\w-]+)/$', cls.as_detail(), name=cls.build_url_name('detail', name_prefix)), ] diff --git a/restless/fl.py b/restless/fl.py index a41a7d6..91b5650 100644 --- a/restless/fl.py +++ b/restless/fl.py @@ -46,11 +46,7 @@ def is_debug(self): return current_app.debug def build_response(self, data, status=OK): - if status == NO_CONTENT: - # Avoid crashing the client when it tries to parse nonexisting JSON. - content_type = 'text/plain' - else: - content_type = 'application/json' + content_type = 'text/plain' if status == NO_CONTENT else 'application/json' return make_response(data, status, { 'Content-Type': content_type, }) @@ -74,9 +70,7 @@ def build_endpoint_name(cls, name, endpoint_prefix=None): :rtype: string """ if endpoint_prefix is None: - endpoint_prefix = 'api_{}'.format( - cls.__name__.replace('Resource', '').lower() - ) + endpoint_prefix = f"api_{cls.__name__.replace('Resource', '').lower()}" endpoint_prefix = endpoint_prefix.rstrip('_') return '_'.join([endpoint_prefix, name]) @@ -111,8 +105,8 @@ def add_url_rules(cls, app, rule_prefix, endpoint_prefix=None): methods=methods ) app.add_url_rule( - rule_prefix + '/', + f'{rule_prefix}/', endpoint=cls.build_endpoint_name('detail', endpoint_prefix), view_func=cls.as_detail(), - methods=methods + methods=methods, ) diff --git a/restless/preparers.py b/restless/preparers.py index 0515bc6..9d653a7 100644 --- a/restless/preparers.py +++ b/restless/preparers.py @@ -116,11 +116,7 @@ def lookup_data(self, lookup, data): if callable(value) and not hasattr(value, 'db_manager'): value = value() - if not remaining_lookup: - return value - - # There's more to lookup, so dive in recursively. - return self.lookup_data(remaining_lookup, value) + return self.lookup_data(remaining_lookup, value) if remaining_lookup else value class SubPreparer(FieldsPreparer): @@ -234,9 +230,4 @@ def prepare(self, data): Returns a list of data as the response. """ - result = [] - - for item in self.get_inner_data(data): - result.append(self.preparer.prepare(item)) - - return result + return [self.preparer.prepare(item) for item in self.get_inner_data(data)] diff --git a/restless/pyr.py b/restless/pyr.py index c42e1d2..d4c51cc 100644 --- a/restless/pyr.py +++ b/restless/pyr.py @@ -29,13 +29,8 @@ def _wrapper(request): return _wrapper def build_response(self, data, status=OK): - if status == NO_CONTENT: - # Avoid crashing the client when it tries to parse nonexisting JSON. - content_type = 'text/plain' - else: - content_type = 'application/json' - resp = Response(data, status_code=status, content_type=content_type) - return resp + content_type = 'text/plain' if status == NO_CONTENT else 'application/json' + return Response(data, status_code=status, content_type=content_type) @classmethod def build_routename(cls, name, routename_prefix=None): @@ -56,9 +51,7 @@ def build_routename(cls, name, routename_prefix=None): :rtype: string """ if routename_prefix is None: - routename_prefix = 'api_{}'.format( - cls.__name__.replace('Resource', '').lower() - ) + routename_prefix = f"api_{cls.__name__.replace('Resource', '').lower()}" routename_prefix = routename_prefix.rstrip('_') return '_'.join([routename_prefix, name]) diff --git a/restless/resources.py b/restless/resources.py index 95a742a..e1ac675 100644 --- a/restless/resources.py +++ b/restless/resources.py @@ -270,14 +270,12 @@ def handle(self, endpoint, *args, **kwargs): try: # Use ``.get()`` so we can also dodge potentially incorrect # ``endpoint`` errors as well. - if not method in self.http_methods.get(endpoint, {}): + if method not in self.http_methods.get(endpoint, {}): raise MethodNotImplemented( - "Unsupported method '{}' for {} endpoint.".format( - method, - endpoint - ) + f"Unsupported method '{method}' for {endpoint} endpoint." ) + if not self.is_authenticated(): raise Unauthorized() @@ -340,10 +338,7 @@ def deserialize_list(self, body): :returns: The deserialized body or an empty ``list`` """ - if body: - return self.serializer.deserialize(body) - - return [] + return self.serializer.deserialize(body) if body else [] def deserialize_detail(self, body): """ @@ -354,10 +349,7 @@ def deserialize_detail(self, body): :returns: The deserialized body or an empty ``dict`` """ - if body: - return self.serializer.deserialize(body) - - return {} + return self.serializer.deserialize(body) if body else {} def serialize(self, method, endpoint, data): """ @@ -402,10 +394,11 @@ def serialize_list(self, data): # Check for a ``Data``-like object. We should assume ``True`` (all # data gets prepared) unless it's explicitly marked as not. - if not getattr(data, 'should_prepare', True): - prepped_data = data.value - else: - prepped_data = [self.prepare(item) for item in data] + prepped_data = ( + [self.prepare(item) for item in data] + if getattr(data, 'should_prepare', True) + else data.value + ) final_data = self.wrap_list_response(prepped_data) return self.serializer.serialize(final_data) @@ -425,10 +418,11 @@ def serialize_detail(self, data): # Check for a ``Data``-like object. We should assume ``True`` (all # data gets prepared) unless it's explicitly marked as not. - if not getattr(data, 'should_prepare', True): - prepped_data = data.value - else: - prepped_data = self.prepare(data) + prepped_data = ( + self.prepare(data) + if getattr(data, 'should_prepare', True) + else data.value + ) return self.serializer.serialize(prepped_data) @@ -479,10 +473,7 @@ def is_authenticated(self): :returns: Whether the request is authenticated or not. :rtype: boolean """ - if self.request_method() == 'GET': - return True - - return False + return self.request_method() == 'GET' # Common methods the user should implement. diff --git a/restless/tnd.py b/restless/tnd.py index 6c6618a..acd3029 100644 --- a/restless/tnd.py +++ b/restless/tnd.py @@ -24,11 +24,7 @@ from tornado.concurrent import Future - if futures is None: - FUTURES = Future - else: - FUTURES = (Future, futures.Future) - + FUTURES = Future if futures is None else (Future, futures.Future) is_future = lambda x: isinstance(x, FUTURES) @@ -100,22 +96,27 @@ def as_view(cls, view_type, *init_args, **init_kwargs): global _method new_cls = type( - cls.__name__ + '_' + _BridgeMixin.__name__ + '_restless', - (_BridgeMixin, cls._request_handler_base_,), + f'{cls.__name__}_{_BridgeMixin.__name__}_restless', + ( + _BridgeMixin, + cls._request_handler_base_, + ), dict( __resource_cls__=cls, __resource_args__=init_args, __resource_kwargs__=init_kwargs, - __resource_view_type__=view_type) + __resource_view_type__=view_type, + ), ) + """ Add required http-methods to the newly created class We need to scan through MRO to find what functions users declared, and then add corresponding http-methods used by Tornado. """ bases = inspect.getmro(cls) - bases = bases[0:bases.index(Resource)-1] + bases = bases[:bases.index(Resource)-1] for k, v in cls.http_methods[view_type].items(): if any(v in base_cls.__dict__ for base_cls in bases): setattr(new_cls, k.lower(), _method) @@ -129,13 +130,8 @@ def request_body(self): return self.request.body def build_response(self, data, status=OK): - if status == NO_CONTENT: - # Avoid crashing the client when it tries to parse nonexisting JSON. - content_type = 'text/plain' - else: - content_type = 'application/json' - self.ref_rh.set_header("Content-Type", "{}; charset=UTF-8" - .format(content_type)) + content_type = 'text/plain' if status == NO_CONTENT else 'application/json' + self.ref_rh.set_header("Content-Type", f"{content_type}; charset=UTF-8") self.ref_rh.set_status(status) self.ref_rh.finish(data) @@ -152,14 +148,12 @@ def handle(self, endpoint, *args, **kwargs): method = self.request_method() try: - if not method in self.http_methods.get(endpoint, {}): + if method not in self.http_methods.get(endpoint, {}): raise MethodNotImplemented( - "Unsupported method '{}' for {} endpoint.".format( - method, - endpoint - ) + f"Unsupported method '{method}' for {endpoint} endpoint." ) + if not self.is_authenticated(): raise Unauthorized() diff --git a/restless/utils.py b/restless/utils.py index b8e3b4e..34f4250 100644 --- a/restless/utils.py +++ b/restless/utils.py @@ -22,7 +22,7 @@ class MoreTypesJSONEncoder(json.JSONEncoder): def default(self, data): if isinstance(data, (datetime.datetime, datetime.date, datetime.time)): return data.isoformat() - elif isinstance(data, decimal.Decimal) or isinstance(data, uuid.UUID): + elif isinstance(data, (decimal.Decimal, uuid.UUID)): return str(data) else: return super(MoreTypesJSONEncoder, self).default(data) @@ -33,8 +33,7 @@ def format_traceback(exc_info): stack = stack[:-2] stack.extend(traceback.format_tb(exc_info[2])) stack.extend(traceback.format_exception_only(exc_info[0], exc_info[1])) - stack_str = "Traceback (most recent call last):\n" - stack_str += "".join(stack) + stack_str = "Traceback (most recent call last):\n" + "".join(stack) # Remove the last \n stack_str = stack_str[:-1] return stack_str diff --git a/tests/test_dj.py b/tests/test_dj.py index 520d712..643e8d2 100644 --- a/tests/test_dj.py +++ b/tests/test_dj.py @@ -66,10 +66,7 @@ def fake_init(self): ] def is_authenticated(self): - if self.request_method() == 'DELETE' and self.endpoint == 'list': - return False - - return True + return self.request_method() != 'DELETE' or self.endpoint != 'list' def list(self): return self.fake_db @@ -80,7 +77,7 @@ def detail(self, pk): return item # If it wasn't found in our fake DB, raise a Django-esque exception. - raise ObjectDoesNotExist("Model with pk {} not found.".format(pk)) + raise ObjectDoesNotExist(f"Model with pk {pk} not found.") def create(self): self.fake_db.append(FakeModel( @@ -154,7 +151,7 @@ def detail(self, pk): return item # If it wasn't found in our fake DB, raise a Django-esque exception. - raise Http404("Model with pk {} not found.".format(pk)) + raise Http404(f"Model with pk {pk} not found.") @unittest.skipIf(not settings, "Django is not available") diff --git a/tests/test_pyr.py b/tests/test_pyr.py index d5abe19..64c02dd 100644 --- a/tests/test_pyr.py +++ b/tests/test_pyr.py @@ -35,10 +35,7 @@ def create(self): self.fake_db.append(self.data) def is_authenticated(self): - if self.request_method() == 'DELETE': - return False - - return True + return self.request_method() != 'DELETE' @unittest.skipIf(not testing, 'Pyramid is not available') diff --git a/tests/test_tnd.py b/tests/test_tnd.py index f45f481..12b36fc 100644 --- a/tests/test_tnd.py +++ b/tests/test_tnd.py @@ -19,10 +19,10 @@ def _newer_or_equal_(v): def _equal_(v): - for i in six.moves.xrange(min(len(v), len(version_info))): - if v[i] != version_info[i]: - return False - return True + return all( + v[i] == version_info[i] + for i in six.moves.xrange(min(len(v), len(version_info))) + ) try: from restless.tnd import TornadoResource, _BridgeMixin @@ -73,10 +73,7 @@ def list(self): return self.fake_db def detail(self, pk): - for item in self.fake_db: - if item['id'] == pk: - return item - return None + return next((item for item in self.fake_db if item['id'] == pk), None) def create(self): self.fake_db.append(self.data)