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