Hello community, here is the log from the commit of package python-wsgi_intercept for openSUSE:Factory checked in at 2016-02-03 10:19:48 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-wsgi_intercept (Old) and /work/SRC/openSUSE:Factory/.python-wsgi_intercept.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "python-wsgi_intercept" Changes: -------- --- /work/SRC/openSUSE:Factory/python-wsgi_intercept/python-wsgi_intercept.changes 2015-05-19 23:47:48.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.python-wsgi_intercept.new/python-wsgi_intercept.changes 2016-02-03 10:19:50.000000000 +0100 @@ -1,0 +2,12 @@ +Mon Feb 1 13:59:28 UTC 2016 - dmueller@suse.com + +- update to 1.1.2: + * no changelog available + +------------------------------------------------------------------- +Mon Feb 1 13:53:40 UTC 2016 - dmueller@suse.com + +- update to 0.10.3: + * bugfixes, no changelog available + +------------------------------------------------------------------- Old: ---- wsgi_intercept-0.10.0.tar.gz New: ---- wsgi_intercept-1.1.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-wsgi_intercept.spec ++++++ --- /var/tmp/diff_new_pack.V2kbcD/_old 2016-02-03 10:19:51.000000000 +0100 +++ /var/tmp/diff_new_pack.V2kbcD/_new 2016-02-03 10:19:51.000000000 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-wsgi_intercept # -# Copyright (c) 2015 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: python-wsgi_intercept -Version: 0.10.0 +Version: 1.1.2 Release: 0 Summary: Installs a WSGI application in place of a real URI for testing License: MIT ++++++ wsgi_intercept-0.10.0.tar.gz -> wsgi_intercept-1.1.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/COPYRIGHT new/wsgi_intercept-1.1.2/COPYRIGHT --- old/wsgi_intercept-0.10.0/COPYRIGHT 2014-08-06 19:55:37.000000000 +0200 +++ new/wsgi_intercept-1.1.2/COPYRIGHT 1970-01-01 01:00:00.000000000 +0100 @@ -1,19 +0,0 @@ -Copyright (c) 2012-2014 wsgi-intercept contributors. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/LICENSE new/wsgi_intercept-1.1.2/LICENSE --- old/wsgi_intercept-0.10.0/LICENSE 1970-01-01 01:00:00.000000000 +0100 +++ new/wsgi_intercept-1.1.2/LICENSE 2015-12-28 13:23:56.000000000 +0100 @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2012-2015 wsgi-intercept contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/MANIFEST.in new/wsgi_intercept-1.1.2/MANIFEST.in --- old/wsgi_intercept-0.10.0/MANIFEST.in 2014-08-06 19:55:37.000000000 +0200 +++ new/wsgi_intercept-1.1.2/MANIFEST.in 2015-12-28 13:23:56.000000000 +0100 @@ -1,3 +1,3 @@ -include COPYRIGHT Makefile +include LICENSE Makefile recursive-include test * recursive-include docs * diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/Makefile new/wsgi_intercept-1.1.2/Makefile --- old/wsgi_intercept-0.10.0/Makefile 2014-08-06 19:55:37.000000000 +0200 +++ new/wsgi_intercept-1.1.2/Makefile 2016-01-27 10:09:49.000000000 +0100 @@ -1,11 +1,21 @@ -.PHONY: test clean tagv pypi release +.PHONY: test clean reclean tagv pypi release docs default: @echo "Pick a target (e.g., clean, test)" clean: - find wsgi_intercept -name "*.pyc" |xargs rm || true - find test -name "*.pyc" |xargs rm || true + find wsgi_intercept test -name "*.py[co]" |xargs rm || true + find wsgi_intercept test -type d -name "__pycache__" |xargs rmdir || true + rm -r dist || true + rm -r build || true + rm -r wsgi_intercept.egg-info || true + rm *.bundle || true + rm -r *-bundle* || true + cd docs && make clean + +reclean: + find wsgi_intercept test -name "*.py[co]" |xargs rm || true + find wsgi_intercept test -type d -name "__pycache__" |xargs rmdir || true rm -r dist || true rm -r build || true rm -r wsgi_intercept.egg-info || true @@ -28,8 +38,7 @@ pypi: python setup.py sdist upload -docs: docs/*.rst +docs: cd docs && $(MAKE) html -release: clean test tagv clean pypi - +release: clean test tagv reclean pypi diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/PKG-INFO new/wsgi_intercept-1.1.2/PKG-INFO --- old/wsgi_intercept-0.10.0/PKG-INFO 2015-05-05 17:58:27.000000000 +0200 +++ new/wsgi_intercept-1.1.2/PKG-INFO 2016-01-27 10:16:53.000000000 +0100 @@ -1,12 +1,12 @@ Metadata-Version: 1.1 Name: wsgi_intercept -Version: 0.10.0 +Version: 1.1.2 Summary: wsgi_intercept installs a WSGI application in place of a real URI for testing. Home-page: http://pypi.python.org/pypi/wsgi_intercept Author: Titus Brown, Kumar McMillan, Chris Dent, Sasha Hart Author-email: cdent@peermore.com License: MIT License -Description: installs a WSGI application in place of a real URI for testing. +Description: Installs a WSGI application in place of a real host for testing. Introduction ============ @@ -27,13 +27,41 @@ emulating a socket. If no intercept is registered for the host and port requested, those requests are passed on to the standard handler. - The functions ``add_wsgi_intercept(host, port, app_create_fn, - script_name='')`` and ``remove_wsgi_intercept(host,port)`` specify - which URLs should be redirect into what applications. Note especially - that ``app_create_fn`` is a *function object* returning a WSGI + The easiest way to use an intercept is to import an appropriate subclass + of ``~wsgi_intercept.interceptor.Interceptor`` and use that as a + context manager over web requests that use the library associated with + the subclass. For example:: + + import httplib2 + from wsgi_intercept.intercept import Httplib2Interceptor + from mywsgiapp import app + + def load_app(): + return app + + http = httplib2.Http() + with Httplib2Interceptor(load_app, host='example.com', port=80) as url: + response, content = http.request('%s%s' % (url, '/path')) + assert response.status == 200 + + The interceptor class may aslo be used directly to install intercepts. + See the module documentation for more information. + + Older versions required that the functions ``add_wsgi_intercept(host, + port, app_create_fn, script_name='')`` and ``remove_wsgi_intercept(host,port)`` + be used to specify which URLs should be redirected into what applications. + These methods are still available, but the ``Interceptor`` classes are likely + easier to use for most use cases. + + Note especially that ``app_create_fn`` is a *function object* returning a WSGI application; ``script_name`` becomes ``SCRIPT_NAME`` in the WSGI app's environment, if set. + Note also that if ``http_proxy`` or ``https_proxy`` is set in the environment + this can cause difficulties with some of the intercepted libraries. If + requests or urllib is being used, these will raise an exception if one of + those variables is set. + Install ======= @@ -60,7 +88,7 @@ `the tests`_. More comprehensive documentation available upon request. - .. _the tests: https://github.com/cdent/python3-wsgi-intercept/tree/master/test + .. _the tests: https://github.com/cdent/wsgi-intercept/tree/master/test History @@ -75,7 +103,7 @@ The Python 2 version of wsgi-intercept was the result. Kumar McMillan later took over maintenance. - The current version works with Python 2.6, 2.7, 3.3 and 3.4 and was assembled + The current version works with Python 2.7, 3.3, 3.4 and 3.5 and was assembled by `Chris Dent`_. Testing and documentation improvements from `Sasha Hart`_. .. _twill: http://www.idyll.org/~t/www-tools/twill.html @@ -93,7 +121,7 @@ Additional documentation is available on `Read The Docs`_. - .. _GitHub: http://github.com/cdent/python3-wsgi-intercept + .. _GitHub: http://github.com/cdent/wsgi-intercept .. _Read The Docs: http://wsgi-intercept.readthedocs.org/en/latest/ @@ -103,10 +131,10 @@ Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 Classifier: Topic :: Internet :: WWW/HTTP :: WSGI Classifier: Topic :: Software Development :: Testing diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/README new/wsgi_intercept-1.1.2/README --- old/wsgi_intercept-0.10.0/README 2014-12-15 21:02:09.000000000 +0100 +++ new/wsgi_intercept-1.1.2/README 2015-12-29 12:19:21.000000000 +0100 @@ -4,7 +4,7 @@ [![travis](https://secure.travis-ci.org/cdent/python3-wsgi-intercept.png)](https://secure.travis-ci.org/cdent/python3-wsgi-intercept) Python3 port of the important bits of wsgi-intercept, now working for -2.6, 2.7, 3.3 and 3.4. +2.7, 3.3, 3.4 and 3.5. Documentation is available on [Read The Docs](http://wsgi-intercept.readthedocs.org/en/latest/). @@ -12,9 +12,9 @@ What is it? =========== -wsgi_intercept installs a WSGI application in place of a real URI for -testing. See the [PyPI page](http://pypi.python.org/pypi/wsgi_intercept) -page for more details. +wsgi_intercept installs a WSGI application in place of a real host for +testing while still preserving HTTP semantics. See the +[PyPI page](http://pypi.python.org/pypi/wsgi_intercept) page for more details. Modern Version ----------- @@ -39,5 +39,4 @@ to keep in mind is that interceptor code from earlier versions, such as the interceptor for `mechanize` ought to work when imported independently (see [related -conversation](https://github.com/cdent/python3-wsgi-intercept/issues/16). - +conversation](https://github.com/cdent/wsgi-intercept/issues/16)). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/docs/conf.py new/wsgi_intercept-1.1.2/docs/conf.py --- old/wsgi_intercept-0.10.0/docs/conf.py 2014-06-24 16:08:28.000000000 +0200 +++ new/wsgi_intercept-1.1.2/docs/conf.py 2015-12-29 12:19:21.000000000 +0100 @@ -20,6 +20,9 @@ # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) +docroot = os.path.abspath('..') +sys.path.insert(0, docroot) + # -- General configuration ------------------------------------------------ @@ -48,16 +51,16 @@ # General information about the project. project = 'wsgi_intercept' -copyright = '2014, Titus Brown, Kumar McMillan, Chris Dent' +copyright = '2015, Titus Brown, Kumar McMillan, Chris Dent, Sasha Hart' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '0.6.2' +#version = '0.6.2' # The full version, including alpha/beta/rc tags. -release = '0.6.2' +#release = '0.6.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -268,7 +271,7 @@ epub_title = 'wsgi_intercept' epub_author = 'Titus Brown, Kumar McMillan, Chris Dent' epub_publisher = 'Titus Brown, Kumar McMillan, Chris Dent' -epub_copyright = '2014, Titus Brown, Kumar McMillan, Chris Dent' +epub_copyright = '2015, Titus Brown, Kumar McMillan, Chris Dent' # The basename for the epub file. It defaults to the project name. #epub_basename = 'wsgi_intercept' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/docs/index.rst new/wsgi_intercept-1.1.2/docs/index.rst --- old/wsgi_intercept-0.10.0/docs/index.rst 2014-06-24 16:08:28.000000000 +0200 +++ new/wsgi_intercept-1.1.2/docs/index.rst 2015-12-29 12:19:21.000000000 +0100 @@ -11,6 +11,7 @@ .. toctree:: :maxdepth: 1 + interceptor http_client httplib2 requests diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/docs/interceptor.rst new/wsgi_intercept-1.1.2/docs/interceptor.rst --- old/wsgi_intercept-0.10.0/docs/interceptor.rst 1970-01-01 01:00:00.000000000 +0100 +++ new/wsgi_intercept-1.1.2/docs/interceptor.rst 2015-12-29 12:19:21.000000000 +0100 @@ -0,0 +1,28 @@ + +Interceptor +=========== + +.. automodule:: wsgi_intercept.interceptor + :members: + +Example using `httplib2`, others are much the same: + +.. testcode:: + + import httplib2 + from wsgi_intercept.interceptor import Httplib2Interceptor + + + def app(environ, start_response): + start_response('200 OK', [('Content-Type', 'text/plain')]) + return [b'Whee'] + + + def make_app(): + return app + + + http = httplib2.Http() + with Httplib2Interceptor(make_app, host='localhost', port=80) as url: + resp, content = http.request(url) + assert content == b'Whee' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/setup.py new/wsgi_intercept-1.1.2/setup.py --- old/wsgi_intercept-0.10.0/setup.py 2014-08-06 19:55:37.000000000 +0200 +++ new/wsgi_intercept-1.1.2/setup.py 2015-12-29 12:19:21.000000000 +0100 @@ -8,11 +8,11 @@ License :: OSI Approved :: MIT License Operating System :: OS Independent Programming Language :: Python :: 2 -Programming Language :: Python :: 2.6 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.3 Programming Language :: Python :: 3.4 +Programming Language :: Python :: 3.5 Topic :: Internet :: WWW/HTTP :: WSGI Topic :: Software Development :: Testing """.strip().splitlines() @@ -30,6 +30,9 @@ 'license': 'MIT License', 'classifiers': CLASSIFIERS, 'packages': find_packages(), + 'install_requires': [ + 'six', + ], 'extras_require': { 'testing': [ 'pytest>=2.4', Files old/wsgi_intercept-0.10.0/test/__init__.pyc and new/wsgi_intercept-1.1.2/test/__init__.pyc differ Files old/wsgi_intercept-0.10.0/test/__pycache__/test_http_client.cpython-27-PYTEST.pyc and new/wsgi_intercept-1.1.2/test/__pycache__/test_http_client.cpython-27-PYTEST.pyc differ Files old/wsgi_intercept-0.10.0/test/__pycache__/test_httplib2.cpython-27-PYTEST.pyc and new/wsgi_intercept-1.1.2/test/__pycache__/test_httplib2.cpython-27-PYTEST.pyc differ Files old/wsgi_intercept-0.10.0/test/__pycache__/test_requests.cpython-27-PYTEST.pyc and new/wsgi_intercept-1.1.2/test/__pycache__/test_requests.cpython-27-PYTEST.pyc differ Files old/wsgi_intercept-0.10.0/test/__pycache__/test_urllib.cpython-27-PYTEST.pyc and new/wsgi_intercept-1.1.2/test/__pycache__/test_urllib.cpython-27-PYTEST.pyc differ Files old/wsgi_intercept-0.10.0/test/__pycache__/test_wsgi_compliance.cpython-27-PYTEST.pyc and new/wsgi_intercept-1.1.2/test/__pycache__/test_wsgi_compliance.cpython-27-PYTEST.pyc differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/test/install.py new/wsgi_intercept-1.1.2/test/install.py --- old/wsgi_intercept-0.10.0/test/install.py 2014-06-24 16:08:28.000000000 +0200 +++ new/wsgi_intercept-1.1.2/test/install.py 2016-01-22 15:33:47.000000000 +0100 @@ -1,9 +1,10 @@ +import os import wsgi_intercept class BaseInstalledApp(object): def __init__(self, app, host, port=80, script_name='', - install=None, uninstall=None): + install=None, uninstall=None, proxy=None): self.app = app self.host = host self.port = port @@ -12,6 +13,7 @@ self._uninstall = uninstall or (lambda: None) self._hits = 0 self._internals = {} + self._proxy = proxy def __call__(self, environ, start_response): self._hits += 1 @@ -32,10 +34,14 @@ wsgi_intercept.remove_wsgi_intercept(self.host, self.port) def install(self): + if self._proxy: + os.environ['http_proxy'] = self._proxy self._install() self.install_wsgi_intercept() def uninstall(self): + if self._proxy: + del os.environ['http_proxy'] self.uninstall_wsgi_intercept() self._uninstall() @@ -56,9 +62,9 @@ uninstall = uninstall or getattr(module, 'uninstall', None) class InstalledApp(BaseInstalledApp): - def __init__(self, app, host, port=80, script_name=''): + def __init__(self, app, host, port=80, script_name='', proxy=None): BaseInstalledApp.__init__( self, app=app, host=host, port=port, script_name=script_name, - install=install, uninstall=uninstall) + install=install, uninstall=uninstall, proxy=proxy) return InstalledApp Files old/wsgi_intercept-0.10.0/test/install.pyc and new/wsgi_intercept-1.1.2/test/install.pyc differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/test/test_http_client.py new/wsgi_intercept-1.1.2/test/test_http_client.py --- old/wsgi_intercept-0.10.0/test/test_http_client.py 2014-12-15 21:02:09.000000000 +0100 +++ new/wsgi_intercept-1.1.2/test/test_http_client.py 2016-01-22 15:37:28.000000000 +0100 @@ -42,6 +42,18 @@ assert app.success() +def test_proxy_handling(): + """Proxy variable no impact.""" + with InstalledApp(wsgi_app.simple_app, host=HOST, port=80, + proxy='some.host:1234') as app: + http_client = http_lib.HTTPConnection(HOST) + http_client.request('GET', '/') + content = http_client.getresponse().read() + http_client.close() + assert content == b'WSGI intercept successful!\n' + assert app.success() + + def test_app_error(): with InstalledApp(wsgi_app.raises_app, host=HOST, port=80): http_client = http_lib.HTTPConnection(HOST) @@ -52,7 +64,7 @@ def test_http_not_intercepted(): - with InstalledApp(wsgi_app.raises_app, host=HOST, port=80) as app: + with InstalledApp(wsgi_app.raises_app, host=HOST, port=80): http_client = http_lib.HTTPConnection('google.com') http_client.request('GET', '/') response = http_client.getresponse() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/test/test_httplib2.py new/wsgi_intercept-1.1.2/test/test_httplib2.py --- old/wsgi_intercept-0.10.0/test/test_httplib2.py 2014-12-15 21:02:09.000000000 +0100 +++ new/wsgi_intercept-1.1.2/test/test_httplib2.py 2016-01-22 15:35:50.000000000 +0100 @@ -44,20 +44,34 @@ with InstalledApp(wsgi_app.simple_app, host=HOST, port=80): py.test.raises( gaierror, - 'httplib2_intercept.HTTP_WSGIInterceptorWithTimeout("_nonexistant_domain_").connect()') + 'httplib2_intercept.HTTP_WSGIInterceptorWithTimeout(' + '"_nonexistant_domain_").connect()') + + +def test_proxy_handling(): + """Proxy has no impact.""" + with InstalledApp(wsgi_app.simple_app, host=HOST, port=80, + proxy='some_proxy.com:1234') as app: + http = httplib2.Http() + resp, content = http.request( + 'http://some_hopefully_nonexistant_domain:80/') + assert content == b'WSGI intercept successful!\n' + assert app.success() def test_https(): with InstalledApp(wsgi_app.simple_app, host=HOST, port=443) as app: http = httplib2.Http() - resp, content = http.request('https://some_hopefully_nonexistant_domain:443/') + resp, content = http.request( + 'https://some_hopefully_nonexistant_domain:443/') assert app.success() def test_https_default_port(): with InstalledApp(wsgi_app.simple_app, host=HOST, port=443) as app: http = httplib2.Http() - resp, content = http.request('https://some_hopefully_nonexistant_domain/') + resp, content = http.request( + 'https://some_hopefully_nonexistant_domain/') assert app.success() environ = app.get_internals() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/test/test_interceptor.py new/wsgi_intercept-1.1.2/test/test_interceptor.py --- old/wsgi_intercept-0.10.0/test/test_interceptor.py 1970-01-01 01:00:00.000000000 +0100 +++ new/wsgi_intercept-1.1.2/test/test_interceptor.py 2015-12-29 12:19:21.000000000 +0100 @@ -0,0 +1,213 @@ +"""Tests of using the context manager style. + +The context manager is based on the InterceptFixture used in gabbi. +""" + + +import socket +from uuid import uuid4 + +import py.test +import requests +from httplib2 import Http, ServerNotFoundError +from six.moves import http_client +from six.moves.urllib.request import urlopen +from six.moves.urllib.error import URLError + +from wsgi_intercept.interceptor import ( + Interceptor, HttpClientInterceptor, Httplib2Interceptor, + RequestsInterceptor, UrllibInterceptor) +from .wsgi_app import simple_app + + +def app(): + return simple_app + + +# Base + +def test_interceptor_instance(): + hostname = str(uuid4()) + port = 9999 + interceptor = Httplib2Interceptor(app=app, host=hostname, port=port, + prefix='/foobar') + assert isinstance(interceptor, Interceptor) + assert interceptor.app == app + assert interceptor.host == hostname + assert interceptor.port == port + assert interceptor.script_name == '/foobar' + assert interceptor.url == 'http://%s:%s/foobar' % (hostname, port) + + +# http_lib + +def test_httpclient_interceptor_host(): + hostname = str(uuid4()) + port = 9999 + with HttpClientInterceptor(app=app, host=hostname, port=port): + client = http_client.HTTPConnection(hostname, port) + client.request('GET', '/') + response = client.getresponse() + content = response.read().decode('utf-8') + assert response.status == 200 + assert 'WSGI intercept successful!' in content + + +def test_httpclient_interceptor_url(): + hostname = str(uuid4()) + port = 9999 + url = 'http://%s:%s/' % (hostname, port) + with HttpClientInterceptor(app=app, url=url): + client = http_client.HTTPConnection(hostname, port) + client.request('GET', '/') + response = client.getresponse() + content = response.read().decode('utf-8') + assert response.status == 200 + assert 'WSGI intercept successful!' in content + + +def test_httpclient_in_out(): + hostname = str(uuid4()) + port = 9999 + url = 'http://%s:%s/' % (hostname, port) + with HttpClientInterceptor(app=app, url=url): + client = http_client.HTTPConnection(hostname, port) + client.request('GET', '/') + response = client.getresponse() + content = response.read().decode('utf-8') + assert response.status == 200 + assert 'WSGI intercept successful!' in content + + # outside the context manager the intercept does not work + with py.test.raises(socket.gaierror): + client = http_client.HTTPConnection(hostname, port) + client.request('GET', '/') + + +# Httplib2 + +def test_httplib2_interceptor_host(): + hostname = str(uuid4()) + port = 9999 + http = Http() + with Httplib2Interceptor(app=app, host=hostname, port=port) as url: + response, content = http.request(url) + assert response.status == 200 + assert 'WSGI intercept successful!' in content.decode('utf-8') + + +def test_httplib2_interceptor_https_host(): + hostname = str(uuid4()) + port = 443 + http = Http() + with Httplib2Interceptor(app=app, host=hostname, port=port) as url: + assert url == 'https://%s' % hostname + response, content = http.request(url) + assert response.status == 200 + assert 'WSGI intercept successful!' in content.decode('utf-8') + + +def test_httplib2_interceptor_no_host(): + # no hostname or port, one will be generated automatically + # we never actually know what it is + http = Http() + with Httplib2Interceptor(app=app) as url: + response, content = http.request(url) + assert response.status == 200 + assert 'WSGI intercept successful!' in content.decode('utf-8') + + +def test_httplib2_interceptor_url(): + hostname = str(uuid4()) + port = 9999 + url = 'http://%s:%s/' % (hostname, port) + http = Http() + with Httplib2Interceptor(app=app, url=url) as target_url: + response, content = http.request(target_url) + assert response.status == 200 + assert 'WSGI intercept successful!' in content.decode('utf-8') + + +def test_httplib2_in_out(): + hostname = str(uuid4()) + port = 9999 + url = 'http://%s:%s/' % (hostname, port) + http = Http() + with Httplib2Interceptor(app=app, url=url) as target_url: + response, content = http.request(target_url) + assert response.status == 200 + assert 'WSGI intercept successful!' in content.decode('utf-8') + + # outside the context manager the intercept does not work + with py.test.raises(ServerNotFoundError): + http.request(url) + + +# Requests + +def test_requests_interceptor_host(): + hostname = str(uuid4()) + port = 9999 + with RequestsInterceptor(app=app, host=hostname, port=port) as url: + response = requests.get(url) + assert response.status_code == 200 + assert 'WSGI intercept successful!' in response.text + + +def test_requests_interceptor_url(): + hostname = str(uuid4()) + port = 9999 + url = 'http://%s:%s/' % (hostname, port) + with RequestsInterceptor(app=app, url=url) as target_url: + response = requests.get(target_url) + assert response.status_code == 200 + assert 'WSGI intercept successful!' in response.text + + +def test_requests_in_out(): + hostname = str(uuid4()) + port = 9999 + url = 'http://%s:%s/' % (hostname, port) + with RequestsInterceptor(app=app, url=url) as target_url: + response = requests.get(target_url) + assert response.status_code == 200 + assert 'WSGI intercept successful!' in response.text + + # outside the context manager the intercept does not work + with py.test.raises(requests.ConnectionError): + requests.get(url) + + +# urllib + +def test_urllib_interceptor_host(): + hostname = str(uuid4()) + port = 9999 + with UrllibInterceptor(app=app, host=hostname, port=port) as url: + response = urlopen(url) + assert response.code == 200 + assert 'WSGI intercept successful!' in response.read().decode('utf-8') + + +def test_urllib_interceptor_url(): + hostname = str(uuid4()) + port = 9999 + url = 'http://%s:%s/' % (hostname, port) + with UrllibInterceptor(app=app, url=url) as target_url: + response = urlopen(target_url) + assert response.code == 200 + assert 'WSGI intercept successful!' in response.read().decode('utf-8') + + +def test_urllib_in_out(): + hostname = str(uuid4()) + port = 9999 + url = 'http://%s:%s/' % (hostname, port) + with UrllibInterceptor(app=app, url=url) as target_url: + response = urlopen(target_url) + assert response.code == 200 + assert 'WSGI intercept successful!' in response.read().decode('utf-8') + + # outside the context manager the intercept does not work + with py.test.raises(URLError): + urlopen(url) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/test/test_module_interceptor.py new/wsgi_intercept-1.1.2/test/test_module_interceptor.py --- old/wsgi_intercept-0.10.0/test/test_module_interceptor.py 1970-01-01 01:00:00.000000000 +0100 +++ new/wsgi_intercept-1.1.2/test/test_module_interceptor.py 2016-01-22 15:37:58.000000000 +0100 @@ -0,0 +1,36 @@ +"""Test intercepting a full module with interceptor.""" + +from uuid import uuid4 + +from httplib2 import Http + +from wsgi_intercept.interceptor import Httplib2Interceptor +from .wsgi_app import simple_app + + +def app(): + return simple_app + + +def setup_module(module): + module.host = str(uuid4()) + module.intercept = Httplib2Interceptor(app, host=module.host) + module.intercept.install_intercept() + + +def teardown_module(module): + module.intercept.uninstall_intercept() + + +def test_simple_request(): + http = Http() + response, content = http.request('http://%s/' % host) + assert response.status == 200 + assert 'WSGI intercept successful!' in content.decode('utf-8') + + +def test_another_request(): + http = Http() + response, content = http.request('http://%s/foobar' % host) + assert response.status == 200 + assert 'WSGI intercept successful!' in content.decode('utf-8') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/test/test_requests.py new/wsgi_intercept-1.1.2/test/test_requests.py --- old/wsgi_intercept-0.10.0/test/test_requests.py 2014-12-15 21:02:09.000000000 +0100 +++ new/wsgi_intercept-1.1.2/test/test_requests.py 2016-01-22 15:33:47.000000000 +0100 @@ -1,3 +1,4 @@ +import os import py.test from wsgi_intercept import requests_intercept, WSGIAppError from test import wsgi_app @@ -40,6 +41,18 @@ 'requests.get("http://_nonexistant_domain_")') +def test_proxy_handling(): + with py.test.raises(RuntimeError) as exc: + with InstalledApp(wsgi_app.simple_app, host=HOST, port=80, + proxy='some_proxy.com:1234'): + requests.get('http://some_hopefully_nonexistant_domain:80/') + assert 'http_proxy or https_proxy set in environment' in str(exc.value) + # We need to do this by hand because the exception was raised + # during the entry of the context manager, so the exit handler + # wasn't reached. + del os.environ['http_proxy'] + + def test_https(): with InstalledApp(wsgi_app.simple_app, host=HOST, port=443) as app: resp = requests.get('https://some_hopefully_nonexistant_domain:443/') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/test/test_urllib.py new/wsgi_intercept-1.1.2/test/test_urllib.py --- old/wsgi_intercept-0.10.0/test/test_urllib.py 2014-12-15 21:02:09.000000000 +0100 +++ new/wsgi_intercept-1.1.2/test/test_urllib.py 2016-01-22 15:38:35.000000000 +0100 @@ -1,3 +1,4 @@ +import os import py.test from wsgi_intercept import urllib_intercept, WSGIAppError from test import wsgi_app @@ -32,6 +33,19 @@ assert environ['wsgi.url_scheme'] == 'http' +def test_proxy_handling(): + """Like requests, urllib gets confused about proxy early on.""" + with py.test.raises(RuntimeError) as exc: + with InstalledApp(wsgi_app.simple_app, host=HOST, port=80, + proxy='some.host:1234'): + url_lib.urlopen('http://some_hopefully_nonexistant_domain:80/') + assert 'http_proxy or https_proxy set in environment' in str(exc.value) + # We need to do this by hand because the exception was raised + # during the entry of the context manager, so the exit handler + # wasn't reached. + del os.environ['http_proxy'] + + def test_https(): with InstalledApp(wsgi_app.simple_app, host=HOST, port=443) as app: url_lib.urlopen('https://some_hopefully_nonexistant_domain:443/') @@ -53,12 +67,12 @@ def test_http_not_intercepted(): - with InstalledApp(wsgi_app.simple_app, host=HOST, port=80) as app: + with InstalledApp(wsgi_app.simple_app, host=HOST, port=80): response = url_lib.urlopen('http://google.com/') assert 200 <= int(response.code) < 400 def test_https_not_intercepted(): - with InstalledApp(wsgi_app.simple_app, host=HOST, port=443) as app: + with InstalledApp(wsgi_app.simple_app, host=HOST, port=443): response = url_lib.urlopen('https://google.com/') assert 200 <= int(response.code) < 400 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/test/test_wsgi_compliance.py new/wsgi_intercept-1.1.2/test/test_wsgi_compliance.py --- old/wsgi_intercept-0.10.0/test/test_wsgi_compliance.py 2015-05-05 17:49:59.000000000 +0200 +++ new/wsgi_intercept-1.1.2/test/test_wsgi_compliance.py 2016-01-22 15:36:42.000000000 +0100 @@ -41,7 +41,10 @@ def test_more_interesting(): - expected_uri = '/%E4%B8%96%E4%B8%8A%E5%8E%9F%E4%BE%86%E9%82%84%E6%9C%89%E3%80%8C%E7%BE%9A%E7%89%9B%E3%80%8D%E9%80%99%E7%A8%AE%E5%8B%95%E7%89%A9%EF%BC%81%2Fbarney?bar=baz%20zoom' + expected_uri = ('/%E4%B8%96%E4%B8%8A%E5%8E%9F%E4%BE%86%E9%82%84%E6' + '%9C%89%E3%80%8C%E7%BE%9A%E7%89%9B%E3%80%8D%E9%80%99' + '%E7%A8%AE%E5%8B%95%E7%89%A9%EF%BC%81%2Fbarney' + '?bar=baz%20zoom') with InstalledApp(wsgi_app.more_interesting_app, host=HOST) as app: http = httplib2.Http() resp, content = http.request( @@ -58,9 +61,11 @@ # Do the rather painful wsgi encoding dance. if sys.version_info[0] > 2: - assert internal_env['PATH_INFO'].encode('latin-1').decode('UTF-8') == expected_path_info + assert internal_env['PATH_INFO'].encode('latin-1').decode( + 'UTF-8') == expected_path_info else: - assert internal_env['PATH_INFO'].decode('UTF-8') == expected_path_info.decode('UTF-8') + assert internal_env['PATH_INFO'].decode( + 'UTF-8') == expected_path_info.decode('UTF-8') def test_script_name(): @@ -75,9 +80,6 @@ assert internal_env['PATH_INFO'] == '/boom/baz' -@py.test.mark.xfail( - sys.version_info[0] == 2 and sys.version_info[1] <= 6, - reason='works okay on 2.7 and beyond. why?') def test_encoding_errors(): with InstalledApp(wsgi_app.more_interesting_app, host=HOST): http = httplib2.Http() @@ -85,3 +87,12 @@ response, content = http.request( 'http://some_hopefully_nonexistant_domain/boom/baz', headers={'Accept': u'application/\u2603'}) + + +def test_post_status_headers(): + with InstalledApp(wsgi_app.post_status_headers_app, host=HOST) as app: + http = httplib2.Http() + resp, content = http.request( + 'http://some_hopefully_nonexistant_domain/', 'GET') + assert app.success() + assert resp.get('content-type') == 'text/plain' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/test/wsgi_app.py new/wsgi_intercept-1.1.2/test/wsgi_app.py --- old/wsgi_intercept-0.10.0/test/wsgi_app.py 2014-06-24 16:08:28.000000000 +0200 +++ new/wsgi_intercept-1.1.2/test/wsgi_app.py 2015-12-28 13:23:56.000000000 +0100 @@ -23,5 +23,12 @@ return [pformat(environ).encode('utf-8')] +def post_status_headers_app(environ, start_response): + headers = [] + start_response('200 OK', headers) + headers.append(('Content-type', 'text/plain')) + return [b'WSGI intercept successful!\n'] + + def raises_app(environ, start_response): raise TypeError("bah") Files old/wsgi_intercept-0.10.0/test/wsgi_app.pyc and new/wsgi_intercept-1.1.2/test/wsgi_app.pyc differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/wsgi_intercept/__init__.py new/wsgi_intercept-1.1.2/wsgi_intercept/__init__.py --- old/wsgi_intercept-0.10.0/wsgi_intercept/__init__.py 2015-05-05 17:54:40.000000000 +0200 +++ new/wsgi_intercept-1.1.2/wsgi_intercept/__init__.py 2016-01-27 10:10:04.000000000 +0100 @@ -1,5 +1,5 @@ -"""installs a WSGI application in place of a real URI for testing. +"""Installs a WSGI application in place of a real host for testing. Introduction ============ @@ -20,13 +20,41 @@ emulating a socket. If no intercept is registered for the host and port requested, those requests are passed on to the standard handler. -The functions ``add_wsgi_intercept(host, port, app_create_fn, -script_name='')`` and ``remove_wsgi_intercept(host,port)`` specify -which URLs should be redirect into what applications. Note especially -that ``app_create_fn`` is a *function object* returning a WSGI +The easiest way to use an intercept is to import an appropriate subclass +of ``~wsgi_intercept.interceptor.Interceptor`` and use that as a +context manager over web requests that use the library associated with +the subclass. For example:: + + import httplib2 + from wsgi_intercept.intercept import Httplib2Interceptor + from mywsgiapp import app + + def load_app(): + return app + + http = httplib2.Http() + with Httplib2Interceptor(load_app, host='example.com', port=80) as url: + response, content = http.request('%s%s' % (url, '/path')) + assert response.status == 200 + +The interceptor class may aslo be used directly to install intercepts. +See the module documentation for more information. + +Older versions required that the functions ``add_wsgi_intercept(host, +port, app_create_fn, script_name='')`` and ``remove_wsgi_intercept(host,port)`` +be used to specify which URLs should be redirected into what applications. +These methods are still available, but the ``Interceptor`` classes are likely +easier to use for most use cases. + +Note especially that ``app_create_fn`` is a *function object* returning a WSGI application; ``script_name`` becomes ``SCRIPT_NAME`` in the WSGI app's environment, if set. +Note also that if ``http_proxy`` or ``https_proxy`` is set in the environment +this can cause difficulties with some of the intercepted libraries. If +requests or urllib is being used, these will raise an exception if one of +those variables is set. + Install ======= @@ -53,7 +81,7 @@ `the tests`_. More comprehensive documentation available upon request. -.. _the tests: https://github.com/cdent/python3-wsgi-intercept/tree/master/test +.. _the tests: https://github.com/cdent/wsgi-intercept/tree/master/test History @@ -68,7 +96,7 @@ The Python 2 version of wsgi-intercept was the result. Kumar McMillan later took over maintenance. -The current version works with Python 2.6, 2.7, 3.3 and 3.4 and was assembled +The current version works with Python 2.7, 3.3, 3.4 and 3.5 and was assembled by `Chris Dent`_. Testing and documentation improvements from `Sasha Hart`_. .. _twill: http://www.idyll.org/~t/www-tools/twill.html @@ -86,13 +114,13 @@ Additional documentation is available on `Read The Docs`_. -.. _GitHub: http://github.com/cdent/python3-wsgi-intercept +.. _GitHub: http://github.com/cdent/wsgi-intercept .. _Read The Docs: http://wsgi-intercept.readthedocs.org/en/latest/ """ from __future__ import print_function -__version__ = '0.10.0' +__version__ = '1.1.2' import sys @@ -368,32 +396,19 @@ 3. build an environment dict out of the traffic in inp; 4. run the WSGI app & grab the result object; 5. concatenate & return the result(s) read from the result object. - - @CTB: 'start_response' should return a function that writes - directly to self.result, too. """ # dynamically construct the start_response function for no good reason. + self.headers = [] + def start_response(status, headers, exc_info=None): # construct the HTTP request. self.output.write(b"HTTP/1.0 " + status.encode('utf-8') + b"\n") - - for k, v in headers: - try: - k = k.encode('utf-8') - except AttributeError: - pass - try: - v = v.encode('utf-8') - except AttributeError: - pass - self.output.write(k + b': ' + v + b"\n") - self.output.write(b'\n') - - def write_fn(s): - self.write_results.append(s) - return write_fn + # Keep the reference of the headers list to write them only + # when the whole application have been processed + self.headers = headers + return self.write_results.append # construct the wsgi.input file from everything that's been # written to this "socket". @@ -411,6 +426,20 @@ raise WSGIAppError(error, sys.exc_info()) self.result = iter(app_result) + # send the headers + + for k, v in self.headers: + try: + k = k.encode('utf-8') + except AttributeError: + pass + try: + v = v.encode('utf-8') + except AttributeError: + pass + self.output.write(k + b': ' + v + b"\n") + self.output.write(b'\n') + ### # read all of the results. the trick here is to get the *first* @@ -575,8 +604,9 @@ self._context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) self._context.options |= ssl.OP_NO_SSLv2 if not hasattr(self, 'check_hostname'): - self._check_hostname = (self._context.verify_mode - != ssl.CERT_NONE) + self._check_hostname = ( + self._context.verify_mode != ssl.CERT_NONE + ) else: self._check_hostname = self.check_hostname except (ImportError, AttributeError): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/wsgi_intercept/httplib2_intercept.py new/wsgi_intercept-1.1.2/wsgi_intercept/httplib2_intercept.py --- old/wsgi_intercept-0.10.0/wsgi_intercept/httplib2_intercept.py 2014-06-24 16:08:28.000000000 +0200 +++ new/wsgi_intercept-1.1.2/wsgi_intercept/httplib2_intercept.py 2016-01-22 15:40:23.000000000 +0100 @@ -1,4 +1,5 @@ -"""Intercept HTTP connections that use `httplib2 https://github.com/jcgregorio/httplib2`_. +"""Intercept HTTP connections that use +`httplib2 https://github.com/jcgregorio/httplib2`_. """ import sys @@ -20,12 +21,8 @@ # In Python3 strict is deprecated if sys.version_info[0] < 3: - try: - HTTPConnection.__init__(self, host, port=port, strict=strict, - timeout=timeout, source_address=source_address) - except TypeError: # Python 2.6 doesn't have source_address - HTTPConnection.__init__(self, host, port=port, strict=strict, - timeout=timeout) + HTTPConnection.__init__(self, host, port=port, strict=strict, + timeout=timeout, source_address=source_address) else: HTTPConnection.__init__(self, host, port=port, timeout=timeout, source_address=source_address) @@ -40,12 +37,8 @@ # ignore proxy_info and ca_certs # In Python3 strict is deprecated if sys.version_info[0] < 3: - try: - HTTPSConnection.__init__(self, host, port=port, strict=strict, - timeout=timeout, source_address=source_address) - except TypeError: # Python 2.6 doesn't have source_address - HTTPSConnection.__init__(self, host, port=port, strict=strict, - timeout=timeout) + HTTPSConnection.__init__(self, host, port=port, strict=strict, + timeout=timeout, source_address=source_address) else: HTTPSConnection.__init__(self, host, port=port, timeout=timeout, source_address=source_address) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/wsgi_intercept/interceptor.py new/wsgi_intercept-1.1.2/wsgi_intercept/interceptor.py --- old/wsgi_intercept-0.10.0/wsgi_intercept/interceptor.py 1970-01-01 01:00:00.000000000 +0100 +++ new/wsgi_intercept-1.1.2/wsgi_intercept/interceptor.py 2015-12-29 12:19:21.000000000 +0100 @@ -0,0 +1,115 @@ +"""Context manager based WSGI interception. +""" + +from importlib import import_module +from uuid import uuid4 + +from six.moves.urllib import parse as urlparse + +import wsgi_intercept + + +class Interceptor(object): + """A convenience class over the guts of wsgi_intercept. + + An Interceptor subclass provides a clean entry point to the wsgi_intercept + functionality in two ways: by encapsulating the interception addition and + removal in methods and by providing a context manager that automates the + process of addition and removal. + + Each Interceptor subclass is associated with a specific http library. + + Each class may be passed a url or a host and a port. If no args are passed + a hostname will be automatically generated and the resulting url will be + returned by the context manager. + """ + + def __init__(self, app, host=None, port=80, prefix=None, url=None): + assert app + if (not host and not url): + host = str(uuid4()) + + self.app = app + + if url: + self._init_from_url(url) + self.url = url + else: + self.host = host + self.port = int(port) + self.script_name = prefix or '' + self.url = self._url_from_primitives() + + self._module = import_module('.%s' % self.MODULE_NAME, + package='wsgi_intercept') + + def __enter__(self): + self.install_intercept() + return self.url + + def __exit__(self, exc_type, value, traceback): + self.uninstall_intercept() + + def _url_from_primitives(self): + if self.port == 443: + scheme = 'https' + else: + scheme = 'http' + + if self.port and self.port not in [443, 80]: + port = ':%s' % self.port + else: + port = '' + netloc = self.host + port + + return urlparse.urlunsplit((scheme, netloc, self.script_name, + None, None)) + + def _init_from_url(self, url): + parsed_url = urlparse.urlsplit(url) + host, port = parsed_url.netloc.split(':') + if not port: + if parsed_url.scheme == 'https': + port = 443 + else: + port = 80 + path = parsed_url.path + if path == '/' or not path: + self.script_name = '' + else: + self.script_name = path + self.host = host + self.port = int(port) + + def install_intercept(self): + self._module.install() + wsgi_intercept.add_wsgi_intercept(self.host, self.port, self.app, + script_name=self.script_name) + + def uninstall_intercept(self): + wsgi_intercept.remove_wsgi_intercept(self.host, self.port) + self._module.uninstall() + + +class HttpClientInterceptor(Interceptor): + """Interceptor for httplib and http.client.""" + + MODULE_NAME = 'http_client_intercept' + + +class Httplib2Interceptor(Interceptor): + """Interceptor for httplib2.""" + + MODULE_NAME = 'httplib2_intercept' + + +class RequestsInterceptor(Interceptor): + """Interceptor for requests.""" + + MODULE_NAME = 'requests_intercept' + + +class UrllibInterceptor(Interceptor): + """Interceptor for urllib2 and urllib.request.""" + + MODULE_NAME = 'urllib_intercept' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/wsgi_intercept/requests_intercept.py new/wsgi_intercept-1.1.2/wsgi_intercept/requests_intercept.py --- old/wsgi_intercept-0.10.0/wsgi_intercept/requests_intercept.py 2014-12-15 21:02:09.000000000 +0100 +++ new/wsgi_intercept-1.1.2/wsgi_intercept/requests_intercept.py 2016-01-22 15:40:33.000000000 +0100 @@ -1,6 +1,8 @@ -"""Intercept HTTP connections that use `requests http://docs.python-requests.org/en/latest/`_. +"""Intercept HTTP connections that use +`requests http://docs.python-requests.org/en/latest/`_. """ +import os import sys from . import WSGI_HTTPConnection, WSGI_HTTPSConnection, wsgi_fake_socket @@ -32,6 +34,9 @@ def install(): + if 'http_proxy' in os.environ or 'https_proxy' in os.environ: + raise RuntimeError( + 'http_proxy or https_proxy set in environment, please unset') HTTPConnectionPool.ConnectionCls = HTTP_WSGIInterceptor HTTPSConnectionPool.ConnectionCls = HTTPS_WSGIInterceptor diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/wsgi_intercept/urllib_intercept.py new/wsgi_intercept-1.1.2/wsgi_intercept/urllib_intercept.py --- old/wsgi_intercept-0.10.0/wsgi_intercept/urllib_intercept.py 2014-06-24 16:08:28.000000000 +0200 +++ new/wsgi_intercept-1.1.2/wsgi_intercept/urllib_intercept.py 2016-01-22 15:40:57.000000000 +0100 @@ -1,5 +1,9 @@ -"""Intercept HTTP connections that use urllib.request (Py3) aka urllib2 (Python 2). +"""Intercept HTTP connections that use urllib.request (Python 3) +aka urllib2 (Python 2). """ + +import os + try: import urllib.request as url_lib except ImportError: @@ -27,6 +31,9 @@ def install_opener(): + if 'http_proxy' in os.environ or 'https_proxy' in os.environ: + raise RuntimeError( + 'http_proxy or https_proxy set in environment, please unset') handlers = [WSGI_HTTPHandler()] if WSGI_HTTPSHandler is not None: handlers.append(WSGI_HTTPSHandler()) @@ -38,3 +45,7 @@ def uninstall_opener(): url_lib.install_opener(None) + + +install = install_opener +uninstall = uninstall_opener diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/wsgi_intercept.egg-info/PKG-INFO new/wsgi_intercept-1.1.2/wsgi_intercept.egg-info/PKG-INFO --- old/wsgi_intercept-0.10.0/wsgi_intercept.egg-info/PKG-INFO 2015-05-05 17:58:27.000000000 +0200 +++ new/wsgi_intercept-1.1.2/wsgi_intercept.egg-info/PKG-INFO 2016-01-27 10:16:53.000000000 +0100 @@ -1,12 +1,12 @@ Metadata-Version: 1.1 Name: wsgi-intercept -Version: 0.10.0 +Version: 1.1.2 Summary: wsgi_intercept installs a WSGI application in place of a real URI for testing. Home-page: http://pypi.python.org/pypi/wsgi_intercept Author: Titus Brown, Kumar McMillan, Chris Dent, Sasha Hart Author-email: cdent@peermore.com License: MIT License -Description: installs a WSGI application in place of a real URI for testing. +Description: Installs a WSGI application in place of a real host for testing. Introduction ============ @@ -27,13 +27,41 @@ emulating a socket. If no intercept is registered for the host and port requested, those requests are passed on to the standard handler. - The functions ``add_wsgi_intercept(host, port, app_create_fn, - script_name='')`` and ``remove_wsgi_intercept(host,port)`` specify - which URLs should be redirect into what applications. Note especially - that ``app_create_fn`` is a *function object* returning a WSGI + The easiest way to use an intercept is to import an appropriate subclass + of ``~wsgi_intercept.interceptor.Interceptor`` and use that as a + context manager over web requests that use the library associated with + the subclass. For example:: + + import httplib2 + from wsgi_intercept.intercept import Httplib2Interceptor + from mywsgiapp import app + + def load_app(): + return app + + http = httplib2.Http() + with Httplib2Interceptor(load_app, host='example.com', port=80) as url: + response, content = http.request('%s%s' % (url, '/path')) + assert response.status == 200 + + The interceptor class may aslo be used directly to install intercepts. + See the module documentation for more information. + + Older versions required that the functions ``add_wsgi_intercept(host, + port, app_create_fn, script_name='')`` and ``remove_wsgi_intercept(host,port)`` + be used to specify which URLs should be redirected into what applications. + These methods are still available, but the ``Interceptor`` classes are likely + easier to use for most use cases. + + Note especially that ``app_create_fn`` is a *function object* returning a WSGI application; ``script_name`` becomes ``SCRIPT_NAME`` in the WSGI app's environment, if set. + Note also that if ``http_proxy`` or ``https_proxy`` is set in the environment + this can cause difficulties with some of the intercepted libraries. If + requests or urllib is being used, these will raise an exception if one of + those variables is set. + Install ======= @@ -60,7 +88,7 @@ `the tests`_. More comprehensive documentation available upon request. - .. _the tests: https://github.com/cdent/python3-wsgi-intercept/tree/master/test + .. _the tests: https://github.com/cdent/wsgi-intercept/tree/master/test History @@ -75,7 +103,7 @@ The Python 2 version of wsgi-intercept was the result. Kumar McMillan later took over maintenance. - The current version works with Python 2.6, 2.7, 3.3 and 3.4 and was assembled + The current version works with Python 2.7, 3.3, 3.4 and 3.5 and was assembled by `Chris Dent`_. Testing and documentation improvements from `Sasha Hart`_. .. _twill: http://www.idyll.org/~t/www-tools/twill.html @@ -93,7 +121,7 @@ Additional documentation is available on `Read The Docs`_. - .. _GitHub: http://github.com/cdent/python3-wsgi-intercept + .. _GitHub: http://github.com/cdent/wsgi-intercept .. _Read The Docs: http://wsgi-intercept.readthedocs.org/en/latest/ @@ -103,10 +131,10 @@ Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 Classifier: Topic :: Internet :: WWW/HTTP :: WSGI Classifier: Topic :: Software Development :: Testing diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/wsgi_intercept.egg-info/SOURCES.txt new/wsgi_intercept-1.1.2/wsgi_intercept.egg-info/SOURCES.txt --- old/wsgi_intercept-0.10.0/wsgi_intercept.egg-info/SOURCES.txt 2015-05-05 17:58:27.000000000 +0200 +++ new/wsgi_intercept-1.1.2/wsgi_intercept.egg-info/SOURCES.txt 2016-01-27 10:16:53.000000000 +0100 @@ -1,4 +1,4 @@ -COPYRIGHT +LICENSE MANIFEST.in Makefile README @@ -10,34 +10,29 @@ docs/http_client.rst docs/httplib2.rst docs/index.rst +docs/interceptor.rst docs/requests.rst docs/urllib.rst docs/_static/.gitignore docs/_templates/.gitignore test/__init__.py -test/__init__.pyc test/install.py -test/install.pyc test/test_http_client.py test/test_httplib2.py +test/test_interceptor.py +test/test_module_interceptor.py test/test_requests.py test/test_urllib.py test/test_wsgi_compliance.py test/wsgi_app.py -test/wsgi_app.pyc -test/__pycache__/test_http_client.cpython-27-PYTEST.pyc -test/__pycache__/test_httplib2.cpython-27-PYTEST.pyc -test/__pycache__/test_requests.cpython-27-PYTEST.pyc -test/__pycache__/test_urllib.cpython-27-PYTEST.pyc -test/__pycache__/test_wsgi_compliance.cpython-27-PYTEST.pyc wsgi_intercept/__init__.py wsgi_intercept/http_client_intercept.py wsgi_intercept/httplib2_intercept.py +wsgi_intercept/interceptor.py wsgi_intercept/requests_intercept.py wsgi_intercept/urllib_intercept.py wsgi_intercept.egg-info/PKG-INFO wsgi_intercept.egg-info/SOURCES.txt wsgi_intercept.egg-info/dependency_links.txt -wsgi_intercept.egg-info/pbr.json wsgi_intercept.egg-info/requires.txt wsgi_intercept.egg-info/top_level.txt \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/wsgi_intercept.egg-info/pbr.json new/wsgi_intercept-1.1.2/wsgi_intercept.egg-info/pbr.json --- old/wsgi_intercept-0.10.0/wsgi_intercept.egg-info/pbr.json 2015-05-05 17:58:27.000000000 +0200 +++ new/wsgi_intercept-1.1.2/wsgi_intercept.egg-info/pbr.json 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -{"is_release": true, "git_version": "86c5d1a"} \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wsgi_intercept-0.10.0/wsgi_intercept.egg-info/requires.txt new/wsgi_intercept-1.1.2/wsgi_intercept.egg-info/requires.txt --- old/wsgi_intercept-0.10.0/wsgi_intercept.egg-info/requires.txt 2015-05-05 17:58:27.000000000 +0200 +++ new/wsgi_intercept-1.1.2/wsgi_intercept.egg-info/requires.txt 2016-01-27 10:16:53.000000000 +0100 @@ -1,3 +1,4 @@ +six [testing] pytest>=2.4