There are several reasons why you might want to extend Zope with C. Most likely you have a ready-made C library that can help you do something, but you're not interested in converting it to Python. In addition, because Python is an interpretive language, any Python code that is invoked in large numbers will slow you down. So even if you've already written some extensions in Python, you'll still want to consider using the most commonly invoked section instead of C to write. Either way, the extended Zope starts with extended Python. In addition, extending Python will give you other benefits because your code will be accessible from any Python script, not just from Zope. The only thing to note here is that Python's current version is 2.1 when writing this article, but Zope can still only run with Python 1.5.21. For C extensions, two versions don't change, but if you're interested in Python packaging your library and want them to work under Zope, you should be careful not to use anything newer than 1.5.2.
What is Zope?
Zope represents the "Z Object Publishing Environment (Z-Objects Publishing Environment)", which is an application server implemented in Python. "That's great," you said, "But what exactly does the application server mean?" "An application server is a long-running process that provides services for active content." The WEB server invokes the application server during run time to build the page.
extended Python: Interesting and rewarding
To extend Zope, you first need to extend Python. Although extended Python is not as complicated as "brain surgery", it is not as laid-back as "walking in a park". There are two basic components for Python extensions. The first one is obviously C code. I will discuss it at once. The other part is the installation file. The installation file describes the module by providing the module name, the location of the module's C code, and any compiler flags you may need. The file is preprocessed to create makefile (on UNIX) or msvc++ Engineering files (msvc++ project file, on Windows). First of all, the Python on ―windows is actually compiled with the Microsoft compiler. python.org people also recommend using msvc++ to compile extensions. Obviously, you should be able to convince the GNU compilers, but I haven't tried them myself.
Anyway, let's define a module called ' foo '. The ' foo ' module will have a function called ' bar '. When we want to use, we can use import foo; To import this function into a Python script, just like importing any module. The installation file is simple:
Listing 1. A Typical installation file
# You can include comment lines. The *shared* directive indicates
# that's following module (s) are to is compiled and linked for
# dynamic Loadin G as opposed to static:. So on Unix,. dll on Windows.
*shared*
# Then can use the variables later using the $ (variable) syntax
# this ' make ' uses. This next line defines we module and tells
# Python where its source code is.
Foo FOOMAIN.C
Writing code
So how do we actually write code that Python knows how to use, you ask? FOOMAIN.C (You can name it, of course) the file contains three items: A method table, an initialization function, and the rest of the code. The method table simply links the function name to the function, and tells the parameters passing mechanism used by the Python functions (you can choose to use a generic list of positional parameters or a mixture of positional and keyword parameters). Python invokes the initialization function when the module is loaded. The initialization function completes all the initialization required by the module, but more importantly, it returns a pointer to the method table to Python.
So let's take a look at the C code for our small foo module.
Listing 2. A typical Python extension module
#include <Python.h>
/* Define the Method table. *
/static Pyobject *foo_bar (Pyobject *self, Pyobject *args) ;
Static Pymethoddef foomethods[] = {
{"bar", Foo_bar, Meth_varargs},
{null, NULL}}
;
/* here ' s the initialization function. We don ' t need to doing anything
for our own needs, but Python needs this method table. *
/void Initfoo ()
{
(void) Py_initmodule ("foo", Foomethods);
}
/* Finally, let's do something ... involved ... as an example function. *
/static Pyobject *foo_bar (Pyobject *self, Pyobject *args)
{
char *string;
int Len;
if (! Pyarg_parsetuple (args, "s", &string)) return
NULL;
Len = strlen (string);
Return Py_buildvalue ("I", Len);
}
In-depth study
Let's take a look at the code. First, please note that you must include Python.h. Unless you have set the path to the file in the include path (include path), you may need to include the-I flag in the installation file to point to the file.
The initialization function must be named Init < module name, which is initfoo in our example. The name of the initialization function is, without a doubt, all the information that Python knows about the module as it loads the module, which is why the name of the initialization function is so inflexible. By the way, the initialization function must be the only global identifier in the file that is not declared static. This is more important for static links than for dynamic links, because non-static identifiers will be globally visible. This is not a big problem for dynamic links, but if you're going to link everything during compilation and not declare everything that can be static as static, you're likely to encounter a name conflict problem.
Now let's look at the actual code to see how the parameters are processed and how the return value is passed. Of course, everything is an object on the Pyobject―python heap. What you get from the argument is a reference to the "This" object (this is for the object method, NULL for an old-fashioned function like bar (), and a parameter tuple stored in args). You use Pyarg_parsetuple to retrieve your parameters, and then use Py_buildvalue to pass back the results. These functions (and more) are archived in the "python/c API" section of the Python document. Unfortunately, there are no simple lists of functions by name, and documents are arranged by topic.
Also note that the function returns NULL in the event of an error. Returning NULL indicates an error; If you want to make Python better, you should throw an exception. I'll show you the documentation on how to do it.
Compiling extensions
Now all that remains is the compilation module. You can do this in two different ways. The first is to run Make-f Makefile.pre.in boot in accordance with the instructions in the documentation, which will compile a Makefile using your Setup. Then you use the Makefile to compile your project. This approach applies only to UNIX. For Windows, there is a script called "compile.py" (see Resources later in this article). The original script is hard to find; I've got a lot of changed copies from a mailing list from Robin Dunn, the wxPython behind the scenes. This script works on UNIX and Windows, and on Windows it will compile Msvc++ engineering files from your Setup.
To compile, you must make the included files and libraries available. Python's standard Zope installation does not contain these files, so you will need to install the Python general installation from www.python.org (see Resources). On Windows, you must also get the Config.h file from the PC directory where the source code is installed; It is a manual version of the config.h that UNIX installed for you to compile. So, on UNIX, you should already have it.
Once this is done, you will get a file with ". PYD" as the extension. Put this file in the "Lib" directory under the Python installation directory (under Zope, Python is in the "bin" directory, so your extension ends up in the "Bin/lib" directory, which is odd.) And then you can call it, just as you would call any source-born Python module.
>>> import foo;
>>> Foo.bar ("This is a Test");
14
When I do this, my first question is asking myself how to define a class that is visible from Python in C. In fact, I may have asked a wrong question. In the example I've studied, everything that is specific to Python is done in Python only, and only the C functions exported from your extension are invoked.
Take it to Zope.
Once your Python extension is complete, the next step is to make Zope work with it. There are several ways you can choose, but to some extent, how you want your extension to work with Zope will first affect how you compile the extensions. The basic way to use Python (and extensions with C) from within Zope is:
- If the function is simple, you can think of it as a variable. These are called "external methods."
- A more complex class that can be invoked from the Zope script (this is a new feature of Zope 2.3).
- You can define a Zope Product and then extend it with Zclass (a set of done, Web-accessible objects), use it in a script, and publish it based on its own permissions (its instance is treated as a page).
Of course, your own applications can use combinations of these methods.
Create an external method
The easiest way to invoke Python from Zope is to make your Python code an external method. An external method is a Python function that is placed in the "Extensions" directory under the Zope installation directory. Once there is such a Python file, you can go to any folder, select Add External method, and add the variables that call the function you want to use. You can then add the DTML field to any page in the folder that displays the result of the call. Let's look at a simple example that uses the Python extension ―foo.bar― defined above.
First, look at the extension itself: we put it in a file called Foo.pyd. Remember, this file is located under Zope in the Extensions directory. In order to proceed smoothly, of course, the foo.pyd we created above must be in the Python library in Bin/lib. A simple package for that purpose might look like this:
Listing 3. A simple external method (file: extensions/foo.py)
Import foo
def bar (self,arg): "" "
A Simple external method.
" " Return ' arg length:%d '% foo.bar (ARG)
It's simple, isn't it? It defines an external method "bar" that can be attached to any folder using the Zope admin interface. To invoke our extensions from any page in the folder, we simply insert a DTML variable reference, as follows:
<dtml-var Bar (' is a test ') >
When the user views our page, the DTML field is replaced by the text "Arg length:14". This is how we extend Zope with C.
Zope script: Cliff Notes Edition
The Zope script is a new feature of Python 2.3 that you want to use instead of an external method. It can be done by external methods, it can be better integrated with security and management systems, provides more flexibility in integration, and there is a lot of access to all of the Zope features exposed in the Zope API.
A script is basically a short Python program. It can define a class or function, but it is not required. It is installed as an object in the Zope folder and can then be invoked as a DTML variable or call (as an external method) or "from the Web" (meaning in Zope that it will be invoked as a page). Of course, this means that a script can generate a response to a form submission like a CGI program, but without CGI overhead. It's really a great feature. In addition, the script has access to the callee or caller object (through the "context" object), the folder where the object is located (through the "container" object), and some other bits and pieces of information. For more information on scripting, see the chapter "Advanced Zope scripting (Advanced zope scripting)" in the Zope Manual (see Resources).
You may mistakenly think that you can simply import foo and use Foo.bar directly from the script (I know I did make this mistake). But that is not the case. Because of security restrictions, only Product can be imported, not any module. In general, the Zope designers believe that any scripting requires access to the file system, and since the script objects are managed by the WEB using the Zope management interface, they are not completely trusted. So I'm going to stop and not show you the sample script, but to discuss Product and base classes.
Focus on Product
Product is a powerful tool for expanding Zope. From the level of the installation directory, Product is a directory in the "lib/python/products" directory located in the Zope directory. In your own Zope installation directory, you can see many product examples, but in essence, the smallest product consists of only two files located in the directory: an arbitrarily named code file and a Zope at startup Si Cho used to initialize the product called __init__. The file for PY. (Note: Zope only reads the Product file at startup, which means that you must be able to stop and restart the Zope process for testing purposes.) This article is just trying to provide you with tips on what you can do with Zope Product.
What you need to know is that Product encapsulates one or more classes that can be used from Zclass, script, or directly from a URL on the Web. (Of course, in the last case, an instance of Product is treated as a folder; The last part of the URL specifies the method to be invoked, which returns any HTML.) You do not have to treat Product as an "additive" object, although this is its primary purpose. To see an excellent, realistic example, you can see the Zcatalog implementation, which is part of the standard Zope distribution. There you can see a very simple setup script in __init__.py, and you can see the Zcatalog class in zcatalog.py, which provides a number of publishing methods. Note that Zope uses a strange convention to determine which methods can be accessed through the web--if a method contains a doc string, the method is accessible through the web; otherwise, it is considered private.
Anyway, let's look at a very simple Product that uses the C module (which we defined on it). First look at the very simple __init__.py; Notice that it does just one thing, which tells Zope the name of the class we are installing. More complex initialization scripts can do more, including declaring global variables maintained by the server, setting access rights, and so on. For more details, please refer to the Zope Developer's Guide in your online documentation, as well as the off-the-shelf Product in your Zope installation directory. As you may have guessed, our sample Product is called "Foo". This will create an Foo subdirectory under the Lib/python/products directory.
Listing 4. Basic Product Initialization Scripts
Import foo
def initialize (context):
Context.registerclass (
foo.foo,
permission= ' Add foo ',
Constructors=foo.manage_addfoo
)
Now notice that this initialization script not only imports that class, makes it accessible to other parts of Zope, but also registers the class as "additive." The Context.registerclass call accomplishes this by first naming the class that we imported, and then specifying the name of the method that can be used to add the instance (this method must display an admin page that will automatically integrate with the Zope management interface). Cool.
Let's summarize this short, simple Product. It exposes our Foo.bar functions to scripts and zclass, and there is a small interface as an "additive" object, which is all.
Listing 5. A simple Zope Product
Import foo
class Foo (simpleitem.item):
"A foo Product"
meta_type = ' foo '
def bar (self, string):
return Foo.bar (String)
def __init__ (self, id):
"Initialize A instance"
self.id = ID
def index_html ( Self):
"Basic view of Object" Return
' me
ID is%s
'% (self.id, Foo.bar (self.id )
def manage_addfoo (self, RESPONSE):
"Management handler to add is instance to a folder."
Self._setobject (' foo_id ', Foo (' foo_id '))
Response.Redirect (' index_html ')
This is just one of the simplest Product. It cannot be absolutely said to be the smallest of the possible Product, but it is already very close. However, it does illustrate some of the key features of Product. First, notice the "index_html" method: It is called to display an object instance, which is done by building HTML. It's actually a page. The Manage_addfoo method is an interface for Zope object Management, which we reference in the __init__.py above. The "__init__" method initializes the object; all it has to do is to record the unique identifier of the instance.
This miniature Product does not interoperate with Zope security. It doesn't do much management work. It has no interactive functionality. So you can add a lot of things to it (and it doesn't even have a very useful feature). I hope this is a good start for you.
What to do in the future
A brief introduction to Zope Product has taught you how to convert the C-language function from C code to Zope. To learn how to write product, you also need to read more documents (many of which are still being perfected), and frankly, look at the existing product and see how they do it. The Zope model has great functionality and flexibility, and they are well worth exploring.
I'm currently doing a big project integrating C and Zope: Integrating my Workflow Toolkit (Workflow Toolkit). Before the publication of this article, I hope to see its embryonic form. It has been listed in the resources below for a look at it, and you should have been able to find a sample extension from it when you read this article. Wish me luck.