Commit f0860413 authored by Jim Fulton's avatar Jim Fulton

Removed previous ExtensionClass implementation.

The new implementation is provided as a collection of individual
packages, corresponding to the original extension modules.
parent 894c7cc6
Zope Public License (ZPL) Version 2.0
-----------------------------------------------
This software is Copyright (c) Zope Corporation (tm) and
Contributors. All rights reserved.
This license has been certified as open source. It has also
been designated as GPL compatible by the Free Software
Foundation (FSF).
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the
following conditions are met:
1. Redistributions in source code must retain the above
copyright notice, this list of conditions, and the following
disclaimer.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions, and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
3. The name Zope Corporation (tm) must not be used to
endorse or promote products derived from this software
without prior written permission from Zope Corporation.
4. The right to distribute this software or to use it for
any purpose does not give you the right to use Servicemarks
(sm) or Trademarks (tm) of Zope Corporation. Use of them is
covered in a separate agreement (see
http://www.zope.com/Marks).
5. If any files are modified, you must cause the modified
files to carry prominent notices stating that you changed
the files and the date of any change.
Disclaimer
THIS SOFTWARE IS PROVIDED BY ZOPE CORPORATION ``AS IS''
AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
NO EVENT SHALL ZOPE CORPORATION OR ITS CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
This software consists of contributions made by Zope
Corporation and many individuals on behalf of Zope
Corporation. Specific attributions are listed in the
accompanying credits file.
See the file doc/ExtensionClass.stx or doc/ExtensionClass.html for a
description of extension class.
This version of ExtensionClass uses Python distutils for installation.
It requires Python version 1.5.2 or higher. To build, test, and
install, do the following:
% python setup.py build
% PYTHONPATH=build/lib-PLAT-VER python test/regrtest.py
% python setup.py install
The build directory is created setup.py during the build. The exact
name of the build subdirectory depends on the platform you are using
and the version of Python.
For more information, contact info@zope.com
# Compatibility module
# $Id: Xaq.py,v 1.1 1997/06/19 19:26:30 jim Exp $
import Acquisition
Acquirer = Acquisition.Explicit
This diff is collapsed.
Acquisition
"Copyright (C) 1996-1998, Digital Creations":COPYRIGHT.html.
Acquisition [1] is a mechanism that allows objects to obtain
attributes from their environment. It is similar to inheritence,
except that, rather than traversing an inheritence hierarchy
to obtain attributes, a containment hierarchy is traversed.
The "ExtensionClass":ExtensionClass.html. release includes mix-in
extension base classes that can be used to add acquisition as a
feature to extension subclasses. These mix-in classes use the
context-wrapping feature of ExtensionClasses to implement
acquisition. Consider the following example::
import ExtensionClass, Acquisition
class C(ExtensionClass.Base):
color='red'
class A(Acquisition.Implicit):
def report(self):
print self.color
a=A()
c=C()
c.a=A()
c.a.report() # prints 'red'
d=C()
d.color='green'
d.a=a
d.a.report() # prints 'green'
a.report() # raises an attribute error
The class 'A' inherits acquisition behavior from
'Acquisition.Implicit'. The object, 'a', "has" the color of
objects 'c' and 'd' when it is accessed through them, but it
has no color by itself. The object 'a' obtains attributes
from it's environment, where it's environment is defined by
the access path used to reach 'a'.
Acquisition wrappers
When an object that supports acquisition is accessed through
an extension class instance, a special object, called an
acquisition wrapper, is returned. In the example above, the
expression 'c.a' returns an acquisition wrapper that
contains references to both 'c' and 'a'. It is this wrapper
that performs attribute lookup in 'c' when an attribute
cannot be found in 'a'.
Aquisition wrappers provide access to the wrapped objects
through the attributes 'aq_parent', 'aq_self', 'aq_base'.
In the example above, the expressions::
'c.a.aq_parent is c'
and::
'c.a.aq_self is a'
both evaluate to true, but the expression::
'c.a is a'
evaluates to false, because the expression 'c.a' evaluates
to an acquisition wrapper around 'c' and 'a', not 'a' itself.
The attribute 'aq_base' is similar to 'aq_self'. Wrappers may be
nested and 'aq_self' may be a wrapped object. The 'aq_base'
attribute is the underlying object with all wrappers removed.
Acquisition Control
Two styles of acquisition are supported in the current
ExtensionClass release, implicit and explicit aquisition.
Implicit acquisition
Implicit acquisition is so named because it searches for
attributes from the environment automatically whenever an
attribute cannot be obtained directly from an object or
through inheritence.
An attribute may be implicitly acquired if it's name does
not begin with an underscore, '_'.
To support implicit acquisition, an object should inherit
from the mix-in class 'Acquisition.Implicit'.
Explicit Acquisition
When explicit acquisition is used, attributes are not
automatically obtained from the environment. Instead, the
method 'aq_aquire' must be used, as in::
print c.a.aq_acquire('color')
To support explicit acquisition, an object should inherit
from the mix-in class 'Acquisition.Explicit'.
Controlled Acquisition
A class (or instance) can provide attribute by attribute control
over acquisition. This is done by:
- subclassing from 'Acquisition.Explicit', and
- setting all attributes that should be acquired to the special
value: 'Acquisition.Acquired'. Setting an attribute to this
value also allows inherited attributes to be overridden with
acquired ones.
For example, in::
class C(Acquisition.Explicit):
id=1
secret=2
color=Acquisition.Acquired
__roles__=Acquisition.Acquired
The *only* attributes that are automatically acquired from
containing objects are 'color', and '__roles__'. Note also
that the '__roles__' attribute is acquired even though it's
name begins with an underscore. In fact, the special
'Acquisition.Acquired' value can be used in
'Acquisition.Implicit' objects to implicitly acquire selected
objects that smell like private objects.
Filtered Acquisition
The acquisition method, 'aq_acquire', accepts two optional
arguments. The first of the additional arguments is a
"filtering" function that is used when considering whether to
acquire an object. The second of the additional arguments is an
object that is passed as extra data when calling the filtering
function and which defaults to 'None'.
The filter function is called with five arguments:
- The object that the 'aq_acquire' method was called on,
- The object where an object was found,
- The name of the object, as passed to 'aq_acquire',
- The object found, and
- The extra data passed to 'aq_acquire'.
If the filter returns a true object that the object found is
returned, otherwise, the acquisition search continues.
For example, in::
from Acquisition import Explicit
class HandyForTesting:
def __init__(self, name): self.name=name
def __str__(self):
return "%s(%s)" % (self.name, self.__class__.__name__)
__repr__=__str__
class E(Explicit, HandyForTesting): pass
class Nice(HandyForTesting):
isNice=1
def __str__(self):
return HandyForTesting.__str__(self)+' and I am nice!'
__repr__=__str__
a=E('a')
a.b=E('b')
a.b.c=E('c')
a.p=Nice('spam')
a.b.p=E('p')
def find_nice(self, ancestor, name, object, extra):
return hasattr(object,'isNice') and object.isNice
print a.b.c.aq_acquire('p', find_nice)
The filtered acquisition in the last line skips over the first
attribute it finds with the name 'p', because the attribute
doesn't satisfy the condition given in the filter. The output of
the last line is::
spam(Nice) and I am nice!
Acquisition and methods
Python methods of objects that support acquisition can use
acquired attributes as in the 'report' method of the first example
above. When a Python method is called on an object that is
wrapped by an acquisition wrapper, the wrapper is passed to the
method as the first argument. This rule also applies to
user-defined method types and to C methods defined in pure mix-in
classes.
Unfortunately, C methods defined in extension base classes that
define their own data structures, cannot use aquired attributes at
this time. This is because wrapper objects do not conform to the
data structures expected by these methods.
Acquiring Acquiring objects
Consider the following example::
from Acquisition import Implicit
class C(Implicit):
def __init__(self, name): self.name=name
def __str__(self):
return "%s(%s)" % (self.name, self.__class__.__name__)
__repr__=__str__
a=C("a")
a.b=C("b")
a.b.pref="spam"
a.b.c=C("c")
a.b.c.color="red"
a.b.c.pref="eggs"
a.x=C("x")
o=a.b.c.x
The expression 'o.color' might be expected to return '"red"'. In
earlier versions of ExtensionClass, however, this expression
failed. Acquired acquiring objects did not acquire from the
environment they were accessed in, because objects were only
wrapped when they were first found, and were not rewrapped as they
were passed down the acquisition tree.
In the current release of ExtensionClass, the expression "o.color"
does indeed return '"red"'.
When searching for an attribute in 'o', objects are searched in
the order 'x', 'a', 'b', 'c'. So, for example, the expression,
'o.pref' returns '"spam"', not '"eggs"'. In earlier releases of
ExtensionClass, the attempt to get the 'pref' attribute from 'o'
would have failed.
If desired, the current rules for looking up attributes in complex
expressions can best be understood through repeated application of
the '__of__' method:
'a.x' -- 'x.__of__(a)'
'a.b' -- 'b.__of__(a)'
'a.b.x' -- 'x.__of__(a).__of__(b.__of__(a))'
'a.b.c' -- 'c.__of__(b.__of__(a))'
'a.b.c.x' --
'x.__of__(a).__of__(b.__of__(a)).__of__(c.__of__(b.__of__(a)))'
and by keeping in mind that attribute lookup in a wrapper
is done by trying to lookup the attribute in the wrapped object
first and then in the parent object. In the expressions above
involving the '__of__' method, lookup proceeds from left to right.
Note that heuristics are used to avoid most of the repeated
lookups. For example, in the expression: 'a.b.c.x.foo', the object
'a' is searched no more than once, even though it is wrapped three
times.
.. [1] Gil, J., Lorenz, D.,
"Environmental Acquisition--A New Inheritance-Like Abstraction Mechanism",
http://www.bell-labs.com/people/cope/oopsla/Oopsla96TechnicalProgramAbstracts.html#GilLorenz,
OOPSLA '96 Proceedings, ACM SIG-PLAN, October, 1996
This diff is collapsed.
This diff is collapsed.
Installation
"Copyright (C) 1996-1998, Digital Creations":COPYRIGHT.html.
The ExtensionClass distribution now uses the "Universal Unix
Makefile for Python extensions", 'Makefile.pre.in', which was
introduced as part of Python1.4. Copies of this file for Python
1.4 and Python 1.5 are included with this release. See the
instructions in the make file, itself. Note that you will need to
copy or rename the the file for the Python version you're using to
Makefile.pre.in.
Files
ExtensionClass.stx -- This file in structured text format
ExtensionClass.html -- This file in HTML format
Installation -- Installation instructions in structured text
format.
Installation.html -- Installation instructions in HTML
format.
Acquisition.stx -- Acquisition documentation in structured text
format.
Acquisition.html -- Acquisition documentation in HTML
format.
MultiMapping.stx -- The MultiMapping example in structured text
format.
MultiMapping.html -- The MultiMapping example in structured text
format.
release.notes -- Release notes in structured text
format.
release.html -- Release notes in HTML format.
README -- A file that says to read this file.
Makefile.pre.in-1.4 -- The Universal Unix Makefile for Python
extensions. This is the Python 1.4
version. Copy this to Makefile.pre.in
before using it.
Makefile.pre.in-1.5 -- The Universal Unix Makefile for Python
extensions. This is the Python 1.5
version. Copy this to Makefile.pre.in
before using it.
Setup -- a configuration file used by the Universal
Unix Makefile for Python extensions
ExtensionClass.c -- The ExtensionClass source
ExtensionClass.h -- The ExtensionClass header file
Acquisition.c -- The source for the 'Acquisition' module
that provides mix-in classes to support
environmental acquisition
MethodObject.c -- The source for the 'MethodObject' module
that provides a mix-in class for
user-defined method types. To create a
user-defined method type, just create an
extension subclass of
'MethodObject.MethodObject' that has an
'__call__' method.
Missing.c -- The source for the 'Missing' module
that provides a class for objects that
model "missing" or unknown data. Missing
objects have the property that all
mathematical operations yield a missing
value. This is included mainly as an
example (and test) of a numeric extension
base class.
MultiMapping.c -- The source for a slightly enhanced
'MultiMapping' module that is based on the
'MultiMapping' example given in this
paper. If present, document templates [2]
will take advantage of this module to
significantly increase rendering
performance.
Sync.py -- A Python module that provides a
'Synchonized' mix-in class that limits access
to an object's methods to one thread at a
time. This requires the installation of
the ThreadLock module.
ThreadLock.c -- The source for the 'ThreadLock' module that
provides 'ThreadLock' objects. These are
similar to the lock objects provided by
the 'thread' modules. Unlike normal
Python lock objects, 'ThreadLock' objects
can be acquired (and released) more than
once by the same thread.
In addition to the files listed above, several "test" modules are
included. These are modules that I used to test ExtensionClass.
They do not constitute a regression testing suit and I've made
little effort to assure that they actually work, although that
would be a good thing to do if time permits.
<h1>Installation</h1>
<p> <a href="COPYRIGHT.html">Copyright (C) 1996-1998, Digital Creations</a>.</p>
<p> The ExtensionClass distribution now uses the "Universal Unix
Makefile for Python extensions", <code>Makefile.pre.in</code>, which was
introduced as part of Python1.4. Copies of this file for Python
1.4 and Python 1.5 are included with this release. See the
instructions in the make file, itself. Note that you will need to
copy or rename the the file for the Python version you're using to
Makefile.pre.in.</p>
<h1>Files</h1>
<dl><dt> ExtensionClass.stx </dt><dd><p>This file in structured text format</p>
</dd>
<dt> ExtensionClass.html </dt><dd><p>This file in HTML format</p>
</dd>
<dt> Installation </dt><dd><p>Installation instructions in structured text
format. </p>
</dd>
<dt> Installation.html </dt><dd><p>Installation instructions in HTML
format. </p>
</dd>
<dt> Acquisition.stx </dt><dd><p>Acquisition documentation in structured text
format. </p>
</dd>
<dt> Acquisition.html </dt><dd><p>Acquisition documentation in HTML
format. </p>
</dd>
<dt> MultiMapping.stx </dt><dd><p>The MultiMapping example in structured text
format. </p>
</dd>
<dt> MultiMapping.html </dt><dd><p>The MultiMapping example in structured text
format. </p>
</dd>
<dt> release.notes </dt><dd><p>Release notes in structured text
format. </p>
</dd>
<dt> release.html </dt><dd><p>Release notes in HTML format. </p>
</dd>
<dt> README </dt><dd><p>A file that says to read this file.</p>
</dd>
<dt> Makefile.pre.in-1.4 </dt><dd><p>The Universal Unix Makefile for Python
extensions. This is the Python 1.4
version. Copy this to Makefile.pre.in
before using it.</p>
</dd>
<dt> Makefile.pre.in-1.5 </dt><dd><p>The Universal Unix Makefile for Python
extensions. This is the Python 1.5
version. Copy this to Makefile.pre.in
before using it.</p>
</dd>
<dt> Setup </dt><dd><p>a configuration file used by the Universal
Unix Makefile for Python extensions </p>
</dd>
<dt> ExtensionClass.c </dt><dd><p>The ExtensionClass source</p>
</dd>
<dt> ExtensionClass.h </dt><dd><p>The ExtensionClass header file</p>
</dd>
<dt> Acquisition.c </dt><dd><p>The source for the <code>Acquisition</code> module
that provides mix-in classes to support
environmental acquisition</p>
</dd>
<dt> MethodObject.c </dt><dd><p>The source for the <code>MethodObject</code> module
that provides a mix-in class for
user-defined method types. To create a
user-defined method type, just create an
extension subclass of
<code>MethodObject.MethodObject</code> that has an
<code>__call__</code> method.</p>
</dd>
<dt> Missing.c </dt><dd><p>The source for the <code>Missing</code> module
that provides a class for objects that
model "missing" or unknown data. Missing
objects have the property that all
mathematical operations yield a missing
value. This is included mainly as an
example (and test) of a numeric extension
base class.</p>
</dd>
<dt> MultiMapping.c </dt><dd><p>The source for a slightly enhanced
<code>MultiMapping</code> module that is based on the
<code>MultiMapping</code> example given in this
paper. If present, document templates <a href="#2">[2]</a>
will take advantage of this module to
significantly increase rendering
performance. </p>
</dd>
<dt> Sync.py </dt><dd><p>A Python module that provides a
<code>Synchonized</code> mix-in class that limits access
to an object's methods to one thread at a
time. This requires the installation of
the ThreadLock module.</p>
</dd>
<dt> ThreadLock.c </dt><dd><p>The source for the <code>ThreadLock</code> module that
provides <code>ThreadLock</code> objects. These are
similar to the lock objects provided by
the <code>thread</code> modules. Unlike normal
Python lock objects, <code>ThreadLock</code> objects
can be acquired (and released) more than
once by the same thread.</p>
</dd></dl>
<p> In addition to the files listed above, several "test" modules are
included. These are modules that I used to test ExtensionClass.
They do not constitute a regression testing suit and I've made
little effort to assure that they actually work, although that
would be a good thing to do if time permits.
</p>
This diff is collapsed.
This diff is collapsed.
<p>
Extension Class</p>
<p> <a href="COPYRIGHT.html">Copyright (C) 1996-1998, Digital Creations</a>.</p>
<p> A lightweight mechanism has been developed for making Python
extension types more class-like. Classes can be developed in an
extension language, such as C or C++, and these classes can be
treated like other python classes:</p>
<ul><li><p>They can be sub-classed in python,</p>
</li>
<li><p>They provide access to method documentation strings, and</p>
</li>
<li><p>They can be used to directly create new instances.</p>
</li></ul>
<p> Extension classes provide additional extensions to class and
instance semantics, including:</p>
<ul><li><p>A protocol for accessing subobjects "in the context of" their
containers. This is used to implement custom method types
and <a href="Acquisition.html">environmental acquisition</a>.</p>
</li>
<li><p>A protocol for overriding method call semantics. This is used
to implement "synchonized" classes and could be used to
implement argument type checking.</p>
</li>
<li><p>A protocol for class initialization that supports execution of a
special <code>__class_init__</code> method after a class has been
initialized.</p>
</li></ul>
<p> Extension classes illustrate how the Python class mechanism can be
extended and may provide a basis for improved or specialized class
models. </p>
<h1>Releases</h1>
<p> The current release is <a href="ExtensionClass-1.2.tar.gz">1.2</a>
To find out what's changed in this release,
see the <a href="release.html">release notes</a>.</p>
<p> Documentation is available <a href="ExtensionClass.html">on-line</a>.</p>
<h1>Windows Binaries</h1>
<p> A win32 binary release, <a href="ec12.zip">ec12.zip</a> is available. This
release includes all of the ExtensionClass modules built as
Windows extension modules (.pyd) files. These were built for
Python 1.5.1 using Microsoft Visual C++ 5.0 in "Release" mode.</p>
<p>
<TABLE BORDER=1 CELLPADDING=2>
</TABLE></p>
Extension Class
"Copyright (C) 1996-1998, Digital Creations":COPYRIGHT.html.
A lightweight mechanism has been developed for making Python
extension types more class-like. Classes can be developed in an
extension language, such as C or C++, and these classes can be
treated like other python classes:
- They can be sub-classed in python,
- They provide access to method documentation strings, and
- They can be used to directly create new instances.
Extension classes provide additional extensions to class and
instance semantics, including:
- A protocol for accessing subobjects "in the context of" their
containers. This is used to implement custom method types
and "environmental acquisition":Acquisition.html.
- A protocol for overriding method call semantics. This is used
to implement "synchonized" classes and could be used to
implement argument type checking.
- A protocol for class initialization that supports execution of a
special '__class_init__' method after a class has been
initialized.
Extension classes illustrate how the Python class mechanism can be
extended and may provide a basis for improved or specialized class
models.
Releases
The current release is "1.2":ExtensionClass-1.2.tar.gz,
To find out what's changed in this release,
see the "release notes":release.html.
Documentation is available "on-line":ExtensionClass.html.
Windows Binaries
A win32 binary release, "ec12.zip":ec12.zip, is available. This
release includes all of the ExtensionClass modules built as
Windows extension modules (.pyd) files. These were built for
Python 1.5.1 using Microsoft Visual C++ 5.0 in "Release" mode.
Release Notes
1.2
This release provides some important bug fixes, some new features,
and a new copyright.
New functionality:
- One or more mapping objects can be passed to the MultiMapping
constructor.
- MultiMapping objects implement the has_key and get methods as
defined for Python 1.5 dictionaries.
Bugs fixed:
- When support was added for passing acquisition wrappers to
methods of data-less C mix-in classes, C-based __call_method__
hooks were broken. The most notable example of this was the
breaking of the Synchronized class.
- Calling C-base-class special methods from overriding methods,
as in::
class LowerMultiMapping(MultiMapping):
def __getitem__(self, key):
return MultiMapping.__getitem__(self, lower(key))
caused infinite loops.
- A typo in the Acquisition probably caused __delitem__
calls on wrapped mapping objects to fail.
1.1
New functionality:
- Changed the way that methods in pure mix-in classes are
constructed. Now methods are wrapped in such a way that
tricky wrapper objects (like Acquisition wrappers) can
bind them to wrapped objects.
- An "is subclass" test is provided via the macros
'ExtensionClassSubclass_Check', and
'ExtensionClassSubclassInstance_Check', which are
documented in 'ExtensionClass.h'.
- Methods and Acquisition wrappers use free list to improve
allocation and deallocation performance.
- Bound methods have attributes who's values are stored in
their instances.
- Added '__module__' attribute to ExtensionClasses to be
consistent with Python 1.5 classes and to work correctly
with 1.5 pickling.
- Added the '__basic__' new class method to allow
ExtensionClass instances to be unpickled without calling
their constructors.
- Acquired acquiring objects can nor acquire from the object
they were accessed in, in addition to the object they were
acquired from.
- Added new 'Acquisition' variable, 'Acquired', to support
"Controlled Acquisition'.
- Added a new 'aq_acquire' method for objects that subclass
'Acquisition.Implicit' or 'Acquisition.Explicit'. This
supports explicit acquisition and provides an option
filter function to support "Filtered Acquisiition".
The 'acquire' method available in earlier releases is still
available, but is deprecated.
Bugs fixed:
- There were some incorrect C-level error return values.
- A bug in handling method chains caused "C inheritence"
to fail. This only affected extension types that
inherited from extension types using method chains, *not*
extension subclasses defined in Python inheriting from
extension base classes defined in C.
- Expressions like 'not foo' or statements like::
if foo:
...
often failed for non-collection types because of an
incorrect attempt to take the 'len' of an object.
- Comparisons of objects with different classes didn't work
correctly.
- Instances provided access to their class '__bases__'
attribute.
1.0.2
Bugs fixed:
- Fixed bug in handling subclasses of Sequence objects.
- Fixed comparison bug in Missing objects.
1.0.1
Added functionality to and fixed bug in Missing module
- Fixed horible reference-counting bug
- Changed so that 'Missing.Value.spam(a1,a2,whatever)'
returns 'Missing.Value' for any method name (except
'__reduce__') and any arguments.
- Changed so that missing values are picklable. Note that
the special global, Missing.Value, is pickled in a
slightly more efficient manner than other missing values.
1.0
First non-beta release
This release is the result of a major rewrite and "hardening"
effort to increase performance and reliability. This version
is being used in several Digital Creations products, so if
parts are broken, we probably don't use them. :-)
This release also contains several new features and example
modules, including:
- Acquisition,
- Custom method calls,
- Class initialization protocol,
- A class method that makes it possible to explicitly call
Python base-class methods.
- A sample application of custom method calls that provides
Java-like synchronized classes that prevent more than one
thread from accessing an object's methods at one time.
Note that there is one known incompatibility with previous
releases. In previouse releases, the method used to support
context wrapping was named '__bind_to_object__'. The name of
this method was changed to '__of__' in this release and I do
not expect this name to change in the future.
<h1>Release Notes</h1>
<h2>1.2</h2>
<p> This release provides some important bug fixes, some new features,
and a new copyright.</p>
<p> New functionality:</p>
<ul><li><p>One or more mapping objects can be passed to the MultiMapping
constructor.</p>
</li>
<li><p>MultiMapping objects implement the has_key and get methods as
defined for Python 1.5 dictionaries.</p>
</li></ul>
<p> Bugs fixed:</p>
<ul><li><p>When support was added for passing acquisition wrappers to
methods of data-less C mix-in classes, C-based __call_method__
hooks were broken. The most notable example of this was the
breaking of the Synchronized class.</p>
</li>
<li><p>Calling C-base-class special methods from overriding methods,
as in::</p>
<PRE>
class LowerMultiMapping(MultiMapping):
def __getitem__(self, key):
return MultiMapping.__getitem__(self, lower(key))
caused infinite loops.
</PRE>
</li>
<li><p>A typo in the Acquisition probably caused __delitem__
calls on wrapped mapping objects to fail.</p>
</li></ul>
<h2>1.1</h2>
<p> New functionality:</p>
<ul><li><p>Changed the way that methods in pure mix-in classes are
constructed. Now methods are wrapped in such a way that
tricky wrapper objects (like Acquisition wrappers) can
bind them to wrapped objects.</p>
</li>
<li><p>An "is subclass" test is provided via the macros
<code>ExtensionClassSubclass_Check</code>, and
<code>ExtensionClassSubclassInstance_Check</code>, which are
documented in <code>ExtensionClass.h</code>.</p>
</li>
<li><p>Methods and Acquisition wrappers use free list to improve
allocation and deallocation performance.</p>
</li>
<li><p>Bound methods have attributes who's values are stored in
their instances.</p>
</li>
<li><p>Added <code>__module__</code> attribute to ExtensionClasses to be
consistent with Python 1.5 classes and to work correctly
with 1.5 pickling.</p>
</li>
<li><p>Added the <code>__basic__</code> new class method to allow
ExtensionClass instances to be unpickled without calling
their constructors.</p>
</li>
<li><p>Acquired acquiring objects can nor acquire from the object
they were accessed in, in addition to the object they were
acquired from.</p>
</li>
<li><p>Added new <code>Acquisition</code> variable, <code>Acquired</code>, to support
"Controlled Acquisition'.</p>
</li>
<li><p>Added a new <code>aq_acquire</code> method for objects that subclass
<code>Acquisition.Implicit</code> or <code>Acquisition.Explicit</code>. This
supports explicit acquisition and provides an option
filter function to support "Filtered Acquisiition".</p>
<p> The <code>acquire</code> method available in earlier releases is still
available, but is deprecated.</p>
</li></ul>
<p> Bugs fixed:</p>
<ul><li><p>There were some incorrect C-level error return values.</p>
</li>
<li><p>A bug in handling method chains caused "C inheritence"
to fail. This only affected extension types that
inherited from extension types using method chains, <em>not</em>
extension subclasses defined in Python inheriting from
extension base classes defined in C.</p>
</li>
<li><p>Expressions like <code>not foo</code> or statements like::</p>
<PRE>
if foo:
...
often failed for non-collection types because of an
incorrect attempt to take the 'len' of an object.
</PRE>
</li>
<li><p>Comparisons of objects with different classes didn't work
correctly.</p>
</li>
<li><p>Instances provided access to their class <code>__bases__</code>
attribute.</p>
</li></ul>
<h2>1.0.2</h2>
<p> Bugs fixed:</p>
<ul><li><p>Fixed bug in handling subclasses of Sequence objects.</p>
</li>
<li><p>Fixed comparison bug in Missing objects.</p>
</li></ul>
<h2>1.0.1</h2>
<p> Added functionality to and fixed bug in Missing module</p>
<ul><li><p>Fixed horible reference-counting bug</p>
</li>
<li><p>Changed so that <code>Missing.Value.spam(a1,a2,whatever)</code>
returns <code>Missing.Value</code> for any method name (except
<code>__reduce__</code>) and any arguments.</p>
</li>
<li><p>Changed so that missing values are picklable. Note that
the special global, Missing.Value, is pickled in a
slightly more efficient manner than other missing values.</p>
</li></ul>
<h2>1.0</h2>
<p> First non-beta release</p>
<p> This release is the result of a major rewrite and "hardening"
effort to increase performance and reliability. This version
is being used in several Digital Creations products, so if
parts are broken, we probably don't use them. :-)</p>
<p> This release also contains several new features and example
modules, including:</p>
<ul><li><p>Acquisition,</p>
</li>
<li><p>Custom method calls,</p>
</li>
<li><p>Class initialization protocol,</p>
</li>
<li><p>A class method that makes it possible to explicitly call
Python base-class methods.</p>
</li>
<li><p>A sample application of custom method calls that provides
Java-like synchronized classes that prevent more than one
thread from accessing an object's methods at one time.</p>
</li></ul>
<p> Note that there is one known incompatibility with previous
releases. In previouse releases, the method used to support
context wrapping was named <code>__bind_to_object__</code>. The name of
this method was changed to <code>__of__</code> in this release and I do
not expect this name to change in the future.
</p>
/*****************************************************************************
Copyright (c) 1996-2002 Zope Corporation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.0 (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
****************************************************************************/
#ifndef __ACQUISITION_H_
#define __ACQUISITION_H_
typedef struct {
PyObject *(*AQ_Acquire) (PyObject *obj, PyObject *name, PyObject *filter,
PyObject *extra, int explicit, PyObject *deflt,
int containment);
PyObject *(*AQ_Get) (PyObject *obj, PyObject *name, PyObject *deflt,
int containment);
int (*AQ_IsWrapper) (PyObject *obj);
PyObject *(*AQ_Base) (PyObject *obj);
PyObject *(*AQ_Parent) (PyObject *obj);
PyObject *(*AQ_Self) (PyObject *obj);
PyObject *(*AQ_Inner) (PyObject *obj);
PyObject *(*AQ_Chain) (PyObject *obj, int containment);
} ACQUISITIONCAPI;
#ifndef _IN_ACQUISITION_C
#define aq_Acquire(obj, name, filter, extra, explicit, deflt, containment ) (AcquisitionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_Acquire(obj, name, filter, extra, explicit, deflt, containment)))
#define aq_acquire(obj, name) (AcquisitionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_Acquire(obj, name, NULL, NULL, 1, NULL, 0)))
#define aq_get(obj, name, deflt, containment) (AcquistionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_Get(obj, name, deflt, containment)))
#define aq_isWrapper(obj) (AcquisitionCAPI == NULL ? -1 : (AcquisitionCAPI->AQ_IsWrapper(obj)))
#define aq_base(obj) (AcquisitionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_Base(obj)))
#define aq_parent(obj) (AcquisitionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_Parent(obj)))
#define aq_self(obj) (AcquisitionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_Self(obj)))
#define aq_inner(obj) (AcquisitionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_Inner(obj)))
#define aq_chain(obj, containment) (AcquisitionCAPI == NULL ? NULL : (AcquisitionCAPI->AQ_CHain(obj, containment)))
static ACQUISITIONCAPI *AcquisitionCAPI = NULL;
#define aq_init() { \
PyObject *module; \
PyObject *api; \
if (! (module = PyImport_ImportModule("Acquisition"))) return; \
if (! (api = PyObject_GetAttrString(module,"AcquisitionCAPI"))) return; \
Py_DECREF(module); \
AcquisitionCAPI = PyCObject_AsVoidPtr(api); \
Py_DECREF(api); \
}
#endif
#endif
/*****************************************************************************
Copyright (c) 1996-2002 Zope Corporation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.0 (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
****************************************************************************/
#include "ExtensionClass.h"
#define UNLESS(E) if(!(E))
#define OBJECT(O) ((PyObject*)(O))
typedef struct {
PyObject_HEAD
PyObject *callable;
int level;
} CA;
static PyObject *
CA__init__(CA *self, PyObject *args)
{
PyObject *callable;
int level=0;
UNLESS(PyArg_ParseTuple(args,"O|i",&callable, &level)) return NULL;
if (level > 0)
{
callable=PyObject_CallFunction(OBJECT(self->ob_type), "Oi",
callable, self->level-1);
UNLESS (callable) return NULL;
self->level=level;
}
else
{
Py_INCREF(callable);
self->level=0;
}
self->callable=callable;
Py_INCREF(Py_None);
return Py_None;
}
static void
CA_dealloc(CA *self)
{
Py_DECREF(self->callable);
Py_DECREF(self->ob_type);
PyMem_DEL(self);
}
static PyObject *
CA_of(CA *self, PyObject *args)
{
if (self->level > 0)
{
Py_INCREF(self->callable);
return self->callable;
}
if (PyString_Check(self->callable))
{
/* Special case string as simple alias. */
PyObject *o;
UNLESS (PyArg_ParseTuple(args,"O", &o)) return NULL;
return PyObject_GetAttr(o, self->callable);
}
return PyObject_CallObject(self->callable, args);
}
static struct PyMethodDef CA_methods[] = {
{"__init__",(PyCFunction)CA__init__, METH_VARARGS, ""},
{"__of__", (PyCFunction)CA_of, METH_VARARGS, ""},
{NULL, NULL} /* sentinel */
};
static PyExtensionClass ComputedAttributeType = {
PyObject_HEAD_INIT(NULL) 0,
"ComputedAttribute", sizeof(CA),
0,
(destructor)CA_dealloc,
0,0,0,0,0, 0,0,0, 0,0,0,0,0, 0,0,
"ComputedAttribute(callable) -- Create a computed attribute",
METHOD_CHAIN(CA_methods),
EXTENSIONCLASS_BINDABLE_FLAG
};
static struct PyMethodDef methods[] = {
{NULL, NULL}
};
void
initComputedAttribute(void)
{
PyObject *m, *d;
UNLESS(ExtensionClassImported) return;
ComputedAttributeType.tp_getattro=
(getattrofunc)PyExtensionClassCAPI->getattro;
/* Create the module and add the functions */
m = Py_InitModule4("ComputedAttribute", methods,
"Provide Computed Attributes\n\n"
"$Id: ComputedAttribute.c,v 1.8 2002/11/13 17:45:53 jeremy Exp $\n",
OBJECT(NULL),PYTHON_API_VERSION);
d = PyModule_GetDict(m);
PyExtensionClass_Export(d,"ComputedAttribute",ComputedAttributeType);
}
This diff is collapsed.
/*****************************************************************************
Copyright (c) 1996-2002 Zope Corporation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.0 (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
****************************************************************************/
#include "ExtensionClass.h"
static PyObject *
of(PyObject *self, PyObject *args)
{
PyObject *inst;
if(PyArg_Parse(args,"O",&inst)) return PyECMethod_New(self,inst);
else return NULL;
}
struct PyMethodDef Method_methods[] = {
{"__of__",(PyCFunction)of,0,""},
{NULL, NULL} /* sentinel */
};
static struct PyMethodDef methods[] = {{NULL, NULL}};
void
initMethodObject(void)
{
PyObject *m, *d;
PURE_MIXIN_CLASS(Method,
"Base class for objects that want to be treated as methods\n"
"\n"
"The method class provides a method, __of__, that\n"
"binds an object to an instance. If a method is a subobject\n"
"of an extension-class instance, the the method will be bound\n"
"to the instance and when the resulting object is called, it\n"
"will call the method and pass the instance in addition to\n"
"other arguments. It is the responsibility of Method objects\n"
"to implement (or inherit) a __call__ method.\n",
Method_methods);
/* Create the module and add the functions */
m = Py_InitModule4("MethodObject", methods,
"Method-object mix-in class module\n\n"
"$Id: MethodObject.c,v 1.9 2002/11/13 17:45:53 jeremy Exp $\n",
(PyObject*)NULL,PYTHON_API_VERSION);
d = PyModule_GetDict(m);
PyExtensionClass_Export(d,"Method",MethodType);
}
/*****************************************************************************
Copyright (c) 1996-2002 Zope Corporation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.0 (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
****************************************************************************/
static char Missing_module_documentation[] =
""
"\n$Id: Missing.c,v 1.20 2003/05/09 21:50:36 jeremy Exp $"
;
#include "ExtensionClass.h"
/* Declarations for objects of type Missing */
typedef struct {
PyObject_HEAD
} Missing;
static PyObject *vname=0, *Missing_dot_Value=0, *empty_string=0, *reduce=0;
static PyObject *theValue, *notMissing;
static void
Missing_dealloc(Missing *self)
{
Py_DECREF(self->ob_type);
PyMem_DEL(self);
}
static PyObject *
Missing_repr(Missing *self)
{
Py_INCREF(Missing_dot_Value);
return Missing_dot_Value;
}
static PyObject *
Missing_str(Missing *self)
{
Py_INCREF(empty_string);
return empty_string;
}
/* Code to access Missing objects as numbers.
We must guarantee that notMissing is never returned to Python code,
because it would violate the guarantee that all Python-accessible
Missing values are equal to each other.
*/
static PyObject *
Missing_bin(PyObject *v, PyObject *w)
{
if (v == notMissing)
v = w;
assert(v != notMissing);
Py_INCREF(v);
return v;
}
static PyObject *
Missing_pow(PyObject *v, PyObject *w, PyObject *z)
{
if (v == notMissing)
v = w;
assert(v != notMissing);
Py_INCREF(v);
return v;
}
static PyObject *
Missing_un(PyObject *v)
{
Py_INCREF(v);
return v;
}
static int
Missing_nonzero(PyObject *v)
{
return 0;
}
/* Always return the distinguished notMissing object as the result
of the coercion. The notMissing object does not compare equal
to other Missing objects.
*/
static int
Missing_coerce(PyObject **pv, PyObject **pw)
{
Py_INCREF(*pv);
Py_INCREF(notMissing);
*pw = notMissing;
return 0;
}
static PyNumberMethods Missing_as_number = {
(binaryfunc)Missing_bin, /*nb_add*/
(binaryfunc)Missing_bin, /*nb_subtract*/
(binaryfunc)Missing_bin, /*nb_multiply*/
(binaryfunc)Missing_bin, /*nb_divide*/
(binaryfunc)Missing_bin, /*nb_remainder*/
(binaryfunc)Missing_bin, /*nb_divmod*/
(ternaryfunc)Missing_pow, /*nb_power*/
(unaryfunc)Missing_un, /*nb_negative*/
(unaryfunc)Missing_un, /*nb_positive*/
(unaryfunc)Missing_un, /*nb_absolute*/
(inquiry)Missing_nonzero, /*nb_nonzero*/
(unaryfunc)Missing_un, /*nb_invert*/
(binaryfunc)Missing_bin, /*nb_lshift*/
(binaryfunc)Missing_bin, /*nb_rshift*/
(binaryfunc)Missing_bin, /*nb_and*/
(binaryfunc)Missing_bin, /*nb_xor*/
(binaryfunc)Missing_bin, /*nb_or*/
(coercion)Missing_coerce, /*nb_coerce*/
0, /*nb_int*/
0, /*nb_long*/
0, /*nb_float*/
0, /*nb_oct*/
0, /*nb_hex*/
#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION != 1
0, /* nb_inplace_add */
0, /* nb_inplace_subtract */
0, /* nb_inplace_multiply */
0, /* nb_inplace_divide */
0, /* nb_inplace_remainder */
0, /* nb_inplace_power */
0, /* nb_inplace_lshift */
0, /* nb_inplace_rshift */
0, /* nb_inplace_and */
0, /* nb_inplace_xor */
0, /* nb_inplace_or */
Missing_bin, /* nb_floor_divide */
Missing_bin, /* nb_true_divide */
0, /* nb_inplace_floor_divide */
0, /* nb_inplace_true_divide */
#endif
};
/* ------------------------------------------------------- */
static PyObject *
Missing_reduce(PyObject *self, PyObject *args, PyObject *kw)
{
if(self==theValue)
{
Py_INCREF(vname);
return vname;
}
return Py_BuildValue("O()",self->ob_type);
}
static struct PyMethodDef reduce_ml[] = {
{"__reduce__", (PyCFunction)Missing_reduce, 1,
"Return a missing value reduced to standard python objects"
}
};
static PyObject *
Missing_getattr(PyObject *self, PyObject *name)
{
char *c, *legal;
if(!(c=PyString_AsString(name))) return NULL;
legal=c;
if (strchr("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
*legal) != NULL)
{
for (legal++; *legal; legal++)
if (strchr("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_",
*legal) == NULL)
{
legal=NULL;
break;
}
}
else legal=NULL;
if(! legal)
{
if(strcmp(c,"__reduce__")==0)
{
if(self==theValue)
{
Py_INCREF(reduce);
return reduce;
}
return PyCFunction_New(reduce_ml, self);
}
PyErr_SetObject(PyExc_AttributeError, name);
return NULL;
}
Py_INCREF(self);
return self;
}
static PyObject *
Missing_call(PyObject *self, PyObject *args, PyObject *kw)
{
Py_INCREF(self);
return self;
}
/* All Missing objects are equal to each other, except for the
special notMissing object. It is returned by coerce to
indicate that Missing is being compare to something else.
*/
static int
Missing_cmp(PyObject *m1, PyObject *m2)
{
if (m1 == notMissing)
return -1;
return (m2 == notMissing);
}
static PyExtensionClass MissingType = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"Missing", /*tp_name*/
sizeof(Missing), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)Missing_dealloc, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)0, /*obsolete tp_getattr*/
(setattrfunc)0, /*obsolete tp_setattr*/
Missing_cmp, /*tp_compare*/
(reprfunc)Missing_repr, /*tp_repr*/
&Missing_as_number, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash*/
(ternaryfunc)Missing_call, /*tp_call*/
(reprfunc)Missing_str, /*tp_str*/
(getattrofunc)Missing_getattr, /*tp_getattro*/
(setattrofunc)0, /*tp_setattro*/
/* Space for future expansion */
0L,0L,
"Represent totally unknown quantities\n"
"\n"
"Missing values are used to represent numeric quantities that are\n"
"unknown. They support all mathematical operations except\n"
"conversions by returning themselves.\n",
METHOD_CHAIN(NULL)
};
/* End of code for Missing objects */
/* -------------------------------------------------------- */
/* List of methods defined in the module */
static struct PyMethodDef Module_Level__methods[] = {
{NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */
};
void
initMissing(void)
{
PyObject *m, *d;
if(! ((vname=PyString_FromString("V"))
&& (Missing_dot_Value=PyString_FromString("Missing.Value"))
&& (empty_string=PyString_FromString(""))
)) return;
/* Create the module and add the functions */
m = Py_InitModule4("Missing", Module_Level__methods,
Missing_module_documentation,
(PyObject*)NULL,PYTHON_API_VERSION);
/* Add some symbolic constants to the module */
d = PyModule_GetDict(m);
PyExtensionClass_Export(d,"Missing",MissingType);
theValue = PyObject_CallObject((PyObject*)&MissingType, NULL);
notMissing = PyObject_CallObject((PyObject*)&MissingType, NULL);
reduce=PyCFunction_New(reduce_ml, theValue);
PyDict_SetItemString(d, "Value", theValue);
PyDict_SetItemString(d, "V", theValue);
PyDict_SetItemString(d, "MV", theValue);
}
/*****************************************************************************
Copyright (c) 1996-2002 Zope Corporation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.0 (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
****************************************************************************/
#include "ExtensionClass.h"
#define UNLESS(E) if(!(E))
typedef struct {
PyObject_HEAD
PyObject *data;
} MMobject;
staticforward PyExtensionClass MMtype;
static PyObject *
MM_push(MMobject *self, PyObject *args)
{
PyObject *src;
UNLESS(PyArg_ParseTuple(args, "O", &src)) return NULL;
UNLESS(-1 != PyList_Append(self->data,src)) return NULL;
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
MM_pop(MMobject *self, PyObject *args)
{
int i=1, l;
PyObject *r;
if(args) UNLESS(PyArg_ParseTuple(args, "|i", &i)) return NULL;
if((l=PyList_Size(self->data)) < 0) return NULL;
i=l-i;
UNLESS(r=PySequence_GetItem(self->data,l-1)) return NULL;
if(PyList_SetSlice(self->data,i,l,NULL) < 0) goto err;
return r;
err:
Py_DECREF(r);
return NULL;
}
static PyObject *
MM__init__(MMobject *self, PyObject *args)
{
UNLESS(self->data=PyList_New(0)) return NULL;
if (args)
{
int l, i;
if ((l=PyTuple_Size(args)) < 0) return NULL;
for (i=0; i < l; i++)
if (PyList_Append(self->data, PyTuple_GET_ITEM(args, i)) < 0)
return NULL;
}
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
MM_subscript(MMobject *self, PyObject *key)
{
long i;
PyObject *e;
UNLESS(-1 != (i=PyList_Size(self->data))) return NULL;
while(--i >= 0)
{
e=PyList_GetItem(self->data,i);
if((e=PyObject_GetItem(e,key))) return e;
PyErr_Clear();
}
PyErr_SetObject(PyExc_KeyError,key);
return NULL;
}
static PyObject *
MM_has_key(MMobject *self, PyObject *args)
{
PyObject *key;
UNLESS(PyArg_ParseTuple(args,"O",&key)) return NULL;
if((key=MM_subscript(self, key)))
{
Py_DECREF(key);
return PyInt_FromLong(1);
}
PyErr_Clear();
return PyInt_FromLong(0);
}
static PyObject *
MM_get(MMobject *self, PyObject *args)
{
PyObject *key, *d=Py_None;
UNLESS(PyArg_ParseTuple(args,"O|O",&key,&d)) return NULL;
if((key=MM_subscript(self, key))) return key;
PyErr_Clear();
Py_INCREF(d);
return d;
}
static struct PyMethodDef MM_methods[] = {
{"__init__", (PyCFunction)MM__init__, METH_VARARGS,
"__init__([m1, m2, ...]) -- Create a new empty multi-mapping"},
{"get", (PyCFunction) MM_get, METH_VARARGS,
"get(key,[default]) -- Return a value for the given key or a default"},
{"has_key", (PyCFunction) MM_has_key, METH_VARARGS,
"has_key(key) -- Return 1 if the mapping has the key, and 0 otherwise"},
{"push", (PyCFunction) MM_push, METH_VARARGS,
"push(mapping_object) -- Add a data source"},
{"pop", (PyCFunction) MM_pop, METH_VARARGS,
"pop([n]) -- Remove and return the last data source added"},
{NULL, NULL} /* sentinel */
};
static void
MM_dealloc(MMobject *self)
{
Py_XDECREF(self->data);
Py_DECREF(self->ob_type);
PyMem_DEL(self);
}
static PyObject *
MM_getattr(MMobject *self, char *name)
{
return Py_FindMethod(MM_methods, (PyObject *)self, name);
}
static int
MM_length(MMobject *self)
{
long l=0, el, i;
PyObject *e=0;
UNLESS(-1 != (i=PyList_Size(self->data))) return -1;
while(--i >= 0)
{
e=PyList_GetItem(self->data,i);
UNLESS(-1 != (el=PyObject_Length(e))) return -1;
l+=el;
}
return l;
}
static PyMappingMethods MM_as_mapping = {
(inquiry)MM_length, /*mp_length*/
(binaryfunc)MM_subscript, /*mp_subscript*/
(objobjargproc)NULL, /*mp_ass_subscript*/
};
/* -------------------------------------------------------- */
static char MMtype__doc__[] =
"MultiMapping -- Combine multiple mapping objects for lookup"
;
static PyExtensionClass MMtype = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"MultiMapping", /*tp_name*/
sizeof(MMobject), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)MM_dealloc, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)MM_getattr, /*tp_getattr*/
(setattrfunc)0, /*tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
&MM_as_mapping, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash*/
(ternaryfunc)0, /*tp_call*/
(reprfunc)0, /*tp_str*/
/* Space for future expansion */
0L,0L,0L,0L,
MMtype__doc__, /* Documentation string */
METHOD_CHAIN(MM_methods)
};
static struct PyMethodDef MultiMapping_methods[] = {
{NULL, NULL} /* sentinel */
};
void
initMultiMapping(void)
{
PyObject *m, *d;
m = Py_InitModule4(
"MultiMapping", MultiMapping_methods,
"MultiMapping -- Wrap multiple mapping objects for lookup"
"\n\n"
"$Id: MultiMapping.c,v 1.12 2002/11/12 20:06:22 jeremy Exp $\n",
(PyObject*)NULL,PYTHON_API_VERSION);
d = PyModule_GetDict(m);
PyExtensionClass_Export(d,"MultiMapping",MMtype);
if (PyErr_Occurred()) Py_FatalError("can't initialize module MultiMapping");
}
This diff is collapsed.
This diff is collapsed.
##############################################################################
#
# Copyright (c) 1996-2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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
#
##############################################################################
# Compatibility module
# $Id: Xaq.py,v 1.3 2002/08/14 21:24:48 mj Exp $
import Acquisition
Acquirer = Acquisition.Explicit
test_MultiMapping
1
2
3
2
[('spam', 3)]
[('eggs', 2), ('spam', 1)]
test_acquisition
A() red
A() green
A()
\ No newline at end of file
test_binding
<type 'Python Method'>
called bar() (1, 2, 3) {'name': 'spam'}
test_explicit_acquisition
A red
A green
A
\ No newline at end of file
test_method_hook
give us a hook, hook, hook...
C() () {}
give us a hook, hook, hook...
C() (1, 2, 3) {}
give us a hook, hook, hook...
C() (1, 2) {'spam': 'eggs'}
#! /usr/bin/env python
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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
#
##############################################################################
import os
import sys
import test.regrtest
ec_tests = ["test_AqAlg", "test_MultiMapping", "test_Missing",
"test_ThreadLock", "test_acquisition", "test_add",
"test_binding", "test_explicit_acquisition",
"test_method_hook"]
ec_testdir = os.path.split(sys.argv[0])[0] or '.'
test.regrtest.STDTESTS = ec_tests
test.regrtest.NOTTESTS = []
test.regrtest.main(testdir=ec_testdir)
This diff is collapsed.
from Missing import Value
assert Value != 12
assert 12 != Value
assert u"abc" != Value
assert Value != u"abc"
assert 1 + Value == Value
assert Value + 1 == Value
assert Value == 1 + Value
assert Value == Value + 1
##############################################################################
#
# Copyright (c) 1996-2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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
#
##############################################################################
from MultiMapping import *
def sortprint(L):
L.sort()
print L
m=MultiMapping()
m.push({'spam':1, 'eggs':2})
print m['spam']
print m['eggs']
m.push({'spam':3})
print m['spam']
print m['eggs']
sortprint(m.pop().items())
sortprint(m.pop().items())
try:
print m.pop()
raise "That\'s odd", "This last pop should have failed!"
except: # I should probably raise a specific error in this case.
pass
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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