Commit efe61189 authored by Hanno Schlichting's avatar Hanno Schlichting

flake8 ZPublisher/Testing

parent 2e9d01ea
The Testing package is a set of shared routines for the Zope unit
testing framework. From Zope 2.8 these are more easily accessed
by using the ZopeTestCase package. See ZopeTestCase/doc for more
information.
To use Testing without ZopeTestCase:
1. Make a 'tests' subdirectory.
2. Copy 'framework.py' into 'tests' from any other package's 'tests'.
Once a test suite has been set up, you can add test modules:
1. Create a file with a name matching 'test*.py'.
2. Define one or more subclasses of 'unittest.TestCase'. The unittest
module is imported by the framework.
3. Define methods for the test classes. Each method's name must start
with 'test'. It should test one small case, using a Python
'assert' statement. Here's a minimal example:
class testClass1(unittest.TestCase):
def testAddition(self):
assert 1 + 1 == 2, 'Addition failed!'
4. You can add 'setUp' and 'tearDown' methods that are automatically
called at the start and end of the test suite.
5. Follow the instructions in 'framework.py' about adding lines to the
top and bottom of the file.
Now you can run the test as "python path/to/tests/testName.py", or
simply go to the 'tests' directory and type "python testName.py".
...@@ -3,10 +3,12 @@ from glob import glob ...@@ -3,10 +3,12 @@ from glob import glob
import ZODB import ZODB
from ZODB.FileStorage import FileStorage from ZODB.FileStorage import FileStorage
def makeDB(): def makeDB():
s = FileStorage('fs_tmp__%s' % os.getpid()) s = FileStorage('fs_tmp__%s' % os.getpid())
return ZODB.DB(s) return ZODB.DB(s)
def cleanDB(): def cleanDB():
for fn in glob('fs_tmp__*'): for fn in glob('fs_tmp__*'):
os.remove(fn) os.remove(fn)
...@@ -15,7 +15,9 @@ ...@@ -15,7 +15,9 @@
After Marius Gedminas' functional.py module for Zope3. After Marius Gedminas' functional.py module for Zope3.
""" """
import sys, re, base64 import base64
import re
import sys
import transaction import transaction
import sandbox import sandbox
import interfaces import interfaces
...@@ -58,8 +60,8 @@ class Functional(sandbox.Sandboxed): ...@@ -58,8 +60,8 @@ class Functional(sandbox.Sandboxed):
'''Publishes the object at 'path' returning a response object.''' '''Publishes the object at 'path' returning a response object.'''
from StringIO import StringIO from StringIO import StringIO
from ZPublisher.Request import Request from ZPublisher.HTTPRequest import HTTPRequest as Request
from ZPublisher.Response import Response from ZPublisher.HTTPResponse import HTTPResponse as Response
from ZPublisher.Publish import publish_module from ZPublisher.Publish import publish_module
# Commit the sandbox for good measure # Commit the sandbox for good measure
...@@ -82,7 +84,7 @@ class Functional(sandbox.Sandboxed): ...@@ -82,7 +84,7 @@ class Functional(sandbox.Sandboxed):
elif len(p) == 2: elif len(p) == 2:
[env['PATH_INFO'], env['QUERY_STRING']] = p [env['PATH_INFO'], env['QUERY_STRING']] = p
else: else:
raise TypeError, '' raise TypeError('')
if basic: if basic:
env['HTTP_AUTHORIZATION'] = "Basic %s" % base64.encodestring(basic) env['HTTP_AUTHORIZATION'] = "Basic %s" % base64.encodestring(basic)
...@@ -99,8 +101,7 @@ class Functional(sandbox.Sandboxed): ...@@ -99,8 +101,7 @@ class Functional(sandbox.Sandboxed):
publish_module('Zope2', publish_module('Zope2',
debug=not handle_errors, debug=not handle_errors,
request=request, request=request,
response=response, response=response)
)
return ResponseWrapper(response, outstream, path) return ResponseWrapper(response, outstream, path)
...@@ -140,4 +141,3 @@ class ResponseWrapper: ...@@ -140,4 +141,3 @@ class ResponseWrapper:
def getCookie(self, name): def getCookie(self, name):
'''Returns a response cookie.''' '''Returns a response cookie.'''
return self.cookies.get(name) return self.cookies.get(name)
...@@ -90,11 +90,14 @@ class DocResponseWrapper(ResponseWrapper): ...@@ -90,11 +90,14 @@ class DocResponseWrapper(ResponseWrapper):
return "%s\n" % (self.header_output) return "%s\n" % (self.header_output)
basicre = re.compile('Basic (.+)?:(.+)?$')
headerre = re.compile('(\S+): (.+)$') headerre = re.compile('(\S+): (.+)$')
def split_header(header): def split_header(header):
return headerre.match(header).group(1, 2) return headerre.match(header).group(1, 2)
basicre = re.compile('Basic (.+)?:(.+)?$')
def auth_header(header): def auth_header(header):
match = basicre.match(header) match = basicre.match(header)
if match: if match:
...@@ -111,6 +114,7 @@ def auth_header(header): ...@@ -111,6 +114,7 @@ def auth_header(header):
def getRootFolder(): def getRootFolder():
return AppZapper().app() return AppZapper().app()
def sync(): def sync():
getRootFolder()._p_jar.sync() getRootFolder()._p_jar.sync()
...@@ -124,7 +128,7 @@ def http(request_string, handle_errors=True): ...@@ -124,7 +128,7 @@ def http(request_string, handle_errors=True):
import urllib import urllib
import rfc822 import rfc822
from cStringIO import StringIO from cStringIO import StringIO
from ZPublisher.Response import Response from ZPublisher.HTTPResponse import HTTPResponse as Response
from ZPublisher.Publish import publish_module from ZPublisher.Publish import publish_module
# Commit work done by previous python code. # Commit work done by previous python code.
...@@ -136,7 +140,7 @@ def http(request_string, handle_errors=True): ...@@ -136,7 +140,7 @@ def http(request_string, handle_errors=True):
# Split off and parse the command line # Split off and parse the command line
l = request_string.find('\n') l = request_string.find('\n')
command_line = request_string[:l].rstrip() command_line = request_string[:l].rstrip()
request_string = request_string[l+1:] request_string = request_string[l + 1:]
method, path, protocol = command_line.split() method, path, protocol = command_line.split()
path = urllib.unquote(path) path = urllib.unquote(path)
...@@ -154,7 +158,7 @@ def http(request_string, handle_errors=True): ...@@ -154,7 +158,7 @@ def http(request_string, handle_errors=True):
elif len(p) == 2: elif len(p) == 2:
[env['PATH_INFO'], env['QUERY_STRING']] = p [env['PATH_INFO'], env['QUERY_STRING']] = p
else: else:
raise TypeError, '' raise TypeError('')
header_output = HTTPHeaderOutput( header_output = HTTPHeaderOutput(
protocol, ('x-content-type-warning', 'x-powered-by', protocol, ('x-content-type-warning', 'x-powered-by',
...@@ -173,7 +177,7 @@ def http(request_string, handle_errors=True): ...@@ -173,7 +177,7 @@ def http(request_string, handle_errors=True):
name = 'HTTP_' + name name = 'HTTP_' + name
env[name] = value.rstrip() env[name] = value.rstrip()
if env.has_key('HTTP_AUTHORIZATION'): if 'HTTP_AUTHORIZATION' in env:
env['HTTP_AUTHORIZATION'] = auth_header(env['HTTP_AUTHORIZATION']) env['HTTP_AUTHORIZATION'] = auth_header(env['HTTP_AUTHORIZATION'])
outstream = StringIO() outstream = StringIO()
...@@ -183,8 +187,8 @@ def http(request_string, handle_errors=True): ...@@ -183,8 +187,8 @@ def http(request_string, handle_errors=True):
response=response, response=response,
stdin=instream, stdin=instream,
environ=env, environ=env,
debug=not handle_errors, debug=not handle_errors)
)
header_output.setResponseStatus(response.getStatus(), response.errmsg) header_output.setResponseStatus(response.getStatus(), response.errmsg)
header_output.setResponseHeaders(response.headers) header_output.setResponseHeaders(response.headers)
header_output.headersl.extend(response._cookie_list()) header_output.headersl.extend(response._cookie_list())
...@@ -246,6 +250,7 @@ class ZopeSuiteFactory: ...@@ -246,6 +250,7 @@ class ZopeSuiteFactory:
test_instance = test_class() test_instance = test_class()
kwsetUp = self._kw.get('setUp') kwsetUp = self._kw.get('setUp')
def setUp(test): def setUp(test):
test_instance.setUp() test_instance.setUp()
test.globs['test'] = test test.globs['test'] = test
...@@ -264,6 +269,7 @@ class ZopeSuiteFactory: ...@@ -264,6 +269,7 @@ class ZopeSuiteFactory:
self._kw['setUp'] = setUp self._kw['setUp'] = setUp
kwtearDown = self._kw.get('tearDown') kwtearDown = self._kw.get('tearDown')
def tearDown(test): def tearDown(test):
if kwtearDown is not None: if kwtearDown is not None:
kwtearDown(test_instance) kwtearDown(test_instance)
...@@ -273,8 +279,8 @@ class ZopeSuiteFactory: ...@@ -273,8 +279,8 @@ class ZopeSuiteFactory:
def setup_optionflags(self): def setup_optionflags(self):
if 'optionflags' not in self._kw: if 'optionflags' not in self._kw:
self._kw['optionflags'] = (doctest.ELLIPSIS self._kw['optionflags'] = (
| doctest.NORMALIZE_WHITESPACE) doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE)
class FunctionalSuiteFactory(ZopeSuiteFactory): class FunctionalSuiteFactory(ZopeSuiteFactory):
...@@ -285,7 +291,8 @@ class FunctionalSuiteFactory(ZopeSuiteFactory): ...@@ -285,7 +291,8 @@ class FunctionalSuiteFactory(ZopeSuiteFactory):
globs['http'] = http globs['http'] = http
globs['getRootFolder'] = getRootFolder globs['getRootFolder'] = getRootFolder
globs['sync'] = sync globs['sync'] = sync
globs['user_auth'] = base64.encodestring('%s:%s' % (user_name, user_password)) globs['user_auth'] = base64.encodestring(
'%s:%s' % (user_name, user_password))
def setup_test_class(self): def setup_test_class(self):
test_class = self._kw.get('test_class', FunctionalTestCase) test_class = self._kw.get('test_class', FunctionalTestCase)
...@@ -297,7 +304,7 @@ class FunctionalSuiteFactory(ZopeSuiteFactory): ...@@ -297,7 +304,7 @@ class FunctionalSuiteFactory(ZopeSuiteFactory):
warnings.warn(("The test_class you are using doesn't " warnings.warn(("The test_class you are using doesn't "
"subclass from ZopeTestCase.Functional. " "subclass from ZopeTestCase.Functional. "
"Please fix that."), UserWarning, 4) "Please fix that."), UserWarning, 4)
if not 'Functional' in name: if 'Functional' not in name:
name = 'Functional%s' % name name = 'Functional%s' % name
test_class = type(name, (Functional, test_class), {}) test_class = type(name, (Functional, test_class), {})
...@@ -306,24 +313,27 @@ class FunctionalSuiteFactory(ZopeSuiteFactory): ...@@ -306,24 +313,27 @@ class FunctionalSuiteFactory(ZopeSuiteFactory):
def setup_optionflags(self): def setup_optionflags(self):
if 'optionflags' not in self._kw: if 'optionflags' not in self._kw:
self._kw['optionflags'] = (doctest.ELLIPSIS self._kw['optionflags'] = (
| doctest.REPORT_NDIFF doctest.ELLIPSIS | doctest.REPORT_NDIFF |
| doctest.NORMALIZE_WHITESPACE) doctest.NORMALIZE_WHITESPACE)
def ZopeDocTestSuite(module=None, **kw): def ZopeDocTestSuite(module=None, **kw):
module = doctest._normalize_module(module) module = doctest._normalize_module(module)
return ZopeSuiteFactory(module, **kw).doctestsuite() return ZopeSuiteFactory(module, **kw).doctestsuite()
def ZopeDocFileSuite(*paths, **kw): def ZopeDocFileSuite(*paths, **kw):
if kw.get('module_relative', True): if kw.get('module_relative', True):
kw['package'] = doctest._normalize_module(kw.get('package')) kw['package'] = doctest._normalize_module(kw.get('package'))
return ZopeSuiteFactory(*paths, **kw).docfilesuite() return ZopeSuiteFactory(*paths, **kw).docfilesuite()
def FunctionalDocTestSuite(module=None, **kw): def FunctionalDocTestSuite(module=None, **kw):
module = doctest._normalize_module(module) module = doctest._normalize_module(module)
return FunctionalSuiteFactory(module, **kw).doctestsuite() return FunctionalSuiteFactory(module, **kw).doctestsuite()
def FunctionalDocFileSuite(*paths, **kw): def FunctionalDocFileSuite(*paths, **kw):
if kw.get('module_relative', True): if kw.get('module_relative', True):
kw['package'] = doctest._normalize_module(kw.get('package')) kw['package'] = doctest._normalize_module(kw.get('package'))
...@@ -335,5 +345,4 @@ __all__ = [ ...@@ -335,5 +345,4 @@ __all__ = [
'ZopeDocFileSuite', 'ZopeDocFileSuite',
'FunctionalDocTestSuite', 'FunctionalDocTestSuite',
'FunctionalDocFileSuite', 'FunctionalDocFileSuite',
] ]
# Default test runner
import unittest
TestRunner = unittest.TextTestRunner
def debug():
test_suite().debug()
def pdebug():
import pdb
pdb.run('debug()')
def test_suite():
# The default test suite includes every subclass of TestCase in
# the module, with 'test' as the test method prefix.
ClassType = type(unittest.TestCase)
tests = []
for v in globals().values():
if isinstance(v, ClassType) and issubclass(v, unittest.TestCase):
tests.append(unittest.makeSuite(v))
if len(tests) > 1:
return unittest.TestSuite(tests)
if len(tests) == 1:
return tests[0]
return
class Dummy:
'''Utility class for quick & dirty instances'''
def __init__(self, **kw):
self.__dict__.update(kw)
def __str__( self ):
return 'Dummy(%s)' % `self.__dict__`
__repr__ = __str__
import os import os
import logging import logging
import ZODB
LOG = logging.getLogger('Testing') LOG = logging.getLogger('Testing')
def getStorage(): def getStorage():
""" Return a storage instance for running ZopeTestCase based """ Return a storage instance for running ZopeTestCase based
tests. By default a DemoStorage is used. Setting tests. By default a DemoStorage is used. Setting
...@@ -15,14 +14,14 @@ def getStorage(): ...@@ -15,14 +14,14 @@ def getStorage():
get = os.environ.get get = os.environ.get
if os.environ.has_key('TEST_ZEO_HOST') and os.environ.has_key('TEST_ZEO_PORT'): if 'TEST_ZEO_HOST' in os.environ and 'TEST_ZEO_PORT' in os.environ:
from ZEO.ClientStorage import ClientStorage from ZEO.ClientStorage import ClientStorage
zeo_host = get('TEST_ZEO_HOST') zeo_host = get('TEST_ZEO_HOST')
zeo_port = int(get('TEST_ZEO_PORT')) zeo_port = int(get('TEST_ZEO_PORT'))
LOG.info('Using ZEO server (%s:%d)' % (zeo_host, zeo_port)) LOG.info('Using ZEO server (%s:%d)' % (zeo_host, zeo_port))
return ClientStorage((zeo_host, zeo_port)) return ClientStorage((zeo_host, zeo_port))
elif os.environ.has_key('TEST_FILESTORAGE'): elif 'TEST_FILESTORAGE' in os.environ:
import ZODB.FileStorage import ZODB.FileStorage
datafs = get('TEST_FILESTORAGE') datafs = get('TEST_FILESTORAGE')
LOG.info('Using Filestorage at (%s)' % datafs) LOG.info('Using Filestorage at (%s)' % datafs)
......
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
# Dispatcher for usage inside Zope test environment
# Andreas Jung, andreas@digicool.com 03/24/2001
import os,sys,re,string
import threading,time,commands,profile
class Dispatcher:
"""
a multi-purpose thread dispatcher
"""
def __init__(self,func=''):
self.fp = sys.stderr
self.f_startup = []
self.f_teardown = []
self.lastlog = ""
self.lock = threading.Lock()
self.func = func
self.profiling = 0
self.doc = getattr(self,self.func).__doc__
def setlog(self,fp):
self.fp = fp
def log(self,s):
if s==self.lastlog: return
self.fp.write(s)
self.fp.flush()
self.lastlog=s
def logn(self,s):
if s==self.lastlog: return
self.fp.write(s + '\n')
self.fp.flush()
self.lastlog=s
def profiling_on():
self.profiling = 1
def profiling_off():
self.profiling = 0
def dispatcher(self,name='', *params):
""" dispatcher for threads
The dispatcher expects one or several tupels:
(functionname, number of threads to start , args, keyword args)
"""
self.mem_usage = [-1]
mem_watcher = threading.Thread(None,self.mem_watcher,name='memwatcher')
mem_watcher.start()
self.start_test = time.time()
self.name = name
self.th_data = {}
self.runtime = {}
self._threads = []
s2s=self.s2s
for func,numthreads,args,kw in params:
f = getattr(self,func)
for i in range(0,numthreads):
kw['t_func'] = func
th = threading.Thread(None,self.worker,name="TH_%s_%03d" % (func,i) ,args=args,kwargs=kw)
self._threads.append(th)
for th in self._threads: th.start()
while threading.activeCount() > 1: time.sleep(1)
self.logn('ID: %s ' % self.name)
self.logn('FUNC: %s ' % self.func)
self.logn('DOC: %s ' % self.doc)
self.logn('Args: %s' % params)
for th in self._threads:
self.logn( '%-30s ........................ %9.3f sec' % (th.getName(), self.runtime[th.getName()]) )
for k,v in self.th_data[th.getName()].items():
self.logn ('%-30s %-15s = %s' % (' ',k,v) )
self.logn("")
self.logn('Complete running time: %9.3f sec' % (time.time()-self.start_test) )
if len(self.mem_usage)>1: self.mem_usage.remove(-1)
self.logn( "Memory: start: %s, end: %s, low: %s, high: %s" % \
(s2s(self.mem_usage[0]),s2s(self.mem_usage[-1]),s2s(min(self.mem_usage)), s2s(max(self.mem_usage))))
self.logn('')
def worker(self,*args,**kw):
for func in self.f_startup: f = getattr(self,func)()
t_func = getattr(self,kw['t_func'])
del kw['t_func']
ts = time.time()
apply(t_func,args,kw)
te = time.time()
for func in self.f_teardown: getattr(self,func)()
def th_setup(self):
""" initalize thread with some environment data """
env = {'start': time.time()
}
return env
def th_teardown(self,env,**kw):
""" famous last actions of thread """
self.lock.acquire()
self.th_data[ threading.currentThread().getName() ] = kw
self.runtime [ threading.currentThread().getName() ] = time.time() - env['start']
self.lock.release()
def getmem(self):
""" try to determine the current memory usage """
if not sys.platform in ['linux2']: return None
cmd = '/bin/ps --no-headers -o pid,vsize --pid %s' % os.getpid()
outp = commands.getoutput(cmd)
pid,vsize = filter(lambda x: x!="" , string.split(outp," ") )
data = open("/proc/%d/statm" % os.getpid()).read()
fields = re.split(" ",data)
mem = string.atoi(fields[0]) * 4096
return mem
def mem_watcher(self):
""" thread for watching memory usage """
running = 1
while running ==1:
self.mem_usage.append( self.getmem() )
time.sleep(1)
if threading.activeCount() == 2: running = 0
def register_startup(self,func):
self.f_startup.append(func)
def register_teardown(self,func):
self.f_teardown.append(func)
def s2s(self,n):
import math
if n <1024.0: return "%8.3lf Bytes" % n
if n <1024.0*1024.0: return "%8.3lf KB" % (1.0*n/1024.0)
if n <1024.0*1024.0*1024.0: return "%8.3lf MB" % (1.0*n/1024.0/1024.0)
else: return n
if __name__=="__main__":
d=Dispatcher()
print d.getmem()
pass
...@@ -15,12 +15,12 @@ Facilitates unit tests which requires an acquirable REQUEST from ...@@ -15,12 +15,12 @@ Facilitates unit tests which requires an acquirable REQUEST from
ZODB objects ZODB objects
""" """
import os
from sys import stdin, stdout from sys import stdin, stdout
from ZPublisher.HTTPRequest import HTTPRequest from ZPublisher.HTTPRequest import HTTPRequest
from ZPublisher.HTTPResponse import HTTPResponse from ZPublisher.HTTPResponse import HTTPResponse
from ZPublisher.BaseRequest import RequestContainer from ZPublisher.BaseRequest import RequestContainer
def makerequest(app, stdout=stdout, environ=None): def makerequest(app, stdout=stdout, environ=None):
""" """
Adds an HTTPRequest at app.REQUEST, and returns Adds an HTTPRequest at app.REQUEST, and returns
...@@ -57,10 +57,9 @@ def makerequest(app, stdout=stdout, environ=None): ...@@ -57,10 +57,9 @@ def makerequest(app, stdout=stdout, environ=None):
req._steps = ['noobject'] # Fake a published object. req._steps = ['noobject'] # Fake a published object.
req['ACTUAL_URL'] = req.get('URL') # Zope 2.7.4 req['ACTUAL_URL'] = req.get('URL') # Zope 2.7.4
# set Zope3-style default skin so that the request is usable for # Set default skin so that the request is usable for view look-ups.
# Zope3-style view look-ups.
from zope.publisher.browser import setDefaultSkin from zope.publisher.browser import setDefaultSkin
setDefaultSkin(req) setDefaultSkin(req)
requestcontainer = RequestContainer(REQUEST = req) requestcontainer = RequestContainer(REQUEST=req)
return app.__of__(requestcontainer) return app.__of__(requestcontainer)
...@@ -19,6 +19,7 @@ from Acquisition import Implicit ...@@ -19,6 +19,7 @@ from Acquisition import Implicit
from Testing.makerequest import makerequest from Testing.makerequest import makerequest
from OFS.SimpleItem import SimpleItem from OFS.SimpleItem import SimpleItem
class MakerequestTests(unittest.TestCase): class MakerequestTests(unittest.TestCase):
def test_makerequest(self): def test_makerequest(self):
...@@ -57,8 +58,3 @@ class MakerequestTests(unittest.TestCase): ...@@ -57,8 +58,3 @@ class MakerequestTests(unittest.TestCase):
environ = {'foofoo': 'barbar'} environ = {'foofoo': 'barbar'}
item = makerequest(SimpleItem(), environ=environ) item = makerequest(SimpleItem(), environ=environ)
self.assertEqual(item.REQUEST.environ['foofoo'], 'barbar') self.assertEqual(item.REQUEST.environ['foofoo'], 'barbar')
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(MakerequestTests))
return suite
...@@ -18,6 +18,7 @@ import unittest ...@@ -18,6 +18,7 @@ import unittest
from Testing.ZopeTestCase import FunctionalDocTestSuite from Testing.ZopeTestCase import FunctionalDocTestSuite
from OFS.SimpleItem import Item from OFS.SimpleItem import Item
class CookieStub(Item): class CookieStub(Item):
"""This is a cookie stub.""" """This is a cookie stub."""
...@@ -25,6 +26,7 @@ class CookieStub(Item): ...@@ -25,6 +26,7 @@ class CookieStub(Item):
REQUEST.RESPONSE.setCookie('evil', 'cookie') REQUEST.RESPONSE.setCookie('evil', 'cookie')
return 'Stub' return 'Stub'
def doctest_cookies(): def doctest_cookies():
""" """
We want to make sure that our testbrowser correctly understands We want to make sure that our testbrowser correctly understands
...@@ -52,6 +54,7 @@ def doctest_cookies(): ...@@ -52,6 +54,7 @@ def doctest_cookies():
True True
""" """
def doctest_camel_case_headers(): def doctest_camel_case_headers():
"""Make sure that the headers come out in camel case. """Make sure that the headers come out in camel case.
......
...@@ -14,11 +14,13 @@ ...@@ -14,11 +14,13 @@
""" """
from urllib import quote as urllib_quote from urllib import quote as urllib_quote
import types
import xmlrpc import xmlrpc
from AccessControl.ZopeSecurityPolicy import getRoles
from Acquisition import aq_base from Acquisition import aq_base
from Acquisition.interfaces import IAcquirer from Acquisition.interfaces import IAcquirer
from ZPublisher.interfaces import UseTraversalDefault from ExtensionClass import Base
from zExceptions import Forbidden from zExceptions import Forbidden
from zExceptions import NotFound from zExceptions import NotFound
from zope.component import queryMultiAdapter from zope.component import queryMultiAdapter
...@@ -34,6 +36,10 @@ from zope.publisher.interfaces.browser import IBrowserPublisher ...@@ -34,6 +36,10 @@ from zope.publisher.interfaces.browser import IBrowserPublisher
from zope.traversing.namespace import namespaceLookup from zope.traversing.namespace import namespaceLookup
from zope.traversing.namespace import nsParse from zope.traversing.namespace import nsParse
from ZPublisher.Converters import type_converters
from ZPublisher.interfaces import UseTraversalDefault
_marker = []
UNSPECIFIED_ROLES = '' UNSPECIFIED_ROLES = ''
...@@ -41,28 +47,17 @@ def quote(text): ...@@ -41,28 +47,17 @@ def quote(text):
# quote url path segments, but leave + and @ intact # quote url path segments, but leave + and @ intact
return urllib_quote(text, '/+@') return urllib_quote(text, '/+@')
try:
from ExtensionClass import Base class RequestContainer(Base):
from ZPublisher.Converters import type_converters __roles__ = None
class RequestContainer(Base):
__roles__=None def __init__(self, **kw):
def __init__(self,**kw): for k, v in kw.items():
for k,v in kw.items(): self.__dict__[k]=v self.__dict__[k] = v
def manage_property_types(self): def manage_property_types(self):
return type_converters.keys() return type_converters.keys()
except ImportError:
class RequestContainer:
__roles__=None
def __init__(self,**kw):
for k,v in kw.items(): self.__dict__[k]=v
try:
from AccessControl.ZopeSecurityPolicy import getRoles
except ImportError:
def getRoles(container, name, value, default):
return getattr(value, '__roles__', default)
class DefaultPublishTraverse(object): class DefaultPublishTraverse(object):
...@@ -74,35 +69,39 @@ class DefaultPublishTraverse(object): ...@@ -74,35 +69,39 @@ class DefaultPublishTraverse(object):
def publishTraverse(self, request, name): def publishTraverse(self, request, name):
object = self.context object = self.context
URL=request['URL'] URL = request['URL']
if name[:1]=='_': if name[:1] == '_':
raise Forbidden("Object name begins with an underscore at: %s" % URL) raise Forbidden(
"Object name begins with an underscore at: %s" % URL)
subobject = UseTraversalDefault # indicator subobject = UseTraversalDefault # indicator
try: try:
if hasattr(object,'__bobo_traverse__'): if hasattr(object, '__bobo_traverse__'):
try: try:
subobject=object.__bobo_traverse__(request, name) subobject = object.__bobo_traverse__(request, name)
if type(subobject) is type(()) and len(subobject) > 1: if isinstance(subobject, tuple) and len(subobject) > 1:
# Add additional parents into the path # Add additional parents into the path
# XXX There are no tests for this: # XXX There are no tests for this:
request['PARENTS'][-1:] = list(subobject[:-1]) request['PARENTS'][-1:] = list(subobject[:-1])
object, subobject = subobject[-2:] object, subobject = subobject[-2:]
except (AttributeError, KeyError, NotFound), e: except (AttributeError, KeyError, NotFound) as e:
# Try to find a view # Try to find a view
subobject = queryMultiAdapter((object, request), Interface, name) subobject = queryMultiAdapter(
(object, request), Interface, name)
if subobject is not None: if subobject is not None:
# OFS.Application.__bobo_traverse__ calls # OFS.Application.__bobo_traverse__ calls
# REQUEST.RESPONSE.notFoundError which sets the HTTP # REQUEST.RESPONSE.notFoundError which sets the HTTP
# status code to 404 # status code to 404
request.response.setStatus(200) request.response.setStatus(200)
# We don't need to do the docstring security check # We don't need to do the docstring security check
# for views, so lets skip it and return the object here. # for views, so lets skip it and
# return the object here.
if IAcquirer.providedBy(subobject): if IAcquirer.providedBy(subobject):
subobject = subobject.__of__(object) subobject = subobject.__of__(object)
return subobject return subobject
# No view found. Reraise the error raised by __bobo_traverse__ # No view found. Reraise the error
# raised by __bobo_traverse__
raise e raise e
except UseTraversalDefault: except UseTraversalDefault:
pass pass
...@@ -123,9 +122,9 @@ class DefaultPublishTraverse(object): ...@@ -123,9 +122,9 @@ class DefaultPublishTraverse(object):
# And lastly, of there is no view, try acquired attributes, but # And lastly, of there is no view, try acquired attributes, but
# only if there is no __bobo_traverse__: # only if there is no __bobo_traverse__:
try: try:
subobject=getattr(object, name) subobject = getattr(object, name)
# Again, clear any error status created by __bobo_traverse__ # Again, clear any error status created by
# because we actually found something: # __bobo_traverse__ because we actually found something:
request.response.setStatus(200) request.response.setStatus(200)
except AttributeError: except AttributeError:
pass pass
...@@ -143,21 +142,15 @@ class DefaultPublishTraverse(object): ...@@ -143,21 +142,15 @@ class DefaultPublishTraverse(object):
doc = getattr(subobject, '__doc__', None) doc = getattr(subobject, '__doc__', None)
if not doc: if not doc:
raise Forbidden( raise Forbidden(
"The object at %s has an empty or missing " \ "The object at %s has an empty or missing "
"docstring. Objects must have a docstring to be " \ "docstring. Objects must have a docstring to be "
"published." % URL "published." % URL
) )
# Hack for security: in Python 2.2.2, most built-in types # Check that built-in types aren't publishable.
# gained docstrings that they didn't have before. That caused
# certain mutable types (dicts, lists) to become publishable
# when they shouldn't be. The following check makes sure that
# the right thing happens in both 2.2.2+ and earlier versions.
if not typeCheck(subobject): if not typeCheck(subobject):
raise Forbidden( raise Forbidden(
"The object at %s is not publishable." % URL "The object at %s is not publishable." % URL)
)
return subobject return subobject
...@@ -175,7 +168,6 @@ class DefaultPublishTraverse(object): ...@@ -175,7 +168,6 @@ class DefaultPublishTraverse(object):
return self.context, () return self.context, ()
_marker=[]
class BaseRequest: class BaseRequest:
"""Provide basic ZPublisher request management """Provide basic ZPublisher request management
...@@ -196,25 +188,27 @@ class BaseRequest: ...@@ -196,25 +188,27 @@ class BaseRequest:
# acquisition of REQUEST is disallowed, which penalizes access # acquisition of REQUEST is disallowed, which penalizes access
# in DTML with tags. # in DTML with tags.
__roles__ = None __roles__ = None
_file=None _file = None
common={} # Common request data common = {} # Common request data
_auth=None _auth = None
_held=() _held = ()
# Allow (reluctantly) access to unprotected attributes # Allow (reluctantly) access to unprotected attributes
__allow_access_to_unprotected_subobjects__=1 __allow_access_to_unprotected_subobjects__ = 1
def __init__(self, other=None, **kw): def __init__(self, other=None, **kw):
"""The constructor is not allowed to raise errors """The constructor is not allowed to raise errors
""" """
self.__doc__ = None # Make BaseRequest objects unpublishable self.__doc__ = None # Make BaseRequest objects unpublishable
if other is None: other=kw if other is None:
else: other.update(kw) other = kw
self.other=other else:
other.update(kw)
self.other = other
def clear(self): def clear(self):
self.other.clear() self.other.clear()
self._held=None self._held = None
def close(self): def close(self):
try: try:
...@@ -231,15 +225,15 @@ class BaseRequest: ...@@ -231,15 +225,15 @@ class BaseRequest:
def __len__(self): def __len__(self):
return 1 return 1
def __setitem__(self,key,value): def __setitem__(self, key, value):
"""Set application variables """Set application variables
This method is used to set a variable in the requests "other" This method is used to set a variable in the requests "other"
category. category.
""" """
self.other[key]=value self.other[key] = value
set=__setitem__ set = __setitem__
def get(self, key, default=None): def get(self, key, default=None):
"""Get a variable value """Get a variable value
...@@ -250,24 +244,27 @@ class BaseRequest: ...@@ -250,24 +244,27 @@ class BaseRequest:
other variables, form data, and then cookies. other variables, form data, and then cookies.
""" """
if key=='REQUEST': return self if key == 'REQUEST':
return self
v=self.other.get(key, _marker) v = self.other.get(key, _marker)
if v is not _marker: return v if v is not _marker:
v=self.common.get(key, default) return v
if v is not _marker: return v v = self.common.get(key, default)
if v is not _marker:
return v
if key=='BODY' and self._file is not None: if key == 'BODY' and self._file is not None:
p=self._file.tell() p = self._file.tell()
self._file.seek(0) self._file.seek(0)
v=self._file.read() v = self._file.read()
self._file.seek(p) self._file.seek(p)
self.other[key]=v self.other[key] = v
return v return v
if key=='BODYFILE' and self._file is not None: if key == 'BODYFILE' and self._file is not None:
v=self._file v = self._file
self.other[key]=v self.other[key] = v
return v return v
return default return default
...@@ -275,7 +272,7 @@ class BaseRequest: ...@@ -275,7 +272,7 @@ class BaseRequest:
def __getitem__(self, key, default=_marker): def __getitem__(self, key, default=_marker):
v = self.get(key, default) v = self.get(key, default)
if v is _marker: if v is _marker:
raise KeyError, key raise KeyError(key)
return v return v
def __bobo_traverse__(self, name): def __bobo_traverse__(self, name):
...@@ -284,17 +281,17 @@ class BaseRequest: ...@@ -284,17 +281,17 @@ class BaseRequest:
def __getattr__(self, key, default=_marker): def __getattr__(self, key, default=_marker):
v = self.get(key, default) v = self.get(key, default)
if v is _marker: if v is _marker:
raise AttributeError, key raise AttributeError(key)
return v return v
def set_lazy(self, key, callable): def set_lazy(self, key, callable):
pass # MAYBE, we could do more, but let HTTPRequest do it pass # MAYBE, we could do more, but let HTTPRequest do it
def has_key(self,key): def has_key(self, key):
return self.get(key, _marker) is not _marker return key in self
def __contains__(self, key): def __contains__(self, key):
return self.has_key(key) return self.get(key, _marker) is not _marker
def keys(self): def keys(self):
keys = {} keys = {}
...@@ -304,16 +301,14 @@ class BaseRequest: ...@@ -304,16 +301,14 @@ class BaseRequest:
def items(self): def items(self):
result = [] result = []
get=self.get
for k in self.keys(): for k in self.keys():
result.append((k, get(k))) result.append((k, self.get(k)))
return result return result
def values(self): def values(self):
result = [] result = []
get=self.get
for k in self.keys(): for k in self.keys():
result.append(get(k)) result.append(self.get(k))
return result return result
def __str__(self): def __str__(self):
...@@ -321,7 +316,7 @@ class BaseRequest: ...@@ -321,7 +316,7 @@ class BaseRequest:
L1.sort() L1.sort()
return '\n'.join("%s:\t%s" % item for item in L1) return '\n'.join("%s:\t%s" % item for item in L1)
__repr__=__str__ __repr__ = __str__
# Original version: see zope.traversing.publicationtraverse # Original version: see zope.traversing.publicationtraverse
def traverseName(self, ob, name): def traverseName(self, ob, name):
...@@ -346,8 +341,8 @@ class BaseRequest: ...@@ -346,8 +341,8 @@ class BaseRequest:
else: else:
adapter = queryMultiAdapter((ob, self), IPublishTraverse) adapter = queryMultiAdapter((ob, self), IPublishTraverse)
if adapter is None: if adapter is None:
## Zope2 doesn't set up its own adapters in a lot of cases # Zope2 doesn't set up its own adapters in a lot of cases
## so we will just use a default adapter. # so we will just use a default adapter.
adapter = DefaultPublishTraverse(ob, self) adapter = DefaultPublishTraverse(ob, self)
ob2 = adapter.publishTraverse(self, name) ob2 = adapter.publishTraverse(self, name)
...@@ -361,59 +356,64 @@ class BaseRequest: ...@@ -361,59 +356,64 @@ class BaseRequest:
The REQUEST must already have a PARENTS item with at least one The REQUEST must already have a PARENTS item with at least one
object in it. This is typically the root object. object in it. This is typically the root object.
""" """
request=self request = self
request_get=request.get request_get = request.get
if response is None: response=self.response if response is None:
response = self.response
# remember path for later use # remember path for later use
browser_path = path browser_path = path
# Cleanup the path list # Cleanup the path list
if path[:1]=='/': path=path[1:] if path[:1] == '/':
if path[-1:]=='/': path=path[:-1] path = path[1:]
clean=[] if path[-1:] == '/':
path = path[:-1]
clean = []
for item in path.split('/'): for item in path.split('/'):
# Make sure that certain things that dont make sense # Make sure that certain things that dont make sense
# cannot be traversed. # cannot be traversed.
if item in ('REQUEST', 'aq_self', 'aq_base'): if item in ('REQUEST', 'aq_self', 'aq_base'):
return response.notFoundError(path) return response.notFoundError(path)
if not item or item=='.': if not item or item == '.':
continue continue
elif item == '..': elif item == '..':
del clean[-1] del clean[-1]
else: clean.append(item) else:
path=clean clean.append(item)
path = clean
# How did this request come in? (HTTP GET, PUT, POST, etc.) # How did this request come in? (HTTP GET, PUT, POST, etc.)
method = request_get('REQUEST_METHOD', 'GET').upper() method = request_get('REQUEST_METHOD', 'GET').upper()
if method in ["GET", "POST", "PURGE"] and not isinstance(response, if (method in ["GET", "POST", "PURGE"] and
xmlrpc.Response): not isinstance(response, xmlrpc.Response)):
# Probably a browser # Probably a browser
no_acquire_flag=0 no_acquire_flag = 0
# index_html is still the default method, only any object can # index_html is still the default method, only any object can
# override it by implementing its own __browser_default__ method # override it by implementing its own __browser_default__ method
method = 'index_html' method = 'index_html'
elif self.maybe_webdav_client: elif self.maybe_webdav_client:
# Probably a WebDAV client. # Probably a WebDAV client.
no_acquire_flag=1 no_acquire_flag = 1
else: else:
no_acquire_flag=0 no_acquire_flag = 0
URL=request['URL'] URL = request['URL']
parents=request['PARENTS'] parents = request['PARENTS']
object=parents[-1] object = parents[-1]
del parents[:] del parents[:]
self.roles = getRoles(None, None, object, UNSPECIFIED_ROLES) self.roles = getRoles(None, None, object, UNSPECIFIED_ROLES)
# if the top object has a __bobo_traverse__ method, then use it # if the top object has a __bobo_traverse__ method, then use it
# to possibly traverse to an alternate top-level object. # to possibly traverse to an alternate top-level object.
if hasattr(object,'__bobo_traverse__'): if hasattr(object, '__bobo_traverse__'):
try: try:
object=object.__bobo_traverse__(request) object = object.__bobo_traverse__(request)
self.roles = getRoles(None, None, object, UNSPECIFIED_ROLES) self.roles = getRoles(None, None, object, UNSPECIFIED_ROLES)
except: pass except Exception:
pass
if not path and not method: if not path and not method:
return response.forbiddenError(self['URL']) return response.forbiddenError(self['URL'])
...@@ -422,10 +422,10 @@ class BaseRequest: ...@@ -422,10 +422,10 @@ class BaseRequest:
if hasattr(object, '__of__'): if hasattr(object, '__of__'):
# Try to bind the top-level object to the request # Try to bind the top-level object to the request
# This is how you get 'self.REQUEST' # This is how you get 'self.REQUEST'
object=object.__of__(RequestContainer(REQUEST=request)) object = object.__of__(RequestContainer(REQUEST=request))
parents.append(object) parents.append(object)
steps=self.steps steps = self.steps
self._steps = _steps = map(quote, steps) self._steps = _steps = map(quote, steps)
path.reverse() path.reverse()
...@@ -485,7 +485,7 @@ class BaseRequest: ...@@ -485,7 +485,7 @@ class BaseRequest:
object, default_path = adapter.browserDefault(self) object, default_path = adapter.browserDefault(self)
if default_path: if default_path:
request._hacked_path=1 request._hacked_path = 1
if len(default_path) > 1: if len(default_path) > 1:
path = list(default_path) path = list(default_path)
method = path.pop() method = path.pop()
...@@ -493,19 +493,21 @@ class BaseRequest: ...@@ -493,19 +493,21 @@ class BaseRequest:
continue continue
else: else:
entry_name = default_path[0] entry_name = default_path[0]
elif (method and hasattr(object,method) elif (method and hasattr(object, method) and
and entry_name != method entry_name != method and
and getattr(object, method) is not None): getattr(object, method) is not None):
request._hacked_path=1 request._hacked_path = 1
entry_name = method entry_name = method
method = 'index_html' method = 'index_html'
else: else:
if hasattr(object, '__call__'): if hasattr(object, '__call__'):
self.roles = getRoles(object, '__call__', object.__call__, self.roles = getRoles(
self.roles) object, '__call__',
object.__call__, self.roles)
if request._hacked_path: if request._hacked_path:
i=URL.rfind('/') i = URL.rfind('/')
if i > 0: response.setBase(URL[:i]) if i > 0:
response.setBase(URL[:i])
break break
step = quote(entry_name) step = quote(entry_name)
_steps.append(step) _steps.append(step)
...@@ -513,7 +515,7 @@ class BaseRequest: ...@@ -513,7 +515,7 @@ class BaseRequest:
try: try:
subobject = self.traverseName(object, entry_name) subobject = self.traverseName(object, entry_name)
if (hasattr(object,'__bobo_traverse__') or if (hasattr(object, '__bobo_traverse__') or
hasattr(object, entry_name)): hasattr(object, entry_name)):
check_name = entry_name check_name = entry_name
else: else:
...@@ -530,7 +532,7 @@ class BaseRequest: ...@@ -530,7 +532,7 @@ class BaseRequest:
"Cannot locate object at: %s" % URL) "Cannot locate object at: %s" % URL)
else: else:
return response.notFoundError(URL) return response.notFoundError(URL)
except Forbidden, e: except Forbidden as e:
if self.response.debug_mode: if self.response.debug_mode:
return response.debugError(e.args) return response.debugError(e.args)
else: else:
...@@ -553,11 +555,11 @@ class BaseRequest: ...@@ -553,11 +555,11 @@ class BaseRequest:
# existing object :( # existing object :(
if (no_acquire_flag and if (no_acquire_flag and
hasattr(parents[1], 'aq_base') and hasattr(parents[1], 'aq_base') and
not hasattr(parents[1],'__bobo_traverse__')): not hasattr(parents[1], '__bobo_traverse__')):
base = parents[1].aq_base base = parents[1].aq_base
if not hasattr(base, entry_name): if not hasattr(base, entry_name):
try: try:
if not entry_name in base: if entry_name not in base:
raise AttributeError(entry_name) raise AttributeError(entry_name)
except TypeError: except TypeError:
raise AttributeError(entry_name) raise AttributeError(entry_name)
...@@ -568,74 +570,90 @@ class BaseRequest: ...@@ -568,74 +570,90 @@ class BaseRequest:
request['PUBLISHED'] = parents.pop(0) request['PUBLISHED'] = parents.pop(0)
# Do authorization checks # Do authorization checks
user=groups=None user = groups = None
i=0 i = 0
if 1: # Always perform authentication. if 1: # Always perform authentication.
last_parent_index=len(parents) last_parent_index = len(parents)
if hasattr(object, '__allow_groups__'): if hasattr(object, '__allow_groups__'):
groups=object.__allow_groups__ groups = object.__allow_groups__
inext=0 inext = 0
else: else:
inext=None inext = None
for i in range(last_parent_index): for i in range(last_parent_index):
if hasattr(parents[i],'__allow_groups__'): if hasattr(parents[i], '__allow_groups__'):
groups=parents[i].__allow_groups__ groups = parents[i].__allow_groups__
inext=i+1 inext = i + 1
break break
if inext is not None: if inext is not None:
i=inext i = inext
if hasattr(groups, 'validate'): v=groups.validate if hasattr(groups, 'validate'):
else: v=old_validation v = groups.validate
else:
v = old_validation
auth=request._auth auth = request._auth
if v is old_validation and self.roles is UNSPECIFIED_ROLES: if v is old_validation and self.roles is UNSPECIFIED_ROLES:
# No roles, so if we have a named group, get roles from # No roles, so if we have a named group, get roles from
# group keys # group keys
if hasattr(groups,'keys'): self.roles=groups.keys() if hasattr(groups, 'keys'):
self.roles = groups.keys()
else: else:
try: groups=groups() try:
except: pass groups = groups()
try: self.roles=groups.keys() except Exception:
except: pass pass
try:
self.roles = groups.keys()
except Exception:
pass
if groups is None: if groups is None:
# Public group, hack structures to get it to validate # Public group, hack structures to get it to validate
self.roles=None self.roles = None
auth='' auth = ''
if v is old_validation: if v is old_validation:
user=old_validation(groups, request, auth, self.roles) user = old_validation(groups, request, auth, self.roles)
elif self.roles is UNSPECIFIED_ROLES: user=v(request, auth) elif self.roles is UNSPECIFIED_ROLES:
else: user=v(request, auth, self.roles) user = v(request, auth)
else:
user = v(request, auth, self.roles)
while user is None and i < last_parent_index: while user is None and i < last_parent_index:
parent=parents[i] parent = parents[i]
i=i+1 i = i + 1
if hasattr(parent, '__allow_groups__'): if hasattr(parent, '__allow_groups__'):
groups=parent.__allow_groups__ groups = parent.__allow_groups__
else: continue else:
if hasattr(groups,'validate'): v=groups.validate continue
else: v=old_validation if hasattr(groups, 'validate'):
v = groups.validate
else:
v = old_validation
if v is old_validation: if v is old_validation:
user=old_validation(groups, request, auth, self.roles) user = old_validation(
elif self.roles is UNSPECIFIED_ROLES: user=v(request, auth) groups, request, auth, self.roles)
else: user=v(request, auth, self.roles) elif self.roles is UNSPECIFIED_ROLES:
user = v(request, auth)
else:
user = v(request, auth, self.roles)
if user is None and self.roles != UNSPECIFIED_ROLES: if user is None and self.roles != UNSPECIFIED_ROLES:
response.unauthorized() response.unauthorized()
if user is not None: if user is not None:
if validated_hook is not None: validated_hook(self, user) if validated_hook is not None:
request['AUTHENTICATED_USER']=user validated_hook(self, user)
request['AUTHENTICATION_PATH']='/'.join(steps[:-i]) request['AUTHENTICATED_USER'] = user
request['AUTHENTICATION_PATH'] = '/'.join(steps[:-i])
# Remove http request method from the URL. # Remove http request method from the URL.
request['URL']=URL request['URL'] = URL
# Run post traversal hooks # Run post traversal hooks
if post_traverse: if post_traverse:
...@@ -657,19 +675,22 @@ class BaseRequest: ...@@ -657,19 +675,22 @@ class BaseRequest:
try: try:
pairs = self._post_traverse pairs = self._post_traverse
except AttributeError: except AttributeError:
raise RuntimeError, ('post_traverse() may only be called ' raise RuntimeError('post_traverse() may only be called '
'during publishing traversal.') 'during publishing traversal.')
else: else:
pairs.append((f, tuple(args))) pairs.append((f, tuple(args)))
retry_count=0 retry_count = 0
def supports_retry(self): return 0
def supports_retry(self):
return 0
def _hold(self, object): def _hold(self, object):
"""Hold a reference to an object to delay it's destruction until mine """Hold a reference to an object to delay it's destruction until mine
""" """
if self._held is not None: if self._held is not None:
self._held=self._held+(object,) self._held = self._held + (object, )
def exec_callables(callables): def exec_callables(callables):
result = None result = None
...@@ -679,74 +700,84 @@ def exec_callables(callables): ...@@ -679,74 +700,84 @@ def exec_callables(callables):
if result is not None: if result is not None:
return result return result
def old_validation(groups, request, auth, def old_validation(groups, request, auth,
roles=UNSPECIFIED_ROLES): roles=UNSPECIFIED_ROLES):
if auth: if auth:
auth=request._authUserPW() auth = request._authUserPW()
if auth: name,password = auth if auth:
elif roles is None: return '' name, password = auth
else: return None elif roles is None:
return ''
else:
return None
elif 'REMOTE_USER' in request.environ: elif 'REMOTE_USER' in request.environ:
name=request.environ['REMOTE_USER'] name = request.environ['REMOTE_USER']
password=None password = None
else: else:
if roles is None: return '' if roles is None:
return ''
return None return None
if roles is None: return name if roles is None:
return name
keys=None keys = None
try: try:
keys=groups.keys keys = groups.keys
except: except Exception:
try: try:
groups=groups() # Maybe it was a method defining a group groups = groups() # Maybe it was a method defining a group
keys=groups.keys keys = groups.keys
except: pass except Exception:
pass
if keys is not None: if keys is not None:
# OK, we have a named group, so apply the roles to the named # OK, we have a named group, so apply the roles to the named
# group. # group.
if roles is UNSPECIFIED_ROLES: roles=keys() if roles is UNSPECIFIED_ROLES:
g=[] roles = keys()
g = []
for role in roles: for role in roles:
if role in groups: g.append(groups[role]) if role in groups:
groups=g g.append(groups[role])
groups = g
for d in groups: for d in groups:
if name in d and (d[name]==password or password is None): if name in d and (d[name] == password or password is None):
return name return name
if keys is None: if keys is None:
# Not a named group, so don't go further # Not a named group, so don't go further
raise Forbidden, ( raise Forbidden(
"""<strong>You are not authorized to access this resource""") """<strong>You are not authorized to access this resource""")
return None return None
# This mapping contains the built-in types that gained docstrings itypes = {
# between Python 2.1 and 2.2.2. By specifically checking for these bool: 0,
# types during publishing, we ensure the same publishing rules in types.CodeType: 0,
# both versions. The downside is that this needs to be extended as complex: 0,
# new built-in types are added and future Python versions are dict: 0,
# supported. float: 0,
types.FrameType: 0,
import types frozenset: 0,
int: 0,
itypes = {} list: 0,
for name in ('NoneType', 'IntType', 'LongType', 'FloatType', 'StringType', type(None): 0,
'BufferType', 'TupleType', 'ListType', 'DictType', 'XRangeType', set: 0,
'SliceType', 'EllipsisType', 'UnicodeType', 'CodeType', slice: 0,
'TracebackType', 'FrameType', 'DictProxyType', 'BooleanType', str: 0,
'ComplexType'): types.TracebackType: 0,
tuple: 0,
}
for name in ('BufferType', 'DictProxyType', 'EllipsisType',
'LongType', 'UnicodeType', 'XRangeType'):
if hasattr(types, name): if hasattr(types, name):
itypes[getattr(types, name)] = 0 itypes[getattr(types, name)] = 0
# Python 2.4 no longer maintains the types module.
itypes[set] = 0
itypes[frozenset] = 0
def typeCheck(obj, deny=itypes): def typeCheck(obj, deny=itypes):
# Return true if its ok to publish the type, false otherwise. # Return true if its ok to publish the type, false otherwise.
......
...@@ -15,10 +15,9 @@ ...@@ -15,10 +15,9 @@
from zExceptions import Unauthorized, Forbidden, NotFound, BadRequest from zExceptions import Unauthorized, Forbidden, NotFound, BadRequest
class BaseResponse: class BaseResponse:
"""Base Response Class """Base Response Class
What should be here?
""" """
debug_mode = None debug_mode = None
_auth = None _auth = None
...@@ -59,7 +58,7 @@ class BaseResponse: ...@@ -59,7 +58,7 @@ class BaseResponse:
'Returns the current HTTP status code as an integer. ' 'Returns the current HTTP status code as an integer. '
return self.status return self.status
def setCookie(self,name,value,**kw): def setCookie(self, name, value, **kw):
'''\ '''\
Set an HTTP cookie on the browser Set an HTTP cookie on the browser
...@@ -101,12 +100,12 @@ class BaseResponse: ...@@ -101,12 +100,12 @@ class BaseResponse:
return str(self.body) return str(self.body)
def __repr__(self): def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, `self.body`) return '%s(%r)' % (self.__class__.__name__, self.body)
def flush(self): def flush(self):
pass pass
def write(self,data): def write(self, data):
"""\ """\
Return data as a stream Return data as a stream
...@@ -121,7 +120,7 @@ class BaseResponse: ...@@ -121,7 +120,7 @@ class BaseResponse:
after beginning stream-oriented output. after beginning stream-oriented output.
""" """
self.body = self.body+data self.body = self.body + data
def exception(self, fatal=0, info=None): def exception(self, fatal=0, info=None):
"""Handle an exception. """Handle an exception.
...@@ -135,23 +134,23 @@ class BaseResponse: ...@@ -135,23 +134,23 @@ class BaseResponse:
def notFoundError(self, v=''): def notFoundError(self, v=''):
"""Generate an error indicating that an object was not found. """Generate an error indicating that an object was not found.
""" """
raise NotFound, v raise NotFound(v)
def debugError(self, v=''): def debugError(self, v=''):
"""Raise an error with debigging info and in debugging mode""" """Raise an error with debigging info and in debugging mode"""
raise NotFound, "Debugging notice: %s" % v raise NotFound("Debugging notice: %s" % v)
def badRequestError(self, v=''): def badRequestError(self, v=''):
"""Raise an error indicating something wrong with the request""" """Raise an error indicating something wrong with the request"""
raise BadRequest, v raise BadRequest(v)
def forbiddenError(self, v=''): def forbiddenError(self, v=''):
"""Raise an error indicating that the request cannot be done""" """Raise an error indicating that the request cannot be done"""
raise Forbidden, v raise Forbidden(v)
def unauthorized(self): def unauthorized(self):
"""Raise an eror indicating that the user was not authizated """Raise an eror indicating that the user was not authizated
Make sure to generate an appropriate challenge, as appropriate. Make sure to generate an appropriate challenge, as appropriate.
""" """
raise Unauthorized raise Unauthorized()
...@@ -15,10 +15,9 @@ ...@@ -15,10 +15,9 @@
from Acquisition import aq_base from Acquisition import aq_base
from logging import getLogger from logging import getLogger
# Interface
LOG = getLogger('ZPublisher') LOG = getLogger('ZPublisher')
def registerBeforeTraverse(container, object, app_handle, priority=99): def registerBeforeTraverse(container, object, app_handle, priority=99):
"""Register an object to be called before a container is traversed. """Register an object to be called before a container is traversed.
...@@ -36,6 +35,7 @@ def registerBeforeTraverse(container, object, app_handle, priority=99): ...@@ -36,6 +35,7 @@ def registerBeforeTraverse(container, object, app_handle, priority=99):
btr[(priority, app_handle)] = object btr[(priority, app_handle)] = object
rewriteBeforeTraverse(container, btr) rewriteBeforeTraverse(container, btr)
def unregisterBeforeTraverse(container, app_handle): def unregisterBeforeTraverse(container, app_handle):
"""Unregister a __before_traverse__ hook object, given its 'app_handle'. """Unregister a __before_traverse__ hook object, given its 'app_handle'.
...@@ -50,6 +50,7 @@ def unregisterBeforeTraverse(container, app_handle): ...@@ -50,6 +50,7 @@ def unregisterBeforeTraverse(container, app_handle):
rewriteBeforeTraverse(container, btr) rewriteBeforeTraverse(container, btr)
return objects return objects
def queryBeforeTraverse(container, app_handle): def queryBeforeTraverse(container, app_handle):
"""Find __before_traverse__ hook objects, given an 'app_handle'. """Find __before_traverse__ hook objects, given an 'app_handle'.
...@@ -61,7 +62,6 @@ def queryBeforeTraverse(container, app_handle): ...@@ -61,7 +62,6 @@ def queryBeforeTraverse(container, app_handle):
objects.append((k[0], btr[k])) objects.append((k[0], btr[k]))
return objects return objects
# Implementation tools
def rewriteBeforeTraverse(container, btr): def rewriteBeforeTraverse(container, btr):
"""Rewrite the list of __before_traverse__ hook objects""" """Rewrite the list of __before_traverse__ hook objects"""
...@@ -79,6 +79,7 @@ def rewriteBeforeTraverse(container, btr): ...@@ -79,6 +79,7 @@ def rewriteBeforeTraverse(container, btr):
for key in keys: for key in keys:
bpth.add(btr[key]) bpth.add(btr[key])
class MultiHook: class MultiHook:
"""Class used to multiplex hook. """Class used to multiplex hook.
...@@ -102,13 +103,12 @@ class MultiHook: ...@@ -102,13 +103,12 @@ class MultiHook:
try: try:
cob(container, request) cob(container, request)
except TypeError: except TypeError:
LOG.error('%s call %s failed.' % ( LOG.error('%r call %r failed.' % (
`self._hookname`, `cob`), exc_info=True) self._hookname, cob), exc_info=True)
def add(self, cob): def add(self, cob):
self._list.append(cob) self._list.append(cob)
# Helper class
class NameCaller: class NameCaller:
"""Class used to proxy sibling objects by name. """Class used to proxy sibling objects by name.
...@@ -134,10 +134,8 @@ class NameCaller: ...@@ -134,10 +134,8 @@ class NameCaller:
# This happens especially, if "meth" is a "CookieCrumber" instance, # This happens especially, if "meth" is a "CookieCrumber" instance,
# i.e. in a CMF Portal, if a DTMLMethod (or a similar object # i.e. in a CMF Portal, if a DTMLMethod (or a similar object
# with a fake "func_code" is in the acquisition context # with a fake "func_code" is in the acquisition context
#args = getattr(getattr(meth, 'func_code', None), 'co_argcount', 2)
args = getattr(getattr(aq_base(meth), 'func_code', None), args = getattr(getattr(aq_base(meth), 'func_code', None),
'co_argcount', 'co_argcount', 2)
2)
try: try:
meth(*(container, request, None)[:args]) meth(*(container, request, None)[:args])
...@@ -148,5 +146,5 @@ class NameCaller: ...@@ -148,5 +146,5 @@ class NameCaller:
# Only catch exceptions that are likely to be logic errors. # Only catch exceptions that are likely to be logic errors.
# We shouldn't catch Redirects, Unauthorizeds, etc. since # We shouldn't catch Redirects, Unauthorizeds, etc. since
# the programmer may want to raise them deliberately. # the programmer may want to raise them deliberately.
LOG.error('BeforeTraverse: Error while invoking hook: "%s"' % self.name, LOG.error('BeforeTraverse: Error while invoking hook: "%s"' %
exc_info=True) self.name, exc_info=True)
#!/bin/sh
""":"
exec python $0 ${1+"$@"}
"""
#"
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Bobo call interface
This module provides tools for accessing web objects as if they were
functions or objects with methods. It also provides a simple call function
that allows one to simply make a single web request.
Function -- Function-like objects that return both header and body
data when called.
Object -- Treat a URL as a web object with methods
call -- Simple interface to call a remote function.
The module also provides a command-line interface for calling objects.
"""
import sys, re, socket, mimetools
from httplib import HTTP
from os import getpid
from time import time
from random import random
from base64 import encodestring
from urllib import urlopen, quote
from types import FileType, ListType, DictType, TupleType
from string import translate, maketrans
from urlparse import urlparse
class BadReply(Exception):
pass
class Function:
username=None
password=None
method=None
timeout=60
def __init__(self,url,
arguments=(),method=None,username=None,password=None,
timeout=None,
**headers):
while url[-1:]=='/': url=url[:-1]
self.url=url
self.headers=headers
if not headers.has_key('Host') and not headers.has_key('host'):
headers['Host']=urlparse(url)[1]
self.func_name=url[url.rfind('/')+1:]
self.__dict__['__name__']=self.func_name
self.func_defaults = self.__defaults__ = ()
self.args=arguments
if method is not None: self.method=method
if username is not None: self.username=username
if password is not None: self.password=password
if timeout is not None: self.timeout=timeout
mo = urlregex.match(url)
if mo is not None:
host,port,rurl=mo.group(1,2,3)
if port: port=int(port[1:])
else: port=80
self.host=host
self.port=port
rurl=rurl or '/'
self.rurl=rurl
else: raise ValueError, url
def __call__(self,*args,**kw):
method=self.method
if method=='PUT' and len(args)==1 and not kw:
query=[args[0]]
args=()
else:
query=[]
for i in range(len(args)):
try:
k=self.args[i]
if kw.has_key(k): raise TypeError, 'Keyword arg redefined'
kw[k]=args[i]
except IndexError: raise TypeError, 'Too many arguments'
headers={}
for k, v in self.headers.items(): headers[translate(k,dashtrans)]=v
method=self.method
if headers.has_key('Content-Type'):
content_type=headers['Content-Type']
if content_type=='multipart/form-data':
return self._mp_call(kw)
else:
content_type=None
if not method or method=='POST':
for v in kw.values():
if hasattr(v,'read'): return self._mp_call(kw)
can_marshal=type2marshal.has_key
for k,v in kw.items():
t=type(v)
if can_marshal(t): q=type2marshal[t](k,v)
else: q='%s=%s' % (k,quote(v))
query.append(q)
url=self.rurl
if query:
query='&'.join(query)
method=method or 'POST'
if method == 'PUT':
headers['Content-Length']=str(len(query))
if method != 'POST':
url="%s?%s" % (url,query)
query=''
elif not content_type:
headers['Content-Type']='application/x-www-form-urlencoded'
headers['Content-Length']=str(len(query))
else: method=method or 'GET'
if (self.username and self.password and
not headers.has_key('Authorization')):
headers['Authorization']=(
"Basic %s" %
encodestring('%s:%s' % (self.username,self.password)).replace(
'\012','')
)
try:
h=HTTP(self.host, self.port)
h.putrequest(method, self.rurl)
for hn,hv in headers.items():
h.putheader(translate(hn,dashtrans),hv)
h.endheaders()
if query: h.send(query)
ec,em,headers=h.getreply()
response =h.getfile().read()
except:
raise NotAvailable, RemoteException(
NotAvailable,sys.exc_info()[1],self.url,query)
if (ec - (ec % 100)) == 200:
return (headers,response)
self.handleError(query, ec, em, headers, response)
def handleError(self, query, ec, em, headers, response):
try: v=headers.dict['bobo-exception-value']
except: v=ec
try: f=headers.dict['bobo-exception-file']
except: f='Unknown'
try: l=headers.dict['bobo-exception-line']
except: l='Unknown'
try: t=exceptmap[headers.dict['bobo-exception-type']]
except:
if ec >= 400 and ec < 500: t=NotFound
elif ec == 503: t=NotAvailable
else: t=ServerError
raise t, RemoteException(t,v,f,l,self.url,query,ec,em,response)
def _mp_call(self,kw,
type2suffix={
type(1.0): ':float',
type(1): ':int',
type(1L): ':long',
type([]): ':list',
type(()): ':tuple',
}
):
# Call a function using the file-upload protcol
# Add type markers to special values:
d={}
special_type=type2suffix.has_key
for k,v in kw.items():
if ':' not in k:
t=type(v)
if special_type(t): d['%s%s' % (k,type2suffix[t])]=v
else: d[k]=v
else: d[k]=v
rq=[('POST %s HTTP/1.0' % self.rurl),]
for n,v in self.headers.items():
rq.append('%s: %s' % (n,v))
if self.username and self.password:
c=encodestring('%s:%s' % (self.username,self.password)).replace('\012','')
rq.append('Authorization: Basic %s' % c)
rq.append(MultiPart(d).render())
rq='\r\n'.join(rq)
try:
sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((self.host,self.port))
sock.send(rq)
reply=sock.makefile('rb')
sock=None
line=reply.readline()
try:
[ver, ec, em] = line.split(None, 2)
except ValueError:
raise BadReply,'Bad reply from server: '+line
if ver[:5] != 'HTTP/':
raise BadReply,'Bad reply from server: '+line
ec=int(ec)
em=em.strip()
headers=mimetools.Message(reply,0)
response=reply.read()
finally:
if 0:
raise NotAvailable, (
RemoteException(NotAvailable,sys.exc_info()[1],
self.url,'<MultiPart Form>'))
if ec==200: return (headers,response)
self.handleError('', ec, em, headers, response)
class Object:
"""Surrogate object for an object on the web"""
username=None
password=None
method=None
timeout=None
special_methods= 'GET','POST','PUT'
def __init__(self, url,
method=None,username=None,password=None,
timeout=None,
**headers):
self.url=url
self.headers=headers
if not headers.has_key('Host') and not headers.has_key('host'):
headers['Host']=urlparse(url)[1]
if method is not None: self.method=method
if username is not None: self.username=username
if password is not None: self.password=password
if timeout is not None: self.timeout=timeout
def __getattr__(self, name):
if name in self.special_methods:
method=name
url=self.url
else:
method=self.method
url="%s/%s" % (self.url, name)
f=Function(url,
method=method,
username=self.username,
password=self.password,
timeout=self.timeout)
f.headers=self.headers
return f
def call(url,username=None, password=None, **kw):
return apply(Function(url,username=username, password=password), (), kw)
##############################################################################
# Implementation details below here
urlregex=re.compile(r'http://([^:/]+)(:[0-9]+)?(/.+)?', re.I)
dashtrans=maketrans('_','-')
def marshal_float(n,f): return '%s:float=%s' % (n,f)
def marshal_int(n,f): return '%s:int=%s' % (n,f)
def marshal_long(n,f):
value = '%s:long=%s' % (n, f)
if value[-1] == 'L':
value = value[:-1]
return value
def marshal_list(n,l,tname='list', lt=type([]), tt=type(())):
r=[]
for v in l:
t=type(v)
if t is lt or t is tt:
raise TypeError, 'Invalid recursion in data to be marshaled.'
r.append(marshal_whatever("%s:%s" % (n,tname) ,v))
return '&'.join(r)
def marshal_tuple(n,l):
return marshal_list(n,l,'tuple')
type2marshal={
type(1.0): marshal_float,
type(1): marshal_int,
type(1L): marshal_long,
type([]): marshal_list,
type(()): marshal_tuple,
}
def marshal_whatever(k,v):
try: q=type2marshal[type(v)](k,v)
except KeyError: q='%s=%s' % (k,quote(str(v)))
return q
def querify(items):
query=[]
for k,v in items: query.append(marshal_whatever(k,v))
return query and '&'.join(query) or ''
NotFound ='bci.NotFound'
InternalError='bci.InternalError'
BadRequest ='bci.BadRequest'
Unauthorized ='bci.Unauthorized'
ServerError ='bci.ServerError'
NotAvailable ='bci.NotAvailable'
exceptmap ={'AttributeError' :AttributeError,
'BadRequest' :BadRequest,
'EOFError' :EOFError,
'IOError' :IOError,
'ImportError' :ImportError,
'IndexError' :IndexError,
'InternalError' :InternalError,
'KeyError' :KeyError,
'MemoryError' :MemoryError,
'NameError' :NameError,
'NotAvailable' :NotAvailable,
'NotFound' :NotFound,
'OverflowError' :OverflowError,
'RuntimeError' :RuntimeError,
'ServerError' :ServerError,
'SyntaxError' :SyntaxError,
'SystemError' :SystemError,
'SystemExit' :SystemExit,
'TypeError' :TypeError,
'Unauthorized' :Unauthorized,
'ValueError' :ValueError,
'ZeroDivisionError':ZeroDivisionError}
class RemoteException:
def __init__(self,etype=None,evalue=None,efile=None,eline=None,url=None,
query=None,http_code=None,http_msg=None, http_resp=None):
"""Contains information about an exception which
occurs in a remote method call"""
self.exc_type =etype
self.exc_value =evalue
self.exc_file =efile
self.exc_line =eline
self.url =url
self.query =query
self.http_code =http_code
self.http_message=http_msg
self.response =http_resp
def __repr__(self):
return '%s (File: %s Line: %s)\n%s %s for %s' % (
self.exc_value,self.exc_file,self.exc_line,
self.http_code,self.http_message,self.url)
class MultiPart:
def __init__(self,*args):
c=len(args)
if c==1: name,val=None,args[0]
elif c==2: name,val=args[0],args[1]
else: raise ValueError, 'Invalid arguments'
h={'Content-Type': {'_v':''},
'Content-Transfer-Encoding': {'_v':''},
'Content-Disposition': {'_v':''},}
dt=type(val)
b=t=None
if dt==DictType:
t=1
b=self.boundary()
d=[]
h['Content-Type']['_v']='multipart/form-data; boundary=%s' % b
for n,v in val.items():
d.append(MultiPart(n,v))
elif (dt==ListType) or (dt==TupleType):
raise ValueError, 'Sorry, nested multipart is not done yet!'
elif dt==FileType or hasattr(val,'read'):
if hasattr(val,'name'):
fn=val.name.replace( '\\', '/')
fn=fn[(fn.rfind('/')+1):]
ex=(fn[(fn.rfind('.')+1):]).lower()
if self._extmap.has_key(ex):
ct=self._extmap[ex]
else:
ct=self._extmap['']
else:
fn=''
ct=self._extmap[None]
if self._encmap.has_key(ct): ce=self._encmap[ct]
else: ce=''
h['Content-Disposition']['_v'] ='form-data'
h['Content-Disposition']['name'] ='"%s"' % name
h['Content-Disposition']['filename']='"%s"' % fn
h['Content-Transfer-Encoding']['_v']=ce
h['Content-Type']['_v'] =ct
d=[]
l=val.read(8192)
while l:
d.append(l)
l=val.read(8192)
else:
h['Content-Disposition']['_v']='form-data'
h['Content-Disposition']['name']='"%s"' % name
d=[str(val)]
self._headers =h
self._data =d
self._boundary=b
self._top =t
def boundary(self):
return '%s_%s_%s' % (int(time()), getpid(), int(random()*1000000000))
def render(self):
h=self._headers
s=[]
if self._top:
for n,v in h.items():
if v['_v']:
s.append('%s: %s' % (n,v['_v']))
for k in v.keys():
if k != '_v': s.append('; %s=%s' % (k, v[k]))
s.append('\r\n')
p=[]
t=[]
b=self._boundary
for d in self._data: p.append(d.render())
t.append('--%s\n' % b)
t.append(('\n--%s\n' % b).join(p))
t.append('\n--%s--\n' % b)
t=''.join(t)
s.append('Content-Length: %s\r\n\r\n' % len(t))
s.append(t)
return ''.join(s)
else:
for n,v in h.items():
if v['_v']:
s.append('%s: %s' % (n,v['_v']))
for k in v.keys():
if k != '_v': s.append('; %s=%s' % (k, v[k]))
s.append('\r\n')
s.append('\r\n')
if self._boundary:
p=[]
b=self._boundary
for d in self._data: p.append(d.render())
s.append('--%s\n' % b)
s.append(('\n--%s\n' % b).join(p))
s.append('\n--%s--\n' % b)
return ''.join(s)
else:
return ''.join(s+self._data)
_extmap={'': 'text/plain',
'rdb': 'text/plain',
'html': 'text/html',
'dtml': 'text/html',
'htm': 'text/html',
'dtm': 'text/html',
'gif': 'image/gif',
'jpg': 'image/jpeg',
'exe': 'application/octet-stream',
None : 'application/octet-stream',
}
_encmap={'image/gif': 'binary',
'image/jpg': 'binary',
'application/octet-stream': 'binary',
}
def ErrorTypes(code):
if code >= 400 and code < 500: return NotFound
if code >= 500 and code < 600: return ServerError
return 'HTTP_Error_%s' % code
usage="""
Usage: %s [-u username:password] url [name=value ...]
where url is the web resource to call.
The -u option may be used to provide a user name and password.
Optional arguments may be provides as name=value pairs.
In a name value pair, if a name ends in ":file", then the value is
treated as a file name and the file is send using the file-upload
protocol. If the file name is "-", then data are taken from standard
input.
The body of the response is written to standard output.
The headers of the response are written to standard error.
""" % sys.argv[0]
def main():
import getopt
user=None
try:
optlist, args = getopt.getopt(sys.argv[1:],'u:')
url=args[0]
u =filter(lambda o: o[0]=='-u', optlist)
if u:
[user, pw] = u[0][1].split(':')
kw={}
for arg in args[1:]:
[name,v]=arg.split('=')
if name[-5:]==':file':
name=name[:-5]
if v=='-': v=sys.stdin
else: v=open(v, 'rb')
kw[name]=v
except:
print usage
sys.exit(1)
# The "main" program for this module
f=Function(url)
if user: f.username, f.password = user, pw
headers, body = apply(f,(),kw)
sys.stderr.write(''.join(map(lambda h: "%s: %s\n" % h, headers.items()))
+"\n\n")
print body
if __name__ == "__main__":
main()
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
############################################################################## ##############################################################################
import re import re
from types import ListType, TupleType, UnicodeType
from DateTime import DateTime from DateTime import DateTime
from DateTime.interfaces import SyntaxError from DateTime.interfaces import SyntaxError
from cgi import escape from cgi import escape
...@@ -20,89 +19,106 @@ from cgi import escape ...@@ -20,89 +19,106 @@ from cgi import escape
# This may get overwritten during configuration # This may get overwritten during configuration
default_encoding = 'utf-8' default_encoding = 'utf-8'
def field2string(v): def field2string(v):
if hasattr(v,'read'): return v.read() if hasattr(v, 'read'):
elif isinstance(v,UnicodeType): return v.read()
elif isinstance(v, unicode):
return v.encode(default_encoding) return v.encode(default_encoding)
else: else:
return str(v) return str(v)
def field2text(v, nl=re.compile('\r\n|\n\r').search): def field2text(v, nl=re.compile('\r\n|\n\r').search):
v = field2string(v) v = field2string(v)
mo = nl(v) mo = nl(v)
if mo is None: return v if mo is None:
return v
l = mo.start(0) l = mo.start(0)
r=[] r = []
s=0 s = 0
while l >= s: while l >= s:
r.append(v[s:l]) r.append(v[s:l])
s=l+2 s = l + 2
mo=nl(v,s) mo = nl(v, s)
if mo is None: l=-1 if mo is None:
else: l=mo.start(0) l = -1
else:
l = mo.start(0)
r.append(v[s:]) r.append(v[s:])
return '\n'.join(r) return '\n'.join(r)
def field2required(v): def field2required(v):
v = field2string(v) v = field2string(v)
if v.strip(): return v if v.strip():
raise ValueError, 'No input for required field<p>' return v
raise ValueError('No input for required field<p>')
def field2int(v): def field2int(v):
if isinstance(v, (ListType, TupleType)): if isinstance(v, (list, tuple)):
return map(field2int, v) return map(field2int, v)
v = field2string(v) v = field2string(v)
if v: if v:
try: return int(v) try:
return int(v)
except ValueError: except ValueError:
raise ValueError, ( raise ValueError(
"An integer was expected in the value %s" % escape(`v`) "An integer was expected in the value %r" % escape(v)
) )
raise ValueError, 'Empty entry when <strong>integer</strong> expected' raise ValueError('Empty entry when <strong>integer</strong> expected')
def field2float(v): def field2float(v):
if isinstance(v, (ListType, TupleType)): if isinstance(v, (list, tuple)):
return map(field2float, v) return map(field2float, v)
v = field2string(v) v = field2string(v)
if v: if v:
try: return float(v) try:
return float(v)
except ValueError: except ValueError:
raise ValueError, ( raise ValueError(
"A floating-point number was expected in the value %s" % "A floating-point number was expected in the value %r" %
escape(`v`) escape(v)
) )
raise ValueError, ( raise ValueError(
'Empty entry when <strong>floating-point number</strong> expected') 'Empty entry when <strong>floating-point number</strong> expected')
def field2long(v): def field2long(v):
if isinstance(v, (ListType, TupleType)): if isinstance(v, (list, tuple)):
return map(field2long, v) return map(field2long, v)
v = field2string(v) v = field2string(v)
# handle trailing 'L' if present. # handle trailing 'L' if present.
if v[-1:] in ('L', 'l'): if v[-1:] in ('L', 'l'):
v = v[:-1] v = v[:-1]
if v: if v:
try: return long(v) try:
return int(v)
except ValueError: except ValueError:
raise ValueError, ( raise ValueError(
"A long integer was expected in the value %s" % escape(`v`) "A long integer was expected in the value %r" % escape(v)
) )
raise ValueError, 'Empty entry when <strong>integer</strong> expected' raise ValueError('Empty entry when <strong>integer</strong> expected')
def field2tokens(v): def field2tokens(v):
v = field2string(v) v = field2string(v)
return v.split() return v.split()
def field2lines(v): def field2lines(v):
if isinstance(v, (ListType, TupleType)): if isinstance(v, (list, tuple)):
result=[] result = []
for item in v: for item in v:
result.append(str(item)) result.append(str(item))
return result return result
return field2text(v).splitlines() return field2text(v).splitlines()
def field2date(v): def field2date(v):
v = field2string(v) v = field2string(v)
try: try:
...@@ -111,6 +127,7 @@ def field2date(v): ...@@ -111,6 +127,7 @@ def field2date(v):
raise SyntaxError("Invalid DateTime " + escape(repr(v))) raise SyntaxError("Invalid DateTime " + escape(repr(v)))
return v return v
def field2date_international(v): def field2date_international(v):
v = field2string(v) v = field2string(v)
try: try:
...@@ -119,46 +136,57 @@ def field2date_international(v): ...@@ -119,46 +136,57 @@ def field2date_international(v):
raise SyntaxError("Invalid DateTime " + escape(repr(v))) raise SyntaxError("Invalid DateTime " + escape(repr(v)))
return v return v
def field2boolean(v): def field2boolean(v):
if v == 'False': if v == 'False':
return not 1 return not 1
return not not v return not not v
class _unicode_converter: class _unicode_converter:
def __call__(self,v):
# Convert a regular python string. This probably doesnt do what you want, def __call__(self, v):
# whatever that might be. If you are getting exceptions below, you # Convert a regular python string. This probably doesn't do
# probably missed the encoding tag from a form field name. Use: # what you want, whatever that might be. If you are getting
# exceptions below, you probably missed the encoding tag
# from a form field name. Use:
# <input name="description:utf8:ustring" ..... # <input name="description:utf8:ustring" .....
# rather than # rather than
# <input name="description:ustring" ..... # <input name="description:ustring" .....
if hasattr(v,'read'): v=v.read() if hasattr(v, 'read'):
v = v.read()
v = unicode(v) v = unicode(v)
return self.convert_unicode(v) return self.convert_unicode(v)
def convert_unicode(self,v): def convert_unicode(self, v):
raise NotImplementedError('convert_unicode') raise NotImplementedError('convert_unicode')
class field2ustring(_unicode_converter): class field2ustring(_unicode_converter):
def convert_unicode(self,v): def convert_unicode(self, v):
return v return v
field2ustring = field2ustring() field2ustring = field2ustring()
class field2utokens(_unicode_converter): class field2utokens(_unicode_converter):
def convert_unicode(self,v): def convert_unicode(self, v):
return v.split() return v.split()
field2utokens = field2utokens() field2utokens = field2utokens()
class field2utext(_unicode_converter): class field2utext(_unicode_converter):
def convert_unicode(self,v): def convert_unicode(self, v):
return unicode(field2text(v.encode('utf8')),'utf8') return unicode(field2text(v.encode('utf8')), 'utf8')
field2utext = field2utext() field2utext = field2utext()
class field2ulines: class field2ulines:
def __call__(self, v): def __call__(self, v):
if hasattr(v,'read'): if hasattr(v, 'read'):
v=v.read() v = v.read()
if isinstance(v, (ListType, TupleType)): if isinstance(v, (list, tuple)):
return [field2ustring(x) for x in v] return [field2ustring(x) for x in v]
v = unicode(v) v = unicode(v)
return self.convert_unicode(v) return self.convert_unicode(v)
...@@ -184,6 +212,6 @@ type_converters = { ...@@ -184,6 +212,6 @@ type_converters = {
'utokens': field2utokens, 'utokens': field2utokens,
'ulines': field2ulines, 'ulines': field2ulines,
'utext': field2utext, 'utext': field2utext,
} }
get_converter=type_converters.get get_converter = type_converters.get
...@@ -19,11 +19,13 @@ flag-interface and some support functions for implementing this functionality. ...@@ -19,11 +19,13 @@ flag-interface and some support functions for implementing this functionality.
For an implementation example, see the File class in OFS/Image.py. For an implementation example, see the File class in OFS/Image.py.
""" """
import re, sys import re
import sys
from zope.interface import Interface from zope.interface import Interface
WHITESPACE = re.compile('\s*', re.MULTILINE) WHITESPACE = re.compile('\s*', re.MULTILINE)
def parseRange(header): def parseRange(header):
"""RFC 2616 (HTTP 1.1) Range header parsing. """RFC 2616 (HTTP 1.1) Range header parsing.
...@@ -32,7 +34,6 @@ def parseRange(header): ...@@ -32,7 +34,6 @@ def parseRange(header):
end offset to be inclusive, we return python convention indexes, where the end offset to be inclusive, we return python convention indexes, where the
end is exclusive. Syntactically incorrect headers are to be ignored, so if end is exclusive. Syntactically incorrect headers are to be ignored, so if
we encounter one we return None. we encounter one we return None.
""" """
ranges = [] ranges = []
...@@ -43,8 +44,11 @@ def parseRange(header): ...@@ -43,8 +44,11 @@ def parseRange(header):
header = WHITESPACE.sub('', header) header = WHITESPACE.sub('', header)
# A range header only can specify a byte range # A range header only can specify a byte range
try: spec, sets = header.split('=') try:
except ValueError: return None spec, sets = header.split('=')
except ValueError:
return None
if spec != 'bytes': if spec != 'bytes':
return None return None
...@@ -57,8 +61,10 @@ def parseRange(header): ...@@ -57,8 +61,10 @@ def parseRange(header):
return None return None
for set in sets: for set in sets:
try: start, end = set.split('-') try:
except ValueError: return None start, end = set.split('-')
except ValueError:
return None
# Catch empty sets # Catch empty sets
if not start and not end: if not start and not end:
...@@ -67,10 +73,14 @@ def parseRange(header): ...@@ -67,10 +73,14 @@ def parseRange(header):
# Convert to integers or None (which will raise errors if # Convert to integers or None (which will raise errors if
# non-integers were used (which is what we want)). # non-integers were used (which is what we want)).
try: try:
if start == '': start = None if start == '':
else: start = int(start) start = None
if end == '': end = None else:
else: end = int(end) start = int(start)
if end == '':
end = None
else:
end = int(end)
except ValueError: except ValueError:
return None return None
...@@ -94,11 +104,11 @@ def parseRange(header): ...@@ -94,11 +104,11 @@ def parseRange(header):
return ranges return ranges
def expandRanges(ranges, size): def expandRanges(ranges, size):
"""Expand Range sets, given those sets and the length of the resource. """Expand Range sets, given those sets and the length of the resource.
Expansion means relative start values and open ends Expansion means relative start values and open ends
""" """
expanded = [] expanded = []
...@@ -107,13 +117,15 @@ def expandRanges(ranges, size): ...@@ -107,13 +117,15 @@ def expandRanges(ranges, size):
if start < 0: if start < 0:
start = size + start start = size + start
end = end or size end = end or size
if end > size: end = size if end > size:
end = size
# Only use satisfiable ranges # Only use satisfiable ranges
if start < size: if start < size:
add((start, end)) add((start, end))
return expanded return expanded
class HTTPRangeInterface(Interface): class HTTPRangeInterface(Interface):
"""Objects implementing this Interface support the HTTP Range header. """Objects implementing this Interface support the HTTP Range header.
...@@ -124,5 +136,4 @@ class HTTPRangeInterface(Interface): ...@@ -124,5 +136,4 @@ class HTTPRangeInterface(Interface):
This interface specifies no methods, as this functionality can either be This interface specifies no methods, as this functionality can either be
implemented in the index_html or __call__ methods of a published object. implemented in the index_html or __call__ methods of a published object.
""" """
...@@ -71,21 +71,23 @@ _gzip_header = ("\037\213" # magic ...@@ -71,21 +71,23 @@ _gzip_header = ("\037\213" # magic
"\002" "\002"
"\377") "\377")
uncompressableMimeMajorTypes = ('image',) # these mime major types should # these mime major types should not be gzip content encoded
# not be gzip content encoded uncompressableMimeMajorTypes = ('image',)
# The environment variable DONT_GZIP_MAJOR_MIME_TYPES can be set to a list # The environment variable DONT_GZIP_MAJOR_MIME_TYPES can be set to a list
# of comma seperated mime major types which should also not be compressed # of comma seperated mime major types which should also not be compressed
otherTypes = os.environ.get('DONT_GZIP_MAJOR_MIME_TYPES','').lower() otherTypes = os.environ.get('DONT_GZIP_MAJOR_MIME_TYPES', '').lower()
if otherTypes: if otherTypes:
uncompressableMimeMajorTypes += tuple(otherTypes.split(',')) uncompressableMimeMajorTypes += tuple(otherTypes.split(','))
_CRLF = re.compile(r'[\r\n]') _CRLF = re.compile(r'[\r\n]')
def _scrubHeader(name, value): def _scrubHeader(name, value):
return ''.join(_CRLF.split(str(name))), ''.join(_CRLF.split(str(value))) return ''.join(_CRLF.split(str(name))), ''.join(_CRLF.split(str(value)))
class HTTPResponse(BaseResponse): class HTTPResponse(BaseResponse):
""" An object representation of an HTTP response. """ An object representation of an HTTP response.
...@@ -104,7 +106,7 @@ class HTTPResponse(BaseResponse): ...@@ -104,7 +106,7 @@ class HTTPResponse(BaseResponse):
If stream oriented output is used, then the response object If stream oriented output is used, then the response object
passed into the object must be used. passed into the object must be used.
""" #' """
body = '' body = ''
base = '' base = ''
...@@ -124,8 +126,7 @@ class HTTPResponse(BaseResponse): ...@@ -124,8 +126,7 @@ class HTTPResponse(BaseResponse):
status=200, status=200,
headers=None, headers=None,
stdout=sys.stdout, stdout=sys.stdout,
stderr=sys.stderr, stderr=sys.stderr):
):
""" Create a new response using the given values. """ Create a new response using the given values.
""" """
if headers is None: if headers is None:
...@@ -166,8 +167,8 @@ class HTTPResponse(BaseResponse): ...@@ -166,8 +167,8 @@ class HTTPResponse(BaseResponse):
# It has already been determined. # It has already been determined.
return return
if (isinstance(status, (type, types.ClassType)) if (isinstance(status, (type, types.ClassType)) and
and issubclass(status, Exception)): issubclass(status, Exception)):
status = status.__name__ status = status.__name__
if isinstance(status, str): if isinstance(status, str):
...@@ -304,7 +305,7 @@ class HTTPResponse(BaseResponse): ...@@ -304,7 +305,7 @@ class HTTPResponse(BaseResponse):
h = "%s%s%s" % (h, delimiter, value) h = "%s%s%s" % (h, delimiter, value)
else: else:
h = value h = value
self.setHeader(name,h, scrubbed=True) self.setHeader(name, h, scrubbed=True)
def addHeader(self, name, value): def addHeader(self, name, value):
""" Set a new HTTP return header with the given value, """ Set a new HTTP return header with the given value,
...@@ -334,8 +335,7 @@ class HTTPResponse(BaseResponse): ...@@ -334,8 +335,7 @@ class HTTPResponse(BaseResponse):
self.base = str(base) self.base = str(base)
def insertBase(self, def insertBase(self,
base_re_search=re.compile('(<base.*?>)',re.I).search base_re_search=re.compile('(<base.*?>)', re.I).search):
):
# Only insert a base tag if content appears to be html. # Only insert a base tag if content appears to be html.
content_type = self.headers.get('content-type', '').split(';')[0] content_type = self.headers.get('content-type', '').split(';')[0]
...@@ -370,9 +370,8 @@ class HTTPResponse(BaseResponse): ...@@ -370,9 +370,8 @@ class HTTPResponse(BaseResponse):
latin1_alias_match=re.compile( latin1_alias_match=re.compile(
r'text/html(\s*;\s*charset=((latin)|(latin[-_]?1)|' r'text/html(\s*;\s*charset=((latin)|(latin[-_]?1)|'
r'(cp1252)|(cp819)|(csISOLatin1)|(IBM819)|(iso-ir-100)|' r'(cp1252)|(cp819)|(csISOLatin1)|(IBM819)|(iso-ir-100)|'
r'(iso[-_]8859[-_]1(:1987)?)))?$',re.I).match, r'(iso[-_]8859[-_]1(:1987)?)))?$', re.I).match,
lock=None lock=None):
):
""" Set the body of the response """ Set the body of the response
Sets the return body equal to the (string) argument "body". Also Sets the return body equal to the (string) argument "body". Also
...@@ -411,7 +410,7 @@ class HTTPResponse(BaseResponse): ...@@ -411,7 +410,7 @@ class HTTPResponse(BaseResponse):
title, body = body title, body = body
if not isinstance(body, str): if not isinstance(body, str):
if hasattr(body,'asHTML'): if hasattr(body, 'asHTML'):
body = body.asHTML() body = body.asHTML()
if isinstance(body, unicode): if isinstance(body, unicode):
...@@ -425,7 +424,7 @@ class HTTPResponse(BaseResponse): ...@@ -425,7 +424,7 @@ class HTTPResponse(BaseResponse):
body = self._encode_unicode(unicode(body)) body = self._encode_unicode(unicode(body))
l = len(body) l = len(body)
if ((l < 200) and body[:1] == '<' and body.find('>') == l-1 and if ((l < 200) and body[:1] == '<' and body.find('>') == l - 1 and
bogus_str_search(body) is not None): bogus_str_search(body) is not None):
self.notFoundError(body[1:-1]) self.notFoundError(body[1:-1])
else: else:
...@@ -466,25 +465,25 @@ class HTTPResponse(BaseResponse): ...@@ -466,25 +465,25 @@ class HTTPResponse(BaseResponse):
self.insertBase() self.insertBase()
if self.use_HTTP_content_compression and \ if (self.use_HTTP_content_compression and
self.headers.get('content-encoding', 'gzip') == 'gzip': self.headers.get('content-encoding', 'gzip') == 'gzip'):
# use HTTP content encoding to compress body contents unless # use HTTP content encoding to compress body contents unless
# this response already has another type of content encoding # this response already has another type of content encoding
if content_type.split('/')[0] not in uncompressableMimeMajorTypes: if content_type.split('/')[0] not in uncompressableMimeMajorTypes:
# only compress if not listed as uncompressable # only compress if not listed as uncompressable
body = self.body body = self.body
startlen = len(body) startlen = len(body)
co = zlib.compressobj(6,zlib.DEFLATED,-zlib.MAX_WBITS, co = zlib.compressobj(6, zlib.DEFLATED, -zlib.MAX_WBITS,
zlib.DEF_MEM_LEVEL,0) zlib.DEF_MEM_LEVEL, 0)
chunks = [_gzip_header, co.compress(body), chunks = [_gzip_header, co.compress(body),
co.flush(), co.flush(),
struct.pack("<ll",zlib.crc32(body),startlen)] struct.pack("<ll", zlib.crc32(body), startlen)]
z = "".join(chunks) z = "".join(chunks)
newlen = len(z) newlen = len(z)
if newlen < startlen: if newlen < startlen:
self.body = z self.body = z
self.setHeader('content-length', newlen) self.setHeader('content-length', newlen)
self.setHeader('content-encoding','gzip') self.setHeader('content-encoding', 'gzip')
if self.use_HTTP_content_compression == 1: if self.use_HTTP_content_compression == 1:
# use_HTTP_content_compression == 1 if force was # use_HTTP_content_compression == 1 if force was
# NOT used in enableHTTPCompression(). # NOT used in enableHTTPCompression().
...@@ -542,7 +541,7 @@ class HTTPResponse(BaseResponse): ...@@ -542,7 +541,7 @@ class HTTPResponse(BaseResponse):
self.use_HTTP_content_compression = 0 self.use_HTTP_content_compression = 0
elif (force or elif (force or
(REQUEST.get('HTTP_ACCEPT_ENCODING','').find('gzip') != -1)): (REQUEST.get('HTTP_ACCEPT_ENCODING', '').find('gzip') != -1)):
if force: if force:
self.use_HTTP_content_compression = 2 self.use_HTTP_content_compression = 2
else: else:
...@@ -574,7 +573,7 @@ class HTTPResponse(BaseResponse): ...@@ -574,7 +573,7 @@ class HTTPResponse(BaseResponse):
""" """
return self._shutdown_flag is not None return self._shutdown_flag is not None
def _encode_unicode(self,body, def _encode_unicode(self, body,
charset_re=re.compile( charset_re=re.compile(
r'(?:application|text)/[-+0-9a-z]+\s*;\s*' + r'(?:application|text)/[-+0-9a-z]+\s*;\s*' +
r'charset=([-_0-9a-z]+' + r'charset=([-_0-9a-z]+' +
...@@ -587,8 +586,8 @@ class HTTPResponse(BaseResponse): ...@@ -587,8 +586,8 @@ class HTTPResponse(BaseResponse):
if body.startswith('<?xml'): if body.startswith('<?xml'):
pos_right = body.find('?>') # right end of the XML preamble pos_right = body.find('?>') # right end of the XML preamble
body = ('<?xml version="1.0" encoding="%s" ?>' body = ('<?xml version="1.0" encoding="%s" ?>' %
% encoding) + body[pos_right+2:] encoding) + body[pos_right + 2:]
return body return body
# Encode the Unicode data as requested # Encode the Unicode data as requested
...@@ -603,8 +602,8 @@ class HTTPResponse(BaseResponse): ...@@ -603,8 +602,8 @@ class HTTPResponse(BaseResponse):
return body return body
else: else:
if ct.startswith('text/') or ct.startswith('application/'): if ct.startswith('text/') or ct.startswith('application/'):
self.headers['content-type'] = '%s; charset=%s' % (ct, self.headers['content-type'] = '%s; charset=%s' % (
default_encoding) ct, default_encoding)
# Use the default character encoding # Use the default character encoding
body = body.encode(default_encoding, 'replace') body = body.encode(default_encoding, 'replace')
...@@ -619,12 +618,11 @@ class HTTPResponse(BaseResponse): ...@@ -619,12 +618,11 @@ class HTTPResponse(BaseResponse):
tb = format_exception(t, v, tb, as_html=as_html) tb = format_exception(t, v, tb, as_html=as_html)
return '\n'.join(tb) return '\n'.join(tb)
def _html(self, title, body):
def _html(self,title,body):
return ("<html>\n" return ("<html>\n"
"<head>\n<title>%s</title>\n</head>\n" "<head>\n<title>%s</title>\n</head>\n"
"<body>\n%s\n</body>\n" "<body>\n%s\n</body>\n"
"</html>\n" % (title,body)) "</html>\n" % (title, body))
def _error_html(self, title, body): def _error_html(self, title, body):
return ("""<html> return ("""<html>
...@@ -635,8 +633,7 @@ class HTTPResponse(BaseResponse): ...@@ -635,8 +633,7 @@ class HTTPResponse(BaseResponse):
</p> </p>
<p><strong>%s</strong></p> <p><strong>%s</strong></p>
%s""" % (title, body) + \ %s""" % (title, body) + """
"""
<hr noshade="noshade"/> <hr noshade="noshade"/>
<p>Troubleshooting Suggestions</p> <p>Troubleshooting Suggestions</p>
...@@ -652,7 +649,7 @@ class HTTPResponse(BaseResponse): ...@@ -652,7 +649,7 @@ class HTTPResponse(BaseResponse):
Thank you for your patience. Thank you for your patience.
</p></body></html>""") </p></body></html>""")
def notFoundError(self,entry='Unknown'): def notFoundError(self, entry='Unknown'):
self.setStatus(404) self.setStatus(404)
raise NotFound(self._error_html( raise NotFound(self._error_html(
"Resource not found", "Resource not found",
...@@ -660,18 +657,18 @@ class HTTPResponse(BaseResponse): ...@@ -660,18 +657,18 @@ class HTTPResponse(BaseResponse):
"<p>Check the URL and try again.</p>" + "<p>Check the URL and try again.</p>" +
"<p><b>Resource:</b> %s</p>" % escape(entry))) "<p><b>Resource:</b> %s</p>" % escape(entry)))
forbiddenError = notFoundError # If a resource is forbidden, # If a resource is forbidden, why reveal that it exists?
# why reveal that it exists? forbiddenError = notFoundError
def debugError(self,entry): def debugError(self, entry):
raise NotFound(self._error_html( raise NotFound(self._error_html(
"Debugging Notice", "Debugging Notice",
"Zope has encountered a problem publishing your object.<p>" "Zope has encountered a problem publishing your object.<p>"
"\n%s</p>" % entry)) "\n%s</p>" % entry))
def badRequestError(self,name): def badRequestError(self, name):
self.setStatus(400) self.setStatus(400)
if re.match('^[A-Z_0-9]+$',name): if re.match('^[A-Z_0-9]+$', name):
raise InternalError(self._error_html( raise InternalError(self._error_html(
"Internal Error", "Internal Error",
"Sorry, an internal error occurred in this resource.")) "Sorry, an internal error occurred in this resource."))
...@@ -761,15 +758,14 @@ class HTTPResponse(BaseResponse): ...@@ -761,15 +758,14 @@ class HTTPResponse(BaseResponse):
else: else:
try: try:
l, b = v l, b = v
if (isinstance(l, str) if (isinstance(l, str) and absuri_match(l) is not None):
and absuri_match(l) is not None):
if self.status == 300: if self.status == 300:
self.setStatus(302) self.setStatus(302)
self.setHeader('location', l) self.setHeader('location', l)
self.setBody(b) self.setBody(b)
tb = None # one more patch covered tb = None # one more patch covered
return self return self
except: except Exception:
pass # tb is not cleared in this case pass # tb is not cleared in this case
b = v b = v
...@@ -785,8 +781,8 @@ class HTTPResponse(BaseResponse): ...@@ -785,8 +781,8 @@ class HTTPResponse(BaseResponse):
if fatal and t is SystemExit and v.code == 0: if fatal and t is SystemExit and v.code == 0:
body = self.setBody( body = self.setBody(
(str(t), (str(t),
'Zope has exited normally.<p>' 'Zope has exited normally.<p>' +
+ self._traceback(t, v, tb) + '</p>'), self._traceback(t, v, tb) + '</p>'),
is_error=1) is_error=1)
else: else:
try: try:
...@@ -796,9 +792,8 @@ class HTTPResponse(BaseResponse): ...@@ -796,9 +792,8 @@ class HTTPResponse(BaseResponse):
if match is None: if match is None:
body = self.setBody( body = self.setBody(
(str(t), (str(t),
'Sorry, a site error occurred.<p>' 'Sorry, a site error occurred.<p>' +
+ self._traceback(t, v, tb) self._traceback(t, v, tb) + '</p>'),
+ '</p>'),
is_error=1) is_error=1)
elif self.isHTML(b): elif self.isHTML(b):
# error is an HTML document, not just a snippet of html # error is an HTML document, not just a snippet of html
...@@ -809,7 +804,7 @@ class HTTPResponse(BaseResponse): ...@@ -809,7 +804,7 @@ class HTTPResponse(BaseResponse):
body = self.setBody(b, is_error=1) body = self.setBody(b, is_error=1)
else: else:
body = self.setBody( body = self.setBody(
(str(t), b + self._traceback(t,'(see above)', tb, 0)), (str(t), b + self._traceback(t, '(see above)', tb, 0)),
is_error=1) is_error=1)
del tb del tb
return body return body
...@@ -831,15 +826,15 @@ class HTTPResponse(BaseResponse): ...@@ -831,15 +826,15 @@ class HTTPResponse(BaseResponse):
for name, v in attrs.items(): for name, v in attrs.items():
name = name.lower() name = name.lower()
if name == 'expires': if name == 'expires':
cookie = '%s; Expires=%s' % (cookie,v) cookie = '%s; Expires=%s' % (cookie, v)
elif name == 'domain': elif name == 'domain':
cookie = '%s; Domain=%s' % (cookie,v) cookie = '%s; Domain=%s' % (cookie, v)
elif name == 'path': elif name == 'path':
cookie = '%s; Path=%s' % (cookie,v) cookie = '%s; Path=%s' % (cookie, v)
elif name == 'max_age': elif name == 'max_age':
cookie = '%s; Max-Age=%s' % (cookie,v) cookie = '%s; Max-Age=%s' % (cookie, v)
elif name == 'comment': elif name == 'comment':
cookie = '%s; Comment=%s' % (cookie,v) cookie = '%s; Comment=%s' % (cookie, v)
elif name == 'secure' and v: elif name == 'secure' and v:
cookie = '%s; Secure' % cookie cookie = '%s; Secure' % cookie
# Some browsers recognize this cookie attribute # Some browsers recognize this cookie attribute
...@@ -856,8 +851,8 @@ class HTTPResponse(BaseResponse): ...@@ -856,8 +851,8 @@ class HTTPResponse(BaseResponse):
""" Set headers required by various parts of protocol. """ Set headers required by various parts of protocol.
""" """
body = self.body body = self.body
if (not 'content-length' in self.headers and if ('content-length' not in self.headers and
not 'transfer-encoding' in self.headers): 'transfer-encoding' not in self.headers):
self.setHeader('content-length', len(body)) self.setHeader('content-length', len(body))
return "%d %s" % (self.status, self.errmsg), self.listHeaders() return "%d %s" % (self.status, self.errmsg), self.listHeaders()
...@@ -881,9 +876,7 @@ class HTTPResponse(BaseResponse): ...@@ -881,9 +876,7 @@ class HTTPResponse(BaseResponse):
result.extend(self.accumulated_headers) result.extend(self.accumulated_headers)
return result return result
def __str__(self, def __str__(self, html_search=re.compile('<html>', re.I).search):
html_search=re.compile('<html>',re.I).search,
):
if self._wrote: if self._wrote:
return '' # Streaming output was used. return '' # Streaming output was used.
...@@ -902,8 +895,8 @@ class HTTPResponse(BaseResponse): ...@@ -902,8 +895,8 @@ class HTTPResponse(BaseResponse):
chunks.append(body) chunks.append(body)
return '\r\n'.join(chunks) return '\r\n'.join(chunks)
def write(self,data): def write(self, data):
"""\ """
Return data as a stream Return data as a stream
HTML data may be returned using a stream-oriented interface. HTML data may be returned using a stream-oriented interface.
...@@ -915,10 +908,8 @@ class HTTPResponse(BaseResponse): ...@@ -915,10 +908,8 @@ class HTTPResponse(BaseResponse):
Note that published objects must not generate any errors Note that published objects must not generate any errors
after beginning stream-oriented output. after beginning stream-oriented output.
""" """
if not self._wrote: if not self._wrote:
notify(PubBeforeStreaming(self)) notify(PubBeforeStreaming(self))
self.outputBody() self.outputBody()
......
from zope.interface import Interface from zope.interface import Interface
from zope.interface import implements from zope.interface import implements
class IUnboundStreamIterator(Interface): class IUnboundStreamIterator(Interface):
""" """
An iterator with unknown length that can be published. An iterator with unknown length that can be published.
...@@ -25,7 +26,8 @@ class IStreamIterator(IUnboundStreamIterator): ...@@ -25,7 +26,8 @@ class IStreamIterator(IUnboundStreamIterator):
is still closed, ZODB would raise an error. If the connection is still closed, ZODB would raise an error. If the connection
happens to be re-opened by another thread, ZODB might allow it, happens to be re-opened by another thread, ZODB might allow it,
but it has a chance of going insane if it happens to be loading but it has a chance of going insane if it happens to be loading
or storing something in the other thread at the same time. """ or storing something in the other thread at the same time.
"""
def __len__(): def __len__():
""" """
...@@ -42,7 +44,7 @@ class filestream_iterator(file): ...@@ -42,7 +44,7 @@ class filestream_iterator(file):
implements(IStreamIterator) implements(IStreamIterator)
def __init__(self, name, mode='r', bufsize=-1, streamsize=1<<16): def __init__(self, name, mode='r', bufsize=-1, streamsize=1 << 16):
file.__init__(self, name, mode, bufsize) file.__init__(self, name, mode, bufsize)
self.streamsize = streamsize self.streamsize = streamsize
...@@ -57,5 +59,4 @@ class filestream_iterator(file): ...@@ -57,5 +59,4 @@ class filestream_iterator(file):
self.seek(0, 2) self.seek(0, 2)
size = self.tell() size = self.tell()
self.seek(cur_pos, 0) self.seek(cur_pos, 0)
return size return size
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
""" """
import os import os
import sys import sys
from thread import allocate_lock
import transaction import transaction
from urlparse import urlparse from urlparse import urlparse
...@@ -24,11 +25,10 @@ from zope.publisher.interfaces.browser import IBrowserPage ...@@ -24,11 +25,10 @@ from zope.publisher.interfaces.browser import IBrowserPage
from zope.publisher.skinnable import setDefaultSkin from zope.publisher.skinnable import setDefaultSkin
from zope.security.management import newInteraction, endInteraction from zope.security.management import newInteraction, endInteraction
from .mapply import mapply from ZPublisher.mapply import mapply
from .maybe_lock import allocate_lock
from ZPublisher import pubevents from ZPublisher import pubevents
from .Request import Request from ZPublisher.HTTPRequest import HTTPRequest as Request
from .Response import Response from ZPublisher.HTTPResponse import HTTPResponse as Response
class Retry(Exception): class Retry(Exception):
...@@ -36,37 +36,47 @@ class Retry(Exception): ...@@ -36,37 +36,47 @@ class Retry(Exception):
""" """
def __init__(self, t=None, v=None, tb=None): def __init__(self, t=None, v=None, tb=None):
self._args=t, v, tb self._args = t, v, tb
def reraise(self): def reraise(self):
t, v, tb = self._args t, v, tb = self._args
if t is None: t=Retry if t is None:
if tb is None: raise t, v t = Retry
try: raise t, v, tb if tb is None:
finally: tb=None raise t(v)
try:
raise t, v, tb
finally:
tb = None
def call_object(object, args, request): def call_object(object, args, request):
result=apply(object,args) # Type s<cr> to step into published object. return object(*args)
return result
def missing_name(name, request): def missing_name(name, request):
if name=='self': return request['PARENTS'][0] if name == 'self':
return request['PARENTS'][0]
request.response.badRequestError(name) request.response.badRequestError(name)
def dont_publish_class(klass, request): def dont_publish_class(klass, request):
request.response.forbiddenError("class %s" % klass.__name__) request.response.forbiddenError("class %s" % klass.__name__)
_default_debug_mode = False _default_debug_mode = False
_default_realm = None _default_realm = None
def set_default_debug_mode(debug_mode): def set_default_debug_mode(debug_mode):
global _default_debug_mode global _default_debug_mode
_default_debug_mode = debug_mode _default_debug_mode = debug_mode
def set_default_authentication_realm(realm): def set_default_authentication_realm(realm):
global _default_realm global _default_realm
_default_realm = realm _default_realm = realm
def publish(request, module_name, after_list, debug=0, def publish(request, module_name, after_list, debug=0,
# Optimize: # Optimize:
call_object=call_object, call_object=call_object,
...@@ -76,10 +86,10 @@ def publish(request, module_name, after_list, debug=0, ...@@ -76,10 +86,10 @@ def publish(request, module_name, after_list, debug=0,
): ):
(bobo_before, bobo_after, object, realm, debug_mode, err_hook, (bobo_before, bobo_after, object, realm, debug_mode, err_hook,
validated_hook, transactions_manager)= get_module_info(module_name) validated_hook, transactions_manager) = get_module_info(module_name)
parents=None parents = None
response=None response = None
try: try:
notify(pubevents.PubStart(request)) notify(pubevents.PubStart(request))
...@@ -88,8 +98,8 @@ def publish(request, module_name, after_list, debug=0, ...@@ -88,8 +98,8 @@ def publish(request, module_name, after_list, debug=0,
request.processInputs() request.processInputs()
request_get=request.get request_get = request.get
response=request.response response = request.response
# First check for "cancel" redirect: # First check for "cancel" redirect:
if request_get('SUBMIT', '').strip().lower() == 'cancel': if request_get('SUBMIT', '').strip().lower() == 'cancel':
...@@ -105,27 +115,27 @@ def publish(request, module_name, after_list, debug=0, ...@@ -105,27 +115,27 @@ def publish(request, module_name, after_list, debug=0,
cancel = '' cancel = ''
break break
if cancel: if cancel:
raise Redirect, cancel raise Redirect(cancel)
after_list[0]=bobo_after after_list[0] = bobo_after
if debug_mode: if debug_mode:
response.debug_mode=debug_mode response.debug_mode = debug_mode
if realm and not request.get('REMOTE_USER',None): if realm and not request.get('REMOTE_USER', None):
response.realm=realm response.realm = realm
if bobo_before is not None: if bobo_before is not None:
bobo_before() bobo_before()
# Get the path list. # Get the path list.
# According to RFC1738 a trailing space in the path is valid. # According to RFC1738 a trailing space in the path is valid.
path=request_get('PATH_INFO') path = request_get('PATH_INFO')
request['PARENTS']=parents=[object] request['PARENTS'] = parents = [object]
if transactions_manager: if transactions_manager:
transactions_manager.begin() transactions_manager.begin()
object=request.traverse(path, validated_hook=validated_hook) object = request.traverse(path, validated_hook=validated_hook)
if IBrowserPage.providedBy(object): if IBrowserPage.providedBy(object):
request.postProcessInputs() request.postProcessInputs()
...@@ -135,8 +145,8 @@ def publish(request, module_name, after_list, debug=0, ...@@ -135,8 +145,8 @@ def publish(request, module_name, after_list, debug=0,
if transactions_manager: if transactions_manager:
transactions_manager.recordMetaData(object, request) transactions_manager.recordMetaData(object, request)
result=mapply(object, request.args, request, result = mapply(object, request.args, request,
call_object,1, call_object, 1,
missing_name, missing_name,
dont_publish_class, dont_publish_class,
request, bind=1) request, bind=1)
...@@ -163,16 +173,16 @@ def publish(request, module_name, after_list, debug=0, ...@@ -163,16 +173,16 @@ def publish(request, module_name, after_list, debug=0,
if sm is not None: if sm is not None:
from asyncore import compact_traceback from asyncore import compact_traceback
cl,val= sys.exc_info()[:2] cl, val = sys.exc_info()[:2]
sm('%s: %s %s' % ( sm('%s: %s %s' % (
getattr(cl,'__name__',cl), val, getattr(cl, '__name__', cl), val,
debug_mode and compact_traceback()[-1] or '')) debug_mode and compact_traceback()[-1] or ''))
# debug is just used by tests (has nothing to do with debug_mode!) # debug is just used by tests (has nothing to do with debug_mode!)
if not debug and err_hook is not None: if not debug and err_hook is not None:
retry = False retry = False
if parents: if parents:
parents=parents[0] parents = parents[0]
try: try:
try: try:
return err_hook(parents, request, return err_hook(parents, request,
...@@ -189,7 +199,8 @@ def publish(request, module_name, after_list, debug=0, ...@@ -189,7 +199,8 @@ def publish(request, module_name, after_list, debug=0,
) )
retry = True retry = True
finally: finally:
# Note: 'abort's can fail. Nevertheless, we want end request handling # Note: 'abort's can fail.
# Nevertheless, we want end request handling.
try: try:
try: try:
notify(pubevents.PubBeforeAbort( notify(pubevents.PubBeforeAbort(
...@@ -202,7 +213,7 @@ def publish(request, module_name, after_list, debug=0, ...@@ -202,7 +213,7 @@ def publish(request, module_name, after_list, debug=0,
notify(pubevents.PubFailure(request, exc_info, retry)) notify(pubevents.PubFailure(request, exc_info, retry))
# Only reachable if Retry is raised and request supports retry. # Only reachable if Retry is raised and request supports retry.
newrequest=request.retry() newrequest = request.retry()
request.close() # Free resources held by the request. request.close() # Free resources held by the request.
# Set the default layer/skin on the newly generated request # Set the default layer/skin on the newly generated request
...@@ -214,7 +225,8 @@ def publish(request, module_name, after_list, debug=0, ...@@ -214,7 +225,8 @@ def publish(request, module_name, after_list, debug=0,
newrequest.close() newrequest.close()
else: else:
# Note: 'abort's can fail. Nevertheless, we want end request handling # Note: 'abort's can fail.
# Nevertheless, we want end request handling.
try: try:
try: try:
notify(pubevents.PubBeforeAbort(request, exc_info, False)) notify(pubevents.PubBeforeAbort(request, exc_info, False))
...@@ -226,24 +238,26 @@ def publish(request, module_name, after_list, debug=0, ...@@ -226,24 +238,26 @@ def publish(request, module_name, after_list, debug=0,
notify(pubevents.PubFailure(request, exc_info, False)) notify(pubevents.PubFailure(request, exc_info, False))
raise raise
def publish_module_standard(module_name,
def publish_module_standard(
module_name,
stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr,
environ=os.environ, debug=0, request=None, response=None): environ=os.environ, debug=0, request=None, response=None):
must_die=0 must_die = 0
status=200 status = 200
after_list=[None] after_list = [None]
try: try:
try: try:
if response is None: if response is None:
response=Response(stdout=stdout, stderr=stderr) response = Response(stdout=stdout, stderr=stderr)
else: else:
stdout=response.stdout stdout = response.stdout
# debug is just used by tests (has nothing to do with debug_mode!) # debug is just used by tests (has nothing to do with debug_mode!)
response.handle_errors = not debug response.handle_errors = not debug
if request is None: if request is None:
request=Request(stdin, environ, response) request = Request(stdin, environ, response)
# make sure that the request we hand over has the # make sure that the request we hand over has the
# default layer/skin set on it; subsequent code that # default layer/skin set on it; subsequent code that
...@@ -267,116 +281,129 @@ def publish_module_standard(module_name, ...@@ -267,116 +281,129 @@ def publish_module_standard(module_name,
status = response.getStatus() status = response.getStatus()
if response: if response:
outputBody=getattr(response, 'outputBody', None) outputBody = getattr(response, 'outputBody', None)
if outputBody is not None: if outputBody is not None:
outputBody() outputBody()
else: else:
response=str(response) response = str(response)
if response: stdout.write(response) if response:
stdout.write(response)
# The module defined a post-access function, call it # The module defined a post-access function, call it
if after_list[0] is not None: after_list[0]() if after_list[0] is not None:
after_list[0]()
finally: finally:
if request is not None: request.close() if request is not None:
request.close()
if must_die: if must_die:
# Try to turn exception value into an exit code. # Try to turn exception value into an exit code.
try: try:
if hasattr(must_die[1], 'code'): if hasattr(must_die[1], 'code'):
code = must_die[1].code code = must_die[1].code
else: code = int(must_die[1]) else:
code = int(must_die[1])
except: except:
code = must_die[1] and 1 or 0 code = must_die[1] and 1 or 0
if hasattr(request.response, '_requestShutdown'): if hasattr(request.response, '_requestShutdown'):
request.response._requestShutdown(code) request.response._requestShutdown(code)
try: raise must_die[0], must_die[1], must_die[2] try:
finally: must_die=None raise must_die[0], must_die[1], must_die[2]
finally:
must_die = None
return status return status
_l = allocate_lock()
_l=allocate_lock()
def get_module_info(module_name, modules={}, def get_module_info(module_name, modules={},
acquire=_l.acquire, acquire=_l.acquire,
release=_l.release, release=_l.release):
):
if module_name in modules: return modules[module_name] if module_name in modules:
return modules[module_name]
if module_name[-4:]=='.cgi': module_name=module_name[:-4] if module_name[-4:] == '.cgi':
module_name = module_name[:-4]
acquire() acquire()
tb=None tb = None
g = globals() g = globals()
try: try:
try: try:
module=__import__(module_name, g, g, ('__doc__',)) module = __import__(module_name, g, g, ('__doc__',))
# Let the app specify a realm # Let the app specify a realm
if hasattr(module,'__bobo_realm__'): if hasattr(module, '__bobo_realm__'):
realm=module.__bobo_realm__ realm = module.__bobo_realm__
elif _default_realm is not None: elif _default_realm is not None:
realm=_default_realm realm = _default_realm
else: else:
realm=module_name realm = module_name
# Check for debug mode # Check for debug mode
debug_mode=None debug_mode = None
if hasattr(module,'__bobo_debug_mode__'): if hasattr(module, '__bobo_debug_mode__'):
debug_mode=not not module.__bobo_debug_mode__ debug_mode = bool(module.__bobo_debug_mode__)
else: else:
debug_mode = _default_debug_mode debug_mode = _default_debug_mode
bobo_before = getattr(module, "__bobo_before__", None) bobo_before = getattr(module, "__bobo_before__", None)
bobo_after = getattr(module, "__bobo_after__", None) bobo_after = getattr(module, "__bobo_after__", None)
if hasattr(module,'bobo_application'): if hasattr(module, 'bobo_application'):
object=module.bobo_application object = module.bobo_application
elif hasattr(module,'web_objects'): elif hasattr(module, 'web_objects'):
object=module.web_objects object = module.web_objects
else: object=module else:
object = module
error_hook=getattr(module,'zpublisher_exception_hook', None) error_hook = getattr(module, 'zpublisher_exception_hook', None)
validated_hook=getattr(module,'zpublisher_validated_hook', None) validated_hook = getattr(module, 'zpublisher_validated_hook', None)
transactions_manager=getattr( transactions_manager = getattr(
module,'zpublisher_transactions_manager', None) module, 'zpublisher_transactions_manager', None)
if not transactions_manager: if not transactions_manager:
# Create a default transactions manager for use # Create a default transactions manager for use
# by software that uses ZPublisher and ZODB but # by software that uses ZPublisher and ZODB but
# not the rest of Zope. # not the rest of Zope.
transactions_manager = DefaultTransactionsManager() transactions_manager = DefaultTransactionsManager()
info= (bobo_before, bobo_after, object, realm, debug_mode, info = (bobo_before, bobo_after, object, realm, debug_mode,
error_hook, validated_hook, transactions_manager) error_hook, validated_hook, transactions_manager)
modules[module_name]=modules[module_name+'.cgi']=info modules[module_name] = modules[module_name + '.cgi'] = info
return info return info
except: except:
t,v,tb=sys.exc_info() t, v, tb = sys.exc_info()
v=str(v) v = str(v)
raise ImportError, (t, v), tb raise ImportError, (t, v), tb
finally: finally:
tb=None tb = None
release() release()
class DefaultTransactionsManager: class DefaultTransactionsManager:
def begin(self): def begin(self):
transaction.begin() transaction.begin()
def commit(self): def commit(self):
transaction.commit() transaction.commit()
def abort(self): def abort(self):
transaction.abort() transaction.abort()
def recordMetaData(self, object, request): def recordMetaData(self, object, request):
# Is this code needed? # Is this code needed?
request_get = request.get request_get = request.get
T= transaction.get() T = transaction.get()
T.note(request_get('PATH_INFO')) T.note(request_get('PATH_INFO'))
auth_user=request_get('AUTHENTICATED_USER',None) auth_user = request_get('AUTHENTICATED_USER', None)
if auth_user is not None: if auth_user is not None:
T.setUser(auth_user, request_get('AUTHENTICATION_PATH')) T.setUser(auth_user, request_get('AUTHENTICATION_PATH'))
...@@ -384,6 +411,6 @@ class DefaultTransactionsManager: ...@@ -384,6 +411,6 @@ class DefaultTransactionsManager:
def publish_module(module_name, def publish_module(module_name,
stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr,
environ=os.environ, debug=0, request=None, response=None): environ=os.environ, debug=0, request=None, response=None):
""" publish a Python module, with or without profiling enabled """ """ publish a Python module """
return publish_module_standard(module_name, stdin, stdout, stderr, return publish_module_standard(module_name, stdin, stdout, stderr,
environ, debug, request, response) environ, debug, request, response)
...@@ -10,6 +10,11 @@ ...@@ -10,6 +10,11 @@
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE
# #
############################################################################## ##############################################################################
import HTTPRequest
Request=HTTPRequest.HTTPRequest from zope.deferredimport import deprecated
del HTTPRequest
# BBB: Zope 5.0
deprecated(
'Please import from ZPublisher.HTTPRequest',
Request='ZPublisher.HTTPRequest:HTTPRequest',
)
...@@ -10,6 +10,11 @@ ...@@ -10,6 +10,11 @@
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE
# #
############################################################################## ##############################################################################
import HTTPResponse
Response=HTTPResponse.HTTPResponse from zope.deferredimport import deprecated
del HTTPResponse
# BBB: Zope 5.0
deprecated(
'Please import from ZPublisher.HTTPResponse',
Response='ZPublisher.HTTPResponse:HTTPResponse',
)
...@@ -11,18 +11,6 @@ ...@@ -11,18 +11,6 @@
# #
############################################################################## ##############################################################################
# This allows ZPublisher to work with embedded interpreters from zExceptions import NotFound, BadRequest, InternalError, Forbidden # NOQA
# that for some reason have no sys.argv (required by cgi.py).
import sys
if not hasattr(sys, 'argv'):
sys.argv=[]
from zExceptions import NotFound, BadRequest, InternalError, Forbidden from ZPublisher.Publish import publish_module, Retry # NOQA
from Publish import publish_module, Retry
def test(*args, **kw):
global test
import Test
test=Test.publish
return apply(test, args, kw)
...@@ -4,6 +4,7 @@ from zope.interface import Interface, Attribute ...@@ -4,6 +4,7 @@ from zope.interface import Interface, Attribute
# Publication events # Publication events
# These are events notified in 'ZPublisher.Publish.publish'. # These are events notified in 'ZPublisher.Publish.publish'.
class IPubEvent(Interface): class IPubEvent(Interface):
'''Base class for publication events. '''Base class for publication events.
...@@ -12,9 +13,11 @@ class IPubEvent(Interface): ...@@ -12,9 +13,11 @@ class IPubEvent(Interface):
''' '''
request = Attribute('The request being affected') request = Attribute('The request being affected')
class IPubStart(IPubEvent): class IPubStart(IPubEvent):
'''Event notified at the beginning of 'ZPublisher.Publish.publish'.''' '''Event notified at the beginning of 'ZPublisher.Publish.publish'.'''
class IPubEnd(IPubEvent): class IPubEnd(IPubEvent):
'''Event notified after request processing. '''Event notified after request processing.
...@@ -22,16 +25,19 @@ class IPubEnd(IPubEvent): ...@@ -22,16 +25,19 @@ class IPubEnd(IPubEvent):
itself is considered a new event. itself is considered a new event.
''' '''
class IPubSuccess(IPubEnd): class IPubSuccess(IPubEnd):
'''A successful request processing.''' '''A successful request processing.'''
class IPubFailure(IPubEnd): class IPubFailure(IPubEnd):
'''A failed request processing. '''A failed request processing.
Note: If a subscriber to 'IPubSuccess' raises an exception, Note: If a subscriber to 'IPubSuccess' raises an exception,
then 'IPubFailure' may be notified in addtion to 'IPubSuccess'. then 'IPubFailure' may be notified in addtion to 'IPubSuccess'.
''' '''
exc_info = Attribute('''The exception info as returned by 'sys.exc_info()'.''') exc_info = Attribute(
'''The exception info as returned by 'sys.exc_info()'.''')
retry = Attribute('Whether the request will be retried') retry = Attribute('Whether the request will be retried')
...@@ -44,29 +50,28 @@ class IPubBeforeCommit(IPubEvent): ...@@ -44,29 +50,28 @@ class IPubBeforeCommit(IPubEvent):
request processing is finished). request processing is finished).
""" """
class IPubBeforeAbort(IPubEvent): class IPubBeforeAbort(IPubEvent):
"""notified immediately before the transaction abort (i.e. after the main """notified immediately before the transaction abort (i.e. after the main
request processing is finished, and there was an error). request processing is finished, and there was an error).
""" """
exc_info = Attribute('''The exception info as returned by 'sys.exc_info()'.''') exc_info = Attribute(
'''The exception info as returned by 'sys.exc_info()'.''')
retry = Attribute('Whether the request will be retried') retry = Attribute('Whether the request will be retried')
class IPubBeforeStreaming(Interface): class IPubBeforeStreaming(Interface):
"""Event fired just before a streaming response is initiated, i.e. when """Event fired just before a streaming response is initiated, i.e. when
something calls response.write() for the first time. Note that this is something calls response.write() for the first time. Note that this is
carries a reference to the *response*, not the request. carries a reference to the *response*, not the request.
""" """
response = Attribute(u"The current HTTP response") response = Attribute(u"The current HTTP response")
# Exceptions
class UseTraversalDefault(Exception): class UseTraversalDefault(Exception):
"""Indicate default traversal in ``__bobo_traverse__`` """Indicate default traversal in ``__bobo_traverse__``
This exception can be raised by '__bobo_traverse__' implementations to This exception can be raised by '__bobo_traverse__' implementations to
indicate that it has no special casing for the given name and that standard indicate that it has no special casing for the given name and that standard
traversal logic should be applied. traversal logic should be applied.
""" """
...@@ -14,22 +14,26 @@ ...@@ -14,22 +14,26 @@
""" """
import zope.publisher.publish import zope.publisher.publish
def default_call_object(object, args, context): def default_call_object(object, args, context):
result=object(*args) # Type s<cr> to step into published object. result = object(*args) # Type s<cr> to step into published object.
return result return result
def default_missing_name(name, context): def default_missing_name(name, context):
raise TypeError, 'argument %s was ommitted' % name raise TypeError('argument %s was ommitted' % name)
def default_handle_class(klass, context): def default_handle_class(klass, context):
if hasattr(klass,'__init__'): if hasattr(klass, '__init__'):
f=klass.__init__.im_func f = klass.__init__.im_func
c=f.func_code c = f.func_code
names=c.co_varnames[1:c.co_argcount] names = c.co_varnames[1:c.co_argcount]
return klass, names, f.func_defaults return klass, names, f.func_defaults
else: else:
return klass, (), () return klass, (), ()
def mapply(object, positional=(), keyword={}, def mapply(object, positional=(), keyword={},
debug=None, maybe=None, debug=None, maybe=None,
missing_name=default_missing_name, missing_name=default_missing_name,
...@@ -37,7 +41,7 @@ def mapply(object, positional=(), keyword={}, ...@@ -37,7 +41,7 @@ def mapply(object, positional=(), keyword={},
context=None, bind=0, context=None, bind=0,
): ):
if hasattr(object,'__bases__'): if hasattr(object, '__bases__'):
f, names, defaults = handle_class(object, context) f, names, defaults = handle_class(object, context)
else: else:
try: try:
...@@ -50,29 +54,34 @@ def mapply(object, positional=(), keyword={}, ...@@ -50,29 +54,34 @@ def mapply(object, positional=(), keyword={},
defaults = f.func_defaults defaults = f.func_defaults
names = code.co_varnames[count:code.co_argcount] names = code.co_varnames[count:code.co_argcount]
nargs=len(names) nargs = len(names)
if positional: if positional:
positional=list(positional) positional = list(positional)
if bind and nargs and names[0]=='self': if bind and nargs and names[0] == 'self':
positional.insert(0, missing_name('self', context)) positional.insert(0, missing_name('self', context))
if len(positional) > nargs: raise TypeError, 'too many arguments' if len(positional) > nargs:
args=positional raise TypeError('too many arguments')
args = positional
else: else:
if bind and nargs and names[0]=='self': if bind and nargs and names[0] == 'self':
args=[missing_name('self', context)] args = [missing_name('self', context)]
else: else:
args=[] args = []
get=keyword.get get = keyword.get
nrequired=len(names) - (len(defaults or ())) nrequired = len(names) - (len(defaults or ()))
for index in range(len(args), len(names)): for index in range(len(args), len(names)):
name=names[index] name = names[index]
v=get(name, args) v = get(name, args)
if v is args: if v is args:
if index < nrequired: v=missing_name(name, context) if index < nrequired:
else: v=defaults[index-nrequired] v = missing_name(name, context)
else:
v = defaults[index - nrequired]
args.append(v) args.append(v)
args=tuple(args) args = tuple(args)
if debug is not None: return debug(object,args,context) if debug is not None:
else: return object(*args) return debug(object, args, context)
else:
return object(*args)
...@@ -11,9 +11,4 @@ ...@@ -11,9 +11,4 @@
# #
############################################################################## ##############################################################################
# Waaaa, I wish I didn't have to work this hard. from thread import allocate_lock # NOQA
try: from thread import allocate_lock
except:
class allocate_lock:
def acquire(*args): pass
def release(*args): pass
...@@ -9,9 +9,12 @@ for detailed time related analysis, inline request monitoring. ...@@ -9,9 +9,12 @@ for detailed time related analysis, inline request monitoring.
''' '''
from zope.interface import implements from zope.interface import implements
from interfaces import IPubStart, IPubSuccess, IPubFailure, \ from ZPublisher.interfaces import (
IPubAfterTraversal, IPubBeforeCommit, IPubBeforeAbort, \ IPubStart, IPubSuccess, IPubFailure,
IPubBeforeStreaming IPubAfterTraversal, IPubBeforeCommit, IPubBeforeAbort,
IPubBeforeStreaming,
)
class _Base(object): class _Base(object):
"""PubEvent base class.""" """PubEvent base class."""
...@@ -19,14 +22,17 @@ class _Base(object): ...@@ -19,14 +22,17 @@ class _Base(object):
def __init__(self, request): def __init__(self, request):
self.request = request self.request = request
class PubStart(_Base): class PubStart(_Base):
'''notified at the beginning of 'ZPublisher.Publish.publish'.''' '''notified at the beginning of 'ZPublisher.Publish.publish'.'''
implements(IPubStart) implements(IPubStart)
class PubSuccess(_Base): class PubSuccess(_Base):
'''notified at successful request end.''' '''notified at successful request end.'''
implements(IPubSuccess) implements(IPubSuccess)
class PubFailure(object): class PubFailure(object):
'''notified at failed request end.''' '''notified at failed request end.'''
implements(IPubFailure) implements(IPubFailure)
...@@ -44,6 +50,7 @@ class PubBeforeCommit(_Base): ...@@ -44,6 +50,7 @@ class PubBeforeCommit(_Base):
"""notified immediately before the commit.""" """notified immediately before the commit."""
implements(IPubBeforeCommit) implements(IPubBeforeCommit)
class PubBeforeAbort(_Base): class PubBeforeAbort(_Base):
"""notified immediately before an abort.""" """notified immediately before an abort."""
implements(IPubBeforeAbort) implements(IPubBeforeAbort)
...@@ -51,6 +58,7 @@ class PubBeforeAbort(_Base): ...@@ -51,6 +58,7 @@ class PubBeforeAbort(_Base):
def __init__(self, request, exc_info, retry): def __init__(self, request, exc_info, retry):
self.request, self.exc_info, self.retry = request, exc_info, retry self.request, self.exc_info, self.retry = request, exc_info, retry
class PubBeforeStreaming(object): class PubBeforeStreaming(object):
"""Notified immediately before streaming via response.write() commences """Notified immediately before streaming via response.write() commences
""" """
......
## This script requires:
## - python >= 2.4
## - zope.testbrowser
##
## The just run:
## $python generate_conflicts.py
import base64
import string
import threading
import urllib2
from zope.testbrowser.browser import Browser
# create our browser
class AuthBrowser(Browser):
def addBasicAuth(self,username,password):
self.addHeader(
'Authorization',
'Basic '+base64.encodestring(username+':'+password).strip()
)
def open(self,uri,include_server=True):
if include_server:
uri = server+uri
return Browser.open(self,uri)
browser = AuthBrowser()
# constants
server = 'http://localhost:8080'
# the following user must be able to view the management screens
# and create file objects
username = 'username'
password = 'password'
browser.addBasicAuth(username,password)
threads = 10
filename = 'conflict.txt'
filesize = 10000
hits = 5
# delete the file if it's already there
browser.open('/manage_main')
if filename in [c.optionValue
for c in browser.getControl(name='ids:list').controls]:
browser.open('/manage_delObjects?ids:list='+filename)
# create it
browser.open('/manage_addFile?id='+filename)
# edit it, hopefully causing conflicts
data = 'X'*filesize
class EditThread(threading.Thread):
def __init__(self,i):
self.conflicts = 0
self.browser = AuthBrowser()
self.browser.handleErrors = False
self.browser.addBasicAuth(username,password)
threading.Thread.__init__(self,name=str(i))
def run(self):
for i in range(1,hits+1):
self.browser.open('/conflict.txt/manage_main')
self.browser.getControl(name='title').value='Test Title'
self.browser.getControl(name='filedata:text').value = data
try:
self.browser.getControl(name='manage_edit:method').click()
except urllib2.HTTPError,e:
# print e.read()
self.conflicts += 1
print "Thread %s - CONFLICT" % self.getName()
else:
print "Thread %s - EDIT" % self.getName()
thread_objects = []
for i in range(1,threads+1):
t = EditThread(i)
thread_objects.append(t)
t.start()
for t in thread_objects:
t.join()
total = 0
print
for t in thread_objects:
print "Thread %s - %i conflicts seen" % (t.getName(),t.conflicts)
total += t.conflicts
print
print "%i conflicts seen by browsers" % total
...@@ -33,26 +33,24 @@ class BaseRequest_factory: ...@@ -33,26 +33,24 @@ class BaseRequest_factory:
if base is None: if base is None:
base = '' base = ''
elif not base.endswith('/'): elif not base.endswith('/'):
base = base+'/' base = base + '/'
self.base = str(base) self.base = str(base)
def notFoundError(self, name): def notFoundError(self, name):
from zExceptions import NotFound from zExceptions import NotFound
raise NotFound(name) raise NotFound(name)
# Real responses raise NotFound, to avoid information disclosure
#def forbiddenError(self, name):
# from zExceptions import Forbidden
# raise Forbidden(name)
forbiddenError = notFoundError forbiddenError = notFoundError
response = DummyResponse() response = DummyResponse()
environment = { 'URL': '', environment = {
'URL': '',
'PARENTS': [root], 'PARENTS': [root],
'steps': [], 'steps': [],
'_hacked_path': 0, '_hacked_path': 0,
'_test_counter': 0, '_test_counter': 0,
'response': response } 'response': response
}
return self._getTargetClass()(environment) return self._getTargetClass()(environment)
def _makeBasicObjectClass(self): def _makeBasicObjectClass(self):
...@@ -112,7 +110,7 @@ class BaseRequest_factory: ...@@ -112,7 +110,7 @@ class BaseRequest_factory:
else: else:
raise RuntimeError('Infinite loop detected.') raise RuntimeError('Infinite loop detected.')
REQUEST['TraversalRequestNameStack'] += self._path REQUEST['TraversalRequestNameStack'] += self._path
REQUEST._hacked_path=1 REQUEST._hacked_path = 1
return DummyObjectWithBPTH() return DummyObjectWithBPTH()
...@@ -151,16 +149,18 @@ class BaseRequest_factory: ...@@ -151,16 +149,18 @@ class BaseRequest_factory:
def _makeObjectWithBDBBT(self): def _makeObjectWithBDBBT(self):
class DummyObjectWithBDBBT(self._makeBasicObjectClass()): class DummyObjectWithBDBBT(self._makeBasicObjectClass()):
"""Dummy class with __browser_default__.""" """Dummy class with __browser_default__."""
def __browser_default__(self, REQUEST): def __browser_default__(self, REQUEST):
if REQUEST['_test_counter'] < 100: if REQUEST['_test_counter'] < 100:
REQUEST['_test_counter'] += 1 REQUEST['_test_counter'] += 1
else: else:
raise RuntimeError('Infinite loop detected.') raise RuntimeError('Infinite loop detected.')
return self, self._default_path return self, self._default_path
def __bobo_traverse__(self, REQUEST, name): def __bobo_traverse__(self, REQUEST, name):
if name == self._default_path[0]: if name == self._default_path[0]:
return getattr(self, name) return getattr(self, name)
raise AttributeError, name raise AttributeError(name)
return DummyObjectWithBDBBT() return DummyObjectWithBDBBT()
def _makeObjectWithEmptyDocstring(self): def _makeObjectWithEmptyDocstring(self):
...@@ -292,8 +292,9 @@ class TestBaseRequest(unittest.TestCase, BaseRequest_factory): ...@@ -292,8 +292,9 @@ class TestBaseRequest(unittest.TestCase, BaseRequest_factory):
# that we get a NotFound # that we get a NotFound
from ZPublisher import NotFound from ZPublisher import NotFound
root, folder = self._makeRootAndFolder() root, folder = self._makeRootAndFolder()
def _faux___bobo_traverse__(REQUEST, name): def _faux___bobo_traverse__(REQUEST, name):
raise AttributeError, name raise AttributeError(name)
obj = self._makeBasicObject() obj = self._makeBasicObject()
obj.__bobo_traverse__ = _faux___bobo_traverse__ obj.__bobo_traverse__ = _faux___bobo_traverse__
folder._setObject('objWithBBT', obj) folder._setObject('objWithBBT', obj)
...@@ -309,7 +310,8 @@ class TestBaseRequest(unittest.TestCase, BaseRequest_factory): ...@@ -309,7 +310,8 @@ class TestBaseRequest(unittest.TestCase, BaseRequest_factory):
self.assertEqual(r.traverse('folder/objWithBBT/normal').tag, 'Normal') self.assertEqual(r.traverse('folder/objWithBBT/normal').tag, 'Normal')
# test default usage # test default usage
r = self._makeOne(root) r = self._makeOne(root)
self.assertEqual(r.traverse('folder/objWithBBT/default').tag, 'Default') self.assertEqual(
r.traverse('folder/objWithBBT/default').tag, 'Default')
def test_traverse_withBDBBT(self): def test_traverse_withBDBBT(self):
# Test for an object which has a __browser_default__ # Test for an object which has a __browser_default__
...@@ -444,6 +446,7 @@ class TestBaseRequest(unittest.TestCase, BaseRequest_factory): ...@@ -444,6 +446,7 @@ class TestBaseRequest(unittest.TestCase, BaseRequest_factory):
r = self._makeOne(root) r = self._makeOne(root)
r.other['foo'] = 'Foo' r.other['foo'] = 'Foo'
BEFORE = subscribers[:] BEFORE = subscribers[:]
def _broken(event): def _broken(event):
raise ValueError("I'm broken") raise ValueError("I'm broken")
subscribers.append(_broken) subscribers.append(_broken)
...@@ -481,7 +484,7 @@ class TestBaseRequest(unittest.TestCase, BaseRequest_factory): ...@@ -481,7 +484,7 @@ class TestBaseRequest(unittest.TestCase, BaseRequest_factory):
self.assertRaises(NotFound, r.traverse, 'not_found') self.assertRaises(NotFound, r.traverse, 'not_found')
class TestRequestZope3ViewsBase(unittest.TestCase, BaseRequest_factory): class TestRequestViewsBase(unittest.TestCase, BaseRequest_factory):
_dummy_interface = None _dummy_interface = None
...@@ -492,7 +495,7 @@ class TestRequestZope3ViewsBase(unittest.TestCase, BaseRequest_factory): ...@@ -492,7 +495,7 @@ class TestRequestZope3ViewsBase(unittest.TestCase, BaseRequest_factory):
def _makeOne(self, root): def _makeOne(self, root):
from zope.interface import directlyProvides from zope.interface import directlyProvides
from zope.publisher.browser import IDefaultBrowserLayer from zope.publisher.browser import IDefaultBrowserLayer
request = super(TestRequestZope3ViewsBase, self)._makeOne(root) request = super(TestRequestViewsBase, self)._makeOne(root)
# The request needs to implement the proper interface # The request needs to implement the proper interface
directlyProvides(request, IDefaultBrowserLayer) directlyProvides(request, IDefaultBrowserLayer)
return request return request
...@@ -532,6 +535,7 @@ class TestRequestZope3ViewsBase(unittest.TestCase, BaseRequest_factory): ...@@ -532,6 +535,7 @@ class TestRequestZope3ViewsBase(unittest.TestCase, BaseRequest_factory):
class DummyObjectZ3(self._makeBasicObjectClass()): class DummyObjectZ3(self._makeBasicObjectClass()):
implements(self._dummyInterface()) implements(self._dummyInterface())
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
...@@ -542,12 +546,14 @@ class TestRequestZope3ViewsBase(unittest.TestCase, BaseRequest_factory): ...@@ -542,12 +546,14 @@ class TestRequestZope3ViewsBase(unittest.TestCase, BaseRequest_factory):
class DummyObjectZ3WithAttr(self._makeBasicObjectClass()): class DummyObjectZ3WithAttr(self._makeBasicObjectClass()):
implements(self._dummyInterface()) implements(self._dummyInterface())
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
def meth(self): def meth(self):
"""doc""" """doc"""
return 'meth on %s' % self.name return 'meth on %s' % self.name
def methonly(self): def methonly(self):
"""doc""" """doc"""
return 'methonly on %s' % self.name return 'methonly on %s' % self.name
...@@ -577,11 +583,11 @@ class TestRequestZope3ViewsBase(unittest.TestCase, BaseRequest_factory): ...@@ -577,11 +583,11 @@ class TestRequestZope3ViewsBase(unittest.TestCase, BaseRequest_factory):
def __init__(self, content, request): def __init__(self, content, request):
self.content = content self.content = content
self.request = request self.request = request
def __call__(self): def __call__(self):
return 'view on %s' % (self.content.name) return 'view on %s' % (self.content.name)
class DummyPage(BrowserPage): class DummyPage(BrowserPage):
# BrowserPage is an IBrowserPublisher with a browserDefault that # BrowserPage is an IBrowserPublisher with a browserDefault that
# returns self, () so that __call__ is invoked by the publisher. # returns self, () so that __call__ is invoked by the publisher.
...@@ -594,7 +600,8 @@ class TestRequestZope3ViewsBase(unittest.TestCase, BaseRequest_factory): ...@@ -594,7 +600,8 @@ class TestRequestZope3ViewsBase(unittest.TestCase, BaseRequest_factory):
# intentionally return something that's not self # intentionally return something that's not self
return DummyPage(self.context, request), () return DummyPage(self.context, request), ()
# __call__ remains unimplemented, baseclass raises NotImplementedError # __call__ remains unimplemented,
# baseclass raises NotImplementedError
class DummyPage3(BrowserPage): class DummyPage3(BrowserPage):
...@@ -605,7 +612,8 @@ class TestRequestZope3ViewsBase(unittest.TestCase, BaseRequest_factory): ...@@ -605,7 +612,8 @@ class TestRequestZope3ViewsBase(unittest.TestCase, BaseRequest_factory):
def foo(self): def foo(self):
return 'Test page' return 'Test page'
# __call__ remains unimplemented, baseclass raises NotImplementedError # __call__ remains unimplemented,
# baseclass raises NotImplementedError
class DummyPage4(Implicit, DummyPage): class DummyPage4(Implicit, DummyPage):
# a normal page that can implicitly acquire attributes # a normal page that can implicitly acquire attributes
...@@ -641,10 +649,10 @@ class TestRequestZope3ViewsBase(unittest.TestCase, BaseRequest_factory): ...@@ -641,10 +649,10 @@ class TestRequestZope3ViewsBase(unittest.TestCase, BaseRequest_factory):
IDefaultViewName, '') IDefaultViewName, '')
class TestBaseRequestZope3Views(TestRequestZope3ViewsBase): class TestBaseRequestViews(TestRequestViewsBase):
def test_traverse_view(self): def test_traverse_view(self):
#simple view # simple view
root, folder = self._makeRootAndFolder() root, folder = self._makeRootAndFolder()
folder._setObject('obj', self._makeDummyObject('obj')) folder._setObject('obj', self._makeDummyObject('obj'))
r = self._makeOne(root) r = self._makeOne(root)
...@@ -658,9 +666,10 @@ class TestBaseRequestZope3Views(TestRequestZope3ViewsBase): ...@@ -658,9 +666,10 @@ class TestBaseRequestZope3Views(TestRequestZope3ViewsBase):
self.assertEqual(ob(), 'view on obj') self.assertEqual(ob(), 'view on obj')
def test_traverse_view_attr_local(self): def test_traverse_view_attr_local(self):
#method on object used first # method on object used first
root, folder = self._makeRootAndFolder() root, folder = self._makeRootAndFolder()
folder._setObject('withattr', self._makeDummyObjectWithAttr('withattr')) folder._setObject(
'withattr', self._makeDummyObjectWithAttr('withattr'))
r = self._makeOne(root) r = self._makeOne(root)
ob = r.traverse('folder/withattr/meth') ob = r.traverse('folder/withattr/meth')
self.assertEqual(ob(), 'meth on withattr') self.assertEqual(ob(), 'meth on withattr')
...@@ -672,9 +681,10 @@ class TestBaseRequestZope3Views(TestRequestZope3ViewsBase): ...@@ -672,9 +681,10 @@ class TestBaseRequestZope3Views(TestRequestZope3ViewsBase):
self.assertEqual(ob(), 'view on withattr') self.assertEqual(ob(), 'view on withattr')
def test_traverse_view_attr_above(self): def test_traverse_view_attr_above(self):
#view takes precedence over acquired attribute # view takes precedence over acquired attribute
root, folder = self._makeRootAndFolder() root, folder = self._makeRootAndFolder()
folder2 = root._setObject('folder2', self._makeDummyObjectWithAttr('folder2')) folder2 = root._setObject(
'folder2', self._makeDummyObjectWithAttr('folder2'))
folder2._setObject('obj2', self._makeDummyObject('obj2')) folder2._setObject('obj2', self._makeDummyObject('obj2'))
r = self._makeOne(root) r = self._makeOne(root)
ob = r.traverse('folder2/obj2/meth') ob = r.traverse('folder2/obj2/meth')
...@@ -687,10 +697,12 @@ class TestBaseRequestZope3Views(TestRequestZope3ViewsBase): ...@@ -687,10 +697,12 @@ class TestBaseRequestZope3Views(TestRequestZope3ViewsBase):
self.assertEqual(ob(), 'view on obj2') self.assertEqual(ob(), 'view on obj2')
def test_traverse_view_attr_local2(self): def test_traverse_view_attr_local2(self):
#method with other method above # method with other method above
root, folder = self._makeRootAndFolder() root, folder = self._makeRootAndFolder()
folder2 = root._setObject('folder2', self._makeDummyObjectWithAttr('folder2')) folder2 = root._setObject(
folder2._setObject('withattr2', self._makeDummyObjectWithAttr('withattr2')) 'folder2', self._makeDummyObjectWithAttr('folder2'))
folder2._setObject(
'withattr2', self._makeDummyObjectWithAttr('withattr2'))
r = self._makeOne(root) r = self._makeOne(root)
ob = r.traverse('folder2/withattr2/meth') ob = r.traverse('folder2/withattr2/meth')
self.assertEqual(ob(), 'meth on withattr2') self.assertEqual(ob(), 'meth on withattr2')
...@@ -702,10 +714,11 @@ class TestBaseRequestZope3Views(TestRequestZope3ViewsBase): ...@@ -702,10 +714,11 @@ class TestBaseRequestZope3Views(TestRequestZope3ViewsBase):
self.assertEqual(ob(), 'view on withattr2') self.assertEqual(ob(), 'view on withattr2')
def test_traverse_view_attr_acquired(self): def test_traverse_view_attr_acquired(self):
#normal acquired attribute without view # normal acquired attribute without view
from ZPublisher import NotFound from ZPublisher import NotFound
root, folder = self._makeRootAndFolder() root, folder = self._makeRootAndFolder()
folder2 = root._setObject('folder2', self._makeDummyObjectWithAttr('folder2')) folder2 = root._setObject(
'folder2', self._makeDummyObjectWithAttr('folder2'))
folder2._setObject('obj2', self._makeDummyObject('obj2')) folder2._setObject('obj2', self._makeDummyObject('obj2'))
r = self._makeOne(root) r = self._makeOne(root)
ob = r.traverse('folder2/obj2/methonly') ob = r.traverse('folder2/obj2/methonly')
...@@ -716,7 +729,7 @@ class TestBaseRequestZope3Views(TestRequestZope3ViewsBase): ...@@ -716,7 +729,7 @@ class TestBaseRequestZope3Views(TestRequestZope3ViewsBase):
self.assertRaises(NotFound, r.traverse, 'folder2/obj2') self.assertRaises(NotFound, r.traverse, 'folder2/obj2')
def test_quoting_goggles(self): def test_quoting_goggles(self):
#View goggles ('@@') should not be quoted # View goggles ('@@') should not be quoted
root, folder = self._makeRootAndFolder() root, folder = self._makeRootAndFolder()
folder._setObject('obj', self._makeDummyObject('obj')) folder._setObject('obj', self._makeDummyObject('obj'))
r = self._makeOne(root) r = self._makeOne(root)
...@@ -724,7 +737,7 @@ class TestBaseRequestZope3Views(TestRequestZope3ViewsBase): ...@@ -724,7 +737,7 @@ class TestBaseRequestZope3Views(TestRequestZope3ViewsBase):
self.assertEqual(r['URL'], '/folder/obj/@@meth') self.assertEqual(r['URL'], '/folder/obj/@@meth')
def test_quoting_plusplus(self): def test_quoting_plusplus(self):
#View markers ('++ should not be quoted # View markers ('++ should not be quoted
root, folder = self._makeRootAndFolder() root, folder = self._makeRootAndFolder()
folder._setObject('obj', self._makeDummyObject('obj')) folder._setObject('obj', self._makeDummyObject('obj'))
r = self._makeOne(root) r = self._makeOne(root)
...@@ -764,10 +777,3 @@ class TestBaseRequestZope3Views(TestRequestZope3ViewsBase): ...@@ -764,10 +777,3 @@ class TestBaseRequestZope3Views(TestRequestZope3ViewsBase):
self.assertEqual(ob(), 'Test page') self.assertEqual(ob(), 'Test page')
# make sure we can acquire # make sure we can acquire
self.assertEqual(ob.ob2, ob2) self.assertEqual(ob.ob2, ob2)
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(TestBaseRequest),
unittest.makeSuite(TestBaseRequestZope3Views),
))
import sys import doctest
import logging
from Acquisition import Implicit from Acquisition import Implicit
from ZPublisher import BeforeTraverse
from ZPublisher.BaseRequest import BaseRequest from ZPublisher.BaseRequest import BaseRequest
from ZPublisher.HTTPResponse import HTTPResponse from ZPublisher.HTTPResponse import HTTPResponse
def makeBaseRequest(root): def makeBaseRequest(root):
response = HTTPResponse() response = HTTPResponse()
environment = { 'URL': '', environment = {
'URL': '',
'PARENTS': [root], 'PARENTS': [root],
'steps': [], 'steps': [],
'_hacked_path': 0, '_hacked_path': 0,
'_test_counter': 0, '_test_counter': 0,
'response': response } 'response': response,
}
return BaseRequest(environment) return BaseRequest(environment)
...@@ -23,11 +24,12 @@ class DummyObjectBasic(Implicit): ...@@ -23,11 +24,12 @@ class DummyObjectBasic(Implicit):
pass pass
class BrokenHook: class BrokenHook(object):
def __call__(self, *args): def __call__(self, *args):
print self.__class__.__name__, 'called' print('%s called' % self.__class__.__name__)
raise TypeError, self.__class__.__name__ raise TypeError(self.__class__.__name__)
def testBeforeTraverse(self): def testBeforeTraverse(self):
""" """
...@@ -36,6 +38,10 @@ def testBeforeTraverse(self): ...@@ -36,6 +38,10 @@ def testBeforeTraverse(self):
special API for registering hooks, and the hooks themselves are special API for registering hooks, and the hooks themselves are
called during traversal by ZPublisher. called during traversal by ZPublisher.
>>> import sys
>>> import logging
>>> from ZPublisher import BeforeTraverse
>>> root = DummyObjectBasic() >>> root = DummyObjectBasic()
>>> request = makeBaseRequest(root) >>> request = makeBaseRequest(root)
...@@ -131,10 +137,7 @@ def testBeforeTraverse(self): ...@@ -131,10 +137,7 @@ def testBeforeTraverse(self):
>>> logger.handlers = handlers[:] >>> logger.handlers = handlers[:]
""" """
pass
import doctest
def test_suite(): def test_suite():
return doctest.DocTestSuite(optionflags=doctest.ELLIPSIS) return doctest.DocTestSuite(optionflags=doctest.ELLIPSIS)
...@@ -16,17 +16,19 @@ from ZPublisher.HTTPRangeSupport import parseRange, expandRanges ...@@ -16,17 +16,19 @@ from ZPublisher.HTTPRangeSupport import parseRange, expandRanges
import unittest import unittest
class TestRangeHeaderParse(unittest.TestCase): class TestRangeHeaderParse(unittest.TestCase):
# Utility methods # Utility methods
def expectNone(self, header): def expectNone(self, header):
result = parseRange(header) result = parseRange(header)
self.assertTrue(result is None, 'Expected None, got %s' % `result`) self.assertTrue(result is None, 'Expected None, got %r' % result)
def expectSets(self, header, sets): def expectSets(self, header, sets):
result = parseRange(header) result = parseRange(header)
self.assertTrue(result == sets, self.assertTrue(
'Expected %s, got %s' % (`sets`, `result`)) result == sets,
'Expected %r, got %r' % (sets, result))
# Syntactically incorrect headers # Syntactically incorrect headers
def testGarbage(self): def testGarbage(self):
...@@ -67,7 +69,8 @@ class TestRangeHeaderParse(unittest.TestCase): ...@@ -67,7 +69,8 @@ class TestRangeHeaderParse(unittest.TestCase):
self.expectSets('bytes=100-100', [(100, 101)]) self.expectSets('bytes=100-100', [(100, 101)])
def testMultiple(self): def testMultiple(self):
self.expectSets('bytes=-100,,1-2,20-', self.expectSets(
'bytes=-100,,1-2,20-',
[(-100, None), (1, 3), (20, None)]) [(-100, None), (1, 3), (20, None)])
def testFirstByte(self): def testFirstByte(self):
...@@ -81,8 +84,9 @@ class TestExpandRanges(unittest.TestCase): ...@@ -81,8 +84,9 @@ class TestExpandRanges(unittest.TestCase):
def expectSets(self, sets, size, expect): def expectSets(self, sets, size, expect):
result = expandRanges(sets, size) result = expandRanges(sets, size)
self.assertTrue(result == expect, self.assertTrue(
'Expected %s, got %s' % (`expect`, `result`)) result == expect,
'Expected %r, got %r' % (expect, result))
def testExpandOpenEnd(self): def testExpandOpenEnd(self):
self.expectSets([(1, 2), (5, None)], 50, [(1, 2), (5, 50)]) self.expectSets([(1, 2), (5, None)], 50, [(1, 2), (5, 50)])
...@@ -91,23 +95,28 @@ class TestExpandRanges(unittest.TestCase): ...@@ -91,23 +95,28 @@ class TestExpandRanges(unittest.TestCase):
self.expectSets([(1, 2), (-5, None)], 50, [(1, 2), (45, 50)]) self.expectSets([(1, 2), (-5, None)], 50, [(1, 2), (45, 50)])
def testNoOverlapInOrder(self): def testNoOverlapInOrder(self):
self.expectSets([(1, 5), (1000, 2000), (3000, None)], 5000, self.expectSets(
[(1, 5), (1000, 2000), (3000, None)], 5000,
[(1, 5), (1000, 2000), (3000, 5000)]) [(1, 5), (1000, 2000), (3000, 5000)])
def testNoOverlapOutOfOrder(self): def testNoOverlapOutOfOrder(self):
self.expectSets([(1000, 2000), (3000, None), (1, 5)], 5000, self.expectSets(
[(1000, 2000), (3000, None), (1, 5)], 5000,
[(1000, 2000), (3000, 5000), (1, 5)]) [(1000, 2000), (3000, 5000), (1, 5)])
def testOverlapInOrder(self): def testOverlapInOrder(self):
self.expectSets([(1, 10), (8, 20), (25, None)], 5000, self.expectSets(
[(1, 10), (8, 20), (25, None)], 5000,
[(1, 10), (8, 20), (25, 5000)]) [(1, 10), (8, 20), (25, 5000)])
def testOverlapOutOfOrder(self): def testOverlapOutOfOrder(self):
self.expectSets([(25, 50), (8, None), (1, 10)], 5000, self.expectSets(
[(25, 50), (8, None), (1, 10)], 5000,
[(25, 50), (8, 5000), (1, 10)]) [(25, 50), (8, 5000), (1, 10)])
def testAdjacentInOrder(self): def testAdjacentInOrder(self):
self.expectSets([(1, 10), (10, 20), (25, 50)], 5000, self.expectSets(
[(1, 10), (10, 20), (25, 50)], 5000,
[(1, 10), (10, 20), (25, 50)]) [(1, 10), (10, 20), (25, 50)])
def testAdjacentOutOfOrder(self): def testAdjacentOutOfOrder(self):
...@@ -119,20 +128,3 @@ class TestExpandRanges(unittest.TestCase): ...@@ -119,20 +128,3 @@ class TestExpandRanges(unittest.TestCase):
def testRemoveUnsatisfiable(self): def testRemoveUnsatisfiable(self):
self.expectSets([(sys.maxint, None), (10, 20)], 50, [(10, 20)]) self.expectSets([(sys.maxint, None), (10, 20)], 50, [(10, 20)])
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestRangeHeaderParse, 'test'))
suite.addTest(unittest.makeSuite(TestExpandRanges, 'test'))
return suite
def main():
unittest.TextTestRunner().run(test_suite())
def debug():
test_suite().debug()
def pdebug():
import pdb
pdb.run('debug()')
import unittest import unittest
from ZPublisher.tests.testBaseRequest import TestRequestZope3ViewsBase from ZPublisher.tests.testBaseRequest import TestRequestViewsBase
from zope.testing.cleanup import cleanUp from zope.testing.cleanup import cleanUp
...@@ -54,8 +54,8 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -54,8 +54,8 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
def _processInputs(self, inputs): def _processInputs(self, inputs):
from urllib import quote_plus from urllib import quote_plus
# Have the inputs processed, and return a HTTPRequest object holding the # Have the inputs processed, and return a HTTPRequest object
# result. # holding the result.
# inputs is expected to be a list of (key, value) tuples, no CGI # inputs is expected to be a list of (key, value) tuples, no CGI
# encoding is required. # encoding is required.
...@@ -86,55 +86,66 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -86,55 +86,66 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
retval = 0 retval = 0
if isinstance(val, TaintedString): if isinstance(val, TaintedString):
self.assertFalse(not '<' in val, self.assertFalse(
'<' not in val,
"%r is not dangerous, no taint required." % val) "%r is not dangerous, no taint required." % val)
retval = 1 retval = 1
elif isinstance(val, record): elif isinstance(val, record):
for attr, value in val.__dict__.items(): for attr, value in val.__dict__.items():
rval = self._valueIsOrHoldsTainted(attr) rval = self._valueIsOrHoldsTainted(attr)
if rval: retval = 1 if rval:
retval = 1
rval = self._valueIsOrHoldsTainted(value) rval = self._valueIsOrHoldsTainted(value)
if rval: retval = 1 if rval:
retval = 1
elif type(val) in (list, tuple): elif type(val) in (list, tuple):
for entry in val: for entry in val:
rval = self._valueIsOrHoldsTainted(entry) rval = self._valueIsOrHoldsTainted(entry)
if rval: retval = 1 if rval:
retval = 1
elif type(val) in (str, unicode): elif type(val) in (str, unicode):
self.assertFalse('<' in val, self.assertFalse(
'<' in val,
"'%s' is dangerous and should have been tainted." % val) "'%s' is dangerous and should have been tainted." % val)
return retval return retval
def _noFormValuesInOther(self, req): def _noFormValuesInOther(self, req):
for key in req.taintedform.keys(): for key in req.taintedform.keys():
self.assertFalse(req.other.has_key(key), self.assertFalse(
key in req.other,
'REQUEST.other should not hold tainted values at first!') 'REQUEST.other should not hold tainted values at first!')
for key in req.form.keys(): for key in req.form.keys():
self.assertFalse(req.other.has_key(key), self.assertFalse(
key in req.other,
'REQUEST.other should not hold form values at first!') 'REQUEST.other should not hold form values at first!')
def _onlyTaintedformHoldsTaintedStrings(self, req): def _onlyTaintedformHoldsTaintedStrings(self, req):
for key, val in req.taintedform.items(): for key, val in req.taintedform.items():
self.assert_(self._valueIsOrHoldsTainted(key) or self.assert_(
self._valueIsOrHoldsTainted(key) or
self._valueIsOrHoldsTainted(val), self._valueIsOrHoldsTainted(val),
'Tainted form holds item %s that is not tainted' % key) 'Tainted form holds item %s that is not tainted' % key)
for key, val in req.form.items(): for key, val in req.form.items():
if req.taintedform.has_key(key): if key in req.taintedform:
continue continue
self.assertFalse(self._valueIsOrHoldsTainted(key) or self.assertFalse(
self._valueIsOrHoldsTainted(key) or
self._valueIsOrHoldsTainted(val), self._valueIsOrHoldsTainted(val),
'Normal form holds item %s that is tainted' % key) 'Normal form holds item %s that is tainted' % key)
def _taintedKeysAlsoInForm(self, req): def _taintedKeysAlsoInForm(self, req):
for key in req.taintedform.keys(): for key in req.taintedform.keys():
self.assert_(req.form.has_key(key), self.assert_(
key in req.form,
"Found tainted %s not in form" % key) "Found tainted %s not in form" % key)
self.assertEquals(req.form[key], req.taintedform[key], self.assertEqual(
req.form[key], req.taintedform[key],
"Key %s not correctly reproduced in tainted; expected %r, " "Key %s not correctly reproduced in tainted; expected %r, "
"got %r" % (key, req.form[key], req.taintedform[key])) "got %r" % (key, req.form[key], req.taintedform[key]))
...@@ -156,7 +167,7 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -156,7 +167,7 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
req = self._makeOne(environ=env) req = self._makeOne(environ=env)
req.processInputs() req.processInputs()
self._noFormValuesInOther(req) self._noFormValuesInOther(req)
self.assertEquals(req.form, {}) self.assertEqual(req.form, {})
def test_processInputs_wo_marshalling(self): def test_processInputs_wo_marshalling(self):
inputs = ( inputs = (
...@@ -168,12 +179,13 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -168,12 +179,13 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
formkeys = list(req.form.keys()) formkeys = list(req.form.keys())
formkeys.sort() formkeys.sort()
self.assertEquals(formkeys, ['foo', 'key', 'multi', 'number', self.assertEqual(
'spacey key', 'spam']) formkeys,
self.assertEquals(req['number'], '1') ['foo', 'key', 'multi', 'number', 'spacey key', 'spam'])
self.assertEquals(req['multi'], ['1', '2']) self.assertEqual(req['number'], '1')
self.assertEquals(req['spacey key'], 'val') self.assertEqual(req['multi'], ['1', '2'])
self.assertEquals(req['key'], 'spacey val') self.assertEqual(req['spacey key'], 'val')
self.assertEqual(req['key'], 'spacey val')
self._noTaintedValues(req) self._noTaintedValues(req)
self._onlyTaintedformHoldsTaintedStrings(req) self._onlyTaintedformHoldsTaintedStrings(req)
...@@ -191,18 +203,20 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -191,18 +203,20 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
formkeys = list(req.form.keys()) formkeys = list(req.form.keys())
formkeys.sort() formkeys.sort()
self.assertEquals(formkeys, ['2tokens', 'accountedfor', 'aday', 'bign', self.assertEqual(
formkeys,
['2tokens', 'accountedfor', 'aday', 'bign',
'fract', 'morewords', 'multiline', 'num', 'words']) 'fract', 'morewords', 'multiline', 'num', 'words'])
self.assertEquals(req['2tokens'], ['one', 'two']) self.assertEqual(req['2tokens'], ['one', 'two'])
self.assertEquals(req['accountedfor'], 'yes') self.assertEqual(req['accountedfor'], 'yes')
self.assertEquals(req['aday'], DateTime('2002/07/23')) self.assertEqual(req['aday'], DateTime('2002/07/23'))
self.assertEquals(req['bign'], 45L) self.assertEqual(req['bign'], 45)
self.assertEquals(req['fract'], 4.2) self.assertEqual(req['fract'], 4.2)
self.assertEquals(req['morewords'], 'one\ntwo\n') self.assertEqual(req['morewords'], 'one\ntwo\n')
self.assertEquals(req['multiline'], ['one', 'two']) self.assertEqual(req['multiline'], ['one', 'two'])
self.assertEquals(req['num'], 42) self.assertEqual(req['num'], 42)
self.assertEquals(req['words'], 'Some words') self.assertEqual(req['words'], 'Some words')
self._noTaintedValues(req) self._noTaintedValues(req)
self._onlyTaintedformHoldsTaintedStrings(req) self._onlyTaintedformHoldsTaintedStrings(req)
...@@ -217,16 +231,17 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -217,16 +231,17 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
formkeys = list(req.form.keys()) formkeys = list(req.form.keys())
formkeys.sort() formkeys.sort()
self.assertEquals(formkeys, ['nouconverter', 'ulines', 'ustring', self.assertEqual(
'utext', 'utokens']) formkeys,
['nouconverter', 'ulines', 'ustring', 'utext', 'utokens'])
self.assertEquals(req['ustring'], u'test\u00AE') self.assertEqual(req['ustring'], u'test\u00AE')
self.assertEquals(req['utext'], u'test\u00AE\ntest\u00AE\n') self.assertEqual(req['utext'], u'test\u00AE\ntest\u00AE\n')
self.assertEquals(req['utokens'], [u'test\u00AE', u'test\u00AE']) self.assertEqual(req['utokens'], [u'test\u00AE', u'test\u00AE'])
self.assertEquals(req['ulines'], [u'test\u00AE', u'test\u00AE']) self.assertEqual(req['ulines'], [u'test\u00AE', u'test\u00AE'])
# expect a utf-8 encoded version # expect a utf-8 encoded version
self.assertEquals(req['nouconverter'], 'test\xc2\xae') self.assertEqual(req['nouconverter'], 'test\xc2\xae')
self._noTaintedValues(req) self._noTaintedValues(req)
self._onlyTaintedformHoldsTaintedStrings(req) self._onlyTaintedformHoldsTaintedStrings(req)
...@@ -244,20 +259,21 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -244,20 +259,21 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
formkeys = list(req.form.keys()) formkeys = list(req.form.keys())
formkeys.sort() formkeys.sort()
self.assertEquals(formkeys, ['alist', 'atuple', 'oneitem', self.assertEqual(
'oneitemtuple', 'onerec', 'setrec']) formkeys,
['alist', 'atuple', 'oneitem', 'oneitemtuple', 'onerec', 'setrec'])
self.assertEquals(req['oneitem'], ['one'])
self.assertEquals(req['oneitemtuple'], ('one',)) self.assertEqual(req['oneitem'], ['one'])
self.assertEquals(req['alist'], ['one', 'two']) self.assertEqual(req['oneitemtuple'], ('one',))
self.assertEquals(req['atuple'], ('one', 'two')) self.assertEqual(req['alist'], ['one', 'two'])
self.assertEquals(req['onerec'].foo, 'foo') self.assertEqual(req['atuple'], ('one', 'two'))
self.assertEquals(req['onerec'].bar, 'bar') self.assertEqual(req['onerec'].foo, 'foo')
self.assertEquals(len(req['setrec']), 2) self.assertEqual(req['onerec'].bar, 'bar')
self.assertEquals(req['setrec'][0].foo, 'foo') self.assertEqual(len(req['setrec']), 2)
self.assertEquals(req['setrec'][0].bar, 'bar') self.assertEqual(req['setrec'][0].foo, 'foo')
self.assertEquals(req['setrec'][1].foo, 'spam') self.assertEqual(req['setrec'][0].bar, 'bar')
self.assertEquals(req['setrec'][1].bar, 'eggs') self.assertEqual(req['setrec'][1].foo, 'spam')
self.assertEqual(req['setrec'][1].bar, 'eggs')
self._noTaintedValues(req) self._noTaintedValues(req)
self._onlyTaintedformHoldsTaintedStrings(req) self._onlyTaintedformHoldsTaintedStrings(req)
...@@ -273,11 +289,11 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -273,11 +289,11 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
formkeys = list(req.form.keys()) formkeys = list(req.form.keys())
formkeys.sort() formkeys.sort()
self.assertEquals(formkeys, ['ftuple', 'ilist', 'tlist']) self.assertEqual(formkeys, ['ftuple', 'ilist', 'tlist'])
self.assertEquals(req['ilist'], [1, 2, 3]) self.assertEqual(req['ilist'], [1, 2, 3])
self.assertEquals(req['ftuple'], (1.0, 1.1, 1.2)) self.assertEqual(req['ftuple'], (1.0, 1.1, 1.2))
self.assertEquals(req['tlist'], [['one', 'two'], ['3', '4']]) self.assertEqual(req['tlist'], [['one', 'two'], ['3', '4']])
self._noTaintedValues(req) self._noTaintedValues(req)
self._onlyTaintedformHoldsTaintedStrings(req) self._onlyTaintedformHoldsTaintedStrings(req)
...@@ -303,20 +319,20 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -303,20 +319,20 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
formkeys = list(req.form.keys()) formkeys = list(req.form.keys())
formkeys.sort() formkeys.sort()
self.assertEquals(formkeys, ['onerec', 'setrec']) self.assertEqual(formkeys, ['onerec', 'setrec'])
self.assertEquals(req['onerec'].name, 'foo') self.assertEqual(req['onerec'].name, 'foo')
self.assertEquals(req['onerec'].tokens, ['one', 'two']) self.assertEqual(req['onerec'].tokens, ['one', 'two'])
# Implicit sequences and records don't mix. # Implicit sequences and records don't mix.
self.assertEquals(req['onerec'].ints, 2) self.assertEqual(req['onerec'].ints, 2)
self.assertEquals(len(req['setrec']), 2) self.assertEqual(len(req['setrec']), 2)
self.assertEquals(req['setrec'][0].name, 'first') self.assertEqual(req['setrec'][0].name, 'first')
self.assertEquals(req['setrec'][1].name, 'second') self.assertEqual(req['setrec'][1].name, 'second')
for i in range(2): for i in range(2):
self.assertEquals(req['setrec'][i].ilist, [1, 2]) self.assertEqual(req['setrec'][i].ilist, [1, 2])
self.assertEquals(req['setrec'][i].ituple, (1, 2)) self.assertEqual(req['setrec'][i].ituple, (1, 2))
self._noTaintedValues(req) self._noTaintedValues(req)
self._onlyTaintedformHoldsTaintedStrings(req) self._onlyTaintedformHoldsTaintedStrings(req)
...@@ -350,21 +366,21 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -350,21 +366,21 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
formkeys = list(req.form.keys()) formkeys = list(req.form.keys())
formkeys.sort() formkeys.sort()
self.assertEquals(formkeys, ['alist', 'bar', 'explicitlist', 'foo', self.assertEqual(
'setrec']) formkeys, ['alist', 'bar', 'explicitlist', 'foo', 'setrec'])
self.assertEquals(req['alist'], [1, 2, 3, 4, 5]) self.assertEqual(req['alist'], [1, 2, 3, 4, 5])
self.assertEquals(req['explicitlist'], [1, 2, 3, 4, 5]) self.assertEqual(req['explicitlist'], [1, 2, 3, 4, 5])
self.assertEquals(req['foo'], 5) self.assertEqual(req['foo'], 5)
self.assertEquals(req['bar'].spam, 'eggs') self.assertEqual(req['bar'].spam, 'eggs')
self.assertEquals(req['bar'].foo, 'baz') self.assertEqual(req['bar'].foo, 'baz')
self.assertEquals(len(req['setrec']), 2) self.assertEqual(len(req['setrec']), 2)
self.assertEquals(req['setrec'][0].spam, 'eggs') self.assertEqual(req['setrec'][0].spam, 'eggs')
self.assertEquals(req['setrec'][0].foo, 'baz') self.assertEqual(req['setrec'][0].foo, 'baz')
self.assertEquals(req['setrec'][1].spam, 'eggs') self.assertEqual(req['setrec'][1].spam, 'eggs')
self.assertEquals(req['setrec'][1].foo, 'ham') self.assertEqual(req['setrec'][1].foo, 'ham')
self._noTaintedValues(req) self._noTaintedValues(req)
self._onlyTaintedformHoldsTaintedStrings(req) self._onlyTaintedformHoldsTaintedStrings(req)
...@@ -383,7 +399,9 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -383,7 +399,9 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
taintedformkeys = list(req.taintedform.keys()) taintedformkeys = list(req.taintedform.keys())
taintedformkeys.sort() taintedformkeys.sort()
self.assertEquals(taintedformkeys, ['<tainted key>', 'tainted', self.assertEqual(
taintedformkeys,
['<tainted key>', 'tainted',
'tallmulti', 'tdefermulti', 'tinitmulti']) 'tallmulti', 'tdefermulti', 'tinitmulti'])
self._taintedKeysAlsoInForm(req) self._taintedKeysAlsoInForm(req)
...@@ -393,7 +411,8 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -393,7 +411,8 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
inputs = ( inputs = (
('<tnum>:int', '42'), ('<tfract>:float', '4.2'), ('<tnum>:int', '42'), ('<tfract>:float', '4.2'),
('<tbign>:long', '45'), ('<tbign>:long', '45'),
('twords:string', 'Some <words>'), ('t2tokens:tokens', 'one <two>'), ('twords:string', 'Some <words>'),
('t2tokens:tokens', 'one <two>'),
('<taday>:date', '2002/07/23'), ('<taday>:date', '2002/07/23'),
('taccountedfor:required', '<yes>'), ('taccountedfor:required', '<yes>'),
('tmultiline:lines', '<one\ntwo>'), ('tmultiline:lines', '<one\ntwo>'),
...@@ -402,7 +421,9 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -402,7 +421,9 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
taintedformkeys = list(req.taintedform.keys()) taintedformkeys = list(req.taintedform.keys())
taintedformkeys.sort() taintedformkeys.sort()
self.assertEquals(taintedformkeys, ['<taday>', '<tbign>', '<tfract>', self.assertEqual(
taintedformkeys,
['<taday>', '<tbign>', '<tfract>',
'<tnum>', 't2tokens', 'taccountedfor', 'tmorewords', 'tmultiline', '<tnum>', 't2tokens', 'taccountedfor', 'tmorewords', 'tmultiline',
'twords']) 'twords'])
...@@ -410,7 +431,8 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -410,7 +431,8 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
self._onlyTaintedformHoldsTaintedStrings(req) self._onlyTaintedformHoldsTaintedStrings(req)
def test_processInputs_w_unicode_w_taints(self): def test_processInputs_w_unicode_w_taints(self):
inputs = (('tustring:ustring:utf8', '<test\xc2\xae>'), inputs = (
('tustring:ustring:utf8', '<test\xc2\xae>'),
('tutext:utext:utf8', '<test\xc2\xae>\n<test\xc2\xae\n>'), ('tutext:utext:utf8', '<test\xc2\xae>\n<test\xc2\xae\n>'),
('tinitutokens:utokens:utf8', '<test\xc2\xae> test\xc2\xae'), ('tinitutokens:utokens:utf8', '<test\xc2\xae> test\xc2\xae'),
...@@ -419,12 +441,15 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -419,12 +441,15 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
('tdeferutokens:utokens:utf8', 'test\xc2\xae <test\xc2\xae>'), ('tdeferutokens:utokens:utf8', 'test\xc2\xae <test\xc2\xae>'),
('tdeferulines:ulines:utf8', 'test\xc2\xae\n<test\xc2\xae>'), ('tdeferulines:ulines:utf8', 'test\xc2\xae\n<test\xc2\xae>'),
('tnouconverter:string:utf8', '<test\xc2\xae>')) ('tnouconverter:string:utf8', '<test\xc2\xae>'),
)
req = self._processInputs(inputs) req = self._processInputs(inputs)
taintedformkeys = list(req.taintedform.keys()) taintedformkeys = list(req.taintedform.keys())
taintedformkeys.sort() taintedformkeys.sort()
self.assertEquals(taintedformkeys, ['tdeferulines', 'tdeferutokens', self.assertEqual(
taintedformkeys,
['tdeferulines', 'tdeferutokens',
'tinitulines', 'tinitutokens', 'tnouconverter', 'tustring', 'tinitulines', 'tinitutokens', 'tnouconverter', 'tustring',
'tutext']) 'tutext'])
...@@ -470,7 +495,9 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -470,7 +495,9 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
taintedformkeys = list(req.taintedform.keys()) taintedformkeys = list(req.taintedform.keys())
taintedformkeys.sort() taintedformkeys.sort()
self.assertEquals(taintedformkeys, ['<tkeyoneitem>', 'tdeferalist', self.assertEqual(
taintedformkeys,
['<tkeyoneitem>', 'tdeferalist',
'tdeferatuple', 'tdeferdefersetrec', 'tdeferinitsetrec', 'tdeferatuple', 'tdeferdefersetrec', 'tdeferinitsetrec',
'tdeferonerec', 'tinitalist', 'tinitatuple', 'tinitdefersetrec', 'tdeferonerec', 'tinitalist', 'tinitatuple', 'tinitdefersetrec',
'tinitinitsetrec', 'tinitonerec', 'toneitem', 'toneitemtuple']) 'tinitinitsetrec', 'tinitonerec', 'toneitem', 'toneitemtuple'])
...@@ -520,7 +547,9 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -520,7 +547,9 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
taintedformkeys = list(req.taintedform.keys()) taintedformkeys = list(req.taintedform.keys())
taintedformkeys.sort() taintedformkeys.sort()
self.assertEquals(taintedformkeys, ['tdeferfirstsetrec', 'tdeferonerec', self.assertEqual(
taintedformkeys,
['tdeferfirstsetrec', 'tdeferonerec',
'tdefersecondsetrec', 'tinitonerec', 'tinitsetrec']) 'tdefersecondsetrec', 'tinitonerec', 'tinitsetrec'])
self._taintedKeysAlsoInForm(req) self._taintedKeysAlsoInForm(req)
...@@ -572,7 +601,9 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -572,7 +601,9 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
taintedformkeys = list(req.taintedform.keys()) taintedformkeys = list(req.taintedform.keys())
taintedformkeys.sort() taintedformkeys.sort()
self.assertEquals(taintedformkeys, ['tdeferbar', 'tdeferlist', self.assertEqual(
taintedformkeys,
['tdeferbar', 'tdeferlist',
'tdefersetrec', 'tfoo', 'tinitbar', 'tinitlist', 'tinitsetrec']) 'tdefersetrec', 'tfoo', 'tinitbar', 'tinitlist', 'tinitsetrec'])
self._taintedKeysAlsoInForm(req) self._taintedKeysAlsoInForm(req)
...@@ -592,10 +623,12 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -592,10 +623,12 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
try: try:
convert('<html garbage>') convert('<html garbage>')
except Exception as e: except Exception as e:
self.assertFalse('<' in e.args, self.assertFalse(
'<' in e.args,
'%s converter does not quote unsafe value!' % type) '%s converter does not quote unsafe value!' % type)
except SyntaxError as e: except SyntaxError as e:
self.assertFalse('<' in e, self.assertFalse(
'<' in e,
'%s converter does not quote unsafe value!' % type) '%s converter does not quote unsafe value!' % type)
def test_processInputs_w_dotted_name_as_tuple(self): def test_processInputs_w_dotted_name_as_tuple(self):
...@@ -606,9 +639,9 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -606,9 +639,9 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
formkeys = list(req.form.keys()) formkeys = list(req.form.keys())
formkeys.sort() formkeys.sort()
self.assertEquals(formkeys, ['name.']) self.assertEqual(formkeys, ['name.'])
self.assertEquals(req['name.'], ('name with dot as tuple',)) self.assertEqual(req['name.'], ('name with dot as tuple',))
self._noTaintedValues(req) self._noTaintedValues(req)
self._onlyTaintedformHoldsTaintedStrings(req) self._onlyTaintedformHoldsTaintedStrings(req)
...@@ -618,20 +651,20 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -618,20 +651,20 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
env['HTTP_COOKIE'] = 'foo=bar; baz=gee' env['HTTP_COOKIE'] = 'foo=bar; baz=gee'
req = self._makeOne(environ=env) req = self._makeOne(environ=env)
self.assertEquals(req.cookies['foo'], 'bar') self.assertEqual(req.cookies['foo'], 'bar')
self.assertEquals(req.cookies['baz'], 'gee') self.assertEqual(req.cookies['baz'], 'gee')
env['HTTP_COOKIE'] = 'foo=bar; baz="gee, like, e=mc^2"' env['HTTP_COOKIE'] = 'foo=bar; baz="gee, like, e=mc^2"'
req = self._makeOne(environ=env) req = self._makeOne(environ=env)
self.assertEquals(req.cookies['foo'], 'bar') self.assertEqual(req.cookies['foo'], 'bar')
self.assertEquals(req.cookies['baz'], 'gee, like, e=mc^2') self.assertEqual(req.cookies['baz'], 'gee, like, e=mc^2')
# Collector #1498: empty cookies # Collector #1498: empty cookies
env['HTTP_COOKIE'] = 'foo=bar; hmm; baz=gee' env['HTTP_COOKIE'] = 'foo=bar; hmm; baz=gee'
req = self._makeOne(environ=env) req = self._makeOne(environ=env)
self.assertEquals(req.cookies['foo'], 'bar') self.assertEqual(req.cookies['foo'], 'bar')
self.assertEquals(req.cookies['hmm'], '') self.assertEqual(req.cookies['hmm'], '')
self.assertEquals(req.cookies['baz'], 'gee') self.assertEqual(req.cookies['baz'], 'gee')
# Unquoted multi-space cookies # Unquoted multi-space cookies
env['HTTP_COOKIE'] = 'single=cookie data; ' \ env['HTTP_COOKIE'] = 'single=cookie data; ' \
...@@ -639,12 +672,12 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -639,12 +672,12 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
'multi=cookie data with unquoted spaces; ' \ 'multi=cookie data with unquoted spaces; ' \
'multi2=cookie data with unquoted spaces' 'multi2=cookie data with unquoted spaces'
req = self._makeOne(environ=env) req = self._makeOne(environ=env)
self.assertEquals(req.cookies['single'], 'cookie data') self.assertEqual(req.cookies['single'], 'cookie data')
self.assertEquals(req.cookies['quoted'], self.assertEqual(req.cookies['quoted'],
'cookie data with unquoted spaces') 'cookie data with unquoted spaces')
self.assertEquals(req.cookies['multi'], self.assertEqual(req.cookies['multi'],
'cookie data with unquoted spaces') 'cookie data with unquoted spaces')
self.assertEquals(req.cookies['multi2'], self.assertEqual(req.cookies['multi2'],
'cookie data with unquoted spaces') 'cookie data with unquoted spaces')
def test_postProcessInputs(self): def test_postProcessInputs(self):
...@@ -707,43 +740,43 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -707,43 +740,43 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
req = self._makeOne(stdin=s, environ=TEST_ENVIRON.copy()) req = self._makeOne(stdin=s, environ=TEST_ENVIRON.copy())
req.processInputs() req.processInputs()
f=req.form.get('file') f = req.form.get('file')
self.assertEqual(list(f),['test\n']) self.assertEqual(list(f), ['test\n'])
f.seek(0) f.seek(0)
self.assertEqual(f.next(),'test\n') self.assertEqual(f.next(), 'test\n')
f.seek(0) f.seek(0)
self.assertEqual(f.xreadlines(),f) self.assertEqual(f.xreadlines(), f)
def test__authUserPW_simple( self ): def test__authUserPW_simple(self):
import base64 import base64
user_id = 'user' user_id = 'user'
password = 'password' password = 'password'
encoded = base64.encodestring( '%s:%s' % ( user_id, password ) ) encoded = base64.encodestring('%s:%s' % (user_id, password))
auth_header = 'basic %s' % encoded auth_header = 'basic %s' % encoded
environ = { 'HTTP_AUTHORIZATION': auth_header } environ = {'HTTP_AUTHORIZATION': auth_header}
request = self._makeOne( environ=environ ) request = self._makeOne(environ=environ)
user_id_x, password_x = request._authUserPW() user_id_x, password_x = request._authUserPW()
self.assertEqual( user_id_x, user_id ) self.assertEqual(user_id_x, user_id)
self.assertEqual( password_x, password ) self.assertEqual(password_x, password)
def test__authUserPW_with_embedded_colon( self ): def test__authUserPW_with_embedded_colon(self):
# http://www.zope.org/Collectors/Zope/2039 # http://www.zope.org/Collectors/Zope/2039
import base64 import base64
user_id = 'user' user_id = 'user'
password = 'embedded:colon' password = 'embedded:colon'
encoded = base64.encodestring( '%s:%s' % ( user_id, password ) ) encoded = base64.encodestring('%s:%s' % (user_id, password))
auth_header = 'basic %s' % encoded auth_header = 'basic %s' % encoded
environ = { 'HTTP_AUTHORIZATION': auth_header } environ = {'HTTP_AUTHORIZATION': auth_header}
request = self._makeOne( environ=environ ) request = self._makeOne(environ=environ)
user_id_x, password_x = request._authUserPW() user_id_x, password_x = request._authUserPW()
self.assertEqual( user_id_x, user_id ) self.assertEqual(user_id_x, user_id)
self.assertEqual( password_x, password ) self.assertEqual(password_x, password)
def test_debug_not_in_qs_still_gets_attr(self): def test_debug_not_in_qs_still_gets_attr(self):
from zope.publisher.base import DebugFlags from zope.publisher.base import DebugFlags
...@@ -774,14 +807,6 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -774,14 +807,6 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
request['debug'] = '2' request['debug'] = '2'
self.assertEqual(request.debug, '2') self.assertEqual(request.debug, '2')
def test_interfaces(self):
from zope.publisher.interfaces.browser import IBrowserRequest
from zope.interface.verify import verifyClass
klass = self._getTargetClass()
# TODO
# verifyClass(IBrowserRequest, klass)
def test_locale_property_accessor(self): def test_locale_property_accessor(self):
from zope.component import provideAdapter from zope.component import provideAdapter
from zope.publisher.browser import BrowserLanguages from zope.publisher.browser import BrowserLanguages
...@@ -920,7 +945,7 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -920,7 +945,7 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
def test_getClientAddr_one_trusted_proxy(self): def test_getClientAddr_one_trusted_proxy(self):
from ZPublisher.HTTPRequest import trusted_proxies from ZPublisher.HTTPRequest import trusted_proxies
env = {'REMOTE_ADDR': '127.0.0.1', env = {'REMOTE_ADDR': '127.0.0.1',
'HTTP_X_FORWARDED_FOR': '10.1.20.30, 192.168.1.100' } 'HTTP_X_FORWARDED_FOR': '10.1.20.30, 192.168.1.100'}
orig = trusted_proxies[:] orig = trusted_proxies[:]
try: try:
...@@ -1029,6 +1054,7 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -1029,6 +1054,7 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
def test_clone_preserves_direct_interfaces(self): def test_clone_preserves_direct_interfaces(self):
from zope.interface import directlyProvides from zope.interface import directlyProvides
from zope.interface import Interface from zope.interface import Interface
class IFoo(Interface): class IFoo(Interface):
pass pass
request = self._makeOne() request = self._makeOne()
...@@ -1047,7 +1073,8 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -1047,7 +1073,8 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
request.resolve_url(request.script + '/') request.resolve_url(request.script + '/')
finally: finally:
zope.event.subscribers.remove(events.append) zope.event.subscribers.remove(events.append)
self.assertFalse(len(events), self.assertFalse(
len(events),
"HTTPRequest.resolve_url should not emit events") "HTTPRequest.resolve_url should not emit events")
def test_resolve_url_errorhandling(self): def test_resolve_url_errorhandling(self):
...@@ -1056,37 +1083,36 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin): ...@@ -1056,37 +1083,36 @@ class HTTPRequestTests(unittest.TestCase, HTTPRequestFactoryMixin):
from zExceptions import NotFound from zExceptions import NotFound
request = self._makeOne() request = self._makeOne()
request['PARENTS'] = [object()] request['PARENTS'] = [object()]
self.assertRaises( NotFound self.assertRaises(
, request.resolve_url NotFound, request.resolve_url, request.script + '/does_not_exist')
, request.script + '/does_not_exist'
)
def test_parses_json_cookies(self): def test_parses_json_cookies(self):
# https://bugs.launchpad.net/zope2/+bug/563229 # https://bugs.launchpad.net/zope2/+bug/563229
# reports cookies in the wild with embedded double quotes (e.g, # reports cookies in the wild with embedded double quotes (e.g,
# JSON-encoded data structures. # JSON-encoded data structures.
env = {'SERVER_NAME': 'testingharnas', env = {
'SERVER_NAME': 'testingharnas',
'SERVER_PORT': '80', 'SERVER_PORT': '80',
'HTTP_COOKIE': 'json={"intkey":123,"stringkey":"blah"}; ' 'HTTP_COOKIE': 'json={"intkey":123,"stringkey":"blah"}; '
'anothercookie=boring; baz' 'anothercookie=boring; baz'
} }
req = self._makeOne(environ=env) req = self._makeOne(environ=env)
self.assertEquals(req.cookies['json'], self.assertEqual(req.cookies['json'],
'{"intkey":123,"stringkey":"blah"}') '{"intkey":123,"stringkey":"blah"}')
self.assertEquals(req.cookies['anothercookie'], 'boring') self.assertEqual(req.cookies['anothercookie'], 'boring')
def test_getVirtualRoot(self): def test_getVirtualRoot(self):
# https://bugs.launchpad.net/zope2/+bug/193122 # https://bugs.launchpad.net/zope2/+bug/193122
req = self._makeOne() req = self._makeOne()
req._script = [] req._script = []
self.assertEquals(req.getVirtualRoot(), '') self.assertEqual(req.getVirtualRoot(), '')
req._script = ['foo', 'bar'] req._script = ['foo', 'bar']
self.assertEquals(req.getVirtualRoot(), '/foo/bar') self.assertEqual(req.getVirtualRoot(), '/foo/bar')
class TestHTTPRequestZope3Views(TestRequestZope3ViewsBase,): class TestHTTPRequestZope3Views(TestRequestViewsBase):
def _makeOne(self, root): def _makeOne(self, root):
from zope.interface import directlyProvides from zope.interface import directlyProvides
...@@ -1118,7 +1144,7 @@ TEST_ENVIRON = { ...@@ -1118,7 +1144,7 @@ TEST_ENVIRON = {
'REQUEST_METHOD': 'POST', 'REQUEST_METHOD': 'POST',
'SERVER_NAME': 'localhost', 'SERVER_NAME': 'localhost',
'SERVER_PORT': '80', 'SERVER_PORT': '80',
} }
TEST_FILE_DATA = ''' TEST_FILE_DATA = '''
--12345 --12345
...@@ -1138,11 +1164,3 @@ Content-Type: application/octet-stream ...@@ -1138,11 +1164,3 @@ Content-Type: application/octet-stream
test %s test %s
''' % ('test' * 1000) ''' % ('test' * 1000)
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(RecordTests))
suite.addTest(unittest.makeSuite(HTTPRequestTests))
suite.addTest(unittest.makeSuite(TestHTTPRequestZope3Views))
return suite
...@@ -13,17 +13,15 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -13,17 +13,15 @@ class HTTPResponseTests(unittest.TestCase):
self._setDefaultEncoding(self._old_default_encoding) self._setDefaultEncoding(self._old_default_encoding)
def _setDefaultEncoding(self, value): def _setDefaultEncoding(self, value):
from ZPublisher import HTTPResponse as module from ZPublisher import HTTPResponse
(module.default_encoding, (HTTPResponse.default_encoding,
self._old_default_encoding) = (value, module.default_encoding) self._old_default_encoding) = (value, HTTPResponse.default_encoding)
def _getTargetClass(self): def _getTargetClass(self):
from ZPublisher.HTTPResponse import HTTPResponse from ZPublisher.HTTPResponse import HTTPResponse
return HTTPResponse return HTTPResponse
def _makeOne(self, *args, **kw): def _makeOne(self, *args, **kw):
return self._getTargetClass()(*args, **kw) return self._getTargetClass()(*args, **kw)
def test_ctor_defaults(self): def test_ctor_defaults(self):
...@@ -43,8 +41,7 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -43,8 +41,7 @@ class HTTPResponseTests(unittest.TestCase):
def test_ctor_w_headers(self): def test_ctor_w_headers(self):
response = self._makeOne(headers={'foo': 'bar'}) response = self._makeOne(headers={'foo': 'bar'})
self.assertEqual(response.headers, {'foo': 'bar', self.assertEqual(response.headers, {'foo': 'bar'})
})
def test_ctor_w_status_code(self): def test_ctor_w_status_code(self):
response = self._makeOne(status=401) response = self._makeOne(status=401)
...@@ -83,9 +80,9 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -83,9 +80,9 @@ class HTTPResponseTests(unittest.TestCase):
'application/foo') 'application/foo')
def test_ctor_charset_application_header_with_header(self): def test_ctor_charset_application_header_with_header(self):
response = self._makeOne(body='foo', response = self._makeOne(
headers={'content-type': body='foo',
'application/foo; charset: something'}) headers={'content-type': 'application/foo; charset: something'})
self.assertEqual(response.headers.get('content-type'), self.assertEqual(response.headers.get('content-type'),
'application/foo; charset: something') 'application/foo; charset: something')
...@@ -109,16 +106,18 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -109,16 +106,18 @@ class HTTPResponseTests(unittest.TestCase):
def test_ctor_body_recodes_to_match_content_type_charset(self): def test_ctor_body_recodes_to_match_content_type_charset(self):
xml = (u'<?xml version="1.0" encoding="iso-8859-15" ?>\n' xml = (u'<?xml version="1.0" encoding="iso-8859-15" ?>\n'
'<foo><bar/></foo>') u'<foo><bar/></foo>')
response = self._makeOne(body=xml, headers={'content-type': response = self._makeOne(
'text/xml; charset=utf-8'}) body=xml, headers={
'content-type': 'text/xml; charset=utf-8'})
self.assertEqual(response.body, xml.replace('iso-8859-15', 'utf-8')) self.assertEqual(response.body, xml.replace('iso-8859-15', 'utf-8'))
def test_ctor_body_already_matches_charset_unchanged(self): def test_ctor_body_already_matches_charset_unchanged(self):
xml = (u'<?xml version="1.0" encoding="iso-8859-15" ?>\n' xml = (u'<?xml version="1.0" encoding="iso-8859-15" ?>\n'
'<foo><bar/></foo>') u'<foo><bar/></foo>')
response = self._makeOne(body=xml, headers={'content-type': response = self._makeOne(
'text/xml; charset=iso-8859-15'}) body=xml, headers={
'content-type': 'text/xml; charset=iso-8859-15'})
self.assertEqual(response.body, xml) self.assertEqual(response.body, xml)
def test_retry(self): def test_retry(self):
...@@ -278,7 +277,7 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -278,7 +277,7 @@ class HTTPResponseTests(unittest.TestCase):
cookies = response._cookie_list() cookies = response._cookie_list()
self.assertEqual(len(cookies), 1) self.assertEqual(len(cookies), 1)
self.assertEqual(cookies[0], ('Set-Cookie','foo="bar"; Secure')) self.assertEqual(cookies[0], ('Set-Cookie', 'foo="bar"; Secure'))
def test_setCookie_w_secure_false_value(self): def test_setCookie_w_secure_false_value(self):
response = self._makeOne() response = self._makeOne()
...@@ -586,6 +585,7 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -586,6 +585,7 @@ class HTTPResponseTests(unittest.TestCase):
def test_setBody_object_with_asHTML(self): def test_setBody_object_with_asHTML(self):
HTML = '<html><head></head><body></body></html>' HTML = '<html><head></head><body></body></html>'
class Dummy: class Dummy:
def asHTML(self): def asHTML(self):
return HTML return HTML
...@@ -598,7 +598,8 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -598,7 +598,8 @@ class HTTPResponseTests(unittest.TestCase):
self.assertEqual(response.getHeader('Content-Length'), str(len(HTML))) self.assertEqual(response.getHeader('Content-Length'), str(len(HTML)))
def test_setBody_object_with_unicode(self): def test_setBody_object_with_unicode(self):
HTML = u'<html><head></head><body><h1>Tr\u0039s Bien</h1></body></html>' HTML = (u'<html><head></head><body>'
u'<h1>Tr\u0039s Bien</h1></body></html>')
ENCODED = HTML.encode('utf-8') ENCODED = HTML.encode('utf-8')
response = self._makeOne() response = self._makeOne()
result = response.setBody(HTML) result = response.setBody(HTML)
...@@ -646,14 +647,13 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -646,14 +647,13 @@ class HTTPResponseTests(unittest.TestCase):
def test_setBody_calls_insertBase(self): def test_setBody_calls_insertBase(self):
response = self._makeOne() response = self._makeOne()
lamb = {} lamb = {}
def _insertBase(): def _insertBase():
lamb['flavor'] = 'CURRY' lamb['flavor'] = 'CURRY'
response.insertBase = _insertBase response.insertBase = _insertBase
response.setBody('Garlic Naan') response.setBody('Garlic Naan')
self.assertEqual(lamb['flavor'], 'CURRY') self.assertEqual(lamb['flavor'], 'CURRY')
#def test_setBody_w_HTTP_content_compression(self):
def test_setBody_compression_uncompressible_mimetype(self): def test_setBody_compression_uncompressible_mimetype(self):
BEFORE = 'foo' * 100 # body must get smaller on compression BEFORE = 'foo' * 100 # body must get smaller on compression
response = self._makeOne() response = self._makeOne()
...@@ -701,7 +701,7 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -701,7 +701,7 @@ class HTTPResponseTests(unittest.TestCase):
response = self._makeOne() response = self._makeOne()
response.enableHTTPCompression({'HTTP_ACCEPT_ENCODING': 'gzip'}) response.enableHTTPCompression({'HTTP_ACCEPT_ENCODING': 'gzip'})
response.setHeader('Vary', PRIOR) response.setHeader('Vary', PRIOR)
response.setBody('foo'*100) response.setBody('foo' * 100)
self.assertEqual(response.getHeader('Vary'), PRIOR) self.assertEqual(response.getHeader('Vary'), PRIOR)
def test_setBody_compression_no_prior_vary_header_but_forced(self): def test_setBody_compression_no_prior_vary_header_but_forced(self):
...@@ -770,8 +770,8 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -770,8 +770,8 @@ class HTTPResponseTests(unittest.TestCase):
response = self._makeOne() response = self._makeOne()
response.setHeader('Content-Type', 'text/html; charset=latin1') response.setHeader('Content-Type', 'text/html; charset=latin1')
self.assertEqual(response._encode_unicode(UNICODE), self.assertEqual(response._encode_unicode(UNICODE),
'<?xml version="1.0" encoding="latin1" ?>\n' '<?xml version="1.0" encoding="latin1" ?>\n' +
+ ELEMENT.encode('latin1')) ELEMENT.encode('latin1'))
response.getHeader('Content-Type', 'text/html; charset=latin1') response.getHeader('Content-Type', 'text/html; charset=latin1')
def test_quoteHTML(self): def test_quoteHTML(self):
...@@ -786,7 +786,7 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -786,7 +786,7 @@ class HTTPResponseTests(unittest.TestCase):
response = self._makeOne() response = self._makeOne()
try: try:
response.notFoundError() response.notFoundError()
except NotFound, raised: except NotFound as raised:
self.assertEqual(response.status, 404) self.assertEqual(response.status, 404)
self.assertTrue("<p><b>Resource:</b> Unknown</p>" in str(raised)) self.assertTrue("<p><b>Resource:</b> Unknown</p>" in str(raised))
else: else:
...@@ -797,7 +797,7 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -797,7 +797,7 @@ class HTTPResponseTests(unittest.TestCase):
response = self._makeOne() response = self._makeOne()
try: try:
response.notFoundError('ENTRY') response.notFoundError('ENTRY')
except NotFound, raised: except NotFound as raised:
self.assertEqual(response.status, 404) self.assertEqual(response.status, 404)
self.assertTrue("<p><b>Resource:</b> ENTRY</p>" in str(raised)) self.assertTrue("<p><b>Resource:</b> ENTRY</p>" in str(raised))
else: else:
...@@ -808,7 +808,7 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -808,7 +808,7 @@ class HTTPResponseTests(unittest.TestCase):
response = self._makeOne() response = self._makeOne()
try: try:
response.forbiddenError() response.forbiddenError()
except NotFound, raised: except NotFound as raised:
self.assertEqual(response.status, 404) self.assertEqual(response.status, 404)
self.assertTrue("<p><b>Resource:</b> Unknown</p>" in str(raised)) self.assertTrue("<p><b>Resource:</b> Unknown</p>" in str(raised))
else: else:
...@@ -819,7 +819,7 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -819,7 +819,7 @@ class HTTPResponseTests(unittest.TestCase):
response = self._makeOne() response = self._makeOne()
try: try:
response.forbiddenError('ENTRY') response.forbiddenError('ENTRY')
except NotFound, raised: except NotFound as raised:
self.assertEqual(response.status, 404) self.assertEqual(response.status, 404)
self.assertTrue("<p><b>Resource:</b> ENTRY</p>" in str(raised)) self.assertTrue("<p><b>Resource:</b> ENTRY</p>" in str(raised))
else: else:
...@@ -830,7 +830,7 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -830,7 +830,7 @@ class HTTPResponseTests(unittest.TestCase):
response = self._makeOne() response = self._makeOne()
try: try:
response.debugError('testing') response.debugError('testing')
except NotFound, raised: except NotFound as raised:
self.assertEqual(response.status, 200) self.assertEqual(response.status, 200)
self.assertTrue("Zope has encountered a problem publishing " self.assertTrue("Zope has encountered a problem publishing "
"your object.<p>\ntesting</p>" in str(raised)) "your object.<p>\ntesting</p>" in str(raised))
...@@ -842,7 +842,7 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -842,7 +842,7 @@ class HTTPResponseTests(unittest.TestCase):
response = self._makeOne() response = self._makeOne()
try: try:
response.badRequestError('some_parameter') response.badRequestError('some_parameter')
except BadRequest, raised: except BadRequest as raised:
self.assertEqual(response.status, 400) self.assertEqual(response.status, 400)
self.assertTrue("The parameter, <em>some_parameter</em>, " self.assertTrue("The parameter, <em>some_parameter</em>, "
"was omitted from the request." in str(raised)) "was omitted from the request." in str(raised))
...@@ -854,7 +854,7 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -854,7 +854,7 @@ class HTTPResponseTests(unittest.TestCase):
response = self._makeOne() response = self._makeOne()
try: try:
response.badRequestError('URL1') response.badRequestError('URL1')
except InternalError, raised: except InternalError as raised:
self.assertEqual(response.status, 400) self.assertEqual(response.status, 400)
self.assertTrue("Sorry, an internal error occurred in this " self.assertTrue("Sorry, an internal error occurred in this "
"resource." in str(raised)) "resource." in str(raised))
...@@ -870,7 +870,7 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -870,7 +870,7 @@ class HTTPResponseTests(unittest.TestCase):
def test__unauthorized_w_default_realm(self): def test__unauthorized_w_default_realm(self):
response = self._makeOne() response = self._makeOne()
response._unauthorized() response._unauthorized()
self.assertTrue('WWW-Authenticate' in response.headers) #literal self.assertTrue('WWW-Authenticate' in response.headers) # literal
self.assertEqual(response.headers['WWW-Authenticate'], self.assertEqual(response.headers['WWW-Authenticate'],
'basic realm="Zope"') 'basic realm="Zope"')
...@@ -878,7 +878,7 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -878,7 +878,7 @@ class HTTPResponseTests(unittest.TestCase):
response = self._makeOne() response = self._makeOne()
response.realm = 'Folly' response.realm = 'Folly'
response._unauthorized() response._unauthorized()
self.assertTrue('WWW-Authenticate' in response.headers) #literal self.assertTrue('WWW-Authenticate' in response.headers) # literal
self.assertEqual(response.headers['WWW-Authenticate'], self.assertEqual(response.headers['WWW-Authenticate'],
'basic realm="Folly"') 'basic realm="Folly"')
...@@ -887,7 +887,7 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -887,7 +887,7 @@ class HTTPResponseTests(unittest.TestCase):
response = self._makeOne() response = self._makeOne()
try: try:
response.unauthorized() response.unauthorized()
except Unauthorized, raised: except Unauthorized as raised:
self.assertEqual(response.status, 200) # publisher sets 401 later self.assertEqual(response.status, 200) # publisher sets 401 later
self.assertTrue("You are not authorized " self.assertTrue("You are not authorized "
"to access this resource." in str(raised)) "to access this resource." in str(raised))
...@@ -900,7 +900,7 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -900,7 +900,7 @@ class HTTPResponseTests(unittest.TestCase):
response.debug_mode = True response.debug_mode = True
try: try:
response.unauthorized() response.unauthorized()
except Unauthorized, raised: except Unauthorized as raised:
self.assertTrue("\nNo Authorization header found." self.assertTrue("\nNo Authorization header found."
in str(raised)) in str(raised))
else: else:
...@@ -913,7 +913,7 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -913,7 +913,7 @@ class HTTPResponseTests(unittest.TestCase):
response._auth = 'bogus' response._auth = 'bogus'
try: try:
response.unauthorized() response.unauthorized()
except Unauthorized, raised: except Unauthorized as raised:
self.assertTrue("\nUsername and password are not correct." self.assertTrue("\nUsername and password are not correct."
in str(raised)) in str(raised))
else: else:
...@@ -1058,9 +1058,9 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -1058,9 +1058,9 @@ class HTTPResponseTests(unittest.TestCase):
response = self._makeOne() response = self._makeOne()
response.expireCookie('qux', path='/') response.expireCookie('qux', path='/')
headers = response.listHeaders() headers = response.listHeaders()
self.assertEqual(headers, self.assertEqual(
[('X-Powered-By', 'Zope (www.zope.org), ' headers,
'Python (www.python.org)'), [('X-Powered-By', 'Zope (www.zope.org), Python (www.python.org)'),
('Set-Cookie', 'qux="deleted"; ' ('Set-Cookie', 'qux="deleted"; '
'Path=/; ' 'Path=/; '
'Expires=Wed, 31-Dec-97 23:59:59 GMT; ' 'Expires=Wed, 31-Dec-97 23:59:59 GMT; '
...@@ -1300,12 +1300,3 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -1300,12 +1300,3 @@ class HTTPResponseTests(unittest.TestCase):
'See the server error log for details') 'See the server error log for details')
self.assertTrue('bobo-exception-file' in response.headers) self.assertTrue('bobo-exception-file' in response.headers)
self.assertTrue('bobo-exception-line' in response.headers) self.assertTrue('bobo-exception-line' in response.headers)
#TODO
# def test_exception_* WAAAAAA!
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(HTTPResponseTests, 'test'))
return suite
...@@ -2,11 +2,8 @@ import unittest ...@@ -2,11 +2,8 @@ import unittest
from zope.interface.verify import verifyClass from zope.interface.verify import verifyClass
from ZPublisher.Iterators import IStreamIterator, filestream_iterator from ZPublisher.Iterators import IStreamIterator, filestream_iterator
class TestFileStreamIterator(unittest.TestCase): class TestFileStreamIterator(unittest.TestCase):
def testInterface(self): def testInterface(self):
verifyClass(IStreamIterator, filestream_iterator) verifyClass(IStreamIterator, filestream_iterator)
def test_suite():
suite = unittest.TestSuite()
suite.addTest( unittest.makeSuite( TestFileStreamIterator ) )
return suite
from unittest import TestCase, TestSuite, makeSuite, main from unittest import TestCase
import Testing
import Zope2
Zope2.startup()
from Acquisition import Implicit from Acquisition import Implicit
from ZPublisher.BaseRequest import BaseRequest from ZPublisher.BaseRequest import BaseRequest
from ZPublisher.HTTPResponse import HTTPResponse from ZPublisher.HTTPResponse import HTTPResponse
import Zope2
Zope2.startup()
# Various post traversal methods
pt_simple_was_run = 0 pt_simple_was_run = 0
def pt_simple(): def pt_simple():
global pt_simple_was_run global pt_simple_was_run
pt_simple_was_run = 1 pt_simple_was_run = 1
pass pass
def pt_static_arg(request, b): def pt_static_arg(request, b):
request.set('b', b) request.set('b', b)
pass pass
def pt_simple_redirect(a): def pt_simple_redirect(a):
return a return a
def pt_chain_test(request, string): def pt_chain_test(request, string):
request.set('a', request.get('a', '') + string) request.set('a', request.get('a', '') + string)
class DummyObjectBasic(Implicit): class DummyObjectBasic(Implicit):
""" Dummy class with docstring. """ Dummy class with docstring.
""" """
...@@ -41,6 +42,7 @@ class DummyObjectBasic(Implicit): ...@@ -41,6 +42,7 @@ class DummyObjectBasic(Implicit):
""" """
return 'view content' return 'view content'
class DummyObjectWithPTHook(DummyObjectBasic): class DummyObjectWithPTHook(DummyObjectBasic):
""" Dummy class with docstring. """ Dummy class with docstring.
""" """
...@@ -51,21 +53,24 @@ class DummyObjectWithPTHook(DummyObjectBasic): ...@@ -51,21 +53,24 @@ class DummyObjectWithPTHook(DummyObjectBasic):
for x in self.traversal: for x in self.traversal:
REQUEST.post_traverse(*x) REQUEST.post_traverse(*x)
class TestBaseRequestPT(TestCase): class TestBaseRequestPT(TestCase):
def setUp(self): def setUp(self):
self.root = DummyObjectBasic() self.root = DummyObjectBasic()
self.f1 = self.root._setObject('folder', DummyObjectBasic() ) self.f1 = self.root._setObject('folder', DummyObjectBasic())
self.f1._setObject('objBasic', DummyObjectWithPTHook() ) self.f1._setObject('objBasic', DummyObjectWithPTHook())
def makeBaseRequest(self): def makeBaseRequest(self):
response = HTTPResponse() response = HTTPResponse()
environment = { 'URL': '', environment = {
'URL': '',
'PARENTS': [self.root], 'PARENTS': [self.root],
'steps': [], 'steps': [],
'_hacked_path': 0, '_hacked_path': 0,
'_test_counter': 0, '_test_counter': 0,
'response': response } 'response': response,
}
return BaseRequest(environment) return BaseRequest(environment)
def test_post_basic(self): def test_post_basic(self):
...@@ -101,13 +106,15 @@ class TestBaseRequestPT(TestCase): ...@@ -101,13 +106,15 @@ class TestBaseRequestPT(TestCase):
r = self.makeBaseRequest() r = self.makeBaseRequest()
self.f1.objBasic.traversal = [ (pt_chain_test, (r, 'a')), self.f1.objBasic.traversal = [
(pt_chain_test, (r, 'a')),
(pt_chain_test, (r, 'b')), (pt_chain_test, (r, 'b')),
(pt_chain_test, (r, 'c')), (pt_chain_test, (r, 'c')),
(pt_chain_test, (r, 'd'))] (pt_chain_test, (r, 'd')),
]
x = r.traverse('folder/objBasic') r.traverse('folder/objBasic')
self.assertEqual(r.get('a',''), 'abcd') self.assertEqual(r.get('a', ''), 'abcd')
self.f1.objBasic.traversal = [] self.f1.objBasic.traversal = []
...@@ -123,17 +130,16 @@ class TestBaseRequestPT(TestCase): ...@@ -123,17 +130,16 @@ class TestBaseRequestPT(TestCase):
def test_hook_chain_redirect(self): def test_hook_chain_redirect(self):
r = self.makeBaseRequest() r = self.makeBaseRequest()
check = [] check = []
self.f1.objBasic.traversal = [ (pt_chain_test, (r, 'a')), self.f1.objBasic.traversal = [
(pt_chain_test, (r, 'a')),
(pt_chain_test, (r, 'b')), (pt_chain_test, (r, 'b')),
(pt_chain_test, (r, 'c')), (pt_chain_test, (r, 'c')),
(pt_simple_redirect, (check,)), (pt_simple_redirect, (check,)),
(pt_simple_redirect, (1,)), (pt_simple_redirect, (1,)),
(pt_chain_test, (r, 'd'))] (pt_chain_test, (r, 'd')),
]
x = r.traverse('folder/objBasic') x = r.traverse('folder/objBasic')
self.assertEqual(r.get('a',''), 'abc') self.assertEqual(r.get('a', ''), 'abc')
self.assertEqual(x, check) self.assertEqual(x, check)
def test_suite():
return TestSuite( ( makeSuite(TestBaseRequestPT), ) )
import doctest
from zope.interface import implements from zope.interface import implements
from zope.publisher.interfaces.browser import IDefaultBrowserLayer from zope.publisher.interfaces.browser import IDefaultBrowserLayer
from zope.publisher.interfaces.browser import IBrowserRequest from zope.publisher.interfaces.browser import IBrowserRequest
...@@ -5,6 +7,7 @@ from zope.publisher.skinnable import setDefaultSkin ...@@ -5,6 +7,7 @@ from zope.publisher.skinnable import setDefaultSkin
from ZPublisher import Retry from ZPublisher import Retry
from ZODB.POSException import ConflictError from ZODB.POSException import ConflictError
class Tracer: class Tracer:
"""Trace used to record pathway taken through the publisher """Trace used to record pathway taken through the publisher
machinery. And provide framework for spewing out exceptions at machinery. And provide framework for spewing out exceptions at
...@@ -23,7 +26,7 @@ class Tracer: ...@@ -23,7 +26,7 @@ class Tracer:
def showTracedPath(self): def showTracedPath(self):
for arg in self.tracedPath: for arg in self.tracedPath:
print arg print(arg)
def possiblyRaiseException(self, context): def possiblyRaiseException(self, context):
exceptions = tracer.exceptions.get(context, None) exceptions = tracer.exceptions.get(context, None)
...@@ -35,9 +38,9 @@ class Tracer: ...@@ -35,9 +38,9 @@ class Tracer:
self.append('raising %s from %s' % (exceptionShortName, context)) self.append('raising %s from %s' % (exceptionShortName, context))
raise exception raise exception
tracer = Tracer() tracer = Tracer()
class TransactionsManager: class TransactionsManager:
"""Mock TransactionManager to replace """Mock TransactionManager to replace
Zope2.App.startup.TransactionsManager. Zope2.App.startup.TransactionsManager.
...@@ -58,6 +61,7 @@ class TransactionsManager: ...@@ -58,6 +61,7 @@ class TransactionsManager:
zpublisher_transactions_manager = TransactionsManager() zpublisher_transactions_manager = TransactionsManager()
def zpublisher_exception_hook(published, request, t, v, traceback): def zpublisher_exception_hook(published, request, t, v, traceback):
"""Mock zpublisher_exception_hook to replace """Mock zpublisher_exception_hook to replace
Zope2.App.startup.zpublisher_exception_hook Zope2.App.startup.zpublisher_exception_hook
...@@ -71,6 +75,7 @@ def zpublisher_exception_hook(published, request, t, v, traceback): ...@@ -71,6 +75,7 @@ def zpublisher_exception_hook(published, request, t, v, traceback):
tracer.possiblyRaiseException('zpublisher_exception_hook') tracer.possiblyRaiseException('zpublisher_exception_hook')
return 'zpublisher_exception_hook' return 'zpublisher_exception_hook'
class Object: class Object:
"""Mock object for traversing to. """Mock object for traversing to.
""" """
...@@ -80,6 +85,7 @@ class Object: ...@@ -80,6 +85,7 @@ class Object:
tracer.possiblyRaiseException('__call__') tracer.possiblyRaiseException('__call__')
return '__call__' return '__call__'
class Response: class Response:
"""Mock Response to replace ZPublisher.HTTPResponse.HTTPResponse. """Mock Response to replace ZPublisher.HTTPResponse.HTTPResponse.
""" """
...@@ -87,6 +93,7 @@ class Response: ...@@ -87,6 +93,7 @@ class Response:
def setBody(self, a): def setBody(self, a):
pass pass
class Request: class Request:
"""Mock Request to replace ZPublisher.HTTPRequest.HTTPRequest. """Mock Request to replace ZPublisher.HTTPRequest.HTTPRequest.
""" """
...@@ -125,6 +132,7 @@ class Request: ...@@ -125,6 +132,7 @@ class Request:
r.retry_count = self.retry_count r.retry_count = self.retry_count
return r return r
class RequestWithSkinCheck(Request): class RequestWithSkinCheck(Request):
def traverse(self, path, validated_hook): def traverse(self, path, validated_hook):
if IDefaultBrowserLayer.providedBy(self): if IDefaultBrowserLayer.providedBy(self):
...@@ -331,6 +339,7 @@ def testPublisher(): ...@@ -331,6 +339,7 @@ def testPublisher():
""" """
pass pass
class ObjectNotFound: class ObjectNotFound:
"""Mock object for traversing to. """Mock object for traversing to.
""" """
...@@ -357,6 +366,7 @@ class PathRequest(Request): ...@@ -357,6 +366,7 @@ class PathRequest(Request):
else: else:
return ObjectNotFound() return ObjectNotFound()
def testPublishPath(): def testPublishPath():
""" """
Tests to ensure that publish passes paths through to the request without Tests to ensure that publish passes paths through to the request without
...@@ -388,10 +398,7 @@ def testPublishPath(): ...@@ -388,10 +398,7 @@ def testPublishPath():
commit commit
""" """
pass
import doctest
def test_suite(): def test_suite():
return doctest.DocTestSuite() return doctest.DocTestSuite()
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
import unittest import unittest
class ConvertersTests(unittest.TestCase): class ConvertersTests(unittest.TestCase):
def test_field2string_with_string(self): def test_field2string_with_string(self):
...@@ -29,21 +30,12 @@ class ConvertersTests(unittest.TestCase): ...@@ -29,21 +30,12 @@ class ConvertersTests(unittest.TestCase):
def test_field2string_with_filelike_object(self): def test_field2string_with_filelike_object(self):
from ZPublisher.Converters import field2string from ZPublisher.Converters import field2string
to_convert = 'to_convert' to_convert = 'to_convert'
class Filelike: class Filelike:
def read(self): def read(self):
return to_convert return to_convert
self.assertEqual(field2string(Filelike()), to_convert) self.assertEqual(field2string(Filelike()), to_convert)
#TODO def test_field2text....
#TODO def test_field2required....
#TODO def test_field2int....
#TODO def test_field2float....
#TODO def test_field2tokens....
def test_field2lines_with_list(self): def test_field2lines_with_list(self):
from ZPublisher.Converters import field2lines from ZPublisher.Converters import field2lines
to_convert = ['one', 'two'] to_convert = ['one', 'two']
...@@ -69,19 +61,6 @@ class ConvertersTests(unittest.TestCase): ...@@ -69,19 +61,6 @@ class ConvertersTests(unittest.TestCase):
to_convert = 'abc\ndef\nghi' to_convert = 'abc\ndef\nghi'
self.assertEqual(field2lines(to_convert), to_convert.splitlines()) self.assertEqual(field2lines(to_convert), to_convert.splitlines())
#TODO def test_field2date....
#TODO def test_field2date_international....
#TODO def test_field2boolean....
#TODO def test_field2ustring....
#TODO def test_field2utokens....
#TODO def test_field2utext....
def test_field2ulines_with_list(self): def test_field2ulines_with_list(self):
from ZPublisher.Converters import field2ulines from ZPublisher.Converters import field2ulines
to_convert = [u'one', 'two'] to_convert = [u'one', 'two']
...@@ -108,7 +87,3 @@ class ConvertersTests(unittest.TestCase): ...@@ -108,7 +87,3 @@ class ConvertersTests(unittest.TestCase):
from ZPublisher.Converters import field2ulines from ZPublisher.Converters import field2ulines
to_convert = u'abc\ndef\nghi' to_convert = u'abc\ndef\nghi'
self.assertEqual(field2ulines(to_convert), to_convert.splitlines()) self.assertEqual(field2ulines(to_convert), to_convert.splitlines())
def test_suite():
return unittest.TestSuite((unittest.makeSuite(ConvertersTests),))
...@@ -40,8 +40,11 @@ class ExceptionRaiser3(SimpleItem): ...@@ -40,8 +40,11 @@ class ExceptionRaiser3(SimpleItem):
def test_suite(): def test_suite():
return unittest.TestSuite([ return unittest.TestSuite([
FunctionalDocFileSuite('exception_handling.txt', FunctionalDocFileSuite(
globs={'ExceptionRaiser1': ExceptionRaiser1, 'exception_handling.txt',
globs={
'ExceptionRaiser1': ExceptionRaiser1,
'ExceptionRaiser2': ExceptionRaiser2, 'ExceptionRaiser2': ExceptionRaiser2,
'ExceptionRaiser3': ExceptionRaiser3,}), 'ExceptionRaiser3': ExceptionRaiser3,
}),
]) ])
...@@ -11,19 +11,21 @@ ...@@ -11,19 +11,21 @@
# FOR A PARTICULAR PURPOSE. # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Test mapply() function
"""
import unittest import unittest
import ExtensionClass import ExtensionClass
import Acquisition import Acquisition
from ZPublisher.mapply import mapply from ZPublisher.mapply import mapply
class MapplyTests(unittest.TestCase): class MapplyTests(unittest.TestCase):
def testMethod(self): def testMethod(self):
def compute(a,b,c=4):
def compute(a, b, c=4):
return '%d%d%d' % (a, b, c) return '%d%d%d' % (a, b, c)
values = {'a':2, 'b':3, 'c':5}
values = {'a': 2, 'b': 3, 'c': 5}
v = mapply(compute, (), values) v = mapply(compute, (), values)
self.assertEqual(v, '235') self.assertEqual(v, '235')
...@@ -31,12 +33,16 @@ class MapplyTests(unittest.TestCase): ...@@ -31,12 +33,16 @@ class MapplyTests(unittest.TestCase):
self.assertEqual(v, '735') self.assertEqual(v, '735')
def testClass(self): def testClass(self):
values = {'a':2, 'b':3, 'c':5} values = {'a': 2, 'b': 3, 'c': 5}
class c(object): class c(object):
a = 3 a = 3
def __call__(self, b, c=4): def __call__(self, b, c=4):
return '%d%d%d' % (self.a, b, c) return '%d%d%d' % (self.a, b, c)
compute = __call__ compute = __call__
cc = c() cc = c()
v = mapply(cc, (), values) v = mapply(cc, (), values)
self.assertEqual(v, '335') self.assertEqual(v, '335')
...@@ -91,8 +97,3 @@ class MapplyTests(unittest.TestCase): ...@@ -91,8 +97,3 @@ class MapplyTests(unittest.TestCase):
ob = NoCallButAcquisition().__of__(Root()) ob = NoCallButAcquisition().__of__(Root())
self.assertRaises(TypeError, mapply, ob, (), {}) self.assertRaises(TypeError, mapply, ob, (), {})
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(MapplyTests))
return suite
import unittest import unittest
from DateTime import DateTime from DateTime import DateTime
class FauxResponse: class FauxResponse:
def __init__(self): def __init__(self):
...@@ -16,10 +17,12 @@ class FauxResponse: ...@@ -16,10 +17,12 @@ class FauxResponse:
def setStatus(self, status): def setStatus(self, status):
self._status = status self._status = status
class FauxInstance: class FauxInstance:
def __init__(self, **kw): def __init__(self, **kw):
self.__dict__.update(kw) self.__dict__.update(kw)
class XMLRPCResponseTests(unittest.TestCase): class XMLRPCResponseTests(unittest.TestCase):
def _getTargetClass(self): def _getTargetClass(self):
...@@ -87,7 +90,8 @@ class XMLRPCResponseTests(unittest.TestCase): ...@@ -87,7 +90,8 @@ class XMLRPCResponseTests(unittest.TestCase):
def test_instanceattribute_recursive(self): def test_instanceattribute_recursive(self):
# Instance "flattening" should work recursively, ad infinitum # Instance "flattening" should work recursively, ad infinitum
import xmlrpclib import xmlrpclib
body = FauxInstance(public=FauxInstance(public=FauxInstance(_secret='abc', public='def'))) body = FauxInstance(public=FauxInstance(public=FauxInstance(
_secret='abc', public='def')))
faux = FauxResponse() faux = FauxResponse()
response = self._makeOne(faux) response = self._makeOne(faux)
response.setBody(body) response.setBody(body)
...@@ -148,7 +152,8 @@ class XMLRPCResponseTests(unittest.TestCase): ...@@ -148,7 +152,8 @@ class XMLRPCResponseTests(unittest.TestCase):
def test_zopedatetimeattribute_recursive(self): def test_zopedatetimeattribute_recursive(self):
# DateTime encoding should work recursively # DateTime encoding should work recursively
import xmlrpclib import xmlrpclib
body = FauxInstance(public=FauxInstance(public=DateTime('2006-05-24 07:00:00 GMT+0'))) body = FauxInstance(public=FauxInstance(
public=DateTime('2006-05-24 07:00:00 GMT+0')))
faux = FauxResponse() faux = FauxResponse()
response = self._makeOne(faux) response = self._makeOne(faux)
response.setBody(body) response.setBody(body)
...@@ -185,7 +190,10 @@ class XMLRPCResponseTests(unittest.TestCase): ...@@ -185,7 +190,10 @@ class XMLRPCResponseTests(unittest.TestCase):
# Cannot marshal functions or methods, obviously # Cannot marshal functions or methods, obviously
import sys import sys
import xmlrpclib import xmlrpclib
def foo(): pass
def foo():
pass
body = FauxInstance(public=foo) body = FauxInstance(public=foo)
faux = FauxResponse() faux = FauxResponse()
response = self._makeOne(faux) response = self._makeOne(faux)
...@@ -208,7 +216,3 @@ class XMLRPCResponseTests(unittest.TestCase): ...@@ -208,7 +216,3 @@ class XMLRPCResponseTests(unittest.TestCase):
data, method = xmlrpclib.loads(faux._body) data, method = xmlrpclib.loads(faux._body)
data = data[0] data = data[0]
self.assertEqual(data, {'': True}) self.assertEqual(data, {'': True})
def test_suite():
return unittest.TestSuite((unittest.makeSuite(XMLRPCResponseTests),))
from StringIO import StringIO from StringIO import StringIO
from sys import modules, exc_info from sys import modules, exc_info
from unittest import TestCase, TestSuite, makeSuite, main from unittest import TestCase
from ZODB.POSException import ConflictError from ZODB.POSException import ConflictError
from zope.interface.verify import verifyObject from zope.interface.verify import verifyObject
...@@ -9,19 +9,24 @@ from zope.event import subscribers ...@@ -9,19 +9,24 @@ from zope.event import subscribers
from ZPublisher.Publish import publish, Retry from ZPublisher.Publish import publish, Retry
from ZPublisher.BaseRequest import BaseRequest from ZPublisher.BaseRequest import BaseRequest
from ZPublisher.HTTPResponse import HTTPResponse from ZPublisher.HTTPResponse import HTTPResponse
from ZPublisher.pubevents import PubStart, PubSuccess, PubFailure, \ from ZPublisher.pubevents import (
PubAfterTraversal, PubBeforeCommit, PubBeforeAbort, \ PubStart, PubSuccess, PubFailure,
PubBeforeStreaming PubAfterTraversal, PubBeforeCommit, PubBeforeAbort,
from ZPublisher.interfaces import \ PubBeforeStreaming,
IPubStart, IPubEnd, IPubSuccess, IPubFailure, \ )
IPubAfterTraversal, IPubBeforeCommit, \ from ZPublisher.interfaces import (
IPubBeforeStreaming IPubStart, IPubEnd, IPubSuccess, IPubFailure,
IPubAfterTraversal, IPubBeforeCommit,
IPubBeforeStreaming,
)
PUBMODULE = 'TEST_testpubevents' PUBMODULE = 'TEST_testpubevents'
_g=globals() _g = globals()
class TestInterface(TestCase): class TestInterface(TestCase):
def testPubStart(self): def testPubStart(self):
verifyObject(IPubStart, PubStart(_Request())) verifyObject(IPubStart, PubStart(_Request()))
...@@ -32,8 +37,10 @@ class TestInterface(TestCase): ...@@ -32,8 +37,10 @@ class TestInterface(TestCase):
def testPubFailure(self): def testPubFailure(self):
# get some exc info # get some exc info
try: raise ValueError() try:
except: exc = exc_info() raise ValueError()
except Exception:
exc = exc_info()
e = PubFailure(_Request(), exc, False) e = PubFailure(_Request(), exc, False)
verifyObject(IPubFailure, e) verifyObject(IPubFailure, e)
verifyObject(IPubEnd, e) verifyObject(IPubEnd, e)
...@@ -50,6 +57,7 @@ class TestInterface(TestCase): ...@@ -50,6 +57,7 @@ class TestInterface(TestCase):
e = PubBeforeStreaming(_Response()) e = PubBeforeStreaming(_Response())
verifyObject(IPubBeforeStreaming, e) verifyObject(IPubBeforeStreaming, e)
class TestPubEvents(TestCase): class TestPubEvents(TestCase):
def setUp(self): def setUp(self):
self._saved_subscribers = subscribers[:] self._saved_subscribers = subscribers[:]
...@@ -59,11 +67,13 @@ class TestPubEvents(TestCase): ...@@ -59,11 +67,13 @@ class TestPubEvents(TestCase):
self.request = _Request() self.request = _Request()
def tearDown(self): def tearDown(self):
if PUBMODULE in modules: del modules[PUBMODULE] if PUBMODULE in modules:
del modules[PUBMODULE]
subscribers[:] = self._saved_subscribers subscribers[:] = self._saved_subscribers
def testSuccess(self): def testSuccess(self):
r = self.request; r.action = 'succeed' r = self.request
r.action = 'succeed'
publish(r, PUBMODULE, [None]) publish(r, PUBMODULE, [None])
events = self.reporter.events events = self.reporter.events
self.assertEqual(len(events), 4) self.assertEqual(len(events), 4)
...@@ -78,7 +88,8 @@ class TestPubEvents(TestCase): ...@@ -78,7 +88,8 @@ class TestPubEvents(TestCase):
self.assertEqual(events[2].request, r) self.assertEqual(events[2].request, r)
def testFailureReturn(self): def testFailureReturn(self):
r = self.request; r.action = 'fail_return' r = self.request
r.action = 'fail_return'
publish(r, PUBMODULE, [None]) publish(r, PUBMODULE, [None])
events = self.reporter.events events = self.reporter.events
self.assertEqual(len(events), 3) self.assertEqual(len(events), 3)
...@@ -93,7 +104,8 @@ class TestPubEvents(TestCase): ...@@ -93,7 +104,8 @@ class TestPubEvents(TestCase):
self.assertEqual(len(events[2].exc_info), 3) self.assertEqual(len(events[2].exc_info), 3)
def testFailureException(self): def testFailureException(self):
r = self.request; r.action = 'fail_exception' r = self.request
r.action = 'fail_exception'
self.assertRaises(Exception, publish, r, PUBMODULE, [None]) self.assertRaises(Exception, publish, r, PUBMODULE, [None])
events = self.reporter.events events = self.reporter.events
self.assertEqual(len(events), 3) self.assertEqual(len(events), 3)
...@@ -109,7 +121,8 @@ class TestPubEvents(TestCase): ...@@ -109,7 +121,8 @@ class TestPubEvents(TestCase):
self.assertEqual(len(events[2].exc_info), 3) self.assertEqual(len(events[2].exc_info), 3)
def testFailureConflict(self): def testFailureConflict(self):
r = self.request; r.action = 'conflict' r = self.request
r.action = 'conflict'
publish(r, PUBMODULE, [None]) publish(r, PUBMODULE, [None])
events = self.reporter.events events = self.reporter.events
self.assertEqual(len(events), 7) self.assertEqual(len(events), 7)
...@@ -135,7 +148,6 @@ class TestPubEvents(TestCase): ...@@ -135,7 +148,6 @@ class TestPubEvents(TestCase):
self.assert_(isinstance(events[6], PubSuccess)) self.assert_(isinstance(events[6], PubSuccess))
def testStreaming(self): def testStreaming(self):
out = StringIO() out = StringIO()
response = HTTPResponse(stdout=out) response = HTTPResponse(stdout=out)
response.write('datachunk1') response.write('datachunk1')
...@@ -149,19 +161,26 @@ class TestPubEvents(TestCase): ...@@ -149,19 +161,26 @@ class TestPubEvents(TestCase):
self.assertTrue('datachunk1datachunk2' in out.getvalue()) self.assertTrue('datachunk1datachunk2' in out.getvalue())
# Auxiliaries
def _succeed(): def _succeed():
''' ''' ''' '''
return 'success' return 'success'
class _Application(object): pass
class _Application(object):
pass
class _Reporter(object): class _Reporter(object):
def __init__(self): self.events = [] def __init__(self):
def __call__(self, event): self.events.append(event) self.events = []
def __call__(self, event):
self.events.append(event)
class _Response(object): class _Response(object):
def setBody(*unused): pass def setBody(*unused):
pass
class _Request(BaseRequest): class _Request(BaseRequest):
...@@ -174,7 +193,9 @@ class _Request(BaseRequest): ...@@ -174,7 +193,9 @@ class _Request(BaseRequest):
self['PATH_INFO'] = self['URL'] = '' self['PATH_INFO'] = self['URL'] = ''
self.steps = [] self.steps = []
def supports_retry(self): return True def supports_retry(self):
return True
def retry(self): def retry(self):
r = self.__class__() r = self.__class__()
r.action = 'succeed' r.action = 'succeed'
...@@ -182,25 +203,29 @@ class _Request(BaseRequest): ...@@ -182,25 +203,29 @@ class _Request(BaseRequest):
def traverse(self, *unused, **unused_kw): def traverse(self, *unused, **unused_kw):
action = self.action action = self.action
if action.startswith('fail'): raise Exception(action) if action.startswith('fail'):
if action == 'conflict': raise ConflictError() raise Exception(action)
if action == 'succeed': return _succeed if action == 'conflict':
else: raise ValueError('unknown action: %s' % action) raise ConflictError()
if action == 'succeed':
return _succeed
else:
raise ValueError('unknown action: %s' % action)
def close(self):
# override to get rid of the 'EndRequestEvent' notification # override to get rid of the 'EndRequestEvent' notification
def close(self): pass pass
# define things necessary for publication # define things necessary for publication
bobo_application = _Application() bobo_application = _Application()
def zpublisher_exception_hook(parent, request, *unused): def zpublisher_exception_hook(parent, request, *unused):
action = request.action action = request.action
if action == 'fail_return': return 0 if action == 'fail_return':
if action == 'fail_exception': raise Exception() return 0
if action == 'conflict': raise Retry() if action == 'fail_exception':
raise Exception()
if action == 'conflict':
raise Retry()
raise ValueError('unknown action: %s' % action) raise ValueError('unknown action: %s' % action)
def test_suite():
return TestSuite((makeSuite(c) for c in (TestPubEvents, TestInterface)))
...@@ -33,6 +33,7 @@ from ZODB.POSException import ConflictError ...@@ -33,6 +33,7 @@ from ZODB.POSException import ConflictError
from DateTime.DateTime import DateTime from DateTime.DateTime import DateTime
WRAPPERS = xmlrpclib.WRAPPERS + (DateTime, ) WRAPPERS = xmlrpclib.WRAPPERS + (DateTime, )
def dump_instance(self, value, write): def dump_instance(self, value, write):
# Check for special wrappers # Check for special wrappers
if value.__class__ in WRAPPERS: if value.__class__ in WRAPPERS:
...@@ -95,6 +96,7 @@ def parse_input(data): ...@@ -95,6 +96,7 @@ def parse_input(data):
######################################################################## ########################################################################
# Possible implementation helpers: # Possible implementation helpers:
class Response: class Response:
"""Customized Response that handles XML-RPC-specific details. """Customized Response that handles XML-RPC-specific details.
...@@ -115,16 +117,22 @@ class Response: ...@@ -115,16 +117,22 @@ class Response:
# we have to use delegation, rather than inheritance to do the # we have to use delegation, rather than inheritance to do the
# customization. # customization.
def __init__(self, real): self.__dict__['_real']=real def __init__(self, real):
self.__dict__['_real'] = real
def __getattr__(self, name):
return getattr(self._real, name)
def __getattr__(self, name): return getattr(self._real, name) def __setattr__(self, name, v):
def __setattr__(self, name, v): return setattr(self._real, name, v) return setattr(self._real, name, v)
def __delattr__(self, name): return delattr(self._real, name)
def __delattr__(self, name):
return delattr(self._real, name)
def setBody(self, body, title='', is_error=0, bogus_str_search=None): def setBody(self, body, title='', is_error=0, bogus_str_search=None):
if isinstance(body, xmlrpclib.Fault): if isinstance(body, xmlrpclib.Fault):
# Convert Fault object to XML-RPC response. # Convert Fault object to XML-RPC response.
body=xmlrpclib.dumps(body, methodresponse=1, allow_none=True) body = xmlrpclib.dumps(body, methodresponse=1, allow_none=True)
else: else:
# Marshall our body as an XML-RPC response. Strings will be sent # Marshall our body as an XML-RPC response. Strings will be sent
# strings, integers as integers, etc. We do *not* convert # strings, integers as integers, etc. We do *not* convert
...@@ -161,8 +169,8 @@ class Response: ...@@ -161,8 +169,8 @@ class Response:
return self._real.exception(fatal=fatal, info=info) return self._real.exception(fatal=fatal, info=info)
# Create an appropriate Fault object. Containing error information # Create an appropriate Fault object. Containing error information
Fault=xmlrpclib.Fault Fault = xmlrpclib.Fault
f=None f = None
try: try:
# Strip HTML tags from the error value # Strip HTML tags from the error value
vstr = str(v) vstr = str(v)
...@@ -175,15 +183,15 @@ class Response: ...@@ -175,15 +183,15 @@ class Response:
else: else:
value = '%s - %s' % (t, vstr) value = '%s - %s' % (t, vstr)
if isinstance(v, Fault): if isinstance(v, Fault):
f=v f = v
elif isinstance(v, Exception): elif isinstance(v, Exception):
f=Fault(-1, 'Unexpected Zope exception: %s' % value) f = Fault(-1, 'Unexpected Zope exception: %s' % value)
else: else:
f=Fault(-2, 'Unexpected Zope error value: %s' % value) f = Fault(-2, 'Unexpected Zope error value: %s' % value)
except ConflictError: except ConflictError:
raise raise
except: except Exception:
f=Fault(-3, "Unknown Zope fault type") f = Fault(-3, "Unknown Zope fault type")
# Do the damage. # Do the damage.
self.setBody(f) self.setBody(f)
...@@ -191,4 +199,4 @@ class Response: ...@@ -191,4 +199,4 @@ class Response:
return tb return tb
response=Response response = Response
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment