[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