[Zope-Perl] Perl External Methods
Gisle Aas
gisle@ActiveState.com
13 Jun 2000 12:13:28 +0200
The Perl External Method implementation now looks like this:
# Copyright 2000 Digital Creations
# Copyright 2000 ActiveState Tool Corp.
#
# This module can be redistributed and/or modified
# under the terms of the Zope Public Licence v1.0.
#
# This product includes software developed by Digital Creations
# for use in the Z Object Publishing Environment (http://www.zope.org/).
from Globals import Persistent, HTMLFile, MessageDialog
import OFS.SimpleItem
import AccessControl.Role
import Acquisition
from string import split, strip, join, find
import re
ZopeExt_name_re = re.compile(r"^ZopeExt(::[a-zA-Z]\w*)+$")
def get_perl_func(name, module):
if not ZopeExt_name_re.match(name):
raise ValueError, "ZopeExt:: prefix required for perl function name"
if not ZopeExt_name_re.match(module):
raise ValueError, "ZopeExt:: prefix required for perl module name"
# By importing here we only load perl on demand, i.e. when the first perl
# function is called. Perl easily adds a few megabytes to the Zope image.
import perl
# unshift(@INC, "./Extensions");
inc = perl.get_ref("@INC")
inc_copy = inc[:]
inc.insert(0, INSTANCE_HOME + "/Extensions")
#inc[1:] = perl.get_ref("@") # only leave "$INSTANCE_HOME/Extensions"
try:
perl.require(module) # XXX do we need some kind of untaint here??
finally:
inc[:] = inc_copy # restore @INC
f = perl.get_ref(name)
return f
# A class emulating internal func-code objects enough to fool
# ZPublisher's mapply. Need to emulate co_varnames and co_argcount.
# For Python External methods you find a similar class in
# App.Extentions.
class FuncCode:
def __init__(self, args = ""):
self.co_varnames = filter(None, map(strip, split(args, ",")))
self.co_argcount = len(self.co_varnames)
def args(self):
return join(self.co_varnames, ", ")
# This is the actual class that we will store instances of, in the
# ZODB as this product is instantiated through manage_addPerlExtMethod
# below. It stores enough information to obtain a reference to a single
# perl function loaded from the file system on demand and the information
# needed to fool mapply into thinking that this object is a function
# by itself.
class PerlExtMethod(OFS.SimpleItem.Item, Persistent, Acquisition.Explicit,
AccessControl.Role.RoleManager):
"""Web-callable functions that encapsulate external perl functions."""
# Emulated attributes of functions. The func_code class attribute
# is not really used as instances override it with their own version.
func_defaults=()
func_code=FuncCode()
# Set up various stuff that the Zope frameworks wants
meta_type='Perl External Method'
manage_options=(
(
{'label':'Properties', 'action':'manage_main',},
{'label':'Try It', 'action':'manage_try'},
)
+OFS.SimpleItem.Item.manage_options
+AccessControl.Role.RoleManager.manage_options
)
__ac_permissions__=(
('View management screens', ('manage_main',)),
('Change External Methods', ('manage_edit',)),
('View', ('__call__','')),
)
# I am not really sure why :-(
ZopeTime=Acquisition.Acquired
HelpSys=Acquisition.Acquired
def __init__(self, id, title, module, function, args):
self.id=id
self.manage_edit(title, module, function, args)
manage_main = HTMLFile('methodEdit', globals())
def manage_edit(self, title, module, function, args, REQUEST=None):
"""Change the perl external method"""
self.title = str(title)
module = strip(str(module))
if module[-3:] == '.pm': module=module[:-3]
if module[0:9] != 'ZopeExt::': module = "ZopeExt::" + module
self.module = module
function = strip(str(function))
if find(function, "::") < 0:
function = module + "::" + function
self.function = function
self.func_code = FuncCode(str(args))
# try to load the function to get early warnings in case
# there are problems
get_perl_func(self.function, self.module)
if REQUEST: return MessageDialog(
title ='Changed %s' % self.id,
message='%s has been updated' % self.id,
action =REQUEST['URL1']+'/manage_main',
target ='manage_main')
manage_try = HTMLFile('methodTry', globals())
def __call__(self, *args, **kw):
"""Invoke a PerlExternalMethod"""
# The "_v_" prefix tells ZODB not try to store this thing
if not hasattr(self, "_v_f"):
self._v_f = get_perl_func(self.function, self.module)
return apply(self._v_f, args, kw)
def args(self):
return self.func_code.args()
# another interface to calling that dtml-methods are able to invoke
call = __call__
manage_addPerlExtMethodForm=HTMLFile('methodAdd', globals())
def manage_addPerlExtMethod(self, id, title, module, function, args,
REQUEST=None):
"""Add a perl external method to a folder"""
self._setObject(id, PerlExtMethod(id, title, module, function, args))
if REQUEST:
return self.manage_main(self, REQUEST)
def initialize(context):
context.registerClass(
PerlExtMethod,
constructors=(manage_addPerlExtMethodForm,
manage_addPerlExtMethod),
icon='extmethod.gif',
)