Skip to content

Commit

Permalink
merge request and response wrapper mixins
Browse files Browse the repository at this point in the history
  • Loading branch information
davidism committed Jan 15, 2021
1 parent 8606f86 commit 2cd4fa9
Show file tree
Hide file tree
Showing 33 changed files with 2,815 additions and 2,829 deletions.
16 changes: 14 additions & 2 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,20 @@ Unreleased
- Deprecate the ``environ["werkzeug.server.shutdown"]`` function
that is available when running the development server. :issue:`1752`
- Remove the unused, internal ``posixemulation`` module. :issue:`1759`
- ``JSONMixin`` no longer uses simplejson if it's installed. To use
another JSON module, override ``JSONMixin.json_module``. :pr:`1766`
- Merge all request and response wrapper mixin code into single
``Request`` and ``Response`` classes. Using the mixin classes is no
longer necessary and will show a deprecation warning. Checking
``isinstance`` or ``issubclass`` against ``BaseRequest`` and
``BaseResponse`` will show a deprecation warning and check against
``Request`` or ``Response`` instead. :issue:`1963`
- JSON support no longer uses simplejson if it's installed. To use
another JSON module, override ``Request.json_module`` and
``Response.json_module``. :pr:`1766`
- ``Response.get_json()`` no longer caches the result, and the
``cache`` parameter is removed. :issue:`1698`
- ``Response.freeze()`` generates an ``ETag`` header if one is not
set. The ``no_etag`` parameter (which usually wasn't visible
anyway) is no longer used. :issue:`1963`
- Add a ``url_scheme`` argument to :meth:`~routing.MapAdapter.build`
to override the bound scheme. :pr:`1721`
- When passing a ``Headers`` object to a test client method or
Expand Down
8 changes: 4 additions & 4 deletions docs/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ This way we can also access URL arguments (the query string) and data that
was transmitted in a POST/PUT request.

For testing purposes we can create a request object from supplied data
using the :meth:`~BaseRequest.from_values` method:
using the :meth:`~Request.from_values` method:

>>> from io import StringIO
>>> data = "name=this+is+encoded+form+data&another_key=another+one"
Expand Down Expand Up @@ -102,7 +102,7 @@ example::
The files are represented as :class:`FileStorage` objects which provide
some common operations to work with them.

Request headers can be accessed by using the :class:`~BaseRequest.headers`
Request headers can be accessed by using the :class:`~Request.headers`
attribute:

>>> request.headers['Content-Length']
Expand Down Expand Up @@ -245,7 +245,7 @@ code or provide a message as well:
'400 BAD REQUEST'

As you can see attributes work in both directions. So you can set both
:attr:`~BaseResponse.status` and :attr:`~BaseResponse.status_code` and the
:attr:`~Response.status` and :attr:`~Response.status_code` and the
change will be reflected to the other.

Also common headers are exposed as attributes or with methods to set /
Expand Down Expand Up @@ -307,7 +307,7 @@ response conditional against a request. Which means that if the request
can assure that it has the information already, no data besides the headers
is sent over the network which saves traffic. For that you should set at
least an etag (which is used for comparison) and the date header and then
call :class:`~BaseRequest.make_conditional` with the request object.
call :class:`~Request.make_conditional` with the request object.

The response is modified accordingly (status code changed, response body
removed, entity headers removed etc.)
56 changes: 18 additions & 38 deletions docs/request_data.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The input stream has no end-of-file marker. If you would call the
application to hang on conforming servers. This is actually intentional
however painful. Werkzeug solves that problem by wrapping the input
stream in a special :class:`LimitedStream`. The input stream is exposed
on the request objects as :attr:`~BaseRequest.stream`. This one is either
on the request objects as :attr:`~Request.stream`. This one is either
an empty stream (if the form data was parsed) or a limited stream with
the contents of the input stream.

Expand All @@ -28,8 +28,8 @@ When does Werkzeug Parse?

Werkzeug parses the incoming data under the following situations:

- you access either :attr:`~BaseRequest.form`, :attr:`~BaseRequest.files`,
or :attr:`~BaseRequest.stream` and the request method was
- you access either :attr:`~Request.form`, :attr:`~Request.files`,
or :attr:`~Request.stream` and the request method was
`POST` or `PUT`.
- if you call :func:`parse_form_data`.

Expand All @@ -52,39 +52,39 @@ How does it Parse?
The standard Werkzeug parsing behavior handles three cases:

- input content type was `multipart/form-data`. In this situation the
:class:`~BaseRequest.stream` will be empty and
:class:`~BaseRequest.form` will contain the regular `POST` / `PUT`
data, :class:`~BaseRequest.files` will contain the uploaded
:class:`~Request.stream` will be empty and
:class:`~Request.form` will contain the regular `POST` / `PUT`
data, :class:`~Request.files` will contain the uploaded
files as :class:`FileStorage` objects.
- input content type was `application/x-www-form-urlencoded`. Then the
:class:`~BaseRequest.stream` will be empty and
:class:`~BaseRequest.form` will contain the regular `POST` / `PUT`
data and :class:`~BaseRequest.files` will be empty.
- the input content type was neither of them, :class:`~BaseRequest.stream`
:class:`~Request.stream` will be empty and
:class:`~Request.form` will contain the regular `POST` / `PUT`
data and :class:`~Request.files` will be empty.
- the input content type was neither of them, :class:`~Request.stream`
points to a :class:`LimitedStream` with the input data for further
processing.

Special note on the :attr:`~BaseRequest.get_data` method: Calling this
Special note on the :attr:`~Request.get_data` method: Calling this
loads the full request data into memory. This is only safe to do if the
:attr:`~BaseRequest.max_content_length` is set. Also you can *either*
read the stream *or* call :meth:`~BaseRequest.get_data`.
:attr:`~Request.max_content_length` is set. Also you can *either*
read the stream *or* call :meth:`~Request.get_data`.


Limiting Request Data
---------------------

To avoid being the victim of a DDOS attack you can set the maximum
accepted content length and request field sizes. The :class:`BaseRequest`
class has two attributes for that: :attr:`~BaseRequest.max_content_length`
and :attr:`~BaseRequest.max_form_memory_size`.
accepted content length and request field sizes. The :class:`Request`
class has two attributes for that: :attr:`~Request.max_content_length`
and :attr:`~Request.max_form_memory_size`.

The first one can be used to limit the total content length. For example
by setting it to ``1024 * 1024 * 16`` the request won't accept more than
16MB of transmitted data.

Because certain data can't be moved to the hard disk (regular post data)
whereas temporary files can, there is a second limit you can set. The
:attr:`~BaseRequest.max_form_memory_size` limits the size of `POST`
:attr:`~Request.max_form_memory_size` limits the size of `POST`
transmitted form data. By setting it to ``1024 * 1024 * 2`` you can make
sure that all in memory-stored fields are not more than 2MB in size.

Expand All @@ -96,25 +96,5 @@ How to extend Parsing?
----------------------

Modern web applications transmit a lot more than multipart form data or
url encoded data. To extend the capabilities, subclass :class:`BaseRequest`
url encoded data. To extend the capabilities, subclass :class:`Request`
or :class:`Request` and add or extend methods.

There is already a mixin that provides JSON parsing::

from werkzeug.wrappers import Request
from werkzeug.wrappers.json import JSONMixin

class JSONRequest(JSONMixin, Request):
pass

The basic implementation of that looks like::

import json
from werkzeug.utils import cached_property
from werkzeug.wrappers import Request

class JSONRequest(Request):
@cached_property
def json(self):
if self.mimetype == "application/json":
return json.loads(self.data)
2 changes: 1 addition & 1 deletion docs/terms.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ application but does not do any request processing. Usually you have a view
function or controller method that processes the request and assembles a
response object.

A response object is *not* necessarily the :class:`BaseResponse` object or a
A response object is *not* necessarily the :class:`Response` class or a
subclass thereof.

For example Pylons/webob provide a very similar response class that can
Expand Down
8 changes: 4 additions & 4 deletions docs/unicode.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,14 @@ encoding and error handling.

.. code-block:: python
from werkzeug.wrappers import Request as _Request
from werkzeug.wrappers import Response as _Response
from werkzeug.wrappers.request import Request
from werkzeug.wrappers.response import Response
class Request(_Request):
class Latin1Request(Request):
charset = "latin1"
encoding_errors = "strict"
class Response(_BaseResponse):
class Latin1Response(Response):
charset = "latin1"
The error handling can only be changed for the request. Werkzeug will
Expand Down
150 changes: 9 additions & 141 deletions docs/wrappers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,152 +73,20 @@ For the response object the following rules apply:
4. It's possible to create copies using `copy.deepcopy`.


Base Wrappers
=============

These objects implement a common set of operations. They are missing fancy
addon functionality like user agent parsing or etag handling. These features
are available by mixing in various mixin classes or using :class:`Request` and
:class:`Response`.

.. autoclass:: BaseRequest
:members:

.. attribute:: environ

The WSGI environment that the request object uses for data retrival.

.. attribute:: shallow

`True` if this request object is shallow (does not modify :attr:`environ`),
`False` otherwise.

.. automethod:: _get_file_stream


.. autoclass:: BaseResponse
:members:

.. attribute:: response

The application iterator. If constructed from a string this will be a
list, otherwise the object provided as application iterator. (The first
argument passed to :class:`BaseResponse`)

.. attribute:: headers

A :class:`Headers` object representing the response headers.

.. attribute:: direct_passthrough

If ``direct_passthrough=True`` was passed to the response object or if
this attribute was set to `True` before using the response object as
WSGI application, the wrapped iterator is returned unchanged. This
makes it possible to pass a special `wsgi.file_wrapper` to the response
object. See :func:`wrap_file` for more details.

.. automethod:: __call__

.. automethod:: _ensure_sequence


Mixin Classes
=============

Werkzeug also provides helper mixins for various HTTP related functionality
such as etags, cache control, user agents etc. When subclassing you can
mix those classes in to extend the functionality of the :class:`BaseRequest`
or :class:`BaseResponse` object. Here a small example for a request object
that parses accept headers::

from werkzeug.wrappers import AcceptMixin, BaseRequest

class Request(BaseRequest, AcceptMixin):
pass

The :class:`Request` and :class:`Response` classes subclass the :class:`BaseRequest`
and :class:`BaseResponse` classes and implement all the mixins Werkzeug provides:

Wrapper Classes
===============

.. autoclass:: Request

.. autoclass:: Response


Common Descriptors
------------------

.. autoclass:: CommonRequestDescriptorsMixin
:members:

.. autoclass:: CommonResponseDescriptorsMixin
:members:


Response Stream
---------------

.. autoclass:: ResponseStreamMixin
:members:


Accept
------

.. autoclass:: AcceptMixin
:members:


Authentication
--------------

.. autoclass:: AuthorizationMixin
:members:

.. autoclass:: WWWAuthenticateMixin
:members:


CORS
----

.. autoclass:: werkzeug.wrappers.cors.CORSRequestMixin
:members:
:member-order: bysource

.. autoclass:: werkzeug.wrappers.cors.CORSResponseMixin
:members:


ETag
----

.. autoclass:: ETagRequestMixin
:members:

.. autoclass:: ETagResponseMixin
:members:


User Agent
----------
.. automethod:: _get_file_stream

.. autoclass:: UserAgentMixin
:members:


Extra Mixin Classes
===================

These mixins are not included in the default :class:`Request` and
:class:`Response` classes. They provide extra behavior that needs to be
opted into by creating your own subclasses::

class Response(JSONMixin, BaseResponse):
pass

.. autoclass:: Response
:members:
:member-order: bysource

JSON
----
.. automethod:: __call__

.. autoclass:: werkzeug.wrappers.json.JSONMixin
:members:
.. automethod:: _ensure_sequence
6 changes: 3 additions & 3 deletions examples/coolmagic/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
from jinja2 import FileSystemLoader
from werkzeug.local import Local
from werkzeug.local import LocalManager
from werkzeug.wrappers import BaseRequest
from werkzeug.wrappers import BaseResponse
from werkzeug.wrappers import Request as BaseRequest
from werkzeug.wrappers import Response as BaseResponse


local = Local()
Expand Down Expand Up @@ -62,7 +62,7 @@ class Request(BaseRequest):
charset = "utf-8"

def __init__(self, environ, url_adapter):
BaseRequest.__init__(self, environ)
super().__init__(environ)
self.url_adapter = url_adapter
local.request = self

Expand Down
6 changes: 3 additions & 3 deletions examples/i18nurls/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
from werkzeug.exceptions import HTTPException
from werkzeug.exceptions import NotFound
from werkzeug.routing import RequestRedirect
from werkzeug.wrappers import BaseResponse
from werkzeug.wrappers import Request as _Request
from werkzeug.wrappers import Request as BaseRequest
from werkzeug.wrappers import Response as BaseResponse

from .urls import map

Expand All @@ -24,7 +24,7 @@ def wrapped(f):
return wrapped


class Request(_Request):
class Request(BaseRequest):
def __init__(self, environ, urls):
super().__init__(environ)
self.urls = urls
Expand Down
Loading

0 comments on commit 2cd4fa9

Please sign in to comment.