ZopeMag's mascot the ZOPE fish


Article Finder
People
Issue 4 - Revision 9  /   July 9, 2003 


 
  ZopeMag Links:
Latest Issue
Credits
Issue 10
Issue 09
Issue 08
Issue 07
Issue 06
Issue 05
Issue 04
Issue 03
Issue 02
Issue 01
 
 
Downloads
     
  Letter from the Editor:
   Issue 4

Interviews:
Each issue we interview important people in the Zope world.

 Lalo Martins

Articles:
Throughout the quarter we cover topics of interest to Zope developers, designers, and users.

  Intro to Zope 3 Part II

  Working with My Media Manager

  Transactions by example: Part II

  Open Page Templates

  Python Scripts

  Building a New UI For Zope 3 Part II

Product Review:
Too many Products, too little time? ZopeMag keeps you up-to-date which Zope Products are worthwhile downloading.

  icoya
  Easy Publisher
  ZopeTestCase
  CPS
 
 
Downloads
     
  URLs / Download
Products we talk about in this issues Articles and Reviews

  icoya
  Easy Publisher
  CPS
  ZopeTestCase
 
     

Illustration by Lia Avant
Python Scripts Cover
Using Python Scripts

Using Python Scripts
- Advanced Zope Scripting
- - - - - - - - - - - -

By Nico Grubert | July 1, 2003

print

Introduction

A Zope-based Web application is a mixture of presentation, logic and data which are managed by means of Zope objects. For presentation DTML Documents and DTML Methods, as well as Page Templates, can be used, although DTML can do things that Page Templates can't. However, DTML has been overused by developers who often mixed logic and presentation in writing DTML code. The result is that the code is hard to understand and mixing logic and presentation makes it difficult for the designer and developer to work on the code at the same time. Due to the various disadvantages of DTML, Zope Page Templates (ZPTs) have become increasingly popular and many Zope developers now manage presentation with ZPTs and logic with Zope Scripts.

Zope Scripts can be "Python Scripts", which give you general purpose Python programming facilities within Zope; "External Methods", which are written in Python and stored in the file system; and "Perl-based Scripts" to script Zope objects and access Perl libraries. While "Python Scripts" and "External Methods" already come with Zope, you need to download and install third-party extensions before you can use Perl-based scripts in Zope.

This article is about Python Scripts, Zope Scripts that let you control Zope objects and perform other tasks that have often been done in the past by using complex DTML code.

What are Python Scripts?

Python Scripts are callable objects within the Zope environment. They are built by the Zope product "PythonScript" in /lib/python/Products/. As External Methods, Python Scripts already come with the Zope distribution.

To add a Python Script one either chooses "Script (Python)" from the Product Add list or one calls the constructor method defined in the "Python Script" product.

Python Scripts can be called by any other type of object. There two common ways to call a Python Script: either you call the script directly from another object or you call the script by its URL, which makes use of Zope's "URL Traversal and Acquisition" mechanism.

A Python Script has the following metadata, which can be changed by the developer:

  • Id - Zope id of the script
  • Title - Zope title of the script
  • Parameter List - Function parameters for this script, separated by commas.
  • Bound Names - The names used by the script as bindings to refer to bound elements. The defaults are:
    context - the script's "parent" with respect to acquisition.
    container - the script's "parent" with respect to containment. This is usually the Zope folder the script resides in.
    script - the script object itself.
    traverse_subpath - if the script is called directly from a URL, this is the portion of the URL path following the script's name, split at slash separators into a list of strings. If the script is not called directly from a URL, this will be an empty list.
  • Code - The Python Code itself, which executes when the script is run.

Script instances execute in a restricted context, bound by the user's permission level in Zope, and certain global restrictions on all through-the-web code. Details about restrictions in Python Scripts will be discussed later on.

In addition to the common Python syntax and rules, there are certain extra rules for writing Python code in Python Scripts:

  • variables must not have a leading underscore
  • classes and methods can be defined
  • some Python built-ins are not accessible (e.g. dir(), type())
  • running a Python Script via its “Test” tab or via the URL does not print the value that the Python Script returns, e.g.
    return "Hello world!"
    won’t display "Hello, world". To print "Hello, world" on the screen, one uses:
    print "Hello world!"
    return printed
    

However, Python Scripts are not used for printing text; they are used to process the logic of the Zope application, while templates (ZPTs or DTML) present the layout.

Upload a file into a Python Script

If one uploads a file into a "Script (Python)" instance, the existing settings (or default settings of the Python Script) for bindings, parameters, and title will remain. A file on disk that is used to add a Python Script in Zope could look like this, for example:

	## Script (Python) "getParent"
	##bind container=container
	##bind context=context
	##bind namespace=
	##bind script=script
	##bind subpath=traverse_subpath
	##parameters=
	##title=
	##
	parentObjects = []
	for parent in context.REQUEST.PARENTS[1:]:
		parentObjects.insert(0, parent.getId())
		return "/".join(parentObjects)

The lines following "##" are metadata about the Script (Python) instance which survive a round trip via FTP or through the Web interface. When these lines are encountered by the parser after an upload (or Webform save), they serve to modify the settings of the Script (Python) instance with the metadata contained within the blocked area. Lines beginning with "##" without any spaces after the "##" are contextually meaningful to the file upload parser. There are three keywords which can directly follow "##": "bind", "parameters", and "title".

Using the "bind" keyword after "##" binds a name to an object within the context of this Script (Python) instance. For example, the line "##bind container=MyProjectFolder" binds the name "MyProjectFolder" to the acquisition parent of the script, allowing you to refer to "MyProjectFolder" in the script body. Legal objects which can be bound to are: container, context, namespace, script, and subpath.

The "title" keyword following a "##" defines a title for the script. E.g. "title=This is a Python Script" Using the "parameters" keyword following "##" provides parameters to the Script (Python) instance. E.g. "parameters=x,y,REQUEST".

Importing Modules

The following modules can be imported into Python Scripts:

array, binascii, cPickle, cStringIO, errno, exceptions, imp, marshal, math, md5, new, nt, operator, regex, rotor, sha, signal, strop, struct, sys, thread, time

If you need to import other Python modules, you have to provide extra access to them. See /lib/python/Products/ PythonScripts/README.txt for details.

However, there are some helpful functions which can be imported, too. They are defined in the standard.py module in /lib/python/Products/ PythonScripts/. The standard.py module can be imported by using:

import Products.PythonScripts.standard 

If you wish to import only a few methods from the module, e.g. html_quote, you use

from Products.PythonScripts.standard import html_quote.
Calling Python Scripts via the URL

Assume we add a Python Script "getParents" that returns all the parent objects of a given object:

# Python Script getParents to return an object's parent objects
parentObjects = []
for parent in context.REQUEST.PARENTS:
    parentObjects.insert(0, parent.getId())
return "/".join(parentObjects)

This Python Script can be called by the following URL:

http://localhost:8080/MyProjectFolder/Member01/getParents

This returns: /PythonScripts/MyProjectFolder

Note that the Script "getParents" is in the folder "MyProjectFolder" and "Member01" is an object that, say, stores information about a person (the type of this "Member01" object is not important here). In object-oriented terms, this script is called as a method of the object "Member01". Zope finds the Python Script "getParents" by acquiring it from its parent container which is "MyProjectFolder". In other words: The Python Script "getParents" can be called in the context of any suitable object just by constructing an appropriate URL where the context can be seen as the environment in which the script is executed.

Calling Python Scripts from other objects

As mentioned above, Python Scripts can be called from other objects such as DTML Documents /Methods, Page Templates, or Scripts (Python Scripts or External Methods). The semantics of how a Python Script is called differs from situation to situation (DTML, ZPT or URL) but the acquisition mechanism always works the same way.

Zope User Interface

Assume we have the following short Python Script that removes all underscore characters in a given string and returns the new string.

	Id:			removeUnderscores
	Parameter List:  	mystring = ""

	# removes all underscores from a given string
	newstring = mystring.replace('_', '')
	return newstring

Calling fromCode
DTML<dtml-var “removeUnderscores(mystring=’my_new_string’)“>
ZPT <span tal:content="python:here.removeUnderscores (mystring='my_new_string')"/>
URL /removeUnderscores?mystring=my_new_string

Note that, an mentioned above, calling the Python Script via the URL does not print anything until one replaces the line

return newstring

by the following two lines:

print newstring
return printed
Defining classes and methods within a Python Script

The usual methods can be defined within a Python Script. The following example shows how you can define and call methods within a Python Script.

def add(a,b):
    " Adds two digits "
    result = a + b
    return result

x = add(5,8)
print x
return printed

Although you can define classes within a Python Script, it does not help you very much.

Let’t take a look at a short example where we define a class, a class attribute called ‘interests’ and a class method "getInterests" which returns the interests attribute of a class instance:

class Member:
    interests = ['sports', 'swimming']

    def getInterests(self):
        return self.interests

# create class instance
m = Member()

We can create a class instance ‘m’ which is held in memory, as you can see by using the print statement:

print m
# ""

Also, we can check if m is an instance of the Member class by using:

if isinstance(m, Member):
    print "m is an instance of class Member"
else:
    print "m is not an instance of class Member"

However, the following statements are not allowed within Python Scripts – they produce the error reports shown:

m.age = 30
# --->  Error Type: TypeError
#       Error Value: attribute-less object (assign or del)

print m.interests
# ---> Error Type: Unauthorized
#      Error Value: You are not allowed to access 
#       interests in this context

print m.getInterests()
# ---> Error Type: Unauthorized
#      Error Value: You are not allowed to access 
#       getInterests in this 
#      context

The reason for the error reports is that the class instance doesn't have Zope security information. Furthermore, it is not possible to define a class constructor named __init__ for the basic properties of the class instance because leading underscores are not allowed in Python Scripts.

If you want to build classes and create class instances, you either have to use External Methods or develop a disk-based Zope Product in Python.

Conclusion

Python Scripts are not used to build new object types using different Python modules or 3rd party Zope Products. They are used for the logic of the Web application, while the templates (DTML or ZPTs) are used for the layout. Before Zope Page Templates were developed, Zope developers often mixed logic and layout using DTML. By using Zope Page Templates and Python Scripts you can develop Web applications which have a clean structure in which the layout and the logic are kept separate from each other.


Nico Grubert: Nico Grubert: was born in Germany and grew up in a small village in eastern Germany. In 1997, he began computer science studies. After his University graduation he moved to Berlin to work with beehive. He also co-authored the Zope book "Zope: How to Build and Deliver Web Applications". When Nico's not thinking about Zope you can find him producing and playing electronic music.


shim
shim  ZopeMag is committed to bringing you the best in Zope Documentation. shim
shim


Home   Subscribe   FAQ   Contact   Write for us   Privacy Policy   Weekly News   PyZine   opensourcexperts.com  

Reproduction of material from any of ZopeMag's pages without prior written permission is strictly prohibited. Copyright 2003 - 2005 ZopeMag Zope/Plone hosting by Nidelven IT