Skip to content

Commit

Permalink
Partial python3 support
Browse files Browse the repository at this point in the history
  • Loading branch information
kedder committed Feb 27, 2013
1 parent 94d5c1f commit e6fd007
Show file tree
Hide file tree
Showing 13 changed files with 74 additions and 148 deletions.
14 changes: 10 additions & 4 deletions buildout.cfg
@@ -1,13 +1,19 @@
[buildout]
extends =
http://download.zope.org/zopetoolkit/index/1.1/zopeapp-versions.cfg
http://download.zope.org/zopetoolkit/index/1.1/ztk-versions.cfg
#extends =
# http://download.zope.org/zopetoolkit/index/1.1/zopeapp-versions.cfg
# http://download.zope.org/zopetoolkit/index/1.1/ztk-versions.cfg
develop = .
../zope.testbrowser.svn
../zope.app.appsetup
../zope.app.publication
../ZODB
parts = test
versions=versions

[test]
recipe = zc.recipe.testrunner
eggs = zope.app.wsgi [test]

[versions]
zope.testbrowser = 4.0.0
zope.i18n = 4.0.0a4
zope.tal=4.0.0a1
18 changes: 8 additions & 10 deletions setup.py
Expand Up @@ -43,39 +43,37 @@
package_dir = {'': 'src'},
namespace_packages=['zope', 'zope.app'],
extras_require = dict(test=[
'zope.testbrowser[wsgi] >= 4.0.0',
'zope.annotation',
'zope.authentication',
'zope.browserpage',
'zope.login',
'zope.password',
'zope.principalregistry',
'zope.securitypolicy',
'zope.principalregistry >=4.0.0a1',
'zope.securitypolicy >=4.0.0a1',
'zope.testing',
'zope.traversing',
],
testbrowser=['zope.testbrowser[wsgi] >= 4.0.0']),
install_requires=[
'setuptools',
'WebTest',
'ZConfig',
'ZODB',
'zope.app.appsetup >= 3.14',
'zope.processlifetime',
'zope.app.publication',
'zope.event',
'zope.interface',
'zope.publisher',
'zope.security',
'zope.publisher>=4.0.0a1',
'zope.security>4.0.0a2',
'zope.component',
'zope.configuration',
'zope.container',
'zope.error',
'zope.lifecycleevent',
'zope.processlifetime',
'zope.session',
'zope.site',
'zope.session >= 4.0.0a1',
'zope.site >= 4.0.0a1',
'zope.testing',
'zope.traversing',
'zope.traversing>=4.0.0a1',
],
entry_points={
'paste.app_factory': [
Expand Down
19 changes: 9 additions & 10 deletions src/zope/app/wsgi/README.txt
Expand Up @@ -26,10 +26,10 @@ which the output data can be written.
Even though this is commonly done by the server, we now have to create an
appropriate environment for the request.

>>> import cStringIO
>>> import io
>>> environ = {
... 'PATH_INFO': '/',
... 'wsgi.input': cStringIO.StringIO('')}
... 'wsgi.input': io.BytesIO(b'')}

Next we create a WSGI-compliant ``start_response()`` method that accepts the
status of the response to the HTTP request and the headers that are part of
Expand All @@ -46,7 +46,7 @@ and return ``None`` as the write method.

Now we can send the fabricated HTTP request to the application for processing:

>>> print ''.join(app(environ, start_response))
>>> print(b''.join(app(environ, start_response)).decode('utf-8'))
<html><head><title>SystemError</title></head>
<body><h2>SystemError</h2>
A server error occurred.
Expand Down Expand Up @@ -108,7 +108,7 @@ adapter that returns the principal id as value::
>>> pprint(environ)
{'PATH_INFO': '/',
'REMOTE_USER': 'zope.anybody',
'wsgi.input': <cStringIO.StringI object at ...>,
'wsgi.input': <...BytesIO object at ...>,
'wsgi.logging_info': 'zope.anybody'}

.. edge case
Expand All @@ -118,13 +118,13 @@ adapter that returns the principal id as value::
>>> environ = {
... 'PATH_INFO': '/',
... 'REMOTE_USER': 'someoneelse',
... 'wsgi.input': cStringIO.StringIO('')}
... 'wsgi.input': io.BytesIO(b'')}

>>> _ = list(app(environ, start_response))
>>> pprint(environ)
{'PATH_INFO': '/',
'REMOTE_USER': 'someoneelse',
'wsgi.input': <cStringIO.StringI object at ...>,
'wsgi.input': <...BytesIO object at ...>,
'wsgi.logging_info': 'zope.anybody'}


Expand All @@ -143,10 +143,9 @@ example:
>>> import os, tempfile
>>> temp_dir = tempfile.mkdtemp()
>>> sitezcml = os.path.join(temp_dir, 'site.zcml')
>>> open(sitezcml, 'w').write('<configure />')
>>> written = open(sitezcml, 'w').write('<configure />')

>>> from cStringIO import StringIO
>>> configFile = StringIO('''
>>> configFile = io.StringIO('''
... site-definition %s
...
... <zodb>
Expand Down Expand Up @@ -189,7 +188,7 @@ Create an WSGI application.
The product configs were parsed:

>>> import zope.app.appsetup.product as zapp
>>> print zapp.getProductConfiguration('sample')
>>> print(zapp.getProductConfiguration('sample'))
{'key1': 'val1'}

>>> import shutil
Expand Down
5 changes: 5 additions & 0 deletions src/zope/app/wsgi/__init__.py
Expand Up @@ -34,6 +34,11 @@
from zope.app.wsgi import interfaces


from zope.app.wsgi._compat import PYTHON3

if PYTHON3:
basestring = (str, bytes)

@implementer(interfaces.IWSGIApplication)
class WSGIPublisherApplication(object):
"""A WSGI application implementation for the zope publisher
Expand Down
1 change: 0 additions & 1 deletion src/zope/app/wsgi/_compat.py
Expand Up @@ -30,4 +30,3 @@
import http.client as httpclient
from io import IOBase
FileType = IOBase

12 changes: 6 additions & 6 deletions src/zope/app/wsgi/fileresult.txt
Expand Up @@ -16,17 +16,17 @@ Lets look at an example with a regular file object:
>>> component.provideAdapter(zope.app.wsgi.fileresult.TemporaryFileResult)

>>> import tempfile
>>> dir = tempfile.mkdtemp()
>>> tdir = tempfile.mkdtemp()
>>> import os
>>> f = open(os.path.join(dir, 'f'), 'w+b')
>>> f.write('One\nTwo\nThree\nHa ha! I love to count!\n')
>>> f = open(os.path.join(tdir, 'f'), 'w+b')
>>> written = f.write(b'One\nTwo\nThree\nHa ha! I love to count!\n')
>>> from zope.security.checker import ProxyFactory
>>> from zope.publisher.interfaces.http import IResult
>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()
>>> result = component.getMultiAdapter((ProxyFactory(f), request), IResult)
>>> for line in result:
... print line
... print(line)
One
Two
Three
Expand Down Expand Up @@ -87,9 +87,9 @@ You can suppress this behavior by setting the content length yourself:

>>> request = TestRequest()
>>> request.response.setHeader('content-length', '10')
>>> f.seek(7)
>>> _s = f.seek(7)
>>> result = component.getMultiAdapter((ProxyFactory(t), request), IResult)
>>> print f.tell()
>>> print(f.tell())
7

>>> request.response.getHeader('content-length')
Expand Down
2 changes: 1 addition & 1 deletion src/zope/app/wsgi/filereturns.txt
Expand Up @@ -12,7 +12,7 @@ nothing bad happens. :)
... checker.NamesChecker(['browserDefault', '__call__']),
... )

>>> from zope.app.wsgi.testlayer import Browser
>>> from zope.testbrowser.wsgi import Browser
>>> browser = Browser()
>>> browser.handleErrors = False
>>> browser.open('http://localhost/@@test-file-view.html')
Expand Down
4 changes: 4 additions & 0 deletions src/zope/app/wsgi/paste.py
Expand Up @@ -19,6 +19,10 @@
import zope.event
import zope.processlifetime

from zope.app.wsgi._compat import PYTHON3

if PYTHON3:
basestring = (str, bytes)

def asbool(obj):
if isinstance(obj, basestring):
Expand Down
6 changes: 3 additions & 3 deletions src/zope/app/wsgi/paste.txt
Expand Up @@ -34,9 +34,9 @@ Let's create testing site.zcml and zope.conf file.
>>> import os, tempfile
>>> temp_dir = tempfile.mkdtemp()
>>> sitezcml = os.path.join(temp_dir, 'site.zcml')
>>> open(sitezcml, 'w').write('<configure />')
>>> written = open(sitezcml, 'w').write('<configure />')
>>> zopeconf = os.path.join(temp_dir, 'zope.conf')
>>> open(zopeconf, 'w').write('''
>>> written = open(zopeconf, 'w').write('''
... site-definition %s
...
... <zodb>
Expand Down Expand Up @@ -74,7 +74,7 @@ We can also specify handle_errors as false using boolean or strings:
False

>>> for v in ('0', 'false', 'no', 'f', 'n'):
... print ZopeApplication({}, zopeconf, handle_errors=v).handleErrors
... print(ZopeApplication({}, zopeconf, handle_errors=v).handleErrors)
False
False
False
Expand Down
3 changes: 1 addition & 2 deletions src/zope/app/wsgi/testing.py
Expand Up @@ -23,9 +23,8 @@
from zope.app.wsgi.testlayer import BrowserLayer


@interface.implementer(zope.publisher.interfaces.browser.IBrowserPublisher)
class FileView:

interface.implements(zope.publisher.interfaces.browser.IBrowserPublisher)
component.adapts(interface.Interface,
zope.publisher.interfaces.browser.IBrowserRequest)

Expand Down
97 changes: 1 addition & 96 deletions src/zope/app/wsgi/testlayer.py
Expand Up @@ -12,13 +12,11 @@
#
##############################################################################
from io import BytesIO
import base64
import re

import transaction
from zope.app.appsetup.testlayer import ZODBLayer
from zope.app.wsgi import WSGIPublisherApplication
#import zope.testbrowser.wsgi
from zope.testbrowser.wsgi import Layer as WSGILayer, AuthorizationMiddleware
from webtest import TestRequest

# BBB
Expand Down Expand Up @@ -48,99 +46,6 @@ def __call__(self, environ, start_response):
self.root_factory()._p_jar.sync()


basicre = re.compile('Basic (.+)?:(.+)?$')


def auth_header(header):
"""This function takes an authorization HTTP header and encode the
couple user, password into base 64 like the HTTP protocol wants
it.
"""
match = basicre.match(header)
if match:
u, p = match.group(1, 2)
if u is None:
u = ''
if p is None:
p = ''
plain = '%s:%s' % (u, p)
auth = base64.encodestring(plain.encode('utf-8'))
return 'Basic %s' % str(auth.rstrip().decode('latin1'))
return header


def is_wanted_header(header):
"""Return True if the given HTTP header key is wanted.
"""
key, value = header
return key.lower() not in ('x-content-type-warning', 'x-powered-by')


class AuthorizationMiddleware(object):
"""This middleware makes the WSGI application compatible with the
HTTPCaller behavior defined in zope.app.testing.functional:
- It modifies the HTTP Authorization header to encode user and
password into base64 if it is Basic authentication.
"""

def __init__(self, wsgi_stack):
self.wsgi_stack = wsgi_stack

def __call__(self, environ, start_response):
# Handle authorization
auth_key = 'HTTP_AUTHORIZATION'
if auth_key in environ:
environ[auth_key] = auth_header(environ[auth_key])

# Remove unwanted headers
def application_start_response(status, headers, exc_info=None):
headers = filter(is_wanted_header, headers)
start_response(status, headers)

for entry in self.wsgi_stack(environ, application_start_response):
yield entry


_APP_UNDER_TEST = None # setup and torn down by the Layer class

# This is a copy of zope.testbrowser.wsgi.Layer, made to drop dependency on
# zope.testbrowser, that, in turn depends on non python-3 compatible mechanize.
class WSGILayer(object):
"""Test layer which sets up WSGI application for use with
WebTest/testbrowser.
"""

__bases__ = ()
__name__ = 'Layer'

@classmethod
def get_app(cls):
return _APP_UNDER_TEST

def make_wsgi_app(self):
# Override this method in subclasses of this layer in order to set up
# the WSGI application.
raise NotImplementedError

def cooperative_super(self, method_name):
# Calling `super` for multiple inheritance:
method = getattr(super(WSGILayer, self), method_name, None)
if method is not None:
method()

def setUp(self):
self.cooperative_super('setUp')
global _APP_UNDER_TEST
if _APP_UNDER_TEST is not None:
raise AssertionError("Already Setup")
_APP_UNDER_TEST = self.make_wsgi_app()

def tearDown(self):
global _APP_UNDER_TEST
_APP_UNDER_TEST = None
self.cooperative_super('tearDown')

class BrowserLayer(WSGILayer, ZODBLayer):
"""This create a test layer with a test database and register a wsgi
application to use that test database.
Expand Down
2 changes: 1 addition & 1 deletion src/zope/app/wsgi/testlayer.txt
Expand Up @@ -19,7 +19,7 @@ First, create and register a view to test:

The `silly middleware` has injected information into the page:

>>> from zope.app.wsgi.testlayer import Browser
>>> from zope.testbrowser.wsgi import Browser
>>> browser = Browser()
>>> browser.open('http://localhost/index.html')
>>> print browser.contents
Expand Down

0 comments on commit e6fd007

Please sign in to comment.