[Python-modules-commits] [python-werkzeug] 01/07: Import python-werkzeug_0.14.1+dfsg1.orig.tar.gz

Ondrej Novy onovy at debian.org
Mon Jan 8 16:46:17 UTC 2018


This is an automated email from the git hooks/post-receive script.

onovy pushed a commit to branch master
in repository python-werkzeug.

commit d604d9b2e4b9ebb7720d6f8b6fa397624dc2f3ff
Author: Ondřej Nový <onovy at debian.org>
Date:   Mon Jan 8 17:13:23 2018 +0100

    Import python-werkzeug_0.14.1+dfsg1.orig.tar.gz
---
 AUTHORS                                 |  37 ++++-
 CHANGES.rst                             |  54 +++++++-
 docs/middlewares.rst                    |   2 +
 docs/tutorial.rst                       |   4 +-
 setup.cfg                               |   1 +
 tests/__init__.py                       |   4 +-
 tests/conftest.py                       |  22 +--
 tests/contrib/cache/test_cache.py       |  55 ++++++--
 tests/contrib/{cache => }/test_cache.py | 235 ++++++++++++++++----------------
 tests/test_datastructures.py            |  46 ++++++-
 tests/test_http.py                      |  25 ++++
 tests/test_routing.py                   |  22 +++
 tests/test_security.py                  |   3 +-
 tests/test_serving.py                   |  59 +++++++-
 tests/test_test.py                      |  13 ++
 tests/test_wrappers.py                  | 101 +++++++++++++-
 tests/test_wsgi.py                      |  49 +++++++
 werkzeug/__init__.py                    |   2 +-
 werkzeug/_internal.py                   |  25 ++--
 werkzeug/_reloader.py                   |  58 ++++----
 werkzeug/contrib/cache.py               |  87 +++++++++---
 werkzeug/datastructures.py              |  68 ++++++---
 werkzeug/debug/__init__.py              |   6 +-
 werkzeug/formparser.py                  |  23 +++-
 werkzeug/http.py                        |  21 ++-
 werkzeug/routing.py                     |  28 ++--
 werkzeug/serving.py                     |  23 +++-
 werkzeug/test.py                        |  51 ++++++-
 werkzeug/useragents.py                  |   3 +-
 werkzeug/wrappers.py                    |  43 ++++--
 werkzeug/wsgi.py                        | 161 +++++++++++++++++++++-
 31 files changed, 1057 insertions(+), 274 deletions(-)

diff --git a/AUTHORS b/AUTHORS
index 82cd585..dc1c352 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -9,7 +9,42 @@ are:
 
 A full list of contributors is available from git with:
 
-    git shortlog -sne
+- Georg Brandl
+- Leif K-Brooks <eurleif at gmail.com>
+- Thomas Johansson
+- Marian Sigler
+- Ronny Pfannschmidt
+- Noah Slater <nslater at tumbolia.org>
+- Alec Thomas
+- Shannon Behrens
+- Christoph Rauch
+- Clemens Hermann
+- Jason Kirtland
+- Ali Afshar
+- Christopher Grebs <cg at webshox.org>
+- Sean Cazzell <seancazzell at gmail.com>
+- Florent Xicluna
+- Kyle Dawkins
+- Pedro Algarvio
+- Zahari Petkov
+- Ludvig Ericson
+- Kenneth Reitz
+- Daniel Neuhäuser
+- Markus Unterwaditzer
+- Joe Esposito <joe at joeyespo.com>
+- Abhinav Upadhyay <er.abhinav.upadhyay at gmail.com>
+- immerrr <immerrr at gmail.com>
+- Cédric Krier
+- Phil Jones
+- Michael Hunsinger
+- Lars Holm Nielsen
+- Joël Charles
+- Benjamin Dopplinger
+- Nils Steinger
+- Mark Szymanski
+- Andrew Bednar
+- Craig Blaszczyk
+- Felix König
 
 The SSL parts of the Werkzeug development server are partially taken
 from Paste. The same is true for the range support which comes from
diff --git a/CHANGES.rst b/CHANGES.rst
index 487df94..3ca5a62 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,6 +1,56 @@
 Werkzeug Changelog
 ==================
 
+Version 0.14.1
+--------------
+
+Released on December 31st 2017
+
+- Resolved a regression with status code handling in the integrated
+  development server.
+
+Version 0.14
+------------
+
+Released on December 31st 2017
+
+- HTTP exceptions are now automatically caught by
+  ``Request.application``.
+- Added support for edge as browser.
+- Added support for platforms that lack ``SpooledTemporaryFile``.
+- Add support for etag handling through if-match
+- Added support for the SameSite cookie attribute.
+- Added ``werkzeug.wsgi.ProxyMiddleware``
+- Implemented ``has`` for ``NullCache``
+- ``get_multi`` on cache clients now returns lists all the time.
+- Improved the watchdog observer shutdown for the reloader to not crash
+  on exit on older Python versions.
+- Added support for ``filename*`` filename attributes according to
+  RFC 2231
+- Resolved an issue where machine ID for the reloader PIN was not
+  read accurately on windows.
+- Added a workaround for syntax errors in init files in the reloader.
+- Added support for using the reloader with console scripts on windows.
+- The built-in HTTP server will no longer close a connection in cases
+  where no HTTP body is expected (204, 204, HEAD requests etc.)
+- The ``EnvironHeaders`` object now skips over empty content type and
+  lengths if they are set to falsy values.
+- Werkzeug will no longer send the content-length header on 1xx or
+  204/304 responses.
+- Cookie values are now also permitted to include slashes and equal
+  signs without quoting.
+- Relaxed the regex for the routing converter arguments.
+- If cookies are sent without values they are now assumed to have an
+  empty value and the parser accepts this.  Previously this could have
+  corrupted cookies that followed the value.
+- The test ``Client`` and ``EnvironBuilder`` now support mimetypes like
+  the request object does.
+- Added support for static weights in URL rules.
+- Better handle some more complex reloader scenarios where sys.path
+  contained non directory paths.
+- ``EnvironHeaders`` no longer raises weird errors if non string keys
+  are passed to it.
+
 
 Version 0.13
 ------------
@@ -54,7 +104,6 @@ Released on December 7th 2017
 .. _#1205: https://github.com/pallets/werkzeug/pull/1205
 .. _#1208: https://github.com/pallets/werkzeug/pull/1208
 
-
 Version 0.12.2
 --------------
 
@@ -122,6 +171,9 @@ Released on March 10th 2017
 - Color run_simple's terminal output based on HTTP codes ``#1013``.
 - Fix self-XSS in debugger console, see ``#1031``.
 - Fix IPython 5.x shell support, see ``#1033``.
+- Change Accept datastructure to sort by specificity first, allowing for more
+  accurate results when using ``best_match`` for mime types (for example in
+  ``requests.accept_mimetypes.best_match``)
 
 Version 0.11.16
 ---------------
diff --git a/docs/middlewares.rst b/docs/middlewares.rst
index 41f9a98..ced87ad 100644
--- a/docs/middlewares.rst
+++ b/docs/middlewares.rst
@@ -12,6 +12,8 @@ implemented as a WSGI middleware.
 .. autoclass:: SharedDataMiddleware
    :members: is_allowed
 
+.. autoclass:: ProxyMiddleware
+
 .. autoclass:: DispatcherMiddleware
 
 Also there's the …
diff --git a/docs/tutorial.rst b/docs/tutorial.rst
index 99f84e0..9435e34 100644
--- a/docs/tutorial.rst
+++ b/docs/tutorial.rst
@@ -195,7 +195,7 @@ Intermezzo: Running the Application
 Now you should be able to execute the file with `python` and see a server
 on your local machine::
 
-    $ python shortly.py 
+    $ python shortly.py
      * Running on http://127.0.0.1:5000/
      * Restarting with reloader: stat() polling
 
@@ -445,6 +445,8 @@ Step 9: The Style
 For this to look better than ugly black and white, here a simple
 stylesheet that goes along:
 
+*static/style.css*:
+
 .. sourcecode:: css
 
     body        { background: #E8EFF0; margin: 0; padding: 0; }
diff --git a/setup.cfg b/setup.cfg
index 358bb50..3b7506e 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -2,6 +2,7 @@
 minversion = 3.0
 testpaths = tests
 norecursedirs = tests/hypothesis
+filterwarnings = ignore::requests.packages.urllib3.exceptions.InsecureRequestWarning
 
 [bdist_wheel]
 universal = 1
diff --git a/tests/__init__.py b/tests/__init__.py
index b06cdc6..238b50c 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -2,7 +2,7 @@ def strict_eq(x, y):
     """Equality test bypassing the implicit string conversion in
     Python 2."""
     __tracebackhide__ = True
-    assert x == y
+    assert x == y, (x, y)
     assert issubclass(type(x), type(y)) or issubclass(type(y), type(x))
     if isinstance(x, dict) and isinstance(y, dict):
         x = sorted(x.items())
@@ -10,4 +10,4 @@ def strict_eq(x, y):
     elif isinstance(x, set) and isinstance(y, set):
         x = sorted(x)
         y = sorted(y)
-    assert repr(x) == repr(y)
+    assert repr(x) == repr(y), (x, y)
diff --git a/tests/conftest.py b/tests/conftest.py
index 37902fd..ce88577 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -20,6 +20,7 @@ import pytest
 from werkzeug import serving
 from werkzeug.utils import cached_property
 from werkzeug._compat import to_bytes
+from itertools import count
 
 
 try:
@@ -34,6 +35,9 @@ else:
         return xprocess
 
 
+port_generator = count(13220)
+
+
 def _patch_reloader_loop():
     def f(x):
         print('reloader loop finished')
@@ -64,9 +68,6 @@ def _dev_server():
     serving.run_simple(hostname='localhost', application=app,
                        **testsuite_app.kwargs)
 
-if __name__ == '__main__':
-    _dev_server()
-
 
 class _ServerInfo(object):
     xprocess = None
@@ -95,7 +96,6 @@ class _ServerInfo(object):
             except Exception as e:  # urllib also raises socketerrors
                 print(self.url)
                 print(e)
-        return False
 
     def wait_for_reloader(self):
         old_pid = self.last_pid
@@ -127,8 +127,9 @@ def dev_server(tmpdir, subprocess, request, monkeypatch):
     def run_dev_server(application):
         app_pkg = tmpdir.mkdir('testsuite_app')
         appfile = app_pkg.join('__init__.py')
+        port = next(port_generator)
         appfile.write('\n\n'.join((
-            'kwargs = dict(port=5001)',
+            'kwargs = dict(port=%d)' % port,
             textwrap.dedent(application)
         )))
 
@@ -151,7 +152,7 @@ def dev_server(tmpdir, subprocess, request, monkeypatch):
 
         def preparefunc(cwd):
             args = [sys.executable, __file__, str(tmpdir)]
-            return info.request_pid, args
+            return lambda: 'pid=%s' % info.request_pid(), args
 
         subprocess.ensure('dev_server', preparefunc, restart=True)
 
@@ -159,10 +160,15 @@ def dev_server(tmpdir, subprocess, request, monkeypatch):
             # Killing the process group that runs the server, not just the
             # parent process attached. xprocess is confused about Werkzeug's
             # reloader and won't help here.
-            pid = info.last_pid
-            os.killpg(os.getpgid(pid), signal.SIGTERM)
+            pid = info.request_pid()
+            if pid:
+                os.killpg(os.getpgid(pid), signal.SIGTERM)
         request.addfinalizer(teardown)
 
         return info
 
     return run_dev_server
+
+
+if __name__ == '__main__':
+    _dev_server()
diff --git a/tests/contrib/cache/test_cache.py b/tests/contrib/cache/test_cache.py
index b777b8c..8dce427 100644
--- a/tests/contrib/cache/test_cache.py
+++ b/tests/contrib/cache/test_cache.py
@@ -8,11 +8,10 @@
     :copyright: (c) 2014 by Armin Ronacher.
     :license: BSD, see LICENSE for more details.
 """
-import os
-
 import errno
 import pytest
 
+from werkzeug._compat import text_type
 from werkzeug.contrib import cache
 
 try:
@@ -34,6 +33,7 @@ except ImportError:
 
 class CacheTests(object):
     _can_use_fast_sleep = True
+    _guaranteed_deletes = False
 
     @pytest.fixture
     def fast_sleep(self, monkeypatch):
@@ -128,7 +128,8 @@ class CacheTests(object):
         fast_sleep(3)
         # timeout of zero means no timeout
         assert c.get('foo') == 'bar'
-        assert c.get('baz') is None
+        if self._guaranteed_deletes:
+            assert c.get('baz') is None
 
     def test_generic_has(self, c):
         assert c.has('foo') in (False, 0)
@@ -142,6 +143,8 @@ class CacheTests(object):
 
 
 class TestSimpleCache(CacheTests):
+    _guaranteed_deletes = True
+
     @pytest.fixture
     def make_cache(self):
         return cache.SimpleCache
@@ -157,6 +160,8 @@ class TestSimpleCache(CacheTests):
 
 
 class TestFileSystemCache(CacheTests):
+    _guaranteed_deletes = True
+
     @pytest.fixture
     def make_cache(self, tmpdir):
         return lambda **kw: cache.FileSystemCache(cache_dir=str(tmpdir), **kw)
@@ -168,23 +173,52 @@ class TestFileSystemCache(CacheTests):
         for i in range(2 * THRESHOLD):
             assert c.set(str(i), i)
 
-        cache_files = os.listdir(c._path)
-        assert len(cache_files) <= THRESHOLD
+        nof_cache_files = c.get(c._fs_count_file)
+        assert nof_cache_files <= THRESHOLD
 
     def test_filesystemcache_clear(self, c):
         assert c.set('foo', 'bar')
-        cache_files = os.listdir(c._path)
-        assert len(cache_files) == 1
+        nof_cache_files = c.get(c._fs_count_file)
+        assert nof_cache_files == 1
         assert c.clear()
-        cache_files = os.listdir(c._path)
+        nof_cache_files = c.get(c._fs_count_file)
+        assert nof_cache_files == 0
+        cache_files = c._list_dir()
         assert len(cache_files) == 0
 
+    def test_no_threshold(self, make_cache):
+        THRESHOLD = 0
+        c = make_cache(threshold=THRESHOLD)
+
+        for i in range(10):
+            assert c.set(str(i), i)
+
+        cache_files = c._list_dir()
+        assert len(cache_files) == 10
+
+        # File count is not maintained with threshold = 0
+        nof_cache_files = c.get(c._fs_count_file)
+        assert nof_cache_files is None
+
+    def test_count_file_accuracy(self, c):
+        assert c.set('foo', 'bar')
+        assert c.set('moo', 'car')
+        c.add('moo', 'tar')
+        assert c.get(c._fs_count_file) == 2
+        assert c.add('too', 'far')
+        assert c.get(c._fs_count_file) == 3
+        assert c.delete('moo')
+        assert c.get(c._fs_count_file) == 2
+        assert c.clear()
+        assert c.get(c._fs_count_file) == 0
+
 
 # don't use pytest.mark.skipif on subclasses
 # https://bitbucket.org/hpk42/pytest/issue/568
 # skip happens in requirements fixture instead
 class TestRedisCache(CacheTests):
     _can_use_fast_sleep = False
+    _guaranteed_deletes = True
 
     @pytest.fixture(scope='class', autouse=True)
     def requirements(self, subprocess):
@@ -228,6 +262,11 @@ class TestRedisCache(CacheTests):
         assert c._client.set(c.key_prefix + 'foo', '42')
         assert c.get('foo') == 42
 
+    def test_empty_host(self):
+        with pytest.raises(ValueError) as exc_info:
+            cache.RedisCache(host=None)
+        assert text_type(exc_info.value) == 'RedisCache host parameter may not be None'
+
 
 class TestMemcachedCache(CacheTests):
     _can_use_fast_sleep = False
diff --git a/tests/contrib/cache/test_cache.py b/tests/contrib/test_cache.py
similarity index 56%
copy from tests/contrib/cache/test_cache.py
copy to tests/contrib/test_cache.py
index b777b8c..a6eec99 100644
--- a/tests/contrib/cache/test_cache.py
+++ b/tests/contrib/test_cache.py
@@ -8,10 +8,9 @@
     :copyright: (c) 2014 by Armin Ronacher.
     :license: BSD, see LICENSE for more details.
 """
-import os
-
-import errno
 import pytest
+import os
+import random
 
 from werkzeug.contrib import cache
 
@@ -36,6 +35,11 @@ class CacheTests(object):
     _can_use_fast_sleep = True
 
     @pytest.fixture
+    def make_cache(self):
+        '''Return a cache class or factory.'''
+        raise NotImplementedError()
+
+    @pytest.fixture
     def fast_sleep(self, monkeypatch):
         if self._can_use_fast_sleep:
             def sleep(delta):
@@ -48,13 +52,8 @@ class CacheTests(object):
             return time.sleep
 
     @pytest.fixture
-    def make_cache(self):
-        """Return a cache class or factory."""
-        raise NotImplementedError()
-
-    @pytest.fixture
     def c(self, make_cache):
-        """Return a cache instance."""
+        '''Return a cache instance.'''
         return make_cache()
 
     def test_generic_get_dict(self, c):
@@ -69,7 +68,6 @@ class CacheTests(object):
     def test_generic_set_get(self, c):
         for i in range(3):
             assert c.set(str(i), i * i)
-
         for i in range(3):
             result = c.get(str(i))
             assert result == i * i, result
@@ -81,13 +79,18 @@ class CacheTests(object):
     def test_generic_get_many(self, c):
         assert c.set('foo', ['bar'])
         assert c.set('spam', 'eggs')
-        assert c.get_many('foo', 'spam') == [['bar'], 'eggs']
+        assert list(c.get_many('foo', 'spam')) == [['bar'], 'eggs']
 
     def test_generic_set_many(self, c):
         assert c.set_many({'foo': 'bar', 'spam': ['eggs']})
         assert c.get('foo') == 'bar'
         assert c.get('spam') == ['eggs']
 
+    def test_generic_expire(self, c, fast_sleep):
+        assert c.set('foo', 'bar', 1)
+        fast_sleep(5)
+        assert c.get('foo') is None
+
     def test_generic_add(self, c):
         # sanity check that add() works like set()
         assert c.add('foo', 'bar')
@@ -120,15 +123,21 @@ class CacheTests(object):
         assert c.set('bar', False)
         assert c.get('bar') in (False, 0)
 
-    def test_generic_timeout(self, c, fast_sleep):
+    def test_generic_no_timeout(self, c, fast_sleep):
+        # Timeouts of zero should cause the cache to never expire
         c.set('foo', 'bar', 0)
+        fast_sleep(random.randint(1, 5))
         assert c.get('foo') == 'bar'
-        c.set('baz', 'qux', 1)
-        assert c.get('baz') == 'qux'
-        fast_sleep(3)
-        # timeout of zero means no timeout
+
+    def test_generic_timeout(self, c, fast_sleep):
+        # Check that cache expires when the timeout is reached
+        timeout = random.randint(1, 5)
+        c.set('foo', 'bar', timeout)
         assert c.get('foo') == 'bar'
-        assert c.get('baz') is None
+        # sleep a bit longer than timeout to ensure there are no
+        # race conditions
+        fast_sleep(timeout + 5)
+        assert c.get('foo') is None
 
     def test_generic_has(self, c):
         assert c.has('foo') in (False, 0)
@@ -142,6 +151,7 @@ class CacheTests(object):
 
 
 class TestSimpleCache(CacheTests):
+
     @pytest.fixture
     def make_cache(self):
         return cache.SimpleCache
@@ -157,6 +167,7 @@ class TestSimpleCache(CacheTests):
 
 
 class TestFileSystemCache(CacheTests):
+
     @pytest.fixture
     def make_cache(self, tmpdir):
         return lambda **kw: cache.FileSystemCache(cache_dir=str(tmpdir), **kw)
@@ -164,130 +175,114 @@ class TestFileSystemCache(CacheTests):
     def test_filesystemcache_prune(self, make_cache):
         THRESHOLD = 13
         c = make_cache(threshold=THRESHOLD)
-
         for i in range(2 * THRESHOLD):
             assert c.set(str(i), i)
-
         cache_files = os.listdir(c._path)
         assert len(cache_files) <= THRESHOLD
 
     def test_filesystemcache_clear(self, c):
         assert c.set('foo', 'bar')
         cache_files = os.listdir(c._path)
-        assert len(cache_files) == 1
+        # count = 2 because of the count file
+        assert len(cache_files) == 2
         assert c.clear()
+
+        # The only file remaining is the count file
         cache_files = os.listdir(c._path)
-        assert len(cache_files) == 0
+        assert os.listdir(c._path) == [
+            os.path.basename(c._get_filename(c._fs_count_file))]
 
 
-# don't use pytest.mark.skipif on subclasses
+# Don't use pytest marker
 # https://bitbucket.org/hpk42/pytest/issue/568
-# skip happens in requirements fixture instead
-class TestRedisCache(CacheTests):
-    _can_use_fast_sleep = False
-
-    @pytest.fixture(scope='class', autouse=True)
-    def requirements(self, subprocess):
-        if redis is None:
-            pytest.skip('Python package "redis" is not installed.')
-
-        def prepare(cwd):
-            return '[Rr]eady to accept connections', ['redis-server']
-
-        try:
-            subprocess.ensure('redis_server', prepare)
-        except IOError as e:
-            # xprocess raises FileNotFoundError
-            if e.errno == errno.ENOENT:
-                pytest.skip('Redis is not installed.')
-            else:
-                raise
-
-        yield
-        subprocess.getinfo('redis_server').terminate()
-
-    @pytest.fixture(params=(None, False, True))
-    def make_cache(self, request):
-        if request.param is None:
-            host = 'localhost'
-        elif request.param:
-            host = redis.StrictRedis()
-        else:
-            host = redis.Redis()
-
-        c = cache.RedisCache(
-            host=host,
-            key_prefix='werkzeug-test-case:',
-        )
-        yield lambda: c
-        c.clear()
-
-    def test_compat(self, c):
-        assert c._client.set(c.key_prefix + 'foo', 'Awesome')
-        assert c.get('foo') == b'Awesome'
-        assert c._client.set(c.key_prefix + 'foo', '42')
-        assert c.get('foo') == 42
+if redis is not None:
+    class TestRedisCache(CacheTests):
+        _can_use_fast_sleep = False
+
+        @pytest.fixture(params=[
+            ([], dict()),
+            ([redis.Redis()], dict()),
+            ([redis.StrictRedis()], dict())
+        ])
+        def make_cache(self, xprocess, request):
+            def preparefunc(cwd):
+                return 'Ready to accept connections', ['redis-server']
+
+            xprocess.ensure('redis_server', preparefunc)
+            args, kwargs = request.param
+            c = cache.RedisCache(*args, key_prefix='werkzeug-test-case:',
+                                 **kwargs)
+            request.addfinalizer(c.clear)
+            return lambda: c
+
+        def test_compat(self, c):
+            assert c._client.set(c.key_prefix + 'foo', 'Awesome')
+            assert c.get('foo') == b'Awesome'
+            assert c._client.set(c.key_prefix + 'foo', '42')
+            assert c.get('foo') == 42
+
+
+# Don't use pytest marker
+# https://bitbucket.org/hpk42/pytest/issue/568
+if memcache is not None:
+    class TestMemcachedCache(CacheTests):
+        _can_use_fast_sleep = False
+
+        @pytest.fixture
+        def make_cache(self, xprocess, request):
+            def preparefunc(cwd):
+                return '', ['memcached']
+
+            xprocess.ensure('memcached', preparefunc)
+            c = cache.MemcachedCache(key_prefix='werkzeug-test-case:')
+            request.addfinalizer(c.clear)
+            return lambda: c
+
+        def test_compat(self, c):
+            assert c._client.set(c.key_prefix + 'foo', 'bar')
+            assert c.get('foo') == 'bar'
+
+        def test_huge_timeouts(self, c):
+            # Timeouts greater than epoch are interpreted as POSIX timestamps
+            # (i.e. not relative to now, but relative to epoch)
+            import random
+            epoch = 2592000
+            timeout = epoch + random.random() * 100
+            c.set('foo', 'bar', timeout)
+            assert c.get('foo') == 'bar'
+
+
+def _running_in_uwsgi():
+    try:
+        import uwsgi  # NOQA
+    except ImportError:
+        return False
+    else:
+        return True
 
 
-class TestMemcachedCache(CacheTests):
+ at pytest.mark.skipif(not _running_in_uwsgi(),
+                    reason="uWSGI module can't be imported outside of uWSGI")
+class TestUWSGICache(CacheTests):
     _can_use_fast_sleep = False
 
-    @pytest.fixture(scope='class', autouse=True)
-    def requirements(self, subprocess):
-        if memcache is None:
-            pytest.skip(
-                'Python package for memcache is not installed. Need one of '
-                '"pylibmc", "google.appengine", or "memcache".'
-            )
-
-        def prepare(cwd):
-            return '', ['memcached']
+    @pytest.fixture
+    def make_cache(self, xprocess, request):
+        c = cache.UWSGICache(cache='werkzeugtest')
+        request.addfinalizer(c.clear)
+        return lambda: c
 
-        try:
-            subprocess.ensure('memcached', prepare)
-        except IOError as e:
-            # xprocess raises FileNotFoundError
-            if e.errno == errno.ENOENT:
-                pytest.skip('Memcached is not installed.')
-            else:
-                raise
 
-        yield
-        subprocess.getinfo('memcached').terminate()
+class TestNullCache(object):
 
     @pytest.fixture
     def make_cache(self):
-        c = cache.MemcachedCache(key_prefix='werkzeug-test-case:')
-        yield lambda: c
-        c.clear()
-
-    def test_compat(self, c):
-        assert c._client.set(c.key_prefix + 'foo', 'bar')
-        assert c.get('foo') == 'bar'
-
-    def test_huge_timeouts(self, c):
-        # Timeouts greater than epoch are interpreted as POSIX timestamps
-        # (i.e. not relative to now, but relative to epoch)
-        epoch = 2592000
-        c.set('foo', 'bar', epoch + 100)
-        assert c.get('foo') == 'bar'
-
-
-class TestUWSGICache(CacheTests):
-    _can_use_fast_sleep = False
-
-    @pytest.fixture(scope='class', autouse=True)
-    def requirements(self):
-        try:
-            import uwsgi  # NOQA
-        except ImportError:
-            pytest.skip(
-                'Python "uwsgi" package is only avaialable when running '
-                'inside uWSGI.'
-            )
+        return cache.NullCache
 
     @pytest.fixture
-    def make_cache(self):
-        c = cache.UWSGICache(cache='werkzeugtest')
-        yield lambda: c
-        c.clear()
+    def c(self, make_cache):
+        return make_cache()
+
+    def test_nullcache_has(self, c):
+        assert c.has('foo') in (False, 0)
+        assert c.has('spam') in (False, 0)
diff --git a/tests/test_datastructures.py b/tests/test_datastructures.py
index d23dae5..4fde4fd 100644
--- a/tests/test_datastructures.py
+++ b/tests/test_datastructures.py
@@ -771,6 +771,24 @@ class TestEnvironHeaders(object):
         ]
         assert not self.storage_class({'wsgi.version': (1, 0)})
         assert len(self.storage_class({'wsgi.version': (1, 0)})) == 0
+        assert 42 not in headers
+
+    def test_skip_empty_special_vars(self):
+        env = {
+            'HTTP_X_FOO':               '42',
+            'CONTENT_TYPE':             '',
+            'CONTENT_LENGTH':           '',
+        }
+        headers = self.storage_class(env)
+        assert dict(headers) == {'X-Foo': '42'}
+
+        env = {
+            'HTTP_X_FOO':               '42',
+            'CONTENT_TYPE':             '',
+            'CONTENT_LENGTH':           '0',
+        }
+        headers = self.storage_class(env)
+        assert dict(headers) == {'X-Foo': '42', 'Content-Length': '0'}
 
     def test_return_type_is_unicode(self):
         # environ contains native strings; we return unicode
@@ -966,7 +984,14 @@ class TestAccept(object):
             'asterisk'
         assert accept.best_match(['star'], default=None) is None
 
-    @pytest.mark.skipif(True, reason='Werkzeug doesn\'t respect specificity.')
+    def test_accept_keep_order(self):
+        accept = self.storage_class([('*', 1)])
+        assert accept.best_match(["alice", "bob"]) == "alice"
+        assert accept.best_match(["bob", "alice"]) == "bob"
+        accept = self.storage_class([('alice', 1), ('bob', 1)])
+        assert accept.best_match(["alice", "bob"]) == "alice"
+        assert accept.best_match(["bob", "alice"]) == "bob"
+
     def test_accept_wildcard_specificity(self):
         accept = self.storage_class([('asterisk', 0), ('star', 0.5), ('*', 1)])
         assert accept.best_match(['star', 'asterisk'], default=None) == 'star'
@@ -976,6 +1001,25 @@ class TestAccept(object):
         assert accept.best_match(['asterisk'], default=None) is None
 
 
+class TestMIMEAccept(object):
+    storage_class = datastructures.MIMEAccept
+
+    def test_accept_wildcard_subtype(self):
+        accept = self.storage_class([('text/*', 1)])
+        assert accept.best_match(['text/html'], default=None) == 'text/html'
+        assert accept.best_match(['image/png', 'text/plain']) == 'text/plain'
+        assert accept.best_match(['image/png'], default=None) is None
+
+    def test_accept_wildcard_specificity(self):
+        accept = self.storage_class([('*/*', 1), ('text/html', 1)])
+        assert accept.best_match(['image/png', 'text/html']) == 'text/html'
+        assert accept.best_match(['image/png', 'text/plain']) == 'image/png'
+        accept = self.storage_class([('*/*', 1), ('text/html', 1),
+                                     ('image/*', 1)])
+        assert accept.best_match(['image/png', 'text/html']) == 'text/html'
+        assert accept.best_match(['text/plain', 'image/png']) == 'image/png'
+
+
 class TestFileStorage(object):
     storage_class = datastructures.FileStorage
 
diff --git a/tests/test_http.py b/tests/test_http.py
index 04ad73c..521cdee 100644
--- a/tests/test_http.py
+++ b/tests/test_http.py
@@ -380,6 +380,22 @@ class TestHTTPUtility(object):
         strict_eq(dict(http.parse_cookie('fo234{=bar; blub=Blah')),
                   {'fo234{': u'bar', 'blub': u'Blah'})
 
+        strict_eq(http.dump_cookie('key', 'xxx/'), 'key=xxx/; Path=/')
+        strict_eq(http.dump_cookie('key', 'xxx='), 'key=xxx=; Path=/')
+
+    def test_bad_cookies(self):
+        strict_eq(
+            dict(http.parse_cookie('first=IamTheFirst ; a=1; oops ; a=2 ;'
+                                   'second = andMeTwo;')),
+            {
+                'first': u'IamTheFirst',
+                'a': u'1',
+                'a': u'2',
+                'oops': u'',
+                'second': u'andMeTwo',
+            }
+        )
+
     def test_cookie_quoting(self):
         val = http.dump_cookie("foo", "?foo")
         strict_eq(val, 'foo="?foo"; Path=/')
@@ -440,6 +456,15 @@ class TestHTTPUtility(object):
         w = recwarn.pop()
         assert 'the limit is 512 bytes' in str(w.message)
 
+    @pytest.mark.parametrize('input, expected', [
+        ('strict', 'foo=bar; Path=/; SameSite=Strict'),
+        ('lax', 'foo=bar; Path=/; SameSite=Lax'),
+        (None, 'foo=bar; Path=/'),
+    ])
+    def test_cookie_samesite_attribute(self, input, expected):
+        val = http.dump_cookie('foo', 'bar', samesite=input)
+        strict_eq(val, expected)
+
 
 class TestRange(object):
 
diff --git a/tests/test_routing.py b/tests/test_routing.py
index dcdbc52..8bf5f15 100644
--- a/tests/test_routing.py
+++ b/tests/test_routing.py
@@ -209,6 +209,8 @@ def test_path():
         r.Rule('/', defaults={'name': 'FrontPage'}, endpoint='page'),
         r.Rule('/Special', endpoint='special'),
         r.Rule('/<int:year>', endpoint='year'),
+        r.Rule('/<path:name>:foo', endpoint='foopage'),
+        r.Rule('/<path:name>:<path:name2>', endpoint='twopage'),
         r.Rule('/<path:name>', endpoint='page'),
         r.Rule('/<path:name>/edit', endpoint='editpage'),
         r.Rule('/<path:name>/silly/<path:name2>', endpoint='sillypage'),
@@ -216,7 +218,9 @@ def test_path():
         r.Rule('/Talk:<path:name>', endpoint='talk'),
         r.Rule('/User:<username>', endpoint='user'),
         r.Rule('/User:<username>/<path:name>', endpoint='userpage'),
+        r.Rule('/User:<username>/comment/<int:id>-<int:replyId>', endpoint='usercomment'),
         r.Rule('/Files/<path:file>', endpoint='files'),
+        r.Rule('/<admin>/<manage>/<things>', endpoint='admin'),
     ])
     adapter = map.bind('example.org', '/')
 
@@ -224,6 +228,8 @@ def test_path():
     pytest.raises(r.RequestRedirect, lambda: adapter.match('/FrontPage'))
     assert adapter.match('/Special') == ('special', {})
     assert adapter.match('/2007') == ('year', {'year': 2007})
+    assert adapter.match('/Some:foo') == ('foopage', {'name': 'Some'})
+    assert adapter.match('/Some:bar') == ('twopage', {'name': 'Some', 'name2': 'bar'})
     assert adapter.match('/Some/Page') == ('page', {'name': 'Some/Page'})
     assert adapter.match('/Some/Page/edit') == ('editpage', {'name': 'Some/Page'})
     assert adapter.match('/Foo/silly/bar') == ('sillypage', {'name': 'Foo', 'name2': 'bar'})
@@ -233,8 +239,12 @@ def test_path():
     assert adapter.match('/User:thomas') == ('user', {'username': 'thomas'})
     assert adapter.match('/User:thomas/projects/werkzeug') == \
         ('userpage', {'username': 'thomas', 'name': 'projects/werkzeug'})
+    assert adapter.match('/User:thomas/comment/123-456') == \
+        ('usercomment', {'username': 'thomas', 'id': 123, 'replyId': 456})
     assert adapter.match('/Files/downloads/werkzeug/0.2.zip') == \
         ('files', {'file': 'downloads/werkzeug/0.2.zip'})
+    assert adapter.match('/Jerry/eats/cheese') == \
+        ('admin', {'admin': 'Jerry', 'manage': 'eats', 'things': 'cheese'})
 
 
 def test_dispatch():
@@ -520,6 +530,18 @@ def test_converter_with_tuples():
     assert kwargs['foo'] == ('qwert', 'yuiop')
 
 
+def test_anyconverter():
+    m = r.Map([
+        r.Rule('/<any(a1, a2):a>', endpoint='no_dot'),
+        r.Rule('/<any(a.1, a.2):a>', endpoint='yes_dot')
+    ])
+    a = m.bind('example.org', '/')
+    assert a.match('/a1') == ('no_dot', {'a': 'a1'})
+    assert a.match('/a2') == ('no_dot', {'a': 'a2'})
+    assert a.match('/a.1') == ('yes_dot', {'a': 'a.1'})
+    assert a.match('/a.2') == ('yes_dot', {'a': 'a.2'})
+
+
 def test_build_append_unknown():
     map = r.Map([
         r.Rule('/bar/<float:bazf>', endpoint='barf')
diff --git a/tests/test_security.py b/tests/test_security.py
index 1632bcb..8b4a9a3 100644
--- a/tests/test_security.py
+++ b/tests/test_security.py
@@ -9,6 +9,7 @@
     :license: BSD, see LICENSE for more details.
 """
 import os
+import posixpath
 import pytest
 
 from werkzeug.security import check_password_hash, generate_password_hash, \
@@ -71,7 +72,7 @@ def test_password_hashing():
 
 
 def test_safe_join():
-    assert safe_join('foo', 'bar/baz') == os.path.join('foo', 'bar/baz')
+    assert safe_join('foo', 'bar/baz') == posixpath.join('foo', 'bar/baz')
     assert safe_join('foo', '../bar/baz') is None
     if os.name == 'nt':
         assert safe_join('foo', 'foo\\bar') is None
diff --git a/tests/test_serving.py b/tests/test_serving.py
index fb46a0b..95462ee 100644
--- a/tests/test_serving.py
+++ b/tests/test_serving.py
@@ -11,8 +11,9 @@
 import os
 import ssl
 import sys
-import subprocess
 import textwrap
+import time
+import subprocess
 
 
 try:
@@ -34,7 +35,7 @@ import requests
 import requests.exceptions
 import pytest
 
-from werkzeug import __version__ as version, serving
+from werkzeug import __version__ as version, serving, _reloader
 
 
 def test_serving(dev_server):
@@ -209,6 +210,60 @@ def test_reloader_nested_broken_imports(tmpdir, dev_server):
     assert r.content == b'hello'
 
 
+ at pytest.mark.skipif(watchdog is None, reason='Watchdog not installed.')
+def test_reloader_reports_correct_file(tmpdir, dev_server):
+    real_app = tmpdir.join('real_app.py')
... 1532 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-werkzeug.git



More information about the Python-modules-commits mailing list