extension module help -- somewhat LONG

knotwell at my-deja.com knotwell at my-deja.com
Tue Aug 29 14:17:32 EDT 2000


Hello all--

Sorry 'bout the long post.  Anyhow, I've been working on an extension
module providing something similar to Common Lisp's standard sequence
functions.  Anyhow, I first started by recreating map.  I've replicated
Python's map functionality, but I've a small problem -- expressions like
map(None,(1,2),(3,4)).

With a python hack (see id and idstr in the init portion of the module;
this is why I've included the whole module), I've added a function that
tupleizes the arguments.  This is bad (bad is approximately 4 times
slower than the built-in and leaks memory).  In any case, does anyone
know what I need to do to get nextTupleApply to return the correct
value?  FWIW:  I thought I'd just be able to return alist, but I
apparently have some sort of reference count error when I do this.

TIA and apologies for the formatting.

--Brad

/* copyright Brad Knotwell 2000
   with parts taken from bltinmodule.c. . .how that works copyright-wise
   I've no idea */

#include <stdio.h>
#include <stdlib.h>
#include "Python.h"

typedef struct {
  PyObject *seq;
  PySequenceMethods *sqf;
  int len;
} sequence;

typedef struct {
  int maxlen;
  int numseqs;
  PyObject *function;
  sequence *seqs;
} argdata;

static enum {
  TooFewArgs,
  NotASequence,
  BadTupleLen
} clseqErrorEnums;

static char *clseqErrorStrings[] = {
  "%s() requires at least two args",
  "argument %d to %s() must be a sequence object",
  "bad tuple length(%d) passed to %s"
};

static PyObject *id;

#define NULLRETURN { -1, 0, NULL, NULL }

static argdata
validateArguments(char *pyname,PyObject *args)
{
  char errstring[80];
  int n;
  argdata retval = NULLRETURN;

  if ((n = PyTuple_Size(args)) < 2)
    {
      sprintf(errstring,(char *) clseqErrorStrings[TooFewArgs],pyname);
      PyErr_SetString(PyExc_TypeError,(char *) errstring);
      goto Fail;
    }

  retval.function = PyTuple_GetItem(args,0);
  retval.numseqs = --n;
  if (! (retval.seqs = PyMem_NEW(sequence,n)))
    {
      PyErr_NoMemory();
      goto Fail;
    }

  {
    sequence *sqp;
    int i;
    for (retval.maxlen = 0, i = 0, sqp = retval.seqs; i < n; ++i, ++sqp)
      {
        int tuple_len;
        if ((sqp->seq = PyTuple_GetItem(args,i+1)) == NULL)
          goto Fail;

        sqp->sqf = sqp->seq->ob_type->tp_as_sequence;
        if ((! sqp->sqf) ||
            (! sqp->sqf->sq_length) ||
            (! sqp->sqf->sq_item))
          {

sprintf(errstring,clseqErrorStrings[NotASequence],i+2,pyname);
            PyErr_SetString(PyExc_TypeError,errstring);
            goto Fail;
          }

        if ((tuple_len = (*sqp->sqf->sq_length)(sqp->seq)) < 0)
          {

sprintf(errstring,clseqErrorStrings[BadTupleLen],pyname,tuple_len);
            PyErr_SetString(PyExc_TypeError,errstring);
            goto Fail;
          }

        if (tuple_len > retval.maxlen)
          retval.maxlen = tuple_len;
      }
  }

  return retval;

 Fail:
  if (retval.seqs)
    PyMem_DEL(retval.seqs);
  return (argdata) NULLRETURN;

}

static PyObject *
nextTupleApply(const argdata arguments,int index,PyObject *alist)
{

  PyObject *item;
  sequence *sqp;
  int i,status = 0;

  for(i = 0, sqp = arguments.seqs; i < arguments.numseqs; i++, sqp++)
    {
      if (! (item = (*sqp->sqf->sq_item)(sqp->seq,index)))
 	{
	  if (PyErr_ExceptionMatches(PyExc_IndexError))
	    {
	      PyErr_Clear();
	      Py_INCREF(Py_None);
	      item = Py_None;
	    }
	  else
	    return NULL;
	}

      if (PyTuple_SetItem(alist,i,item) < 0)
	{
	  Py_DECREF(item);
	  return NULL;
	}
    }

  if (arguments.function != Py_None)
    return PyEval_CallObject(arguments.function,alist);
  else
    return PyEval_CallObject(id,alist);

}

static PyObject *
map(const argdata arguments,PyObject *alist)
{
  PyObject *result,*value;
  int outerindex = 0;
  int max_seq_len = arguments.maxlen;

  if ((result = (PyObject *) PyList_New(arguments.maxlen)) == NULL)
    return NULL;

 mainloop:
  if ((value = nextTupleApply(arguments,outerindex,alist)) &&
      (! PyList_SetItem(result,outerindex,value)))
    {
      if (++outerindex == arguments.maxlen)
	{
	  Py_DECREF(alist);
	  return result;
	}

      goto mainloop;
    }

  if (value)
    goto VALUEFAIL;

 VALUEFAIL:
  Py_DECREF(value);
 RESULTFAIL:
  Py_DECREF(result);
  return NULL;
}

static PyObject *
clseq2_map(PyObject *self,PyObject *args)
{
  PyObject *retval;
  argdata arguments = validateArguments("map",args);

  if (arguments.maxlen != -1)
    {
      if ((arguments.function != Py_None) || (arguments.numseqs != 1))
	{
	  PyObject *alist;
	  if ((alist = (PyObject *) PyTuple_New(arguments.numseqs)))
	    {
	      retval = map(arguments,alist);
	      Py_DECREF(alist);
	    }
	  else
	    {
	      PyMem_DEL(arguments.seqs);
	      return NULL;
	    }
	}
      else
	retval = PySequence_List((arguments.seqs)->seq);

      PyMem_DEL(arguments.seqs);
      return retval;
    }
  else
    return NULL;
}

static char map_doc[] =
"map(function, sequence[, sequence, ...]) -> list\n\
\n\
Return a list of the results of applying the function to the items of\n\
the argument sequence(s).  If more than one sequence is given, the\n\
function is called with an argument list consisting of the
corresponding\n\
item of each sequence, substituting None for missing values when not
all\n\
sequences have the same length.  If the function is None, return a list
of\n\
the items of the sequence (or a list of tuples if more than one
sequence).";


static PyMethodDef clseq2_methods[] = {
  {"map",       clseq2_map,      1, map_doc},
  {NULL,        NULL},
};

static char clseq2_doc[] = "common lisp sequence functions\n";

PyObject *
initclseq2(void)
{
  PyObject *mod, *dict, *debug;
  char idstr[80] = "def __id__(*args): \n  return tuple(args)\n";

  mod = Py_InitModule4("clseq2", clseq2_methods,
                       clseq2_doc, (PyObject *)NULL,
                       PYTHON_API_VERSION);
  if (mod == NULL)
    return NULL;
  dict = PyModule_GetDict(mod);
  if (PyDict_SetItemString(dict, "None", Py_None) < 0)
    return NULL;
  debug = PyInt_FromLong(Py_OptimizeFlag == 0);
  if (PyDict_SetItemString(dict, "__debug__", debug) < 0) {
    Py_XDECREF(debug);
    return NULL;
  }
  Py_XDECREF(debug);


  if (PyRun_SimpleString(idstr) < 0)
    return NULL;

  id =
PyRun_String("__id__",Py_eval_input,PyEval_GetGlobals(),PyEval_GetLocals());
  if (! id)
    return NULL;

  return mod;
}


Sent via Deja.com http://www.deja.com/
Before you buy.



More information about the Python-list mailing list