Create Extension Types

  1. A Dive-in Example
  2. Defining A New Extension Type
  3. Adding Instance Members to Types
  4. Adding Class Members to Types
  5. Adding Methods to Types
  6. Adding Special Methods
  7. Adding Properties
  8. Generate and Test the Example
  9. Create Base Extension Types
  10. Create Numeric Types
  11. Create Mapping Types
  12. Create Sequence Types
  13. Create Iterator Types
  14. Static array dimension
  15. Exposing nested C struct
  16. Writing your __init__ function
  17. Warnings on Py_INCREF
  18. Conclusion

A Dive-in Example

In the example below, the module only has a class extension:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
"""My 3rd module with expy-cxpy.
@head:
#define VERSION "0.1.0"
""" 
#File name: module3.py
from expy import * 
class mate(public):
   """mate: a class for a buddy."""
   _age = ifield(int, acc=private, doc='the age')
   name = ifield(str, flag='RO', doc='the name')
   version = cfield(rawtype(str, 'VERSION')) #class field/member

   @imethod(void)
   def rename(me, name=str):
      """change the name."""
      return """{
   if(me->name) free(me->name);
   int nn = strlen(name)+1;
   me->name = malloc(sizeof(char)*nn);
   if(me->name) strcpy(me->name, name);
   else PyErr_NoMemory(); 
}"""

   def get_age(me, closure=rawtype('void*')):
      return """{
   return Py_BuildValue("l", me->_age);//Py_INCREF
}"""

   @throws('!PyInt_CheckExact(v)', TypeError, "Not an Integer!")
   @throws('PyInt_AsLong(v)<=0', ValueError, "Not Positive!")
   def set_age(me, v, closure=rawtype('void*')):
      return """{
   int age = PyInt_AsLong(v);
   me->_age = age;
   return 0; //success
}"""

   age = property(get_age, set_age, doc="the age.")

@function(Int)
def refcount(a):
   return 'a->ob_refcnt'

#register the type yourself.
from expy import _typeinfo_
_typeinfo_(mate, 'module3_mate*').register()

The explanation of this example follows.

Defining A New Extension Type

Line 6 start the definition of your extension type: the main thing you should notice is that it extends public, which is a class defined in the expy module. This basically says that this class should be totally visible in Python.

Adding Instance Members to Types

Line 8-9 declares two instance members, by using the ifield (i.e.: instance field) class provided in the expy module. You provide the type, the access (such as public or private), the document string, and you can also provide the flag (must be a string), which corresponds to the flags field of the PyMemberDef structure in Python C API. In Line 8, the _age member is of ‘int’ type, and it is a private member. In Line 9, the name member is of ‘str’ type (which in C it corresponds to ‘char*’), and is ‘readonly’, as indicated by the “RO” flag. The following flag constants are defined in structmember.h; they may be combined using bitwise-OR.

Constant Meaning
READONLY Never writable.
RO Shorthand for READONLY.
READ_RESTRICTED Not readable in restricted mode.
WRITE_RESTRICTED Not writable in restricted mode.
RESTRICTED Not readable or writable in restricted mode.

An interesting advantage of using the ifield() to build fields/members at runtime is that any attribute defined this way can have an associated doc string simply by providing the text in the constructor. An application can use the introspection API to retrieve the descriptor from the class object, and get the doc string using its __doc__ attribute. One more good thing about instance members:

The order in which these fields appear will be
preserved in the generated object structure.

Adding Class Members to Types

Class members/attributes of extension types are read only, thus they are ideal for providing constants/data to users. Line 10 defines a class member/field, which is the version of the class. Instead of using an ‘ifield’ constructor, you use a ‘cfield’ (class field) constructor, which takes only one parameter, namely, the value you want to store. This parameter is specified in the same way as default values to your function arguments. This class field is accessed by mate.version, once you have built and imported the module.

Adding Methods to Types

Lines 19-29 defines a method named “rename” for the extension type. In Line 19, we use the decorator imethod to declare that this is an “instance method”, whose return type is ‘void’. The first argument will be the instance, the next argument is the new name, of type ‘str’ (which translated in C is ‘char*’). It is very similar to how we defined functions for modules. The name mangling is done by appending the class name to the function name, connected by an underscore ‘_’.

Adding Special Methods

Lines 12-17 defines a special method “__init__”, which initializes an instance. Those special methods does not need any decoration, they are automatically detected, because their return type, access type, etc, are known in advance. Similar to other methods, you can use @throws decorators. Unlike other special methods, you can specify arbitrary set of arguments to the __init__ method, just like you do with pure python classes. Other special methods (in the object protocal) that are recognized – more can be added if anything is missing:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
special_methods={
#returns: int, with 0:success, -1:failure
'__init__':{'cat':'imethod', 'ret':int, 'acc':public},
#signature: def __new__(cls, args, kwds):
'__new__':{'cat':'cmethod', 'ret':object, 'acc':public, 'nowrap':True},
#signature: def __del__(me):
'__del__':{'cat':'imethod', 'ret':void, 'acc':public, 'nowrap':True},
#signature: def __call__(cls, args, kwds):
'__call__':{'cat':'imethod', 'ret':object, 'acc':public, 'nowrap':True},
#signature: def __cmp__(me, u[=lambda:mytype]): [optional]
'__cmp__':{'cat':'imethod', 'ret':int, 'acc':public, 'nowrap':True},
#signature: def __getattr__(me, name=str):
'__getattr__':{'cat':'imethod', 'ret':object, 'acc':public, 'nowrap':True},
#signature: def __setattr__(me, name=str, attr=object):
'__setattr__':{'cat':'imethod', 'ret':int, 'acc':public, 'nowrap':True},
#signature: def __getattro__(me, name):
'__getattro__':{'cat':'imethod', 'ret':object, 'acc':public, 'nowrap':True},
#signature: def __setattro__(me, name, attr):
'__setattro__':{'cat':'imethod', 'ret':int, 'acc':public, 'nowrap':True},
#signature: def __hash__(me):
'__hash__':{'cat':'imethod', 'ret':long, 'acc':public, 'nowrap':True},
#signature: def __print__(me, f=raw_type('FILE*'), flags=int):
'__print__':{'cat':'imethod', 'ret':int, 'acc':public, 'nowrap':True},
#signature: def __repr__(me):
'__repr__':{'cat':'imethod', 'ret':object, 'acc':public, 'nowrap':True},
#signature: def __str__(me):
'__str__':{'cat':'imethod', 'ret':object, 'acc':public, 'nowrap':True},
#signature: def __iter__(me):
'__iter__':{'cat':'imethod', 'ret':object, 'acc':public, 'nowrap':True},
#signature: def __next__(me):
'__next__':{'cat':'imethod', 'ret':object, 'acc':public, 'nowrap':True},
}

You may refer to the Python C API to learn more about those special methods, and how to implement them.

Adding Properties

Lines 31-51 defines a property named ‘age’. Tow functions ‘get_age’ and ‘set_age’ are defined, without decoration. They must conform to that signature so that the generated C function is a well defined getter and setter function for a property. Similar to other methods, you can use @throws decorators. The main feature provided here is to throw an exception if the age argument is of the wrong type or value.

Generate and Test the Example

Now we use the following setup code to generate and compile this module.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# File: module3_setup.py
from distutils.core import setup, Extension
import sys #To find the 'expy' and 'cxpy'
sys.path.insert(1, '../src')
import cxpy, expy_module3 as modu
cxpy.generate(modu) #generate the C code
# Now, finally, define that module!
modext = Extension( modu.__name__, 
   sources = [modu.__name__+'.c'])
setup(name = modu.__name__, version = '0.1.0',
      description = 'My third module.',
      ext_modules = [modext])

And here is the generated code with the header file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//@head:begin

#define VERSION "0.1.0"

//@head:end

#ifndef Py_TYPE  //Python 2.5
#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
#endif

/*--- class mate ---*/

typedef struct{
  PyObject_HEAD
  int _age;
  char* name;
} mate;

static PyObject* module3_refcount_wrap(
   PyObject* _NOUSE,
   PyObject* _args_
);
void mate_rename(mate* me, const char* name);
static PyObject* mate_rename_wrap(
   mate* me,
   PyObject* _args_
);
int mate_set_age(mate* me, PyObject* v, void* closure);
static int mate_set_age_wrap(mate* me, PyObject* v, void* closure);
static PyObject* mate_get_age(mate* me, void* closure);
#define mate_Check(op) PyObject_TypeCheck(op, &mateType)
#define mate_CheckExact(op) (Py_TYPE(op)==&mateType)
#define mate_NEW() PyObject_NEW(mate, &mateType) //NEW reference!
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#include <Python.h>
#include "structmember.h"
#include "module3.h"
static PyTypeObject mateType;

/**======== MODULE IMPLEMENTATION ========**/

static PyObject* module3_refcount_wrap(
   PyObject* _NOUSE,
   PyObject* _args_
){
    PyObject* a;
    //no keywords support

    if(!PyArg_ParseTuple(
       _args_, "O"
       , &a)) return 0;

    /*****Argument Exception Handling*****/

    unsigned int cxpyret = PyErr_Occurred()?(unsigned int)0:
                 a->ob_refcnt;

    /*****Return Value Exception Handling*****/

    if(PyErr_Occurred()) return 0;
    return Py_BuildValue("I", cxpyret);
}

/*--- class mate ---*/
void mate_rename(mate* me, const char* name){
   if(me->name) free(me->name);
   int nn = strlen(name)+1;
   me->name = malloc(sizeof(char)*nn);
   if(me->name) strcpy(me->name, name);
   else PyErr_NoMemory(); 
}
static PyObject* mate_rename_wrap(
   mate* me,
   PyObject* _args_
){
    const char* name;
    //no keywords support

    if(!PyArg_ParseTuple(
       _args_, "s"
       , &name)) return 0;

    /*****Argument Exception Handling*****/

    if(!PyErr_Occurred()) mate_rename(me, name);

    /*****Return Value Exception Handling*****/

    if(PyErr_Occurred()) return 0;
    return Py_BuildValue("");
}

int mate_set_age(mate* me, PyObject* v, void* closure){
   int age = PyInt_AsLong(v);
   me->_age = age;
   return 0; //success
}
static int mate_set_age_wrap(mate* me, PyObject* v, void* closure){

    /*****Argument Exception Handling*****/

    if(!PyErr_Occurred() && (!PyInt_CheckExact(v))){ // throws
       PyErr_SetString(PyExc_TypeError, "Not an Integer!");
    }

    if(!PyErr_Occurred() && (PyInt_AsLong(v)<=0)){ // throws
       PyErr_SetString(PyExc_ValueError, "Not Positive!");
    }

    int cxpyret = PyErr_Occurred()?(int)1:
                 mate_set_age(me, v, closure);

    /*****Return Value Exception Handling*****/

    return cxpyret; //don't need PyObject*!
}
static PyObject* mate_get_age(mate* me, void* closure){
   return Py_BuildValue("l", me->_age);//Py_INCREF
}
static struct PyMethodDef mateMeths[] = {
{  "rename", 
  (PyCFunction)mate_rename_wrap, 
  METH_VARARGS,
  "change the name."
},
{NULL}           /* sentinel */ };

static PyMemberDef mateFields[] = {
{"name", T_STRING, offsetof(mate, name), RO,
"the name"},
{NULL}  /* Sentinel */
};

/**Type mate is not numeric.**/

/**Type mate is not mapping.**/

/**Type mate is not sequence.**/


static PyGetSetDef mateProps[] = {
{"age", (getter)mate_get_age, (setter)mate_set_age_wrap,
      "the age.", NULL},
    {NULL}  /* Sentinel */
};


static PyTypeObject mateType = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "module3.mate",       /*tp_name*/
    sizeof(mate),          /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor)0,       /*tp_dealloc*/
    (printfunc)0,      /*tp_print*/
    (getattrfunc)0,  /*tp_getattr*/
    (setattrfunc)0,  /*tp_setattr*/
    (cmpfunc)0,          /*tp_compare*/
    (reprfunc)0,        /*tp_repr*/
    0,                  /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    (hashfunc)0,        /*tp_hash */
    (ternaryfunc)0,     /*tp_call*/
    (reprfunc)0,         /*tp_str*/
    (getattrofunc)0,/*tp_getattro*/
    (setattrofunc)0,/*tp_setattro*/
    0,                  /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT,                  /*tp_flags*/
    "mate: a class for a buddy.",                  /* tp_doc */
    (traverseproc)0,	       /* tp_traverse */
    (inquiry)0,		       /* tp_clear */
    (richcmpfunc)0,	 /* tp_richcompare */
    0,		               /* tp_weaklistoffset */
    (getiterfunc)0,        /* tp_iter */
    (iternextfunc)0,	  /* tp_iternext */
    mateMeths,                  /* tp_methods */
    mateFields,                  /* tp_members */
    mateProps,                         /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    (descrgetfunc)0,       /* tp_descr_get */
    (descrsetfunc)0,       /* tp_descr_set */
    0,                         /* tp_dictoffset */
    (initproc)0,        /* tp_init */
    0,                         /* tp_alloc */
    0,                   /* tp_new */
};/*http://docs.python.org/c-api/typeobj.html*/

static struct PyMethodDef module3Funcs[] = {
{  "refcount", 
  (PyCFunction)module3_refcount_wrap, 
  METH_VARARGS,
  ""
},
{NULL}           /* sentinel */ };


PyMODINIT_FUNC 
initmodule3(void) { //must be: init<module>
  PyObject *mod;
  
    //Initialize the type table for "mate".
    if(mateType.tp_new == 0) 
         mateType.tp_new = PyType_GenericNew;
    if (PyType_Ready(&mateType) < 0) return;

  mod = Py_InitModule3("module3", module3Funcs, 
     "My 3rd module with expy-cxpy.");
  if (mod==0) return;
  
    Py_INCREF(&mateType);
    PyModule_AddObject(mod, "mate", (PyObject *)&mateType);
    /* class fields for class mate: */

  PyDict_SetItemString(mateType.tp_dict, "version", 
     Py_BuildValue("s",VERSION));
  /*End class mate.*/

  /* Custom Exceptions */
  /* Global Members */

}

Note that for each extention type you create, you will get three macros for free: <type>_Check, <type>_CheckExact, and <type>_NEW. Start a Python session and test what we have got here:

ylan@montop:~/expy/Doc$ python module3_setup.py build
ylan@montop:~/expy/Doc$ cd build/lib.linux-i686-2.5/
ylan@montop:~/expy/Doc/build/lib.linux-i686-2.5$ python
Python 2.5.2 (r252:60911, Jul 31 2008, 17:28:52)
[GCC 4.2.3 (Ubuntu 4.2.3-2ubuntu7)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from module3 import mate
>>> x = mate('John', 23)
>>> x.name
'John'
>>> x.age
23
>>> x.age = -23
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Must be Positive
>>> x.age = x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Must be an Integer
>>> x.age
23
>>> x.rename('Job')
>>> x.name
'Job'

Create Base Extension Types

If you have implemented your methods carefully, you can declare your extension types as a base type, so that somebody can extend it in pure Python. To declare that your type can serve as a base type, you simply do this:

class mybase(public, basetype):
   """this extension type can be a base type."""
   def ameth(me, you):
      ...

For more information on how to implement a base type, please refer to Python C API.

Create Numeric Types

Python allows one to create types/classes that can act like numbers, by implementing the numeric protocol. You can create a numeric extension type in similar ways as you create a pure python numeric type with expy-cxpy. There are, however, two distinct categories of numeric extension types in Python C API. Category 1: the special numeric type (by indicating that the second argument is also of the same type, see example below), whose numeric operations are only defined between an object of the defining type and an object of the same type; Category 2: the generic numeric type, whose numeric operations are defined between an object of the defining type and an arbitrary object (so the second argument is of the default ‘object’ type)). When you implement your numeric type, don’t mix up these two categories, otherwise EXPY will complain.

The following example is for Category 1. A new extension type ‘counter’ is created, it has an integer number, and a name. We only defined the __add__ operator, when two counters added together, not only their numbers are added, but also their names are merged.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
"""Numeric extension type (Cat 1) with expy-cxpy."""
#File name: module4.py
from expy import * 

class counter(public):
   """counter: a simple numeric class."""
   num = ifield(int, doc='the counter')
   name = ifield(str, flag='RO', doc='the name')

   @throws(False, code="if($ret==-1) PyErr_NoMemory();")
   def __init__(me, name=str, num=0):
      return """{
   me->num = num;
   num = strlen(name)+1; 
   me->name = malloc(sizeof(char)*num);
   if (!me->name) return -1; //fail
   strcpy(me->name, name);
   return 0; //success! (-1 for failure)
}"""

   @throws('!counter_CheckExact(u)', TypeError, 
	"Second operand is not a counter!")
   def __add__(me, u=lambda:counter):
      """add counters, and merge names."""
      return """{
   counter* sum = counter_NEW();//Py_INCREF
   sum->num = me->num + u->num;
   int lm = strlen(me->name);
   sum->name = malloc(sizeof(char)*(lm+strlen(u->name)+2));
   if(!sum->name){ 
      Py_DECREF(sum);
      return 0; //no memory
   }
   strcpy(sum->name, me->name);
   strcpy(sum->name+lm, "&");
   strcpy(sum->name+lm+1, u->name);
   return (PyObject*)sum;
}"""

To indicate that the second argument to __add__ function must be of the same ‘counter’ type, we use ‘lambda:counter’ to specify its type. It might be more appealing to simply use something like ‘__add__(me, u=counter)’, but since ‘counter’ is not defined yet, Python compiler would complain about this and abort, so the ‘lambda’ function is used here to delay the evaluation till code generation. To enforce the type restriction, we can add a @throws decorator to check on its actual type. Here is the setup script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# File: module4_setup.py
from distutils.core import setup, Extension
import sys #To find the 'expy' and 'cxpy'
sys.path.insert(1, '../src')
import cxpy, expy_module4 as modu
cxpy.generate(modu) #generate the C code
# Now, finally, define that module!
modext = Extension( modu.__name__, 
   sources = [modu.__name__+'.c'])
setup(name = modu.__name__, version = '0.1.0',
      description = 'My third module.',
      ext_modules = [modext])

And the generated C code is also included for your information:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/*--- class counter ---*/

typedef struct{
  PyObject_HEAD
  int num;
  char* name;
} counter;

int counter___init__(counter* me, const char* name, int num);
static int counter___init___wrap(
   counter* me,
   PyObject* _args_,
   PyObject* _kwds_
);
PyObject* counter___add__(counter* me, counter* u);
static PyObject* counter___add___wrap(counter* me, counter* u);
#define counter_Check(op) PyObject_TypeCheck(op, &counterType)
#define counter_CheckExact(op) (Py_TYPE(op)==&counterType)
#define counter_NEW() PyObject_NEW(counter, &counterType) //NEW reference!
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#include <Python.h>
#include "structmember.h"
#include "module4.h"
static PyTypeObject counterType;

/**======== MODULE IMPLEMENTATION ========**/

/*--- class counter ---*/
int counter___init__(counter* me, const char* name, int num){
   me->num = num;
   num = strlen(name)+1; 
   me->name = malloc(sizeof(char)*num);
   if (!me->name) return -1; //fail
   strcpy(me->name, name);
   return 0; //success! (-1 for failure)
}
static int counter___init___wrap(
   counter* me,
   PyObject* _args_,
   PyObject* _kwds_
){
    const char* name;
    int num=0;
    static char* _pctrl[] ={"name", "num",  0};

    if(!PyArg_ParseTupleAndKeywords(
       _args_, _kwds_, "s|i", 
       _pctrl, &name, &num)) return -1;

    /*****Argument Exception Handling*****/

    int cxpyret = PyErr_Occurred()?(int)-1:
                 counter___init__(me, name, num);

    /*****Return Value Exception Handling*****/

    if(!PyErr_Occurred()){
if(cxpyret==-1) PyErr_NoMemory();
    }

    return cxpyret; //don't need PyObject*!
}
PyObject* counter___add__(counter* me, counter* u){
   counter* sum = counter_NEW();//Py_INCREF
   sum->num = me->num + u->num;
   int lm = strlen(me->name);
   sum->name = malloc(sizeof(char)*(lm+strlen(u->name)+2));
   if(!sum->name){ 
      Py_DECREF(sum);
      return 0; //no memory
   }
   strcpy(sum->name, me->name);
   strcpy(sum->name+lm, "&");
   strcpy(sum->name+lm+1, u->name);
   return (PyObject*)sum;
}
static PyObject* counter___add___wrap(counter* me, counter* u){

    /*****Argument Exception Handling*****/

    if(!PyErr_Occurred() && (!counter_CheckExact(u))){ // throws
       PyErr_SetString(PyExc_TypeError, "Second operand is not a counter!");
    }

    PyObject* cxpyret = PyErr_Occurred()?(PyObject*)0:
                 counter___add__(me, u);

    /*****Return Value Exception Handling*****/

    if(PyErr_Occurred()) return 0;
    //Direct return: had Py_INCREF???
    return cxpyret;
}

static struct PyMethodDef counterMeths[] = {
{NULL}           /* sentinel */ };

static PyMemberDef counterFields[] = {
{"num", T_INT, offsetof(counter, num), 0,
"the counter"},
{"name", T_STRING, offsetof(counter, name), RO,
"the name"},
{NULL}  /* Sentinel */
};


static PyNumberMethods counterAsNum = {
        (binaryfunc)counter___add___wrap,       /*nb_add*/
        (binaryfunc)0,       /*nb_subtract*/
        (binaryfunc)0,       /*nb_multiply*/
        (binaryfunc)0,       /*nb_divide*/
        (binaryfunc)0,       /*nb_remainder*/
        (binaryfunc)0,    /*nb_divmod*/
        (ternaryfunc)0,      /*nb_power*/
        (unaryfunc)0,        /*nb_negative*/
        (unaryfunc)0,        /*nb_positive*/
        (unaryfunc)0,        /*nb_absolute*/
        (inquiry)0,      /*nb_nonzero*/
        (unaryfunc)0,     /*nb_invert*/
        (binaryfunc)0,    /*nb_lshift*/
        (binaryfunc)0,    /*nb_rshift*/
        (binaryfunc)0,       /*nb_and*/
        (binaryfunc)0,       /*nb_xor*/
        (binaryfunc)0,        /*nb_or*/
        (coercion)0,      /*nb_coerce*/
        (unaryfunc)0,        /*nb_int*/
        (unaryfunc)0,       /*nb_long*/
        (unaryfunc)0,      /*nb_float*/
        (unaryfunc)0,        /*nb_oct*/
        (unaryfunc)0,        /*nb_hex*/

     /* Added in release 2.0 */
     (binaryfunc)0,        /*nb_inplace_add*/
     (binaryfunc)0,        /* nb_inplace_subtract */
     (binaryfunc)0,        /* nb_inplace_multiply */
     (binaryfunc)0,        /* nb_inplace_divide */
     (binaryfunc)0,        /* nb_inplace_remainder */
     (ternaryfunc)0,        /*nb_inplace_power */
     (binaryfunc)0,        /* nb_inplace_lshift */
     (binaryfunc)0,        /* nb_inplace_rshift */
     (binaryfunc)0,        /* nb_inplace_and */
     (binaryfunc)0,        /* nb_inplace_xor */
     (binaryfunc)0,        /* nb_inplace_or */

     /* Added in release 2.2 */
     (binaryfunc)0,        /* nb_floor_divide */
     (binaryfunc)0,        /* nb_true_divide */
     (binaryfunc)0,        /* nb_inplace_floor_divide */
     (binaryfunc)0,        /* nb_inplace_true_divide */

     /* Added in release 2.5 */
     (unaryfunc)0,         /* nb_index */
};

/**Type counter is not mapping.**/

/**Type counter is not sequence.**/

/* NO PROPS: counter */


static PyTypeObject counterType = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "module4.counter",       /*tp_name*/
    sizeof(counter),          /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor)0,       /*tp_dealloc*/
    (printfunc)0,      /*tp_print*/
    (getattrfunc)0,  /*tp_getattr*/
    (setattrfunc)0,  /*tp_setattr*/
    (cmpfunc)0,          /*tp_compare*/
    (reprfunc)0,        /*tp_repr*/
    &counterAsNum,                  /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    (hashfunc)0,        /*tp_hash */
    (ternaryfunc)0,     /*tp_call*/
    (reprfunc)0,         /*tp_str*/
    (getattrofunc)0,/*tp_getattro*/
    (setattrofunc)0,/*tp_setattro*/
    0,                  /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT,                  /*tp_flags*/
    "counter: a simple numeric class.",                  /* tp_doc */
    (traverseproc)0,	       /* tp_traverse */
    (inquiry)0,		       /* tp_clear */
    (richcmpfunc)0,	 /* tp_richcompare */
    0,		               /* tp_weaklistoffset */
    (getiterfunc)0,        /* tp_iter */
    (iternextfunc)0,	  /* tp_iternext */
    counterMeths,                  /* tp_methods */
    counterFields,                  /* tp_members */
    0,                         /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    (descrgetfunc)0,       /* tp_descr_get */
    (descrsetfunc)0,       /* tp_descr_set */
    0,                         /* tp_dictoffset */
    (initproc)counter___init___wrap,        /* tp_init */
    0,                         /* tp_alloc */
    0,                   /* tp_new */
};/*http://docs.python.org/c-api/typeobj.html*/

static struct PyMethodDef module4Funcs[] = {
{NULL}           /* sentinel */ };


PyMODINIT_FUNC 
initmodule4(void) { //must be: init<module>
  PyObject *mod;
  
    //Initialize the type table for "counter".
    if(counterType.tp_new == 0) 
         counterType.tp_new = PyType_GenericNew;
    if (PyType_Ready(&counterType) < 0) return;

  mod = Py_InitModule3("module4", module4Funcs, 
     "Numeric extension type (Cat 1) with expy-cxpy.");
  if (mod==0) return;
  
    Py_INCREF(&counterType);
    PyModule_AddObject(mod, "counter", (PyObject *)&counterType);
    /* class fields for class counter: */
  /*End class counter.*/

  /* Custom Exceptions */
  /* Global Members */

}

And a testing session is invoked to illustrate the result:

ylan@montop:~/expy/Doc/build/lib.linux-i686-2.5$ python
>>> from module4 import *
>>> a = counter('VA', 20)
>>> b = counter('MD', 14)
>>> c = a+b
>>> c.name, c.num
('VA&MD', 34)

Defining numeric extension of Category 2 is similar, only that the second operand will be of PyObject type, so the prototype would be something like this:

def __add__(me, u):
   """The C prototype would be:
static PyObject* counter___add__(counter* me, PyObject* u);"""

And before you work on the argument ‘u’, you usually do type checking to ensure that it is of the right type. As there are quite a number of numeric methods, enlisting all of them here would be boring. Please refer to the cxpy.py source code, where you shall find all of them.

Create Mapping Types

You can also implement the mapping protocol for your extension types. Below is a simple example, where our mapping type only help access some static integer array.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
"""Mapping extension type with expy-cxpy.
@body:
#define MEMLEN 30 
static int mapst[MEMLEN];
"""
#File name: module5.py
from expy import * 
class statmap(public):
   """A Map type to access static array data."""

   def __maplen__(me):
      """The length of the map."""
      return """{ return MEMLEN; }"""

   @throws('!PyInt_Check(key)', TypeError, 'key must be an integer')
   def __mapget__(me, key):
      return """{
   int i = PyInt_AsLong(key);
   if (i<0) i += MEMLEN;
   if(i >= MEMLEN || i <0){
      PyErr_SetString(PyExc_ValueError, "Index out of Range");
      return NULL; //failed
   }
   return PyInt_FromLong(mapst[i]); //implicit Py_INCREF
}"""

   def __mapset__(me, key, v):
      return """{
   if(!PyInt_Check(key)){ 
      PyErr_SetString(PyExc_TypeError, "Key must be an Integer");
      return 1; //failed
   }
   if(!PyInt_Check(v)){ 
      PyErr_SetString(PyExc_TypeError, "Must be an Integer");
      return 1; //failed
   }
   int i = PyInt_AsLong(key);
   if (i<0) i += MEMLEN;
   if(i >= MEMLEN || i <0){
      PyErr_SetString(PyExc_ValueError, "Index out of Range");
      return 1; //fail
   }
   mapst[i] = PyInt_AsLong(v);
   return 0; //good
}"""

Note that the three special method names and the signature, which are different from the way you define a pure python mapping type. This is to avoid name conflicts with the sequence protocal (next). And here is the generated C code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
/*--- class statmap ---*/

typedef struct{
  PyObject_HEAD
} statmap;

static int statmap___mapset__(statmap* me, PyObject* key, PyObject* v);
PyObject* statmap___mapget__(statmap* me, PyObject* key);
static PyObject* statmap___mapget___wrap(statmap* me, PyObject* key);
static int statmap___maplen__(statmap* me);
#define statmap_Check(op) PyObject_TypeCheck(op, &statmapType)
#define statmap_CheckExact(op) (Py_TYPE(op)==&statmapType)
#define statmap_NEW() PyObject_NEW(statmap, &statmapType) //NEW reference!
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#include <Python.h>
#include "structmember.h"
#include "module5.h"
static PyTypeObject statmapType;

/**======== MODULE IMPLEMENTATION ========**/

//@body:begin

#define MEMLEN 30 
static int mapst[MEMLEN];

//@body:end
/*--- class statmap ---*/
static int statmap___mapset__(statmap* me, PyObject* key, PyObject* v){
   if(!PyInt_Check(key)){ 
      PyErr_SetString(PyExc_TypeError, "Key must be an Integer");
      return 1; //failed
   }
   if(!PyInt_Check(v)){ 
      PyErr_SetString(PyExc_TypeError, "Must be an Integer");
      return 1; //failed
   }
   int i = PyInt_AsLong(key);
   if (i<0) i += MEMLEN;
   if(i >= MEMLEN || i <0){
      PyErr_SetString(PyExc_ValueError, "Index out of Range");
      return 1; //fail
   }
   mapst[i] = PyInt_AsLong(v);
   return 0; //good
}
PyObject* statmap___mapget__(statmap* me, PyObject* key){
   int i = PyInt_AsLong(key);
   if (i<0) i += MEMLEN;
   if(i >= MEMLEN || i <0){
      PyErr_SetString(PyExc_ValueError, "Index out of Range");
      return NULL; //failed
   }
   return PyInt_FromLong(mapst[i]); //implicit Py_INCREF
}
static PyObject* statmap___mapget___wrap(statmap* me, PyObject* key){

    /*****Argument Exception Handling*****/

    if(!PyErr_Occurred() && (!PyInt_Check(key))){ // throws
       PyErr_SetString(PyExc_TypeError, "key must be an integer");
    }

    PyObject* cxpyret = PyErr_Occurred()?(PyObject*)0:
                 statmap___mapget__(me, key);

    /*****Return Value Exception Handling*****/

    if(PyErr_Occurred()) return 0;
    //Direct return: had Py_INCREF???
    return cxpyret;
}

static int statmap___maplen__(statmap* me){ return MEMLEN; }
static struct PyMethodDef statmapMeths[] = {
{NULL}           /* sentinel */ };

static PyMemberDef statmapFields[]={{0}};

/**Type statmap is not numeric.**/


static PyMappingMethods statmapAsMap = {
        (lenfunc)statmap___maplen__,               /*mp_length*/
        (binaryfunc)statmap___mapget___wrap,        /*mp_subscript*/
        (objobjargproc)statmap___mapset__,     /*mp_ass_subscript*/
};/*http://docs.python.org/c-api/typeobj.html#PyMappingMethods*/

/**Type statmap is not sequence.**/

/* NO PROPS: statmap */


static PyTypeObject statmapType = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "module5.statmap",       /*tp_name*/
    sizeof(statmap),          /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor)0,       /*tp_dealloc*/
    (printfunc)0,      /*tp_print*/
    (getattrfunc)0,  /*tp_getattr*/
    (setattrfunc)0,  /*tp_setattr*/
    (cmpfunc)0,          /*tp_compare*/
    (reprfunc)0,        /*tp_repr*/
    0,                  /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    &statmapAsMap,                         /*tp_as_mapping*/
    (hashfunc)0,        /*tp_hash */
    (ternaryfunc)0,     /*tp_call*/
    (reprfunc)0,         /*tp_str*/
    (getattrofunc)0,/*tp_getattro*/
    (setattrofunc)0,/*tp_setattro*/
    0,                  /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT,                  /*tp_flags*/
    "A Map type to access static array data.",                  /* tp_doc */
    (traverseproc)0,	       /* tp_traverse */
    (inquiry)0,		       /* tp_clear */
    (richcmpfunc)0,	 /* tp_richcompare */
    0,		               /* tp_weaklistoffset */
    (getiterfunc)0,        /* tp_iter */
    (iternextfunc)0,	  /* tp_iternext */
    statmapMeths,                  /* tp_methods */
    statmapFields,                  /* tp_members */
    0,                         /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    (descrgetfunc)0,       /* tp_descr_get */
    (descrsetfunc)0,       /* tp_descr_set */
    0,                         /* tp_dictoffset */
    (initproc)0,        /* tp_init */
    0,                         /* tp_alloc */
    0,                   /* tp_new */
};/*http://docs.python.org/c-api/typeobj.html*/

static struct PyMethodDef module5Funcs[] = {
{NULL}           /* sentinel */ };


PyMODINIT_FUNC 
initmodule5(void) { //must be: init<module>
  PyObject *mod;
  
    //Initialize the type table for "statmap".
    if(statmapType.tp_new == 0) 
         statmapType.tp_new = PyType_GenericNew;
    if (PyType_Ready(&statmapType) < 0) return;

  mod = Py_InitModule3("module5", module5Funcs, 
     "Mapping extension type with expy-cxpy.");
  if (mod==0) return;
  
    Py_INCREF(&statmapType);
    PyModule_AddObject(mod, "statmap", (PyObject *)&statmapType);
    /* class fields for class statmap: */
  /*End class statmap.*/

  /* Custom Exceptions */
  /* Global Members */

}

The testing of this is left for the reader. Again, for a full list of all related special methods, you are encouraged to take a look at the cxpy.py source file.

Create Sequence Types

To implement the sequence protocol, simply define those special methods for the sequence protocol.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
"""Sequence extension type with expy-cxpy."""
#File name: module6.py
from expy import * 

class strbuf(public):
   """A Sequence type to simulate StringBuffer."""
   size = ifield(int)
   leng = ifield(int)
   buff = ifield(str, flag='RO')

   def __init__(me, size=int):
      return """{
   me->size = size;
   me->leng = 0;
   me->buff = malloc(sizeof(char)*(size+1));
   if(!me->buff) return -1; //fail
   me->buff[me->leng] = 0;
   return 0;
}"""

   def __len__(me):
      """The length of the buffer."""
      return """{ return me->leng; }"""

   def __getitem__(me, i=int):
      return """{
   if (i<0) i += me->leng;
   if(i >= me->leng || i<0){
      PyErr_SetString(PyExc_ValueError, "Index out of Range");
      return 0;
   }
   //implicit Py_INCREF:
   return PyString_FromStringAndSize(me->buff+i,1); 
}"""

   def __setitem__(me, i=int, v=object):
      return """{
   if (i<0) i += me->leng;
   if(i >= me->leng || i<0){
      PyErr_SetString(PyExc_ValueError, "Index out of Range");
      return 1; //failed
   }
   if(!PyString_Check(v) || PyString_Size(v)!=1){ 
      PyErr_SetString(PyExc_TypeError, "Must be a String of size 1.");
      return 1; //failed
   }
   me->buff[i] = PyString_AsString(v)[0];
   return 0;
}"""

   @imethod(int)
   def enlarge(me, size=0):
      """enlarge to the given size, or double the capacity,
whichever is the bigger."""
      return """{
   if(size < me->size*2 +1) 
      size = 1 + me->size * 2;
   char* buff = malloc(sizeof(char)*size);
   if(!buff) return -1; //fail
   strcpy(buff, me->buff);
   free(me->buff);
   me->buff = buff;
   me->size = size;
   return size;
}"""

   def __concat__(me, u):
      return """{
   if(!PyString_Check(u) && !strbuf_Check(u))
     return NULL; //failed
   const char* buf = NULL; 
   int len = me->leng;
   if(PyString_Check(u)){ 
      buf = PyString_AsString(u);
      len += PyString_Size(u);
   }else if(strbuf_Check(u)){
      buf= ((strbuf*)u)->buff;
      len += ((strbuf*)u)->leng;
   }
   strbuf* sb = strbuf_NEW(); //implict Py_INCREF
   if(!sb) return NULL;
   sb->size = sb->leng = len;
   sb->buff = malloc(sizeof(char)*(len+1));
   if(!sb->buff){ Py_DECREF(sb); return NULL; }
   strcpy(sb->buff, me->buff);
   strcpy(sb->buff + me->leng, buf);
   return (PyObject*)sb;
}"""

   def __iconcat__(me, u):
      return """{
   if(!PyString_CheckExact(u) 
   && !strbuf_CheckExact(u))
     return NULL; //failed
   int len = me->leng;
   const char* buf = NULL;
   if(PyString_CheckExact(u)){ 
      buf = PyString_AsString(u);
      len += PyString_Size(u);
   }else if(strbuf_CheckExact(u)){
      buf = ((strbuf*)u)->buff;
      len += ((strbuf*)u)->leng;
   }
   if(len > me->size) 
      if(strbuf_enlarge(me, len)<0)
         return NULL; //failed
   strcpy(me->buff+me->leng, buf);
   me->leng  = len;
   Py_INCREF(me); //New Reference!
   return (PyObject*) me;
}"""

   def __repeat__(me, t=int):
      return """{
   if (t<=0) return NULL;
   int len = me->leng * t;
   strbuf* sb = strbuf_NEW(); //implict Py_INCREF
   if(!sb) return NULL;
   sb->size = sb->leng = len;
   sb->buff = malloc(sizeof(char)*(len+1));
   if(!sb->buff){ Py_DECREF(sb); return NULL; }
   char * buff = sb->buff;
   for(; t > 0; t--){ 
      strcpy(buff, me->buff);
      buff += me->leng;
   }
   return (PyObject*) sb;
}"""

   def __irepeat__(me, t=int):
      return """{
   if(t<=0) return NULL;
   int len = me->leng * t;
   if(len > me->size) 
      if(strbuf_enlarge(me, len)<0)
         return NULL; //failed
   char * buff = me->buff;
   for(; t > 0; t--){ 
      strncpy(buff, me->buff, me->leng);
      buff += me->leng;
   }
   me->leng = len;
   me->buff[len] = 0;
   Py_INCREF(me);
   return (PyObject*) me;
}"""

Again notice the choice of the special method names __concat__ and __repeat__, which are not standard in pure python, this is again to avoid name conflict between the special methods for maps and sequences. The generated C code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*--- class strbuf ---*/

typedef struct{
  PyObject_HEAD
  int size;
  int leng;
  char* buff;
} strbuf;

int strbuf_enlarge(strbuf* me, int size);
static PyObject* strbuf_enlarge_wrap(
   strbuf* me,
   PyObject* _args_
);
static PyObject* strbuf___getitem__(strbuf* me, int i);
static PyObject* strbuf___concat__(strbuf* me, PyObject* u);
static int strbuf___setitem__(strbuf* me, int i, PyObject* v);
static PyObject* strbuf___irepeat__(strbuf* me, int t);
int strbuf___init__(strbuf* me, int size);
static int strbuf___init___wrap(
   strbuf* me,
   PyObject* _args_,
   PyObject* _kwds_
);
static PyObject* strbuf___repeat__(strbuf* me, int t);
static PyObject* strbuf___iconcat__(strbuf* me, PyObject* u);
static int strbuf___len__(strbuf* me);
#define strbuf_Check(op) PyObject_TypeCheck(op, &strbufType)
#define strbuf_CheckExact(op) (Py_TYPE(op)==&strbufType)
#define strbuf_NEW() PyObject_NEW(strbuf, &strbufType) //NEW reference!
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
#include <Python.h>
#include "structmember.h"
#include "module6.h"
static PyTypeObject strbufType;

/**======== MODULE IMPLEMENTATION ========**/

/*--- class strbuf ---*/
int strbuf_enlarge(strbuf* me, int size){
   if(size < me->size*2 +1) 
      size = 1 + me->size * 2;
   char* buff = malloc(sizeof(char)*size);
   if(!buff) return -1; //fail
   strcpy(buff, me->buff);
   free(me->buff);
   me->buff = buff;
   me->size = size;
   return size;
}
static PyObject* strbuf_enlarge_wrap(
   strbuf* me,
   PyObject* _args_
){
    int size=0;
    //no keywords support

    if(!PyArg_ParseTuple(
       _args_, "|i"
       , &size)) return 0;

    /*****Argument Exception Handling*****/

    int cxpyret = PyErr_Occurred()?(int)0:
                 strbuf_enlarge(me, size);

    /*****Return Value Exception Handling*****/

    if(PyErr_Occurred()) return 0;
    return Py_BuildValue("i", cxpyret);
}

static PyObject* strbuf___getitem__(strbuf* me, int i){
   if (i<0) i += me->leng;
   if(i >= me->leng || i<0){
      PyErr_SetString(PyExc_ValueError, "Index out of Range");
      return 0;
   }
   //implicit Py_INCREF:
   return PyString_FromStringAndSize(me->buff+i,1); 
}
static PyObject* strbuf___concat__(strbuf* me, PyObject* u){
   if(!PyString_Check(u) && !strbuf_Check(u))
     return NULL; //failed
   const char* buf = NULL; 
   int len = me->leng;
   if(PyString_Check(u)){ 
      buf = PyString_AsString(u);
      len += PyString_Size(u);
   }else if(strbuf_Check(u)){
      buf= ((strbuf*)u)->buff;
      len += ((strbuf*)u)->leng;
   }
   strbuf* sb = strbuf_NEW(); //implict Py_INCREF
   if(!sb) return NULL;
   sb->size = sb->leng = len;
   sb->buff = malloc(sizeof(char)*(len+1));
   if(!sb->buff){ Py_DECREF(sb); return NULL; }
   strcpy(sb->buff, me->buff);
   strcpy(sb->buff + me->leng, buf);
   return (PyObject*)sb;
}
static int strbuf___setitem__(strbuf* me, int i, PyObject* v){
   if (i<0) i += me->leng;
   if(i >= me->leng || i<0){
      PyErr_SetString(PyExc_ValueError, "Index out of Range");
      return 1; //failed
   }
   if(!PyString_Check(v) || PyString_Size(v)!=1){ 
      PyErr_SetString(PyExc_TypeError, "Must be a String of size 1.");
      return 1; //failed
   }
   me->buff[i] = PyString_AsString(v)[0];
   return 0;
}
static PyObject* strbuf___irepeat__(strbuf* me, int t){
   if(t<=0) return NULL;
   int len = me->leng * t;
   if(len > me->size) 
      if(strbuf_enlarge(me, len)<0)
         return NULL; //failed
   char * buff = me->buff;
   for(; t > 0; t--){ 
      strncpy(buff, me->buff, me->leng);
      buff += me->leng;
   }
   me->leng = len;
   me->buff[len] = 0;
   Py_INCREF(me);
   return (PyObject*) me;
}
int strbuf___init__(strbuf* me, int size){
   me->size = size;
   me->leng = 0;
   me->buff = malloc(sizeof(char)*(size+1));
   if(!me->buff) return -1; //fail
   me->buff[me->leng] = 0;
   return 0;
}
static int strbuf___init___wrap(
   strbuf* me,
   PyObject* _args_,
   PyObject* _kwds_
){
    int size;
    static char* _pctrl[] ={"size",  0};

    if(!PyArg_ParseTupleAndKeywords(
       _args_, _kwds_, "i", 
       _pctrl, &size)) return -1;

    /*****Argument Exception Handling*****/

    int cxpyret = PyErr_Occurred()?(int)-1:
                 strbuf___init__(me, size);

    /*****Return Value Exception Handling*****/

    return cxpyret; //don't need PyObject*!
}
static PyObject* strbuf___repeat__(strbuf* me, int t){
   if (t<=0) return NULL;
   int len = me->leng * t;
   strbuf* sb = strbuf_NEW(); //implict Py_INCREF
   if(!sb) return NULL;
   sb->size = sb->leng = len;
   sb->buff = malloc(sizeof(char)*(len+1));
   if(!sb->buff){ Py_DECREF(sb); return NULL; }
   char * buff = sb->buff;
   for(; t > 0; t--){ 
      strcpy(buff, me->buff);
      buff += me->leng;
   }
   return (PyObject*) sb;
}
static PyObject* strbuf___iconcat__(strbuf* me, PyObject* u){
   if(!PyString_CheckExact(u) 
   && !strbuf_CheckExact(u))
     return NULL; //failed
   int len = me->leng;
   const char* buf = NULL;
   if(PyString_CheckExact(u)){ 
      buf = PyString_AsString(u);
      len += PyString_Size(u);
   }else if(strbuf_CheckExact(u)){
      buf = ((strbuf*)u)->buff;
      len += ((strbuf*)u)->leng;
   }
   if(len > me->size) 
      if(strbuf_enlarge(me, len)<0)
         return NULL; //failed
   strcpy(me->buff+me->leng, buf);
   me->leng  = len;
   Py_INCREF(me); //New Reference!
   return (PyObject*) me;
}
static int strbuf___len__(strbuf* me){ return me->leng; }
static struct PyMethodDef strbufMeths[] = {
{  "enlarge", 
  (PyCFunction)strbuf_enlarge_wrap, 
  METH_VARARGS,
  "enlarge to the given size, or double the capacity,\n"
"whichever is the bigger."
},
{NULL}           /* sentinel */ };

static PyMemberDef strbufFields[] = {
{"size", T_INT, offsetof(strbuf, size), 0,
""},
{"leng", T_INT, offsetof(strbuf, leng), 0,
""},
{"buff", T_STRING, offsetof(strbuf, buff), RO,
""},
{NULL}  /* Sentinel */
};

/**Type strbuf is not numeric.**/

/**Type strbuf is not mapping.**/


/*cxpy.py: this is according to Python C API 2.6.
You may want to change the method types according
to your own version of Python!*/
static PySequenceMethods strbufAsSeq = {
        (lenfunc)strbuf___len__,               /*sq_length*/
        (binaryfunc)strbuf___concat__,         /*sq_concat*/
        (ssizeargfunc)strbuf___repeat__,       /*sq_repeat*/
        (ssizeargfunc)strbuf___getitem__,      /*sq_item*/
        (ssizessizeargfunc)NULL,/*sq_slice*/
        (ssizeobjargproc)strbuf___setitem__,     /*sq_ass_item*/
        (ssizessizeobjargproc)NULL, /*sq_ass_slice*/
        (objobjproc)NULL,       /*sq_contains*/
        (binaryfunc)strbuf___iconcat__,        /*sq_inplace_concat*/
        (ssizeargfunc)strbuf___irepeat__,      /*sq_inplace_repeat*/
};/*http://docs.python.org/c-api/typeobj.html#PySequenceMethods*/

/* NO PROPS: strbuf */


static PyTypeObject strbufType = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "module6.strbuf",       /*tp_name*/
    sizeof(strbuf),          /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor)0,       /*tp_dealloc*/
    (printfunc)0,      /*tp_print*/
    (getattrfunc)0,  /*tp_getattr*/
    (setattrfunc)0,  /*tp_setattr*/
    (cmpfunc)0,          /*tp_compare*/
    (reprfunc)0,        /*tp_repr*/
    0,                  /*tp_as_number*/
    &strbufAsSeq,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    (hashfunc)0,        /*tp_hash */
    (ternaryfunc)0,     /*tp_call*/
    (reprfunc)0,         /*tp_str*/
    (getattrofunc)0,/*tp_getattro*/
    (setattrofunc)0,/*tp_setattro*/
    0,                  /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT,                  /*tp_flags*/
    "A Sequence type to simulate StringBuffer.",                  /* tp_doc */
    (traverseproc)0,	       /* tp_traverse */
    (inquiry)0,		       /* tp_clear */
    (richcmpfunc)0,	 /* tp_richcompare */
    0,		               /* tp_weaklistoffset */
    (getiterfunc)0,        /* tp_iter */
    (iternextfunc)0,	  /* tp_iternext */
    strbufMeths,                  /* tp_methods */
    strbufFields,                  /* tp_members */
    0,                         /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    (descrgetfunc)0,       /* tp_descr_get */
    (descrsetfunc)0,       /* tp_descr_set */
    0,                         /* tp_dictoffset */
    (initproc)strbuf___init___wrap,        /* tp_init */
    0,                         /* tp_alloc */
    0,                   /* tp_new */
};/*http://docs.python.org/c-api/typeobj.html*/

static struct PyMethodDef module6Funcs[] = {
{NULL}           /* sentinel */ };


PyMODINIT_FUNC 
initmodule6(void) { //must be: init<module>
  PyObject *mod;
  
    //Initialize the type table for "strbuf".
    if(strbufType.tp_new == 0) 
         strbufType.tp_new = PyType_GenericNew;
    if (PyType_Ready(&strbufType) < 0) return;

  mod = Py_InitModule3("module6", module6Funcs, 
     "Sequence extension type with expy-cxpy.");
  if (mod==0) return;
  
    Py_INCREF(&strbufType);
    PyModule_AddObject(mod, "strbuf", (PyObject *)&strbufType);
    /* class fields for class strbuf: */
  /*End class strbuf.*/

  /* Custom Exceptions */
  /* Global Members */

}

And we have a test session for this module:

ylan@montop:~/expy/Doc/build/lib.linux-i686-2.5$ python
Python 2.5.2 (r252:60911, Jul 31 2008, 17:28:52)
[GCC 4.2.3 (Ubuntu 4.2.3-2ubuntu7)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from module6 import *
>>> x = strbuf(100)
>>> x += 'OK!'
>>> x*= 5
>>> x.buff
'OK!OK!OK!OK!OK!'

Create Iterator Types

We will present a relatively complete example, which will provide utilities to test and produce prime numbers, and will factorize an integer via the iterator protocol.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
"""A small module to calculate/test primes.
@head:
/**This software is under GPL.*/
#include <stdio.h>
#include <math.h>
@body:
/*KMAX=5000 covers 2^31.*/
#define KMAX 5000
static int nap=1;
static int ap[KMAX]={2};
"""

from expy import *


#total number of stored primes
nprimes = gfield(5000)

@function(int)
def isprime(n=int):
   """isprime(n) --> returns:
      -- 1 if n is a prime, 
      -- 0 if n is not, """
   return """{
   int k, m, r;
   if(n<2) return 0;
   k = prime_prime(KMAX-1);
   if(n==2 || n==k) return 1;
   if(n%k==0) return 0;
   for(k=0; ;++k){
      m = ap[k]; 
      r = n/m;
      if(n==r*m) return 0;
      if(r<=m) return 1;
   }
}"""

@function(int) #default return value
def prime(k=int):
    """prime(k) --> the k-th prime number."""
    return """{
  if(k<0 || k>=KMAX){
      PyErr_SetString(PyExc_ValueError, "Out of range!");
      return -1;
  }
  if (k<nap) return ap[k];
  int i, m, n = ap[nap-1];
  while (nap <= k){
    n ++;
    for(m=ap[i=0];
         n%m!=0 && n/m>m;
         m=ap[++i]);
    if (n%m!=0) ap[nap++] = n;
  }
  return n;
}"""

@function(int)
def primes(x=int):
   """primes(x): --> number of primes <= x."""
   return """{
   if (x<2) return 0;
   //don't check even numbers
   if (x%2==0) --x;
   int sum = 1; //as 2 is a prime
   for(; x>2; --x, --x) 
      sum += prime_isprime(x);
   return sum;
}"""

@function(int)
def firstpf(a=int, k=int):
   """return the smallest prime factor k'>=k."""
   return """{
  if(k<0) k=0;
  int p = prime_prime(k);
  int r = a/p;
  while(r>=p){
    if(a == p*r) return k;
    p = prime_prime(++k);
    r = a/p;
  };
  return -1;
}"""

class pfact(public):
   """decompose an integer into prime factors."""
   c = ifield(long, flag='RO', doc='unfactorized part') #readonly
   k = ifield(int, flag='RO', doc='minimal prime order') #readonly
   desc = cfield("enumerate prime factors in low to hi order")

   def __init__(me, a=long):
      return """{
   me->c  = a;
   me->k  = 0;
   return 0; //success
}"""

   #The signature must be like this:
   def __new__(cls, args, kwds):
      """A test of the __new__()"""
      return """{
   pfact* self;
   self = pfact_NEW();//Py_INCREF
   if (self == NULL) return NULL;
   self->k = 0;
   return (PyObject*) self; 
}"""

   def getc(me, closure = rawtype('void*')):
       return """{
   return PyInt_FromLong(me->c);//Py_INCREF
}"""

   def setc(me, v, closure = rawtype('void*')):
       return """{
   if(!PyInt_Check(v)){ 
      PyErr_SetString(PyExc_TypeError, "Must be an Integer.");
      return 1; //failed
   }
   me->c = PyInt_AsLong(v);
   return me->k = 0; //success
}"""

   rem = property(getc, setc, doc="remainder for factorization")

   def __iter__(me): #special methods should not be expressions
      return """{
   me->k = 0;
   Py_INCREF(me);
   return (PyObject*) me;
}"""

   def __next__(me):
      return """{
   if(me->c <2) return NULL;
   me->k = prime_firstpf(me->c, me->k);
   int n = me->k<0?me->c:prime_prime(me->k);
   me->c /= n;
   return PyInt_FromLong(n);//Py_INCREF
}"""

   @smethod(int) #static method
   def maxprid(): 
      """maximum prime id."""
      return "KMAX"

   @cmethod(int) #class method
   def maxprime(cls):
      return "prime_prime(KMAX-1)"

In the prime.py, the prologue actually defines some static storage for the primes. This small module is quite useful, it provides the first 5000 primes, and can test any integers within the range of [1, 2^31-1]. And finally, For any integer in that range, it provides the prime factors by the __iter__ and __next__ methods (iterator protocol). Here is the script file to test this module:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import sys, os
for fn in os.listdir('build'):
   if fn.startswith('lib'):
      print "inserted:",fn
      sys.path.insert(0, 'build/'+fn)
from prime import *
print pfact.maxprid()
print pfact.maxprime()
print pfact.desc
#this class field is read only:
#pfact.desc = "set to something else"
print "stored primes:", nprimes
for i in range(10): print i, isprime(i), ';',
print
print "there are %i primes from 0 to 9999."%primes(9999)
print "The 10th prime is", prime(10)
pf = pfact(52)
fs = [i for i in pf]
print "The prime factors for 52 are:", fs
print "Property rem:", pf.rem
pf.rem = 123
print "Property rem modified:", pf.rem
print [i for i in pf]

The output from this test file looks like this:

ylan@montop:~/expy/examples$ python testprime.py
5000
48611
enumerate prime factors in low to hi order
stored primes: 5000
0 0 ; 1 0 ; 2 1 ; 3 1 ; 4 0 ; 5 1 ; 6 0 ; 7 1 ; 8 0 ; 9 0 ;
there are 1229 primes from 0 to 9999.
The 10th prime is 31
The prime factors for 52 are: [2, 2, 13]
Property rem: 1
Property rem modified: 123
[3, 41]

And here is the generated C file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//@head:begin

/**This software is under GPL.*/
#include <stdio.h>
#include <math.h>
//@head:end
/*--- class pfact ---*/

typedef struct{
  PyObject_HEAD
  long int c;
  int k;
} pfact;

int prime_prime(int k);
static PyObject* prime_prime_wrap(
   PyObject* _NOUSE,
   PyObject* _args_
);
int prime_primes(int x);
static PyObject* prime_primes_wrap(
   PyObject* _NOUSE,
   PyObject* _args_
);
int prime_firstpf(int a, int k);
static PyObject* prime_firstpf_wrap(
   PyObject* _NOUSE,
   PyObject* _args_
);
int prime_isprime(int n);
static PyObject* prime_isprime_wrap(
   PyObject* _NOUSE,
   PyObject* _args_
);
static PyObject* pfact_maxprid_wrap(
   PyObject* _NOUSE
);
static int pfact_setc(pfact* me, PyObject* v, void* closure);
static PyObject* pfact_getc(pfact* me, void* closure);
static PyObject* pfact_maxprime_wrap(
   PyTypeObject* cls
);
static PyObject* pfact___iter__(pfact* me);
static PyObject* pfact___next__(pfact* me);
static PyObject* pfact___new__(PyTypeObject* cls, PyObject* args, PyObject* kwds);
int pfact___init__(pfact* me, long int a);
static int pfact___init___wrap(
   pfact* me,
   PyObject* _args_,
   PyObject* _kwds_
);
#define pfact_Check(op) PyObject_TypeCheck(op, &pfactType)
#define pfact_CheckExact(op) (Py_TYPE(op)==&pfactType)
#define pfact_NEW() PyObject_NEW(pfact, &pfactType) //NEW reference!
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
#include <Python.h>
#include "structmember.h"
#include "prime.h"
static PyTypeObject pfactType;

/**======== MODULE IMPLEMENTATION ========**/

//@body:begin

/*KMAX=5000 covers 2^31.*/
#define KMAX 5000
static int nap=1;
static int ap[KMAX]={2};

//@body:end
int prime_prime(int k){
  if(k<0 || k>=KMAX){
      PyErr_SetString(PyExc_ValueError, "Out of range!");
      return -1;
  }
  if (k<nap) return ap[k];
  int i, m, n = ap[nap-1];
  while (nap <= k){
    n ++;
    for(m=ap[i=0];
         n%m!=0 && n/m>m;
         m=ap[++i]);
    if (n%m!=0) ap[nap++] = n;
  }
  return n;
}
static PyObject* prime_prime_wrap(
   PyObject* _NOUSE,
   PyObject* _args_
){
    int k;
    //no keywords support

    if(!PyArg_ParseTuple(
       _args_, "i"
       , &k)) return 0;

    /*****Argument Exception Handling*****/

    int cxpyret = PyErr_Occurred()?(int)0:
                 prime_prime(k);

    /*****Return Value Exception Handling*****/

    if(PyErr_Occurred()) return NULL;
    return Py_BuildValue("i", cxpyret);
}

int prime_primes(int x){
   if (x<2) return 0;
   //don't check even numbers
   if (x%2==0) --x;
   int sum = 1; //as 2 is a prime
   for(; x>2; --x, --x) 
      sum += prime_isprime(x);
   return sum;
}
static PyObject* prime_primes_wrap(
   PyObject* _NOUSE,
   PyObject* _args_
){
    int x;
    //no keywords support

    if(!PyArg_ParseTuple(
       _args_, "i"
       , &x)) return 0;

    /*****Argument Exception Handling*****/

    int cxpyret = PyErr_Occurred()?(int)0:
                 prime_primes(x);

    /*****Return Value Exception Handling*****/

    if(PyErr_Occurred()) return NULL;
    return Py_BuildValue("i", cxpyret);
}

int prime_firstpf(int a, int k){
  if(k<0) k=0;
  int p = prime_prime(k);
  int r = a/p;
  while(r>=p){
    if(a == p*r) return k;
    p = prime_prime(++k);
    r = a/p;
  };
  return -1;
}
static PyObject* prime_firstpf_wrap(
   PyObject* _NOUSE,
   PyObject* _args_
){
    int a;
    int k;
    //no keywords support

    if(!PyArg_ParseTuple(
       _args_, "ii"
       , &a, &k)) return 0;

    /*****Argument Exception Handling*****/

    int cxpyret = PyErr_Occurred()?(int)0:
                 prime_firstpf(a, k);

    /*****Return Value Exception Handling*****/

    if(PyErr_Occurred()) return NULL;
    return Py_BuildValue("i", cxpyret);
}

int prime_isprime(int n){
   int k, m, r;
   if(n<2) return 0;
   k = prime_prime(KMAX-1);
   if(n==2 || n==k) return 1;
   if(n%k==0) return 0;
   for(k=0; ;++k){
      m = ap[k]; 
      r = n/m;
      if(n==r*m) return 0;
      if(r<=m) return 1;
   }
}
static PyObject* prime_isprime_wrap(
   PyObject* _NOUSE,
   PyObject* _args_
){
    int n;
    //no keywords support

    if(!PyArg_ParseTuple(
       _args_, "i"
       , &n)) return 0;

    /*****Argument Exception Handling*****/

    int cxpyret = PyErr_Occurred()?(int)0:
                 prime_isprime(n);

    /*****Return Value Exception Handling*****/

    if(PyErr_Occurred()) return NULL;
    return Py_BuildValue("i", cxpyret);
}

/*--- class pfact ---*/
static PyObject* pfact_maxprid_wrap(
   PyObject* _NOUSE
){

    /*****Argument Exception Handling*****/

    int cxpyret = PyErr_Occurred()?(int)0:
                 KMAX;

    /*****Return Value Exception Handling*****/

    if(PyErr_Occurred()) return NULL;
    return Py_BuildValue("i", cxpyret);
}

static int pfact_setc(pfact* me, PyObject* v, void* closure){
   if(!PyInt_Check(v)){ 
      PyErr_SetString(PyExc_TypeError, "Must be an Integer.");
      return 1; //failed
   }
   me->c = PyInt_AsLong(v);
   return me->k = 0; //success
}
static PyObject* pfact_getc(pfact* me, void* closure){
   return PyInt_FromLong(me->c);//Py_INCREF
}
static PyObject* pfact_maxprime_wrap(
   PyTypeObject* cls
){

    /*****Argument Exception Handling*****/

    int cxpyret = PyErr_Occurred()?(int)0:
                 prime_prime(KMAX-1);

    /*****Return Value Exception Handling*****/

    if(PyErr_Occurred()) return NULL;
    return Py_BuildValue("i", cxpyret);
}

static PyObject* pfact___iter__(pfact* me){
   me->k = 0;
   Py_INCREF(me);
   return (PyObject*) me;
}
static PyObject* pfact___next__(pfact* me){
   if(me->c <2) return NULL;
   me->k = prime_firstpf(me->c, me->k);
   int n = me->k<0?me->c:prime_prime(me->k);
   me->c /= n;
   return PyInt_FromLong(n);//Py_INCREF
}
static PyObject* pfact___new__(PyTypeObject* cls, PyObject* args, PyObject* kwds){
   pfact* self;
   self = pfact_NEW();//Py_INCREF
   if (self == NULL) return NULL;
   self->k = 0;
   return (PyObject*) self; 
}
int pfact___init__(pfact* me, long int a){
   me->c  = a;
   me->k  = 0;
   return 0; //success
}
static int pfact___init___wrap(
   pfact* me,
   PyObject* _args_,
   PyObject* _kwds_
){
    long int a;
    static char* _pctrl[] ={"a",  0};

    if(!PyArg_ParseTupleAndKeywords(
       _args_, _kwds_, "l", 
       _pctrl, &a)) return 0;

    /*****Argument Exception Handling*****/

    int cxpyret = PyErr_Occurred()?(int)0:
                 pfact___init__(me, a);

    /*****Return Value Exception Handling*****/

    return cxpyret; //don't need PyObject*!
}
static struct PyMethodDef pfactMeths[] = {
{  "maxprid", 
  (PyCFunction)pfact_maxprid_wrap, 
  METH_NOARGS | METH_STATIC,
  "maximum prime id."
},
{  "maxprime", 
  (PyCFunction)pfact_maxprime_wrap, 
  METH_NOARGS | METH_CLASS,
  ""
},
{NULL}           /* sentinel */ };

static PyMemberDef pfactFields[] = {
{"c", T_LONG, offsetof(pfact, c), RO,
"unfactorized part"},
{"k", T_INT, offsetof(pfact, k), RO,
"minimal prime order"},
{NULL}  /* Sentinel */
};

/**Type pfact is not numeric.**/

/**Type pfact is not mapping.**/

/**Type pfact is not sequence.**/


static PyGetSetDef pfactProps[] = {
{"rem", (getter)pfact_getc, (setter)pfact_setc,
      "remainder for factorization", NULL},
    {NULL}  /* Sentinel */
};


static PyTypeObject pfactType = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "prime.pfact",       /*tp_name*/
    sizeof(pfact),          /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor)0,       /*tp_dealloc*/
    (printfunc)0,      /*tp_print*/
    (getattrfunc)0,  /*tp_getattr*/
    (setattrfunc)0,  /*tp_setattr*/
    (cmpfunc)0,          /*tp_compare*/
    (reprfunc)0,        /*tp_repr*/
    0,                  /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    (hashfunc)0,        /*tp_hash */
    (ternaryfunc)0,     /*tp_call*/
    (reprfunc)0,         /*tp_str*/
    (getattrofunc)0,/*tp_getattro*/
    (setattrofunc)0,/*tp_setattro*/
    0,                  /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT,                  /*tp_flags*/
    "decompose an integer into prime factors.",                  /* tp_doc */
    (traverseproc)0,	       /* tp_traverse */
    (inquiry)0,		       /* tp_clear */
    (richcmpfunc)0,	 /* tp_richcompare */
    0,		               /* tp_weaklistoffset */
    (getiterfunc)pfact___iter__,        /* tp_iter */
    (iternextfunc)pfact___next__,	  /* tp_iternext */
    pfactMeths,                  /* tp_methods */
    pfactFields,                  /* tp_members */
    pfactProps,                         /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    (descrgetfunc)0,       /* tp_descr_get */
    (descrsetfunc)0,       /* tp_descr_set */
    0,                         /* tp_dictoffset */
    (initproc)pfact___init___wrap,        /* tp_init */
    0,                         /* tp_alloc */
    pfact___new__,                   /* tp_new */
};/*http://docs.python.org/c-api/typeobj.html*/

static struct PyMethodDef primeFuncs[] = {
{  "prime", 
  (PyCFunction)prime_prime_wrap, 
  METH_VARARGS,
  "prime(k) --> the k-th prime number."
},
{  "primes", 
  (PyCFunction)prime_primes_wrap, 
  METH_VARARGS,
  "primes(x): --> number of primes <= x."
},
{  "firstpf", 
  (PyCFunction)prime_firstpf_wrap, 
  METH_VARARGS,
  "return the smallest prime factor k'>=k."
},
{  "isprime", 
  (PyCFunction)prime_isprime_wrap, 
  METH_VARARGS,
  "isprime(n) --> returns:\n"
"      -- 1 if n is a prime, \n"
"      -- 0 if n is not, "
},
{NULL}           /* sentinel */ };


PyMODINIT_FUNC 
initprime(void) { //must be: init<module>
  PyObject *mod;
  
    //Initialize the type table for "pfact".
    if(pfactType.tp_new == 0) 
         pfactType.tp_new = PyType_GenericNew;
    if (PyType_Ready(&pfactType) < 0) return;

  mod = Py_InitModule3("prime", primeFuncs, 
     "A small module to calculate/test primes.");
  if (mod==0) return;
  
    Py_INCREF(&pfactType);
    PyModule_AddObject(mod, "pfact", (PyObject *)&pfactType);
    /* class fields for class pfact: */

  PyDict_SetItemString(pfactType.tp_dict, "desc", 
     Py_BuildValue("s","enumerate prime factors in low to hi order"));
  /*End class pfact.*/

  /* Custom Exceptions */
  /* Global Members */
  PyModule_AddObject(mod, "nprimes", 
    Py_BuildValue("i", 5000));

}

Static array dimension

For instance fields, the keyword ‘dimension’ can be used to declare a static multi-dimensional array:

class myclass(public):
   size = ifield(int, private, dimension='[2]')

the code above would define something like this for the generated C code:

typedef struct {
   int[2] size;
   ...
}myclass;

Of course, you can define multi-dimensions, such as:

dimension='[3][2]'

Usually such a field will be ‘private’, as there ususually not a readily defined way in CPython to access it, but you can define getters/setters for them.

Exposing nested C struct

There is another useful keyword in ifield constructor: exposure. Use this field if your extension object has a nested struct and you would like to access it as a member field. Suppose you have a C struct defined as follows:

struct mystruct{ int x, y; };

and you would like to have that struct as a member of your extention object, then expose the field ‘x’ of it. Here is how to make it happend:

class myclass(public):
   point = ifield(rawtype('struct mystruct'), private)
   x = ifield(int, exposure='point.x')

In this case, the member ‘x’ will be considered already exist in the member ‘point’ as ‘point.x’, so there won’t be another top level ‘x’ member in the C struct of this class, but the field member would be directly exposed to the python object as a a property named ‘x’.

Another use case for ‘exposure’ keyword is to expose it as a different type than what’s claimed, which is often used with keyword ‘dimension’:

class myclass(public):
   chars = ifield(char, dimension='[4]', exposure=int)

this time the ‘chars’ is of a nominal type ‘char’, but it can also be seen as an 32 bit integer, because of the ‘dimension’ keyword, so an ‘exposure’ keyword is used to achieve this.

Writing your __init__ function

When you write your __init__ method, you can specify your arguments just the same way as you do in writing a python __init__ method, and EXPY will produce a wrapper function to parse the arguments. What’s more, you can also throw exceptions (using the @throws decorator). One thing must remember for __init__: return 0 for success, and -1 for failure.

Warnings on Py_INCREF

There are many special methods, such as __iter__, __next__ etc that require returning a python object, and you should increase the reference count. EXPY will check if Py_INCREF is in there, and will give a warning if not.

Conclusion

Through examples I hope it all looks interesting and exciting. Suggestions, volunteer works, bug reports are all welcome, I am available via email: lanyjie at yahoo dot come, and expy also has its own email server, as indicated on the homepage. This concludes our introduction to the major features of expy-cxpy. Thanks for your interests, and I hope you find this a nice tool for developing python extensions. Happy coding!