Repository: Pylons/webob

Issue:
Fixup w.r.PATH_SAFE to match RFC3986
Related to discussion in https://github.com/Pylons/pyramid/pull/2811.

Comments:

Patch:
diff --git a/.travis.yml b/.travis.yml
index de5f487..7037c57 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -12,12 +12,17 @@ matrix:
           env: TOXENV=py34
         - python: 3.5
           env: TOXENV=py35
+        - python: 3.6-dev
+          env: TOXENV=py36
+        - python: nightly
+          env: TOXENV=py37
         - python: pypy
           env: TOXENV=pypy
-        - python: pypy3
-          env: TOXENV=pypy3
         - python: 3.5
           env: TOXENV=py2-cover,py3-cover,coverage
+    allow_failures:
+        - env: TOXENV=py36
+        - env: TOXENV=py37
 
 install:
   - travis_retry pip install tox
diff --git a/CHANGES.txt b/CHANGES.txt
index af25400..2d1795f 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -39,6 +39,9 @@ Feature
 Bugfix
 ~~~~~~
 
+- Fixes request.PATH_SAFE to contain all of the path safe characters according
+  to RFC3986. See https://github.com/Pylons/webob/pull/291
+
 - WebOb's exceptions will lazily read underlying variables when inserted into
   templates to avoid expensive computations/crashes when inserting into the
   template. This had a bad performance regression on Py27 because of the way
@@ -84,4 +87,4 @@ Bugfix
   This is used in the exception handling code so that if you use a WebOb HTTP
   Exception and pass a generator to `app_iter` WebOb won't attempt to read the
   whole thing and instead allows it to be returned to the WSGI server. See
-  https://github.com/Pylons/webob/pull/259 
+  https://github.com/Pylons/webob/pull/259
diff --git a/tox.ini b/tox.ini
index 402fd6a..79ee9c3 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,7 +1,8 @@
 [tox]
 envlist =
-    py27,py33,py34,py35,pypy,pypy3,
+    py27,py33,py34,py35,py36,py37,pypy,
     docs,{py2,py3}-cover,coverage
+skip_missing_interpreters = True
 
 [testenv]
 # Most of these are defaults but if you specify any you can't fall back
@@ -11,10 +12,11 @@ basepython =
     py33: python3.3
     py34: python3.4
     py35: python3.5
+    py36: python3.6
+    py37: python3.7
     pypy: pypy
-    pypy3: pypy3
     py2: python2.7
-    py3: python3.4
+    py3: python3.5
 
 commands =
     pip install webob[testing]
diff --git a/webob/request.py b/webob/request.py
index 45bc6f9..191b1b7 100644
--- a/webob/request.py
+++ b/webob/request.py
@@ -83,7 +83,7 @@ class _NoDefault:
         return '(No Default)'
 NoDefault = _NoDefault()
 
-PATH_SAFE = '/:@&+$,'
+PATH_SAFE = "/~!$&'()*+,;=:@"
 
 _LATIN_ENCODINGS = (
     'ascii', 'latin-1', 'latin', 'latin_1', 'l1', 'latin1',


Test patch:
diff --git a/tests/test_request.py b/tests/test_request.py
index 9650702..de3b149 100644
--- a/tests/test_request.py
+++ b/tests/test_request.py
@@ -2558,11 +2558,17 @@ class TestRequest_functional(object):
         assert req.cookies == {'foo': '?foo'}
 
     def test_path_quoting(self):
-        path = '/:@&+$,/bar'
+        path = "/_.-~!$&'()*+,;=:@/bar"
         req = self._blankOne(path)
         assert req.path == path
         assert req.url.endswith(path)
 
+    def test_path_quoting_pct_encodes(self):
+        path = '/[]/bar'
+        req = self._blankOne(path)
+        assert req.path == '/%5B%5D/bar'
+        assert req.url.endswith('/%5B%5D/bar')
+
     def test_params(self):
         req = self._blankOne('/?a=1&b=2')
         req.method = 'POST'