Repository: liberapay/liberapay.com

Issue:
Sending exceptions to Sentry is failing
That's why the migration to Python 3 appeared to go well when in fact it didn't.


Discussion:
The Sentry library we're using, <https://github.com/getsentry/raven-python>, is being phased out in favor of <https://github.com/getsentry/sentry-python>. Switching to the new lib might solve our problem.

Resolving patch:
diff --git a/liberapay/main.py b/liberapay/main.py
--- a/liberapay/main.py
+++ b/liberapay/main.py
@@ -372,22 +372,16 @@ def _decode_body(self):
 # The monkey-patch below is only for Pando 0.45, it should be removed after that
 def make_franken_uri(path, qs):
     if path:
-        try:
-            if type(path) is bytes:
-                path.decode('ascii')
-            else:
-                path = path.encode('ascii')
-        except UnicodeError:
-            path = urlquote(path, '%/').encode('ascii')
+        if type(path) is bytes:
+            path = urlquote(path, string.punctuation).encode('ascii')
+        else:
+            path = urlquote(path, string.punctuation, 'latin1').encode('ascii')
 
     if qs:
-        try:
-            if type(qs) is bytes:
-                qs.decode('ascii')
-            else:
-                qs = qs.encode('ascii')
-        except UnicodeError:
-            qs = urlquote_plus(qs, '%=&').encode('ascii')
+        if type(qs) is bytes:
+            qs = urlquote_plus(qs, string.punctuation).encode('ascii')
+        else:
+            qs = urlquote_plus(qs, string.punctuation, 'latin1').encode('ascii')
         qs = b'?' + qs
 
     return path + qs
diff --git a/liberapay/wireup.py b/liberapay/wireup.py
--- a/liberapay/wireup.py
+++ b/liberapay/wireup.py
@@ -502,7 +502,8 @@ def tell_sentry(exception, state, allow_reraise=True):
                         'method': request.method,
                         'url': request.line.uri.decoded,
                         'headers': {
-                            k: b', '.join(v) for k, v in request.headers.items()
+                            k.decode('ascii', 'repr'): b', '.join(v).decode('ascii', 'repr')
+                            for k, v in request.headers.items()
                             if k != b'Cookie'
                         },
                     }


Test patch:
diff --git a/tests/py/test_state_chain.py b/tests/py/test_state_chain.py
--- a/tests/py/test_state_chain.py
+++ b/tests/py/test_state_chain.py
@@ -195,25 +195,19 @@ def test_null_byte_results_in_400(self):
         assert r.code == 400, r.text
 
     def test_quoted_unicode_path_is_okay(self):
-        r = self.client.GET('/about/%C3%A9', raise_immediately=False)
-        assert r.code == 404, r.text
         r = self.client.GET('', PATH_INFO='/about/%C3%A9', raise_immediately=False)
         assert r.code == 404, r.text
 
     def test_unquoted_unicode_path_is_okay(self):
-        r = self.client.GET('/about/é'.encode('utf8'), raise_immediately=False)
-        assert r.code == 404, r.text
-        r = self.client.GET('', PATH_INFO='/about/é', raise_immediately=False)
+        path = '/about/é'.encode('utf8').decode('latin1')
+        r = self.client.GET('', PATH_INFO=path, raise_immediately=False)
         assert r.code == 404, r.text
 
     def test_quoted_unicode_querystring_is_okay(self):
-        r = self.client.GET('/', QUERY_STRING=b'%C3%A9=%C3%A9', raise_immediately=False)
-        assert r.code == 200, r.text
         r = self.client.GET('/', QUERY_STRING='%C3%A9=%C3%A9', raise_immediately=False)
         assert r.code == 200, r.text
 
     def test_unquoted_unicode_querystring_is_okay(self):
-        r = self.client.GET('/', QUERY_STRING='é=é'.encode('utf8'), raise_immediately=False)
-        assert r.code == 200, r.text
-        r = self.client.GET('/', QUERY_STRING='é=é', raise_immediately=False)
+        qs = 'é=é'.encode('utf8').decode('latin1')
+        r = self.client.GET('/', QUERY_STRING=qs, raise_immediately=False)
         assert r.code == 200, r.text