Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Z
Zope
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
Zope
Commits
a84477cc
Commit
a84477cc
authored
Nov 18, 1998
by
Jim Fulton
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
*** empty log message ***
parent
656beed4
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
285 additions
and
672 deletions
+285
-672
lib/Components/ExtensionClass/doc/Acquisition.stx
lib/Components/ExtensionClass/doc/Acquisition.stx
+6
-2
lib/Components/ExtensionClass/doc/ExtensionClass.stx
lib/Components/ExtensionClass/doc/ExtensionClass.stx
+134
-668
lib/Components/ExtensionClass/release.fl
lib/Components/ExtensionClass/release.fl
+10
-1
lib/Components/ExtensionClass/src/release.sh
lib/Components/ExtensionClass/src/release.sh
+4
-1
lib/Components/ExtensionClass/test/test_MultiMapping.py
lib/Components/ExtensionClass/test/test_MultiMapping.py
+22
-0
lib/Components/ExtensionClass/test/test_Sync.py
lib/Components/ExtensionClass/test/test_Sync.py
+36
-0
lib/Components/ExtensionClass/test/test_ThreadLock.py
lib/Components/ExtensionClass/test/test_ThreadLock.py
+54
-0
lib/Components/ExtensionClass/test/test_acquisition.py
lib/Components/ExtensionClass/test/test_acquisition.py
+19
-0
No files found.
lib/Components/ExtensionClass/doc/Acquisition.stx
View file @
a84477cc
...
...
@@ -53,8 +53,8 @@ Acquisition
cannot be found in 'a'.
Aquisition wrappers provide access to the wrapped objects
through the attributes 'aq_parent'
and 'aq_self'. In the
example above, the expressions::
through the attributes 'aq_parent'
, 'aq_self', 'aq_base'.
In the
example above, the expressions::
'c.a.aq_parent is c'
...
...
@@ -69,6 +69,10 @@ Acquisition
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
...
...
lib/Components/ExtensionClass/doc/ExtensionClass.stx
View file @
a84477cc
Extension Classes, Python Extension Types Become Classes
Jim
Fulton
,
Digital
Creations
,
L
.
L
.
C
.
Jim Fulton, Digital Creations,
Inc
.
jim@digicool.com
Abstract
A
lightweight
mechanism
,
named
"ExtensionClass"
,
http
://
www
.
digicool
.
com
/
releases
/
ExtensionClass
/
ExtensionClass
-
1.0.2
.
tar
.
gz
,
has
been
developed
for
making
Python
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:
...
...
@@ -40,7 +38,15 @@ Extension Classes, Python Extension Types Become Classes
Extension classes illustrate how the Python class mechanism can be
extended and may provide a basis for improved or specialized class
models.
Releases
To find out what's changed in this
"release":ExtensionClass-1.1.tar.gz,
see the "release notes":release.html.
A "binary release for Python-1.5 on Windows NT/95":ExtensionClass-1.1-win32-Python1.5.zip,
is now available.
Problem
...
...
@@ -187,6 +193,11 @@ Extension Classes, Python Extension Types Become Classes
- Provide a basis for research on alternative semantics for classes
and inheritance.
**Note:** I use *non-standard* terminology here. By standard
*python* terminology, only standard python classes can be called
classes. ExtensionClass "classes" are technically just "types"
that happen to swim, walk and quack like python classes.
Base extension classes and extension subclasses
...
...
@@ -219,7 +230,10 @@ Extension Classes, Python Extension Types Become Classes
'__bases__' -- a sequence of base classes,
'
__dict__
' -- a class dictionary.
'__dict__' -- a class dictionary, and
'__module__' -- the name of the module in which the class was
defined.
The class dictionary provides access to unbound methods and their
documentation strings, including extension methods and special
...
...
@@ -302,407 +316,9 @@ Extension Classes, Python Extension Types Become Classes
to initialize an extension class with necessary behavior.
Example: MultiMapping objects
As an example, consider an extension class that implements a
"MultiMapping". A multi-mapping is an object that encapsulates 0
or more mapping objects. When an attempt is made to lookup an
object, the encapsulated mapping objects are searched until an
object is found.
Consider an implementation of a MultiMapping extension type,
without use of the extension class mechanism::
#include "Python.h"
#define UNLESS(E) if(!(E))
typedef struct {
PyObject_HEAD
PyObject *data;
} MMobject;
staticforward PyTypeObject 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){
long l;
PyObject *r;
static PyObject *emptyList=0;
UNLESS(emptyList) UNLESS(emptyList=PyList_New(0)) return NULL;
UNLESS(PyArg_ParseTuple(args, "")) return NULL;
UNLESS(-1 != (l=PyList_Size(self->data))) return NULL;
l--;
UNLESS(r=PySequence_GetItem(self->data,l)) return NULL;
UNLESS(-1 != PyList_SetSlice(self->data,l,l+1,emptyList)) goto err;
return r;
err:
Py_DECREF(r);
return NULL;
}
static struct PyMethodDef MM_methods[] = {
{"push", (PyCFunction) MM_push, 1,
"push(mapping_object) -- Add a data source"},
{"pop", (PyCFunction) MM_pop, 1,
"pop() -- Remove and return the last data source added"},
{NULL, NULL} /* sentinel */
};
static PyObject *
newMMobject(PyObject *ignored, PyObject *args){
MMobject *self;
UNLESS(PyArg_ParseTuple(args, "")) return NULL;
UNLESS(self = PyObject_NEW(MMobject, &MMtype)) return NULL;
UNLESS(self->data=PyList_New(0)) goto err;
return (PyObject *)self;
err:
Py_DECREF(self);
return NULL;
}
static void
MM_dealloc(MMobject *self){
Py_XDECREF(self->data);
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 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 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 PyTypeObject MMtype = {
PyObject_HEAD_INIT(&PyType_Type)
0, /*ob_size*/
"MultMapping", /*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 */
};
static struct PyMethodDef MultiMapping_methods[] = {
{"MultiMapping", (PyCFunction)newMMobject, 1,
"MultiMapping() -- Create a new empty multi-mapping"},
{NULL, NULL} /* sentinel */
};
void
initMultiMapping(){
PyObject *m;
m = Py_InitModule4(
"MultiMapping", MultiMapping_methods,
"MultiMapping -- Wrap multiple mapping objects for lookup",
(PyObject*)NULL,PYTHON_API_VERSION);
if (PyErr_Occurred())
Py_FatalError("can'
t
initialize
module
MultiMapping
");
}
This module defines an extension type, 'MultiMapping', and exports a
module function, 'MultiMapping', that creates 'MultiMapping'
Instances. The type provides two methods, 'push', and 'pop', for
adding and removing mapping objects to the multi-mapping.
The type provides mapping behavior, implementing mapping length
and subscript operators but not mapping a subscript assignment
operator.
Now consider an extension class implementation of MultiMapping
objects::
#include "
Python
.
h
"
#include "
ExtensionClass
.
h
"
#define UNLESS(E) if(!(E))
typedef struct {
PyObject_HEAD
PyObject *data;
} MMobject;
staticforward PyExtensionClass MMtype;
static PyObject *
MM_push(self, args)
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(self, args)
MMobject *self;
PyObject *args;
{
long l;
PyObject *r;
static PyObject *emptyList=0;
UNLESS(emptyList) UNLESS(emptyList=PyList_New(0)) return NULL;
UNLESS(PyArg_ParseTuple(args, "")) return NULL;
UNLESS(-1 != (l=PyList_Size(self->data))) return NULL;
l--;
UNLESS(r=PySequence_GetItem(self->data,l)) return NULL;
UNLESS(-1 != PyList_SetSlice(self->data,l,l+1,emptyList)) goto err;
return r;
err:
Py_DECREF(r);
return NULL;
}
static PyObject *
MM__init__(self, args)
MMobject *self;
PyObject *args;
{
UNLESS(PyArg_ParseTuple(args, "")) return NULL;
UNLESS(self->data=PyList_New(0)) goto err;
Py_INCREF(Py_None);
return Py_None;
err:
Py_DECREF(self);
return NULL;
}
static struct PyMethodDef MM_methods[] = {
{"
__init__
", (PyCFunction)MM__init__, 1,
"
__init__
()
--
Create
a
new
empty
multi
-
mapping
"},
{"
push
", (PyCFunction) MM_push, 1,
"
push
(
mapping_object
)
--
Add
a
data
source
"},
{"
pop
", (PyCFunction) MM_pop, 1,
"
pop
()
--
Remove
and
return
the
last
data
source
added
"},
{NULL, NULL} /* sentinel */
};
static void
MM_dealloc(self)
MMobject *self;
{
Py_XDECREF(self->data);
PyMem_DEL(self);
}
static PyObject *
MM_getattr(self, name)
MMobject *self;
char *name;
{
return Py_FindMethod(MM_methods, (PyObject *)self, name);
}
static int
MM_length(self)
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 PyObject *
MM_subscript(self, key)
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 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(&PyType_Type)
0, /*ob_size*/
"
MultMapping
", /*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()
{
PyObject *m, *d;
m = Py_InitModule4(
"
MultiMapping
", MultiMapping_methods,
"
MultiMapping
--
Wrap
multiple
mapping
objects
for
lookup
",
(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 version includes '
ExtensionClass
.
h
'. The two declarations of
'
MMtype
' have been changed from '
PyTypeObject
' to '
PyExtensionClass
'.
The '
METHOD_CHAIN
' macro has been used to add methods to the end of
the definition for '
MMtype
'. The module function, newMMobject has
been replaced by the '
MMtype
' method, '
MM__init__
'. Note that this
method does not create or return a new object. Finally, the lines::
d = PyModule_GetDict(m);
PyExtensionClass_Export(d,"MultiMapping",MMtype);
Have been added to both initialize the extension class and to export
it in the module dictionary.
To use this module, compile, link, and import it as with any other
extension module. The following python code illustrates the
module'
s
use
::
from
MultiMapping
import
MultiMapping
m
=
MultiMapping
()
m
.
push
({
'spam'
:
1
,
'eggs'
:
2
})
m
.
push
({
'spam'
:
3
,
'ham'
:
4
})
m
[
'spam'
]
#
returns
3
m
[
'ham'
]
#
returns
4
m
[
'foo'
]
#
raises
a
key
error
Creating
the
'MultiMapping'
object
took
three
steps
,
one
to
create
an
empty
'MultiMapping'
,
and
two
to
add
mapping
objects
to
it
.
We
might
wish
to
simplify
the
process
of
creating
MultiMapping
objects
by
providing
a
constructor
that
takes
source
mapping
objects
as
parameters
.
We
can
do
this
by
sub
-
classing
MultiMapping
in
Python
::
from
MultiMapping
import
MultiMapping
class
ExtendedMultiMapping
(
MultiMapping
):
def
__init__
(
self
,*
data
):
MultiMapping
.
__init__
(
self
)
for
d
in
data
:
self
.
push
(
d
)
m
=
ExtendedMultiMapping
({
'spam'
:
1
,
'eggs'
:
2
},
{
'spam'
:
3
,
'ham'
:
4
})
m
[
'spam'
]
#
returns
3
m
[
'ham'
]
#
returns
4
m
[
'foo'
]
#
raises
a
key
error
An "example":MultiMapping.html, is provided that illustrates the
changes needed to convert an existing type to an ExtensionClass.
Implementing base extension class constructors
...
...
@@ -779,11 +395,13 @@ Extension Classes, Python Extension Types Become Classes
because an object is accessed in the context of the object it is
accessed through.
We have found
two applications for this
:
We have found
many applications for this, including
:
- User-defined method objects,
and
- User-defined method objects,
- Acquisition
- "Acquisition":Acquisition.html, and
- Computed attributes
User-defined method objects
...
...
@@ -829,112 +447,44 @@ Extension Classes, Python Extension Types Become Classes
When run, this program outputs: 'a bar was called'.
Acquisition
Acquisition
[
7
]
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
release
include
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
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
Computed Attributes
a
=
A
()
c
=
C
()
c
.
a
=
A
()
It is not uncommon to wish to expose information via the
attribute interface without affecting implementation data
structures. One can use a custom '__getattr__' method to
implement computed attributes, however, this can be a bit
cumbersome and can interfere with other uses of '__getattr__',
such as for persistence.
c
.
a
.
report
()
#
prints
'red'
The '__of__' protocol provides a convenient way to implement
computed attributes. First, we define a ComputedAttribute
class. a ComputedAttribute is constructed with a function to
be used to compute an attribute, and calls the function when
it's '__of__' method is called:
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'
.
Two
styles
of
acquisition
are
supported
in
the
current
ExtensionClass
release
,
implicit
and
explicit
aquisition
.
Implicit
acquisition
Implicit
acquisition
is
so
name
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 '
aquire
' must be used, as in::
print c.a.acquire('
color
')
To support explicit acquisition, an object should inherit
from the mix-in class '
Acquisition
.
Explicit
'.
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
' and '
aq_self
'. 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
' inself.
Acquisition and methods
Python methods of objects that support acquisition can use
acquired attributes as in the above example. When a Python
method is called on an object that is wrapped by an
acquisition wrapper, the wrapper is passed to the method.
This rule also applies to user-defined method types.
Unfortunately, C methods cannot use aquired attributes at
this time.
import ExtensionClass
class ComputedAttribute(ExtensionClass.Base):
def __init__(self, func): self.func=func
def __of__(self, parent): return self.func(parent)
Then we can use this class to create computed attributes. In the
example below, we create a computed attribute, 'radius':
from math import sqrt
class Point(ExtensionClass.Base):
def __init__(self, x, y): self.x, self.y = x, y
radius=ComputedAttribute(lambda self: sqrt(self.x**2+self.y**2))
which we can use just like an ordinary attribute:
p=Point(2,2)
print p.radius
Overriding method calls
...
...
@@ -966,6 +516,33 @@ Extension Classes, Python Extension Types Become Classes
An interesting application of this mechanism would be to
implement interface checking on method calls.
Method attributes
Methods of ExtensionClass instances can have user-defined
attributes, which are stored in their associated instances.
For example::
class C(ExtensionClass.Base):
def get_secret(self):
"Get a secret"
....
c=C()
c.f.__roles__=['Trusted People']
print c.f.__roles__ # outputs ['Trusted People']
print c.f__roles__ # outputs ['Trusted People']
print C.f.__roles__ # fails, unbound method
A bound method attribute is set by setting an attribute in it's
instance with a name consisting of the concatination of the
method's '__name__' attribute and the attribute name.
Attributes cannot be set on unbound methods.
Class initialization
Normal Python class initialization is similar to but subtley
...
...
@@ -985,148 +562,41 @@ Extension Classes, Python Extension Types Become Classes
argument passed to the method will be the class, *not* an
instance of the class.
Status
The current release of the extension class module is "1.0.2",
http://www.digicool.com/releases/ExtensionClass/ExtensionClass-1.0.2.tar.gz.
The core implementation has less than four thousand lines of code,
including comments. This release requires Python 1.4.
Installation
The ExtensionClass distribution now uses the "Universal Unix Makefile for
Python extensions", '
Makefile
.
pre
.
in
', which was introduced as
part of Python1.4. A copy of this make file is included with this
release. See the instructions in the make file, itself.
Files
ExtensionClass.stx -- This file in structured text format
ExtensionClass.html -- This file in HTML format
README -- A file that says to read this file.
Makefile.pre.in -- The Universal Unix Makefile for Python
extensions
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.
Useful macros defined in ExtensionClass.h
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.
A number of useful macros are defined in ExtensionClass.h.
These are documented in 'ExtensionClass.h'.
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
.
Pickleability
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
.
Classes created with ExtensionClass, including extension base
classes are automatically pickleable. The usual gymnastics
necessary to pickle 'non-standard' types are not necessray for
types that have been modified to be extension base classes.
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.
Release Notes
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.
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.
Status
1.0.2
The current release of the extension class module is "1.1",
http://www.digicool.com/releases/ExtensionClass/ExtensionClass-1.1.tar.gz.
The core implementation has less than four thousand lines of code,
including comments. This release requires Python 1.4 or higher.
- Fixed bug in handling subclasses of Sequence objects.
To find out what's changed in this release, see the
"release notes":release.html.
- Fixed comparison bug in Missing objects
.
"Installation instructions":Installation.html, are provided
.
Issues
There are a number of issues that came up in the course of this work
and that deserve mention.
-
Currently, the class extension mechanism described in [4] requires
-
In Python 1.4, the class extension mechanism described in [4] required
that the first superclass in a list of super-classes must be of the
extended class type. This may not be convenient if mix-in
behavior is desired. If a list of base classes starts with a
standard python class, but includes an extension class, then an
error
i
s raised. It would be more useful if, when a list of base
error
wa
s raised. It would be more useful if, when a list of base
classes contains one or more objects that are not python classes,
the first such object was used to control the extended class
definition. To get around this, the 'ExtensionClass' module exports
...
...
@@ -1134,7 +604,12 @@ Extension Classes, Python Extension Types Become Classes
class in a list of base classes to assure that an extension
subclass is created.
This issue will go away with Python 1.5.
Python 1.5 allows the class extension even if the first non-class
object in the list of base classes is not the first object in
the list. This issue appears to go away in Python 1.5, however,
the restriction that the first non-class object in a list of
base classes must be the first in the list may reappear in later
versions of Python.
- Currently, only one base extension class can define any data in
C. The data layout of subclasses-instances is the same as for the
...
...
@@ -1153,7 +628,7 @@ Extension Classes, Python Extension Types Become Classes
in addition to object pointers.
- There is currently no support for sub-classing in C, beyond that
provided by method chains.
.
provided by method chains.
- Rules for mixed-type arithmetic are different for python class
instances than they are for extension type instances. Python
...
...
@@ -1176,12 +651,6 @@ Extension Classes, Python Extension Types Become Classes
standard class instance semantics and provide these features as
options at a later time.
- It would be useful to be able to specify parameters that control
class creation, but that would otherwise not appear in the class
dictionary. For example, it would be useful to provide parameters
to control mutability of classes (not class instances), or to
turn on caching of inherited class attributes.
- The extension class module defines new method types to bind C and
python methods to extension class instances. It would be useful
for these method objects to provide access to function call
...
...
@@ -1222,30 +691,27 @@ Extension Classes, Python Extension Types Become Classes
References
.. [1] Fulton, J., Providing Persistence for World-Wide-Web
Applications, Proceedings of the 5th Python Workshop.
http://www.digicool.com/papers/Persistence.html
.. [2] Page, R. and Cropper, S., Document Template, Proceedings of the
5th Python Workshop.
http://www.digicool.com/papers/DocumentTemplate.html
.. [1] Fulton, J., "Providing Persistence for World-Wide-Web Applications",
http://www.digicool.com/papers/Persistence.html,
Proceedings of the 5th Python Workshop.
.. [
3] Beaudry, D., Deriving Built-In Classes in Python, Proceedings of
the First International Python Workshop.
http://www.python.org/workshops/1994-11/BuiltInClasses/BuiltInClasses_1.html
.. [
2] Page, R. and Cropper, S., "Document Template",
http://www.digicool.com/papers/DocumentTemplate.html,
Proceedings of the 5th Python Workshop.
.. [
4] Van Rossum, G., Don Beaudry Hack - MESS, presented in the
Developer'
s
Future
Enhancements
session
of
the
4
th
Python
Workshop
.
http
://
www
.
python
.
org
/
workshops
/
1996
-
06
/
notes
/
thursday
.
html
.. [
3] Beaudry, D., "Deriving Built-In Classes in Python",
http://www.python.org/workshops/1994-11/BuiltInClasses/BuiltInClasses_1.html,
Proceedings of the First International Python Workshop.
..
[
5
]
Fulton
,
J
.,
Meta
-
Type
Object
.
This
is
a
small
proposal
,
the
text
of
which
is
contained
in
a
sample
implementation
source
file
,
http
://
www
.
digicool
.
com
/
jim
/
MetaType
.
c
.
.. [4] Van Rossum, G., "Don Beaudry Hack - MESS",
http://www.python.org/workshops/1996-06/notes/thursday.html,
presented in the Developer's Future Enhancements session of the
4th Python Workshop.
..
[
6
]
Beaudry
,
D
.,
and
Ascher
,
D
.,
The
Meta
-
Extension
Set
,
http
://
maigret
.
cog
.
brown
.
edu
/
pyutil
/
.. [5] Fulton, J., "Meta-Type Object",
http://www.digicool.com/jim/MetaType.c,
This is a small proposal, the text of which is contained in a
sample implementation source file,
..
[
7
]
Gil
,
J
.,
Lorenz
,
D
.,
Environmental
Acquisition
--
A
New
Inheritance
-
Like
Abstraction
Mechanism
,
OOPSLA
'96 Proceedings,
ACM SIG-PLAN, October, 1996
http://www.bell-labs.com/people/cope/oopsla/Oopsla96TechnicalProgramAbstracts.html#GilLorenz
.. [6] Beaudry, D., and Ascher, D., "The Meta-Extension Set",
http://starship.skyport.net/~da/mess/.
lib/Components/ExtensionClass/release.fl
View file @
a84477cc
ExtensionClass.stx
ExtensionClass.html
Installation
Installation.html
Acquisition.stx
Acquisition.html
MultiMapping.stx
MultiMapping.html
release.notes
release.html
README
Makefile.pre.in
Makefile.pre.in-1.4
Makefile.pre.in-1.5
Setup
ExtensionClass.c
ExtensionClass.h
...
...
lib/Components/ExtensionClass/src/release.sh
View file @
a84477cc
...
...
@@ -6,5 +6,8 @@ M=ExtensionClass
StructuredText <
$M
.stx
>
$M
.html
rm
-rf
"
$M
-
$R
"
mkdir
"
$M
-
$R
"
tar
-c
-R
release.fl
-f
- |
(
cd
"
$M
-
$R
"
;
tar
xvf -
)
for
f
in
`
cat
release.fl
`
;
do
cp
$f
"
$M
-
$R
/"
done
tar
cvf -
"
$M
-
$R
"
|
gzip
>
"
$M
-
$R
.tar.gz"
lib/Components/ExtensionClass/test/test_MultiMapping.py
0 → 100644
View file @
a84477cc
from
MultiMapping
import
*
m
=
MultiMapping
()
m
.
push
({
'spam'
:
1
,
'eggs'
:
2
})
print
m
[
'spam'
]
print
m
[
'eggs'
]
m
.
push
({
'spam'
:
3
})
print
m
[
'spam'
]
print
m
[
'eggs'
]
print
m
.
pop
()
print
m
.
pop
()
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
lib/Components/ExtensionClass/test/test_Sync.py
0 → 100644
View file @
a84477cc
from
Sync
import
Synchronized
import
thread
from
rand
import
rand
from
time
import
sleep
class
P
(
Synchronized
):
def
__init__
(
self
,
*
args
,
**
kw
):
self
.
count
=
0
def
inc
(
self
):
c
=
self
.
count
sleep
(
rand
()
/
327680.0
)
self
.
count
=
self
.
count
+
1
return
c
,
self
.
count
def
incn
(
self
,
n
):
c
=
self
.
count
for
i
in
range
(
n
):
self
.
inc
()
return
c
,
self
.
count
p
=
P
(
1
,
2
,
spam
=
3
)
def
test
():
for
i
in
range
(
10
):
n
=
3
old
,
new
=
p
.
incn
(
n
)
print
old
,
new
if
old
+
n
!=
new
:
print
'oops'
for
i
in
range
(
10
):
thread
.
start_new_thread
(
test
,())
sleep
(
50
)
lib/Components/ExtensionClass/test/test_ThreadLock.py
0 → 100644
View file @
a84477cc
import
ThreadLock
,
thread
from
rand
import
rand
from
time
import
sleep
from
ExtensionClass
import
Base
with_lock
=
1
class
P
(
Base
):
def
__oldcall_method__
(
self
,
f
,
a
,
k
=
{}):
if
with_lock
:
try
:
lock
=
self
.
lock
except
AttributeError
:
return
apply
(
f
,
a
,
k
)
else
:
return
apply
(
f
,
a
,
k
)
try
:
lock
.
acquire
()
return
apply
(
f
,
a
,
k
)
finally
:
lock
.
release
()
__call_method__
=
apply
def
__init__
(
self
,
*
args
,
**
kw
):
self
.
count
=
0
if
with_lock
:
self
.
lock
=
lock
=
ThreadLock
.
allocate_lock
()
self
.
__call_method__
=
lock
.
guarded_apply
def
inc
(
self
):
c
=
self
.
count
sleep
(
rand
()
/
32768.0
)
self
.
count
=
self
.
count
+
1
return
c
,
self
.
count
def
incn
(
self
,
n
):
c
=
self
.
count
for
i
in
range
(
n
):
self
.
inc
()
return
c
,
self
.
count
p
=
P
(
1
,
2
,
spam
=
3
)
def
test
():
for
i
in
range
(
10
):
n
=
3
old
,
new
=
p
.
incn
(
n
)
print
old
,
new
if
old
+
n
!=
new
:
print
'oops'
for
i
in
range
(
10
):
thread
.
start_new_thread
(
test
,())
sleep
(
50
)
lib/Components/ExtensionClass/test/test_acquisition.py
0 → 100644
View file @
a84477cc
from
ExtensionClass
import
Base
import
Acquisition
class
B
(
Base
):
color
=
'red'
class
A
(
Acquisition
.
Implicit
):
def
hi
(
self
):
print
self
,
self
.
color
b
=
B
()
b
.
a
=
A
()
b
.
a
.
hi
()
b
.
a
.
color
=
'green'
b
.
a
.
hi
()
try
:
A
().
hi
()
raise
'Program error'
,
'spam'
except
AttributeError
:
pass
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment