The WSGI environment on Python 3 works slightly different than it does on
Python 2. For the most part Werkzeug hides the differences from you if
you work on the higher level APIs. The main difference between Python 2
and Python 3 is that on Python 2 the WSGI environment contains bytes
whereas the environment on Python 3 contains a range of differently
encoded strings.

There are two different kinds of strings in the WSGI environ on Python 3:

unicode strings restricted to latin1 values. These are used for
HTTP headers and a few other things.

unicode strings carrying binary payload, roundtripped through latin1
values. This is usually referred as “WSGI encoding dance” throughout
Werkzeug.

Werkzeug provides you with functionality to deal with these automatically
so that you don’t need to be aware of the inner workings. The following
functions and classes should be used to read information out of the
WSGI environment:

Applications are strongly discouraged to create and modify a WSGI
environment themselves on Python 3 unless they take care of the proper
decoding step. All high level interfaces in Werkzeug will apply the
correct encoding and decoding steps as necessary.

URLs in Werkzeug attempt to represent themselves as unicode strings on
Python 3. All the parsing functions generally also provide functionality
that allow operations on bytes. In some cases functions that deal with
URLs allow passing in None as charset to change the return value to byte
objects. Internally Werkzeug will now unify URIs and IRIs as much as
possible.

Request objects on Python 3 and PyPy require explicit closing when file
uploads are involved. This is required to properly close temporary file
objects created by the multipart parser. For that purpose the close()
method was introduced.

In addition to that request objects now also act as context managers that
automatically close.