Using External Methods
Created by .
Last modified on 2003/08/01.
Using External Methods
What are External Methods?
One of the core components of Zope is DTML (Document Template Markup
Language), a powerful variable insertion and expression language that
provides "safe scripting" of Zope objects and dynamic content generation.
DTML is highly integrated with the Zope security model, and this
security integration makes it possible to let less privileged users
perform simple scripting and use the services provided by Zope objects
without compromising the security of the site.
A key concept of "safe scripting" is that unlike some other templating
systems such as PHP or ASP, DTML does not allow you to create blocks of
arbitrary script code. The reason for this is security. It would be
difficult to safely delegate work on a site to less priveleged users
if their code could access the filesystem, perform system calls or
consume system resources.
Instead of allowing arbitrary code embedded in DTML, Zope provides
External Methods. External Methods allow site managers to write
unrestricted code and provide those services to DTML authors in a
controlled, secure way. External Methods are created by writing a
method in a normal Python module, then creating a new External Method
object on your Zope site associated with your Python method.
An External Method is a Zope object in its own right, and has its own
security settings. This gives a site manager the ability to selectively
provide extended services to DTML authors. External Methods can be added
to Folders or any container object in Zope. When you add an External
Method to a Folder, it can be thought of as adding a new custom method
to that Folder.
Note that the actual Python code that an External Method encapsulates
must be created in a file on the server machine - it is not possible
to create a working External Method completely through the Web. This
is a security measure - in order to write unrestricted code, you must
have access to the machine.
The following examples assume that you have a basic familiarity with
DTML and the Python programming
language.
Creating an External Method
Let's look at the standard "Hello World" example implemented with
an External Method. In this example we will create a helloWorld
method in a Python module, create a new External Method object in
Zope associated with it, and finally create a simple DTML Document
that uses the services of our new External Method.
If you have not yet started using External Methods with your Zope
installation, you will first need to create a directory named
Extensions
in the root directory of your Zope installation.
For example, if you installed Zope in /usr/local/zope2
, your
Extensions directory should be /usr/local/zope2/Extensions
.
Note that the directory name is case-sensitive, and make sure
that the new directory is readable by the user that is used to
run your Zope server.
Next we will create a new file in the Extensions directory named
demo.py
. This is the Python module we will use to define our
custom logic. Note that Python modules which define External Methods
must be located in the Extensions directory. Edit the demo.py
file
and add the following Python code:
def helloWorld(self):
"""A simple external method."""
return 'Hello World!'
With your Web browser, access the management interface of your
Zope site and create a new Folder object named demo
. This is
where we will implement our examples. In the new Folder, select
"External Method" from the add list (at the bottom of the right
hand management frame for the demo
Folder). This will bring up
the "Add External Method" form, which will let us provide an id
and title for the External Method and provides fields for entering
the function name and Python module file that implement the new
External Method.
In the "Id" field of the form, enter "helloWorld". The id will be used
as the id of the Zope External Method object. It does not have to be
the same as the actual function name, but for these examples we will
give our External Methods ids that match the names of the methods in
the Python module for clarity.
In the "Title" field you may optionally enter a title for the External
Method. In the "Function name" field, enter "helloWorld" and in the
"Python module file" field enter "demo" (Note that you should omit the
".py" from the name of the Python module file). Click the "Add" button
and you will be returned to the management screen of the demo
Folder
which now includes the newly created helloWorld
External Method
object in its contents listing.
Finally, to use the new External Method, create a new DTML Document
object named test
in the demo
Folder. To call the External Method
from the DTML Document, add the following tag to the source of your
DTML Document:
<dtml-var helloWorld>
The actual External Method object in Zope acts as a kind of pointer
to the method definition in your Python module. To make changes to
an External Method:
- Make your changes and save the Python module.
- Visit the the management interface of the corresponding External
Method object and click the
Edit
button. This will "reload" the
code of your External Method.
Tip: if you are running Zope in development mode, you don't need to
click the "Edit" for External Methods when you make changes. When in
development mode, External Methods detect changes to their corresponding
Python modules and will reload as needed automatically when changes are
made. To run Zope in development mode, pass the -D
command line option
to Zope at startup.
Key Concepts
Basic usage of simple External Methods is pretty straightforward, as
demonstrated by the helloWorld example above. For more complex usage
however, it is important to keep several key concepts in mind that
will help you take full advantage of External Methods.
- External Methods are methods of their containers
As its name implies, an External Method acts as a method of the
object that contains it. You may have noticed that the code for
the helloWorld method in the previous example takes a self
argument. When an External Method is called, the self
argument
will be the container of the actual External Method object - in
the case of our example, self
was the demo
folder containing
the helloWorld method.
Understanding the nature of self
in an External Method is important
because self
is usually what you will use when you need to interact
with the Zope object system. A common question among new Zope users
when writing External Methods is "what do I import to get access to
the object in my Zope site?". The answer is that you don't import
anything - you use the self
parameter to interact with the object
system.
For example, knowing that self
is the container of the External
Method you can find and work with subobjects or parent objects of
'self':
# Get refs to two subfolders of "self" that have
# ids of "subfolder1" and "subfolder2".
sub1=self.subfolder1
sub2=self.subfolder2
# Get a ref to the parent of "self".
parent=self.aq_parent
Also note that if self
contains other External Methods, you can
call them from an External Method just as if they were normal Python
methods:
# Call another external method called sayHello.
msg=self.sayHello()
At the time of this writing, API documentation describing the
interfaces provided by the various Zope objects is still in
progress. Until the API documentation is complete, a good way to
find out about the services of various Zope object is to consult
the Zope source code in the lib/python
directory of your Zope
installation.
- Arguments must be passed explicitly from DTML
In the previous example, we were able to call the helloWorld
method using a normal DTML var tag. This is because the helloWorld
method took only one argument (self
). As in standard Python, the
self
argument is implicit when calling an External Method and does
not need to be passed explicitly by the programmer. Zope will
automatically bind self
when an External Method is called from DTML.
If you define an External Method that takes more arguments than
simply self
, you need to pass those arguments when calling the
External Method from DTML. To expand on the previous example, let's
update the helloWorld
code to personalize its greeting based on the
identity of the Web user. To do this, the helloWorld
method will need
access to the REQUEST object, so we will add it to the method argument
list:
def helloWorld(self, REQUEST):
"""A first external method."""
user=REQUEST.AUTHENTICATED_USER
return 'Hello %s!' % user
Now that the helloWorld
method requires an argument, it will be
necessary to change the way it is called from DTML to make sure that
the required argument is passed. To do this, we will use the DTML
expression syntax to pass in the REQUEST object that exists in the
DTML namespace when calling 'helloWorld':
<dtml-var "helloWorld(REQUEST)">
Argument semantics for External Methods are identical to those for
normal Python methods. You can define External Methods with default
or keyword arguments just as you can in Python. Like Python methods,
External Methods can return any type of data. An External Method
could return a list or tuple for example, which would allow you to
iterate over the result in DTML using the "in" tag.
- External Methods are Zope objects too
External Methods have a dual personality - they act primarily as
methods of their containers, but they are also Zope objects with
many of the common features of other Zope objects. They have their
own security settings like any other Zope object so you can control
access to them on a method by method basis (though the actual code
may all exist in a single Python module).
Like DTML Documents and DTML Methods, you can also navigate directly
to an External Method object through the Web. In the case of the
helloWorld
example, you can point your Web browser to the url
/demo/helloWorld
and the result of the External Method will be
returned to the browser. While accessing External Methods directly
through the Web is less common than calling them from DTML, the fact
that you can access them directly can be extremely useful.
One difference to note when External Methods are called through
the Web is that Zope will automatically map form fields and other
values found in the Web request to the arguments required by the
External Method based on their names.
A Real World Example
The following example demonstrates an External Method that is called
directly via the Web. This example implements a virtual "default
document" that selects the correct actual default document to return
to a user based on the language preferences sent by the user's Web
browser.
The External Method is named index_html
, which is the name that
Zope looks for by default to use as the default document for a Folder.
When the External method is called, it will look in the REQUEST
object to determine the client's preferred language. Finally it
will look in it's container (self
) to see if there is a custom
default document for that language and attempt to render the
document and return it:
def index_html(self, REQUEST):
"""A smart index document redirector"""
# Look for preferred lang in the REQUEST. If it
# is not found, 'en' will be used as the default.
lang=REQUEST.get('HTTP_ACCEPT_LANGUAGE', 'en')
# By convention, we have saved different language
# versions of our index document in the Folder with
# ids of 'index_xx.html', where 'xx' is the lang id.
# This code will attempt to get the right document,
# then return the rendered result to the client.
docname='index_%s.html' % lang
if hasattr(self, docname):
# Try to return the document for the
# requested language.
doc=getattr(self, docname)
return doc(self, REQUEST)
else:
# Return english if we don't have the
# requested language.
doc=getattr(self, 'index_en.html')
return doc(self, REQUEST)
More Information
More information and examples of External Methods can be found in
the Zope Content Manager's Guide.