6.2.4.6. xmap C implementation

Comienzo C section to interscript/core/xmap.c[1 /1 ]
     1: #line 2825 "mxTools.pak"
     2: /*
     3:    xmapmodule.c - Implementation of xmap function/type by
     4:                   Christopher Tavares (mailto:tavares@connix.com).
     5: 
     6:    Version: 0.2
     7: 
     8: */
     9: 
    10: #include "Python.h"
    11: #include "config.h"
    12: #include "assert.h"
    13: 
    14: /*
    15: // Bogus sun headers don't have prototypes for printf or fprintf.
    16: // Add them
    17: */
    18: #if defined(__STDC__) && !defined(STDC_HEADERS)
    19: int printf(const char *, ...);
    20: int fprintf(FILE *, const char *, ...);
    21: #endif
    22: 
    23: #if 0
    24: /*
    25: // This is not needed for Python 1.5. If you're running 1.4,
    26: // enable this
    27: */
    28: 
    29: /*
    30: // Bug fix: workaround for bug in PySequence_GetItem when
    31: // accessing sequences that don't have __len__ defined.
    32: // Thanks to J. Fulton.
    33: */
    34: 
    35: /*
    36: Subject: Re: Help for extending newbie
    37: From: jim@digicool.com (Jim Fulton)
    38: Newsgroups: comp.lang.python
    39: 
    40:   In article <MPG.cedc3757c747a9b989685@news.connix.com>
    41:   tavares@connix.com (Christopher Tavares) writes:
    42: 
    43:         Hi all! I'm working on my first C extension module (a "lazy" version of
    44:         map) and I've run into a snag that I was hoping to get some advice on.
    45: 
    46:           You see, I'm trying to support passing class instances that have
    47:           __getitem__ defined but not __len__. (For testing, I've been using the
    48:           File class on page 32 of Lutz.)  My Python prototype handled this just
    49:           fine. However, when I call PySequence_GetItem for such a class instance,
    50:           I get "AttributeError: __len__."  Is there any way around this without
    51:           acres of coding, or will I just have to require that sequences I work
    52:           with have __len__ defined?
    53: 
    54:                 Yipes, this is a bug in PySequence_GetItem!  :-(
    55: 
    56:                   I'll submit a fix to Guido, but in the mean time, you might want to
    57:                   simply include this replacement in your module:
    58: */
    59: 
    60: static PyObject *
    61: PySequence_GetItem_fix(s, i)
    62: PyObject *s;
    63: int i;
    64: {
    65:     PySequenceMethods *m;
    66:     int l;
    67: 
    68:     if(! s)
    69:     {
    70:                 if(!PyErr_Occurred())
    71:                 {
    72:                         PyErr_SetString(PyExc_SystemError, "null argument to internal routine");
    73:                 }
    74:                 return NULL;
    75:     }
    76: 
    77:     if(! ((m=s->ob_type->tp_as_sequence) && m->sq_item))
    78:     {
    79:                 PyErr_SetString(PyExc_AttributeError, "__getitem__");
    80:                 return NULL;
    81:     }
    82: 
    83:     if(i < 0)
    84:     {
    85:                 if(! m->sq_length || 0 > (l=m->sq_length(s))) return NULL;
    86:                 i += l;
    87:     }
    88: 
    89:     return m->sq_item(s,i);
    90: }
    91: 
    92: #define PySequence_GetItem PySequence_GetItem_fix
    93: #endif
    94: 
    95: /* ----------------------------------------------------- */
    96: 
    97: /* Declarations for objects of type xmaptype */
    98: 
    99: typedef struct {
   100:         PyObject_HEAD
   101:         /* XXXX Add your own stuff here */
   102:         PyObject *func;         /* Function object */
   103:         unsigned nseqs;         /* Number of sequences */
   104:         PyObject **seqs;        /* List of sequences */
   105: } xmaptobject;
   106: 
   107: staticforward PyTypeObject Xmapttype;
   108: 
   109: #define is_xmapobject(s) ((s)->ob_type == &Xmapttype)
   110: 
   111: /* prototypes for functions we need later */
   112: 
   113: static PyObject *xmapt_item Py_PROTO((xmaptobject *self, int i));
   114: static int xmapt_length Py_PROTO((xmaptobject *self));
   115: static PyObject *xmapt_tolist Py_PROTO((PyObject *self, PyObject *args));
   116: 
   117: /* ---------------------------------------------------------------- */
   118: 
   119: static struct PyMethodDef xmapt_methods[] = {
   120:         {"tolist", xmapt_tolist, 1},
   121:         {NULL, NULL}            /* sentinel */
   122: };
   123: 
   124: /* ---------- */
   125: 
   126: /*
   127: // "constructor" for xmap objects. This is called by the xmap.xmap
   128: // function. It is passed the argument tuple passed to xmap.xmap.
   129: // The arguments have already been validated for correct number,
   130: // types, etc.
   131: */
   132: 
   133: static xmaptobject *
   134: newxmaptobject(args)
   135: PyObject *args;
   136: {
   137:     xmaptobject *self;
   138:     PyObject **seqs;
   139:     unsigned nseqs;
   140:     unsigned seq;
   141: 
   142:     /*
   143:     // Grab memory to store sequence lists. This
   144:     // is done before allocating self to make cleanup
   145:     // a little easier for novices like me.
   146:     */
   147:     nseqs = PyObject_Length(args) - 1;
   148:     assert(nseqs > 0);
   149: 
   150:     seqs = (PyObject **)malloc(sizeof(PyObject *) * nseqs);
   151:     if(seqs == NULL)
   152:     {
   153:                 PyErr_SetString(PyExc_MemoryError,
   154:                         "Could not allocate space for sequence list");
   155:                 return NULL;
   156:     }
   157: 
   158:     self = PyObject_NEW(xmaptobject, &Xmapttype);
   159:     if (self == NULL)
   160:     {
   161:                 free(seqs);
   162:                 return NULL;
   163:     }
   164: 
   165:     self->func = PySequence_GetItem(args, 0);
   166:     self->nseqs = nseqs;
   167:     self->seqs = seqs;
   168: 
   169:     for(seq = 0; seq < nseqs; seq++)
   170:     {
   171:                 seqs[seq] = PySequence_GetItem(args, seq + 1);
   172:                 assert(seqs[seq] != NULL);
   173:     }
   174: 
   175:     return self;
   176: }
   177: 
   178: 
   179: static void
   180: xmapt_dealloc(self)
   181: xmaptobject *self;
   182: {
   183:     unsigned seq;
   184: 
   185:     Py_DECREF(self->func);
   186:     for(seq = 0; seq < self->nseqs; seq++)
   187:     {
   188:                 Py_DECREF(self->seqs[seq]);
   189:     }
   190:     free(self->seqs);
   191:     PyMem_DEL(self);
   192: }
   193: 
   194: /*
   195: // standard getattr function
   196: */
   197: 
   198: static PyObject *xmapt_getattr(self, name)
   199: xmaptobject *self;
   200: char *name;
   201: {
   202:     return Py_FindMethod(xmapt_methods, (PyObject *)self, name);
   203: }
   204: 
   205: /* Print object to fp - print it like a tuple */
   206: static int xmapt_print(self, fp, flags)
   207: xmaptobject *self;
   208: FILE *fp;
   209: int flags;
   210: {
   211:     PyObject *element;
   212:     int i = 0, printcomma = 0;
   213: 
   214:     fprintf(fp, "(xmap: ");
   215: 
   216:     do
   217:     {
   218:                 element = xmapt_item(self, i);
   219:                 if(element != NULL)
   220:                 {
   221:                         if(printcomma)
   222:                         {
   223:                                 fprintf(fp, ", ");
   224:                         }
   225:                         PyObject_Print(element, fp, 0);
   226:                         Py_DECREF(element);
   227:                 }
   228:                 i++;
   229:                 printcomma = 1;
   230:     } while(element != NULL);
   231:     fprintf(fp, ")");
   232:     if(PyErr_Occurred() == PyExc_IndexError)
   233:     {
   234:                 PyErr_Clear();
   235:                 return 0;
   236:     }
   237:     return -1;
   238: }
   239: 
   240: /*
   241: // CUSTOM METHOD: tolist()
   242: // Calculates all the values of the mapping and returns them as a
   243: // list. Effectively the same as just doing a map.
   244: */
   245: 
   246: static PyObject *xmapt_tolist(self, args)
   247: PyObject *self, *args;
   248: {
   249:     PyObject *templist, *element;
   250:     int len, i;
   251: 
   252:     if(self == NULL || !is_xmapobject(self))
   253:     {
   254:                 PyErr_SetString(PyExc_SystemError, "bad self pointer to xmap tolist member");
   255:                 return NULL;
   256:     }
   257: 
   258:     if(!PyArg_ParseTuple(args, ""))
   259:     {
   260:                 return NULL;
   261:     }
   262: 
   263:     /*
   264:     // If we have a length, we can preallocate the list. Otherwise,
   265:     // we have to append to it.
   266:     */
   267:     len = xmapt_length((xmaptobject *)self);
   268:     if(len != -1)
   269:     {
   270:                 templist = PyList_New(len);
   271:                 if(templist == NULL)
   272:                 {
   273:                         return NULL;
   274:                 }
   275:                 for(i = 0; i < len; i++)
   276:                 {
   277:                         element = xmapt_item((xmaptobject *)self, i);
   278:                         if(element == NULL)
   279:                         {
   280:                                 goto bailout;
   281:                         }
   282:                         if(PyList_SetItem(templist, i, element) == -1)
   283:                         {
   284:                                 goto bailout;
   285:                         }
   286:                 }
   287:                 return templist;
   288:     }
   289:     else
   290:     {
   291:                 templist = PyList_New(0);
   292:                 if(templist == NULL)
   293:                 {
   294:                         return NULL;
   295:                 }
   296:                 for(i = 0; ; i++)
   297:                 {
   298:                         element = xmapt_item((xmaptobject *)self, i);
   299:                         if(element != NULL)
   300:                         {
   301:                                 if(PyList_Append(templist, element) == -1)
   302:                                 {
   303:                                         goto bailout;
   304:                                 }
   305:                         }
   306:                         else
   307:                         {
   308:                                 if(PyErr_Occurred() == PyExc_IndexError)
   309:                                 {
   310:                                         PyErr_Clear();
   311:                                         return templist;
   312:                                 }
   313:                                 goto bailout;
   314:                         }
   315:                 }
   316:     }
   317: bailout:
   318:     assert(templist != NULL);
   319:     Py_DECREF(templist);
   320:     return NULL;
   321: }
   322: 
   323: /* Code to handle accessing xmaptype objects as sequence objects */
   324: 
   325: /*
   326: // Len is slightly strange because we need to handle
   327: // "generator" types that may not have a length themselves.
   328: // What we do is we get the length of each of our sequences,
   329: // and if any of them fail, we return -1 (failure), otherwise
   330: // we return the longest length.
   331: */
   332: 
   333: static int
   334: xmapt_length(self)
   335: xmaptobject *self;
   336: {
   337:     unsigned seq;
   338:     int len, curlen;
   339: 
   340:     for(len = 0, seq = 0; seq < self->nseqs; seq++)
   341:     {
   342:                 curlen = PyObject_Length(self->seqs[seq]);
   343:                 if(curlen == -1)
   344:                 {
   345:                         return -1;
   346:                 }
   347:                 if(len < curlen)
   348:                 {
   349:                         len = curlen;
   350:                 }
   351:     }
   352:     return len;
   353: }
   354: 
   355: static PyObject *
   356: xmapt_concat(self, bb)
   357: xmaptobject *self;
   358: PyObject *bb;
   359: {
   360:     /* XXXX Return the concatenation of self and bb */
   361:     PyErr_SetString(PyExc_TypeError, "cannot concatenate xmap objects");
   362:     return NULL;
   363: }
   364: 
   365: static PyObject *
   366: xmapt_repeat(self, n)
   367: xmaptobject *self;
   368: int n;
   369: {
   370:     PyErr_SetString(PyExc_TypeError, "Cannot repeat xmap objects");
   371:     return NULL;
   372: }
   373: 
   374: static PyObject *
   375: xmapt_item(self, i)
   376: xmaptobject *self;
   377: int i;
   378: {
   379:     unsigned seq;
   380:     unsigned errcount = self->nseqs;
   381:     PyObject *arg_list;
   382:     PyObject *item;
   383:     PyObject *result;
   384: 
   385:     /* Create argument tuple */
   386:     arg_list = PyTuple_New(self->nseqs);
   387:     if(arg_list == NULL)
   388:     {
   389:                 return NULL;
   390:     }
   391: 
   392:     /* Pull out items from each sequence */
   393:     for(seq = 0; seq < self->nseqs; seq++)
   394:     {
   395:                 item = PySequence_GetItem(self->seqs[seq], i);
   396:                 if(item != NULL)
   397:                 {
   398:                         PyTuple_SET_ITEM(arg_list, seq, item);
   399:                 }
   400:                 else
   401:                 {
   402:                         if(PyErr_Occurred() == PyExc_IndexError)
   403:                         {
   404:                                 PyErr_Clear();
   405:                                 Py_INCREF(Py_None);
   406:                                 PyTuple_SET_ITEM(arg_list, seq, Py_None);
   407:                                 errcount--;
   408:                         }
   409:                         else
   410:                         {
   411:                                 Py_DECREF(arg_list);
   412:                                 return NULL;
   413:                         }
   414:                 }
   415:     }
   416: 
   417:     /*
   418:     // If we got here and errcount == 0, we got IndexError for
   419:     // every sequence. Therefore, we bail, returning IndexError.
   420:     */
   421:     if(errcount == 0)
   422:     {
   423:                 PyErr_SetString(PyExc_IndexError, "index out of range");
   424:                 Py_DECREF(arg_list);
   425:                 return NULL;
   426:     }
   427: 
   428:     /*
   429:     // If function is None, return arg_list tuple, with one exception.
   430:     // If there's only one element in the argument list, just return
   431:     // that element.
   432:     */
   433:     if(self->func == Py_None)
   434:     {
   435:                 if(self->nseqs == 1)
   436:                 {
   437:                         result = PySequence_GetItem(arg_list, 0);
   438:                 }
   439:                 else
   440:                 {
   441:                         result = arg_list;
   442:                         Py_INCREF(result);
   443:                 }
   444:     }
   445:     else
   446:     {
   447:         /*
   448:         // Function is NOT null, so we call it and get the result
   449:                 */
   450:                 result = PyObject_CallObject(self->func, arg_list);
   451:     }
   452: 
   453:     /* Clean up argument list, return result */
   454:     Py_DECREF(arg_list);
   455:     return result;
   456: }
   457: 
   458: /*
   459: // get slice method. We do this by grabbing the indicated slice
   460: // from each input sequence and then creating a new xmap object
   461: // with the same function.
   462: */
   463: 
   464: static PyObject *
   465: xmapt_slice(self, ilow, ihigh)
   466: xmaptobject *self;
   467: int ilow, ihigh;
   468: {
   469:     PyObject *args;             /* arguments to create new xmap object */
   470:     PyObject *slice;            /* Slice of input sequence */
   471:     xmaptobject *new_xmap;      /* New xmap object to be created */
   472:     unsigned int i;
   473: 
   474:     /* Create argument tuple */
   475:     args = PyTuple_New(self->nseqs + 1); /* func + sequences */
   476:     if(args == NULL)
   477:                 return NULL;
   478: 
   479:     Py_INCREF(self->func);
   480:     PyTuple_SET_ITEM(args, 0, self->func);
   481:     for(i = 0; i < self->nseqs; i++)
   482:     {
   483:                 slice = PySequence_GetSlice(self->seqs[i], ilow, ihigh);
   484:                 if(slice == NULL)
   485:                 {
   486:                         Py_DECREF(args);
   487:                         return NULL;
   488:                 }
   489:                 PyTuple_SET_ITEM(args, i + 1, slice);
   490:     }
   491: 
   492:     new_xmap = newxmaptobject(args);
   493:     Py_DECREF(args);
   494:     return (PyObject *)new_xmap;
   495: }
   496: 
   497: static PySequenceMethods xmapt_as_sequence = {
   498:     (inquiry)xmapt_length,      /*sq_length*/
   499:                 (binaryfunc)xmapt_concat,       /*sq_concat*/
   500:                 (intargfunc)xmapt_repeat,       /*sq_repeat*/
   501:                 (intargfunc)xmapt_item, /*sq_item*/
   502:                 (intintargfunc)xmapt_slice,     /*sq_slice*/
   503:                 (intobjargproc)0,               /*sq_ass_item*/
   504:                 (intintobjargproc)0,    /*sq_ass_slice*/
   505: };
   506: 
   507: /* -------------------------------------------------------------- */
   508: 
   509: static char Xmapttype__doc__[] =
   510: ""
   511: ;
   512: 
   513: statichere PyTypeObject Xmapttype = {
   514: #if defined(MS_WINDOWS)
   515:     PyObject_HEAD_INIT(NULL)
   516: #else
   517:                 PyObject_HEAD_INIT(&PyType_Type)
   518: #endif
   519:                 0,                              /*ob_size*/
   520:                 "xmaptype",                     /*tp_name*/
   521:                 sizeof(xmaptobject),    /*tp_basicsize*/
   522:                 0,                              /*tp_itemsize*/
   523:                 /* methods */
   524:                 (destructor)xmapt_dealloc,      /*tp_dealloc*/
   525:                 (printfunc)xmapt_print, /*tp_print*/
   526:                 (getattrfunc)xmapt_getattr,     /*tp_getattr*/
   527:                 (setattrfunc)0,         /*tp_setattr*/
   528:                 (cmpfunc)0,                     /*tp_compare*/
   529:                 (reprfunc)0,            /*tp_repr*/
   530:                 0,                              /*tp_as_number*/
   531:                 &xmapt_as_sequence,             /*tp_as_sequence*/
   532:                 0,                              /*tp_as_mapping*/
   533:                 (hashfunc)0,            /*tp_hash*/
   534:                 (ternaryfunc)0,         /*tp_call*/
   535:                 (reprfunc)0,            /*tp_str*/
   536: 
   537:                 /* Space for future expansion */
   538:                 0L,0L,0L,0L,
   539:                 Xmapttype__doc__ /* Documentation string */
   540: };
   541: 
   542: /* End of code for xmaptype objects */
   543: /* -------------------------------------------------------- */
   544: 
   545: 
   546: static char xmap_xmap__doc__[] =
   547: ""
   548: ;
   549: 
   550: /*
   551: // Verify arguments, then create a new xmap object with
   552: // the same arguments
   553: */
   554: 
   555: static PyObject *
   556: xmap_xmap(self, args)
   557: PyObject *self;
   558: PyObject *args;
   559: {
   560:     PyObject *func;
   561:     PyObject *seq;
   562:     PyObject *result = NULL;
   563:     int arg, len;
   564: 
   565:     /* Check we've got at least 2 arguments */
   566:     len = PyObject_Length(args);
   567:     if(len < 2)
   568:     {
   569:                 PyErr_SetString(PyExc_TypeError, "must have at least two arguments");
   570:                 return NULL;
   571:     }
   572: 
   573:     func = PySequence_GetItem(args, 0);
   574:     if(func != Py_None && !PyCallable_Check(func))
   575:     {
   576:                 PyErr_SetString(PyExc_TypeError, "function argument must be callable");
   577:                 goto done;
   578:     }
   579: 
   580:     for(arg = 1; arg < len; arg++)
   581:     {
   582:                 seq = PySequence_GetItem(args, arg);
   583:                 if(seq == NULL)
   584:                 {
   585:                         goto done;
   586:                 }
   587:                 if(!PySequence_Check(seq))
   588:                 {
   589:                         PyErr_SetString(PyExc_TypeError, "arguments must be sequences");
   590:                         Py_DECREF(seq);
   591:                         goto done;
   592:                 }
   593:                 Py_DECREF(seq);
   594:     }
   595: 
   596:     /* If we're here, arguments are OK */
   597:     result = (PyObject *)newxmaptobject(args);
   598: done:
   599:     /* Clean up and return whatever happened */
   600:     Py_DECREF(func);
   601:     return result;
   602: }
   603: 
   604: /* List of methods defined in the module */
   605: 
   606: static struct PyMethodDef xmap_methods[] = {
   607:         {"xmap",        (PyCFunction)xmap_xmap, METH_VARARGS,   xmap_xmap__doc__},
   608: 
   609:         {NULL,   (PyCFunction)NULL, 0, NULL}            /* sentinel */
   610: };
   611: 
   612: 
   613: /* Initialization function for the module (*must* be called initxmap) */
   614: 
   615: static char xmap_module_documentation[] =
   616: "xmap: \"Lazy\" implementation of map\n"
   617: "Xmap implements an object type that has the same relationship to map\n"
   618: "that xrange does to range. map produces a list and calculates all the\n"
   619: "values up front. xmap produces an object that generates the values\n"
   620: "as they are indexed.\n"
   621: "\nUsage:\n"
   622: "\txmap(func, seq, [seq, seq, ...])\n"
   623: "xmap object support indexing (obviously) and slicing (by forming slices of\n"
   624: "the input sequences and creating a new xmap object).\n"
   625: "\nGetting the length of an xmap object is a special case. Unlike map, xmap\n"
   626: "can handle being given a sequence that does not have a __len__ method.\n"
   627: "However, if any of the input sequences to xmap do not have __len__ defined,\n"
   628: "then the resulting xmap object will not have __len__ defined either.\n"
   629: "\nxmap objects do not support repetition or concatenation.\n"
   630: "\nxmap objects also support one method: x.tolist(). This calculates all the\n"
   631: "values and returns them as a list.\n"
   632: ;
   633: 
   634: void initxmap()
   635: {
   636:         PyObject *m;
   637: 
   638: #ifdef MS_WINDOWS
   639:         /* Get around "Initializer not constant" problem in Windows */
   640:         Xmapttype.ob_type = &PyType_Type;
   641: #endif
   642:         /* Create the module and add the functions */
   643:         m = Py_InitModule4("xmap", xmap_methods,
   644:                 xmap_module_documentation,
   645:                 (PyObject*)NULL,PYTHON_API_VERSION);
   646: 
   647: 
   648:         /* Check for errors */
   649:         if (PyErr_Occurred())
   650:                 Py_FatalError("can't initialize module xmap");
   651: }
   652: 
   653: 
End C section to interscript/core/xmap.c[1]