Commits

Matthew Schinckel committed 4796c37 Merge

Merge

  • Participants
  • Parent commits 080106f, add5d6c

Comments (0)

Files changed (19)

 - added :func:`werkzeug.extract_path_info`
 - fixed a querystring quoting bug in :func:`url_fix`
 - added `fallback_mimetype` to :class:`werkzeug.SharedDataMiddleware`.
+- deprecated :meth:`BaseResponse.iter_encoded`'s charset parameter.
+- added :meth:`BaseResponse.make_sequence`,
+  :attr:`BaseResponse.is_sequence` and
+  :meth:`BaseResponse._ensure_sequence`.
+- added better __repr__ of :class:`werkzeug.Map`
 
 Version 0.5.1
 -------------

File docs/changes.rst

     -   `BaseResponse.header_list` was deprecated.  You should not
         need this function, `get_wsgi_headers` and the `to_list`
         method on the regular headers should serve as a replacement.
+    -   Deprecated `BaseResponse.iter_encoded`'s charset parameter.
     -   :class:`LimitedStream` non-silent usage was deprecated.
 
 `0.5`

File docs/wrappers.rst

 
    .. automethod:: __call__
 
+   .. automethod:: _ensure_sequence
+
 
 Mixin Classes
 =============

File tests/test_compat.py

 
 
 def test_exposed_werkzeug_mod():
-    """Make sure all public classes are from the werkzeug module."""
+    """Make sure all things are importable."""
     import werkzeug
-    wrong_modules = []
     for key in werkzeug.__all__:
-        obj = getattr(werkzeug, key)
-        if isinstance(obj, type) and obj.__module__ != 'werkzeug':
-            wrong_modules.append(obj)
-
-    if wrong_modules:
-        print 'objects with wrong modules: %s' % ', '.join(
-            (x.__module__ + '.' + x.__name__ for x in wrong_modules))
-        assert False, 'found objects with __module__ not set to werkzeug'
+        getattr(werkzeug, key)
 
 
 def test_demand_import():

File tests/test_datastructures.py

         assert type(ud) is type(d)
         print ud.lists()
         assert ud == d
+        assert pickle.loads(s.replace('werkzeug.datastructures', 'werkzeug')) == d
         ud['newkey'] = 'bla'
         assert ud != d
 
             nd = dtype(d)
             od = pickle.loads(pickle.dumps(nd, protocol))
             assert od == nd
+            assert pickle.loads(pickle.dumps(nd, protocol) \
+                .replace('werkzeug.datastructures', 'werkzeug')) == nd
             assert type(od) is type(nd)
 
 
         l = ImmutableList(range(100))
         ul = pickle.loads(pickle.dumps(l, protocol))
         assert l == ul
+        assert pickle.loads(pickle.dumps(l, protocol) \
+            .replace('werkzeug.datastructures', 'werkzeug')) == l
         assert type(l) is type(ul)
 
 

File tests/test_utils.py

 def test_import_string():
     """String based importing"""
     import cgi
+    from werkzeug.debug import DebuggedApplication
     assert import_string('cgi.escape') is cgi.escape
     assert import_string('cgi:escape') is cgi.escape
     assert import_string('XXXXXXXXXXXX', True) is None
     assert import_string('cgi.XXXXXXXXXXXX', True) is None
+    assert import_string(u'cgi.escape') is cgi.escape
+    assert import_string(u'werkzeug.debug.DebuggedApplication') is DebuggedApplication
     assert_raises(ImportError, import_string, 'XXXXXXXXXXXXXXXX')
     assert_raises(AttributeError, import_string, 'cgi.XXXXXXXXXX')
 

File tests/test_wrappers.py

     assert headers['location'] == \
         'http://%C3%BCser:p%C3%A4ssword@xn--n3h.net/p%C3%A5th'
     assert headers['content-location'] == 'http://xn--n3h.net/'
+
+
+def test_new_response_iterator_behavior():
+    """New response iterator encoding behavior"""
+    req = Request.from_values()
+    resp = Response(u'Hello Wörld!')
+
+    def get_content_length(resp):
+        headers = Headers.linked(resp.get_wsgi_headers(req.environ))
+        return headers.get('content-length', type=int)
+
+    def generate_items():
+        yield "Hello "
+        yield u"Wörld!"
+
+    # werkzeug encodes when set to `data` now, which happens
+    # if a string is passed to the response object.
+    assert resp.response == [u'Hello Wörld!'.encode('utf-8')]
+    assert resp.data == u'Hello Wörld!'.encode('utf-8')
+    assert get_content_length(resp) == 13
+    assert not resp.is_streamed
+    assert resp.is_sequence
+
+    # try the same for manual assignment
+    resp.data = u'Wörd'
+    assert resp.response == [u'Wörd'.encode('utf-8')]
+    assert resp.data == u'Wörd'.encode('utf-8')
+    assert get_content_length(resp) == 5
+    assert not resp.is_streamed
+    assert resp.is_sequence
+
+    # automatic generator sequence conversion
+    resp.response = generate_items()
+    assert resp.is_streamed
+    assert not resp.is_sequence
+    assert resp.data == u'Hello Wörld!'.encode('utf-8')
+    assert resp.response == ['Hello ', u'Wörld!'.encode('utf-8')]
+    assert not resp.is_streamed
+    assert resp.is_sequence
+
+    # automatic generator sequence conversion
+    resp.response = generate_items()
+    resp.implicit_seqence_conversion = False
+    assert resp.is_streamed
+    assert not resp.is_sequence
+    assert_raises(RuntimeError, lambda: resp.data)
+    resp.make_sequence()
+    assert resp.data == u'Hello Wörld!'.encode('utf-8')
+    assert resp.response == ['Hello ', u'Wörld!'.encode('utf-8')]
+    assert not resp.is_streamed
+    assert resp.is_sequence
+
+    # stream makes it a list no matter how the conversion is set
+    for val in True, False:
+        resp.implicit_seqence_conversion = val
+        resp.response = ("foo", "bar")
+        assert resp.is_sequence
+        resp.stream.write('baz')
+        assert resp.response == ['foo', 'bar', 'baz']

File werkzeug/_internal.py

 }
 
 
-def public(obj=None):
-    """Marks a function or class as publically available.  Do not call
-    functions that are not public.
-    """
-    obj.__module__ = 'werkzeug'
-    return obj
-
-
 class _Missing(object):
 
     def __repr__(self):
         )
 
 
-@public
 def _easteregg(app):
     """Like the name says.  But who knows how it works?"""
     gyver = '\n'.join([x + (77 - len(x)) * ' ' for x in '''

File werkzeug/datastructures.py

     :private:
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def __reduce_ex__(self, protocol):
         return type(self), (list(self),)
 
     :private:
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     __repr__ = _proxy_repr(list)
 
 
     .. versionadded:: 0.5
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def get(self, key, default=None, type=None):
         """Return the default value if the requested data doesn't exist.
         If `type` is provided and is a callable it should convert the value,
     .. versionadded:: 0.5
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def copy(self):
         """Return a shallow mutable copy of this object.  Keep in mind that
         the standard library's :func:`copy` function is a no-op for this class
                     or `None`.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     # the key error this class raises.  Because of circular dependencies
     # with the http exception module this class is created at the end of
     # this module.
     :param defaults: The list of default values for the :class:`Headers`.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     # the key error this class raises.  Because of circular dependencies
     # with the http exception module this class is created at the end of
     # this module.
     HTTP exceptions.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def __init__(self, environ):
         self.environ = environ
 
     exceptions.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def __reduce_ex__(self, protocol):
         return type(self), (self.dicts,)
 
     .. versionadded:: 0.5
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def add_file(self, name, file, filename=None, content_type=None):
         """Adds a new file to the dict.  `file` can be a file name or
         a :class:`file`-like or a :class:`FileStorage` object.
     .. versionadded:: 0.5
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     __repr__ = _proxy_repr(dict)
 
     def copy(self):
     .. versionadded:: 0.5
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def copy(self):
         """Return a shallow mutable copy of this object.  Keep in mind that
         the standard library's :func:`copy` function is a no-op for this class
        :class:`Accept` objects are forzed immutable now.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def __init__(self, values=()):
         if values is None:
             list.__init__(self)
     mimetypes.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def _value_matches(self, value, item):
         def _normalize(x):
             x = x.lower()
 class LanguageAccept(Accept):
     """Like :class:`Accept` but with normalization for languages."""
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def _value_matches(self, value, item):
         def _normalize(language):
             return _locale_delim_re.split(language.lower())
 class CharsetAccept(Accept):
     """Like :class:`Accept` but with normalization for charsets."""
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def _value_matches(self, value, item):
         def _normalize(name):
             try:
        both for request and response.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     max_stale = cache_property('max-stale', '*', int)
     min_fresh = cache_property('min-fresh', '*', int)
     no_transform = cache_property('no-transform', None, None)
        both for request and response.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     public = cache_property('public', None, bool)
     private = cache_property('private', '*', None)
     must_revalidate = cache_property('must-revalidate', None, bool)
     The function is passed the dict instance.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def __init__(self, initial=None, on_update=None):
         dict.__init__(self, initial or ())
         self.on_update = on_update
     HeaderSet(['foo', 'bar', 'baz'])
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def __init__(self, headers=None, on_update=None):
         self._headers = list(headers or ())
         self._set = set([x.lower() for x in self._headers])
     of etags.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def __init__(self, strong_etags=None, weak_etags=None, star_tag=False):
         self._strong = frozenset(not star_tag and strong_etags or ())
         self._weak = frozenset(weak_etags or ())
        This object became immutable.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def __init__(self, auth_type, data=None):
         dict.__init__(self, data or {})
         self.type = auth_type
 class WWWAuthenticate(UpdateDictMixin, dict):
     """Provides simple access to `WWW-Authenticate` headers."""
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     #: list of keys that require quoting in the generated header
     _require_quoting = frozenset(['domain', 'nonce', 'opaque', 'realm'])
 
     ``storage.stream.read()``.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def __init__(self, stream=None, filename=None, name=None,
                  content_type='application/octet-stream', content_length=-1,
                  headers=None):

File werkzeug/exceptions.py

     The rest of the arguments are forwarded to the exception constructor.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def __init__(self, mapping=None, extra=None):
         if mapping is None:
             mapping = default_exceptions

File werkzeug/local.py

 class Local(object):
     __slots__ = ('__storage__', '__lock__')
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def __init__(self):
         object.__setattr__(self, '__storage__', {})
         object.__setattr__(self, '__lock__', allocate_lock())
     it, will clean up all the data left in the locals for this context.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def __init__(self, locals=None):
         if locals is None:
             self.locals = []
     """
     __slots__ = ('__local', '__dict__', '__name__')
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def __init__(self, local, name):
         object.__setattr__(self, '_LocalProxy__local', local)
         object.__setattr__(self, '__name__', name)

File werkzeug/routing.py

     :license: BSD, see LICENSE for more details.
 """
 import re
+from pprint import pformat
 from urlparse import urljoin
 from itertools import izip
 
             self._remap = False
 
 
+    def __repr__(self):
+        rules = self.iter_rules()
+        return '%s([%s])' % (self.__class__.__name__, pformat(list(rules)))
+
+
+
 class MapAdapter(object):
     """Returned by :meth:`Map.bind` or :meth:`Map.bind_to_environ` and does
     the URL matching and building based on runtime information.

File werkzeug/templates.py

     templates from files on the file system to get better debug output.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     default_context = {
         'escape':           utils.escape,
         'url_quote':        urls.url_quote,

File werkzeug/test.py

     :param charset: the charset used to encode unicode data.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     #: the server protocol to use.  defaults to HTTP/1.1
     server_protocol = 'HTTP/1.1'
 
        builtin cookie support.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def __init__(self, application, response_wrapper=None, use_cookies=True):
         self.application = application
         if response_wrapper is None:

File werkzeug/urls.py

         `sort` and `key` were added.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def __init__(self, base='./', charset='utf-8', sort=False, key=None):
         if not base:
             base = './'

File werkzeug/useragents.py

         the language of the browser
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     _parser = UserAgentParser()
 
     def __init__(self, environ_or_string):

File werkzeug/utils.py

        writeable setting and will always make the property writeable.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     # implementation detail: this property is implemented as non-data
     # descriptor.  non-data descriptors are only invoked if there is
     # no entry with the same name in the instance's __dict__
     by passing ``read_only=False`` to the constructor.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     read_only = True
 
     def lookup(self, obj):
 class header_property(_DictAccessorProperty):
     """Like `environ_property` but for headers."""
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def lookup(self, obj):
         return obj.headers
 
     u'<p>&lt;foo&gt;</p>'
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     from htmlentitydefs import name2codepoint
     _entity_re = re.compile(r'&([^;]+);')
     _entities = name2codepoint.copy()
             module, obj = import_name.rsplit('.', 1)
         else:
             return __import__(import_name)
+        # __import__ is not able to handle unicode strings in the fromlist
+        # if the module is a package
+        if isinstance(obj, unicode):
+            obj = obj.encode('utf-8')
         return getattr(__import__(module, None, None, [obj]), obj)
     except (ImportError, AttributeError):
         if not silent:
 class ArgumentValidationError(ValueError):
     """Raised if :func:`validate_arguments` fails to validate"""
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def __init__(self, missing=None, extra=None, extra_positional=None):
         self.missing = set(missing or ())
         self.extra = extra or {}

File werkzeug/wrappers.py

     return _run_wsgi_app(*args)
 
 
+def _warn_if_string(iterable):
+    """Helper for the response objects to check if the iterable returned
+    to the WSGI server is not a string.
+    """
+    if isinstance(iterable, basestring):
+        from warnings import Warning
+        warn(Warning('response iterable was set to a string.  This appears '
+                     'to work but means that the server will send the '
+                     'data to the client char, by char.  This is almost '
+                     'never intended behavior, use response.data to assign '
+                     'strings to the response object.'), stacklevel=2)
+
+
 class BaseRequest(object):
     """Very basic request object.  This does not implement advanced stuff like
     entity tag parsing or cache controls.  The request object is created with
        data.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     #: the charset for the request, defaults to utf-8
     charset = 'utf-8'
 
                                details.)
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     #: the charset of the response.
     charset = 'utf-8'
 
     #: the default mimetype if none is provided.
     default_mimetype = 'text/plain'
 
+    #: if set to `False` accessing properties on the response object will
+    #: not try to consume the response iterator and convert it into a list.
+    implicit_seqence_conversion = True
+
     def __init__(self, response=None, status=None, headers=None,
                  mimetype=None, content_type=None, direct_passthrough=False):
-        if response is None:
-            self.response = []
-        elif isinstance(response, basestring):
-            self.response = [response]
-        elif isinstance(response, (tuple, list)):
-            self.response = response
-        else:
-            self.response = iter(response)
         if isinstance(headers, Headers):
             self.headers = headers
         elif not headers:
             self.status_code = status
         else:
             self.status = status
+
         self.direct_passthrough = direct_passthrough
+        self._on_close = []
+
+        # we set the response after the headers so that if a class changes
+        # the charset attribute, the data is set in the correct charset.
+        if response is None:
+            self.response = []
+        elif isinstance(response, basestring):
+            self.data = response
+        else:
+            self.response = response
+
+    def call_on_close(self, func):
+        """Adds a function to the internal list of functions that should
+        be called as part of closing down the response.
+
+        .. versionadded:: 0.6
+        """
+        self._on_close.append(func)
 
     def __repr__(self):
-        if isinstance(self.response, (list, tuple)):
+        if self.is_sequence:
             body_info = '%d bytes' % sum(map(len, self.iter_encoded()))
         else:
             body_info = self.is_streamed and 'streamed' or 'likely-streamed'
         """The string representation of the request body.  Whenever you access
         this property the request iterable is encoded and flattened.  This
         can lead to unwanted behavior if you stream big data.
+
+        This behavior can be disabled by setting
+        :attr:`implicit_seqence_conversion` to `False`.
         """
-        if not isinstance(self.response, list):
-            self.response = list(self.response)
+        self._ensure_sequence()
         return ''.join(self.iter_encoded())
     def _set_data(self, value):
+        # if an unicode string is set, it's encoded directly.  this allows
+        # us to guess the content length automatically in `get_wsgi_headers`.
+        if isinstance(value, unicode):
+            value = value.encode(self.charset)
         self.response = [value]
     data = property(_get_data, _set_data, doc=_get_data.__doc__)
     del _get_data, _set_data
 
+    def _ensure_sequence(self, mutable=False):
+        """This method can be called by methods that need a sequence.  If
+        `mutable` is true, it will also ensure that the response sequence
+        is a standard Python list.
+
+        .. versionadded:: 0.6
+        """
+        if self.is_sequence:
+            # if we need a mutable object, we ensure it's a list.
+            if mutable and not isinstance(self.response, list):
+                self.response = list(self.response)
+            return
+        if not self.implicit_seqence_conversion:
+            raise RuntimeError('The response object required the iterable '
+                               'to be a sequence, but the implicit '
+                               'conversion was disabled.  Call '
+                               'make_sequence() yourself.')
+        self.make_sequence()
+
+    def make_sequence(self):
+        """Converts the response iterator in a list.  By default this happens
+        automatically if required.  If `implicit_seqence_conversion` is
+        disabled, this method is not automatically called and some properties
+        might raise exceptions.  This also encodes all the items.
+
+        .. versionadded:: 0.6
+        """
+        if not self.is_sequence:
+            # if we consume an iterable we have to ensure that the close
+            # method of the iterable is called if available when we tear
+            # down the response
+            close = getattr(self.response, 'close', None)
+            self.response = list(self.iter_encoded())
+            if close is not None:
+                self.call_on_close(close)
+
     def iter_encoded(self, charset=None):
-        """Iter the response encoded with the encoding specified.  If no
-        encoding is given the encoding from the class is used.  Note that
-        this does not encode data that is already a bytestring.  If the
-        response object is invoked as WSGI application the return value
-        of this method is used as application iterator except if
+        """Iter the response encoded with the encoding of the response.
+        If the response object is invoked as WSGI application the return
+        value of this method is used as application iterator unless
         :attr:`direct_passthrough` was activated.
+
+        .. versionchanged:: 0.6
+
+           The `charset` parameter was deprecated and became a no-op.
         """
-        charset = charset or self.charset
+        # XXX: deprecated
+        if __debug__ and charset is not None:
+            from warnings import warn
+            warn(DeprecationWarning('charset was deprecated and is ignored.'),
+                 stacklevel=2)
+        charset = self.charset
+        if __debug__:
+            _warn_if_string(self.response)
         for item in self.response:
             if isinstance(item, unicode):
                 yield item.encode(charset)
     @property
     def header_list(self):
         # XXX: deprecated
-        from warnings import warn
-        warn(DeprecationWarning('header_list is deprecated'),
-             stacklevel=2)
+        if __debug__:
+            from warnings import warn
+            warn(DeprecationWarning('header_list is deprecated'),
+                 stacklevel=2)
         return self.headers.to_list(self.charset)
 
     @property
     def is_streamed(self):
-        """If the response is streamed (the response is not a sequence) this
-        property is `True`.  In this case streamed means that there is no
-        information about the number of iterations.  This is usully `True`
-        if a generator is passed to the response object.
+        """If the response is streamed (the response is not a iterable with
+        a length information) this property is `True`.  In this case streamed
+        means that there is no information about the number of iterations.
+        This is usully `True` if a generator is passed to the response object.
 
         This is useful for checking before applying some sort of post
         filtering that should not take place for streamed responses.
             return True
         return False
 
+    @property
+    def is_sequence(self):
+        """If the iterator is buffered, this property will be `True`.  A
+        response object will consider an iterator to be buffered if the
+        response attribute is a list or tuple.
+
+        .. versionadded:: 0.6
+        """
+        return isinstance(self.response, (tuple, list))
+
     def close(self):
         """Close the wrapped response if possible."""
         if hasattr(self.response, 'close'):
             self.response.close()
+        for func in self._on_close:
+            func()
 
     def freeze(self):
         """Call this method if you want to make your response object ready for
         .. versionchanged:: 0.6
            The `Content-Length` header is now set.
         """
-        # this method invokes the descriptor on the base class because
-        # a subclass might change the behavior of that attribute
-        data = BaseResponse.data.__get__(self)
-        self.headers['Content-Length'] = str(len(data))
+        # we explicitly set the length to a list of the *encoded* response
+        # iterator.  Even if the implicit sequence conversion is disabled.
+        self.response = list(self.iter_encoded())
+        self.headers['Content-Length'] = str(sum(map(len, self.response)))
 
     def fix_headers(self, environ):
         # XXX: deprecated
-        from warnings import warn
-        warn(DeprecationWarning('called into deprecated fix_headers baseclass '
-                                'method.  Use get_wsgi_headers instead.'),
-             stacklevel=2)
+        if __debug__:
+            from warnings import warn
+            warn(DeprecationWarning('called into deprecated fix_headers baseclass '
+                                    'method.  Use get_wsgi_headers instead.'),
+                 stacklevel=2)
         self.headers[:] = self.get_wsgi_headers(environ)
 
     def get_wsgi_headers(self, environ):
            the response object in place.  Also since 0.6, IRIs in location
            and content-location headers are handled properly.
 
+           Also starting with 0.6, Werkzeug will attempt to set the content
+           length if it is able to figure it out on its own.  This is the
+           case if all the strings in the response iterable are already
+           encoded and the iterable is buffered.
+
         :param environ: the WSGI environment of the request.
         :return: returns a new :class:`Headers` object.
         """
         elif self.status_code == 304:
             remove_entity_headers(headers)
 
+        # if we can determine the content length automatically, we
+        # should try to do that.  But only if this does not involve
+        # flattening the iterator or encoding of unicode strings in
+        # the response.
+        if self.is_sequence and 'content-length' not in self.headers:
+            try:
+                content_length = sum(len(str(x)) for x in self.response)
+            except UnicodeError:
+                # aha, something non-bytestringy in there, too bad, we
+                # can't savely figure out the length of the response.
+                pass
+            else:
+                headers['Content-Length'] = str(content_length)
+
         return headers
 
     def get_app_iter(self, environ):
            100 <= self.status_code < 200 or self.status_code in (204, 304):
             return ()
         if self.direct_passthrough:
+            if __debug__:
+                _warn_if_string(self.response)
             return self.response
         return self.iter_encoded()
 
         # methods.
         if self.fix_headers.func_code is not \
            BaseResponse.fix_headers.func_code:
-            from warnings import warn
-            warn(DeprecationWarning('fix_headers changed behavior in 0.6 '
-                                    'and is now called get_wsgi_headers. '
-                                    'See documentation for more details.'),
-                 stacklevel=2)
+            if __debug__:
+                from warnings import warn
+                warn(DeprecationWarning('fix_headers changed behavior in 0.6 '
+                                        'and is now called get_wsgi_headers. '
+                                        'See documentation for more details.'),
+                     stacklevel=2)
             self.fix_headers(environ)
             headers = self.headers
         else:
     thereof).
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     @cached_property
     def accept_mimetypes(self):
         """List of mimetypes this client supports as :class:`MIMEAccept`
     only provides access to etags but also to the cache control header.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     @cached_property
     def cache_control(self):
         """A :class:`RequestCacheControl` object for the incoming cache control
     object.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     @cached_property
     def user_agent(self):
         """The current user agent."""
     of the `Authorization` header as :class:`Authorization` object.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     @cached_property
     def authorization(self):
         """The `Authorization` object in parsed form."""
     object that implements a dict like interface similar to :class:`Headers`.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     @property
     def cache_control(self):
         """The Cache-Control general-header field is used to specify
     def write(self, value):
         if self.closed:
             raise ValueError('I/O operation on closed file')
-        buf = self.response.response
-        if not isinstance(buf, list):
-            self.response.response = buf = list(buf)
-        buf.append(value)
+        self.response._ensure_sequence(mutable=True)
+        self.response.response.append(value)
 
     def writelines(self, seq):
         for item in seq:
     a write-only interface to the response iterable.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     @cached_property
     def stream(self):
         """The response iterable as write-only stream."""
     .. versionadded:: 0.5
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     content_type = environ_property('CONTENT_TYPE', doc='''
          The Content-Type entity-header field indicates the media type of
          the entity-body sent to the recipient or, in the case of the HEAD
     HTTP headers with automatic type conversion.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def _get_mimetype(self):
         ct = self.headers.get('content-type')
         if ct:
 class WWWAuthenticateMixin(object):
     """Adds a :attr:`www_authenticate` property to a response object."""
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     @property
     def www_authenticate(self):
         """The `WWW-Authenticate` header in a parsed form."""
     - :class:`CommonRequestDescriptorsMixin` for common headers
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
 
 class Response(BaseResponse, ETagResponseMixin, ResponseStreamMixin,
                CommonResponseDescriptorsMixin,
     - :class:`CommonResponseDescriptorsMixin` for various HTTP descriptors
     - :class:`WWWAuthenticateMixin` for HTTP authentication support
     """
-
-    # this class is public
-    __module__ = 'werkzeug'

File werkzeug/wsgi.py

     :Param cache_timeout: the cache timeout in seconds for the headers.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def __init__(self, app, exports, disallow=None, cache=True,
                  cache_timeout=60 * 60 * 12, fallback_mimetype='text/plain'):
         self.app = app
         })
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def __init__(self, app, mounts=None):
         self.app = app
         self.mounts = mounts or {}
             cleanup_locals()
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def __init__(self, iterable, callbacks=None):
         iterator = iter(iterable)
         self._next = iterator.next
     :param buffer_size: number of bytes for one iteration.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def __init__(self, file, buffer_size=8192):
         self.file = file
         self.buffer_size = buffer_size
                    past the limit and will return an empty string.
     """
 
-    # this class is public
-    __module__ = 'werkzeug'
-
     def __init__(self, stream, limit, silent=True):
         self._read = stream.read
         self._readline = stream.readline