Commit e1b8ed5f authored by Jim Fulton's avatar Jim Fulton

*** empty log message ***

parent 26a2f5ad
/*
$Id: Acquisition.c,v 1.1 1997/02/17 15:05:40 jim Exp $
Acquisition Wrappers -- Implementation of acquisition through wrappers
Copyright
Copyright 1996 Digital Creations, L.C., 910 Princess Anne
Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
rights reserved. Copyright in this software is owned by DCLC,
unless otherwise indicated. Permission to use, copy and
distribute this software is hereby granted, provided that the
above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear. Note that
any product, process or technology described in this software
may be the subject of other Intellectual Property rights
reserved by Digital Creations, L.C. and are not licensed
hereunder.
Trademarks
Digital Creations & DCLC, are trademarks of Digital Creations, L.C..
All other trademarks are owned by their respective companies.
No Warranty
The software is provided "as is" without warranty of any kind,
either express or implied, including, but not limited to, the
implied warranties of merchantability, fitness for a particular
purpose, or non-infringement. This software could include
technical inaccuracies or typographical errors. Changes are
periodically made to the software; these changes will be
incorporated in new editions of the software. DCLC may make
improvements and/or changes in this software at any time
without notice.
Limitation Of Liability
In no event will DCLC be liable for direct, indirect, special,
incidental, economic, cover, or consequential damages arising
out of the use of or inability to use this software even if
advised of the possibility of such damages. Some states do not
allow the exclusion or limitation of implied warranties or
limitation of liability for incidental or consequential
damages, so the above limitation or exclusion may not apply to
you.
If you have questions regarding this software,
contact:
Jim Fulton, jim@digicool.com
Digital Creations L.C.
(540) 371-6909
Full description
$Log: Acquisition.c,v $
Revision 1.1 1997/02/17 15:05:40 jim
*** empty log message ***
*/
#include "ExtensionClass.h"
static void
PyVar_Assign(PyObject **v, PyObject *e)
{
Py_XDECREF(*v);
*v=e;
}
#define ASSIGN(V,E) PyVar_Assign(&(V),(E))
#define UNLESS(E) if(!(E))
#define UNLESS_ASSIGN(V,E) ASSIGN(V,E); UNLESS(V)
static PyObject *py__add__, *py__sub__, *py__mul__, *py__div__,
*py__mod__, *py__pow__, *py__divmod__, *py__lshift__, *py__rshift__,
*py__and__, *py__or__, *py__xor__, *py__coerce__, *py__neg__,
*py__pos__, *py__abs__, *py__nonzero__, *py__inv__, *py__int__,
*py__long__, *py__float__, *py__oct__, *py__hex__,
*py__getitem__, *py__setitem__, *py__delitem__,
*py__getslice__, *py__setslice__, *py__delslice__,
*py__concat__, *py__repeat__, *py__len__, *py__of__, *py__call__,
*py__repr__, *py__str__;
static void
init_py_names()
{
#define INIT_PY_NAME(N) py ## N = PyString_FromString(#N)
INIT_PY_NAME(__add__);
INIT_PY_NAME(__sub__);
INIT_PY_NAME(__mul__);
INIT_PY_NAME(__div__);
INIT_PY_NAME(__mod__);
INIT_PY_NAME(__pow__);
INIT_PY_NAME(__divmod__);
INIT_PY_NAME(__lshift__);
INIT_PY_NAME(__rshift__);
INIT_PY_NAME(__and__);
INIT_PY_NAME(__or__);
INIT_PY_NAME(__xor__);
INIT_PY_NAME(__coerce__);
INIT_PY_NAME(__neg__);
INIT_PY_NAME(__pos__);
INIT_PY_NAME(__abs__);
INIT_PY_NAME(__nonzero__);
INIT_PY_NAME(__inv__);
INIT_PY_NAME(__int__);
INIT_PY_NAME(__long__);
INIT_PY_NAME(__float__);
INIT_PY_NAME(__oct__);
INIT_PY_NAME(__hex__);
INIT_PY_NAME(__getitem__);
INIT_PY_NAME(__setitem__);
INIT_PY_NAME(__delitem__);
INIT_PY_NAME(__getslice__);
INIT_PY_NAME(__setslice__);
INIT_PY_NAME(__delslice__);
INIT_PY_NAME(__concat__);
INIT_PY_NAME(__repeat__);
INIT_PY_NAME(__len__);
INIT_PY_NAME(__of__);
INIT_PY_NAME(__call__);
INIT_PY_NAME(__repr__);
INIT_PY_NAME(__str__);
#undef INIT_PY_NAME
}
static PyObject *
CallMethodO(PyObject *self, PyObject *name,
PyObject *args, PyObject *kw)
{
if(! args && PyErr_Occurred()) return NULL;
UNLESS(name=PyObject_GetAttr(self,name)) return NULL;
ASSIGN(name,PyEval_CallObjectWithKeywords(name,args,kw));
if(args) Py_DECREF(args);
return name;
}
#define Build Py_BuildValue
/* Declarations for objects of type Wrapper */
typedef struct {
PyObject_HEAD
PyObject *obj;
PyObject *container;
} Wrapper;
staticforward PyExtensionClass Wrappertype;
static PyObject *
Wrapper__init__(Wrapper *self, PyObject *args)
{
PyObject *obj, *container;
UNLESS(PyArg_Parse(args,"(OO)",&obj,&container)) return NULL;
Py_INCREF(obj);
Py_INCREF(container);
self->obj=obj;
self->container=container;
Py_INCREF(Py_None);
return Py_None;
}
/* ---------------------------------------------------------------- */
static struct PyMethodDef Wrapper_methods[] = {
{"__init__", (PyCFunction)Wrapper__init__, 0,
"Initialize an Acquirer Wrapper"},
{NULL, NULL} /* sentinel */
};
/* ---------- */
static Wrapper *
newWrapper(PyObject *obj, PyObject *container)
{
Wrapper *self;
UNLESS(self = PyObject_NEW(Wrapper, (PyTypeObject*)&Wrappertype)) return NULL;
Py_INCREF(obj);
Py_INCREF(container);
self->obj=obj;
self->container=container;
return self;
}
static void
Wrapper_dealloc(Wrapper *self)
{
Py_DECREF(self->obj);
Py_DECREF(self->container);
PyMem_DEL(self);
}
static PyObject *
Wrapper_getattro(Wrapper *self, PyObject *oname)
{
PyObject *r;
char *name;
if(self->obj && (r=PyObject_GetAttr(self->obj,oname)))
{
if(r->ob_type==(PyTypeObject*)&Wrappertype)
{
if(r->ob_refcnt==1)
{
Py_INCREF(self);
ASSIGN(((Wrapper*)r)->container,(PyObject*)self);
}
else
ASSIGN(r,(PyObject*)newWrapper(((Wrapper*)r)->obj,
(PyObject*)self));
}
else if(PyECMethod_Check(r) && PyECMethod_Self(r)==self->obj)
ASSIGN(r,PyECMethod_New(r,(PyObject*)self));
else if(has__of__(r))
ASSIGN(r,CallMethodO(r,py__of__,Build("O", self), NULL));
return r;
}
if(self->obj) PyErr_Clear();
name=PyString_AsString(oname);
if(self->container && *name != '_')
{
if(r=PyObject_GetAttr(self->container,oname))
return r;
PyErr_Clear();
}
if(*name++=='a' && *name++=='q' && *name++=='_')
{
if(strcmp(name,"parent")==0)
{
if(self->container) r=self->container;
else r=Py_None;
Py_INCREF(r);
return r;
}
if(strcmp(name,"self")==0)
{
if(self->obj) r=self->obj;
else r=Py_None;
Py_INCREF(r);
return r;
}
}
if(*name++=='_' && strcmp(name,"_init__")==0)
return Py_FindAttr((PyObject*)self,oname);
PyErr_SetObject(PyExc_AttributeError,oname);
return NULL;
}
static int
Wrapper_setattro(Wrapper *self, PyObject *name, PyObject *v)
{
if(v && v->ob_type==(PyTypeObject*)&Wrappertype) v=((Wrapper*)v)->obj;
return PyObject_SetAttr(self->obj, name, v);
}
static int
Wrapper_compare(Wrapper *v, Wrapper *w)
{
return PyObject_Compare(v->obj,w->obj);
}
static PyObject *
Wrapper_repr(Wrapper *self)
{
PyObject *r;
if(r=PyObject_GetAttr((PyObject*)self,py__repr__))
{
ASSIGN(r,PyObject_CallFunction(r,NULL,NULL));
return r;
}
else
{
PyErr_Clear();
return PyObject_Repr(self->obj);
}
}
static PyObject *
Wrapper_str(Wrapper *self)
{
PyObject *r;
if(r=PyObject_GetAttr((PyObject*)self,py__str__))
{
ASSIGN(r,PyObject_CallFunction(r,NULL,NULL));
return r;
}
else
{
PyErr_Clear();
return PyObject_Str(self->obj);
}
}
static long
Wrapper_hash(Wrapper *self)
{
return PyObject_Hash(self->obj);
}
static PyObject *
Wrapper_call(Wrapper *self, PyObject *args, PyObject *kw)
{
Py_INCREF(args);
return CallMethodO((PyObject*)self,py__call__,args,kw);
}
/* Code to handle accessing Wrapper objects as sequence objects */
static int
Wrapper_length(Wrapper *self)
{
long l;
PyObject *r;
UNLESS(r=CallMethodO((PyObject*)self,py__len__,NULL,NULL)) return -1;
l=PyInt_AsLong(r);
Py_DECREF(r);
return l;
}
static PyObject *
Wrapper_concat(Wrapper *self, PyObject *bb)
{
return CallMethodO((PyObject*)self,py__concat__,Build("(O)", bb) ,NULL);
}
static PyObject *
Wrapper_repeat(Wrapper *self, int n)
{
return CallMethodO((PyObject*)self,py__repeat__,Build("(i)", n),NULL);
}
static PyObject *
Wrapper_item(Wrapper *self, int i)
{
return CallMethodO((PyObject*)self,py__getitem__, Build("(i)", i),NULL);
}
static PyObject *
Wrapper_slice(Wrapper *self, int ilow, int ihigh)
{
return CallMethodO((PyObject*)self,py__getslice__,
Build("(ii)", ilow, ihigh),NULL);
}
static int
Wrapper_ass_item(Wrapper *self, int i, PyObject *v)
{
if(v)
{
UNLESS(v=CallMethodO((PyObject*)self,py__setitem__,
Build("(iO)", i, v),NULL))
return -1;
}
else
{
UNLESS(v=CallMethodO((PyObject*)self,py__delitem__,
Build("(iO)", i),NULL))
return -1;
}
Py_DECREF(v);
return 0;
}
static int
Wrapper_ass_slice(Wrapper *self, int ilow, int ihigh, PyObject *v)
{
if(v)
{
UNLESS(v=CallMethodO((PyObject*)self,py__setslice__,
Build("(iiO)", ilow, ihigh, v),NULL))
return -1;
}
else
{
UNLESS(v=CallMethodO((PyObject*)self,py__delslice__,
Build("(ii)", ilow, ihigh),NULL))
return -1;
}
Py_DECREF(v);
return 0;
}
static PySequenceMethods Wrapper_as_sequence = {
(inquiry)Wrapper_length, /*sq_length*/
(binaryfunc)Wrapper_concat, /*sq_concat*/
(intargfunc)Wrapper_repeat, /*sq_repeat*/
(intargfunc)Wrapper_item, /*sq_item*/
(intintargfunc)Wrapper_slice, /*sq_slice*/
(intobjargproc)Wrapper_ass_item, /*sq_ass_item*/
(intintobjargproc)Wrapper_ass_slice, /*sq_ass_slice*/
};
/* -------------------------------------------------------------- */
/* Code to access Wrapper objects as mappings */
static PyObject *
Wrapper_subscript(Wrapper *self, PyObject *key)
{
return CallMethodO((PyObject*)self,py__getitem__,Build("(O)", key),NULL);
}
static int
Wrapper_ass_sub(Wrapper *self, PyObject *key, PyObject *v)
{
if(v)
{
UNLESS(v=CallMethodO((PyObject*)self,py__setitem__,
Build("(OO)", key, v),NULL))
return -1;
}
else
{
UNLESS(v=CallMethodO((PyObject*)self,py__delitem__,
Build("(O)", key),NULL))
return -1;
}
Py_XDECREF(v);
return 0;
}
static PyMappingMethods Wrapper_as_mapping = {
(inquiry)Wrapper_length, /*mp_length*/
(binaryfunc)Wrapper_subscript, /*mp_subscript*/
(objobjargproc)Wrapper_ass_sub, /*mp_ass_subscript*/
};
/* -------------------------------------------------------- */
static char Wrappertype__doc__[] =
""
;
static PyExtensionClass Wrappertype = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"AcquirerWrapper", /*tp_name*/
sizeof(Wrapper), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)Wrapper_dealloc, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)0, /*tp_getattr*/
(setattrfunc)0, /*tp_setattr*/
(cmpfunc)Wrapper_compare, /*tp_compare*/
(reprfunc)Wrapper_repr, /*tp_repr*/
0, /*tp_as_number*/
&Wrapper_as_sequence, /*tp_as_sequence*/
&Wrapper_as_mapping, /*tp_as_mapping*/
(hashfunc)Wrapper_hash, /*tp_hash*/
(ternaryfunc)Wrapper_call, /*tp_call*/
(reprfunc)Wrapper_str, /*tp_str*/
(getattrofunc)Wrapper_getattro, /*tp_getattr with object key*/
(setattrofunc)Wrapper_setattro, /*tp_setattr with object key*/
/* Space for future expansion */
0L,0L,
Wrappertype__doc__, /* Documentation string */
METHOD_CHAIN(Wrapper_methods),
EXTENSIONCLASS_BINDABLE_FLAG,
};
/* End of code for Wrapper objects */
/* -------------------------------------------------------- */
/* List of methods defined in the module */
static struct PyMethodDef Wrap_methods[] = {
{NULL, NULL} /* sentinel */
};
/* Initialization function for the module (*must* be called initWrap) */
static char Wrap_module_documentation[] =
""
;
static PyObject *
acquire_of(PyObject *self, PyObject *args)
{
PyObject *inst;
UNLESS(PyArg_Parse(args,"O",&inst)) return NULL;
UNLESS(PyExtensionInstance_Check(inst))
{
PyErr_SetString(PyExc_TypeError,
"attempt to wrap extension method using an object that\n"
"is not an extension class instance.");
return NULL;
}
return (PyObject*)newWrapper(self,args);
}
struct PyMethodDef Acquirer_methods[] = {
{"__of__",(PyCFunction)acquire_of,0,""},
{NULL, NULL} /* sentinel */
};
static struct PyMethodDef methods[] = {{NULL, NULL}};
void
initAcquisition()
{
PyObject *m, *d;
PURE_MIXIN_CLASS(Acquirer,
"Base class for objects that acquire attributes from containers\n"
, Acquirer_methods)
/* Create the module and add the functions */
m = Py_InitModule4("Acquisition", methods,
"",
(PyObject*)NULL,PYTHON_API_VERSION);
d = PyModule_GetDict(m);
init_py_names();
PyExtensionClass_Export(d,"Acquirer",AcquirerType);
PyExtensionClass_Export(d,"Wrapper",Wrappertype);
CHECK_FOR_ERRORS("can't initialize module Acquisition");
}
Extension Classes, Python Extension Types Become Classes
Jim Fulton, Digital Creations, L.C.
jim@digicool.com
Abstract
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 support for extended method binding
protocols to support additional method types and additional method
call semantics.
An example class shows how extension classes are implemented and how
they differ from extension types.
Extension classes illustrate how the Python class mechanism can be
extended and may provide a basis for improved or specialized class
models.
Problem
Currently, Python provides two ways of defining new kinds of objects:
- Python classes
- Extension types
Each approach has it's strengths. Extension types provide much greater
control to the programmer and, generally, better performance. Because
extension types are written in C, the programmer has greater access to
external resources. (Note that Python's use of the term type has
little to do with the notion of type as a formal specification.)
Classes provide a higher level of abstraction and are generally much
easier to develop. Classes provide full inheritance support, while
support for inheritance when developing extension types is very
limited. Classes provide run-time meta-data, such as method documentation
strings, that are useful for documentation and discovery. Classes
act as factories for creating instances, while separate functions
must be provided to create instances of types.
It would be useful to combine the features of the two approaches. It
would be useful to be able to have better support for inheritance for
types, or to be able to subclass from types in Python. It would be
useful to be able to have class-like meta-data support for types and
the ability to construct instances directly from types.
We have need, in a number of projects, for semantics that are
slightly different than the usual class semantics, yet we want to do
most of our development in C. For example, we have developed a
persistence mechanism [1] that redefines '__getattr__' and
'__setattr__' to take storage-related actions when object state is
accessed or modified. We want to be able to take certain actions on
*every* attribute reference, but for python class instances,
'__getattr__' is only called when attribute lookup fails by normal
means.
As another example, we would like to have greater control over how
methods are bound. Currently, when accessing a class
instance attribute, the attribute value is bound together with the
instance in a method object if and only if the attribute value is a
python function. For some applications, we might also want to be
able to bind extension functions, or other types of callable
objects, such as HTML document templates [2]. Furthermore,
we might want to have greater control over how objects are bound.
For example, we might want to bind instances and callable objects
with special method objects that assure that no more than one thread
accesses the object or method at one time.
We can provide these special semantics in extension types, but we
wish to provide them for classes developed in Python.
Background
At the first Python Conference, Don Beaudry presented work [3] done
at V.I. Corp to integrate Python with C++ frameworks. This system
provided a number of important features, including:
- Definition of extension types that provide class-like meta-data
and that can be called to create instances.
- Ability to subclass in python from C types.
- Ability to define classes in python who's data are stored as
C structures rather than dictionaries to better interface to
C and C++ libraries, and for better performance.
- Less dynamic data structures. In particular, the data structure
for a class is described declaratively during class definition.
- Support for enumeration types.
This work was not released, initially.
Shortly after the workshop, changes were made to Python to support
the sub-classing features described in [3]. These changes were not
documented until recently [4].
At the third Python workshop, I presented some work I had done on
generating module documentation for extension types. Based on the
discussion at this workshop, I developed a meta-type proposal [5].
This meta-type proposal was for an object that simply stored
meta-information for a type, for the purpose of generating module
documentation.
In the summer of 1996, Don Beaudry released the system described in
[3] under the name MESS [6]. MESS addresses a number of needs but
has a few drawbacks:
- Only single inheritance is supported.
- The mechanisms for defining MESS extension types is very different
from and more complicated than the standard Python type creation
mechanism.
- Defining MESS types requires the use of an extensive C
applications programming interface. This presents problems for
configuring dynamically-loaded extension modules unless the MESS
library is linked into the Python interpreter.
- Because the system tries to do a number of different things, it is
fairly large, about 15,000 lines.
- There is very little documentation, especially for the C
programming interface.
- The system is a work in progress, with a number of outstanding
bugs.
As MESS matures, we expect most of these problems to be addressed.
Extension Classes
To meet short term needs for a C-based persistence mechanism [1], an
extension class module was developed using the mechanism described
in [4] and building on ideas from MESS [6]. The extension class module
recasts extension types as "extension classes" by seeking to
eliminate, or at least reduce semantic differences between types and
classes. The module was designed to meet the following goal:
- Provide class-like behavior for extension types, including
interfaces for meta information and for constructing instances.
- Support sub-classing in Python from extension classes, with support
for multiple inheritance.
- Provide a small hardened implementation that can be used for
current products.
- Provide a mechanism that requires minimal modification to existing
extension types.
- Provide a basis for research on alternative semantics for classes
and inheritance.
Base extension classes and extension subclasses
Base extension classes are implemented in C. Extension subclasses
are implemented in python and inherit, directly or indirectly from
one or more base extension classes. An extension subclass may
inherit from base extension classes, extension subclasses, and
ordinary python classes. The usual inheritance order rules
apply. Currently, extension subclasses must conform to the
following two rules:
- The first super class listed in the class statement defining an
extension subclass must be either a base extension class or an
extension subclass.
- At most one base extension direct or indirect super class may
define C data members. If an extension subclass inherits from
multiple base extension classes, then all but one must be mix-in
classes that provide extension methods but no data.
Meta Information
Like standard python classes, extension classes have the following
attributes containing meta-data:
'__doc__' -- a documentation string for the class,
'__name__' -- the class name,
'__bases__' -- a sequence of base classes,
'__dict__' -- a class dictionary.
The class dictionary provides access to unbound methods and their
documentation strings, including extension methods and special
methods, such as methods that implement sequence and numeric
protocols. Unbound methods can be called with instance first
arguments.
Subclass instance data
Extension subclass instances have instance dictionaries, just
like Python class instances do. When fetching attribute values,
extension class instances will first try to obtain data from the
base extension class data structure, then from the instance
dictionary, then from the class dictionary, and finally from base
classes. When setting attributes, extension classes first attempt
to use extension base class attribute setting operations, and if
these fail, then data are placed in the instance dictionary.
Implementing base extension classes
A base extension class is implemented in much the same way that an
extension type is implemented, except:
- The include file, 'ExtensionClass.h', must be included.
- The type structure is declared to be of type 'PyExtensionClass', rather
than of type 'PyTypeObject'.
- The type structure has an additional member that must be defined
after the documentation string. This extra member is a method chain
('PyMethodChain') containing a linked list of method definition
('PyMethodDef') lists. Method chains can be used to implement
method inheritance in C. Most extensions don't use method chains,
but simply define method lists, which are null-terminated arrays
of method definitions. A macro, 'METHOD_CHAIN' is defined in
'ExtensionClass.h' that converts a method list to a method chain.
(See the example below.)
- Module functions that create new instances must be replaced by an
'__init__' method that initializes, but does not create storage for
instances.
- The extension class must be initialized and exported to the module
with::
PyExtensionClass_Export(d,"name",type);
where 'name' is the module name and 'type' is the extension class
type object.
Attribute lookup
Attribute lookup is performed by calling the base extension class
'getattr' operation for the base extension class that includes C
data, or for the first base extension class, if none of the base
extension classes include C data. 'ExtensionClass.h' defines a
macro 'Py_FindAttrString' that can be used to find an object's
attributes that are stored in the object's instance dictionary or
in the object's class or base classes::
v = Py_FindAttrString(self,name);
In addition, a macro is provided that replaces 'Py_FindMethod'
calls with logic to perform the same sort of lookup that is
provided by 'Py_FindAttrString'.
Linking
The extension class mechanism was designed to be useful with
dynamically linked extension modules. Modules that implement
extension classes do not have to be linked against an extension
class library. The macro 'PyExtensionClass_Export' imports the
'ExtensionClass' module and uses objects imported from this module
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(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 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(ignored, args)
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(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 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 the 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
Bind-able objects
Python classes bind Python function attributes into methods. When a
class has a function attribute that is accessed as an instance
attribute, a method object is created and returned that contains
references to the original function and instance. When the method
is called, the original function is called with the instance as the
first argument followed by any arguments passed to the method.
Extension classes provide a similar mechanism for attributes that
are Python functions or inherited extension functions. In
addition, if an extension class attribute is an instance of an
extension class that defines a '__of__' method, then when the
attribute is accessed through an instance, it's '__of__' method
will be called to create a bound method.
Consider the following example::
import ExtensionClass
class foo(ExtensionClass.Base):
def __call__(self,ob): print 'called', ob
class meth:
def __init__(self,m,o): self.meth, self.ob=m,o
def __call__(self): self.meth(self.ob)
def __of__(self,o): return self.meth(self,o)
class bar(ExtensionClass.Base):
hi=foo() # bar has a foo method named hi
x=bar()
hi=x.hi()
Note that 'ExtensionClass.Base' is a base extension class that
provides no function other than creating extension subclasses. It
is used here to allow extension classes to be defined totally in
python to take advantage of the binding mechanism.
When run, this program outputs: 'called <bar instance at 140046420>'
Status
The current release of the extension class module is 0.3 [Down-load]. The
implementation is about two thousand lines in size, including
comments. This release will work with Python version 1.3 or 1.4, but
does not take advantage of attribute access optimizations available
in Python 1.4.
Installation
Dynamic linking installation
Installation is in two steps. First, run 'make' to build the
extension class module, and then run 'make' with a target of
'install' to install the 'ExtensionClass' module and the
'ExtensionClass.h' header file in the standard python
directories. For Python revision 1.4 and higher, use the default
make file::
make
make install
for Python 1.3, use the make file, '1.3-Makefile'::
make -f 1.3-Makefile
make -f 1.3-Makefile install
Note that the make files can also be used to build the sample
extension class module, MultiMapping::
make MODULE=MultiMapping
Static linking installation
To statically link the extension class module into the Python
interpreter:
1. copy the files: 'ExtensionClass.c' and 'ExtensionClass.h' to
the 'Modules' directory in the Python source tree,
2. add the following line to the 'Setup' file in the 'Modules'
directory::
ExtensionClass ExtensionClass.c
3. rebuild python, and
4. copy 'ExtensionClass.h' to the Python run-time include
directory,
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
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 is 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
a base extension class, 'Base', that can be used as the first base
class in a list of base classes to assure that an extension
subclass is created.
- Currently, only one base extension class can define any data in
C. The data layout of subclasses-instances is the same as for the
base class that defines data in C, except that the data structure
is extended to hold an instance dictionary. The data structure
begins with a standard python header, and extension methods expect
the C instance data to occur immediately after the object header. If
two or more base classes defined C data, the methods for the
different base classes would expect their data to be in the same
location. A solution might be to allocate base class instances and
store pointers to these instances in the subclass data structure.
The method binding mechanism would have to be a more complicated
to make sure that methods were bound to the correct base data
structure.
- There is currently no support for sub-classing in C, beyond that
provided by method chains..
- Rules for mixed-type arithmetic are different for python class
instances than they are for extension type instances. Python
classes can define right and left versions of numeric binary
operators, or they can define a coercion operator for converting
binary operator operands to a common type. For extension types,
only the latter, coercion-based, approach is supported. The
coercion-based approach does not work well for many data types for
which coercion rules depend on the operator. Because extension
classes are based on extension types, they are currently limited
to the coercion-based approach. It would be straightforward to
extend the extension class implementation to allow both types of
mixed-type arithmetic control.
- I considered making extension classes immutable, meaning that
class attributes could not be set after class creation. I also
considered making extension subclasses cache inherited
attributes. Both of these are related and attractive for some
applications, however, I decided that it would be better to retain
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
information, such as the number and names of arguments and the
number of defaults, by parsing extension function documentation
strings.
Applications
Aside from test and demonstration applications, the extension class
mechanism has been used to provide an extension-based implementation
of the persistence mechanism described in [1]. We plan to develop
this further to provide features such as automatic deactivation of
objects not used after some period of time and to provide more
efficient persistent-object cache management.
Future projects include creation of Java-like synchronized objects
and implementation of acquisition [7], an inheritance-like mechanism
that provides attribute sharing between container and component
objects.
Summary
The extension-class mechanism described here provides a way to add
class services to extension types. It allows:
- Sub-classing extension classes in Python,
- Construction of extension class instances by calling extension
classes,
- Extension classes to provide meta-data, such as unbound methods
and their documentation string.
In addition, the extension class module provides a relatively
concise example of the use of mechanisms that were added to Python
to support MESS [6], and that were described at the fourth Python
Workshop [4]. It is hoped that this will spur research in improved
and specialized models for class implementation in Python.
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
.. [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
.. [4] Von Rossum, G., Don Beaudry Hack - MESS, presented in the
Developer's Future Enhancements session of the 4th Python Workshop.
http://www.python.org/workshops/1996-06/notes/thursday.html
.. [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.
.. [6] Beaudry, D., and Ascher, D., The Meta-Extension Set,
http://maigret.cog.brown.edu/pyutil/
.. [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
.. [Down-load] ftp://ftp.digicool.com/pub/releases/ExtensionClass-0.3.tar.gz
#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()
{
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",
(PyObject*)NULL,PYTHON_API_VERSION);
d = PyModule_GetDict(m);
PyExtensionClass_Export(d,"Method",MethodType);
/* Check for errors */
CHECK_FOR_ERRORS("can't initialize module MethodObject");
}
See the file ExtensionClass.txt or EXtensionClass.html for a
description of extension class and for installation instructions.
For more information, contact jim@digicool.com
Makefile
ExtensionClass.c
ExtensionClass.h
ExtensionClass.txt
ExtensionClass.html
MultiMapping.c
MethodObject.c
Acquisition.c
README
/*
$Id: Acquisition.c,v 1.1 1997/02/17 15:05:40 jim Exp $
Acquisition Wrappers -- Implementation of acquisition through wrappers
Copyright
Copyright 1996 Digital Creations, L.C., 910 Princess Anne
Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
rights reserved. Copyright in this software is owned by DCLC,
unless otherwise indicated. Permission to use, copy and
distribute this software is hereby granted, provided that the
above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear. Note that
any product, process or technology described in this software
may be the subject of other Intellectual Property rights
reserved by Digital Creations, L.C. and are not licensed
hereunder.
Trademarks
Digital Creations & DCLC, are trademarks of Digital Creations, L.C..
All other trademarks are owned by their respective companies.
No Warranty
The software is provided "as is" without warranty of any kind,
either express or implied, including, but not limited to, the
implied warranties of merchantability, fitness for a particular
purpose, or non-infringement. This software could include
technical inaccuracies or typographical errors. Changes are
periodically made to the software; these changes will be
incorporated in new editions of the software. DCLC may make
improvements and/or changes in this software at any time
without notice.
Limitation Of Liability
In no event will DCLC be liable for direct, indirect, special,
incidental, economic, cover, or consequential damages arising
out of the use of or inability to use this software even if
advised of the possibility of such damages. Some states do not
allow the exclusion or limitation of implied warranties or
limitation of liability for incidental or consequential
damages, so the above limitation or exclusion may not apply to
you.
If you have questions regarding this software,
contact:
Jim Fulton, jim@digicool.com
Digital Creations L.C.
(540) 371-6909
Full description
$Log: Acquisition.c,v $
Revision 1.1 1997/02/17 15:05:40 jim
*** empty log message ***
*/
#include "ExtensionClass.h"
static void
PyVar_Assign(PyObject **v, PyObject *e)
{
Py_XDECREF(*v);
*v=e;
}
#define ASSIGN(V,E) PyVar_Assign(&(V),(E))
#define UNLESS(E) if(!(E))
#define UNLESS_ASSIGN(V,E) ASSIGN(V,E); UNLESS(V)
static PyObject *py__add__, *py__sub__, *py__mul__, *py__div__,
*py__mod__, *py__pow__, *py__divmod__, *py__lshift__, *py__rshift__,
*py__and__, *py__or__, *py__xor__, *py__coerce__, *py__neg__,
*py__pos__, *py__abs__, *py__nonzero__, *py__inv__, *py__int__,
*py__long__, *py__float__, *py__oct__, *py__hex__,
*py__getitem__, *py__setitem__, *py__delitem__,
*py__getslice__, *py__setslice__, *py__delslice__,
*py__concat__, *py__repeat__, *py__len__, *py__of__, *py__call__,
*py__repr__, *py__str__;
static void
init_py_names()
{
#define INIT_PY_NAME(N) py ## N = PyString_FromString(#N)
INIT_PY_NAME(__add__);
INIT_PY_NAME(__sub__);
INIT_PY_NAME(__mul__);
INIT_PY_NAME(__div__);
INIT_PY_NAME(__mod__);
INIT_PY_NAME(__pow__);
INIT_PY_NAME(__divmod__);
INIT_PY_NAME(__lshift__);
INIT_PY_NAME(__rshift__);
INIT_PY_NAME(__and__);
INIT_PY_NAME(__or__);
INIT_PY_NAME(__xor__);
INIT_PY_NAME(__coerce__);
INIT_PY_NAME(__neg__);
INIT_PY_NAME(__pos__);
INIT_PY_NAME(__abs__);
INIT_PY_NAME(__nonzero__);
INIT_PY_NAME(__inv__);
INIT_PY_NAME(__int__);
INIT_PY_NAME(__long__);
INIT_PY_NAME(__float__);
INIT_PY_NAME(__oct__);
INIT_PY_NAME(__hex__);
INIT_PY_NAME(__getitem__);
INIT_PY_NAME(__setitem__);
INIT_PY_NAME(__delitem__);
INIT_PY_NAME(__getslice__);
INIT_PY_NAME(__setslice__);
INIT_PY_NAME(__delslice__);
INIT_PY_NAME(__concat__);
INIT_PY_NAME(__repeat__);
INIT_PY_NAME(__len__);
INIT_PY_NAME(__of__);
INIT_PY_NAME(__call__);
INIT_PY_NAME(__repr__);
INIT_PY_NAME(__str__);
#undef INIT_PY_NAME
}
static PyObject *
CallMethodO(PyObject *self, PyObject *name,
PyObject *args, PyObject *kw)
{
if(! args && PyErr_Occurred()) return NULL;
UNLESS(name=PyObject_GetAttr(self,name)) return NULL;
ASSIGN(name,PyEval_CallObjectWithKeywords(name,args,kw));
if(args) Py_DECREF(args);
return name;
}
#define Build Py_BuildValue
/* Declarations for objects of type Wrapper */
typedef struct {
PyObject_HEAD
PyObject *obj;
PyObject *container;
} Wrapper;
staticforward PyExtensionClass Wrappertype;
static PyObject *
Wrapper__init__(Wrapper *self, PyObject *args)
{
PyObject *obj, *container;
UNLESS(PyArg_Parse(args,"(OO)",&obj,&container)) return NULL;
Py_INCREF(obj);
Py_INCREF(container);
self->obj=obj;
self->container=container;
Py_INCREF(Py_None);
return Py_None;
}
/* ---------------------------------------------------------------- */
static struct PyMethodDef Wrapper_methods[] = {
{"__init__", (PyCFunction)Wrapper__init__, 0,
"Initialize an Acquirer Wrapper"},
{NULL, NULL} /* sentinel */
};
/* ---------- */
static Wrapper *
newWrapper(PyObject *obj, PyObject *container)
{
Wrapper *self;
UNLESS(self = PyObject_NEW(Wrapper, (PyTypeObject*)&Wrappertype)) return NULL;
Py_INCREF(obj);
Py_INCREF(container);
self->obj=obj;
self->container=container;
return self;
}
static void
Wrapper_dealloc(Wrapper *self)
{
Py_DECREF(self->obj);
Py_DECREF(self->container);
PyMem_DEL(self);
}
static PyObject *
Wrapper_getattro(Wrapper *self, PyObject *oname)
{
PyObject *r;
char *name;
if(self->obj && (r=PyObject_GetAttr(self->obj,oname)))
{
if(r->ob_type==(PyTypeObject*)&Wrappertype)
{
if(r->ob_refcnt==1)
{
Py_INCREF(self);
ASSIGN(((Wrapper*)r)->container,(PyObject*)self);
}
else
ASSIGN(r,(PyObject*)newWrapper(((Wrapper*)r)->obj,
(PyObject*)self));
}
else if(PyECMethod_Check(r) && PyECMethod_Self(r)==self->obj)
ASSIGN(r,PyECMethod_New(r,(PyObject*)self));
else if(has__of__(r))
ASSIGN(r,CallMethodO(r,py__of__,Build("O", self), NULL));
return r;
}
if(self->obj) PyErr_Clear();
name=PyString_AsString(oname);
if(self->container && *name != '_')
{
if(r=PyObject_GetAttr(self->container,oname))
return r;
PyErr_Clear();
}
if(*name++=='a' && *name++=='q' && *name++=='_')
{
if(strcmp(name,"parent")==0)
{
if(self->container) r=self->container;
else r=Py_None;
Py_INCREF(r);
return r;
}
if(strcmp(name,"self")==0)
{
if(self->obj) r=self->obj;
else r=Py_None;
Py_INCREF(r);
return r;
}
}
if(*name++=='_' && strcmp(name,"_init__")==0)
return Py_FindAttr((PyObject*)self,oname);
PyErr_SetObject(PyExc_AttributeError,oname);
return NULL;
}
static int
Wrapper_setattro(Wrapper *self, PyObject *name, PyObject *v)
{
if(v && v->ob_type==(PyTypeObject*)&Wrappertype) v=((Wrapper*)v)->obj;
return PyObject_SetAttr(self->obj, name, v);
}
static int
Wrapper_compare(Wrapper *v, Wrapper *w)
{
return PyObject_Compare(v->obj,w->obj);
}
static PyObject *
Wrapper_repr(Wrapper *self)
{
PyObject *r;
if(r=PyObject_GetAttr((PyObject*)self,py__repr__))
{
ASSIGN(r,PyObject_CallFunction(r,NULL,NULL));
return r;
}
else
{
PyErr_Clear();
return PyObject_Repr(self->obj);
}
}
static PyObject *
Wrapper_str(Wrapper *self)
{
PyObject *r;
if(r=PyObject_GetAttr((PyObject*)self,py__str__))
{
ASSIGN(r,PyObject_CallFunction(r,NULL,NULL));
return r;
}
else
{
PyErr_Clear();
return PyObject_Str(self->obj);
}
}
static long
Wrapper_hash(Wrapper *self)
{
return PyObject_Hash(self->obj);
}
static PyObject *
Wrapper_call(Wrapper *self, PyObject *args, PyObject *kw)
{
Py_INCREF(args);
return CallMethodO((PyObject*)self,py__call__,args,kw);
}
/* Code to handle accessing Wrapper objects as sequence objects */
static int
Wrapper_length(Wrapper *self)
{
long l;
PyObject *r;
UNLESS(r=CallMethodO((PyObject*)self,py__len__,NULL,NULL)) return -1;
l=PyInt_AsLong(r);
Py_DECREF(r);
return l;
}
static PyObject *
Wrapper_concat(Wrapper *self, PyObject *bb)
{
return CallMethodO((PyObject*)self,py__concat__,Build("(O)", bb) ,NULL);
}
static PyObject *
Wrapper_repeat(Wrapper *self, int n)
{
return CallMethodO((PyObject*)self,py__repeat__,Build("(i)", n),NULL);
}
static PyObject *
Wrapper_item(Wrapper *self, int i)
{
return CallMethodO((PyObject*)self,py__getitem__, Build("(i)", i),NULL);
}
static PyObject *
Wrapper_slice(Wrapper *self, int ilow, int ihigh)
{
return CallMethodO((PyObject*)self,py__getslice__,
Build("(ii)", ilow, ihigh),NULL);
}
static int
Wrapper_ass_item(Wrapper *self, int i, PyObject *v)
{
if(v)
{
UNLESS(v=CallMethodO((PyObject*)self,py__setitem__,
Build("(iO)", i, v),NULL))
return -1;
}
else
{
UNLESS(v=CallMethodO((PyObject*)self,py__delitem__,
Build("(iO)", i),NULL))
return -1;
}
Py_DECREF(v);
return 0;
}
static int
Wrapper_ass_slice(Wrapper *self, int ilow, int ihigh, PyObject *v)
{
if(v)
{
UNLESS(v=CallMethodO((PyObject*)self,py__setslice__,
Build("(iiO)", ilow, ihigh, v),NULL))
return -1;
}
else
{
UNLESS(v=CallMethodO((PyObject*)self,py__delslice__,
Build("(ii)", ilow, ihigh),NULL))
return -1;
}
Py_DECREF(v);
return 0;
}
static PySequenceMethods Wrapper_as_sequence = {
(inquiry)Wrapper_length, /*sq_length*/
(binaryfunc)Wrapper_concat, /*sq_concat*/
(intargfunc)Wrapper_repeat, /*sq_repeat*/
(intargfunc)Wrapper_item, /*sq_item*/
(intintargfunc)Wrapper_slice, /*sq_slice*/
(intobjargproc)Wrapper_ass_item, /*sq_ass_item*/
(intintobjargproc)Wrapper_ass_slice, /*sq_ass_slice*/
};
/* -------------------------------------------------------------- */
/* Code to access Wrapper objects as mappings */
static PyObject *
Wrapper_subscript(Wrapper *self, PyObject *key)
{
return CallMethodO((PyObject*)self,py__getitem__,Build("(O)", key),NULL);
}
static int
Wrapper_ass_sub(Wrapper *self, PyObject *key, PyObject *v)
{
if(v)
{
UNLESS(v=CallMethodO((PyObject*)self,py__setitem__,
Build("(OO)", key, v),NULL))
return -1;
}
else
{
UNLESS(v=CallMethodO((PyObject*)self,py__delitem__,
Build("(O)", key),NULL))
return -1;
}
Py_XDECREF(v);
return 0;
}
static PyMappingMethods Wrapper_as_mapping = {
(inquiry)Wrapper_length, /*mp_length*/
(binaryfunc)Wrapper_subscript, /*mp_subscript*/
(objobjargproc)Wrapper_ass_sub, /*mp_ass_subscript*/
};
/* -------------------------------------------------------- */
static char Wrappertype__doc__[] =
""
;
static PyExtensionClass Wrappertype = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"AcquirerWrapper", /*tp_name*/
sizeof(Wrapper), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)Wrapper_dealloc, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)0, /*tp_getattr*/
(setattrfunc)0, /*tp_setattr*/
(cmpfunc)Wrapper_compare, /*tp_compare*/
(reprfunc)Wrapper_repr, /*tp_repr*/
0, /*tp_as_number*/
&Wrapper_as_sequence, /*tp_as_sequence*/
&Wrapper_as_mapping, /*tp_as_mapping*/
(hashfunc)Wrapper_hash, /*tp_hash*/
(ternaryfunc)Wrapper_call, /*tp_call*/
(reprfunc)Wrapper_str, /*tp_str*/
(getattrofunc)Wrapper_getattro, /*tp_getattr with object key*/
(setattrofunc)Wrapper_setattro, /*tp_setattr with object key*/
/* Space for future expansion */
0L,0L,
Wrappertype__doc__, /* Documentation string */
METHOD_CHAIN(Wrapper_methods),
EXTENSIONCLASS_BINDABLE_FLAG,
};
/* End of code for Wrapper objects */
/* -------------------------------------------------------- */
/* List of methods defined in the module */
static struct PyMethodDef Wrap_methods[] = {
{NULL, NULL} /* sentinel */
};
/* Initialization function for the module (*must* be called initWrap) */
static char Wrap_module_documentation[] =
""
;
static PyObject *
acquire_of(PyObject *self, PyObject *args)
{
PyObject *inst;
UNLESS(PyArg_Parse(args,"O",&inst)) return NULL;
UNLESS(PyExtensionInstance_Check(inst))
{
PyErr_SetString(PyExc_TypeError,
"attempt to wrap extension method using an object that\n"
"is not an extension class instance.");
return NULL;
}
return (PyObject*)newWrapper(self,args);
}
struct PyMethodDef Acquirer_methods[] = {
{"__of__",(PyCFunction)acquire_of,0,""},
{NULL, NULL} /* sentinel */
};
static struct PyMethodDef methods[] = {{NULL, NULL}};
void
initAcquisition()
{
PyObject *m, *d;
PURE_MIXIN_CLASS(Acquirer,
"Base class for objects that acquire attributes from containers\n"
, Acquirer_methods)
/* Create the module and add the functions */
m = Py_InitModule4("Acquisition", methods,
"",
(PyObject*)NULL,PYTHON_API_VERSION);
d = PyModule_GetDict(m);
init_py_names();
PyExtensionClass_Export(d,"Acquirer",AcquirerType);
PyExtensionClass_Export(d,"Wrapper",Wrappertype);
CHECK_FOR_ERRORS("can't initialize module Acquisition");
}
#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()
{
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",
(PyObject*)NULL,PYTHON_API_VERSION);
d = PyModule_GetDict(m);
PyExtensionClass_Export(d,"Method",MethodType);
/* Check for errors */
CHECK_FOR_ERRORS("can't initialize module MethodObject");
}
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