Skip to content

Commit 009c38e

Browse files
authored
Merge branch 'master' into master
2 parents 49e180d + 858ac78 commit 009c38e

18 files changed

+167
-25
lines changed

doc/conf.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252

5353
# General information about the project.
5454
project = u"Flask-RESTX"
55-
copyright = u"2014, Axel Haustant"
55+
copyright = u"2020, python-restx Authors"
5656

5757
# The version info for the project you're documenting, acts as replacement for
5858
# |version| and |release|, also used in various other places throughout the
@@ -263,7 +263,7 @@
263263
"index",
264264
"Flask-RESTX.tex",
265265
u"Flask-RESTX Documentation",
266-
u"Axel Haustant",
266+
u"python-restx Authors",
267267
"manual",
268268
),
269269
]
@@ -294,7 +294,7 @@
294294
# One entry per manual page. List of tuples
295295
# (source start file, name, description, authors, manual section).
296296
man_pages = [
297-
("index", "flask-restx", u"Flask-RESTX Documentation", [u"Axel Haustant"], 1)
297+
("index", "flask-restx", u"Flask-RESTX Documentation", [u"python-restx Authors"], 1)
298298
]
299299

300300
# If true, show URL addresses after external links.
@@ -311,7 +311,7 @@
311311
"index",
312312
"Flask-RESTX",
313313
u"Flask-RESTX Documentation",
314-
u"Axel Haustant",
314+
u"python-restx Authors",
315315
"Flask-RESTX",
316316
"One line description of project.",
317317
"Miscellaneous",

doc/configuration.rst

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
Configuration
2+
=============
3+
4+
Flask-RESTX provides the following `Flask configuration values <https://flask.palletsprojects.com/en/1.1.x/config/#configuration-handling>`_:
5+
6+
Note: Values with no additional description should be covered in more detail
7+
elsewhere in the documentation. If not, please open an issue on GitHub.
8+
9+
.. py:data:: RESTX_JSON
10+
11+
Provide global configuration options for JSON serialisation as a :class:`dict`
12+
of :func:`json.dumps` keyword arguments.
13+
14+
.. py:data:: RESTX_VALIDATE
15+
16+
Whether to enforce payload validation by default when using the
17+
``@api.expect()`` decorator. See the `@api.expect()
18+
<swagger.html#the-api-expect-decorator>`__ documentation for details.
19+
This setting defaults to ``False``.
20+
21+
.. py:data:: RESTX_MASK_HEADER
22+
23+
Choose the name of the *Header* that will contain the masks to apply to your
24+
answer. See the `Fields masks <mask.html>`__ documentation for details.
25+
This setting defaults to ``X-Fields``.
26+
27+
.. py:data:: RESTX_MASK_SWAGGER
28+
29+
Whether to enable the mask documentation in your swagger or not. See the
30+
`mask usage <mask.html#usage>`__ documentation for details.
31+
This setting defaults to ``True``.
32+
33+
.. py:data:: RESTX_INCLUDE_ALL_MODELS
34+
35+
This option allows you to include all defined models in the generated Swagger
36+
documentation, even if they are not explicitly used in either ``expect`` nor
37+
``marshal_with`` decorators.
38+
This setting defaults to ``False``.
39+
40+
.. py:data:: BUNDLE_ERRORS
41+
42+
Bundle all the validation errors instead of returning only the first one
43+
encountered. See the `Error Handling <parsing.html#error-handling>`__ section
44+
of the documentation for details.
45+
This setting defaults to ``False``.
46+
47+
.. py:data:: ERROR_404_HELP
48+
49+
.. py:data:: HTTP_BASIC_AUTH_REALM
50+
51+
.. py:data:: SWAGGER_VALIDATOR_URL
52+
53+
.. py:data:: SWAGGER_UI_DOC_EXPANSION
54+
55+
.. py:data:: SWAGGER_UI_OPERATION_ID
56+
57+
.. py:data:: SWAGGER_UI_REQUEST_DURATION
58+
59+
.. py:data:: SWAGGER_UI_OAUTH_APP_NAME
60+
61+
.. py:data:: SWAGGER_UI_OAUTH_CLIENT_ID
62+
63+
.. py:data:: SWAGGER_UI_OAUTH_REALM
64+
65+
.. py:data:: SWAGGER_SUPPORTED_SUBMIT_METHODS

doc/example.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Here is a full example of a `TodoMVC <http://todomvc.com/>`_ API.
77
88
from flask import Flask
99
from flask_restx import Api, Resource, fields
10-
from werkzeug.contrib.fixers import ProxyFix
10+
from werkzeug.middleware.proxy_fix import ProxyFix
1111
1212
app = Flask(__name__)
1313
app.wsgi_app = ProxyFix(app.wsgi_app)

doc/index.rst

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,36 @@
44
contain the root `toctree` directive.
55
66
Welcome to Flask-RESTX's documentation!
7-
==========================================
7+
=======================================
88

99
Flask-RESTX is an extension for Flask that adds support for quickly building REST APIs.
1010
Flask-RESTX encourages best practices with minimal setup.
1111
If you are familiar with Flask, Flask-RESTX should be easy to pick up.
1212
It provides a coherent collection of decorators and tools to describe your API
1313
and expose its documentation properly (using Swagger).
1414

15+
Flask-RESTX is a community driven fork of `Flask-RESTPlus
16+
<https://github.com/noirbizarre/flask-restplus>`_
17+
18+
19+
Why did we fork?
20+
================
21+
22+
The community has decided to fork the project due to lack of response from the
23+
original author @noirbizarre. We have been discussing this eventuality for
24+
`a long time <https://github.com/noirbizarre/flask-restplus/issues/593>`_.
25+
26+
Things evolved a bit since that discussion and a few of us have been granted
27+
maintainers access to the github project, but only the original author has
28+
access rights on the PyPi project. As such, we been unable to make any actual
29+
releases. To prevent this project from dying out, we have forked it to continue
30+
development and to support our users.
31+
1532

1633
Compatibility
1734
=============
1835

19-
flask-restx requires Python 2.7+.
36+
flask-restx requires Python 2.7+ or 3.4+.
2037

2138

2239
Installation
@@ -55,6 +72,7 @@ Flask-RESTX with Flask.
5572
postman
5673
scaling
5774
example
75+
configuration
5876

5977

6078
API Reference

doc/marshalling.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ You can define models using `JSON Schema <http://json-schema.org/examples.html>`
521521
'type': 'object'
522522
})
523523
524-
person = address = api.schema_model('Person', {
524+
person = api.schema_model('Person', {
525525
'required': ['address'],
526526
'properties': {
527527
'name': {

doc/parsing.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ and to set the type to :class:`~werkzeug.datastructures.FileStorage`.
249249
url = do_something_with_file(uploaded_file)
250250
return {'url': url}, 201
251251
252-
See the `dedicated Flask documentation section <http://flask.pocoo.org/docs/0.10/patterns/fileuploads/>`_.
252+
See the `dedicated Flask documentation section <https://flask.palletsprojects.com/en/1.1.x/patterns/fileuploads/>`_.
253253

254254

255255
Error Handling

doc/quickstart.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,29 @@ and that you have already installed both Flask and Flask-RESTX.
1010
If not, then follow the steps in the :ref:`installation` section.
1111

1212

13+
Migrate from Flask-RESTPlus
14+
---------------------------
15+
16+
.. warning:: The *migration* commands provided below are for illustration
17+
purposes.
18+
You may need to adapt them to properly fit your needs.
19+
We also recommend you make a backup of your project prior running them.
20+
21+
At this point, Flask-RESTX remains 100% compatible with Flask-RESTPlus' API.
22+
All you need to do is update your requirements to use Flask-RESTX instead of
23+
Flask-RESTPlus. Then you need to update all your imports.
24+
This can be done using something like:
25+
26+
::
27+
find . -type f -name "*.py" | xargs sed -i "s/flask_restplus/flask_restx/g"
28+
29+
Finally, you will need to update your configuration options (described `here
30+
<quickstart.html#configuration>`_). Example:
31+
32+
::
33+
find . -type f -name "*.py" | xargs sed -i "s/RESTPLUS_/RESTX_/g"
34+
35+
1336
Initialization
1437
--------------
1538

doc/swagger.rst

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -536,14 +536,13 @@ For ``POST`` and ``PUT`` methods, use the ``body`` keyword argument to specify t
536536

537537
.. code-block:: python
538538
539-
fields = api.model('MyModel', {
539+
my_model = api.model('MyModel', {
540540
'name': fields.String(description='The name', required=True),
541541
'type': fields.String(description='The object type', enum=['A', 'B']),
542542
'age': fields.Integer(min=0),
543543
})
544544
545545
546-
@api.model(fields={'name': fields.String, 'age': fields.Integer})
547546
class Person(fields.Raw):
548547
def format(self, value):
549548
return {'name': value.name, 'age': value.age}
@@ -552,11 +551,11 @@ For ``POST`` and ``PUT`` methods, use the ``body`` keyword argument to specify t
552551
@api.route('/my-resource/<id>', endpoint='my-resource')
553552
@api.doc(params={'id': 'An ID'})
554553
class MyResource(Resource):
555-
@api.doc(model=fields)
554+
@api.doc(model=my_model)
556555
def get(self, id):
557556
return {}
558557
559-
@api.doc(model='MyModel', body=Person)
558+
@api.doc(model=my_model, body=Person)
560559
def post(self, id):
561560
return {}
562561

examples/complex.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from flask import Flask
2-
from werkzeug.contrib.fixers import ProxyFix
2+
from werkzeug.middleware.proxy_fix import ProxyFix
33

44
from zoo import api
55

examples/todo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from flask import Flask
22
from flask_restx import Api, Resource, fields
3-
from werkzeug.contrib.fixers import ProxyFix
3+
from werkzeug.middleware.proxy_fix import ProxyFix
44

55
app = Flask(__name__)
66
app.wsgi_app = ProxyFix(app.wsgi_app)

examples/todomvc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from flask import Flask
22
from flask_restx import Api, Resource, fields
3-
from werkzeug.contrib.fixers import ProxyFix
3+
from werkzeug.middleware.proxy_fix import ProxyFix
44

55
app = Flask(__name__)
66
app.wsgi_app = ProxyFix(app.wsgi_app)

flask_restx/api.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -515,11 +515,11 @@ def endpoint(self, name):
515515
@property
516516
def specs_url(self):
517517
"""
518-
The Swagger specifications absolute url (ie. `swagger.json`)
518+
The Swagger specifications relative url (ie. `swagger.json`)
519519
520520
:rtype: str
521521
"""
522-
return url_for(self.endpoint("specs"), _external=True)
522+
return url_for(self.endpoint("specs"))
523523

524524
@property
525525
def base_url(self):

flask_restx/fields.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ def __init__(
156156
self.description = description
157157
self.required = required
158158
self.readonly = readonly
159-
self.example = example or self.__schema_example__
159+
self.example = example if example is not None else self.__schema_example__
160160
self.mask = mask
161161

162162
def format(self, value):
@@ -459,7 +459,7 @@ def format(self, value):
459459
if value is None:
460460
return self.default
461461
return int(value)
462-
except ValueError as ve:
462+
except (ValueError, TypeError) as ve:
463463
raise MarshallingError(ve)
464464

465465

@@ -473,7 +473,7 @@ class Float(NumberMixin, Raw):
473473
def format(self, value):
474474
try:
475475
return float(value)
476-
except ValueError as ve:
476+
except (ValueError, TypeError) as ve:
477477
raise MarshallingError(ve)
478478

479479

flask_restx/reqparse.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,6 @@ def __schema__(self):
307307
param["collectionFormat"] = "csv"
308308
if self.choices:
309309
param["enum"] = self.choices
310-
param["collectionFormat"] = "multi"
311310
return param
312311

313312

flask_restx/swagger.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,8 @@ def register_model(self, model):
657657
if name not in self.api.models:
658658
raise ValueError("Model {0} not registered".format(name))
659659
specs = self.api.models[name]
660+
if name in self._registered_models:
661+
return ref(model)
660662
self._registered_models[name] = specs
661663
if isinstance(specs, ModelBase):
662664
for parent in specs.__parents__:

tests/test_fields.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,10 +276,14 @@ def test_with_default(self):
276276
def test_value(self, value, expected):
277277
self.assert_field(fields.Integer(), value, expected)
278278

279-
def test_decode_error(self):
279+
def test_decode_error_on_invalid_value(self):
280280
field = fields.Integer()
281281
self.assert_field_raises(field, "an int")
282282

283+
def test_decode_error_on_invalid_type(self):
284+
field = fields.Integer()
285+
self.assert_field_raises(field, {"a": "dict"})
286+
283287

284288
class BooleanFieldTest(BaseFieldTestMixin, FieldTestCase):
285289
field_class = fields.Boolean
@@ -294,6 +298,14 @@ def test_with_default(self):
294298
assert not field.required
295299
assert field.__schema__ == {"type": "boolean", "default": True}
296300

301+
def test_with_example(self):
302+
field = fields.Boolean(default=True, example=False)
303+
assert field.__schema__ == {
304+
"type": "boolean",
305+
"default": True,
306+
"example": False,
307+
}
308+
297309
@pytest.mark.parametrize(
298310
"value,expected",
299311
[
@@ -330,10 +342,14 @@ def test_value(self, value, expected):
330342
def test_raises(self):
331343
self.assert_field_raises(fields.Float(), "bar")
332344

333-
def test_decode_error(self):
345+
def test_decode_error_on_invalid_value(self):
334346
field = fields.Float()
335347
self.assert_field_raises(field, "not a float")
336348

349+
def test_decode_error_on_invalid_type(self):
350+
field = fields.Float()
351+
self.assert_field_raises(field, {"a": "dict"})
352+
337353

338354
PI_STR = (
339355
"3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117"

tests/test_reqparse.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -943,7 +943,6 @@ def test_choices(self):
943943
"type": "string",
944944
"in": "query",
945945
"enum": ["a", "b"],
946-
"collectionFormat": "multi",
947946
}
948947
]
949948

tests/test_swagger.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2057,6 +2057,27 @@ def get(self):
20572057

20582058
client.get_specs(status=500)
20592059

2060+
def test_recursive_model(self, api, client):
2061+
fields = api.model('Person', {
2062+
'name': restx.fields.String,
2063+
'age': restx.fields.Integer,
2064+
'birthdate': restx.fields.DateTime,
2065+
})
2066+
2067+
fields["children"] = restx.fields.List(
2068+
restx.fields.Nested(fields),
2069+
default=[],
2070+
)
2071+
2072+
@api.route('/recursive-model/')
2073+
@api.doc(get={'model': fields})
2074+
class ModelAsDict(restx.Resource):
2075+
@api.marshal_with(fields)
2076+
def get(self):
2077+
return {}
2078+
2079+
client.get_specs(status=200)
2080+
20602081
def test_specs_no_duplicate_response_keys(self, api, client):
20612082
"""
20622083
This tests that the swagger.json document will not be written with duplicate object keys

0 commit comments

Comments
 (0)