Repository: pallets/werkzeug
Pull request #1518 (2019-04-22 19:08:01)

Make LintMiddleware Python 3 compatible and add tests
  Fix https://github.com/pallets/werkzeug/issues/1510

This commit makes sure LintMiddleware works under Python3 and adds a couple of tests for it.


Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index 49c29ffb..0df304c5 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -16,6 +16,8 @@ Unreleased
 -   Fix the filename format string in
     :class:`~middleware.profiler.ProfilerMiddleware` to correctly handle
     float values. (:issue:`1511`)
+-   Update :class:`~middleware.lint.LintMiddleware` to work on Python 3.
+    (:issue:`1510`)
 
 
 Version 0.15.2
diff --git a/src/werkzeug/middleware/lint.py b/src/werkzeug/middleware/lint.py
index 15372819..98f95817 100644
--- a/src/werkzeug/middleware/lint.py
+++ b/src/werkzeug/middleware/lint.py
@@ -14,6 +14,8 @@ common HTTP errors such as non-empty responses for 304 status codes.
 """
 from warnings import warn
 
+from .._compat import implements_iterator
+from .._compat import PY2
 from .._compat import string_types
 from ..datastructures import Headers
 from ..http import is_entity_header
@@ -124,10 +126,14 @@ class GuardedWrite(object):
         self._chunks.append(len(s))
 
 
+@implements_iterator
 class GuardedIterator(object):
     def __init__(self, iterator, headers_set, chunks):
         self._iterator = iterator
-        self._next = iter(iterator).next
+        if PY2:
+            self._next = iter(iterator).next
+        else:
+            self._next = iter(iterator).__next__
         self.closed = False
         self.headers_set = headers_set
         self.chunks = chunks
@@ -135,7 +141,7 @@ class GuardedIterator(object):
     def __iter__(self):
         return self
 
-    def next(self):
+    def __next__(self):
         if self.closed:
             warn("Iterated over closed 'app_iter'.", WSGIWarning, stacklevel=2)
 


Test patch:
diff --git a/tests/middleware/test_lint.py b/tests/middleware/test_lint.py
new file mode 100644
index 00000000..748a53a3
--- /dev/null
+++ b/tests/middleware/test_lint.py
@@ -0,0 +1,87 @@
+# -*- coding: utf-8 -*-
+import pytest
+
+from werkzeug.middleware.lint import HTTPWarning
+from werkzeug.middleware.lint import LintMiddleware
+from werkzeug.middleware.lint import WSGIWarning
+from werkzeug.test import create_environ
+from werkzeug.test import run_wsgi_app
+
+
+def dummy_application(environ, start_response):
+    start_response("200 OK", [("Content-Type", "text/plain")])
+    return ["Foo"]
+
+
+def test_lint_middleware():
+    """ Test lint middleware runs for a dummy applications without warnings """
+    app = LintMiddleware(dummy_application)
+
+    environ = create_environ("/test")
+    app_iter, status, headers = run_wsgi_app(app, environ, buffered=True)
+    assert status == "200 OK"
+
+
+@pytest.mark.parametrize(
+    "key, value, message",
+    [
+        ("wsgi.version", (0, 7), "Environ is not a WSGI 1.0 environ."),
+        ("SCRIPT_NAME", "test", "'SCRIPT_NAME' does not start with a slash:"),
+        ("PATH_INFO", "test", "'PATH_INFO' does not start with a slash:"),
+    ],
+)
+def test_lint_middleware_check_environ(key, value, message):
+    app = LintMiddleware(dummy_application)
+
+    environ = create_environ("/test")
+    environ[key] = value
+    with pytest.warns(WSGIWarning, match=message):
+        app_iter, status, headers = run_wsgi_app(app, environ, buffered=True)
+    assert status == "200 OK"
+
+
+def test_lint_middleware_invalid_status():
+    def my_dummy_application(environ, start_response):
+        start_response("20 OK", [("Content-Type", "text/plain")])
+        return ["Foo"]
+
+    app = LintMiddleware(my_dummy_application)
+
+    environ = create_environ("/test")
+    with pytest.warns(WSGIWarning) as record:
+        run_wsgi_app(app, environ, buffered=True)
+
+    # Returning status 20 should raise three different warnings
+    assert len(record) == 3
+
+
+@pytest.mark.parametrize(
+    "headers, message",
+    [
+        (tuple([("Content-Type", "text/plain")]), "header list is not a list"),
+        (["fo"], "Headers must tuple 2-item tuples"),
+        ([("status", "foo")], "The status header is not supported"),
+    ],
+)
+def test_lint_middleware_http_headers(headers, message):
+    def my_dummy_application(environ, start_response):
+        start_response("200 OK", headers)
+        return ["Foo"]
+
+    app = LintMiddleware(my_dummy_application)
+
+    environ = create_environ("/test")
+    with pytest.warns(WSGIWarning, match=message):
+        run_wsgi_app(app, environ, buffered=True)
+
+
+def test_lint_middleware_invalid_location():
+    def my_dummy_application(environ, start_response):
+        start_response("200 OK", [("location", "foo")])
+        return ["Foo"]
+
+    app = LintMiddleware(my_dummy_application)
+
+    environ = create_environ("/test")
+    with pytest.warns(HTTPWarning, match="absolute URLs required for location header"):
+        run_wsgi_app(app, environ, buffered=True)