Commit 916bcfd9 authored by Hanno Schlichting's avatar Hanno Schlichting

Moved Products.ExternalMethod to its own distribution

parent f9416b4b
......@@ -49,6 +49,7 @@ eggs =
Missing
MultiMapping
Persistence
Products.ExternalMethod
Products.ZCTextIndex
Record
RestrictedPython
......
......@@ -22,7 +22,8 @@ Bugs Fixed
Restructuring
+++++++++++++
- Factored out the `Products.MIMETools` package into its own distribution.
- Factored out the `Products.MIMETools` and `Products.ExternalMethod` packages
into their own distributions.
- Factored out the `Products.ZSQLMethods` into its own distribution. The
distribution also includes the `Shared.DC.ZRDB` code. The Zope2 distribution
......
......@@ -47,6 +47,7 @@ setup(name='Zope2',
'Missing',
'MultiMapping',
'Persistence',
'Products.ExternalMethod',
'Products.MIMETools',
'Products.ZCTextIndex',
'Record',
......
External Method Changes
External Method 1.3
Features
- Permissions have been updated to work with the Principia 1.2 and
later permission-management enhancements.
External Method 1.2
Bug Fixes
- TypeError exceptions in external methods were reported
incorrectly.
Features
- ExternalMethods now use standard_error_message to report errors.
External Method 1.1
This release changes the way that ExternalMethods are bound to
folders. ExternalMethods now bind themselves to their acquisition
parents, rather than to REQUEST['PARENTS'][0]. This is needed to
make ExternalMethods useful in trees and exprs. The 1.1 release of
Principia is needed for this release of ExternalMethod to
function correctly.
External Method 1.0.3
Bugs Fixed
- A new copy of an external method was written the first time
it was used after being loaded from the database. In addition to
database bloat, this could also cause strange session/locking behavior.
External Method 1.0.2
Bugs Fixed
- Add permissions were not editable.
Features
- If an ExternalMethod takes a single argument, named self, and is
called with no arguments, then the folder in which the method
is accessed is passed. This is handy when the method is called
from a document.
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""External Method Product
This product provides support for external methods, which allow
domain-specific customization of web environments.
"""
__version__='$Revision: 1.52 $'[11:-2]
import os
import stat
import sys
import traceback
from AccessControl.class_init import InitializeClass
from AccessControl.Permissions import change_external_methods
from AccessControl.Permissions import view_management_screens
from AccessControl.Permissions import view as View
from AccessControl.SecurityInfo import ClassSecurityInfo
from Acquisition import Acquired
from Acquisition import Explicit
from App.Dialogs import MessageDialog
from App.Extensions import getObject
from App.Extensions import getPath
from App.Extensions import FuncCode
from App.special_dtml import DTMLFile
from App.special_dtml import HTML
from OFS.role import RoleManager
from OFS.SimpleItem import Item
from OFS.SimpleItem import pretty_tb
from Persistence import Persistent
from App.Management import Navigation
from ComputedAttribute import ComputedAttribute
manage_addExternalMethodForm=DTMLFile('dtml/methodAdd', globals())
def manage_addExternalMethod(self, id, title, module, function, REQUEST=None):
"""Add an external method to a folder
In addition to the standard object-creation arguments,
'id' and title, the following arguments are defined:
function -- The name of the python function. This can be a
an ordinary Python function, or a bound method.
module -- The name of the file containing the function
definition.
The module normally resides in the 'Extensions' directory.
If the zope.conf directive 'extensions' was overriden, then
it will specify where modules should reside.
However, the file name may have a prefix of
'product.', indicating that it should be found in a product
directory.
For example, if the module is: 'ACMEWidgets.foo', then an
attempt will first be made to use the file
'lib/python/Products/ACMEWidgets/Extensions/foo.py'. If this
failes, then the file 'Extensions/ACMEWidgets.foo.py' will be
used.
"""
id=str(id)
title=str(title)
module=str(module)
function=str(function)
i=ExternalMethod(id,title,module,function)
self._setObject(id,i)
if REQUEST is not None:
return self.manage_main(self,REQUEST)
class ExternalMethod(Item, Persistent, Explicit,
RoleManager, Navigation):
"""Web-callable functions that encapsulate external python functions.
The function is defined in an external file. This file is treated
like a module, but is not a module. It is not imported directly,
but is rather read and evaluated. The file must reside in the
'Extensions' subdirectory of the Zope installation, or in the directory
specified by the 'extensions' directive in zope.conf, or in an
'Extensions' subdirectory of a product directory.
Due to the way ExternalMethods are loaded, it is not *currently*
possible to use Python modules that reside in the 'Extensions'
directory. It is possible to load modules found in the
'lib/python' directory of the Zope installation, or in
packages that are in the 'lib/python' directory.
"""
meta_type = 'External Method'
security = ClassSecurityInfo()
security.declareObjectProtected(View)
func_defaults = ComputedAttribute(lambda self: self.getFuncDefaults())
func_code = ComputedAttribute(lambda self: self.getFuncCode())
ZopeTime = Acquired
HelpSys = Acquired
manage_page_header = Acquired
manage_options=(
(
{'label':'Properties', 'action':'manage_main',
'help':('ExternalMethod','External-Method_Properties.stx')},
{'label':'Test', 'action':'',
'help':('ExternalMethod','External-Method_Try-It.stx')},
)
+ Item.manage_options
+ RoleManager.manage_options
)
def __init__(self, id, title, module, function):
self.id=id
self.manage_edit(title, module, function)
security.declareProtected(view_management_screens, 'manage_main')
manage_main=DTMLFile('dtml/methodEdit', globals())
security.declareProtected(change_external_methods, 'manage_edit')
def manage_edit(self, title, module, function, REQUEST=None):
"""Change the external method
See the description of manage_addExternalMethod for a
description of the arguments 'module' and 'function'.
Note that calling 'manage_edit' causes the "module" to be
effectively reloaded. This is useful during debugging to see
the effects of changes, but can lead to problems of functions
rely on shared global data.
"""
title=str(title)
module=str(module)
function=str(function)
self.title=title
if module[-3:]=='.py': module=module[:-3]
elif module[-4:]=='.pyc': module=module[:-4]
self._module=module
self._function=function
self.getFunction(1)
if REQUEST:
message="External Method Uploaded."
return self.manage_main(self,REQUEST,manage_tabs_message=message)
def getFunction(self, reload=0):
f=getObject(self._module, self._function, reload)
if hasattr(f,'im_func'): ff=f.im_func
else: ff=f
self._v_func_defaults = ff.func_defaults
self._v_func_code = FuncCode(ff,f is not ff)
self._v_f=f
return f
def reloadIfChanged(self):
# If the file has been modified since last loaded, force a reload.
ts=os.stat(self.filepath())[stat.ST_MTIME]
if (not hasattr(self, '_v_last_read') or
(ts != self._v_last_read)):
self._v_f=self.getFunction(1)
self._v_last_read=ts
def getFuncDefaults(self):
import Globals # for data
if Globals.DevelopmentMode:
self.reloadIfChanged()
if not hasattr(self, '_v_func_defaults'):
self._v_f = self.getFunction()
return self._v_func_defaults
def getFuncCode(self):
import Globals # for data
if Globals.DevelopmentMode:
self.reloadIfChanged()
if not hasattr(self, '_v_func_code'):
self._v_f = self.getFunction()
return self._v_func_code
security.declareProtected(View, '__call__')
def __call__(self, *args, **kw):
"""Call an ExternalMethod
Calling an External Method is roughly equivalent to calling
the original actual function from Python. Positional and
keyword parameters can be passed as usual. Note however that
unlike the case of a normal Python method, the "self" argument
must be passed explicitly. An exception to this rule is made
if:
- The supplied number of arguments is one less than the
required number of arguments, and
- The name of the function\'s first argument is 'self'.
In this case, the URL parent of the object is supplied as the
first argument.
"""
import Globals # for data
filePath = self.filepath()
if filePath==None:
raise RuntimeError,\
"external method could not be called " \
"because it is None"
if not os.path.exists(filePath):
raise RuntimeError,\
"external method could not be called " \
"because the file does not exist"
if Globals.DevelopmentMode:
self.reloadIfChanged()
if hasattr(self, '_v_f'):
f=self._v_f
else:
f=self.getFunction()
__traceback_info__=args, kw, self._v_func_defaults
try: return f(*args, **kw)
except TypeError, v:
tb=sys.exc_info()[2]
try:
if ((self._v_func_code.co_argcount-
len(self._v_func_defaults or ()) - 1 == len(args))
and self._v_func_code.co_varnames[0]=='self'):
return f(self.aq_parent.this(), *args, **kw)
raise TypeError, v, tb
finally: tb=None
def function(self): return self._function
def module(self): return self._module
def filepath(self):
if not hasattr(self, '_v_filepath'):
self._v_filepath=getPath('Extensions', self._module,
suffixes=('','py','pyc','pyp'))
return self._v_filepath
InitializeClass(ExternalMethod)
External Methods
The External Method product provides support for external Python
methods, exposing them as callable objects within the Zope
environment.
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__doc__='''External Method Product Initialization
$Id$'''
__version__='$Revision: 1.15 $'[11:-2]
import ExternalMethod
# This is the new way to initialize products. It is hoped
# that this more direct mechanism will be more understandable.
def initialize(context):
context.registerClass(
ExternalMethod.ExternalMethod,
constructors=(ExternalMethod.manage_addExternalMethodForm,
ExternalMethod.manage_addExternalMethod),
icon='extmethod.gif',
)
context.registerHelp()
context.registerHelpTitle('Zope Help')
<dtml-var manage_page_header>
<dtml-var "manage_form_title(this(), _,
form_title='Add External Method',
help_product='ExternalMethod',
help_topic='External-Method_Add.stx'
)">
<p class="form-help">
External Methods allow you to add functionality to Zope by writing Python
functions which are exposed as callable Zope objects. The <em>module name</em>
should give the name of the Python module without the &quot;.py&quot;
file extension. The <em>function name</em> should name a callable object
found in the module.
</p>
<form action="manage_addExternalMethod" method="post">
<table cellspacing="0" cellpadding="2" border="0">
<tr>
<td align="left" valign="top">
<div class="form-label">
Id
</div>
</td>
<td align="left" valign="top">
<input type="text" name="id" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="title" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Module Name
</div>
</td>
<td align="left" valign="top">
<input type="text" name="module" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Function Name
</div>
</td>
<td align="left" valign="top">
<input type="text" name="function" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
</td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" name="submit"
value=" Add " />
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<form action="manage_edit" method="post">
<table cellspacing="0" cellpadding="2" border="0">
<tr>
<td align="left" valign="top">
<div class="form-label">
Id
</div>
</td>
<td align="left" valign="top">
&dtml-id;
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="title" size="40"
value="&dtml-title;" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Module Name
</div>
</td>
<td align="left" valign="top">
<input type="text" name="module" size="40"
value="&dtml-module;" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Function Name
</div>
</td>
<td align="left" valign="top">
<input type="text" name="function" size="40"
value="&dtml-function;" />
</td>
</tr>
<tr>
<td align="left" valign="top">
</td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" name="submit"
value="Save Changes" />
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>
External Method: Call Python methods from Zope
External Methods associate a Python function on the filesystem with
an object in Zope. This object can be called from DTML and Python
like other Zope methods.
External Method - Add: Create a new External Method.
Description
Creates a new External Method and adds it to the
current Folder.
Controls
'ID' -- Specifies the id of the external method.
'Title' -- Allows you to specify the title of the external method.
'Function name' -- Allows you to specify the name of the function
to use for the external method.
'Python module file' -- Allows you to specify the module in which
the function is located. The Python module may be located in the
Zope 'Extensions' directory, or in a 'Extensions'
directory in a product directory. Product directories are
located in 'lib/python/Products'. If the Python
module is in a product directory this should be indicated
in the name of the module which should be the name of the
product followed by a dot followed by the name of the
Python module file.
External Method - Properties: Manage the properties of an external method.
Description
View and manage the properties of an External Method.
Controls
'ID' -- Specifies the id of the external method.
'Title' -- Allows you to specify the title of the external method.
'Function name' -- Allows you to specify the name of the function
(method) to use for the external method.
'Python module file' -- Allows you to specify the module in which
the function is located. The Python module may be located in the
Zope 'Extensions' directory, or in a 'Extensions' directory in a
product directory. Product directories are located in
'lib/python/Products'. If the Python module is in a product
directory this should be indicated in the name of the module which
should be the name of the product followed by a dot followed by
the name of the Python module file.
External Method - Try It: Test the external method.
Description
This view executes the external method and returns the results if
any. Only parameters from the web request are passed the the
external method.
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
def manage_addExternalMethod(id, title, module, function):
"""
Add an external method to an
'ObjectManager'.
In addition to the standard object-creation arguments,
'id' and title, the following arguments are defined:
function -- The name of the python function. This can be a
an ordinary Python function, or a bound method.
module -- The name of the file containing the function
definition.
The module normally resides in the 'Extensions'
directory, however, the file name may have a prefix of
'product.', indicating that it should be found in a product
directory.
For example, if the module is: 'ACMEWidgets.foo', then an
attempt will first be made to use the file
'lib/python/Products/ACMEWidgets/Extensions/foo.py'. If this
failes, then the file 'Extensions/ACMEWidgets.foo.py' will be
used.
"""
class ExternalMethod:
"""
Web-callable functions that encapsulate external
Python functions.
The function is defined in an external file. This file is treated
like a module, but is not a module. It is not imported directly,
but is rather read and evaluated. The file must reside in the
'Extensions' subdirectory of the Zope installation, or reside in
the path specified by 'extensions' directive in zope.conf, or in an
'Extensions' subdirectory of a product directory.
Due to the way ExternalMethods are loaded, it is not *currently*
possible to import Python modules that reside in the 'Extensions'
directory. It is possible to import modules found in the
'lib/python' directory of the Zope installation, or in
packages that are in the 'lib/python' directory.
"""
__constructor__=manage_addExternalMethod
def manage_edit(title, module, function, REQUEST=None):
"""
Change the
External Method.
See the description of manage_addExternalMethod for a
description of the arguments 'module' and 'function'.
Note that calling 'manage_edit' causes the "module" to be
effectively reloaded. This is useful during debugging to see
the effects of changes, but can lead to problems of functions
rely on shared global data.
"""
def __call__(*args, **kw):
"""
Call the
External Method.
Calling an External Method is roughly equivalent to calling
the original actual function from Python. Positional and
keyword parameters can be passed as usual. Note however that
unlike the case of a normal Python method, the "self" argument
must be passed explicitly. An exception to this rule is made
if:
- The supplied number of arguments is one less than the
required number of arguments, and
- The name of the function's first argument is 'self'.
In this case, the URL parent of the object is supplied as the
first argument.
"""
from math import sqrt
def testf(arg1, sqrt = sqrt):
return sqrt(arg1)
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
$Id$
"""
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
Revision information:
$Id$
"""
import math
import os
import unittest
import ZODB # dead goat
import Products.ExternalMethod.tests
from Products.ExternalMethod.ExternalMethod import ExternalMethod
import App.config
class TestExternalMethod(unittest.TestCase):
def setUp(self):
self._old = App.config.getConfiguration()
cfg = App.config.DefaultConfiguration()
cfg.instancehome = os.path.dirname(
Products.ExternalMethod.tests.__file__)
App.config.setConfiguration(cfg)
def tearDown(self):
App.config.setConfiguration(self._old)
def testStorage(self):
em1 = ExternalMethod('em', 'test method', 'Test', 'testf')
self.assertEqual(em1(4), math.sqrt(4))
state = em1.__getstate__()
em2 = ExternalMethod.__basicnew__()
em2.__setstate__(state)
self.assertEqual(em2(9), math.sqrt(9))
self.failIf(state.has_key('func_defaults'))
def test_mapply(self):
from ZPublisher.mapply import mapply
em1 = ExternalMethod('em', 'test method', 'Test', 'testf')
self.assertEqual(mapply(em1, (), {'arg1': 4}), math.sqrt(4))
state = em1.__getstate__()
em2 = ExternalMethod.__basicnew__()
em2.__setstate__(state)
self.assertEqual(mapply(em1, (), {'arg1': 9}), math.sqrt(9))
def test_suite():
return unittest.makeSuite(TestExternalMethod)
def package_home(globals_dict):
__name__=globals_dict['__name__']
m=sys.modules[__name__]
if hasattr(m,'__path__'):
r=m.__path__[0]
elif "." in __name__:
r=sys.modules[__name__.split('.',1)[0]].__path__[0]
else:
r=__name__
return os.path.abspath(r)
if __name__=='__main__':
unittest.main(defaultTest='test_suite')
External Method-1-0-0
\ No newline at end of file
......@@ -15,6 +15,7 @@ Missing = 2.13.1
MultiMapping = 2.13.0
nt-svcutils = 2.13.0
Persistence = 2.13.2
Products.ExternalMethod = 2.13.0
Products.MIMETools = 2.13.0
Products.ZCTextIndex = 2.13.0
Record = 2.13.0
......
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