[Zope-Checkins] CVS: Zope/lib/python/Record - _Record.c:1.2 __init__.py:1.2 setup.py:1.2 tests.py:1.2

Jim Fulton cvs-admin at zope.org
Fri Nov 28 11:46:36 EST 2003


Update of /cvs-repository/Zope/lib/python/Record
In directory cvs.zope.org:/tmp/cvs-serv5405/lib/python/Record

Added Files:
	_Record.c __init__.py setup.py tests.py 
Log Message:
remplemented the Record extension based on new-style extension classes.


=== Zope/lib/python/Record/_Record.c 1.1 => 1.2 ===
--- /dev/null	Fri Nov 28 11:46:36 2003
+++ Zope/lib/python/Record/_Record.c	Fri Nov 28 11:46:36 2003
@@ -0,0 +1,491 @@
+/*****************************************************************************
+
+  Copyright (c) 1996-2002 Zope Corporation and Contributors.
+  All Rights Reserved.
+
+  This software is subject to the provisions of the Zope Public License,
+  Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+  WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+  FOR A PARTICULAR PURPOSE
+
+ ****************************************************************************/
+
+static char Record_module_documentation[] = 
+""
+"\n$Id$"
+;
+
+#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)
+#define OBJECT(O) ((PyObject*)(O))
+
+static PyObject *py___record_schema__;
+
+/* Declarations for objects of type Record */
+
+typedef struct {
+  PyObject_HEAD
+  PyObject **data;
+  PyObject *schema;
+} Record;
+
+staticforward PyExtensionClass RecordType;
+
+/* ---------------------------------------------------------------- */
+
+static int
+Record_init(Record *self)
+{
+  int l;
+
+  UNLESS(self->schema)
+    UNLESS(self->schema=PyObject_GetAttr(OBJECT(self->ob_type),
+					 py___record_schema__)) return -1;
+  if((l=PyObject_Length(self->schema)) < 0) return -1;
+  UNLESS(self->data) 
+    {
+      UNLESS(self->data=malloc(sizeof(PyObject*)*(l+1)))
+	{
+	  PyErr_NoMemory();
+	  return -1;
+	}
+      memset(self->data, 0, sizeof(PyObject*)*(l+1));
+    }
+  
+  return l;
+} 
+
+static PyObject *
+Record___setstate__(Record *self, PyObject *args)
+{
+  PyObject *state=0, *parent, **d;
+  int l, ls, i;
+
+  if((l=Record_init(self)) < 0) return NULL;
+
+  UNLESS(PyArg_ParseTuple(args, "|OO", &state, &parent)) return NULL;
+
+  if(state) {
+    if(PyDict_Check(state))
+      {
+	PyObject *k, *v;
+	
+	for(i=0; PyDict_Next(state, &i, &k, &v);)
+	  if(k && v && PyObject_SetAttr(OBJECT(self),k,v) < 0)
+	    PyErr_Clear();
+      }
+    else
+      {
+	if((ls=PyObject_Length(state)) < 0) return NULL;
+	
+	for(i=0, d=self->data; i < l && i < ls; i++, d++)
+	  {
+	    ASSIGN(*d, PySequence_GetItem(state, i));
+	    UNLESS(*d) return NULL;
+	  }
+      }
+  }
+
+  Py_INCREF(Py_None);
+  return Py_None;
+}
+
+static PyObject *
+Record___getstate__( Record *self, PyObject *args)
+{
+  PyObject *r, **d, *v;
+  int i, l;
+
+  UNLESS(self->data) return PyTuple_New(0);
+
+  if((l=Record_init(self)) < 0) return NULL;
+  UNLESS(r=PyTuple_New(l)) return NULL;
+  for(d=self->data, i=0; i < l; i++, d++)
+    {
+      v= *d;
+      if(!v) v=Py_None;
+      Py_INCREF(v);
+      PyTuple_SET_ITEM(r,i,v);
+    }
+  return r;
+}
+
+static void
+Record_deal(Record *self)
+{
+  int l;
+  PyObject **d;
+
+  if(self->schema)
+    {
+      l=PyObject_Length(self->schema);
+      for(d=self->data; --l >= 0; d++)
+	{
+	  Py_XDECREF(*d);
+	}
+      Py_DECREF(self->schema);
+      free(self->data);
+    }
+}
+
+static struct PyMethodDef Record_methods[] = {
+  {"__getstate__",	(PyCFunction)Record___getstate__,	METH_VARARGS,
+   "__getstate__() -- Get the record's data"
+  },
+  {"__setstate__",	(PyCFunction)Record___setstate__,	METH_VARARGS,
+   "__setstate__(v) -- Set the record's data"
+  },
+  {"__init__",	(PyCFunction)Record___setstate__,	METH_VARARGS,
+   "__init__([v]) -- Initialize a record, possibly with state"
+  },
+  {NULL,		NULL}		/* sentinel */
+};
+
+/* ---------- */
+
+static void
+Record_dealloc(Record *self)
+{
+  Record_deal(self);
+  Py_DECREF(self->ob_type);
+  PyMem_DEL(self);
+}
+
+static PyObject *
+Record_getattr(Record *self, PyObject *name)
+{
+  int l, i;
+  PyObject *io;
+
+  if((l=Record_init(self)) < 0) return NULL;
+
+  if ((io=Py_FindAttr((PyObject *)self, name))) return io;
+
+  PyErr_Clear();
+
+  if((io=PyObject_GetItem(self->schema, name)))
+    {
+      UNLESS(PyInt_Check(io))
+	{
+	  PyErr_SetString(PyExc_TypeError, "invalid record schema");
+	  return NULL;
+	}
+      i=PyInt_AsLong(io);
+      if(i >= 0 && i < l)
+	{
+	  ASSIGN(io, self->data[i]);
+	  UNLESS(io) io=Py_None;
+	}
+      else ASSIGN(io, Py_None);
+      Py_INCREF(io);
+      return io;
+    }
+
+  PyErr_SetObject(PyExc_AttributeError, name);  
+  
+  return NULL;
+}
+
+static int
+Record_setattr(Record *self, PyObject *name, PyObject *v)
+{
+  int l, i;
+  PyObject *io;
+
+  if((l=Record_init(self)) < 0) return -1;
+  if((io=PyObject_GetItem(self->schema, name)))
+    {
+      UNLESS(PyInt_Check(io))
+	{
+	  PyErr_SetString(PyExc_TypeError, "invalid record schema");
+	  return -1;
+	}
+      i=PyInt_AsLong(io);
+      Py_DECREF(io);
+      if(i >= 0 && i < l)
+	{
+	  Py_XINCREF(v);
+	  ASSIGN(self->data[i],v);
+	  return 0;
+	}
+    }
+
+  PyErr_SetObject(PyExc_AttributeError, name);
+  return -1;
+}
+
+static int
+Record_compare(Record *v, Record *w)
+{
+  int lv, lw, i, c;
+  PyObject **dv, **dw;
+
+  if((lv=Record_init(v)) < 0) return -1;
+  if((lw=Record_init(w)) < 0) return -1;
+  if(lw < lv) lv=lw;
+
+  for(i=0, dv=v->data, dw=w->data; i < lv; i++, dv++, dw++)
+    {
+      if(*dv)
+	if(*dw)
+	  {
+	    if((c=PyObject_Compare(*dv,*dw))) return c;
+	  }
+	else return 1;
+      else if(*dw) return -1;
+    }
+  if(*dv) return 1;
+  if(*dw) return -1;
+  return 0;
+}
+
+/* Code to handle accessing Record objects as sequence objects */
+
+static PyObject * 
+Record_concat(Record *self, PyObject *bb)
+{
+  PyErr_SetString(PyExc_TypeError,
+		  "Record objects do not support concatenation");
+  return NULL;
+}
+
+static PyObject *
+Record_repeat(Record *self, int n)
+{
+  PyErr_SetString(PyExc_TypeError,
+		  "Record objects do not support repetition");
+  return NULL;
+}
+
+static PyObject *
+IndexError(int i)
+{
+  PyObject *v;
+
+  if((v=PyInt_FromLong(i)))
+    {
+      PyErr_SetObject(PyExc_IndexError, v);
+      Py_DECREF(v);
+    }
+
+  return NULL;
+}
+
+static PyObject *
+Record_item(Record *self, int i)
+{
+  PyObject *o;
+  int l;
+
+  if((l=Record_init(self)) < 0) return NULL;
+  if(i < 0 || i >= l) return IndexError(i);
+
+  o=self->data[i];
+  UNLESS(o) o=Py_None;
+
+  Py_INCREF(o);
+  return o;
+}
+
+static PyObject *
+Record_slice(Record *self, int ilow, int ihigh)
+{
+  PyErr_SetString(PyExc_TypeError,
+		  "Record objects do not support slicing");
+  return NULL;
+}
+
+static int
+Record_ass_item(Record *self, int i, PyObject *v)
+{
+  int l;
+
+  if((l=Record_init(self)) < 0) return -1;
+  if(i < 0 || i >= l)
+    {
+      IndexError(i);
+      return -1;
+    }
+
+  UNLESS(v)
+    {
+      PyErr_SetString(PyExc_TypeError,"cannot delete record items");
+      return -1;
+    }
+
+  Py_INCREF(v);
+  ASSIGN(self->data[i], v);
+  return 0;
+}
+
+static int
+Record_ass_slice(Record *self, int ilow, int ihigh, PyObject *v)
+{
+  PyErr_SetString(PyExc_TypeError,
+		  "Record objects do not support slice assignment");
+  return -1;
+}
+
+static PySequenceMethods Record_as_sequence = {
+  (inquiry)Record_init,			/*sq_length*/
+  (binaryfunc)Record_concat,		/*sq_concat*/
+  (intargfunc)Record_repeat,		/*sq_repeat*/
+  (intargfunc)Record_item,		/*sq_item*/
+  (intintargfunc)Record_slice,		/*sq_slice*/
+  (intobjargproc)Record_ass_item,	/*sq_ass_item*/
+  (intintobjargproc)Record_ass_slice,	/*sq_ass_slice*/
+};
+
+/* -------------------------------------------------------------- */
+
+static PyObject *
+Record_subscript(Record *self, PyObject *key)
+{
+  int i, l;
+  PyObject *io;
+
+  if((l=Record_init(self)) < 0) return NULL;
+
+  if(PyInt_Check(key))
+    {
+      i=PyInt_AsLong(key);
+      if(i<0) i+=l;
+      return Record_item(self, i);
+    }
+
+  if((io=PyObject_GetItem(self->schema, key)))
+    {
+      UNLESS(PyInt_Check(io))
+	{
+	  PyErr_SetString(PyExc_TypeError, "invalid record schema");
+	  return NULL;
+	}
+      i=PyInt_AsLong(io);
+      if(i >= 0 && i < l)
+	{
+	  ASSIGN(io, self->data[i]);
+	  UNLESS(io) io=Py_None;
+	}
+      else ASSIGN(io, Py_None);
+      Py_INCREF(io);
+      return io;
+    }
+
+  PyErr_Clear();
+  if ((io=PyObject_GetAttr(OBJECT(self), key))) return io;
+
+  PyErr_SetObject(PyExc_KeyError, key);					   
+  return NULL;
+}
+
+static int
+Record_ass_sub(Record *self, PyObject *key, PyObject *v)
+{
+  int i, l;
+  PyObject *io;
+
+  if((l=Record_init(self)) < 0) return -1;
+
+  if(PyInt_Check(key))
+    {
+      i=PyInt_AsLong(key);
+      if(i<0) i+=l;
+      return Record_ass_item(self, i, v);
+    }
+
+  if((io=PyObject_GetItem(self->schema, key)))
+    {
+      UNLESS(PyInt_Check(io))
+	{
+	  PyErr_SetString(PyExc_TypeError, "invalid record schema");
+	  return -1;
+	}
+      i=PyInt_AsLong(io);
+      Py_DECREF(io);
+      if(i >= 0 && i < l)
+	{
+	  Py_XINCREF(v);
+	  ASSIGN(self->data[i],v);
+	  return 0;
+	}
+    }
+
+  return -1;
+}
+
+static PyMappingMethods Record_as_mapping = {
+  (inquiry)Record_init,		/*mp_length*/
+  (binaryfunc)Record_subscript,		/*mp_subscript*/
+  (objobjargproc)Record_ass_sub,	/*mp_ass_subscript*/
+};
+
+/* -------------------------------------------------------- */
+
+static PyExtensionClass RecordType = {
+  PyObject_HEAD_INIT(NULL)
+  0,					/*ob_size*/
+  "Record",				/*tp_name*/
+  sizeof(Record),			/*tp_basicsize*/
+  0,					/*tp_itemsize*/
+  /* methods */
+  (destructor)Record_dealloc,		/*tp_dealloc*/
+  (printfunc)0,				/*tp_print*/
+  (getattrfunc)0,			/*obsolete tp_getattr*/
+  (setattrfunc)0,			/*obsolete tp_setattr*/
+  (cmpfunc)Record_compare,		/*tp_compare*/
+  (reprfunc)0,				/*tp_repr*/
+  0,					/*tp_as_number*/
+  &Record_as_sequence,					/*tp_as_sequence*/
+  &Record_as_mapping,					/*tp_as_mapping*/
+  (hashfunc)0,				/*tp_hash*/
+  (ternaryfunc)0,			/*tp_call*/
+  (reprfunc)0,				/*tp_str*/
+  (getattrofunc)Record_getattr,			/*tp_getattro*/
+  (setattrofunc)Record_setattr,			/*tp_setattro*/
+  /* Space for future expansion */
+  0L,0L,
+  "Simple Record Types", 		/* Documentation string */
+  METHOD_CHAIN(Record_methods),
+  (void*)(EXTENSIONCLASS_NOINSTDICT_FLAG),
+};
+
+/* End of code for Record objects */
+/* -------------------------------------------------------- */
+
+
+/* List of methods defined in the module */
+
+static struct PyMethodDef Module_Level__methods[] = {
+  
+  {NULL, (PyCFunction)NULL, 0, NULL}		/* sentinel */
+};
+
+/* Initialization function for the module (*must* be called initRecord) */
+
+void
+init_Record(void)
+{
+  PyObject *m, *d;
+
+  UNLESS(py___record_schema__=PyString_FromString("__record_schema__")) return;
+
+  UNLESS(ExtensionClassImported) return;
+
+  /* Create the module and add the functions */
+  m = Py_InitModule4("_Record", Module_Level__methods,
+		     Record_module_documentation,
+		     (PyObject*)NULL,PYTHON_API_VERSION);
+
+  /* Add some symbolic constants to the module */
+  d = PyModule_GetDict(m);
+
+  PyExtensionClass_Export(d,"Record",RecordType);
+}


=== Zope/lib/python/Record/__init__.py 1.1 => 1.2 ===
--- /dev/null	Fri Nov 28 11:46:36 2003
+++ Zope/lib/python/Record/__init__.py	Fri Nov 28 11:46:36 2003
@@ -0,0 +1,59 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Records
+
+Records are used to provide compact storage for database query result rows.
+
+They don't use instance dictionaries. Rather, they store they data in
+a compact array internally. They use a record schema to map names to
+positions within the array.
+
+>>> class R(Record):
+...     __record_schema__ = {'a': 0, 'b': 1, 'c': 2}
+
+>>> r = R()
+>>> r.__dict__
+Traceback (most recent call last):
+...
+AttributeError: __dict__
+
+>>> r.a
+>>> r.a = 1
+>>> r.a
+1
+>>> r.b
+>>> r.c
+
+Records can be used as mapping objects:
+
+>>> "%(a)s %(b)s %(c)s" % r
+'1 None None'
+
+>>> r['a']
+1
+
+>>> r['b'] = 42
+
+And like sequences:
+
+>>> r[0]
+1
+>>> r[1]
+42
+>>> list(r)
+[1, 42, None]
+
+$Id$
+"""
+from _Record import Record


=== Zope/lib/python/Record/setup.py 1.1 => 1.2 ===
--- /dev/null	Fri Nov 28 11:46:36 2003
+++ Zope/lib/python/Record/setup.py	Fri Nov 28 11:46:36 2003
@@ -0,0 +1,22 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+from distutils.core import setup, Extension
+setup(name="_Record", version="2.0",
+      ext_modules=[
+         Extension("_Record", ["_Record.c"],
+                   include_dirs = ['.', '../ExtensionClass'],
+                   depends = ['../ExtensionClass/ExtensionClass.h']),
+         ])
+


=== Zope/lib/python/Record/tests.py 1.1 => 1.2 ===
--- /dev/null	Fri Nov 28 11:46:36 2003
+++ Zope/lib/python/Record/tests.py	Fri Nov 28 11:46:36 2003
@@ -0,0 +1,50 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Record tests
+
+$Id$
+"""
+
+from Record import Record
+import pickle
+
+class P(Record):
+     __record_schema__ = {'a': 0, 'b': 1, 'c': 2}
+
+def test_RecordPickling():
+    """
+
+    We can create records from sequences:
+    
+    >>> r = P(('x', 42, 1.23))
+
+    We can pickle them:
+
+    >>> r2 = pickle.loads(pickle.dumps(r))
+    >>> list(r) == list(r2)
+    1
+    >>> r.__record_schema__ == r2.__record_schema__
+    1
+    """
+
+import unittest
+from doctest import DocTestSuite
+
+def test_suite():
+    return unittest.TestSuite((
+        DocTestSuite('Record'),
+        DocTestSuite(),
+        ))
+
+if __name__ == '__main__': unittest.main()




More information about the Zope-Checkins mailing list