Skip to content

Commit 36a6345

Browse files
authored
Merge pull request #167 from labthings/dependabot/pip/webargs-7.0.1
Bump webargs from 6.1.1 to 7.0.1
2 parents 6fa790f + 0b2cbbb commit 36a6345

File tree

8 files changed

+163
-42
lines changed

8 files changed

+163
-42
lines changed

poetry.lock

Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ include = ["src/labthings/py.typed"]
1616
python = "^3.6"
1717
Flask = "^1.1.1"
1818
marshmallow = "^3.4.0"
19-
webargs = "^6.0.0"
19+
webargs = ">=6,<8"
2020
apispec = ">=3.2,<5.0"
2121
flask-cors = "^3.0.8"
2222
zeroconf = ">=0.24.5,<0.29.0"

src/labthings/marshalling/args.py

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import logging
22
from functools import update_wrapper, wraps
3-
from typing import Callable, Dict, Union
3+
from typing import Callable, Union, Mapping
44

55
from flask import abort, request
66
from marshmallow.exceptions import ValidationError
@@ -10,15 +10,8 @@
1010
from ..schema import FieldSchema, Schema
1111

1212

13-
class use_body:
14-
"""Gets the request body as a single value and adds it as a positional argument"""
15-
16-
def __init__(
17-
self, schema: Union[Schema, Field, Dict[str, Union[Field, type]]], **_
18-
):
19-
self.schema = schema
20-
21-
def __call__(self, f: Callable):
13+
def use_body(schema: Field, **_) -> Callable:
14+
def inner(f: Callable):
2215
# Wrapper function
2316
@wraps(f)
2417
def wrapper(*args, **kwargs):
@@ -34,17 +27,17 @@ def wrapper(*args, **kwargs):
3427
# If no data is there
3528
if not data:
3629
# If data is required
37-
if self.schema.required:
30+
if schema.required:
3831
# Abort
3932
return abort(400)
4033
# Otherwise, look for the schema fields 'missing' property
41-
if self.schema.missing:
42-
data = self.schema.missing
34+
if schema.missing:
35+
data = schema.missing
4336

4437
# Serialize data if it exists
4538
if data:
4639
try:
47-
data = FieldSchema(self.schema).deserialize(data)
40+
data = FieldSchema(schema).deserialize(data)
4841
except ValidationError as e:
4942
logging.error(e)
5043
return abort(400)
@@ -54,13 +47,13 @@ def wrapper(*args, **kwargs):
5447

5548
return wrapper
5649

50+
return inner
51+
5752

5853
class use_args:
5954
"""Equivalent to webargs.flask_parser.use_args"""
6055

61-
def __init__(
62-
self, schema: Union[Schema, Field, Dict[str, Union[Field, type]]], **kwargs
63-
):
56+
def __init__(self, schema: Union[Schema, Field, Mapping[str, Field]], **kwargs):
6457
self.schema = schema
6558

6659
if isinstance(schema, Field):

src/labthings/marshalling/marshalling.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from collections.abc import Mapping
22
from functools import wraps
3-
from typing import Callable, Dict, Tuple, Union
3+
from typing import Callable, Dict, Tuple, Union, Optional
44

55
from marshmallow import Schema as _Schema
66
from werkzeug.wrappers import Response as ResponseBase
@@ -10,7 +10,9 @@
1010
from ..utilities import unpack
1111

1212

13-
def schema_to_converter(schema: Union[Schema, Field, Dict[str, Union[Field, type]]]):
13+
def schema_to_converter(
14+
schema: Union[Schema, Field, Dict[str, Union[Field, type]]]
15+
) -> Optional[Callable]:
1416
"""Convert a schema into a converter function,
1517
which takes a value as an argument and returns
1618
marshalled data

src/labthings/schema.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,12 @@
1111
from .names import ACTION_ENDPOINT, EXTENSION_LIST_ENDPOINT
1212
from .utilities import description_from_view, view_class_from_endpoint
1313

14-
__all__ = [
15-
"Schema",
16-
"pre_load",
17-
"pre_dump",
18-
"validate",
19-
"FuzzySchemaType"
20-
]
14+
__all__ = ["Schema", "pre_load", "pre_dump", "validate", "FuzzySchemaType"]
2115

2216
# Type alias for a Schema, Field, or Dict of Fields or Types
2317
FuzzySchemaType = Union[Schema, fields.Field, Dict[str, Union[fields.Field, type]]]
18+
19+
2420
class FieldSchema(Schema):
2521
""" "Virtual schema" for handling individual fields treated as schemas.
2622

src/labthings/views/__init__.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import datetime
22
from collections import OrderedDict
3-
from typing import Union, Dict, List, Optional, Set
3+
from typing import Dict, List, Optional, Set
44

55
from flask import request
66
from flask.views import MethodView
@@ -11,8 +11,12 @@
1111
from ..find import current_labthing, find_extension
1212
from ..marshalling import marshal_with, use_args
1313
from ..representations import DEFAULT_REPRESENTATIONS
14-
from ..schema import ActionSchema, EventSchema, Schema, FuzzySchemaType, build_action_schema
15-
from ..fields import Field
14+
from ..schema import (
15+
ActionSchema,
16+
EventSchema,
17+
FuzzySchemaType,
18+
build_action_schema,
19+
)
1620
from ..utilities import unpack
1721
from . import builder, op
1822

tests/test_marshalling.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from labthings.marshalling import marshalling as ms
2+
from labthings.schema import Schema
3+
from labthings import fields
4+
5+
6+
def test_schema_to_converter_schema():
7+
class TestSchema(Schema):
8+
foo = fields.String()
9+
10+
test_schema = TestSchema()
11+
converter = ms.schema_to_converter(test_schema)
12+
assert converter == test_schema.dump
13+
assert converter({"foo": 5}) == {"foo": "5"}
14+
15+
16+
def test_schema_to_converter_map():
17+
test_schema = {"foo": fields.String()}
18+
converter = ms.schema_to_converter(test_schema)
19+
assert converter({"foo": 5}) == {"foo": "5"}
20+
21+
22+
def test_schema_to_converter_field():
23+
field = fields.String()
24+
converter = ms.schema_to_converter(field)
25+
assert converter(5) == "5"
26+
27+
28+
def test_schema_to_converter_none():
29+
assert ms.schema_to_converter(object()) is None

tests/test_marshalling_args.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
from labthings import views, fields
2+
from labthings.schema import Schema
3+
from labthings.marshalling.args import use_args, use_body
4+
5+
6+
def test_use_body_string(app, client):
7+
class Index(views.MethodView):
8+
@use_body(fields.String(required=True))
9+
def post(self, args):
10+
return args
11+
12+
app.add_url_rule("/", view_func=Index.as_view("index"))
13+
14+
with client:
15+
res = client.post("/", data="string", content_type="application/json")
16+
assert res.data.decode() == "string"
17+
18+
19+
def test_use_body_no_data_error(app, client):
20+
class Index(views.MethodView):
21+
@use_body(fields.String(required=True))
22+
def post(self, args):
23+
return args
24+
25+
app.add_url_rule("/", view_func=Index.as_view("index"))
26+
27+
with client:
28+
res = client.post("/", content_type="application/json")
29+
assert res.status_code == 400
30+
31+
32+
def test_use_body_no_data_missing(app, client):
33+
class Index(views.MethodView):
34+
@use_body(fields.String(missing="default"))
35+
def post(self, args):
36+
return args
37+
38+
app.add_url_rule("/", view_func=Index.as_view("index"))
39+
40+
with client:
41+
res = client.post("/", content_type="application/json")
42+
assert res.data.decode() == "default"
43+
44+
45+
def test_use_body_validation_error(app, client):
46+
class Index(views.MethodView):
47+
@use_body(fields.String(required=True))
48+
def post(self, args):
49+
return args
50+
51+
app.add_url_rule("/", view_func=Index.as_view("index"))
52+
53+
with client:
54+
res = client.post("/", json={"foo": "bar"}, content_type="application/json")
55+
assert res.status_code == 400
56+
57+
58+
def test_use_args_field(app, client):
59+
class Index(views.MethodView):
60+
@use_args(fields.String(required=True))
61+
def post(self, args):
62+
return args
63+
64+
app.add_url_rule("/", view_func=Index.as_view("index"))
65+
66+
with client:
67+
res = client.post("/", data="string", content_type="application/json")
68+
assert res.data.decode() == "string"
69+
70+
71+
def test_use_args_map(app, client):
72+
class Index(views.MethodView):
73+
@use_args({"foo": fields.String(required=True)})
74+
def post(self, args):
75+
return args
76+
77+
app.add_url_rule("/", view_func=Index.as_view("index"))
78+
79+
with client:
80+
res = client.post("/", json={"foo": "bar"}, content_type="application/json")
81+
assert res.json == {"foo": "bar"}
82+
83+
84+
def test_use_args_schema(app, client):
85+
class TestSchema(Schema):
86+
foo = fields.String(required=True)
87+
88+
class Index(views.MethodView):
89+
@use_args(TestSchema())
90+
def post(self, args):
91+
return args
92+
93+
app.add_url_rule("/", view_func=Index.as_view("index"))
94+
95+
with client:
96+
res = client.post("/", json={"foo": "bar"}, content_type="application/json")
97+
assert res.json == {"foo": "bar"}

0 commit comments

Comments
 (0)