Giter VIP home page Giter VIP logo

wsgiproxy2's Introduction

Installation

With pip:

$ pip install WSGIProxy2

Install optionnal backends:

$ pip install requests urllib3

Usage

Create a proxy:

>>> from wsgiproxy import HostProxy
>>> proxy = HostProxy(application_url)

Then use it. Here is an example with WebOb but you can use it like a classic WSGI application:

>>> from webob import Request
>>> req = Request.blank('/form.html')
>>> resp = req.get_response(proxy)
>>> print(resp.text)
<html>...
...</html>

The Proxy application accept some keyword arguments. Those arguments are passed to the client during the process.

If no client as specified then python httplib is used. It's recommended to use a more robust client able to manage a connection pool and stuff.

Use urllib3:

>>> proxy = HostProxy(application_url, client='urllib3')

Use requests. This client support response streaming:

>>> proxy = HostProxy(application_url, client='requests')

wsgiproxy2's People

Contributors

almet avatar briansipos avatar cwebber avatar djeebus avatar gawel avatar jjjjw avatar lowks avatar lrowe avatar mgorny avatar vlcinsky avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

wsgiproxy2's Issues

0.4.6: pytest warnings

"setup.py build" -> "setup.py install --root </install/prefix>" -> pytest with PYTHONPATH pointing to python stearch and sitelib inside </install/prefix>

+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-WSGIProxy2-0.4.6-8.fc35.x86_64/usr/lib64/python3.8/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-WSGIProxy2-0.4.6-8.fc35.x86_64/usr/lib/python3.8/site-packages
+ PYTHONDONTWRITEBYTECODE=1
+ /usr/bin/pytest -ra
=========================================================================== test session starts ============================================================================
platform linux -- Python 3.8.9, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: /home/tkloczko/rpmbuild/BUILD/WSGIProxy2-0.4.6
plugins: forked-1.3.0, shutil-1.7.0, virtualenv-1.7.0, expect-1.1.0, httpbin-1.0.0, xdist-2.2.1, flake8-1.0.7, timeout-1.4.2, betamax-0.8.1, freezegun-0.4.2, case-1.5.3, isort-1.3.0, aspectlib-1.5.2, asyncio-0.15.1, toolbox-0.5, xprocess-0.17.1, aiohttp-0.3.0, checkdocs-2.7.0, mock-3.6.1, rerunfailures-9.1.1, requests-mock-1.9.3, Faker-8.4.0, cov-2.12.1, pyfakefs-4.5.0, hypothesis-6.13.14, cases-3.6.1
collected 29 items

. .                                                                                                                                                                  [  3%]
wsgiproxy/test_wsgiproxy.py ...........................                                                                                                              [100%]

============================================================================= warnings summary =============================================================================
../../../../../usr/lib/python3.8/site-packages/webtest/app.py:88
  /usr/lib/python3.8/site-packages/webtest/app.py:88: PytestCollectionWarning: cannot collect test class 'TestApp' because it has a __init__ constructor (from: wsgiproxy/test_wsgiproxy.py)
    class TestApp(object):

wsgiproxy/test_wsgiproxy.py: 21 warnings
  /usr/lib/python3.8/site-packages/waitress/adjustments.py:441: DeprecationWarning: In future versions of Waitress clear_untrusted_proxy_headers will be set to True by default. You may opt-out by setting this value to False, or opt-in explicitly by setting this to True.
    warnings.warn(

-- Docs: https://docs.pytest.org/en/stable/warnings.html
===================================================================== 28 passed, 22 warnings in 6.90s ======================================================================

Python 3.4 Resource Warning

I'm getting a resource warning (socket not being closed) when using WSGIProxy2 with bottle.

It looks like the connection is not being explicitly closed in HttpClient.call. I tried adding a 'conn.close()' before the return and it appears to have fixed the warning.

Query string lost with Werkzeug

When I tried to use WSGIProxy2 with Werkzeug’s test client, I noticed the query string gets lost. Here’s how to reproduce:

import werkzeug.test
import wsgiproxy

proxy = wsgiproxy.HostProxy("http://localhost:8080")
client = werkzeug.test.Client(proxy)
client.get("/x?a=b")

If you have an HTTP server running at localhost:8080, you’ll notice the query string is missing in the access log.

The issue is located here: https://github.com/gawel/WSGIProxy2/blob/0.4.6/wsgiproxy/proxies.py#L149-L166

It’s caused by Werkzeug’s definition of REQUEST_URI which does not contain the query string. I think this is unusual, but since REQUEST_URI is not standardized they’re not objectively wrong. It’s hard to please every WSGI server, each with their own interpretation of the environment, but PEP 3333 is clear: https://www.python.org/dev/peps/pep-3333/#url-reconstruction

Python’s standard library also provide an URL reconstruction method that should be a good fit: https://docs.python.org/3.8/library/wsgiref.html#wsgiref.util.request_uri

I don’t know why in WSGIProxy2 the non-standard REQUEST_URI and RAW_URI take precedence over the standard variables, but I daresay implementations conforming to PEP 3333 should work. However, if for some reason you had to make it that way, how about adding the query string after REQUEST_URI when it doesn’t already contain one even though QUERY_STRING is not empty?

Some tests in wsgiproxy.tests.TestRequests hang with Python 3

Some tests (test_chunked, test_form, test_redirect and test_status) in wsgiproxy.tests.TestRequests hang with Python 3.2 and 3.3.
This problem occurs in WSGIProxy2 0.3 and does not occur in WSGIProxy2 0.2.
(In WSGIProxy2 0.2, test_form and test_redirect fail, without hanging.)
I use Requests 1.2.3.

test_proxy (wsgiproxy.tests.TestExtractUri) ... ok
test_transparent_proxy (wsgiproxy.tests.TestExtractUri) ... ok
test_chunked (wsgiproxy.tests.TestHttplib) ... ok
test_form (wsgiproxy.tests.TestHttplib) ... ok
test_head (wsgiproxy.tests.TestHttplib) ... ok
test_not_allowed_method (wsgiproxy.tests.TestHttplib) ... ok
test_redirect (wsgiproxy.tests.TestHttplib) ... ok
test_status (wsgiproxy.tests.TestHttplib) ... ok
test_webob_error (wsgiproxy.tests.TestHttplib) ... ok
test_exception (wsgiproxy.tests.TestMisc) ... ok
test_rewrite_location (wsgiproxy.tests.TestMisc) ... ok
test_socket_gaierror (wsgiproxy.tests.TestMisc) ... ok
test_socket_timeout (wsgiproxy.tests.TestMisc) ... ok
test_chunked (wsgiproxy.tests.TestRequests) ...

OPTIONS method is not allowed

As hardcoded in proxies.py, OPTIONS is not one of the ALLOWED_METHODS.

This prevents me from fully testing my app via webtest (which uses wsgiproxy in one of its test modes).

Is there anything preventing you from adding it?

0.4.6: sphinx warnings

+ /usr/bin/python3 setup.py build_sphinx -b man --build-dir build/sphinx
running build_sphinx
Running Sphinx v4.0.2
making output directory... done
WARNING: html_static_path entry '_static' does not exist
building [mo]: targets for 0 po files that are out of date
building [man]: all manpages
updating environment: [new config] 3 added, 0 changed, 0 removed
reading sources... [100%] proxies
WARNING: autodoc: failed to import class 'restkit_client.HttpClient' from module 'wsgiproxy'; the following exception was raised:
No module named 'wsgiproxy.restkit_client'
looking for now-outdated files... none found
pickling environment... done
checking consistency... done
writing... python-WSGIProxy2.3 { proxies clients } done
build succeeded, 2 warnings.

Doctests failure due to missing README_fixt.py

README_fixt.py is missing in WSGIProxy2-0.4.zip available on PyPI, which results in failure of doctests.

$ nosetests-2.7
nose.config: INFO: Ignoring files matching ['^\\.', '^_', '^setup\\.py$']
nose.plugins.cover: INFO: Coverage report will include only packages: ['wsgiproxy']
Doctest: README.rst ... FAIL
nose.selector: INFO: /tmp/WSGIProxy2-0.4/wsgiproxy/restkit_client.py is executable; skipped
test_proxy (wsgiproxy.tests.TestExtractUri) ... ok
test_transparent_proxy (wsgiproxy.tests.TestExtractUri) ... ok
test_chunked (wsgiproxy.tests.TestHttplib) ... ok
test_form (wsgiproxy.tests.TestHttplib) ... ok
test_head (wsgiproxy.tests.TestHttplib) ... ok
test_not_allowed_method (wsgiproxy.tests.TestHttplib) ... ok
test_redirect (wsgiproxy.tests.TestHttplib) ... ok
test_status (wsgiproxy.tests.TestHttplib) ... ok
test_webob_error (wsgiproxy.tests.TestHttplib) ... ok
test_exception (wsgiproxy.tests.TestMisc) ... ok
test_rewrite_location (wsgiproxy.tests.TestMisc) ... ok
test_socket_gaierror (wsgiproxy.tests.TestMisc) ... ok
test_socket_timeout (wsgiproxy.tests.TestMisc) ... ok
test_chunked (wsgiproxy.tests.TestRequests) ... ok
test_form (wsgiproxy.tests.TestRequests) ... ok
test_head (wsgiproxy.tests.TestRequests) ... ok
test_not_allowed_method (wsgiproxy.tests.TestRequests) ... ok
test_redirect (wsgiproxy.tests.TestRequests) ... ok
test_status (wsgiproxy.tests.TestRequests) ... ok
test_webob_error (wsgiproxy.tests.TestRequests) ... ok
test_chunked (wsgiproxy.tests.TestRestkit) ... ok
test_form (wsgiproxy.tests.TestRestkit) ... ok
test_head (wsgiproxy.tests.TestRestkit) ... ok
test_not_allowed_method (wsgiproxy.tests.TestRestkit) ... ok
test_redirect (wsgiproxy.tests.TestRestkit) ... ok
test_status (wsgiproxy.tests.TestRestkit) ... ok
test_webob_error (wsgiproxy.tests.TestRestkit) ... ok
test_chunked (wsgiproxy.tests.TestUrllib3) ... ok
test_form (wsgiproxy.tests.TestUrllib3) ... ok
test_head (wsgiproxy.tests.TestUrllib3) ... ok
test_not_allowed_method (wsgiproxy.tests.TestUrllib3) ... ok
test_redirect (wsgiproxy.tests.TestUrllib3) ... ok
test_status (wsgiproxy.tests.TestUrllib3) ... ok
test_webob_error (wsgiproxy.tests.TestUrllib3) ... ok

======================================================================
FAIL: Doctest: README.rst
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib64/python2.7/doctest.py", line 2226, in runTest
    raise self.failureException(self.format_failure(new.getvalue()))
AssertionError: Failed doctest test for README.rst
  File "/tmp/WSGIProxy2-0.4/README.rst", line 0

----------------------------------------------------------------------
File "/tmp/WSGIProxy2-0.4/README.rst", line 19, in README.rst
Failed example:
    proxy = HostProxy(application_url)
Exception raised:
    Traceback (most recent call last):
      File "/usr/lib64/python2.7/doctest.py", line 1315, in __run
        compileflags, 1) in test.globs
      File "<doctest README.rst[1]>", line 1, in <module>
        proxy = HostProxy(application_url)
    NameError: name 'application_url' is not defined
----------------------------------------------------------------------
File "/tmp/WSGIProxy2-0.4/README.rst", line 26, in README.rst
Failed example:
    resp = req.get_response(proxy)
Exception raised:
    Traceback (most recent call last):
      File "/usr/lib64/python2.7/doctest.py", line 1315, in __run
        compileflags, 1) in test.globs
      File "<doctest README.rst[4]>", line 1, in <module>
        resp = req.get_response(proxy)
    NameError: name 'proxy' is not defined
----------------------------------------------------------------------
File "/tmp/WSGIProxy2-0.4/README.rst", line 27, in README.rst
Failed example:
    print(resp.text)
Exception raised:
    Traceback (most recent call last):
      File "/usr/lib64/python2.7/doctest.py", line 1315, in __run
        compileflags, 1) in test.globs
      File "<doctest README.rst[5]>", line 1, in <module>
        print(resp.text)
    NameError: name 'resp' is not defined
----------------------------------------------------------------------
File "/tmp/WSGIProxy2-0.4/README.rst", line 39, in README.rst
Failed example:
    proxy = HostProxy(application_url, client='urllib3')
Exception raised:
    Traceback (most recent call last):
      File "/usr/lib64/python2.7/doctest.py", line 1315, in __run
        compileflags, 1) in test.globs
      File "<doctest README.rst[6]>", line 1, in <module>
        proxy = HostProxy(application_url, client='urllib3')
    NameError: name 'application_url' is not defined
----------------------------------------------------------------------
File "/tmp/WSGIProxy2-0.4/README.rst", line 43, in README.rst
Failed example:
    proxy = HostProxy(application_url, client='requests')
Exception raised:
    Traceback (most recent call last):
      File "/usr/lib64/python2.7/doctest.py", line 1315, in __run
        compileflags, 1) in test.globs
      File "<doctest README.rst[7]>", line 1, in <module>
        proxy = HostProxy(application_url, client='requests')
    NameError: name 'application_url' is not defined


Name                        Stmts   Miss  Cover   Missing
---------------------------------------------------------
wsgiproxy                       3      0   100%   
wsgiproxy.proxies             157      0   100%   
wsgiproxy.requests_client      28      2    93%   26, 32
wsgiproxy.restkit_client        8      0   100%   
wsgiproxy.urllib3_client       21      0   100%   
---------------------------------------------------------
TOTAL                         217      2    99%   
----------------------------------------------------------------------
Ran 35 tests in 3.701s

FAILED (failures=1)

The proxy does not manipulate environ RAW_URI/REQUEST_URI the same way as other environ

As of 0.4.6, if the request environ contains a RAW_URI or REQUEST_URI value it is used directly, ignoring the "strip_script_name" constructor parameter.

This makes the proxy have different behavior for WSGI servers which set RAW_URI or REQUEST_URI from those which do not. It seems like a simple fix would be to detect-and-remove the environ['SCRIPT_NAME'] which is a PEP-333 required variable. The other '_URI' environ names are not PEP-333 standard.

As a workaround, a WSGI middleware can strip REQUEST_URI values.

failures in test from wsgiproxy2-0.4.2 under py3

In testsuite of wsgiproxy2-0.4.2 under py3. there are errorsX3;

test_quoted_utf8_url, test_quoted_utf8_url & test_quoted_utf8_url.

======================================================================
ERROR: test_quoted_utf8_url (wsgiproxy.tests.TestHttplib)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/mnt/gen2/TmpDir/portage/dev-python/wsgiproxy2-0.4.2/work/WSGIProxy2-0.4.2/wsgiproxy/tests.py", line 103, in test_quoted_utf8_url
    resp = self.app.get(path)
  File "/usr/lib64/python3.4/site-packages/webtest/app.py", line 286, in get
    expect_errors=expect_errors)
  File "/usr/lib64/python3.4/site-packages/webtest/app.py", line 595, in do_request
    self._check_status(status, res)
  File "/usr/lib64/python3.4/site-packages/webtest/app.py", line 624, in _check_status
    res)
webtest.app.AppError: Bad response: 500 Internal Server Error (not 200 OK or 3xx redirect for http://127.0.0.1:50200/targets/NR2F1%C3%82-human/)
b'Internal Server Error\r\n\r\nTraceback (most recent call last):\n  File "/usr/lib64/python3.4/site-packages/waitress/channel.py", line 337, in service\n    task.service()\n  File "/usr/lib64/python3.4/site-packages/waitress/task.py", line 173, in service\n    self.execute()\n  File "/usr/lib64/python3.4/site-packages/waitress/task.py", line 392, in execute\n    app_iter = self.channel.server.application(env, start_response)\n  File "/usr/lib64/python3.4/site-packages/webtest/http.py", line 83, in wrapper\n    return self.test_app(environ, start_response)\n  File "/usr/lib64/python3.4/site-packages/webtest/debugapp.py", line 49, in __call__\n    body = body.encode(\'ascii\')\nUnicodeEncodeError: \'ascii\' codec can\'t encode characters in position 151-152: ordinal not in range(128)\n\r\n\r\n(generated by waitress)'

======================================================================
ERROR: test_quoted_utf8_url (wsgiproxy.tests.TestRequests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/mnt/gen2/TmpDir/portage/dev-python/wsgiproxy2-0.4.2/work/WSGIProxy2-0.4.2/wsgiproxy/tests.py", line 103, in test_quoted_utf8_url
    resp = self.app.get(path)
  File "/usr/lib64/python3.4/site-packages/webtest/app.py", line 286, in get
    expect_errors=expect_errors)
  File "/usr/lib64/python3.4/site-packages/webtest/app.py", line 595, in do_request
    self._check_status(status, res)
  File "/usr/lib64/python3.4/site-packages/webtest/app.py", line 624, in _check_status
    res)
nose.proxy.AppError: Bad response: 500 Internal Server Error (not 200 OK or 3xx redirect for http://127.0.0.1:54658/targets/NR2F1%C3%82-human/)
b'Internal Server Error\r\n\r\nTraceback (most recent call last):\n  File "/usr/lib64/python3.4/site-packages/waitress/channel.py", line 337, in service\n    task.service()\n  File "/usr/lib64/python3.4/site-packages/waitress/task.py", line 173, in service\n    self.execute()\n  File "/usr/lib64/python3.4/site-packages/waitress/task.py", line 392, in execute\n    app_iter = self.channel.server.application(env, start_response)\n  File "/usr/lib64/python3.4/site-packages/webtest/http.py", line 83, in wrapper\n    return self.test_app(environ, start_response)\n  File "/usr/lib64/python3.4/site-packages/webtest/debugapp.py", line 49, in __call__\n    body = body.encode(\'ascii\')\nUnicodeEncodeError: \'ascii\' codec can\'t encode characters in position 276-277: ordinal not in range(128)\n\r\n\r\n(generated by waitress)'
-------------------- >> begin captured logging << --------------------
requests.packages.urllib3.connectionpool: INFO: Starting new HTTP connection (1): 127.0.0.1
requests.packages.urllib3.connectionpool: DEBUG: "GET /targets/NR2F1%C3%82-human/ HTTP/1.1" 500 799
--------------------- >> end captured logging << ---------------------

======================================================================
ERROR: test_quoted_utf8_url (wsgiproxy.tests.TestUrllib3)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/mnt/gen2/TmpDir/portage/dev-python/wsgiproxy2-0.4.2/work/WSGIProxy2-0.4.2/wsgiproxy/tests.py", line 103, in test_quoted_utf8_url
    resp = self.app.get(path)
  File "/usr/lib64/python3.4/site-packages/webtest/app.py", line 286, in get
    expect_errors=expect_errors)
  File "/usr/lib64/python3.4/site-packages/webtest/app.py", line 595, in do_request
    self._check_status(status, res)
  File "/usr/lib64/python3.4/site-packages/webtest/app.py", line 624, in _check_status
    res)
nose.proxy.AppError: Bad response: 500 Internal Server Error (not 200 OK or 3xx redirect for http://127.0.0.1:50848/targets/NR2F1%C3%82-human/)
b'Internal Server Error\r\n\r\nTraceback (most recent call last):\n  File "/usr/lib64/python3.4/site-packages/waitress/channel.py", line 337, in service\n    task.service()\n  File "/usr/lib64/python3.4/site-packages/waitress/task.py", line 173, in service\n    self.execute()\n  File "/usr/lib64/python3.4/site-packages/waitress/task.py", line 392, in execute\n    app_iter = self.channel.server.application(env, start_response)\n  File "/usr/lib64/python3.4/site-packages/webtest/http.py", line 83, in wrapper\n    return self.test_app(environ, start_response)\n  File "/usr/lib64/python3.4/site-packages/webtest/debugapp.py", line 49, in __call__\n    body = body.encode(\'ascii\')\nUnicodeEncodeError: \'ascii\' codec can\'t encode characters in position 169-170: ordinal not in range(128)\n\r\n\r\n(generated by waitress)'
-------------------- >> begin captured logging << --------------------
urllib3.connectionpool: INFO: Starting new HTTP connection (1): 127.0.0.1
urllib3.connectionpool: DEBUG: "GET /targets/NR2F1%C3%82-human/ HTTP/1.1" 500 799
--------------------- >> end captured logging << ---------------------

Name                        Stmts   Miss  Cover   Missing
---------------------------------------------------------
wsgiproxy                       3      0   100%   
wsgiproxy.proxies             161      0   100%   
wsgiproxy.requests_client      28      2    93%   26, 32
wsgiproxy.urllib3_client       21      0   100%   
---------------------------------------------------------
TOTAL                         213      2    99%   
----------------------------------------------------------------------
Ran 31 tests in 3.388s

FAILED (errors=3)

They are all UnicodeEncode type Errors re utf under py3. They all source immediately to
the system installed /waitress/channel.py", line 337. It's looking like code inadequately converted or ported to python3. I haven't had another user replicate yet. Can you replicate?

Release 0.4.6?

Just hit the issue fixed in #22. I worked around it, but it would nice to have that fix in a release 😄

The source transfer-encoding is copied to the destination

This is similar to #18 in that it deals with behavior which the HTTP client "fixes" in the response body.

This issue applies at least to the 'requests' client as well as the 'httplib' client. When the source response includes a chunked Transfer-Encoding, this header is relayed to the destination response but the client library has stripped off the chunk framing so the destination response is inconsistent (chunked T-E but non-chunked body).

No license file

The metadata in setup.py indicates the license is Expat (aka MIT), but no license file is provided. Please provide one!

WSGIProxy needs to quote PATH_INFO and QUERY_STRING on Python 3

I'm using WSGIProxy2 as part of WebTest and am seeing errors when using quoted unicode urls. I'll show the stack of what is going on at the point a UnicodeDecodeError is raised:

  test_function():
    testapp = webtest.TestApp(server_url)
    path = '/targets/NR2F1%C3%82-human/'
-> res = testapp.get(path, status=200)

  eggs/WebTest-2.0.16-py3.4.egg/webtest/app.py(321)get()
-> expect_errors=expect_errors)

(Pdb) args
url = '/targets/NR2F1%C3%82-human/'
(Pdb) p req.environ['PATH_INFO']
'/targets/NR2F1Ã\x82-human/'

The path is unquoted by webob.request.environ_from_url(path) which for Python 3 is::

def url_unquote(s):
    return unquote(s.encode('ascii')).decode('latin-1')
  eggs/WebTest-2.0.16-py3.4.egg/webtest/app.py(604)do_request()
-> res = req.get_response(app, catch_exc_info=True)
  eggs/WebOb-1.4-py3.4.egg/webob/request.py(1316)send()
-> application, catch_exc_info=True)
  eggs/WebOb-1.4-py3.4.egg/webob/request.py(1284)call_application()
-> app_iter = application(self.environ, start_response)
  eggs/WebTest-2.0.16-py3.4.egg/webtest/lint.py(198)lint_app()
-> iterator = application(environ, start_response_wrapper)
  eggs/WSGIProxy2-0.4.1-py3.4.egg/wsgiproxy/proxies.py(182)__call__()
-> response = self.process_request(uri, method, new_headers, environ)

(Pdb) pp environ['PATH_INFO']
'/targets/NR2F1Ã\x82-human/'
  eggs/WSGIProxy2-0.4.1-py3.4.egg/wsgiproxy/proxies.py(134)process_request()
-> return self.http(uri, method, environ['wsgi.input'], headers)
  eggs/WSGIProxy2-0.4.1-py3.4.egg/wsgiproxy/proxies.py(79)__call__()
-> conn.request(method, path, body, headers, **self.options)
  lib/python3.4/http/client.py(1090)request()
-> self._send_request(method, url, body, headers)
  lib/python3.4/http/client.py(1118)_send_request()
-> self.putrequest(method, url, **skips)

  lib/python3.4/http/client.py(975)putrequest()
    request = '%s %s %s' % (method, url, self._http_vsn_str)
    # Non-ASCII characters should have been eliminated earlier
-> self._output(request.encode('ascii'))

(Pdb) args
url = '/targets/NR2F1Ã\x82-human/'
(Pdb) p request
'GET /targets/NR2F1Ã\x82-human/ HTTP/1.1'
(Pdb) p request.encode('ascii')
*** UnicodeEncodeError: 'ascii' codec can't encode characters in position 18-19: ordinal not in range(128)

From pyramid.compat for reference:

if PY3: # pragma: no cover
    # see PEP 3333 for why we encode WSGI PATH_INFO to latin-1 before
    # decoding it to utf-8
    def decode_path_info(path):
        return path.encode('latin-1').decode('utf-8')
else:
    def decode_path_info(path):
        return path.decode('utf-8')

if PY3: # pragma: no cover
    # see PEP 3333 for why we decode the path to latin-1 
    from urllib.parse import unquote_to_bytes
    def unquote_bytes_to_wsgi(bytestring):
        return unquote_to_bytes(bytestring).decode('latin-1')
else:
    from urlparse import unquote as unquote_to_bytes
    def unquote_bytes_to_wsgi(bytestring):
        return unquote_to_bytes(bytestring)
(Pdb) from pyramid.compat import unquote_bytes_to_wsgi, decode_path_info
(Pdb) p unquote_bytes_to_wsgi(path)
'/targets/NR2F1Ã\x82-human/'
(Pdb) from webob.compat import url_unquote, url_quote
(Pdb) url_unquote(path)
'/targets/NR2F1Ã\x82-human/'
(Pdb) decode_path_info(url_unquote(path))
'/targets/NR2F1Â-human/'
(Pdb) url_quote(url_unquote(path))
'/targets/NR2F1%C3%83%C2%82-human/'

We'll have to be careful with the requoting. I'll see if I can unpick those parts soon and add to the ticket.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.