Skip to content

Commit

Permalink
Merge pull request #478 from marshmallow-code/pylint
Browse files Browse the repository at this point in the history
Minor refactors and pylint fixes
  • Loading branch information
sirosen committed Feb 27, 2020
2 parents 936a6bb + 97f2d8e commit 3020476
Show file tree
Hide file tree
Showing 15 changed files with 79 additions and 89 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ Features:
provide compatibility with ``falcon.testing`` (:pr:`477`).
Thanks :user:`suola` for the PR.

* *Backwards-incompatible*: Factorize the ``use_args`` / ``use_kwargs`` branch
in all parsers. When ``as_kwargs`` is ``False``, arguments are now
consistently appended to the arguments list by the ``use_args`` decorator.
Before this change, the ``PyramidParser`` would prepend the argument list on
each call to ``use_args``. Pyramid view functions must reverse the order of
their arguments. (:pr:`478`)

6.0.0b8 (2020-02-16)
********************
Expand Down
13 changes: 6 additions & 7 deletions src/webargs/aiohttpparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,12 @@ async def load_json(self, req: Request, schema: Schema) -> typing.Dict:
return core.missing
try:
return await req.json(loads=json.loads)
except json.JSONDecodeError as e:
if e.doc == "":
except json.JSONDecodeError as exc:
if exc.doc == "":
return core.missing
else:
return self._handle_invalid_json_error(e, req)
except UnicodeDecodeError as e:
return self._handle_invalid_json_error(e, req)
return self._handle_invalid_json_error(exc, req)
except UnicodeDecodeError as exc:
return self._handle_invalid_json_error(exc, req)

def load_headers(self, req: Request, schema: Schema) -> MultiDictProxy:
"""Return headers from the request as a MultiDictProxy."""
Expand Down Expand Up @@ -138,7 +137,7 @@ def get_request_from_view_args(
if isinstance(arg, web.Request):
req = arg
break
elif isinstance(arg, web.View):
if isinstance(arg, web.View):
req = arg.request
break
if not isinstance(req, web.Request):
Expand Down
30 changes: 8 additions & 22 deletions src/webargs/asyncparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,10 @@ async def wrapper(*args, **kwargs):
error_status_code=error_status_code,
error_headers=error_headers,
)
if as_kwargs:
kwargs.update(parsed_args or {})
return await func(*args, **kwargs)
else:
# Add parsed_args after other positional arguments
new_args = args + (parsed_args,)
return await func(*new_args, **kwargs)
args, kwargs = self._update_args_kwargs(
args, kwargs, parsed_args, as_kwargs
)
return await func(*args, **kwargs)

else:

Expand All @@ -172,22 +169,11 @@ def wrapper(*args, **kwargs):
error_status_code=error_status_code,
error_headers=error_headers,
)
if as_kwargs:
kwargs.update(parsed_args)
return func(*args, **kwargs) # noqa: B901
else:
# Add parsed_args after other positional arguments
new_args = args + (parsed_args,)
return func(*new_args, **kwargs)
args, kwargs = self._update_args_kwargs(
args, kwargs, parsed_args, as_kwargs
)
return func(*args, **kwargs)

return wrapper

return decorator

def use_kwargs(self, *args, **kwargs) -> typing.Callable:
"""Decorator that injects parsed arguments into a view function or method.
Receives the same arguments as `webargs.core.Parser.use_kwargs`.
"""
return super().use_kwargs(*args, **kwargs)
3 changes: 1 addition & 2 deletions src/webargs/bottleparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ def _raw_load_json(self, req):
# see: https://github.com/bottlepy/bottle/issues/1160
if data is None:
return core.missing
else:
return data
return data

def load_querystring(self, req, schema):
"""Return query params from the request as a MultiDictProxy."""
Expand Down
53 changes: 30 additions & 23 deletions src/webargs/core.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import functools
import inspect
import typing
import logging
import warnings
from collections.abc import Mapping
Expand Down Expand Up @@ -35,8 +36,7 @@ def _callable_or_raise(obj):
"""
if obj and not callable(obj):
raise ValueError("{!r} is not callable.".format(obj))
else:
return obj
return obj


def is_multiple(field):
Expand Down Expand Up @@ -66,17 +66,17 @@ def is_json(mimetype):
return False


def parse_json(s, *, encoding="utf-8"):
if isinstance(s, bytes):
def parse_json(string, *, encoding="utf-8"):
if isinstance(string, bytes):
try:
s = s.decode(encoding)
except UnicodeDecodeError as e:
string = string.decode(encoding)
except UnicodeDecodeError as exc:
raise json.JSONDecodeError(
"Bytes decoding error : {}".format(e.reason),
doc=str(e.object),
pos=e.start,
"Bytes decoding error : {}".format(exc.reason),
doc=str(exc.object),
pos=exc.start,
)
return json.loads(s)
return json.loads(string)


def _ensure_list_of_callables(obj):
Expand Down Expand Up @@ -291,6 +291,16 @@ def get_request_from_view_args(self, view, args, kwargs):
"""
return None

@staticmethod
def _update_args_kwargs(args, kwargs, parsed_args, as_kwargs):
"""Update args or kwargs with parsed_args depending on as_kwargs"""
if as_kwargs:
kwargs.update(parsed_args)
else:
# Add parsed_args after other positional arguments
args += (parsed_args,)
return args, kwargs

def use_args(
self,
argmap,
Expand Down Expand Up @@ -350,20 +360,17 @@ def wrapper(*args, **kwargs):
error_status_code=error_status_code,
error_headers=error_headers,
)
if as_kwargs:
kwargs.update(parsed_args)
return func(*args, **kwargs)
else:
# Add parsed_args after other positional arguments
new_args = args + (parsed_args,)
return func(*new_args, **kwargs)
args, kwargs = self._update_args_kwargs(
args, kwargs, parsed_args, as_kwargs
)
return func(*args, **kwargs)

wrapper.__wrapped__ = func
return wrapper

return decorator

def use_kwargs(self, *args, **kwargs):
def use_kwargs(self, *args, **kwargs) -> typing.Callable:
"""Decorator that injects parsed arguments into a view function or method
as keyword arguments.
Expand Down Expand Up @@ -456,12 +463,12 @@ def load_json(self, req, schema):
# code sharing amongst the built-in webargs parsers
try:
return self._raw_load_json(req)
except json.JSONDecodeError as e:
if e.doc == "":
except json.JSONDecodeError as exc:
if exc.doc == "":
return missing
return self._handle_invalid_json_error(e, req)
except UnicodeDecodeError as e:
return self._handle_invalid_json_error(e, req)
return self._handle_invalid_json_error(exc, req)
except UnicodeDecodeError as exc:
return self._handle_invalid_json_error(exc, req)

def load_json_or_form(self, req, schema):
"""Load data from a request, accepting either JSON or form-encoded
Expand Down
3 changes: 1 addition & 2 deletions src/webargs/falconparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,7 @@ def _raw_load_json(self, req):
body = req.stream.read(req.content_length)
if body:
return core.parse_json(body)
else:
return core.missing
return core.missing

def load_headers(self, req, schema):
"""Return headers from the request."""
Expand Down
11 changes: 6 additions & 5 deletions src/webargs/multidictproxy.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from collections.abc import Mapping

from webargs.compat import MARSHMALLOW_VERSION_INFO
from webargs.core import missing, is_multiple

from collections.abc import Mapping


class MultiDictProxy(Mapping):
"""
Expand All @@ -18,7 +18,8 @@ def __init__(self, multidict, schema):
self.data = multidict
self.multiple_keys = self._collect_multiple_keys(schema)

def _collect_multiple_keys(self, schema):
@staticmethod
def _collect_multiple_keys(schema):
result = set()
for name, field in schema.fields.items():
if not is_multiple(field):
Expand All @@ -35,9 +36,9 @@ def __getitem__(self, key):
return val
if hasattr(self.data, "getlist"):
return self.data.getlist(key)
elif hasattr(self.data, "getall"):
if hasattr(self.data, "getall"):
return self.data.getall(key)
elif isinstance(val, (list, tuple)):
if isinstance(val, (list, tuple)):
return val
if val is None:
return None
Expand Down
9 changes: 4 additions & 5 deletions src/webargs/pyramidparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,10 @@ def wrapper(obj, *args, **kwargs):
error_status_code=error_status_code,
error_headers=error_headers,
)
if as_kwargs:
kwargs.update(parsed_args)
return func(obj, *args, **kwargs)
else:
return func(obj, parsed_args, *args, **kwargs)
args, kwargs = self._update_args_kwargs(
args, kwargs, parsed_args, as_kwargs
)
return func(obj, *args, **kwargs)

wrapper.__wrapped__ = func
return wrapper
Expand Down
12 changes: 5 additions & 7 deletions src/webargs/tornadoparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,16 @@ def __getitem__(self, key):
value = self.data.get(key, core.missing)
if value is core.missing:
return core.missing
elif key in self.multiple_keys:
if key in self.multiple_keys:
return [
_unicode(v) if isinstance(v, (str, bytes)) else v for v in value
]
elif value and isinstance(value, (list, tuple)):
if value and isinstance(value, (list, tuple)):
value = value[0]

if isinstance(value, (str, bytes)):
return _unicode(value)
else:
return value
return value
# based on tornado.web.RequestHandler.decode_argument
except UnicodeDecodeError:
raise HTTPError(400, "Invalid unicode in {}: {!r}".format(key, value[:40]))
Expand All @@ -73,10 +72,9 @@ def __getitem__(self, key):
cookie = self.data.get(key, core.missing)
if cookie is core.missing:
return core.missing
elif key in self.multiple_keys:
if key in self.multiple_keys:
return [cookie.value]
else:
return cookie.value
return cookie.value


class TornadoParser(core.Parser):
Expand Down
10 changes: 5 additions & 5 deletions tests/apps/aiohttp_app.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import asyncio

import aiohttp
import marshmallow as ma
from aiohttp import web
from aiohttp.web import json_response
import marshmallow as ma

from webargs import fields
from webargs.aiohttpparser import parser, use_args, use_kwargs
from webargs.core import MARSHMALLOW_VERSION_INFO, json
Expand Down Expand Up @@ -48,7 +48,7 @@ async def echo_json(request):
try:
parsed = await parser.parse(hello_args, request, location="json")
except json.JSONDecodeError:
raise web.HTTPBadRequest(
raise aiohttp.web.HTTPBadRequest(
body=json.dumps(["Invalid JSON."]).encode("utf-8"),
content_type="application/json",
)
Expand All @@ -59,7 +59,7 @@ async def echo_json_or_form(request):
try:
parsed = await parser.parse(hello_args, request, location="json_or_form")
except json.JSONDecodeError:
raise web.HTTPBadRequest(
raise aiohttp.web.HTTPBadRequest(
body=json.dumps(["Invalid JSON."]).encode("utf-8"),
content_type="application/json",
)
Expand Down Expand Up @@ -184,7 +184,7 @@ async def get(self, request, args):
return json_response(args)


class EchoHandlerView(web.View):
class EchoHandlerView(aiohttp.web.View):
@asyncio.coroutine
@use_args(hello_args, location="query")
def get(self, args):
Expand Down
3 changes: 1 addition & 2 deletions tests/apps/bottle_app.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from webargs.core import json
from bottle import Bottle, HTTPResponse, debug, request, response

import marshmallow as ma
from webargs import fields
from webargs.bottleparser import parser, use_args, use_kwargs
from webargs.core import MARSHMALLOW_VERSION_INFO
from webargs.core import json, MARSHMALLOW_VERSION_INFO


hello_args = {"name": fields.Str(missing="World", validate=lambda n: len(n) >= 3)}
Expand Down
5 changes: 2 additions & 3 deletions tests/apps/django_app/echo/views.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from webargs.core import json
from django.http import HttpResponse
from django.views.generic import View

import marshmallow as ma

from webargs import fields
from webargs.djangoparser import parser, use_args, use_kwargs
from webargs.core import MARSHMALLOW_VERSION_INFO
from webargs.core import json, MARSHMALLOW_VERSION_INFO


hello_args = {"name": fields.Str(missing="World", validate=lambda n: len(n) >= 3)}
Expand Down
1 change: 1 addition & 0 deletions tests/apps/falcon_app.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import falcon
import marshmallow as ma

from webargs import fields
from webargs.core import MARSHMALLOW_VERSION_INFO, json
from webargs.falconparser import parser, use_args, use_kwargs
Expand Down
5 changes: 2 additions & 3 deletions tests/apps/flask_app.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from webargs.core import json
from flask import Flask, jsonify as J, Response, request
from flask.views import MethodView

import marshmallow as ma

from webargs import fields
from webargs.flaskparser import parser, use_args, use_kwargs
from webargs.core import MARSHMALLOW_VERSION_INFO
from webargs.core import json, MARSHMALLOW_VERSION_INFO


class TestAppConfig:
Expand Down
4 changes: 1 addition & 3 deletions tests/apps/pyramid_app.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
from webargs.core import json

from pyramid.config import Configurator
from pyramid.httpexceptions import HTTPBadRequest
import marshmallow as ma

from webargs import fields
from webargs.pyramidparser import parser, use_args, use_kwargs
from webargs.core import MARSHMALLOW_VERSION_INFO
from webargs.core import json, MARSHMALLOW_VERSION_INFO

hello_args = {"name": fields.Str(missing="World", validate=lambda n: len(n) >= 3)}
hello_multiple = {"name": fields.List(fields.Str())}
Expand Down

0 comments on commit 3020476

Please sign in to comment.