[Python-modules-commits] [flask-restful] 01/12: Import flask-restful_0.3.5.orig.tar.gz
Ondřej Nový
onovy at moszumanska.debian.org
Sat Aug 13 21:56:57 UTC 2016
This is an automated email from the git hooks/post-receive script.
onovy pushed a commit to branch master
in repository flask-restful.
commit 90839985ac22066b03cf26576bf553460c71124e
Author: Ondřej Nový <onovy at debian.org>
Date: Sat Aug 13 23:24:13 2016 +0200
Import flask-restful_0.3.5.orig.tar.gz
---
.travis.yml | 1 +
AUTHORS.md | 14 +++++++++++
CHANGES.md | 14 +++++++++++
Makefile | 4 ++-
docs/conf.py | 1 +
docs/extending.rst | 16 ++++++++++++
docs/fields.rst | 7 ++++++
docs/intermediate-usage.rst | 4 +--
docs/quickstart.rst | 4 +--
docs/reqparse.rst | 59 ++++++++++++++++++++++++++++++++++++++++++++-
flask_restful/__init__.py | 34 +++++++++++++++-----------
flask_restful/fields.py | 8 +++---
flask_restful/inputs.py | 8 +++---
flask_restful/reqparse.py | 23 ++++++++++++------
setup.py | 5 +++-
tests/test_accept.py | 27 ++++++++++++++++++---
tests/test_api.py | 53 +++++++++++++++++++++++++++++++++++++---
tests/test_reqparse.py | 23 +++++++++++++++---
18 files changed, 257 insertions(+), 48 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index aac4d80..c5c259e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,6 @@
language: python
python:
+ - 3.5
- 3.4
- 3.3
- 2.7
diff --git a/AUTHORS.md b/AUTHORS.md
index 4e638ac..a6d301a 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -3,6 +3,7 @@ Authors
A huge thanks to all of our contributors:
+
- Alex Gaynor
- Alex M
- Alex Morken
@@ -22,6 +23,7 @@ A huge thanks to all of our contributors:
- Catherine Devlin
- Dan Quirk
- Daniele Esposti
+- Dario Bertini
- David Arnold
- David Baumgold
- David Boucha
@@ -30,15 +32,18 @@ A huge thanks to all of our contributors:
- Doug Black
- Evan Dale Aromin
- Frank Stratton
+- Frank Stratton ☺
- Garret Raziel
- Gary Belvin
- Gilles Dartiguelongue
- Giorgio Salluzzo
- Guillaume BINET
+- Heston Liebowitz
- Jacob Magnusson
- James Booth
- James Ogura
- James Turk
+- Jeff Widman
- Joakim Ekberg
- Johannes
- Jordan Yelloz
@@ -56,6 +61,7 @@ A huge thanks to all of our contributors:
- Lance Ingle
- Lars Holm Nielsen
- Luiz Armesto
+- Malthe Borch
- Marek Hlobil
- Matt Wright
- Max Peterson
@@ -64,6 +70,7 @@ A huge thanks to all of our contributors:
- Michael Newman
- Miguel Grinberg
- Mihai Tomescu
+- Nicolas Harraudeau
- Pavel Tyslyatsky
- Petrus J.v.Rensburg
- Philippe Ndiaye
@@ -73,20 +80,26 @@ A huge thanks to all of our contributors:
- Rod Cloutier
- Ryan Horn
- Sam Kimbrel
+- Samarth Shah
- Sander Sink
- Sasha Baranov
- Saul Diez-Guerra
+- Sergey Romanov
- Sven-Hendrik Haase
- Usman Ehtesham Gul
- Victor Neo
+- Vlad Frolov
- Vladimir Pal
- WooParadog
- Yaniv Aknin
- bret barker
+- hachichaud
+- jbouzekri
- justanr
- k-funk
- kelvinhammond
- kenjones
+- kumy
- lyschoening
- mailto1587
- mniebla
@@ -100,4 +113,5 @@ A huge thanks to all of our contributors:
- siavashg
- silasray
- soasme
+- ueg1990
- y-p
diff --git a/CHANGES.md b/CHANGES.md
index 99e5407..f79e8b2 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -3,6 +3,20 @@ Flask-RESTful Changelog
Here you can see the full list of changes between each Flask-RESTful release.
+
+Version 0.3.5
+-------------
+
+Released December 9, 2015
+
+- Add `nullable` option to request parser to allow/disallow null values for arguments ([#538](https://github.com/flask-restful/flask-restful/pull/538))
+- Use Flask's exception log method in `handle_error(e)` method instead of directly logging the exception notice. ([#496](https://github.com/flask-restful/flask-restful/pull/496))
+- `Argument.help` now allows more flexible message formatting using the `{error_msg}` string interpolation token. ([#518](https://github.com/flask-restful/flask-restful/pull/518))
+- Prevent representation from being chosen at random when `Accept: */*` ([#524](https://github.com/flask-restful/flask-restful/pull/524))
+- Headers from `HTTPException`s are now returned in the response instead of being discarded ([#523](https://github.com/flask-restful/flask-restful/pull/523))
+- Marshalling now checks for a `__marshallable__` method first before defaulting back to `__getitem__` ([](https://github.com/flask-restful/flask-restful/issues/324))
+- Flask 1.0 compatability fixes ([#506](https://github.com/flask-restful/flask-restful/pull/506))
+
Version 0.3.4
-------------
diff --git a/Makefile b/Makefile
index 2d4fdb4..77c127a 100644
--- a/Makefile
+++ b/Makefile
@@ -139,7 +139,7 @@ fix: .depends-dev
test: .depends-test .clean-test
$(NOSE) tests --with-coverage --cover-package=$(PACKAGE)
-test-all: test-py26 test-py27 test-py33 test-py34
+test-all: test-py26 test-py27 test-py33 test-py34 test-py35
test-py26:
PYTHON_MAJOR=2 PYTHON_MINOR=6 $(MAKE) test
test-py27:
@@ -148,6 +148,8 @@ test-py33:
PYTHON_MAJOR=3 PYTHON_MINOR=3 $(MAKE) test
test-py34:
PYTHON_MAJOR=3 PYTHON_MINOR=4 $(MAKE) test
+test-py35:
+ PYTHON_MAJOR=3 PYTHON_MINOR=5 $(MAKE) test
.PHONY: htmlcov
htmlcov: test
diff --git a/docs/conf.py b/docs/conf.py
index 050a709..86fafe6 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -257,6 +257,7 @@ texinfo_documents = [
intersphinx_mapping = {
'flask': ('http://flask.pocoo.org/docs/', None),
+ 'werkzeug': ('http://werkzeug.pocoo.org/docs/', None),
'python': ('https://docs.python.org/2/', None),
'python3': ('https://docs.python.org/3/', None),
'six': ('http://pythonhosted.org/six/', None),
diff --git a/docs/extending.rst b/docs/extending.rst
index 799280d..8888bc6 100644
--- a/docs/extending.rst
+++ b/docs/extending.rst
@@ -40,6 +40,22 @@ object.
application needs these customizations, you can replace the default JSON
representation with one using the Flask JSON module as described above.
+It is possible to configure how the default Flask-RESTful JSON representation
+will format JSON by providing a ``RESTFUL_JSON`` attribute on the application
+configuration. This setting is a dictionary with keys that correspond to the
+keyword arguments of :py:func:`json.dumps`. ::
+
+ class MyConfig(object):
+ RESTFUL_JSON = {'separators': (', ', ': '),
+ 'indent': 2,
+ 'cls': MyCustomEncoder}
+
+.. Note ::
+
+ If the application is running in debug mode (``app.debug = True``) and
+ either ``sort_keys`` or ``indent`` are not declared in the ``RESTFUL_JSON``
+ configuration setting, Flask-RESTful will provide defaults of ``True`` and
+ ``4`` respectively.
Custom Fields & Inputs
----------------------
diff --git a/docs/fields.rst b/docs/fields.rst
index baf7f67..7fd8e96 100644
--- a/docs/fields.rst
+++ b/docs/fields.rst
@@ -79,6 +79,13 @@ A lambda (or any callable) can also be specified as the ``attribute`` ::
'address': fields.String,
}
+Nested properties can also be accessed with ``attribute``:
+
+ fields = {
+ 'name': fields.String(attribute='people_list.0.person_dictionary.name'),
+ 'address': fields.String,
+ }
+
Default Values
--------------
diff --git a/docs/intermediate-usage.rst b/docs/intermediate-usage.rst
index 05f036b..f4399a8 100644
--- a/docs/intermediate-usage.rst
+++ b/docs/intermediate-usage.rst
@@ -120,7 +120,7 @@ exercise a larger amount of options. We'll define a resource named "User". ::
post_parser = reqparse.RequestParser()
post_parser.add_argument(
'username', dest='username',
- location='form', equired=True,
+ location='form', required=True,
help='The user\'s username',
)
post_parser.add_argument(
@@ -197,7 +197,7 @@ invalid. ::
The ``user_priority`` type takes advantage of the ``choices`` argument. This
means that if the provided `user_priority` value doesn't fall in the range
-specified by the ``choices`` argument (in this case ``[1, 2, 3, 4]``),
+specified by the ``choices`` argument (in this case ``[0, 1, 2, 3, 4]``),
Flask-RESTful will automatically respond with a 400 and a descriptive error
message.
diff --git a/docs/quickstart.rst b/docs/quickstart.rst
index a7c78b2..e860073 100644
--- a/docs/quickstart.rst
+++ b/docs/quickstart.rst
@@ -142,7 +142,7 @@ You can also match parts of the path as variables to your resource methods. ::
If a request does not match any of your application's endpoints,
Flask-RESTful will return a 404 error message with suggestions of other
- endpoints that closesly match the requested endpoint. This can be disabled
+ endpoints that closely match the requested endpoint. This can be disabled
by setting ``ERROR_404_HELP`` to ``False`` in your application config.
Argument Parsing
@@ -176,7 +176,7 @@ The :class:`inputs` module provides a number of included common conversion
functions such as :meth:`inputs.date` and :meth:`inputs.url`.
Calling ``parse_args`` with ``strict=True`` ensures that an error is thrown if
-the request includes arguments your parser does not define.
+the request includes arguments your parser does not define. ::
args = parser.parse_args(strict=True)
diff --git a/docs/reqparse.rst b/docs/reqparse.rst
index ddfda12..33d6323 100644
--- a/docs/reqparse.rst
+++ b/docs/reqparse.rst
@@ -3,6 +3,16 @@
Request Parsing
===============
+.. warning ::
+
+ The whole request parser part of Flask-RESTful is slated for removal and
+ will be replaced by documentation on how to integrate with other packages
+ that do the input/output stuff better
+ (such as `marshmallow <http://marshmallow.readthedocs.org>`_).
+ This means that it will be maintained until 2.0 but consider it deprecated.
+ Don't worry, if you have code using that now and wish to continue doing so,
+ it's not going to go away any time too soon.
+
.. currentmodule:: flask_restful
Flask-RESTful's request parsing interface, :mod:`reqparse`, is modeled after
@@ -31,7 +41,7 @@ the :attr:`flask.Request.values` dict: an integer and a string ::
If you specify the ``help`` value, it will be rendered as the error message
when a type error is raised while parsing it. If you do not specify a help
message, the default behavior is to return the message from the type error
-itself.
+itself. See :ref:`error-messages` for more details.
By default, arguments are **not** required. Also, arguments supplied in the
request that are not part of the RequestParser will be ignored.
@@ -101,6 +111,11 @@ to specify alternate locations to pull the values from. Any variable on the
# From file uploads
parser.add_argument('picture', type=werkzeug.datastructures.FileStorage, location='files')
+.. note ::
+
+ Only use ``type=list`` when ``location='json'``. `See this issue for more
+ details <https://github.com/flask-restful/flask-restful/issues/380>`_
+
Multiple Locations
------------------
@@ -108,8 +123,16 @@ Multiple argument locations can be specified by passing a list to ``location``::
parser.add_argument('text', location=['headers', 'values'])
+
+When multiple locations are specified, the arguments from all locations
+specified are combined into a single :class:`~werkzeug.datastructures.MultiDict`.
The last ``location`` listed takes precedence in the result set.
+If the argument location list includes the :attr:`~flask.Request.headers`
+location the argument names will no longer be case insensitive and must match
+their title case names (see :meth:`str.title`). Specifying
+``location='headers'`` (not as a list) will retain case insensitivity.
+
Parser Inheritance
------------------
@@ -188,3 +211,37 @@ The application configuration key is "BUNDLE_ERRORS". For example ::
``BUNDLE_ERRORS`` is a global setting that overrides the ``bundle_errors``
option in individual :class:`~reqparse.RequestParser` instances.
+
+
+.. _error-messages:
+Error Messages
+--------------
+
+Error messages for each field may be customized using the ``help`` parameter
+to ``Argument`` (and also ``RequestParser.add_argument``).
+
+If no help parameter is provided, the error message for the field will be
+the string representation of the type error itself. If ``help`` is provided,
+then the error message will be the value of ``help``.
+
+``help`` may include an interpolation token, ``{error_msg}``, that will be
+replaced with the string representation of the type error. This allows the
+message to be customized while preserving the original error:
+
+ from flask_restful import reqparse
+
+
+ parser = reqparse.RequestParser()
+ parser.add_argument(
+ 'foo',
+ choices=('one', 'two'),
+ help='Bad choice: {error_msg}'
+ )
+
+ # If a request comes in with a value of "three" for `foo`:
+
+ {
+ "message": {
+ "foo": "Bad choice: three is not a valid choice",
+ }
+ }
diff --git a/flask_restful/__init__.py b/flask_restful/__init__.py
index fe6fe6e..82a708a 100644
--- a/flask_restful/__init__.py
+++ b/flask_restful/__init__.py
@@ -7,6 +7,7 @@ from flask import abort as original_flask_abort
from flask import make_response as original_flask_make_response
from flask.views import MethodView
from flask.signals import got_request_exception
+from werkzeug.datastructures import Headers
from werkzeug.exceptions import HTTPException, MethodNotAllowed, NotFound, NotAcceptable, InternalServerError
from werkzeug.http import HTTP_STATUS_CODES
from werkzeug.wrappers import Response as ResponseBase
@@ -33,7 +34,7 @@ def abort(http_status_code, **kwargs):
e.data = kwargs
raise
-DEFAULT_REPRESENTATIONS = {'application/json': output_json}
+DEFAULT_REPRESENTATIONS = [('application/json', output_json)]
class Api(object):
@@ -75,7 +76,7 @@ class Api(object):
default_mediatype='application/json', decorators=None,
catch_all_404s=False, serve_challenge_on_401=False,
url_part_order='bae', errors=None):
- self.representations = dict(DEFAULT_REPRESENTATIONS)
+ self.representations = OrderedDict(DEFAULT_REPRESENTATIONS)
self.urls = {}
self.prefix = prefix
self.default_mediatype = default_mediatype
@@ -286,27 +287,34 @@ class Api(object):
else:
raise e
+ headers = Headers()
if isinstance(e, HTTPException):
code = e.code
default_data = {
'message': getattr(e, 'description', http_status_message(code))
}
+ headers = e.get_response().headers
else:
code = 500
default_data = {
'message': http_status_message(code),
}
+ # Werkzeug exceptions generate a content-length header which is added
+ # to the response in addition to the actual content-length header
+ # https://github.com/flask-restful/flask-restful/issues/534
+ remove_headers = ('Content-Length',)
+
+ for header in remove_headers:
+ headers.pop(header, None)
+
data = getattr(e, 'data', default_data)
- headers = {}
if code >= 500:
- # There's currently a bug in Python3 that disallows calling
- # logging.exception() when an exception hasn't actually be raised
- if sys.exc_info() == (None, None, None):
- current_app.logger.error("Internal Error")
- else:
- current_app.logger.exception("Internal Error")
+ exc_info = sys.exc_info()
+ if exc_info[1] is None:
+ exc_info = None
+ current_app.log_exception(exc_info)
help_on_404 = current_app.config.get("ERROR_404_HELP", True)
if code == 404 and help_on_404:
@@ -326,9 +334,6 @@ class Api(object):
rules[match] for match in close_matches)
) + ' ?'
- if code == 405:
- headers['Allow'] = e.valid_methods
-
error_cls_name = type(e).__name__
if error_cls_name in self.errors:
custom_data = self.errors.get(error_cls_name, {})
@@ -423,7 +428,8 @@ class Api(object):
resource_class_args = kwargs.pop('resource_class_args', ())
resource_class_kwargs = kwargs.pop('resource_class_kwargs', {})
- if endpoint in app.view_functions.keys():
+ # NOTE: 'view_functions' is cleaned up from Blueprint class in Flask 1.0
+ if endpoint in getattr(app, 'view_functions', {}):
previous_view_class = app.view_functions[endpoint].__dict__['view_class']
# if you override the endpoint with a different class, avoid the collision by raising an exception
@@ -583,7 +589,7 @@ class Resource(MethodView):
if isinstance(resp, ResponseBase): # There may be a better way to test
return resp
- representations = self.representations or {}
+ representations = self.representations or OrderedDict()
#noinspection PyUnresolvedReferences
mediatype = request.accept_mimetypes.best_match(representations, default=None)
diff --git a/flask_restful/fields.py b/flask_restful/fields.py
index ea92b53..3da13bd 100644
--- a/flask_restful/fields.py
+++ b/flask_restful/fields.py
@@ -35,7 +35,7 @@ def is_indexable_but_not_string(obj):
def get_value(key, obj, default=None):
"""Helper for pulling a keyed value off various types of objects"""
- if type(key) == int:
+ if isinstance(key, int):
return _get_value_for_key(key, obj, default)
elif callable(key):
return key(obj)
@@ -66,12 +66,12 @@ def to_marshallable_type(obj):
if obj is None:
return None # make it idempotent for None
- if hasattr(obj, '__getitem__'):
- return obj # it is indexable it is ok
-
if hasattr(obj, '__marshallable__'):
return obj.__marshallable__()
+ if hasattr(obj, '__getitem__'):
+ return obj # it is indexable it is ok
+
return dict(obj.__dict__)
diff --git a/flask_restful/inputs.py b/flask_restful/inputs.py
index c9dac10..2d2ad25 100644
--- a/flask_restful/inputs.py
+++ b/flask_restful/inputs.py
@@ -61,7 +61,7 @@ class regex(object):
def __call__(self, value):
if not self.re.search(value):
- message = 'Value does not match pattern: "{}"'.format(self.pattern)
+ message = 'Value does not match pattern: "{0}"'.format(self.pattern)
raise ValueError(message)
return value
@@ -192,7 +192,7 @@ def _get_integer(value):
try:
return int(value)
except (TypeError, ValueError):
- raise ValueError('{} is not a valid integer'.format(value))
+ raise ValueError('{0} is not a valid integer'.format(value))
def natural(value, argument='argument'):
@@ -239,7 +239,7 @@ def boolean(value):
already a native python boolean, and will be passed through without
further parsing.
"""
- if type(value) == bool:
+ if isinstance(value, bool):
return value
if not value:
@@ -249,7 +249,7 @@ def boolean(value):
return True
if value in ('false', '0',):
return False
- raise ValueError("Invalid literal for boolean(): {}".format(value))
+ raise ValueError("Invalid literal for boolean(): {0}".format(value))
def datetime_from_rfc822(datetime_str):
diff --git a/flask_restful/reqparse.py b/flask_restful/reqparse.py
index 31191be..07e42d9 100644
--- a/flask_restful/reqparse.py
+++ b/flask_restful/reqparse.py
@@ -54,19 +54,22 @@ class Argument(object):
iterator. The last item listed takes precedence in the result set.
:param choices: A container of the allowable values for the argument.
:param help: A brief description of the argument, returned in the
- response when the argument is invalid with the name of the argument and
- the message passed to any exception raised by a type converter.
+ response when the argument is invalid. May optionally contain
+ an "{error_msg}" interpolation token, which will be replaced with
+ the text of the error raised by the type converter.
:param bool case_sensitive: Whether argument values in the request are
case sensitive or not (this will convert all values to lowercase)
:param bool store_missing: Whether the arguments default value should
be stored if the argument is missing from the request.
:param bool trim: If enabled, trims whitespace around the argument.
+ :param bool nullable: If enabled, allows null value in argument.
"""
def __init__(self, name, default=None, dest=None, required=False,
ignore=False, type=text_type, location=('json', 'values',),
choices=(), action='store', help=None, operators=('=',),
- case_sensitive=True, store_missing=True, trim=False):
+ case_sensitive=True, store_missing=True, trim=False,
+ nullable=True):
self.name = name
self.default = default
self.dest = dest
@@ -81,6 +84,7 @@ class Argument(object):
self.operators = operators
self.store_missing = store_missing
self.trim = trim
+ self.nullable = nullable
def source(self, request):
"""Pulls values off the request in the provided location
@@ -107,7 +111,10 @@ class Argument(object):
def convert(self, value, op):
# Don't cast None
if value is None:
- return None
+ if self.nullable:
+ return None
+ else:
+ raise ValueError('Must not be null!')
# and check if we're expecting a filestorage and haven't overridden `type`
# (required because the below instantiation isn't valid for FileStorage)
@@ -134,12 +141,12 @@ class Argument(object):
dict with the name of the argument and the error message to be
bundled
"""
- help_str = '(%s) ' % self.help if self.help else ''
- error_msg = ' '.join([help_str, str(error)]) if help_str else str(error)
+ error_str = six.text_type(error)
+ error_msg = self.help.format(error_msg=error_str) if self.help else error_str
+ msg = {self.name: "{0}".format(error_msg)}
+
if current_app.config.get("BUNDLE_ERRORS", False) or bundle_errors:
- msg = {self.name: "%s" % (error_msg)}
return error, msg
- msg = {self.name: "%s" % (error_msg)}
flask_restful.abort(400, message=msg)
def parse(self, request, bundle_errors=False):
diff --git a/setup.py b/setup.py
index ac6d86c..f9fc494 100755
--- a/setup.py
+++ b/setup.py
@@ -16,7 +16,7 @@ if PY26:
setup(
name='Flask-RESTful',
- version='0.3.4',
+ version='0.3.5',
license='BSD',
url='https://www.github.com/flask-restful/flask-restful/',
author='Twilio API Team',
@@ -25,10 +25,13 @@ setup(
packages=find_packages(exclude=['tests']),
classifiers=[
'Framework :: Flask',
+ 'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
+ 'Programming Language :: Python :: 3.5',
'License :: OSI Approved :: BSD License',
],
zip_safe=False,
diff --git a/tests/test_accept.py b/tests/test_accept.py
index 7d78118..6eb3416 100644
--- a/tests/test_accept.py
+++ b/tests/test_accept.py
@@ -5,6 +5,7 @@ from werkzeug import exceptions
from nose.tools import assert_equals
+
class AcceptTestCase(unittest.TestCase):
def test_accept_default_application_json(self):
@@ -58,6 +59,28 @@ class AcceptTestCase(unittest.TestCase):
assert_equals(res.content_type, 'application/json')
+ def test_accept_default_any_pick_first(self):
+
+ class Foo(flask_restful.Resource):
+ def get(self):
+ return "data"
+
+ app = Flask(__name__)
+ api = flask_restful.Api(app)
+
+ @api.representation('text/plain')
+ def text_rep(data, status_code, headers=None):
+ resp = app.make_response((str(data), status_code, headers))
+ return resp
+
+ api.add_resource(Foo, '/')
+
+ with app.test_client() as client:
+ res = client.get('/', headers=[('Accept', '*/*')])
+ assert_equals(res.status_code, 200)
+ assert_equals(res.content_type, 'application/json')
+
+
def test_accept_no_default_no_match_not_acceptable(self):
class Foo(flask_restful.Resource):
@@ -216,7 +239,3 @@ class AcceptTestCase(unittest.TestCase):
with app.test_client() as client:
res = client.get('/', headers=[('Accept', 'text/plain')])
assert_equals(res.status_code, 500)
-
-
-
-
diff --git a/tests/test_api.py b/tests/test_api.py
index d8d5f2e..bb8d362 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -1,6 +1,6 @@
import unittest
import json
-from flask import Flask, Blueprint, redirect, views
+from flask import Flask, Blueprint, redirect, views, abort as flask_abort
from flask.signals import got_request_exception, signals_available
try:
from mock import Mock
@@ -10,6 +10,7 @@ except:
import flask
import werkzeug
from werkzeug.exceptions import HTTPException, Unauthorized, BadRequest, NotFound
+from werkzeug.http import quote_etag, unquote_etag
from flask_restful.utils import http_status_message, unpack
import flask_restful
import flask_restful.fields
@@ -52,7 +53,7 @@ class APITestCase(unittest.TestCase):
response.headers = {}
with app.test_request_context('/foo'):
response = api.unauthorized(response)
- assert_false('WWW-Autheneticate' in response.headers)
+ assert_false('WWW-Authenticate' in response.headers)
def test_unauthorized(self):
app = Flask(__name__)
@@ -779,9 +780,55 @@ class APITestCase(unittest.TestCase):
resp = app.post('/ids/3')
self.assertEquals(resp.status_code, 405)
self.assertEquals(resp.content_type, api.default_mediatype)
- self.assertEquals(set(resp.headers.get_all('Allow')),
+ # Allow can be of the form 'GET, PUT, POST'
+ allow = ', '.join(set(resp.headers.get_all('Allow')))
+ allow = set(method.strip() for method in allow.split(','))
+ self.assertEquals(allow,
set(['HEAD', 'OPTIONS'] + HelloWorld.methods))
+ def test_exception_header_forwarded(self):
+ """Test that HTTPException's headers are extended properly"""
+ app = Flask(__name__)
+ app.config['DEBUG'] = True
+ api = flask_restful.Api(app)
+
+ class NotModified(HTTPException):
+ code = 304
+
+ def __init__(self, etag, *args, **kwargs):
+ super(NotModified, self).__init__(*args, **kwargs)
+ self.etag = quote_etag(etag)
+
+ def get_headers(self, *args, **kwargs):
+ """Get a list of headers."""
+ return [('ETag', self.etag)]
+
+ class Foo1(flask_restful.Resource):
+ def get(self):
+ flask_abort(304, etag='myETag')
+
+ api.add_resource(Foo1, '/foo')
+ flask_abort.mapping.update({304: NotModified})
+
+ with app.test_client() as client:
+ foo = client.get('/foo')
+ self.assertEquals(foo.get_etag(),
+ unquote_etag(quote_etag('myETag')))
+
+ def test_exception_header_forwarding_doesnt_duplicate_headers(self):
+ """Test that HTTPException's headers do not add a duplicate
+ Content-Length header
+
+ https://github.com/flask-restful/flask-restful/issues/534
+ """
+ app = Flask(__name__)
+ api = flask_restful.Api(app)
+
+ with app.test_request_context('/'):
+ r = api.handle_error(BadRequest())
+
+ self.assertEqual(len(r.headers.getlist('Content-Length')), 1)
+
def test_will_prettyprint_json_in_debug_mode(self):
app = Flask(__name__)
app.config['DEBUG'] = True
diff --git a/tests/test_reqparse.py b/tests/test_reqparse.py
index a07f2d4..b0cb136 100644
--- a/tests/test_reqparse.py
+++ b/tests/test_reqparse.py
@@ -18,15 +18,27 @@ class ReqParseTestCase(unittest.TestCase):
self.assertEquals(arg.help, None)
@patch('flask_restful.abort')
- def test_help(self, abort):
+ def test_help_with_error_msg(self, abort):
app = Flask(__name__)
with app.app_context():
parser = RequestParser()
- parser.add_argument('foo', choices=['one', 'two'], help='Bad choice')
+ parser.add_argument('foo', choices=('one', 'two'), help='Bad choice: {error_msg}')
req = Mock(['values'])
req.values = MultiDict([('foo', 'three')])
parser.parse_args(req)
- expected = {'foo': '(Bad choice) three is not a valid choice'}
+ expected = {'foo': 'Bad choice: three is not a valid choice'}
+ abort.assert_called_with(400, message=expected)
+
+ @patch('flask_restful.abort')
+ def test_help_no_error_msg(self, abort):
+ app = Flask(__name__)
+ with app.app_context():
+ parser = RequestParser()
+ parser.add_argument('foo', choices=['one', 'two'], help='Please select a valid choice')
+ req = Mock(['values'])
+ req.values = MultiDict([('foo', 'three')])
+ parser.parse_args(req)
+ expected = {'foo': 'Please select a valid choice'}
abort.assert_called_with(400, message=expected)
@patch('flask_restful.abort', side_effect=exceptions.BadRequest('Bad Request'))
@@ -153,10 +165,13 @@ class ReqParseTestCase(unittest.TestCase):
self.assertEquals(arg.source(req), MultiDict(req.headers))
def test_convert_default_type_with_null_input(self):
- """convert() should properly handle case where input is None"""
arg = Argument('foo')
self.assertEquals(arg.convert(None, None), None)
+ def test_convert_with_null_input_when_not_nullable(self):
+ arg = Argument('foo', nullable=False)
+ self.assertRaises(ValueError, lambda: arg.convert(None, None))
+
def test_source_bad_location(self):
req = Mock(['values'])
arg = Argument('foo', location=['foo'])
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/flask-restful.git
More information about the Python-modules-commits
mailing list