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: