Skip to content

Commit f435e5e

Browse files
authored
new: Graceful deprecation rename of marshal_schema to response_body_schema (plangrid#101)
* chg: Refactor utilities into a separate utils package
1 parent 12d9d4c commit f435e5e

18 files changed

+584
-292
lines changed

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ Example
6565
rule='/todos',
6666
method='GET',
6767
query_string_schema=GetTodosQueryStringSchema(),
68-
marshal_schema=GetTodosResponseSchema(),
68+
response_body_schema=GetTodosResponseSchema(), # for versions <= 1.7.0, use marshal_schema
6969
)
7070
def get_todos():
7171
"""

docs/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Here's what a basic Flask-Rebar application looks like:
5656
rule='/todos',
5757
method='GET',
5858
query_string_schema=GetTodosQueryStringSchema(),
59-
marshal_schema=GetTodosResponseSchema(),
59+
response_body_schema=GetTodosResponseSchema(), # For version <= 1.7.0 use marshal_schema
6060
)
6161
def get_todos():
6262
"""

docs/quickstart/basics.rst

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Let's take a look at a very basic API using Flask-Rebar:
2525
@registry.handles(
2626
rule='/todos/<id>',
2727
method='GET',
28-
marshal_schema=TodoSchema()
28+
response_body_schema=TodoSchema() # for versions <= 1.7.0, use marshal_schema
2929
)
3030
def get_todo(id):
3131
...
@@ -47,7 +47,7 @@ We then create a handler registry that we will use to declare handlers for the s
4747

4848
``method`` is the HTTP method that the handler will accept. To register multiple methods for a single handler function, decorate the function multiple times.
4949

50-
``marshal_schema`` is a Marshmallow schema that will be used marshal the return value of the function. `marshmallow.Schema.dump <http://marshmallow.readthedocs.io/en/latest/api_reference.html#marshmallow.Schema.dump>`_ will be called on the return value. ``marshal_schema`` can also be a dictionary mapping status codes to Marshmallow schemas - see :ref:`Marshaling`.
50+
``response_body_schema`` is a Marshmallow schema that will be used marshal the return value of the function. `marshmallow.Schema.dump <http://marshmallow.readthedocs.io/en/latest/api_reference.html#marshmallow.Schema.dump>`_ will be called on the return value. ``response_body_schema`` can also be a dictionary mapping status codes to Marshmallow schemas - see :ref:`Marshaling`. *NOTE: In Flask-Rebar 1.0-1.7.0, this was referred to as ``marshal_schema``. It is being renamed and both names will function until version 2.0*
5151

5252
The handler function should accept any arguments specified in ``rule``, just like a Flask view function.
5353

@@ -155,7 +155,7 @@ This default can be overriden in any particular handler by setting ``headers_sch
155155
Marshaling
156156
==========
157157

158-
The ``marshal_schema`` argument of ``HandlerRegistry.handles`` can be one of three types: a ``marshmallow.Schema``, a dictionary mapping integers to ``marshmallow.Schema``, or ``None``.
158+
The ``response_body_schema`` (previously ``marshal_schema``) argument of ``HandlerRegistry.handles`` can be one of three types: a ``marshmallow.Schema``, a dictionary mapping integers to ``marshmallow.Schema``, or ``None``.
159159

160160
In the case of a ``marshmallow.Schema``, that schema is used to ``dump`` the return value of the handler function.
161161

@@ -166,7 +166,7 @@ In the case of a dictionary mapping integers to ``marshmallow.Schemas``, the int
166166
@registry.handles(
167167
rule='/todos',
168168
method='POST',
169-
marshal_schema={
169+
response_body_schema={
170170
201: TodoSchema()
171171
}
172172
)
@@ -176,15 +176,15 @@ In the case of a dictionary mapping integers to ``marshmallow.Schemas``, the int
176176
177177
The schema to use for marshaling will be retrieved based on the status code the handler function returns. This isn't the prettiest part of Flask-Rebar, but it's done this way to help with the automatic Swagger generation.
178178

179-
In the case of ``None`` (which is also the default), no marshaling takes place, and the return value is passed directly through to Flask. This means the if ``marshal_schema`` is ``None``, the return value must be a return value that Flask supports, e.g. a string or a ``Flask.Response`` object.
179+
In the case of ``None`` (which is also the default), no marshaling takes place, and the return value is passed directly through to Flask. This means the if ``response_body_schema`` is ``None``, the return value must be a return value that Flask supports, e.g. a string or a ``Flask.Response`` object.
180180

181181
.. code-block:: python
182182
183183
184184
@registry.handles(
185185
rule='/todos',
186186
method='GET',
187-
marshal_schema=None
187+
response_body_schema=None
188188
)
189189
def get_todos():
190190
...

docs/recipes.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ Here is a simple recipe for using Flask-Rebar with these pluggable views:
5353
registry.add_handler(
5454
func=Todo.as_view(method + "_todo", database=database),
5555
rule="/todos/<id>",
56-
marshal_schema=TodoSchema(),
56+
response_body_schema=TodoSchema(), # for versions <= 1.7.0, use marshal_schema
5757
method=method,
5858
request_body_schema=request_body_schema,
5959
)

examples/todo/todo.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ def envelope_in_data(self, data):
7777
tags=["todo"],
7878
# This dictionary tells framer which schema to use for which response code.
7979
# This is a little ugly, but tremendously helpful for generating swagger.
80-
marshal_schema={201: TodoResourceSchema()},
80+
response_body_schema={
81+
201: TodoResourceSchema()
82+
}, # for versions <= 1.7.0, use marshal_schema
8183
)
8284
def create_todo():
8385
global todo_id_sequence, todo_database
@@ -105,7 +107,7 @@ def create_todo():
105107
tags=["todo"],
106108
# If the value for this is not a dictionary, the response code is assumed
107109
# to be 200
108-
marshal_schema=TodoListSchema(),
110+
response_body_schema=TodoListSchema(), # for versions <= 1.7.0, use marshal_schema
109111
)
110112
def get_todos():
111113
global todo_database
@@ -127,7 +129,7 @@ def get_todos():
127129
@registry.handles(
128130
rule="/todos/<int:todo_id>",
129131
method="PATCH",
130-
marshal_schema=TodoResourceSchema(),
132+
response_body_schema=TodoResourceSchema(), # for versions <= 1.7.0, use marshal_schema
131133
request_body_schema=UpdateTodoSchema(),
132134
tags=["todo"],
133135
)

flask_rebar/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import unicode_literals
22

3-
from flask_rebar.request_utils import marshal, response
3+
from flask_rebar.utils.request_utils import marshal, response
44

55
from flask_rebar.rebar import (
66
Rebar,

flask_rebar/rebar.py

Lines changed: 54 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -26,36 +26,19 @@
2626
from flask_rebar import messages
2727
from flask_rebar.authenticators import USE_DEFAULT
2828
from flask_rebar import errors
29-
from flask_rebar.request_utils import marshal
30-
from flask_rebar.request_utils import response
31-
from flask_rebar.request_utils import get_header_params_or_400
32-
from flask_rebar.request_utils import get_json_body_params_or_400
33-
from flask_rebar.request_utils import get_query_string_params_or_400
29+
from flask_rebar.utils.request_utils import marshal
30+
from flask_rebar.utils.request_utils import response
31+
from flask_rebar.utils.request_utils import get_header_params_or_400
32+
from flask_rebar.utils.request_utils import get_json_body_params_or_400
33+
from flask_rebar.utils.request_utils import get_query_string_params_or_400
34+
from flask_rebar.utils.deprecation import deprecated, deprecated_parameters
3435
from flask_rebar.swagger_generation import SwaggerV2Generator
3536
from flask_rebar.swagger_ui import create_swagger_ui_blueprint
3637

37-
# Metadata about a declared handler function. This can be used to both
38-
# declare the flask routing and to autogenerate swagger.
39-
PathDefinition = namedtuple(
40-
"PathDefinition",
41-
[
42-
"func",
43-
"path",
44-
"method",
45-
"endpoint",
46-
"marshal_schema",
47-
"query_string_schema",
48-
"request_body_schema",
49-
"headers_schema",
50-
"authenticator",
51-
"tags",
52-
"mimetype",
53-
],
54-
)
55-
5638

5739
# To catch redirection exceptions, app.errorhandler expects 301 in versions
5840
# below 0.11.0 but the exception itself in versions greater than 0.11.0.
41+
# NOTE: as of Flask 1.0.3, redirects are no longer bubbled up as exceptions
5942
if LooseVersion(flask_version) < LooseVersion("0.11.0"):
6043
MOVED_PERMANENTLY_ERROR = 301
6144
PERMANENT_REDIRECT_ERROR = 308
@@ -96,13 +79,14 @@ def _unpack_view_func_return_value(rv):
9679
return data, int(status), headers
9780

9881

82+
@deprecated_parameters(marshal_schema=("response_body_schema", "2.0"))
9983
def _wrap_handler(
10084
f,
10185
authenticator=None,
10286
query_string_schema=None,
10387
request_body_schema=None,
10488
headers_schema=None,
105-
marshal_schema=None,
89+
response_body_schema=None,
10690
mimetype=None,
10791
):
10892
"""
@@ -130,12 +114,12 @@ def wrapped(*args, **kwargs):
130114

131115
rv = f(*args, **kwargs)
132116

133-
if not marshal_schema:
117+
if not response_body_schema:
134118
return rv
135119

136120
data, status_code, headers = _unpack_view_func_return_value(rv)
137121

138-
schema = marshal_schema[status_code] # May raise KeyError.
122+
schema = response_body_schema[status_code] # May raise KeyError.
139123

140124
# The schema may be declared as None to bypass marshaling (e.g. for 204 responses).
141125
if schema is None:
@@ -209,6 +193,38 @@ def prefix_url(prefix, url):
209193
return "/{}/{}".format(prefix, url)
210194

211195

196+
# Metadata about a declared handler function. This can be used to both
197+
# declare the flask routing and to autogenerate swagger.
198+
class PathDefinition(
199+
namedtuple(
200+
"_PathDefinition",
201+
[
202+
"func",
203+
"path",
204+
"method",
205+
"endpoint",
206+
"response_body_schema",
207+
"query_string_schema",
208+
"request_body_schema",
209+
"headers_schema",
210+
"authenticator",
211+
"tags",
212+
"mimetype",
213+
],
214+
)
215+
):
216+
__slots__ = ()
217+
218+
@deprecated_parameters(marshal_schema=("response_body_schema", "2.0"))
219+
def __new__(cls, *args, **kwargs):
220+
return super(PathDefinition, cls).__new__(cls, *args, **kwargs)
221+
222+
@property
223+
@deprecated("response_body_schema", "2.0")
224+
def marshal_schema(self):
225+
return self.response_body_schema
226+
227+
212228
class HandlerRegistry(object):
213229
"""
214230
Registry for request handlers.
@@ -318,7 +334,7 @@ def paths(self):
318334
path=path,
319335
method=definition_.method,
320336
endpoint=definition_.endpoint,
321-
marshal_schema=definition_.marshal_schema,
337+
response_body_schema=definition_.response_body_schema,
322338
query_string_schema=definition_.query_string_schema,
323339
request_body_schema=definition_.request_body_schema,
324340
headers_schema=definition_.headers_schema,
@@ -329,13 +345,14 @@ def paths(self):
329345

330346
return paths
331347

348+
@deprecated_parameters(marshal_schema=("response_body_schema", "2.0"))
332349
def add_handler(
333350
self,
334351
func,
335352
rule,
336353
method="GET",
337354
endpoint=None,
338-
marshal_schema=None,
355+
response_body_schema=None,
339356
query_string_schema=None,
340357
request_body_schema=None,
341358
headers_schema=USE_DEFAULT,
@@ -353,7 +370,7 @@ def add_handler(
353370
:param str method:
354371
The HTTP method this handler accepts
355372
:param str endpoint:
356-
:param dict[int, marshmallow.Schema] marshal_schema:
373+
:param dict[int, marshmallow.Schema] response_body_schema:
357374
Dictionary mapping response codes to schemas to use to marshal
358375
the response. For now this assumes everything is JSON.
359376
:param marshmallow.Schema query_string_schema:
@@ -372,15 +389,15 @@ def add_handler(
372389
:param Type[USE_DEFAULT]|None|str mimetype:
373390
Content-Type header to add to the response schema
374391
"""
375-
if isinstance(marshal_schema, marshmallow.Schema):
376-
marshal_schema = {200: marshal_schema}
392+
if isinstance(response_body_schema, marshmallow.Schema):
393+
response_body_schema = {200: response_body_schema}
377394

378395
self._paths[rule][method] = PathDefinition(
379396
func=func,
380397
path=rule,
381398
method=method,
382399
endpoint=endpoint,
383-
marshal_schema=marshal_schema,
400+
response_body_schema=response_body_schema,
384401
query_string_schema=query_string_schema,
385402
request_body_schema=request_body_schema,
386403
headers_schema=headers_schema,
@@ -389,12 +406,13 @@ def add_handler(
389406
mimetype=mimetype,
390407
)
391408

409+
@deprecated_parameters(marshal_schema=("response_body_schema", "2.0"))
392410
def handles(
393411
self,
394412
rule,
395413
method="GET",
396414
endpoint=None,
397-
marshal_schema=None,
415+
response_body_schema=None,
398416
query_string_schema=None,
399417
request_body_schema=None,
400418
headers_schema=USE_DEFAULT,
@@ -413,7 +431,7 @@ def wrapper(f):
413431
rule=rule,
414432
method=method,
415433
endpoint=endpoint,
416-
marshal_schema=marshal_schema,
434+
response_body_schema=response_body_schema,
417435
query_string_schema=query_string_schema,
418436
request_body_schema=request_body_schema,
419437
headers_schema=headers_schema,
@@ -457,7 +475,7 @@ def _register_routes(self, app):
457475
if definition_.headers_schema is USE_DEFAULT
458476
else definition_.headers_schema
459477
),
460-
marshal_schema=definition_.marshal_schema,
478+
response_body_schema=definition_.response_body_schema,
461479
mimetype=(
462480
self.default_mimetype
463481
if definition_.mimetype is USE_DEFAULT

0 commit comments

Comments
 (0)