Commit 8f0efe11 authored by Shane Hathaway's avatar Shane Hathaway

Merged RestrictedPythonBranch!

parent d7508240
...@@ -84,20 +84,18 @@ ...@@ -84,20 +84,18 @@
############################################################################## ##############################################################################
'''Add security system support to Document Templates '''Add security system support to Document Templates
$Id: DTML.py,v 1.2 2000/05/11 18:54:13 jim Exp $''' $Id: DTML.py,v 1.3 2001/04/27 20:27:37 shane Exp $'''
__version__='$Revision: 1.2 $'[11:-2] __version__='$Revision: 1.3 $'[11:-2]
from DocumentTemplate import DT_Util from DocumentTemplate import DT_Util
import SecurityManagement import SecurityManagement, string, math, whrandom, random
# Allow access to unprotected attributes # Allow access to unprotected attributes
DT_Util.TemplateDict.__allow_access_to_unprotected_subobjects__=1 DT_Util.TemplateDict.__allow_access_to_unprotected_subobjects__=1
DT_Util.string.__allow_access_to_unprotected_subobjects__=1 string.__allow_access_to_unprotected_subobjects__=1
DT_Util.math.__allow_access_to_unprotected_subobjects__=1 math.__allow_access_to_unprotected_subobjects__=1
DT_Util.whrandom.__allow_access_to_unprotected_subobjects__=1 whrandom.__allow_access_to_unprotected_subobjects__=1
random.__allow_access_to_unprotected_subobjects__=1
try: DT_Util.random.__allow_access_to_unprotected_subobjects__=1
except: pass
# Add security testing capabilities # Add security testing capabilities
......
...@@ -85,11 +85,11 @@ ...@@ -85,11 +85,11 @@
__doc__='''Support for owned objects __doc__='''Support for owned objects
$Id: Owned.py,v 1.10 2001/01/12 15:23:05 chrism Exp $''' $Id: Owned.py,v 1.11 2001/04/27 20:27:37 shane Exp $'''
__version__='$Revision: 1.10 $'[11:-2] __version__='$Revision: 1.11 $'[11:-2]
import Globals, urlparse, SpecialUsers, ExtensionClass, string import Globals, urlparse, SpecialUsers, ExtensionClass, string
from AccessControl import getSecurityManager from AccessControl import getSecurityManager, Unauthorized
from Acquisition import aq_get, aq_parent, aq_base from Acquisition import aq_get, aq_parent, aq_base
UnownableOwner=[] UnownableOwner=[]
...@@ -201,7 +201,7 @@ class Owned(ExtensionClass.Base): ...@@ -201,7 +201,7 @@ class Owned(ExtensionClass.Base):
urlparse.urlparse(REQUEST['HTTP_REFERER'])[:3]) urlparse.urlparse(REQUEST['HTTP_REFERER'])[:3])
__traceback_info__=want_referer, got_referer __traceback_info__=want_referer, got_referer
if (want_referer != got_referer or security.calledByExecutable()): if (want_referer != got_referer or security.calledByExecutable()):
raise 'Unauthorized', ( raise Unauthorized, (
'manage_takeOwnership was called from an invalid context' 'manage_takeOwnership was called from an invalid context'
) )
......
...@@ -84,8 +84,8 @@ ...@@ -84,8 +84,8 @@
############################################################################## ##############################################################################
__doc__='''Collect rules for access to objects that don\'t have roles. __doc__='''Collect rules for access to objects that don\'t have roles.
$Id: SimpleObjectPolicies.py,v 1.6 2001/01/10 20:21:03 chrism Exp $''' $Id: SimpleObjectPolicies.py,v 1.7 2001/04/27 20:27:37 shane Exp $'''
__version__='$Revision: 1.6 $'[11:-2] __version__='$Revision: 1.7 $'[11:-2]
_noroles=[] # this is imported from various places _noroles=[] # this is imported from various places
...@@ -100,12 +100,15 @@ ContainerAssertions={ ...@@ -100,12 +100,15 @@ ContainerAssertions={
type({}): 1, type({}): 1,
} }
class _dummy_class: pass
from DocumentTemplate.DT_Util import TemplateDict from DocumentTemplate.DT_Util import TemplateDict
# Temporarily create a DictInstance so that we can mark its type as # Temporarily create a DictInstance so that we can mark its type as
# being a key in the ContainerAssertions. # being a key in the ContainerAssertions.
templateDict = TemplateDict() templateDict = TemplateDict()
try: try:
dictInstance = templateDict(dummy=1)[0] dictInstance = templateDict(dummy=1)[0]
if type(dictInstance) is not type(_dummy_class()):
ContainerAssertions[type(dictInstance)]=1 ContainerAssertions[type(dictInstance)]=1
except: except:
# Hmm, this may cause _() and _.namespace() to fail. # Hmm, this may cause _() and _.namespace() to fail.
......
...@@ -84,7 +84,7 @@ ...@@ -84,7 +84,7 @@
############################################################################## ##############################################################################
"""Access control package""" """Access control package"""
__version__='$Revision: 1.148 $'[11:-2] __version__='$Revision: 1.149 $'[11:-2]
import Globals, socket, SpecialUsers,re import Globals, socket, SpecialUsers,re
import os import os
...@@ -98,7 +98,7 @@ from App.ImageFile import ImageFile ...@@ -98,7 +98,7 @@ from App.ImageFile import ImageFile
from Role import RoleManager from Role import RoleManager
from PermissionRole import _what_not_even_god_should_do, rolesForPermissionOn from PermissionRole import _what_not_even_god_should_do, rolesForPermissionOn
from AuthEncoding import pw_validate from AuthEncoding import pw_validate
from AccessControl import getSecurityManager from AccessControl import getSecurityManager, Unauthorized
from AccessControl.SecurityManagement import newSecurityManager from AccessControl.SecurityManagement import newSecurityManager
from AccessControl.SecurityManagement import noSecurityManager from AccessControl.SecurityManagement import noSecurityManager
from AccessControl.ZopeSecurityPolicy import _noroles from AccessControl.ZopeSecurityPolicy import _noroles
...@@ -572,7 +572,7 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager, ...@@ -572,7 +572,7 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager,
except: except:
noSecurityManager() noSecurityManager()
raise raise
except 'Unauthorized': pass except Unauthorized: pass
return 0 return 0
def validate(self, request, auth='', roles=_noroles): def validate(self, request, auth='', roles=_noroles):
......
...@@ -83,92 +83,66 @@ ...@@ -83,92 +83,66 @@
# #
############################################################################## ##############################################################################
__version__='$Revision: 1.9 $'[11:-2] __version__='$Revision: 1.2 $'[11:-2]
from zbytecodehacks.VSExec import SafeBlock, GuardedBinaryOps, \ from RestrictedPython.Guards import safe_builtins, _full_read_guard, \
UntupleFunction, RedirectWrites, WriteGuard, RedirectReads, ReadGuard, \ full_write_guard
LoopLimits, bind from RestrictedPython.Utilities import utility_builtins
from DocumentTemplate.VSEval import careful_mul from SecurityManagement import getSecurityManager
from DocumentTemplate.DT_Util import TemplateDict, \ from SecurityInfo import secureModule
careful_pow, d, ValidationError
from DocumentTemplate.DT_Var import special_formats
from AccessControl import getSecurityManager
try:
from AccessControl import secureModule
except:
# Allow pre-2.3 Zopes to operate without import capability
def secureModule(*args):
return
safefuncs = TemplateDict()
safebin = {}
for name in ('None', 'abs', 'chr', 'divmod', 'float', 'hash', 'hex', 'int',
'len', 'max', 'min', 'oct', 'ord', 'round', 'str'):
safebin[name] = d[name]
for name in ('range', 'pow', 'DateTime', 'test', 'namespace', 'render'):
safebin[name] = getattr(safefuncs, name)
for name in ('ArithmeticError', 'AssertionError', 'AttributeError',
'EOFError', 'EnvironmentError', 'FloatingPointError',
'IOError', 'ImportError', 'IndexError', 'KeyError',
'LookupError', 'NameError', 'OSError', 'OverflowError',
'RuntimeError', 'StandardError', 'SyntaxError',
'TypeError', 'ValueError', 'ZeroDivisionError',
'apply', 'callable', 'cmp', 'complex', 'isinstance',
'issubclass', 'long', 'repr'):
safebin[name] = __builtins__[name]
def same_type(arg1, *args): Unauthorized = 'Unauthorized'
'''Compares the type of two or more objects.'''
base = getattr(arg1, 'aq_base', arg1)
t = type(base)
for arg in args:
argbase = getattr(arg, 'aq_base', arg)
if type(argbase) is not t:
return 0
return 1
safebin['same_type'] = same_type
_marker = [] # Create a new marker object. _marker = [] # Create a new marker object.
safe_builtins = safe_builtins.copy()
safe_builtins.update(utility_builtins)
def aq_validate(inst, obj, name, v, validate): def aq_validate(inst, obj, name, v, validate):
return validate(inst, obj, name, v) return validate(inst, obj, name, v)
def __careful_getattr__(inst, name, default=_marker): def guarded_getattr(inst, name, default=_marker):
if name[:1]!='_': if name[:1] != '_':
try: # Try to get the attribute normally so that unusual
# Filter out the objects we can't access. # exceptions are caught early.
if hasattr(inst, 'aq_acquire'): try: v = getattr(inst, name)
return inst.aq_acquire(name, aq_validate,
getSecurityManager().validate)
# Or just try to get the attribute directly.
v = getattr(inst, name)
except AttributeError: except AttributeError:
if default is not _marker: if default is not _marker:
return default return default
raise raise AttributeError
if getSecurityManager().validate(inst,inst,name,v): validate = getSecurityManager().validate
# Filter out the objects we can't access.
if hasattr(inst, 'aq_acquire'):
return inst.aq_acquire(name, aq_validate, validate)
# Or just try to get the attribute directly.
if validate(inst, inst, name, v):
return v return v
raise ValidationError, name raise Unauthorized, name
safebin['getattr'] = __careful_getattr__ safe_builtins['getattr'] = guarded_getattr
def __careful_setattr__(object, name, value): def guarded_hasattr(object, name):
setattr(WriteGuard(object), name, value)
safebin['setattr'] = __careful_setattr__
def __careful_delattr__(object, name):
delattr(WriteGuard(object), name)
safebin['delattr'] = __careful_delattr__
def __careful_hasattr__(object, name):
try: try:
__careful_getattr__(object, name) guarded_getattr(object, name)
except (AttributeError, ValidationError): except (AttributeError, Unauthorized):
return 0 return 0
return 1 return 1
safebin['hasattr'] = __careful_hasattr__ safe_builtins['hasattr'] = guarded_hasattr
slicetype = type(slice(0))
def guarded_getitem(object, index):
if type(object) is slicetype:
# We don't guard slices
return object[index.start:index.stop]
v = object[index]
if getSecurityManager().validate(object, object, index, v):
return v
raise Unauthorized, 'unauthorized access to element %s' % `i`
def __careful_filter__(f, seq, skip_unauthorized=0): full_read_guard = _full_read_guard(guarded_getattr, guarded_getitem)
def guarded_filter(f, seq, skip_unauthorized=0):
if type(seq) is type(''): if type(seq) is type(''):
return filter(f, seq) return filter(f, seq)
if f is None: if f is None:
...@@ -180,34 +154,21 @@ def __careful_filter__(f, seq, skip_unauthorized=0): ...@@ -180,34 +154,21 @@ def __careful_filter__(f, seq, skip_unauthorized=0):
if v(seq, seq, None, el): if v(seq, seq, None, el):
if f(el): a(el) if f(el): a(el)
elif not skip_unauthorized: elif not skip_unauthorized:
raise ValidationError, 'unauthorized access to element' raise Unauthorized, 'unauthorized access to element'
return result return result
safebin['filter'] = __careful_filter__ safe_builtins['filter'] = guarded_filter
def __careful_list__(seq): def guarded_map(f, *seqs):
if type(seq) is type(''):
raise TypeError, 'cannot convert string to list'
return list(seq)
safebin['list'] = __careful_list__
def __careful_tuple__(seq):
if type(seq) is type(''):
raise TypeError, 'cannot convert string to tuple'
return tuple(seq)
safebin['tuple'] = __careful_tuple__
def __careful_map__(f, *seqs):
for seq in seqs: for seq in seqs:
if type(seq) is type(''): if type(seq) is type(''):
raise TypeError, 'cannot map a string' raise TypeError, 'cannot map a string'
list(seq) # Ensure that it's a sequence list(seq) # Ensure that it's a sequence
return apply(map, tuple([f] + map(ReadGuard, seqs))) return apply(map, tuple([f] + map(full_read_guard, seqs)))
safebin['map'] = __careful_map__ safe_builtins['map'] = guarded_map
import sys import sys
from string import split def guarded_import(mname, globals={}, locals={}, fromlist=None):
def __careful_import__(mname, globals={}, locals={}, fromlist=None): mnameparts = mname.split('.')
mnameparts = split(mname, '.')
firstmname = mnameparts[0] firstmname = mnameparts[0]
validate = getSecurityManager().validate validate = getSecurityManager().validate
module = load_module(None, None, mnameparts, validate, globals, locals) module = load_module(None, None, mnameparts, validate, globals, locals)
...@@ -225,14 +186,14 @@ def __careful_import__(mname, globals={}, locals={}, fromlist=None): ...@@ -225,14 +186,14 @@ def __careful_import__(mname, globals={}, locals={}, fromlist=None):
v = load_module(module, mname, [name], validate, v = load_module(module, mname, [name], validate,
globals, locals) globals, locals)
if not validate(module, module, name, v): if not validate(module, module, name, v):
raise "Unauthorized" raise Unauthorized
else: else:
return __import__(mname, globals, locals, fromlist) return __import__(mname, globals, locals, fromlist)
except "Unauthorized": except Unauthorized:
raise ImportError, ('import of "%s" from "%s" is unauthorized' raise ImportError, ('import of "%s" from "%s" is unauthorized'
% (name, mname)) % (name, mname))
raise ImportError, 'import of "%s" is unauthorized' % mname raise ImportError, 'import of "%s" is unauthorized' % mname
safebin['__import__'] = __careful_import__ safe_builtins['__import__'] = guarded_import
def load_module(module, mname, mnameparts, validate, globals, locals): def load_module(module, mname, mnameparts, validate, globals, locals):
modules = sys.modules modules = sys.modules
...@@ -253,34 +214,3 @@ def load_module(module, mname, mnameparts, validate, globals, locals): ...@@ -253,34 +214,3 @@ def load_module(module, mname, mnameparts, validate, globals, locals):
return return
module = nextmodule module = nextmodule
return module return module
class Guard:
mul = careful_mul
pow = careful_pow
theGuard = Guard()
class GuardedBlock(SafeBlock):
Mungers = SafeBlock.Mungers + [GuardedBinaryOps(theGuard),
RedirectWrites(), RedirectReads(), LoopLimits]
class _ReadGuardWrapper:
#def validate(self, *args):
# return apply(getSecurityManager().validate, args[:4])
def __getattr__(self, name):
ob = self.__dict__['_ob']
return __careful_getattr__(ob, name)
def __getitem__(self, i):
ob = self.__dict__['_ob']
v = ob[i]
if type(ob) is type(''): return v
if getSecurityManager().validate(ob, ob, None, v):
return v
raise ValidationError, 'unauthorized access to element %s' % `i`
ReadGuard = bind(ReadGuard, Wrapper=_ReadGuardWrapper)
...@@ -85,10 +85,11 @@ ...@@ -85,10 +85,11 @@
__doc__='''Define Zope\'s default security policy __doc__='''Define Zope\'s default security policy
$Id: ZopeSecurityPolicy.py,v 1.9 2001/01/16 20:01:09 evan Exp $''' $Id: ZopeSecurityPolicy.py,v 1.10 2001/04/27 20:27:37 shane Exp $'''
__version__='$Revision: 1.9 $'[11:-2] __version__='$Revision: 1.10 $'[11:-2]
import SimpleObjectPolicies import SimpleObjectPolicies
from AccessControl import Unauthorized
_noroles=SimpleObjectPolicies._noroles _noroles=SimpleObjectPolicies._noroles
from zLOG import LOG, PROBLEM from zLOG import LOG, PROBLEM
...@@ -165,7 +166,7 @@ class ZopeSecurityPolicy: ...@@ -165,7 +166,7 @@ class ZopeSecurityPolicy:
if not p: if not p:
if (containerbase is accessedbase): if (containerbase is accessedbase):
raise 'Unauthorized', cleanupName(name, value) raise Unauthorized, cleanupName(name, value)
else: else:
return 0 return 0
...@@ -196,7 +197,7 @@ class ZopeSecurityPolicy: ...@@ -196,7 +197,7 @@ class ZopeSecurityPolicy:
# We don't want someone to acquire if they can't # We don't want someone to acquire if they can't
# get an unacquired! # get an unacquired!
if accessedbase is containerbase: if accessedbase is containerbase:
raise 'Unauthorized', ( raise Unauthorized, (
'You are not authorized to access <em>%s</em>.' \ 'You are not authorized to access <em>%s</em>.' \
% cleanupName(name, value)) % cleanupName(name, value))
return 0 return 0
...@@ -209,7 +210,7 @@ class ZopeSecurityPolicy: ...@@ -209,7 +210,7 @@ class ZopeSecurityPolicy:
# Proxy roles actually limit access! # Proxy roles actually limit access!
if accessedbase is containerbase: if accessedbase is containerbase:
raise 'Unauthorized', ( raise Unauthorized, (
'You are not authorized to access <em>%s</em>.' \ 'You are not authorized to access <em>%s</em>.' \
% cleanupName(name, value)) % cleanupName(name, value))
...@@ -222,7 +223,7 @@ class ZopeSecurityPolicy: ...@@ -222,7 +223,7 @@ class ZopeSecurityPolicy:
# We don't want someone to acquire if they can't get an unacquired! # We don't want someone to acquire if they can't get an unacquired!
if accessedbase is containerbase: if accessedbase is containerbase:
raise 'Unauthorized', ( raise Unauthorized, (
'You are not authorized to access <em>%s</em>.' \ 'You are not authorized to access <em>%s</em>.' \
% cleanupName(name, value)) % cleanupName(name, value))
......
...@@ -83,6 +83,8 @@ ...@@ -83,6 +83,8 @@
# #
############################################################################## ##############################################################################
Unauthorized = 'Unauthorized'
import DTML import DTML
del DTML del DTML
...@@ -92,6 +94,6 @@ from SecurityInfo import ACCESS_PRIVATE ...@@ -92,6 +94,6 @@ from SecurityInfo import ACCESS_PRIVATE
from SecurityInfo import ACCESS_PUBLIC from SecurityInfo import ACCESS_PUBLIC
from SecurityInfo import ACCESS_NONE from SecurityInfo import ACCESS_NONE
from SecurityInfo import secureModule from SecurityInfo import secureModule
from ZopeGuards import full_read_guard, full_write_guard, safe_builtins
msec = ModuleSecurityInfo('AccessControl') ModuleSecurityInfo('AccessControl').declarePublic('getSecurityManager')
msec.declarePublic('getSecurityManager')
##############################################################################
#
# Zope Public License (ZPL) Version 1.0
# -------------------------------------
#
# Copyright (c) Digital Creations. All rights reserved.
#
# This license has been certified as Open Source(tm).
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions in source code must retain the above copyright
# notice, this list of conditions, and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions, and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# 3. Digital Creations requests that attribution be given to Zope
# in any manner possible. Zope includes a "Powered by Zope"
# button that is installed by default. While it is not a license
# violation to remove this button, it is requested that the
# attribution remain. A significant investment has been put
# into Zope, and this effort will continue if the Zope community
# continues to grow. This is one way to assure that growth.
#
# 4. All advertising materials and documentation mentioning
# features derived from or use of this software must display
# the following acknowledgement:
#
# "This product includes software developed by Digital Creations
# for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# In the event that the product being advertised includes an
# intact Zope distribution (with copyright and license included)
# then this clause is waived.
#
# 5. Names associated with Zope or Digital Creations must not be used to
# endorse or promote products derived from this software without
# prior written permission from Digital Creations.
#
# 6. Modified redistributions of any form whatsoever must retain
# the following acknowledgment:
#
# "This product includes software developed by Digital Creations
# for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# Intact (re-)distributions of any official Zope release do not
# require an external acknowledgement.
#
# 7. Modifications are encouraged but must be packaged separately as
# patches to official Zope releases. Distributions that do not
# clearly separate the patches from the original work must be clearly
# labeled as unofficial distributions. Modifications which do not
# carry the name Zope may be packaged in any form, as long as they
# conform to all of the clauses above.
#
#
# Disclaimer
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
#
# This software consists of contributions made by Digital Creations and
# many individuals on behalf of Digital Creations. Specific
# attributions are listed in the accompanying credits file.
#
##############################################################################
"""Document Template Tests
"""
__rcs_id__='$Id: testSecurity.py,v 1.2 2001/04/27 20:27:38 shane Exp $'
__version__='$Revision: 1.2 $'[11:-2]
import sys, os
import unittest
if __name__=='__main__':
sys.path.append(os.path.join(os.pardir, os.pardir))
here = os.curdir
else:
from App.Common import package_home
here = package_home(globals())
import ZODB
from DocumentTemplate import HTML
from DocumentTemplate.tests.testDTML import DTMLTests
from Products.PythonScripts.standard import DTML
from AccessControl import User, Unauthorized
from ExtensionClass import Base
class UnownedDTML(DTML):
def getOwner(self):
return None
class SecurityTests (DTMLTests):
doc_class = UnownedDTML
unrestricted_doc_class = HTML
def checkNoImplicitAccess(self):
class person:
name='Jim'
try:
res = self.doc_class(
'<dtml-with person>Hi, my name is '
'<dtml-var name></dtml-with>')(person=person)
except Unauthorized:
# Passed the test.
pass
else:
assert 0, 'Did not protect class instance'
def checkExprExplicitDeny(self):
class myclass (Base):
__roles__ = None # Public
somemethod__roles__ = () # Private
def somemethod(self):
return "This is a protected operation of public object"
html = self.doc_class('<dtml-var expr="myinst.somemethod()">')
try:
res = html(myinst=myclass())
except Unauthorized:
# Passed the test.
pass
else:
assert 0, 'Did not deny attribute access'
def checkSecurityInSyntax(self):
'''
Ensures syntax errors are thrown for an expr with restricted
syntax.
'''
expr = '<dtml-var expr="(lambda x, _read=(lambda ob:ob): x.y)(c)">'
try:
# This would be a security hole.
html = self.doc_class(expr)
html()
except SyntaxError:
# Passed the test.
pass
else:
assert 0, 'Did not catch bad expr'
# Now be sure the syntax error occurred for security purposes.
html = self.unrestricted_doc_class(expr)
class c:
y = 10
res = html(c=c)
assert res == '10', res
# Note: we need more tests!
def test_suite():
return unittest.makeSuite(SecurityTests, 'check')
def main():
alltests = test_suite()
runner = unittest.TextTestRunner()
runner.run(alltests)
def debug():
test_suite().debug()
def pdebug():
import pdb
pdb.run('debug()')
if __name__=='__main__':
if len(sys.argv) > 1:
globals()[sys.argv[1]]()
else:
main()
...@@ -180,7 +180,7 @@ class DTMLFile(Bindings, Explicit, ClassicHTMLFile): ...@@ -180,7 +180,7 @@ class DTMLFile(Bindings, Explicit, ClassicHTMLFile):
cns = bound_data['caller_namespace'] cns = bound_data['caller_namespace']
ns = self._Bindings_ns_class() ns = self._Bindings_ns_class()
push = ns._push push = ns._push
ns.validate = None ns.read_guard = None
req = None req = None
kw_bind = kw kw_bind = kw
......
...@@ -402,11 +402,12 @@ ...@@ -402,11 +402,12 @@
''' #' ''' #'
__rcs_id__='$Id: DT_In.py,v 1.49 2001/04/27 18:07:09 andreas Exp $' __rcs_id__='$Id: DT_In.py,v 1.50 2001/04/27 20:27:39 shane Exp $'
__version__='$Revision: 1.49 $'[11:-2] __version__='$Revision: 1.50 $'[11:-2]
import sys
from DT_Util import ParseError, parse_params, name_param, str from DT_Util import ParseError, parse_params, name_param, str
from DT_Util import render_blocks, InstanceDict, ValidationError, VSEval, expr_globals from DT_Util import render_blocks, InstanceDict, ValidationError, Eval
from string import find, atoi, join, split, lower from string import find, atoi, join, split, lower
import re import re
from DT_InSV import sequence_variables, opt from DT_InSV import sequence_variables, opt
...@@ -445,10 +446,10 @@ class InClass: ...@@ -445,10 +446,10 @@ class InClass:
if sort=='sequence-item': self.sort='' if sort=='sequence-item': self.sort=''
if has_key('sort_expr'): if has_key('sort_expr'):
self.sort_expr=VSEval.Eval(args['sort_expr'], expr_globals) self.sort_expr=Eval(args['sort_expr'])
if has_key('reverse_expr'): if has_key('reverse_expr'):
self.reverse_expr=VSEval.Eval(args['reverse_expr'], expr_globals) self.reverse_expr=Eval(args['reverse_expr'])
if has_key('reverse'): if has_key('reverse'):
self.reverse=args['reverse'] self.reverse=args['reverse']
...@@ -596,7 +597,7 @@ class InClass: ...@@ -596,7 +597,7 @@ class InClass:
else: else:
result = [] result = []
append=result.append append=result.append
validate=md.validate read_guard = md.read_guard
for index in range(first,end): for index in range(first,end):
# preset # preset
kw['previous-sequence']= 0 kw['previous-sequence']= 0
...@@ -626,17 +627,18 @@ class InClass: ...@@ -626,17 +627,18 @@ class InClass:
if index==last: kw['sequence-end']=1 if index==last: kw['sequence-end']=1
client=sequence[index] if read_guard is not None:
try: client = read_guard(sequence)[index]
if validate is not None: except ValidationError, vv:
try: vv=validate(sequence,sequence,None,client,md)
except: vv=0
if not vv:
if (params.has_key('skip_unauthorized') and if (params.has_key('skip_unauthorized') and
params['skip_unauthorized']): params['skip_unauthorized']):
if index==first: kw['sequence-start']=0 if index==first: kw['sequence-start']=0
continue continue
raise ValidationError, index tb = sys.exc_info()[2]
raise ValidationError, '(item %s): %s' % (
index, vv), tb
else:
client = sequence[index]
kw['sequence-index']=index kw['sequence-index']=index
if type(client)==TupleType and len(client)==2: if type(client)==TupleType and len(client)==2:
...@@ -654,6 +656,7 @@ class InClass: ...@@ -654,6 +656,7 @@ class InClass:
result=join(result, '') result=join(result, '')
finally: finally:
tb = None
if cache: pop() if cache: pop()
pop() pop()
...@@ -709,20 +712,21 @@ class InClass: ...@@ -709,20 +712,21 @@ class InClass:
try: try:
result = [] result = []
append=result.append append=result.append
validate=md.validate read_guard = md.read_guard
for index in range(l): for index in range(l):
if index==last: kw['sequence-end']=1 if index==last: kw['sequence-end']=1
client=sequence[index] if read_guard is not None:
try: client = read_guard(sequence)[index]
if validate is not None: except ValidationError, vv:
try: vv=validate(sequence,sequence,None,client,md)
except: vv=0
if not vv:
if (self.args.has_key('skip_unauthorized') and if (self.args.has_key('skip_unauthorized') and
self.args['skip_unauthorized']): self.args['skip_unauthorized']):
if index==1: kw['sequence-start']=0 if index==1: kw['sequence-start']=0
continue continue
raise ValidationError, index tb = sys.exc_info()[2]
raise ValidationError, '(item %s): %s' % (
index, vv), tb
else:
client = sequence[index]
kw['sequence-index']=index kw['sequence-index']=index
if type(client)==TupleType and len(client)==2: if type(client)==TupleType and len(client)==2:
...@@ -738,6 +742,7 @@ class InClass: ...@@ -738,6 +742,7 @@ class InClass:
result=join(result, '') result=join(result, '')
finally: finally:
tb = None
if cache: pop() if cache: pop()
pop() pop()
...@@ -841,7 +846,6 @@ def int_param(params,md,name,default=0, st=type('')): ...@@ -841,7 +846,6 @@ def int_param(params,md,name,default=0, st=type('')):
def nocase(str1, str2): def nocase(str1, str2):
return cmp(lower(str1), lower(str2)) return cmp(lower(str1), lower(str2))
import sys
if sys.modules.has_key("locale"): # only if locale is already imported if sys.modules.has_key("locale"): # only if locale is already imported
from locale import strcoll from locale import strcoll
......
...@@ -112,7 +112,7 @@ ...@@ -112,7 +112,7 @@
as desired. as desired.
''' '''
from DT_Util import render_blocks, Eval, expr_globals, ParseError, strip from DT_Util import render_blocks, Eval, ParseError, strip
from DT_Util import str # Probably needed due to hysterical pickles. from DT_Util import str # Probably needed due to hysterical pickles.
import re import re
...@@ -132,7 +132,7 @@ class Let: ...@@ -132,7 +132,7 @@ class Let:
if expr[:1]=='"' and expr[-1:]=='"' and len(expr) > 1: if expr[:1]=='"' and expr[-1:]=='"' and len(expr) > 1:
# expr shorthand # expr shorthand
expr=expr[1:-1] expr=expr[1:-1]
try: args[i] = name, Eval(expr, expr_globals).eval try: args[i] = name, Eval(expr).eval
except SyntaxError, v: except SyntaxError, v:
m,(huh,l,c,src) = v m,(huh,l,c,src) = v
raise ParseError, ( raise ParseError, (
...@@ -174,12 +174,12 @@ def parse_let_params(text, ...@@ -174,12 +174,12 @@ def parse_let_params(text,
value='"%s"' % mo1.group(3) value='"%s"' % mo1.group(3)
l=len(mo1.group(1)) l=len(mo1.group(1))
else: else:
if not text or not strip(text): return result if not text or not text.strip(): return result
raise ParseError, ('invalid parameter: "%s"' % text, tag) raise ParseError, ('invalid parameter: "%s"' % text, tag)
result.append((name,value)) result.append((name,value))
text=strip(text[l:]) text=text[l:].strip()
if text: return apply(parse_let_params,(text,result,tag),parms) if text: return apply(parse_let_params,(text,result,tag),parms)
else: return result else: return result
...@@ -82,9 +82,9 @@ ...@@ -82,9 +82,9 @@
# attributions are listed in the accompanying credits file. # attributions are listed in the accompanying credits file.
# #
############################################################################## ##############################################################################
__version__='$Revision: 1.2 $'[11:-2] __version__='$Revision: 1.3 $'[11:-2]
from DT_Util import parse_params, name_param, html_quote, str from DT_Util import parse_params, name_param, str
import string, sys import string, sys
from string import find, split, join, atoi, rfind from string import find, split, join, atoi, rfind
......
############################################################################## ##############################################################################
# #
# Zope Public License (ZPL) Version 1.0 # Zope Public License (ZPL) Version 1.0
...@@ -83,7 +82,7 @@ ...@@ -83,7 +82,7 @@
# attributions are listed in the accompanying credits file. # attributions are listed in the accompanying credits file.
# #
############################################################################## ##############################################################################
"$Id: DT_String.py,v 1.40 2001/04/27 18:07:10 andreas Exp $" "$Id: DT_String.py,v 1.41 2001/04/27 20:27:39 shane Exp $"
from string import split, strip from string import split, strip
import thread,re import thread,re
...@@ -506,7 +505,7 @@ class String: ...@@ -506,7 +505,7 @@ class String:
if globals: push(globals) if globals: push(globals)
if mapping: if mapping:
push(mapping) push(mapping)
md.validate=self.validate md.read_guard=self.read_guard
if client is not None: if client is not None:
if type(client)==type(()): if type(client)==type(()):
md.this=client[-1] md.this=client[-1]
...@@ -551,8 +550,8 @@ class String: ...@@ -551,8 +550,8 @@ class String:
if pushed: md._pop(pushed) # Get rid of circular reference! if pushed: md._pop(pushed) # Get rid of circular reference!
md.level=level # Restore previous level md.level=level # Restore previous level
validate__roles__=() read_guard__roles__=()
validate=None read_guard=None
def __str__(self): def __str__(self):
return self.read() return self.read()
......
This diff is collapsed.
...@@ -217,13 +217,17 @@ Evaluating expressions without rendering results ...@@ -217,13 +217,17 @@ Evaluating expressions without rendering results
''' # ' ''' # '
__rcs_id__='$Id: DT_Var.py,v 1.38 2001/04/27 18:07:11 andreas Exp $' __rcs_id__='$Id: DT_Var.py,v 1.39 2001/04/27 20:27:39 shane Exp $'
__version__='$Revision: 1.38 $'[11:-2] __version__='$Revision: 1.39 $'[11:-2]
from DT_Util import parse_params, name_param, html_quote, str from DT_Util import parse_params, name_param, str
import re, string, sys import re, string, sys
from string import find, split, join, atoi, rfind from string import find, split, join, atoi, rfind
from urllib import quote, quote_plus from urllib import quote, quote_plus
from cgi import escape
def html_quote(v, name='(Unknown name)', md={}):
return escape(str(v), 1)
class Var: class Var:
name='var' name='var'
...@@ -322,7 +326,7 @@ class Var: ...@@ -322,7 +326,7 @@ class Var:
if have_arg('size'): if have_arg('size'):
size=args['size'] size=args['size']
try: size=atoi(size) try: size=int(size)
except: raise 'Document Error',( except: raise 'Document Error',(
'''a <code>size</code> attribute was used in a <code>var</code> '''a <code>size</code> attribute was used in a <code>var</code>
tag with a non-integer value.''') tag with a non-integer value.''')
...@@ -426,7 +430,7 @@ special_formats={ ...@@ -426,7 +430,7 @@ special_formats={
'collection-length': len_format, 'collection-length': len_format,
'structured-text': structured_text, 'structured-text': structured_text,
# The rest are depricated: # The rest are deprecated:
'sql-quote': sql_quote, 'sql-quote': sql_quote,
'html-quote': html_quote, 'html-quote': html_quote,
'url-quote': url_quote, 'url-quote': url_quote,
......
...@@ -105,8 +105,8 @@ ...@@ -105,8 +105,8 @@
''' '''
__rcs_id__='$Id: DT_With.py,v 1.11 2000/05/11 18:54:14 jim Exp $' __rcs_id__='$Id: DT_With.py,v 1.12 2001/04/27 20:27:39 shane Exp $'
__version__='$Revision: 1.11 $'[11:-2] __version__='$Revision: 1.12 $'[11:-2]
from DT_Util import parse_params, name_param, InstanceDict, render_blocks, str from DT_Util import parse_params, name_param, InstanceDict, render_blocks, str
from DT_Util import TemplateDict from DT_Util import TemplateDict
...@@ -139,8 +139,8 @@ class With: ...@@ -139,8 +139,8 @@ class With:
if self.only: if self.only:
_md=md _md=md
md=TemplateDict() md=TemplateDict()
if hasattr(_md, 'validate'): if hasattr(_md, 'read_guard'):
md.validate=_md.validate md.read_guard = _md.read_guard
md._push(v) md._push(v)
try: return render_blocks(self.section, md) try: return render_blocks(self.section, md)
......
...@@ -145,31 +145,15 @@ Access Control ...@@ -145,31 +145,15 @@ Access Control
Document templates provide a basic level of access control by Document templates provide a basic level of access control by
preventing access to names beginning with an underscore. preventing access to names beginning with an underscore.
Addational control may be provided by providing document templates Additional control may be provided by providing document templates
with a 'validate' method. This would typically be done by with a 'read_guard' method. This would typically be done by
subclassing one or more of the DocumentTemplate classes. subclassing one or more of the DocumentTemplate classes.
If provided, the the 'validate' method will be called when objects If provided, the the 'read_guard' method will be called when objects
are accessed as accessed as instance attributes or when they are are accessed as accessed as instance attributes or when they are
accessed through keyed access in an expression.. The 'validate' accessed through keyed access in an expression.. The 'read_guard'
method will be called with five arguments: method will be called with the containing object. It can
return a wrapper object from which the attribute will be accessed.
1. The containing object that the object was accessed from,
2. The actual containing object that the object was found in,
which may be different from the containing onject the object
was accessed from, if the containing object supports
acquisition,
3. The name used to acces the object,
4. The object, and
5. The namespace object used to render the document template.
If a document template was called from Bobo, then the namespace
object will have an attribute, AUTHENTICATED_USER that is the
user object that was found if and when Bobo authenticated a user.
Document Templates may be created 4 ways: Document Templates may be created 4 ways:
...@@ -194,11 +178,10 @@ Document Templates may be created 4 ways: ...@@ -194,11 +178,10 @@ Document Templates may be created 4 ways:
''' '''
__version__='$Revision: 1.10 $'[11:-2] __version__='$Revision: 1.11 $'[11:-2]
ParseError='Document Template Parse Error' ParseError='Document Template Parse Error'
from DT_String import String, File from DT_String import String, File
from DT_HTML import HTML, HTMLFile, HTMLDefault from DT_HTML import HTML, HTMLFile, HTMLDefault
# import DT_UI # Install HTML editing # import DT_UI # Install HTML editing
from DT_Util import html_quote
...@@ -87,9 +87,8 @@ __doc__='''Package wrapper for Document Template ...@@ -87,9 +87,8 @@ __doc__='''Package wrapper for Document Template
This wrapper allows the (now many) document template modules to be This wrapper allows the (now many) document template modules to be
segregated in a separate package. segregated in a separate package.
$Id: __init__.py,v 1.14 1999/05/17 16:14:59 jim Exp $''' $Id: __init__.py,v 1.15 2001/04/27 20:27:39 shane Exp $'''
__version__='$Revision: 1.14 $'[11:-2] __version__='$Revision: 1.15 $'[11:-2]
import ExtensionClass # work-around for import bug. import ExtensionClass # work-around for import bug.
from DocumentTemplate import String, File, HTML, HTMLDefault, HTMLFile from DocumentTemplate import String, File, HTML, HTMLDefault, HTMLFile
from DocumentTemplate import html_quote
...@@ -84,7 +84,7 @@ ...@@ -84,7 +84,7 @@
****************************************************************************/ ****************************************************************************/
static char cDocumentTemplate_module_documentation[] = static char cDocumentTemplate_module_documentation[] =
"" ""
"\n$Id: cDocumentTemplate.c,v 1.35 2000/11/21 22:08:50 evan Exp $" "\n$Id: cDocumentTemplate.c,v 1.36 2001/04/27 20:27:39 shane Exp $"
; ;
#include "ExtensionClass.h" #include "ExtensionClass.h"
...@@ -92,7 +92,7 @@ static char cDocumentTemplate_module_documentation[] = ...@@ -92,7 +92,7 @@ static char cDocumentTemplate_module_documentation[] =
static PyObject *py_isDocTemp=0, *py_blocks=0, *py_=0, *join=0, *py_acquire; static PyObject *py_isDocTemp=0, *py_blocks=0, *py_=0, *join=0, *py_acquire;
static PyObject *py___call__, *py___roles__, *py_AUTHENTICATED_USER; static PyObject *py___call__, *py___roles__, *py_AUTHENTICATED_USER;
static PyObject *py_hasRole, *py__proxy_roles, *py_Unauthorized; static PyObject *py_hasRole, *py__proxy_roles, *py_Unauthorized;
static PyObject *py_Unauthorized_fmt, *py_validate; static PyObject *py_Unauthorized_fmt, *py_read_guard;
static PyObject *py__push, *py__pop, *py_aq_base, *py_renderNS; static PyObject *py__push, *py__pop, *py_aq_base, *py_renderNS;
/* ----------------------------------------------------- */ /* ----------------------------------------------------- */
...@@ -108,7 +108,7 @@ typedef struct { ...@@ -108,7 +108,7 @@ typedef struct {
PyObject *inst; PyObject *inst;
PyObject *cache; PyObject *cache;
PyObject *namespace; PyObject *namespace;
PyObject *validate; PyObject *read_guard;
} InstanceDictobject; } InstanceDictobject;
staticforward PyExtensionClass InstanceDictType; staticforward PyExtensionClass InstanceDictType;
...@@ -116,18 +116,18 @@ staticforward PyExtensionClass InstanceDictType; ...@@ -116,18 +116,18 @@ staticforward PyExtensionClass InstanceDictType;
static PyObject * static PyObject *
InstanceDict___init__(InstanceDictobject *self, PyObject *args) InstanceDict___init__(InstanceDictobject *self, PyObject *args)
{ {
self->validate=NULL; self->read_guard=NULL;
UNLESS(PyArg_ParseTuple(args, "OO|O", UNLESS(PyArg_ParseTuple(args, "OO|O",
&(self->inst), &(self->inst),
&(self->namespace), &(self->namespace),
&(self->validate))) &(self->read_guard)))
return NULL; return NULL;
Py_INCREF(self->inst); Py_INCREF(self->inst);
Py_INCREF(self->namespace); Py_INCREF(self->namespace);
if (self->validate) if (self->read_guard)
Py_INCREF(self->validate); Py_INCREF(self->read_guard);
else else
UNLESS(self->validate=PyObject_GetAttr(self->namespace, py_validate)) UNLESS(self->read_guard=PyObject_GetAttr(self->namespace, py_read_guard))
return NULL; return NULL;
UNLESS(self->cache=PyDict_New()) return NULL; UNLESS(self->cache=PyDict_New()) return NULL;
...@@ -150,7 +150,7 @@ InstanceDict_dealloc(InstanceDictobject *self) ...@@ -150,7 +150,7 @@ InstanceDict_dealloc(InstanceDictobject *self)
Py_XDECREF(self->inst); Py_XDECREF(self->inst);
Py_XDECREF(self->cache); Py_XDECREF(self->cache);
Py_XDECREF(self->namespace); Py_XDECREF(self->namespace);
Py_XDECREF(self->validate); Py_XDECREF(self->read_guard);
Py_DECREF(self->ob_type); Py_DECREF(self->ob_type);
PyMem_DEL(self); PyMem_DEL(self);
} }
...@@ -182,7 +182,7 @@ InstanceDict_subscript( InstanceDictobject *self, PyObject *key) ...@@ -182,7 +182,7 @@ InstanceDict_subscript( InstanceDictobject *self, PyObject *key)
char *name; char *name;
/* Try to get value from the cache */ /* Try to get value from the cache */
if (r=PyObject_GetItem(self->cache, key)) return r; if ((r=PyObject_GetItem(self->cache, key))) return r;
PyErr_Clear(); PyErr_Clear();
/* Check for __str__ */ /* Check for __str__ */
...@@ -193,26 +193,21 @@ InstanceDict_subscript( InstanceDictobject *self, PyObject *key) ...@@ -193,26 +193,21 @@ InstanceDict_subscript( InstanceDictobject *self, PyObject *key)
return PyObject_Str(self->inst); return PyObject_Str(self->inst);
} }
/* Do explicit acquisition with "roles" rule */ if (self->read_guard != Py_None) {
if (r=PyObject_GetAttr(self->inst, py_acquire)) r = PyObject_CallFunction(self->read_guard, "O", self->inst);
{ if (!r) return NULL;
/* Sanity check in case of explicit Aq */ }
if (v=PyObject_GetAttr(self->inst, key)) Py_DECREF(v); else {
else r = self->inst;
{ Py_INCREF(r);
Py_DECREF(r);
goto KeyError;
} }
if (self->validate != Py_None) ASSIGN(r, PyObject_GetAttr(r, key));
{ if (!r) {
UNLESS_ASSIGN(r,PyObject_CallFunction(
r, "OOO", key, self->validate, self->namespace))
{
PyObject *tb; PyObject *tb;
PyErr_Fetch(&r, &v, &tb); PyErr_Fetch(&r, &v, &tb);
if (r != PyExc_AttributeError || PyObject_Compare(v,key)) if (r != PyExc_AttributeError) /* || PyObject_Compare(v,key)) */
{ {
PyErr_Restore(r,v,tb); PyErr_Restore(r,v,tb);
return NULL; return NULL;
...@@ -223,26 +218,6 @@ InstanceDict_subscript( InstanceDictobject *self, PyObject *key) ...@@ -223,26 +218,6 @@ InstanceDict_subscript( InstanceDictobject *self, PyObject *key)
goto KeyError; goto KeyError;
} }
}
else
UNLESS_ASSIGN(r, PyObject_GetAttr(self->inst, key)) goto KeyError;
}
else
{
PyErr_Clear();
/* OK, use getattr */
UNLESS(r=PyObject_GetAttr(self->inst, key)) goto KeyError;
if (self->validate != Py_None)
{
UNLESS(v=PyObject_CallFunction(
self->validate,"OOOOO",
self->inst, self->inst, key, r, self->namespace))
return NULL;
Py_DECREF(v);
}
}
if (r && PyObject_SetItem(self->cache, key, r) < 0) PyErr_Clear(); if (r && PyObject_SetItem(self->cache, key, r) < 0) PyErr_Clear();
...@@ -312,9 +287,7 @@ typedef struct { ...@@ -312,9 +287,7 @@ typedef struct {
staticforward PyExtensionClass MMtype; staticforward PyExtensionClass MMtype;
static PyObject * static PyObject *
MM_push(self, args) MM_push(MM *self, PyObject *args)
MM *self;
PyObject *args;
{ {
PyObject *src; PyObject *src;
UNLESS(PyArg_Parse(args, "O", &src)) return NULL; UNLESS(PyArg_Parse(args, "O", &src)) return NULL;
...@@ -324,9 +297,7 @@ MM_push(self, args) ...@@ -324,9 +297,7 @@ MM_push(self, args)
} }
static PyObject * static PyObject *
MM_pop(self, args) MM_pop(MM *self, PyObject *args)
MM *self;
PyObject *args;
{ {
int i=1, l; int i=1, l;
PyObject *r; PyObject *r;
...@@ -343,9 +314,7 @@ err: ...@@ -343,9 +314,7 @@ err:
} }
static PyObject * static PyObject *
MM__init__(self, args) MM__init__(MM *self, PyObject *args)
MM *self;
PyObject *args;
{ {
UNLESS(PyArg_Parse(args, "")) return NULL; UNLESS(PyArg_Parse(args, "")) return NULL;
UNLESS(self->data=PyList_New(0)) return NULL; UNLESS(self->data=PyList_New(0)) return NULL;
...@@ -385,7 +354,7 @@ dtObjectIsDocTemp(PyObject *ob) { ...@@ -385,7 +354,7 @@ dtObjectIsDocTemp(PyObject *ob) {
Py_INCREF(base); Py_INCREF(base);
} }
if ( value = PyObject_GetAttr(base, py_isDocTemp) ) { if ( (value = PyObject_GetAttr(base, py_isDocTemp)) ) {
if (PyObject_IsTrue(value)) { if (PyObject_IsTrue(value)) {
result = 1; result = 1;
} }
...@@ -408,12 +377,12 @@ MM_cget(MM *self, PyObject *key, int call) ...@@ -408,12 +377,12 @@ MM_cget(MM *self, PyObject *key, int call)
while (--i >= 0) while (--i >= 0)
{ {
e=PyList_GetItem(self->data,i); e=PyList_GetItem(self->data,i);
if (e=PyObject_GetItem(e,key)) if ((e=PyObject_GetItem(e,key)))
{ {
if (!call) return e; if (!call) return e;
/* Try calling __render_with_namespace__ */ /* Try calling __render_with_namespace__ */
if (rr = PyObject_GetAttr(e, py_renderNS)) if ((rr = PyObject_GetAttr(e, py_renderNS)))
{ {
Py_DECREF(e); Py_DECREF(e);
UNLESS_ASSIGN(rr, PyObject_CallFunction(rr, "O", self)) UNLESS_ASSIGN(rr, PyObject_CallFunction(rr, "O", self))
...@@ -516,8 +485,7 @@ static struct PyMethodDef MM_methods[] = { ...@@ -516,8 +485,7 @@ static struct PyMethodDef MM_methods[] = {
}; };
static void static void
MM_dealloc(self) MM_dealloc(MM *self)
MM *self;
{ {
Py_XDECREF(self->data); Py_XDECREF(self->data);
Py_XDECREF(self->dict); Py_XDECREF(self->dict);
...@@ -538,7 +506,7 @@ MM_getattro(MM *self, PyObject *name) ...@@ -538,7 +506,7 @@ MM_getattro(MM *self, PyObject *name)
{ {
PyObject *v; PyObject *v;
if (v=PyDict_GetItem(self->dict, name)) if ((v=PyDict_GetItem(self->dict, name)))
{ {
Py_INCREF(v); Py_INCREF(v);
return v; return v;
...@@ -568,8 +536,7 @@ MM_setattro(MM *self, PyObject *name, PyObject *v) ...@@ -568,8 +536,7 @@ MM_setattro(MM *self, PyObject *name, PyObject *v)
} }
static int static int
MM_length(self) MM_length(MM *self)
MM *self;
{ {
long l=0, el, i; long l=0, el, i;
PyObject *e=0; PyObject *e=0;
...@@ -800,7 +767,7 @@ render_blocks_(PyObject *blocks, PyObject *rendered, ...@@ -800,7 +767,7 @@ render_blocks_(PyObject *blocks, PyObject *rendered,
{ {
/* We have to be careful to handle key errors here */ /* We have to be careful to handle key errors here */
n=cond; n=cond;
if (cond=PyObject_GetItem(md,cond)) if ((cond=PyObject_GetItem(md,cond)))
{ {
if (PyDict_SetItem(cache, n, cond) < 0) if (PyDict_SetItem(cache, n, cond) < 0)
{ {
...@@ -912,10 +879,10 @@ static struct PyMethodDef Module_Level__methods[] = { ...@@ -912,10 +879,10 @@ static struct PyMethodDef Module_Level__methods[] = {
}; };
void void
initcDocumentTemplate() initcDocumentTemplate(void)
{ {
PyObject *m, *d; PyObject *m, *d;
char *rev="$Revision: 1.35 $"; char *rev="$Revision: 1.36 $";
DictInstanceType.ob_type=&PyType_Type; DictInstanceType.ob_type=&PyType_Type;
...@@ -927,7 +894,7 @@ initcDocumentTemplate() ...@@ -927,7 +894,7 @@ initcDocumentTemplate()
UNLESS(py___roles__=PyString_FromString("__roles__")) return; UNLESS(py___roles__=PyString_FromString("__roles__")) return;
UNLESS(py__proxy_roles=PyString_FromString("_proxy_roles")) return; UNLESS(py__proxy_roles=PyString_FromString("_proxy_roles")) return;
UNLESS(py_hasRole=PyString_FromString("hasRole")) return; UNLESS(py_hasRole=PyString_FromString("hasRole")) return;
UNLESS(py_validate=PyString_FromString("validate")) return; UNLESS(py_read_guard=PyString_FromString("read_guard")) return;
UNLESS(py__push=PyString_FromString("_push")) return; UNLESS(py__push=PyString_FromString("_push")) return;
UNLESS(py__pop=PyString_FromString("_pop")) return; UNLESS(py__pop=PyString_FromString("_pop")) return;
UNLESS(py_aq_base=PyString_FromString("aq_base")) return; UNLESS(py_aq_base=PyString_FromString("aq_base")) return;
......
...@@ -85,8 +85,8 @@ ...@@ -85,8 +85,8 @@
__doc__='''Python implementations of document template some features __doc__='''Python implementations of document template some features
$Id: pDocumentTemplate.py,v 1.27 2001/01/16 18:08:27 evan Exp $''' $Id: pDocumentTemplate.py,v 1.28 2001/04/27 20:27:39 shane Exp $'''
__version__='$Revision: 1.27 $'[11:-2] __version__='$Revision: 1.28 $'[11:-2]
import string, sys, types import string, sys, types
from string import join from string import join
...@@ -117,14 +117,14 @@ isSimpleType=isSimpleType.has_key ...@@ -117,14 +117,14 @@ isSimpleType=isSimpleType.has_key
class InstanceDict: class InstanceDict:
validate=None read_guard=None
def __init__(self,o,namespace,validate=None): def __init__(self,o,namespace,read_guard=None):
self.self=o self.self=o
self.cache={} self.cache={}
self.namespace=namespace self.namespace=namespace
if validate is None: self.validate=namespace.validate if read_guard is None: self.read_guard=namespace.read_guard
else: self.validate=validate else: self.read_guard=read_guard
def has_key(self,key): def has_key(self,key):
return hasattr(self.self,key) return hasattr(self.self,key)
...@@ -144,13 +144,15 @@ class InstanceDict: ...@@ -144,13 +144,15 @@ class InstanceDict:
if key[:1]=='_': if key[:1]=='_':
if key != '__str__': if key != '__str__':
raise KeyError, key # Don't divuldge private data raise KeyError, key # Don't divuldge private data
r=str(inst)
else: else:
try: r=getattr(inst,key) return str(inst)
except AttributeError: raise KeyError, key
read_guard = self.read_guard
if read_guard is not None:
inst = read_guard(inst)
v=self.validate try: r = getattr(inst, key)
if v is not None: v(inst,inst,key,r,self.namespace) except AttributeError: raise KeyError, key
self.cache[key]=r self.cache[key]=r
return r return r
...@@ -212,9 +214,14 @@ class TemplateDict: ...@@ -212,9 +214,14 @@ class TemplateDict:
return v.__render_with_namespace__(self) return v.__render_with_namespace__(self)
vbase = getattr(v, 'aq_base', v) vbase = getattr(v, 'aq_base', v)
if callable(vbase): if callable(vbase):
if getattr(vbase, 'isDocTemp', None): try:
return v(None, self) if getattr(vbase, 'isDocTemp', 0):
return v() v = v(None, self)
else:
v = v()
except AttributeError, n:
if n != '__call__':
raise
return v return v
def has_key(self,key): def has_key(self,key):
......
<html><head><title>Inventory by Dealer</title></head><body>
<dl>
<dtml-in inventory mapping size=5 start=first_ad>
<dtml-if previous-sequence>
<dtml-in previous-batches mapping>
(<dtml-var batch-start-var-dealer> <dtml-var
batch-start-var-year> <dtml-var
batch-start-var-make> <dtml-var
batch-start-var-model> - <dtml-var
batch-end-var-dealer> <dtml-var
batch-end-var-year> <dtml-var
batch-end-var-make> <dtml-var
batch-end-var-model>)
</dtml-in previous-batches>
</dtml-if previous-sequence>
<dtml-if first-dealer>
<dt><dtml-var dealer></dt><dd>
</dtml-if first-dealer>
<dtml-var year> <dtml-var make> <dtml-var model> <p>
<dtml-if last-dealer>
</dd>
</dtml-if last-dealer>
<dtml-if next-sequence>
<dtml-in next-batches mapping>
(<dtml-var batch-start-var-dealer> <dtml-var
batch-start-var-year> <dtml-var
batch-start-var-make> <dtml-var
batch-start-var-model> - <dtml-var
batch-end-var-dealer> <dtml-var
batch-end-var-year> <dtml-var
batch-end-var-make> <dtml-var
batch-end-var-model>)
</dtml-in next-batches>
</dtml-if next-sequence>
</dtml-in inventory>
</dl>
</body></html>
<html><head><title>Inventory by Dealer</title></head><body>
<dl>
(Bay Chevy 96 Chevrolet Caprice - Bay Chevy 96 Chevrolet Nova)
(Bay Chevy 96 Chevrolet Corvett - Bay Chevy 96 Chevrolet Corsica)
(Bay Chevy 96 Chevrolet Corsica - Colman Olds 96 Olds Ciera)
<dt>Colman Olds</dt><dd>
96 Olds Cutlass <p>
95 Olds Cutlas <p>
93 Dodge Shadow <p>
94 Jeep Cheroke <p>
92 Toyota Previa <p>
</dd>
(Colman Olds 93 Toyota Celica - Colman Olds 94 Honda Civic)
(Colman Olds 93 Honda Civix - Spam Chev 96 Chevrolet Nova)
(Spam Chev 96 Chevrolet Corvett - Spam Chev 96 Chevrolet Corsica)
(Spam Chev 96 Chevrolet Corsica - Spam Olds 96 Olds Ciera)
(Spam Olds 96 Olds Cutlass - Spam Olds 92 Toyota Previa)
(Spam Olds 93 Toyota Celica - Spam Olds 93 Honda Civix)
</dl>
</body></html>
This diff is collapsed.
...@@ -84,9 +84,8 @@ ...@@ -84,9 +84,8 @@
############################################################################## ##############################################################################
"""DTML Document objects.""" """DTML Document objects."""
__version__='$Revision: 1.43 $'[11:-2] __version__='$Revision: 1.44 $'[11:-2]
from DocumentTemplate.DT_Util import InstanceDict, TemplateDict
from ZPublisher.Converters import type_converters from ZPublisher.Converters import type_converters
from Globals import HTML, DTMLFile, MessageDialog from Globals import HTML, DTMLFile, MessageDialog
from OFS.content_types import guess_content_type from OFS.content_types import guess_content_type
......
...@@ -84,7 +84,7 @@ ...@@ -84,7 +84,7 @@
############################################################################## ##############################################################################
"""DTML Method objects.""" """DTML Method objects."""
__version__='$Revision: 1.63 $'[11:-2] __version__='$Revision: 1.64 $'[11:-2]
import History import History
from Globals import HTML, DTMLFile, MessageDialog from Globals import HTML, DTMLFile, MessageDialog
...@@ -100,7 +100,7 @@ from ZDOM import ElementWithTitle ...@@ -100,7 +100,7 @@ from ZDOM import ElementWithTitle
from DateTime.DateTime import DateTime from DateTime.DateTime import DateTime
from urllib import quote from urllib import quote
import Globals, sys, Acquisition import Globals, sys, Acquisition
from AccessControl import getSecurityManager from AccessControl import getSecurityManager, full_read_guard
from Cache import Cacheable from Cache import Cacheable
_marker = [] # Create a new marker object. _marker = [] # Create a new marker object.
...@@ -261,8 +261,8 @@ class DTMLMethod(HTML, Acquisition.Implicit, RoleManager, ...@@ -261,8 +261,8 @@ class DTMLMethod(HTML, Acquisition.Implicit, RoleManager,
# deprecated; use get_size! # deprecated; use get_size!
getSize=get_size getSize=get_size
def validate(self, inst, parent, name, value, md): def read_guard(self, ob):
return getSecurityManager().validate(inst, parent, name, value) return full_read_guard(ob)
manage_editForm=DTMLFile('dtml/documentEdit', globals()) manage_editForm=DTMLFile('dtml/documentEdit', globals())
......
...@@ -83,17 +83,17 @@ ...@@ -83,17 +83,17 @@
# #
############################################################################## ##############################################################################
__doc__="""Find support""" __doc__="""Find support"""
__version__='$Revision: 1.23 $'[11:-2] __version__='$Revision: 1.24 $'[11:-2]
import sys, os, string, time, Globals, ExtensionClass import sys, os, string, time, Globals, ExtensionClass
from DocumentTemplate.DT_Util import Eval, expr_globals from DocumentTemplate.DT_Util import Eval
from AccessControl.Permission import name_trans from AccessControl.Permission import name_trans
from Globals import DTMLFile from Globals import DTMLFile
from DocumentTemplate.DT_Util import InstanceDict, TemplateDict from DocumentTemplate.DT_Util import InstanceDict, TemplateDict
from DateTime import DateTime from DateTime import DateTime
from string import find from string import find
from AccessControl import getSecurityManager from AccessControl import getSecurityManager, full_read_guard
class FindSupport(ExtensionClass.Base): class FindSupport(ExtensionClass.Base):
"""Find support for Zope Folders""" """Find support for Zope Folders"""
...@@ -146,7 +146,7 @@ class FindSupport(ExtensionClass.Base): ...@@ -146,7 +146,7 @@ class FindSupport(ExtensionClass.Base):
if obj_expr: if obj_expr:
# Setup expr machinations # Setup expr machinations
md=td() md=td()
obj_expr=(Eval(obj_expr, expr_globals), md, md._push, md._pop) obj_expr=(Eval(obj_expr), md, md._push, md._pop)
base=obj base=obj
if hasattr(obj, 'aq_base'): if hasattr(obj, 'aq_base'):
...@@ -238,7 +238,7 @@ class FindSupport(ExtensionClass.Base): ...@@ -238,7 +238,7 @@ class FindSupport(ExtensionClass.Base):
if obj_expr: if obj_expr:
# Setup expr machinations # Setup expr machinations
md=td() md=td()
obj_expr=(Eval(obj_expr, expr_globals), md, md._push, md._pop) obj_expr=(Eval(obj_expr), md, md._push, md._pop)
base=obj base=obj
if hasattr(obj, 'aq_base'): if hasattr(obj, 'aq_base'):
...@@ -307,8 +307,8 @@ class FindSupport(ExtensionClass.Base): ...@@ -307,8 +307,8 @@ class FindSupport(ExtensionClass.Base):
class td(TemplateDict): class td(TemplateDict):
def validate(self, inst, parent, name, value, md): def read_guard(self, ob):
return getSecurityManager().validate(inst, parent, name, value) return full_read_guard(ob)
def expr_match(ob, ed, c=InstanceDict, r=0): def expr_match(ob, ed, c=InstanceDict, r=0):
......
...@@ -84,14 +84,14 @@ ...@@ -84,14 +84,14 @@
############################################################################## ##############################################################################
"""Object Histories""" """Object Histories"""
__version__='$Revision: 1.6 $'[11:-2] __version__='$Revision: 1.7 $'[11:-2]
import Globals, ndiff, ExtensionClass import Globals, ndiff, ExtensionClass
from DateTime import DateTime from DateTime import DateTime
from Acquisition import Implicit from Acquisition import Implicit
from string import join, split, atoi, strip from string import join, split, atoi, strip
from struct import pack, unpack from struct import pack, unpack
from DocumentTemplate.DT_Util import html_quote from cgi import escape
class TemporalParadox(Exception): pass class TemporalParadox(Exception): pass
...@@ -267,7 +267,7 @@ def dump(tag, x, lo, hi, r): ...@@ -267,7 +267,7 @@ def dump(tag, x, lo, hi, r):
"<td><pre>\n%s\n</pre></td>\n" "<td><pre>\n%s\n</pre></td>\n"
"<td><pre>\n%s\n</pre></td>\n" "<td><pre>\n%s\n</pre></td>\n"
"</tr>\n" "</tr>\n"
% (join(r1,'\n'), html_quote(join(r2, '\n')))) % (join(r1,'\n'), escape(join(r2, '\n'))))
def replace(x, xlo, xhi, y, ylo, yhi, r): def replace(x, xlo, xhi, y, ylo, yhi, r):
...@@ -289,7 +289,7 @@ def replace(x, xlo, xhi, y, ylo, yhi, r): ...@@ -289,7 +289,7 @@ def replace(x, xlo, xhi, y, ylo, yhi, r):
"<td><pre>\n%s\n%s\n</pre></td>\n" "<td><pre>\n%s\n%s\n</pre></td>\n"
"</tr>\n" "</tr>\n"
% (join(rx1, '\n'), join(ry1, '\n'), % (join(rx1, '\n'), join(ry1, '\n'),
html_quote(join(rx2, '\n')), html_quote(join(ry2, '\n')))) escape(join(rx2, '\n')), escape(join(ry2, '\n'))))
def html_diff(s1, s2): def html_diff(s1, s2):
a=split(s1,'\n') a=split(s1,'\n')
......
...@@ -84,7 +84,7 @@ ...@@ -84,7 +84,7 @@
############################################################################## ##############################################################################
"""Property management""" """Property management"""
__version__='$Revision: 1.37 $'[11:-2] __version__='$Revision: 1.38 $'[11:-2]
import ExtensionClass, Globals import ExtensionClass, Globals
import ZDOM import ZDOM
...@@ -92,7 +92,6 @@ from PropertySheets import DefaultPropertySheets, vps ...@@ -92,7 +92,6 @@ from PropertySheets import DefaultPropertySheets, vps
from ZPublisher.Converters import type_converters from ZPublisher.Converters import type_converters
from Globals import DTMLFile, MessageDialog from Globals import DTMLFile, MessageDialog
from string import find,join,lower,split from string import find,join,lower,split
from DocumentTemplate import html_quote
from Acquisition import Implicit, aq_base from Acquisition import Implicit, aq_base
from Globals import Persistent from Globals import Persistent
from DateTime import DateTime from DateTime import DateTime
......
...@@ -84,12 +84,11 @@ ...@@ -84,12 +84,11 @@
############################################################################## ##############################################################################
"""Property sheets""" """Property sheets"""
__version__='$Revision: 1.72 $'[11:-2] __version__='$Revision: 1.73 $'[11:-2]
import time, string, App.Management, Globals import time, string, App.Management, Globals
from webdav.WriteLockInterface import WriteLockInterface from webdav.WriteLockInterface import WriteLockInterface
from ZPublisher.Converters import type_converters from ZPublisher.Converters import type_converters
from DocumentTemplate.DT_Util import html_quote
from Globals import DTMLFile, MessageDialog from Globals import DTMLFile, MessageDialog
from string import find,join,lower,split,rfind from string import find,join,lower,split,rfind
from Acquisition import Implicit, Explicit from Acquisition import Implicit, Explicit
......
...@@ -82,8 +82,8 @@ ...@@ -82,8 +82,8 @@
# attributions are listed in the accompanying credits file. # attributions are listed in the accompanying credits file.
# #
############################################################################## ##############################################################################
__rcs_id__='$Id: MIMETag.py,v 1.5 2000/05/10 18:43:34 tseaver Exp $' __rcs_id__='$Id: MIMETag.py,v 1.6 2001/04/27 20:27:41 shane Exp $'
__version__='$Revision: 1.5 $'[11:-2] __version__='$Revision: 1.6 $'[11:-2]
from DocumentTemplate.DT_Util import * from DocumentTemplate.DT_Util import *
from DocumentTemplate.DT_String import String from DocumentTemplate.DT_String import String
...@@ -136,40 +136,40 @@ class MIMETag: ...@@ -136,40 +136,40 @@ class MIMETag:
if has_key('type_expr'): if has_key('type_expr'):
if has_key('type'): if has_key('type'):
raise ParseError, _tm('type and type_expr given', 'mime') raise ParseError, _tm('type and type_expr given', 'mime')
args['type_expr']=VSEval.Eval(args['type_expr'], expr_globals) args['type_expr']=Eval(args['type_expr'])
elif not has_key('type'): elif not has_key('type'):
args['type']='application/octet-stream' args['type']='application/octet-stream'
if has_key('disposition_expr'): if has_key('disposition_expr'):
if has_key('disposition'): if has_key('disposition'):
raise ParseError, _tm('disposition and disposition_expr given', 'mime') raise ParseError, _tm('disposition and disposition_expr given', 'mime')
args['disposition_expr']=VSEval.Eval(args['disposition_expr'], expr_globals) args['disposition_expr']=Eval(args['disposition_expr'])
elif not has_key('disposition'): elif not has_key('disposition'):
args['disposition']='' args['disposition']=''
if has_key('encode_expr'): if has_key('encode_expr'):
if has_key('encode'): if has_key('encode'):
raise ParseError, _tm('encode and encode_expr given', 'mime') raise ParseError, _tm('encode and encode_expr given', 'mime')
args['encode_expr']=VSEval.Eval(args['encode_expr'], expr_globals) args['encode_expr']=Eval(args['encode_expr'])
elif not has_key('encode'): elif not has_key('encode'):
args['encode']='base64' args['encode']='base64'
if has_key('name_expr'): if has_key('name_expr'):
if has_key('name'): if has_key('name'):
raise ParseError, _tm('name and name_expr given', 'mime') raise ParseError, _tm('name and name_expr given', 'mime')
args['name_expr']=VSEval.Eval(args['name_expr'], expr_globals) args['name_expr']=Eval(args['name_expr'])
elif not has_key('name'): elif not has_key('name'):
args['name']='' args['name']=''
if has_key('filename_expr'): if has_key('filename_expr'):
if has_key('filename'): if has_key('filename'):
raise ParseError, _tm('filename and filename_expr given', 'mime') raise ParseError, _tm('filename and filename_expr given', 'mime')
args['filename_expr']=VSEval.Eval(args['filename_expr'], expr_globals) args['filename_expr']=Eval(args['filename_expr'])
elif not has_key('filename'): elif not has_key('filename'):
args['filename']='' args['filename']=''
if has_key('skip_expr'): if has_key('skip_expr'):
args['skip_expr']=VSEval.Eval(args['skip_expr'], expr_globals) args['skip_expr']=Eval(args['skip_expr'])
if args['encode'] not in \ if args['encode'] not in \
('base64', 'quoted-printable', 'uuencode', 'x-uuencode', ('base64', 'quoted-printable', 'uuencode', 'x-uuencode',
......
...@@ -82,11 +82,11 @@ ...@@ -82,11 +82,11 @@
# attributions are listed in the accompanying credits file. # attributions are listed in the accompanying credits file.
# #
############################################################################## ##############################################################################
__rcs_id__='$Id: SendMailTag.py,v 1.10 2001/03/27 16:14:42 brian Exp $' __rcs_id__='$Id: SendMailTag.py,v 1.11 2001/04/27 20:27:42 shane Exp $'
__version__='$Revision: 1.10 $'[11:-2] __version__='$Revision: 1.11 $'[11:-2]
from MailHost import MailBase from MailHost import MailBase
from DocumentTemplate.DT_Util import * from DocumentTemplate.DT_Util import parse_params
from DocumentTemplate.DT_String import String from DocumentTemplate.DT_String import String
from socket import gethostname from socket import gethostname
import string import string
......
2001-04-26 Evan Simpson <evan@digicool.com>
* Version 2.0.0
* Totally replaced zbytecodhacks engine with Zope's new
RestrictedPython package, which it shares with DTML.
1999-12-13 Evan Simpson <evan@4-am.com> 1999-12-13 Evan Simpson <evan@4-am.com>
* Version 0.1.7 * Version 0.1.7
......
...@@ -90,7 +90,7 @@ Scripts. It can be accessed from Python with the statement ...@@ -90,7 +90,7 @@ Scripts. It can be accessed from Python with the statement
"import Products.PythonScripts.standard" "import Products.PythonScripts.standard"
""" """
__version__='$Revision: 1.4 $'[11:-2] __version__='$Revision: 1.5 $'[11:-2]
from AccessControl import ModuleSecurityInfo, getSecurityManager from AccessControl import ModuleSecurityInfo, getSecurityManager
security = ModuleSecurityInfo() security = ModuleSecurityInfo()
...@@ -105,6 +105,7 @@ from DocumentTemplate.DT_Var import special_formats, \ ...@@ -105,6 +105,7 @@ from DocumentTemplate.DT_Var import special_formats, \
html_quote, url_quote, url_quote_plus, newline_to_br, thousands_commas html_quote, url_quote, url_quote_plus, newline_to_br, thousands_commas
from Globals import HTML from Globals import HTML
from AccessControl import full_read_guard
security.declarePublic('DTML') security.declarePublic('DTML')
class DTML(HTML): class DTML(HTML):
...@@ -121,8 +122,8 @@ class DTML(HTML): ...@@ -121,8 +122,8 @@ class DTML(HTML):
finally: security.removeContext(self) finally: security.removeContext(self)
def validate(self, inst, parent, name, value, md): def read_guard(self, ob):
return getSecurityManager().validate(inst, parent, name, value) return full_read_guard(ob)
security.apply(globals()) security.apply(globals())
...@@ -110,7 +110,7 @@ class TestPythonScriptNoAq(TestCase): ...@@ -110,7 +110,7 @@ class TestPythonScriptNoAq(TestCase):
ps = PythonScript('ps') ps = PythonScript('ps')
ps.ZBindings_edit(bind or {}) ps.ZBindings_edit(bind or {})
ps.write(txt) ps.write(txt)
ps._makeFunction(1) ps._makeFunction()
return ps return ps
def fail(self): def fail(self):
...@@ -201,18 +201,17 @@ class TestPythonScriptNoAq(TestCase): ...@@ -201,18 +201,17 @@ class TestPythonScriptNoAq(TestCase):
def testComplexPrint(self): def testComplexPrint(self):
txt = self._newPS(readf('complex_print'))() txt = self._newPS(readf('complex_print'))()
assert txt == 'double\ndouble\n x: 1\ny: 0 1 2\n\n', txt assert txt == 'double\ndouble\nx: 1\ny: 0 1 2\n\n', txt
def testNSBind(self): def testNSBind(self):
f = self._newPS(readf('ns_bind'), bind={'name_ns': '_'}) f = self._newPS(readf('ns_bind'), bind={'name_ns': '_'})
bound = f.__render_with_namespace__({'yes': 1, 'no': self.fail}) bound = f.__render_with_namespace__({'yes': 1, 'no': self.fail})
assert bound == 1, bound assert bound == 1, bound
def testManyArgs(self): def testBooleanMap(self):
f = self._newPS(readf('manyargs')) true = self._newPS(readf('boolean_map'))()
f() assert true
ss = f._v_f.func_code.co_stacksize
assert ss == 24, ss
test_classes = (TestPythonScriptNoAq,) test_classes = (TestPythonScriptNoAq,)
......
result = []
for c in 'abcdef':
result.append({'a-c': (c <= 'c') or None,
'd-f': (c >= 'd') and 2})
return result == [
{'a-c': 1, 'd-f': 0},
{'a-c': 1, 'd-f': 0},
{'a-c': 1, 'd-f': 0},
{'a-c': None, 'd-f': 2},
{'a-c': None, 'd-f': 2},
{'a-c': None, 'd-f': 2},
]
def f(*args, **kwarg):
pass
print f(1, 2, 3, a='', b='', c='', d='', e='', f='', g='', h='', i='')
return printed
...@@ -12,13 +12,13 @@ ...@@ -12,13 +12,13 @@
Title Title
</div> </div>
</td> </td>
<td align="left" valign="top"> <td align="left" valign="top" width="99%">
<input type="text" name="title" size="40" <input type="text" name="title" size="40"
value="&dtml-title;" /> value="&dtml-title;" />
</td> </td>
</tr> </tr>
<tr> <tr>
<td align="left" valign="top"> <td align="left" valign="top" nowrap>
<div class="form-optional"> <div class="form-optional">
Parameter List Parameter List
</div> </div>
...@@ -59,6 +59,25 @@ ...@@ -59,6 +59,25 @@
</div> </div>
</td> </td>
</tr> </tr>
<dtml-if errors>
<tr>
<td align="left" valign="middle" class="form-label">Errors</td>
<td align="left" valign="middle" style="background-color: #FFDDDD">
<pre><dtml-var expr="'\n'.join(errors)" html_quote></pre>
</td>
</tr>
</dtml-if>
<dtml-if warnings>
<tr>
<td align="left" valign="middle" class="form-label">Warnings</td>
<td align="left" valign="middle" style="background-color: #FFEEDD">
<pre><dtml-var expr="'\n'.join(warnings)" html_quote></pre>
</td>
</tr>
</dtml-if>
<dtml-with keyword_args mapping> <dtml-with keyword_args mapping>
<tr> <tr>
<td align="left" valign="top" colspan="2"> <td align="left" valign="top" colspan="2">
......
1999-12-11 Evan Simpson <evan@4-am.com>
* Tupleizing and UntupleFunction now handle nested function definitions
* Subdirectories reverted to v0.5 bytecodehacks, since the only change
I've ever made was to accidentally change line endings in them to CRLF
1999-09-09 Evan Simpson <evan@4-am.com>
* Tupleizing a function now omits globals
* New UntupleFunction re-applies globals, with automatic initialization
of variables to None, since there's no way to check if they exist.
It also includes $functions, and checks __builtins__ handling.
* Moved all bytecode manipulation into Munge_window class, which uses
op.execute to maintain information about who did what to the stack.
* Added Munger which re-enables creation of dictionary literals.
* Made all Mungers load frequently used functions from the global dict
into the local instead of storing them in co_consts.
* Simplified GuardedOps and turned off test Guard.
* Wrote lots of docstring.
1999-08-28 Evan Simpson <evan@4-am.com>
* Ripped out Fleshy acquisition-style class and added
CycleHandle in an attempt to improve speed.
* code_editor.py: Added "as_tuple" to Function and EditableCode
to provide (hopefully) pickleable representations. Their __init__s
now accept these tuples.
* Added VSExec.py (the point of all this), which provides facilities
for compiling arbitrary blocks of code with heavy restrictions.
1999-06-11 Michael Hudson <mwh21@cam.ac.uk>
* a monumental amount has changed. I haven't been keeping the
ChangeLog up to date, sorry.
1999-05-16 Michael Hudson <mwh21@cam.ac.uk>
* doc/bch.tex: documented macro and macros.
* macros.py: added basic library of macros.
* setq2.py: does same job as setq.py, but uses the new macro
package.
* macro.py It's a macro packages of sorts. Needs documentation.
1999-05-15 Michael Hudson <mwh21@cam.ac.uk>
* inline.py: Substantially rewritten to use find_function_call,
and to support keyword arguments. No varags yet.
* setq.py Added changes written by Christian Tismer (now converts
globals to locals)
1999-05-13 Michael Hudson <mwh21@cam.ac.uk>
* Release 0.11 - cleaned up production of documentation following
advice from the documentation master, Fred L. Drake.
1999-05-12 Michael Hudson <mwh21@cam.ac.uk>
* Release 0.10.
* doc/ There's documentation (gasp)
1999-05-10 Michael Hudson <mwh21@cam.ac.uk>
* inline.py: Python now has inline functions! Bet you never
expected that.
* It's all changing again! Much polish, some docstrings,
everything rewritten to use code_editor that wasn't already, many
style fixes.
1999-05-06 Michael Hudson <mwh21@cam.ac.uk>
* attr_freeze.py: implement an attribute freezer that works.
* xapply2.py: implement xapply for functions (again!) using the
new code editing framework from code_editor.py.
* code_editor.py: That's more like it!
1999-05-04 Michael Hudson <mwh21@cam.ac.uk>
* attr_freeze.py: implements a (buggy) attempt at freezing
attribute references.
* read_code.py,opbases.py,ops.py,write_ops.py,
common.py,__init__.py: Much stuff added/changed. It not pretty or
internally consistent yet. I might bash on it some more some
time. I'm afraid I don't feel like explaining myself properly yet
either.
1999-05-02 Michael Hudson <mwh21@cam.ac.uk>
* README,ChangeLog: Added.
* xapply.py: Added, following prompting by Christian Tismer.
* closure.py: Made improvements suggested by Tim Peters.
SUBDIRS=tests code_gen doc
clean: clean-local clean-recursive
release: version src-release doc-release
clean-local:
$(RM) *.pyc *~ *.pyo
clean-recursive:
for i in $(SUBDIRS); do \
(cd $$i && make clean); \
done
src-release: clean
cd .. && ./mkdist.sh
doc-release: clean
cd doc && make release
Welcome to the bytecodehacks!
There's docmentation in doc/; To build it you need an unpacked python
source distribution somewhere, and for html output you also need
latex2html.
Build docs like this:
$(path to Python source)/Doc/tools/mkhowto --$(format) (--a4) bch.tex
You can get built html docs at
ftp://starship.python.net/pub/crew/mwh/bytecodehacks-doc-$(VERSION).tar.gz.
The bytecodehacks rewrite the bytecode of functions to do unlikely
things in Python.
The package (and that's how it's distributed) splits into two parts -
the byte code editing routines and the "bytecodehacks" that are
usuable without a degree in python arcanery, although one might help
understand some of the consequences.
Some highlights:
bytecodehacks.closure - bind global references to constants
bytecodehacks.xapply - a sort-of lazy apply
bytecodehacks.setq - this one should interest people!
bytecodehacks.inline - Python gets inline functions
bytecodehacks.macro - Python gets semantic (hygenic) macros!
Please note that these modules are not really bullet-proof, more a
proof-of-concept than anything else.
The are also public domain; do what you like with them. If you find
bugs, or more imaginative uses for these techniques, I'd surely like
to know!
Thanks for taking an interest.
__all__=[
'closure',
'xapply',
'common',
'inline',
'code_editor',
'opbases',
'ops',
'attr_freeze',
'code_gen']
from code_editor import Function
from ops import LOAD_GLOBAL, LOAD_ATTR, LOAD_CONST
def freeze_one_attr(cs,code,attr,value):
looking_for=0
is_global=1
inserted=0
i = 0
while i < len(cs):
op=cs[i]
if is_global:
if op.__class__ is LOAD_GLOBAL:
if code.co_names[op.arg]==attr[looking_for]:
looking_for=looking_for+1
is_global=0
else:
if op.__class__ is LOAD_ATTR \
and code.co_names[op.arg]==attr[looking_for]:
looking_for=looking_for+1
if looking_for == len(attr):
inserted=1
newop=LOAD_CONST(len(code.co_consts))
cs[i-len(attr)+1:i+1]=[newop]
i=i-len(attr)
looking_for=0
is_global=1
else:
looking_for=0
is_global=1
i=i+1
if inserted:
code.co_consts.append(value)
return cs
class Ref:
def __init__(self,name=()):
self.name=name
def __getattr__(self,attr):
return Ref(self.name+(attr,))
def __call__(self):
return self.name
def __repr__(self):
return `self.name`
def freeze_attrs(func,*vars):
func=Function(func)
code=func.func_code
cs=code.co_code
if len(vars)%2 <> 0:
raise TypeError, "wrong number of arguments"
for i in range(0,len(vars),2):
freeze_one_attr(cs,code,vars[i](),vars[i+1])
return func.make_function()
"""\
closure
implements a form of closures by abusing the co_consts field of a code
object.
exports: bind, bind_locals, bind_now
and contains two examples: make_adder, make_balance
"""
from code_editor import Function
from ops import *
def scan_for_STORE(func,name):
for i in func.func_code.co_code:
if i.__class__ in [STORE_FAST,STORE_NAME,STORE_GLOBAL] \
and i.name == name:
return 1
return 0
def bind(function,newname=None,**vars):
"""\
bind(function[,newname],var1=value1,var2=value2,...) -> function
returns a new function (optionally renamed) where every reference to
one of var1, var2, etc is replaced by a reference to the respective
valueN."""
func = Function(function)
code = func.func_code
cs = func.func_code.co_code
name2index = {}
mutated = {}
for name in vars.keys():
mutated[name] = scan_for_STORE(func,name)
if 0 in code.co_consts:
zeroIndex = code.co_consts.index(0)
else:
zeroIndex = len(code.co_consts)
code.co_consts.append(0)
i = 0
while i < len(cs):
op = cs[i]
i = i + 1
# should LOAD_NAME be here??? tricky, I'd say
if op.__class__ in [LOAD_GLOBAL,LOAD_NAME,LOAD_FAST]:
if not vars.has_key(op.name):
continue
if mutated[name]:
if not name2index.has_key(op.name):
name2index[op.name]=len(code.co_consts)
code.co_consts.append([vars[op.name]])
cs[i-1:i] = [LOAD_CONST(name2index[op.name]),
LOAD_CONST(zeroIndex),
BINARY_SUBSCR()]
i = i + 2
else:
if not name2index.has_key(op.name):
name2index[op.name]=len(code.co_consts)
code.co_consts.append(vars[op.name])
cs[i-1] = LOAD_CONST(name2index[op.name])
elif op.__class__ in [STORE_FAST,STORE_NAME,STORE_GLOBAL]:
if not vars.has_key(op.name):
continue
if not mutated[name]:
continue # shouldn't be reached
cs[i-1:i] = [LOAD_CONST(name2index[op.name]),
LOAD_CONST(zeroIndex),
STORE_SUBSCR()]
i = i + 2
if newname is not None:
func.func_name = newname
return func.make_function()
bind=Function(bind)
bind.func_code.co_varnames[0]='$function'
bind.func_code.co_varnames[1]='$newname'
bind=bind.make_function()
def bind_locals(func):
"""bind_locals(func) -> function
returns a new function where every global variable reference in func
is replaced, if possible, by a reference to a local variable in the
callers context."""
try:
raise ""
except:
import sys
frame = sys.exc_traceback.tb_frame.f_back
name = func.func_name+'+'
l = apply(bind,(func,name),frame.f_locals)
frame = None
return l
def bind_now(func):
"""bind_now(func) -> function
returns a new function where every global variable reference in func
is replaced, if possible, by a reference to a variable in the callers
context."""
try:
raise ""
except:
import sys
frame = sys.exc_traceback.tb_frame.f_back
l = apply(bind,(func,),frame.f_locals)
g = apply(bind,(l,),frame.f_globals)
frame = None
return g
## examples
def make_adder(n):
"""make_adder(n) -> function
return a monadic function that adds n to its argument."""
def adder(x):
return x+n
return bind_locals(adder)
def make_balance(initial_amount):
"""make_balance(initial_amount) -> function
demonstrates an object with state, sicp style."""
def withdraw(amount):
if current[0]<amount:
raise "debt!"
else:
current[0]=current[0]-amount
return current[0]
return bind(withdraw,current=[initial_amount])
# -*- python -*-
STOP_CODE:
pass
POP_TOP:
stack.pop()
ROT_TWO:
stack[-2:]=[stack[-1],stack[-2]]
ROT_THREE:
stack[-3:]=[
stack[-1],
stack[-3],
stack[-2]]
DUP_TOP:
stack.append(stack[-1])
UNARY_POSITIVE:
UNARY_NEGATIVE:
UNARY_NOT:
UNARY_CONVERT:
UNARY_INVERT:
stack[-1:]=[self]
BINARY_POWER:
BINARY_MULTIPLY:
BINARY_DIVIDE:
BINARY_MODULO:
BINARY_ADD:
BINARY_SUBTRACT:
BINARY_SUBSCR:
BINARY_LSHIFT:
BINARY_RSHIFT:
BINARY_AND:
BINARY_XOR:
BINARY_OR:
stack[-2:]=[self]
SLICE_0:
stack[-1:]=[self]
SLICE_1:
SLICE_2:
stack[-2:]=[self]
SLICE_3:
stack[-3:]=[self]
STORE_SLICE_0:
del stack[-2:]
STORE_SLICE_1:
STORE_SLICE_2:
del stack[-3:]
STORE_SLICE_3:
del stack[-4:]
DELETE_SLICE_0:
del stack[-1:]
DELETE_SLICE_1:
DELETE_SLICE_2:
del stack[-2:]
DELETE_SLICE_3:
del stack[-3:]
STORE_SUBSCR:
del stack[-3:]
DELETE_SUBSCR:
del stack[-2:]
PRINT_EXPR:
PRINT_ITEM:
stack.pop()
PRINT_NEWLINE:
pass
BREAK_LOOP:
raise "No jumps here!"
LOAD_LOCALS:
stack.append(self)
RETURN_VALUE:
stack[:] = []
EXEC_STMT:
pass
POP_BLOCK:
pass
END_FINALLY:
pass
BUILD_CLASS:
stack[-3:] = [self]
STORE_NAME:
DELETE_NAME:
stack.pop()
UNPACK_TUPLE:
UNPACK_LIST:
stack.append([self] * self.arg)
STORE_ATTR:
DELETE_ATTR:
STORE_GLOBAL:
DELETE_GLOBAL:
stack.pop()
LOAD_CONST:
LOAD_NAME:
stack.append(self)
BUILD_TUPLE:
BUILD_LIST:
if self.arg>0:
stack[-self.arg:]=[self]
else:
stack.append(self)
BUILD_MAP:
stack.append(self)
LOAD_ATTR:
stack[-1] = self
COMPARE_OP:
stack[-2:]=[self] # ????
IMPORT_NAME:
stack.append(self)
IMPORT_FROM:
pass
JUMP_FORWARD:
JUMP_IF_TRUE:
JUMP_IF_FALSE:
JUMP_ABSOLUTE:
raise "jumps not handled here!"
FOR_LOOP:
raise "loop alert"
LOAD_GLOBAL:
stack.append(self)
SETUP_LOOP:
raise "loop alert!"
SETUP_EXCEPT:
SETUP_FINALLY:
pass # ??
LOAD_FAST:
stack.append(self)
STORE_FAST:
DELETE_FAST:
stack.pop()
SET_LINENO:
pass
RAISE_VARARGS:
raise "Exception!"
CALL_FUNCTION:
num_keyword_args=self.arg>>8
num_regular_args=self.arg&0xFF
stack[-2*num_keyword_args-num_regular_args-1:]=[self]
MAKE_FUNCTION:
stack[-self.arg-1:]=[self]
BUILD_SLICE:
stack[-self.arg:]=[self]
import os,string
file=open(os.path.join(os.path.dirname(__file__ ),'op_execute_methods'),'r')
lines=string.split(file.read(),'\n')[1:]
exec_funcs={}
n=len(lines)
for i in range(n):
if (not lines[i]) or lines[i][0]==' ':
continue
j=i
body=[]
while j<n:
if lines[j][0]==' ':
while lines[j] and lines[j][0]==' ':
body.append(lines[j])
j=j+1
break
j=j+1
body=' '+string.join(body,'\n ')
exec_funcs[lines[i][:-1]]=body
#!/usr/local/bin/python
from bytecodehacks.code_gen import write_ops
write_ops.Main()
import dis,re,sys,os,string
from bytecodehacks.code_gen import opexfuncread
temphead="""\
# this file is autogenerated by running
# from bytecodehacks.code_gen import write_ops
# write_ops.Main()
from bytecodehacks import opbases
from bytecodehacks.label import Label
_opbases = opbases
_Label = Label
del Label
del opbases
_bytecodes={}
"""
noargtemplate="""\
class %(name)s(_opbases.%(base)s):
op = %(index)d
opc = '\\%(index)03o'
def __init__(self,cs=None,code=None):
if cs is not None:
_opbases.%(base)s.__init__(self,cs,code)
def execute(self,stack):
%(exec_body)s
_bytecodes[%(name)s.opc]=%(name)s
"""
argtemplate="""\
class %(name)s(_opbases.%(base)s):
op = %(index)d
opc = '\\%(index)03o'
def __init__(self,csorarg,code=None):
if code is not None:
_opbases.%(base)s.__init__(self,csorarg,code)
else:
self.user_init(csorarg)
def execute(self,stack):
%(exec_body)s
_bytecodes[%(name)s.opc]=%(name)s
"""
jumptemplate="""\
class %(name)s(_opbases.%(base)s):
op = %(index)d
opc = '\\%(index)03o'
def __init__(self,csorarg=None,code=None):
if csorarg is not None:
if code is not None:
_opbases.%(base)s.__init__(self,csorarg,code)
else:
self.label = _Label()
self.user_init(csorarg)
else:
self.label = _Label()
def execute(self,stack):
%(exec_body)s
_bytecodes[%(name)s.opc]=%(name)s
"""
idprog=re.compile('^[_a-zA-Z][_a-zA-Z0-9]*$')
notopprog=re.compile('^<[0-9]+>$')
def main(file=sys.stdout):
file.write(temphead)
trans=string.maketrans('+','_')
for index in range(len(dis.opname)):
name=string.translate(dis.opname[index],trans)
if notopprog.match(name):
continue
if not idprog.match(name):
name="Opcode_%d"%index
s = "generating %s ..."%name
pad = " " * (30-len(s))
print s,pad,
base="GenericOneByteCode"
if index < dis.HAVE_ARGUMENT:
template = noargtemplate
base="GenericOneByteCode"
elif index in dis.hasjrel:
template = jumptemplate
base="JRel"
elif index in dis.hasjabs:
template = jumptemplate
base="JAbs"
elif index in dis.hasname:
template = argtemplate
base="NameOpcode"
elif index in dis.haslocal:
template = argtemplate
base="LocalOpcode"
else:
template = argtemplate
base="GenericThreeByteCode"
exec_body=opexfuncread.exec_funcs[name]
file.write(template%locals())
print "done"
def Main():
from bytecodehacks import __init__
main(open(os.path.join(os.path.dirname(__init__.__file__),'ops.py'),'w'))
import new
def copy_code_with_changes(codeobject,
argcount=None,
nlocals=None,
stacksize=None,
flags=None,
code=None,
consts=None,
names=None,
varnames=None,
filename=None,
name=None,
firstlineno=None,
lnotab=None):
if argcount is None: argcount = codeobject.co_argcount
if nlocals is None: nlocals = codeobject.co_nlocals
if stacksize is None: stacksize = codeobject.co_stacksize
if flags is None: flags = codeobject.co_flags
if code is None: code = codeobject.co_code
if consts is None: consts = codeobject.co_consts
if names is None: names = codeobject.co_names
if varnames is None: varnames = codeobject.co_varnames
if filename is None: filename = codeobject.co_filename
if name is None: name = codeobject.co_name
if firstlineno is None: firstlineno = codeobject.co_firstlineno
if lnotab is None: lnotab = codeobject.co_lnotab
return new.code(argcount,
nlocals,
stacksize,
flags,
code,
consts,
names,
varnames,
filename,
name,
firstlineno,
lnotab)
code_attrs=['argcount',
'nlocals',
'stacksize',
'flags',
'code',
'consts',
'names',
'varnames',
'filename',
'name',
'firstlineno',
'lnotab']
class CycleHandle:
'''CycleHandles are proxies for cycle roots
A CycleHandle subclass should create one or more workers, and pass them
to _set_workers. These workers can then participate in cycles, as long
as deleting all of the worker's attributes will break the cycle. When a
CycleHandle instance goes away, it deletes all attributes of all of
its workers. You could also explicitly call drop_workers.
For example,
>>> class Ham:
... def __del__(self):
... print 'A ham has died!'
...
>>> ct = CycleHandle()
>>> ct._set_workers(Ham(), Ham())
>>> ct._workers[0].ham2 = ct._workers[1]
>>> ct._workers[1].ham1 = ct._workers[0]
>>> del ct
A ham has died!
A ham has died!
'''
_workers = ()
def _set_workers(self, *workers):
self.__dict__['_workers'] = workers
def _not_mutable(self, *x):
raise TypeError, 'CycleHandle is not mutable'
__delattr__ = _not_mutable
def __setattr__(self, attr, val):
for worker in self._workers:
if hasattr(worker, '__setattr__'):
return getattr(worker, '__setattr__')(attr, val)
_not_mutable_defs = ('__delslice__', '__setslice__', '__delitem__',
'__setitem__')
def __getattr__(self, attr):
for worker in self._workers:
if hasattr(worker, attr):
return getattr(worker, attr)
if attr in self._not_mutable_defs:
return self._not_mutable
raise AttributeError, attr
def _drop_workers(self):
for worker in self._workers:
worker.__dict__.clear()
self.__dict__['_workers'] = ()
def __del__(self, drop_workers=_drop_workers):
drop_workers(self)
def _test():
import doctest, cyclehandle
return doctest.testmod(cyclehandle)
if __name__ == "__main__":
_test()
from code_editor import Function
from ops import *
import dis,new,string
PRECONDITIONS = 1
POSTCONDITIONS = 2
INVARIANTS = 4
EVERYTHING = PRECONDITIONS|POSTCONDITIONS|INVARIANTS
if __debug__:
__strength__ = PRECONDITIONS|POSTCONDITIONS
else:
__strength__ = 0
# TODO: docs, sort out inheritance.
if __debug__:
def add_contracts(target_class,contract_class,strength=None):
if strength is None:
strength = __strength__
newmethods = {}
contractmethods = contract_class.__dict__
if strength & INVARIANTS:
inv = contractmethods.get("class_invariants",None)
for name, meth in target_class.__dict__.items():
if strength & PRECONDITIONS:
pre = contractmethods.get("pre_"+name,None)
if pre is not None:
meth = add_precondition(meth,pre)
if strength & POSTCONDITIONS:
post = contractmethods.get("post_"+name,None)
if post is not None:
meth = add_postcondition(meth,post)
if (strength & INVARIANTS) and inv \
and type(meth) is type(add_contracts):
if name <> '__init__':
meth = add_precondition(meth,inv)
meth = add_postcondition(meth,inv)
newmethods[name] = meth
return new.classobj(target_class.__name__,
target_class.__bases__,
newmethods)
def add_precondition(meth,cond):
meth = Function(meth)
cond = Function(cond)
mcs = meth.func_code.co_code
ccs = cond.func_code.co_code
nlocals = len(meth.func_code.co_varnames)
nconsts = len(meth.func_code.co_consts)
nnames = len(meth.func_code.co_names)
nargs = meth.func_code.co_argcount
retops = []
for op in ccs:
if op.__class__ is RETURN_VALUE:
# RETURN_VALUEs have to be replaced by JUMP_FORWARDs
newop = JUMP_FORWARD()
ccs[ccs.index(op)] = newop
retops.append(newop)
elif op.op in dis.hasname:
op.arg = op.arg + nnames
elif op.op in dis.haslocal:
if op.arg >= nargs:
op.arg = op.arg + nlocals
elif op.op in dis.hasconst:
op.arg = op.arg + nconsts
new = POP_TOP()
mcs.insert(0,new)
mcs[0:0] = ccs.opcodes
for op in retops:
op.label.op = new
meth.func_code.co_consts.extend(cond.func_code.co_consts)
meth.func_code.co_varnames.extend(cond.func_code.co_varnames)
meth.func_code.co_names.extend(cond.func_code.co_names)
return meth.make_function()
def add_postcondition(meth,cond):
""" a bit of a monster! """
meth = Function(meth)
cond = Function(cond)
mcode = meth.func_code
ccode = cond.func_code
mcs = mcode.co_code
ccs = ccode.co_code
nlocals = len(mcode.co_varnames)
nconsts = len(mcode.co_consts)
nnames = len(mcode.co_names)
nargs = ccode.co_argcount
cretops = []
Result_index = len(meth.func_code.co_varnames)
mcode.co_varnames.append('Result')
old_refs = find_old_refs(cond)
for op in ccs:
if op.__class__ is RETURN_VALUE:
newop = JUMP_FORWARD()
ccs[ccs.index(op)] = newop
cretops.append(newop)
elif op.op in dis.hasname:
if cond.func_code.co_names[op.arg] == 'Result' \
and op.__class__ is LOAD_GLOBAL:
ccs[ccs.index(op)] = LOAD_FAST(Result_index)
else:
op.arg = op.arg + nnames
elif op.op in dis.haslocal:
if op.arg >= nargs:
op.arg = op.arg + nlocals + 1 # + 1 for Result
elif op.op in dis.hasconst:
op.arg = op.arg + nconsts
# lets generate the prologue code to save values for `Old'
# references and point the LOAD_FASTs inserted by
# find_old_refs to the right locations.
prologue = []
for ref, load_op in old_refs:
if ref[0] in mcode.co_varnames:
prologue.append(LOAD_FAST(mcode.co_varnames.index(ref[0])))
else:
prologue.append(LOAD_GLOBAL(mcode.name_index(ref[0])))
for name in ref[1:]:
prologue.append(LOAD_ATTR(mcode.name_index(name)))
lname = string.join(ref,'.')
lindex = len(mcode.co_varnames)
mcode.co_varnames.append(lname)
prologue.append(STORE_FAST(lindex))
load_op.arg = lindex
mcs[0:0] = prologue
mretops = []
for op in mcs:
if op.__class__ is RETURN_VALUE:
newop = JUMP_FORWARD()
mcs[mcs.index(op)] = newop
mretops.append(newop)
n = len(mcs)
# insert the condition code
mcs[n:n] = ccs.opcodes
# store the returned value in Result
store_result = STORE_FAST(Result_index)
mcs.insert(n, store_result)
# target the returns in the method to this store
for op in mretops:
op.label.op = store_result
# the post condition will leave a value on the stack; lose it.
# could just strip off the LOAD_CONST & RETURN_VALLUE at the
# end of the function and scan for RETURN_VALUES in the
# postcondition as a postcondition shouldn't be returning
# things (certainly not other than None).
new = POP_TOP()
mcs.append(new)
# redirect returns in the condition to the POP_TOP just
# inserted...
for op in cretops:
op.label.op = new
# actually return Result...
mcs.append(LOAD_FAST(Result_index))
mcs.append(RETURN_VALUE())
# and add the new constants and names (to avoid core dumps!)
mcode.co_consts .extend(ccode.co_consts )
mcode.co_varnames.extend(ccode.co_varnames)
mcode.co_names .extend(ccode.co_names )
return meth.make_function()
def find_old_refs(func):
chaining = 0
refs = []
ref = []
code = func.func_code
cs = code.co_code
i = 0
while i < len(cs):
op=cs[i]
if not chaining:
if op.__class__ is LOAD_GLOBAL:
if code.co_names[op.arg]=='Old':
chaining=1
else:
if op.__class__ is LOAD_ATTR:
ref.append(code.co_names[op.arg])
else:
newop = LOAD_FAST(0)
cs[i-len(ref)-1:i] = [newop]
i = i - len(ref)
refs.append((ref,newop))
ref = []
chaining = 0
i=i+1
return refs
else: # if not __debug__
def add_contracts(target_class,contracts_class):
return target_class
# example
class Uncontracted:
def __init__(self,x,y):
self.x=x
self.y=y
def do(self):
# self.x = self.x + 1 # sneaky!
return self.x/self.y
class Contracts:
def pre___init__(self,x,y):
assert y <> 0
def post_do(self):
assert Old.self.x == self.x
assert Old.self.y == self.y
assert Result > 0, "Result was %s"%`Result`
def class_invariants(self):
assert self.x > 0
Contracted = add_contracts(Uncontracted,Contracts)
from code_editor import Function
from ops import *
def find_function_call(infunc,calledfuncname, allowkeywords=0, startindex=0):
i = startindex
code = infunc.func_code
cs = code.co_code
def match(op,name = calledfuncname):
return getattr(op,'name',None) == name
while i < len(cs):
op = code.co_code[i]
if match(op):
try:
if allowkeywords:
return simulate_stack_with_keywords(code,i)
else:
return simulate_stack(code,i)
except:
i = i + 1
i = i + 1
if allowkeywords:
return None,0
else:
return None
def call_stack_length_usage(arg):
num_keyword_args=arg>>8
num_regular_args=arg&0xFF
return 2*num_keyword_args + num_regular_args
def simulate_stack(code,index_start):
stack = []
cs = code.co_code
i, n = index_start, len(cs)
while i < n:
op = cs[i]
if op.__class__ is CALL_FUNCTION and op.arg+1==len(stack):
stack.append(op)
return stack
elif op.is_jump():
i = cs.index(op.label.op)+1
else:
op.execute(stack)
i = i + 1
raise "no call found!"
def simulate_stack_with_keywords(code,index_start):
stack = []
cs = code.co_code
i, n = index_start, len(cs)
while i < n:
op = cs[i]
if op.__class__ is CALL_FUNCTION \
and call_stack_length_usage(op.arg)+1==len(stack):
stack.append(op)
return stack, op.arg>>8
elif op.is_jump():
i = cs.index(op.label.op)+1
else:
op.execute(stack)
i = i + 1
raise "no call found!"
from code_editor import Function
from ops import *
from find_function_call import find_function_call
def iifize(func):
func = Function(func)
cs = func.func_code.co_code
while 1:
stack = find_function_call(func,"iif")
if stack is None:
break
load, test, consequent, alternative, call = stack
cs.remove(load)
jump1 = JUMP_IF_FALSE(alternative)
cs.insert(cs.index(test)+1,jump1)
jump2 = JUMP_FORWARD(call)
cs.insert(cs.index(consequent)+1,jump2)
cs.remove(call)
cs = None
return func.make_function()
import dis
from code_editor import Function
from find_function_call import find_function_call
from ops import \
LOAD_GLOBAL, RETURN_VALUE, SET_LINENO, CALL_FUNCTION, \
JUMP_FORWARD, STORE_FAST
INLINE_MAX_DEPTH = 100
def inline(func, **funcs):
func = Function(func)
code = func.func_code
for name, function in funcs.items():
count = inline1(func, name, function)
if count <> 0:
fcode=function.func_code
code.co_consts=code.co_consts+list(fcode.co_consts)
code.co_varnames=code.co_varnames+list(fcode.co_varnames)
code.co_names=code.co_names+list(fcode.co_names)
code.co_stacksize=code.co_stacksize+fcode.co_stacksize
return func.make_function()
def munge_code(function,code):
f = Function(function)
fcs = f.func_code.co_code
i, n = 0, len(fcs)
retops = []
while i < n:
op = fcs[i]
if op.__class__ is RETURN_VALUE:
# RETURN_VALUEs have to be replaced by JUMP_FORWARDs
newop = JUMP_FORWARD()
fcs[i] = newop
retops.append(newop)
elif op.op in dis.hasname:
op.arg = op.arg + len(code.co_names)
elif op.op in dis.haslocal:
op.arg = op.arg + len(code.co_varnames)
elif op.op in dis.hasconst:
op.arg = op.arg + len(code.co_consts)
# should we hack out SET_LINENOs? doesn't seem worth it.
i = i + 1
return fcs.opcodes, retops
def inline1(func,funcname,function):
code = func.func_code
cs = code.co_code
count = 0
defaults_added = 0
while count < INLINE_MAX_DEPTH:
stack, numkeywords = find_function_call(func,funcname,allowkeywords=1)
if stack is None:
return count
count = count + 1
load_func, posargs, kwargs, function_call = \
stack[0], stack[1:-2*numkeywords-1], stack[-2*numkeywords-1:-1], stack[-1]
kw={}
for i in range(0,len(kwargs),2):
name = code.co_consts[kwargs[i].arg]
valuesrc = kwargs[i+1]
kw[name] = valuesrc
varnames = list(function.func_code.co_varnames)
for i in kw.keys():
if i in varnames:
if varnames.index(i) < len(posargs):
raise TypeError, "keyword parameter redefined"
else:
raise TypeError, "unexpected keyword argument: %s"%i
# no varargs yet!
# flags = function.func_code.co_flags
# varargs = flags & (1<<2)
# varkeys = flags & (1<<3)
args_got = len(kw) + len(posargs)
args_expected = function.func_code.co_argcount
if args_got > args_expected:
raise TypeError,"too many arguments; expected %d, got %d"%(ac,len(lf) + len(posargs))
elif args_got < args_expected:
# default args?
raise TypeError,"not enough arguments; expected %d, got %d"%(ac,len(lf) + len(posargs))
cs.remove(load_func)
local_index = len(code.co_varnames)
for insn in posargs:
new = STORE_FAST(local_index)
cs.insert(cs.index(insn)+1,new)
labels = cs.find_labels(cs.index(new)+1)
for label in labels:
label.op = new
local_index = local_index + 1
for name, insn in kw.items():
new = STORE_FAST(varnames.index(name) + len(code.co_varnames))
cs.insert(cs.index(insn)+1,new)
labels = cs.find_labels(cs.index(new)+1)
for label in labels:
label.op = new
newops, retops = munge_code(function,code)
call_index = cs.index(function_call)
nextop = cs[call_index + 1]
cs[call_index:call_index + 1] = newops
for op in retops:
op.label.op = nextop
raise RuntimeError, "are we trying to inline a recursive function here?"
import struct
class Label:
def __init__(self,byte=None):
self.byte=byte
self.__op=None
self.absrefs=[]
self.relrefs=[]
def resolve(self,code):
self.__op=code.opcodes[code.byte2op[self.byte]]
def add_absref(self,byte):
# request that the absolute address of self.op be written to
# the argument of the opcode starting at byte in the
# codestring
self.absrefs.append(byte)
def add_relref(self,byte):
# request that the relative address of self.op be written to
# the argument of the opcode starting at byte in the
# codestring
self.relrefs.append(byte)
def __setattr__(self,attr,value):
if attr == 'op':
self.__op = value
else:
self.__dict__[attr] = value
def __getattr__(self,attr):
if attr == 'op':
return self.__op
else:
raise AttributeError, attr
def write_refs(self,cs):
address=self.__op.byte
for byte in self.absrefs:
cs.seek(byte+1)
cs.write(struct.pack('<h',address))
for byte in self.relrefs:
offset=address-byte-3
cs.seek(byte+1)
cs.write(struct.pack('<h',offset))
from macro import add_macro
def main():
def setq((x),v):
x = v
return v
add_macro(setq)
def pre_incr((x)):
x = x + 1
return x
add_macro(pre_incr)
def post_incr((x)):
t = x
x = x + 1
return t
add_macro(post_incr)
def pre_decr((x)):
x = x - 1
return x
add_macro(pre_decr)
def post_decr((x)):
t = x
x = x + 1
return t
add_macro(post_decr)
def add_set((x),v):
x = x + v
return x
add_macro(add_set)
def sub_set((x),v):
x = x - v
return x
add_macro(sub_set)
def mul_set((x),v):
x = x * v
return x
add_macro(mul_set)
def div_set((x),v):
x = x / v
return x
add_macro(div_set)
def mod_set((x),v):
x = x % v
return x
add_macro(mod_set)
main()
def test():
from macro import expand
def f(x):
i = 0
while pre_incr(i) < len(x):
if setq(c, x[i]) == 3:
print c, 42
x = expand(f)
return x
x(range(10))
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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