summaryrefslogtreecommitdiff
path: root/pypers/meta/meta1.html
diff options
context:
space:
mode:
Diffstat (limited to 'pypers/meta/meta1.html')
-rwxr-xr-xpypers/meta/meta1.html399
1 files changed, 399 insertions, 0 deletions
diff --git a/pypers/meta/meta1.html b/pypers/meta/meta1.html
new file mode 100755
index 0000000..683a2a8
--- /dev/null
+++ b/pypers/meta/meta1.html
@@ -0,0 +1,399 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.3.7: http://docutils.sourceforge.net/" />
+<title></title>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+<body>
+<div class="document">
+<p>Metaclass programming in Python
+Contents:
+Review of object-oriented programming
+A metaprogramming rejoinder
+Metaclasses: a solution looking for a problem?
+Solving problems with magic
+More ways to solve problems with magic
+Meta conveniences
+Resources
+About the authors
+Rate this article
+Related content:
+AspectJ brings AOP to the Java language
+XML Matters: Enforcing validity with the gnosis.xml.validity library
+Guide to Python introspection
+Subscribe to the developerWorks newsletter
+developerWorks Toolbox subscription
+Also in the Linux zone:
+Tutorials
+Tools and products
+Code and components
+Articles
+Pushing object-oriented programming to the next level</p>
+<p>Level: Introductory</p>
+<p>David Mertz, Ph.D. (<a class="reference" href="mailto:mertz&#64;gnosis.cx">mertz&#64;gnosis.cx</a>), Developer, Gnosis Software, Inc.
+Michele Simionato, Ph.D. (<a class="reference" href="mailto:mis6+&#64;pitt.edu">mis6+&#64;pitt.edu</a>), Physicist, University of Pittsburgh</p>
+<p>February 26, 2003
+Most readers are already familiar with the concepts of object-oriented
+programming: inheritance, encapsulation, polymorphism. But the creation
+of objects of a given class, with certain parents, is usually thought of
+as a &quot;just so&quot; operation. It turns out that a number of new programming
+constructs become either easier, or possible at all, when you can
+customize the process of object creation. Metaclasses enable certain
+types of &quot;aspect-oriented programming,&quot; for example, you can enhance
+classes with features like tracing capabilities, object persistence,
+exception logging, and more.</p>
+<p>Review of object-oriented programming
+Let's start with a 30-second review of just what OOP is. In an object-oriented programming language, you can define classes, whose purpose is to bundle together related data and behaviors. These classes can inherit some or all of their qualities from their parents, but they can also define attributes (data) or methods (behaviors) of their own. At the end of the process, classes generally act as templates for the creation of instances (at times also called simply objects). Different instances of the same class will typically have different data, but it will come in the same shape -- for example, the Employee objects bob and jane both have a .salary and a .room_number, but not the same room and salary as each other.</p>
+<p>Some OOP languages, including Python, allow for objects to be introspective (also called reflective). That is, an introspective object is able to describe itself: What class does the instance belong to? What ancestors does that class have? What methods and attributes are available to the object? Introspection lets a function or method that handles objects make decisions based on what kind of object it is passed. Even without introspection, functions frequently branch based on instance data -- for example, the route to jane.room_number differs from that to bob.room_number because they are in different rooms. With introspection, you can also safely calculate the bonus jane gets, while skipping the calculation for bob, for example, because jane has a .profit_share attribute, or because bob is an instance of the subclass Hourly(Employee).</p>
+<p>A metaprogramming rejoinder
+The basic OOP system sketched above is quite powerful. But there is one element brushed over in the description: in Python (and other languages), classes are themselves objects that can be passed around and introspected. Since objects, as stated, are produced using classes as templates, what acts as a template for producing classes? The answer, of course, is metaclasses.</p>
+<p>Python has always had metaclasses. But the machinery involved in metaclasses became much better exposed with Python 2.2. Specifically, with version 2.2, Python stopped being a language with just one special (mostly hidden) metaclass that created every class object. Now programmers can subclass the aboriginal metaclass type and even dynamically generate classes with varying metaclasses. Of course, just because you can manipulate metaclasses in Python 2.2, that does not explain why you might want to.</p>
+<p>Moreover, you do not need to use custom metaclasses to manipulate the production of classes. A slightly less brain-melting concept is a class factory: An ordinary function can return a class that was dynamically created within the function body. In traditional Python syntax, you can write:</p>
+<p>Python 1.5.2 (#0, Jun 27 1999, 11:23:01) [...]
+Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
+&gt;&gt;&gt; def class_with_method(func):
+... class klass: pass
+... setattr(klass, func.__name__, func)
+... return klass
+...
+&gt;&gt;&gt; def say_foo(self): print 'foo'
+...
+&gt;&gt;&gt; Foo = class_with_method(say_foo)
+&gt;&gt;&gt; foo = Foo()
+&gt;&gt;&gt; foo.say_foo()
+foo</p>
+<p>The factory function class_with_method() dynamically creates and returns a class that contains the method/function passed into the factory. The class itself is manipulated within the function body before being returned. The new module provides a more concise spelling, but without the same options for custom code within the body of the class factory, for example:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from new import classobj
+&gt;&gt;&gt; Foo2 = classobj('Foo2',(Foo,),{'bar':lambda self:'bar'})
+&gt;&gt;&gt; Foo2().bar()
+'bar'
+&gt;&gt;&gt; Foo2().say_foo()
+foo
+</pre>
+<p>In all these cases, the behaviors of the class (Foo, Foo2) are not directly written as code, but are instead created by calling functions at runtime, with dynamic arguments. And it should be emphasized that it is not merely the instances that are so dynamically created, but the classes themselves.</p>
+<p>Metaclasses: a solution looking for a problem?
+Metaclasses are deeper magic than 99% of users should ever worry about. If you wonder whether you need them, you don't (the people who actually need them know with certainty that they need them, and don't need an explanation about why). -- Python Guru Tim Peters</p>
+<p>Methods (of classes), like plain functions, can return objects. So in that sense it is obvious that class factories can be classes just as easily as they can be functions. In particular, Python 2.2+ provides a special class called type that is just such a class factory. Of course, readers will recognize type() as a less ambitious built-in function of older Python versions -- fortunately, the behaviors of the old type() function are maintained by the type class (in other words, type(obj) returns the type/class of the object obj). The new type class works as a class factory in just the same way that the function new.classobj long has:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; X = type('X',(),{'foo':lambda self:'foo'})
+&gt;&gt;&gt; X, X().foo()
+(&lt;class '__main__.X'&gt;, 'foo')
+</pre>
+<p>But since type is now a (meta)class, you are free to subclass it:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class ChattyType(type):
+... def __new__(cls, name, bases, dct):
+... print &quot;Allocating memory for class&quot;, name
+... return type.__new__(cls, name, bases, dct)
+... def __init__(cls, name, bases, dct):
+... print &quot;Init'ing (configuring) class&quot;, name
+... super(ChattyType, cls).__init__(name, bases, dct)
+...
+&gt;&gt;&gt; X = ChattyType('X',(),{'foo':lambda self:'foo'})
+Allocating memory for class X
+Init'ing (configuring) class X
+&gt;&gt;&gt; X, X().foo()
+(&lt;class '__main__.X'&gt;, 'foo')
+</pre>
+<p>The magic methods .__new__() and .__init__() are special, but in conceptually the same way they are for any other class. The .__init__() method lets you configure the created object; the .__new__() method lets you customize its allocation. The latter, of course, is not widely used, but exists for every Python 2.2 new-style class (usually inherited but not overridden).</p>
+<p>There is one feature of type descendents to be careful about; it catches everyone who first plays with metaclasses. The first argument to methods is conventionally called cls rather than self, because the methods operate on the produced class, not the metaclass. Actually, there is nothing special about this; all methods attach to their instances, and the instance of a metaclass is a class. A non-special name makes this more obvious:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class Printable(type):
+... def whoami(cls): print &quot;I am a&quot;, cls.__name__
+...
+&gt;&gt;&gt; Foo = Printable('Foo',(),{})
+&gt;&gt;&gt; Foo.whoami()
+I am a Foo
+&gt;&gt;&gt; Printable.whoami()
+Traceback (most recent call last):
+TypeError: unbound method whoami() [...]
+</pre>
+<p>All this surprisingly non-remarkable machinery comes with some syntax sugar that both makes working with metaclasses easier, and confuses new users. There are several elements to the extra syntax. The resolution order of these new variations is tricky though. Classes can inherit metaclasses from their ancestors -- notice that this is not the same thing as having metaclasses as ancestors (another common confusion). For old-style classes, defining a global _metaclass_ variable can force a custom metaclass to be used. But most of the time, and the safest approach, is to set a _metaclass_ class attribute for a class that wants to be created via a custom metaclass. You must set the variable in the class definition itself since the metaclass is not used if the attribute is set later (after the class object has already been created). For example:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; class Bar:
+... __metaclass__ = Printable
+... def foomethod(self): print 'foo'
+...
+&gt;&gt;&gt; Bar.whoami()
+I am a Bar
+&gt;&gt;&gt; Bar().foomethod()
+foo
+</pre>
+<p>Solving problems with magic
+So far, we have seen the basics of metaclasses. But putting metaclasses to work is more subtle. The challenge with utilizing metaclasses is that in typical OOP design, classes do not really do much. The inheritance structure of classes is useful to encapsulate and package data and methods, but it is typically instances that one works with in the concrete.</p>
+<p>There are two general categories of programming tasks where we think metaclasses are genuinely valuable.</p>
+<p>The first, and probably more common category is where you do not know at design time exactly what a class needs to do. Obviously, you will have some idea about it, but some particular detail might depend on information that is not available until later. &quot;Later&quot; itself can be of two sorts: (a) When a library module is used by an application; (b) At runtime when some situation exists. This category is close to what is often called &quot;Aspect-Oriented Programming&quot; (AOP). We'll show what we think is an elegant example:</p>
+<p>% cat dump.py
+#!/usr/bin/python
+import sys
+if len(sys.argv) &gt; 2:</p>
+<div class="system-message">
+<p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">meta1.txt</tt>, line 153)</p>
+Unexpected indentation.</div>
+<blockquote>
+module, metaklass = sys.argv[1:3]
+m = __import__(module, globals(), locals(), [metaklass])
+__metaclass__ = getattr(m, metaklass)</blockquote>
+<dl class="docutils">
+<dt>class Data:</dt>
+<dd><dl class="first docutils">
+<dt>def __init__(self):</dt>
+<dd>self.num = 38
+self.lst = ['a','b','c']
+self.str = 'spam'</dd>
+</dl>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 162)</p>
+Definition list ends without a blank line; unexpected unindent.</div>
+<p class="last">dumps = lambda self: <cite>self</cite>
+__str__ = lambda self: self.dumps()</p>
+</dd>
+</dl>
+<p>data = Data()
+print data</p>
+<p>% dump.py
+&lt;__main__.Data instance at 1686a0&gt;</p>
+<p>As you would expect, this application prints out a rather generic description of the data object (a conventional instance object). But if runtime arguments are passed to the application, we can get a rather different result:</p>
+<p>% dump.py gnosis.magic MetaXMLPickler
+&lt;?xml version=&quot;1.0&quot;?&gt;
+&lt;!DOCTYPE PyObject SYSTEM &quot;PyObjects.dtd&quot;&gt;
+&lt;PyObject module=&quot;__main__&quot; class=&quot;Data&quot; id=&quot;720748&quot;&gt;
+&lt;attr name=&quot;lst&quot; type=&quot;list&quot; id=&quot;980012&quot; &gt;</p>
+<div class="system-message">
+<p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">meta1.txt</tt>, line 180)</p>
+Unexpected indentation.</div>
+<blockquote>
+&lt;item type=&quot;string&quot; value=&quot;a&quot; /&gt;
+&lt;item type=&quot;string&quot; value=&quot;b&quot; /&gt;
+&lt;item type=&quot;string&quot; value=&quot;c&quot; /&gt;</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 183)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>&lt;/attr&gt;
+&lt;attr name=&quot;num&quot; type=&quot;numeric&quot; value=&quot;38&quot; /&gt;
+&lt;attr name=&quot;str&quot; type=&quot;string&quot; value=&quot;spam&quot; /&gt;
+&lt;/PyObject&gt;</p>
+<p>The particular example uses the serialization style of gnosis.xml.pickle, but the most current gnosis.magic package also contains metaclass serializers MetaYamlDump, MetaPyPickler, and MetaPrettyPrint. Moreover, a user of the dump.py &quot;application&quot; can impose the use of any &quot;MetaPickler&quot; desired, from any Python package that defines one. Writing an appropriate metaclass for this purpose will look something like:</p>
+<dl class="docutils">
+<dt>class MetaPickler(type):</dt>
+<dd><p class="first">&quot;Metaclass for gnosis.xml.pickle serialization&quot;
+def __init__(cls, name, bases, dict):</p>
+<div class="system-message">
+<p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">meta1.txt</tt>, line 195)</p>
+Unexpected indentation.</div>
+<blockquote class="last">
+from gnosis.xml.pickle import dumps
+super(MetaPickler, cls).__init__(name, bases, dict)
+setattr(cls, 'dumps', dumps)</blockquote>
+</dd>
+</dl>
+<p>The remarkable achievement of this arrangement is that the application programmer need have no knowledge about what serialization will be used -- nor even whether serialization or some other cross-sectional capability will be added at the command-line.</p>
+<p>Perhaps the most common use of metaclasses is similar to that of MetaPicklers: adding, deleting, renaming, or substituting methods for those defined in the produced class. In our example, a &quot;native&quot; Data.dump() method is replaced by a different one from outside the application, at the time the class Data is created (and therefore in every subsequent instance).</p>
+<p>More ways to solve problems with magic
+There is a programming niche where classes are often more important than instances. For example, declarative mini-languages are Python libraries whose program logic is expressed directly in class declarations. David examines them in his article &quot;Create declarative mini-languages&quot;. In such cases, using metaclasses to affect the process of class creation can be quite powerful.</p>
+<p>One class-based declarative framework is gnosis.xml.validity. Under this framework, you declare a number of &quot;validity classes&quot; that express a set of constraints about valid XML documents. These declarations are very close to those contained in DTDs. For example, a &quot;dissertation&quot; document can be configured with the code:</p>
+<p>from gnosis.xml.validity import *
+class figure(EMPTY): pass
+class _mixedpara(Or): _disjoins = (PCDATA, figure)
+class paragraph(Some): _type = _mixedpara
+class title(PCDATA): pass
+class _paras(Some): _type = paragraph
+class chapter(Seq): _order = (title, _paras)
+class dissertation(Some): _type = chapter</p>
+<p>If you try to instantiate the dissertation class without the right component subelements, a descriptive exception is raised; likewise for each of the subelements. The proper subelements will be generated from simpler arguments when there is only one unambiguous way of &quot;lifting&quot; the arguments to the correct types.</p>
+<p>Even though validity classes are often (informally) based on a pre-existing DTD, instances of these classes print themselves as unadorned XML document fragments, for example:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from simple_diss import *
+&gt;&gt;&gt; ch = LiftSeq(chapter, ('It Starts','When it began'))
+&gt;&gt;&gt; print ch
+&lt;chapter&gt;&lt;title&gt;It Starts&lt;/title&gt;
+&lt;paragraph&gt;When it began&lt;/paragraph&gt;
+&lt;/chapter&gt;
+</pre>
+<p>By using a metaclass to create the validity classes, we can generate a DTD out of the class declarations themselves (and add an extra method to the classes while we do it):</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; from gnosis.magic import DTDGenerator, \
+... import_with_metaclass, \
+... from_import
+&gt;&gt;&gt; d = import_with_metaclass('simple_diss',DTDGenerator)
+&gt;&gt;&gt; from_import(d,'**')
+&gt;&gt;&gt; ch = LiftSeq(chapter, ('It Starts','When it began'))
+&gt;&gt;&gt; print ch.with_internal_subset()
+&lt;?xml version='1.0'?&gt;
+&lt;!DOCTYPE chapter [
+&lt;!ELEMENT figure EMPTY&gt;
+&lt;!ELEMENT dissertation (chapter)+&gt;
+&lt;!ELEMENT chapter (title,paragraph+)&gt;
+&lt;!ELEMENT title (#PCDATA)&gt;
+&lt;!ELEMENT paragraph ((#PCDATA|figure))+&gt;
+]&gt;
+&lt;chapter&gt;&lt;title&gt;It Starts&lt;/title&gt;
+&lt;paragraph&gt;When it began&lt;/paragraph&gt;
+&lt;/chapter&gt;
+</pre>
+<p>The package gnosis.xml.validity knows nothing about DTDs and internal subsets. Those concepts and capabilities are introduced entirely by the metaclass DTDGenerator, without any change made to either gnosis.xml.validity or simple_diss.py. DTDGenerator does not substitute its own .__str__() method into classes it produces -- you can still print the unadorned XML fragment -- but it a metaclass could easily modify such magic methods.</p>
+<p>Meta conveniences
+The package gnosis.magic contains several utilities for working with
+metaclasses, as well as some sample metaclasses you can use in
+aspect-oriented programming. The most important of these utilities
+is import_with_metaclass(). This function, utilized in the above
+example, lets you import a third-party module, but create all the
+module classes using a custom metaclass rather than type. Whatever
+new capability you might want to impose on that third-party module
+can be defined in a metaclass that you create (or get from somewhere
+else altogether). gnosis.magic contains some pluggable serialization
+metaclasses; some other package might contain tracing capabilities,
+or object persistence, or exception logging, or something else.</p>
+<p>The import_with_metclass() function illustrates several qualities
+of metaclass programming:</p>
+<dl class="docutils">
+<dt>def import_with_metaclass(modname, metaklass):</dt>
+<dd><p class="first">&quot;Module importer substituting custom metaclass&quot;
+class Meta(object): __metaclass__ = metaklass
+dct = {'__module__':modname}
+mod = __import__(modname)
+for key, val in mod.__dict__.items():</p>
+<div class="system-message">
+<p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">meta1.txt</tt>, line 281)</p>
+Unexpected indentation.</div>
+<blockquote>
+<dl class="docutils">
+<dt>if inspect.isclass(val):</dt>
+<dd>setattr(mod, key, type(key,(val,Meta),dct))</dd>
+</dl>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 283)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p class="last">return mod</p>
+</dd>
+</dl>
+<p>One notable style in this function is that an ordinary class Meta is
+produced using the specified metaclass. But once Meta is added as an
+ancestor, its descendent is also produced using the custom metaclass.
+In principle, a class like Meta could carry with it both a metaclass
+producer and a set of inheritable methods -- the two aspects of its
+bequest are orthogonal.</p>
+<p>Resources</p>
+<blockquote>
+<ul class="simple">
+<li>A useful book on metaclasses is Putting Metaclasses to Work by</li>
+</ul>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 295)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>Ira R. Forman and Scott Danforth (Addison-Wesley; 1999).</p>
+<blockquote>
+<ul class="simple">
+<li>For metaclasses in Python specifically, Guido van Rossum's</li>
+</ul>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 298)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>essay, &quot;Unifying types and classes in Python 2.2&quot; is useful as well.</p>
+<blockquote>
+<ul>
+<li><dl class="first docutils">
+<dt>Also by David on developerWorks, read:</dt>
+<dd><p class="first last">o &quot;Guide to Python introspection&quot;
+o &quot;Create declarative mini-languages&quot;
+o &quot;XML Matters: Enforcing validity with the gnosis.xml.</p>
+</dd>
+</dl>
+</li>
+</ul>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 304)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>validity library&quot;</p>
+<blockquote>
+<ul class="simple">
+<li>Don't know Tim Peters? You should! Begin with Tim's wiki page</li>
+</ul>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 308)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>and end with reading <a class="reference" href="news:comp.lang.python">news:comp.lang.python</a> more regularly.</p>
+<blockquote>
+<ul class="simple">
+<li>New to AOP? You may find this &quot;Introduction to Aspect-Oriented</li>
+</ul>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 311)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>Programming&quot; (PDF) by Ken Wing Kuen Lee of the Hong Kong University
+of Science and Technology interesting.</p>
+<blockquote>
+<ul class="simple">
+<li>Gregor Kiczales and his team at Xerox PARC coined the term</li>
+</ul>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 315)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>aspect-oriented programming in the 1990s and championed it as a
+way to allow software programmers to spend more time writing code
+and less time correcting it.</p>
+<blockquote>
+<ul class="simple">
+<li>&quot;Connections between Demeter/Adaptive Programming and</li>
+</ul>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 320)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>Aspect-Oriented Programming (AOP)&quot; by Karl J. Lieberherr also
+describes AOP.</p>
+<blockquote>
+<ul class="simple">
+<li>You'll also find subject-oriented programming interesting. As</li>
+</ul>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 324)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>described by the folks at IBM Research, it's essentially the same
+thing as aspect-oriented programming.</p>
+<blockquote>
+<ul class="simple">
+<li>Find and download the Gnosis utils, mentioned several times in</li>
+</ul>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 328)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>this article, at David's site.</p>
+<blockquote>
+<ul class="simple">
+<li>Find more resources for Linux developers in the developerWorks</li>
+</ul>
+</blockquote>
+<div class="system-message">
+<p class="system-message-title">System Message: WARNING/2 (<tt class="docutils">meta1.txt</tt>, line 331)</p>
+Block quote ends without a blank line; unexpected unindent.</div>
+<p>Linux zone.</p>
+<p>About the authors
+David Mertz David Mertz thought his brain would melt when he wrote
+about continuations or semi-coroutines, but he put the gooey mess
+back in his skull cavity and moved on to metaclasses. David may be
+reached at <a class="reference" href="mailto:mertz&#64;gnosis.cx">mertz&#64;gnosis.cx</a>; his life pored over at his personal
+Web page. Suggestions and recommendations on this, past, or future
+columns are welcome. Learn about his forthcoming book, Text
+Processing in Python.</p>
+<p>Michele Simionato Michele Simionato is a plain, ordinary, theoretical
+physicist who was driven to Python by a quantum fluctuation that could
+well have passed without consequences had he not met David Mertz. He
+will let his readers judge the final outcome. Michele can be reached
+at <a class="reference" href="mailto:mis6+&#64;pitt.edu">mis6+&#64;pitt.edu</a>.</p>
+</div>
+</body>
+</html>