[Stackless-checkins] CVS: slpdev/src/2.2/src/Tools/framer/framer __init__.py, NONE, 1.1 bases.py, NONE, 1.1 function.py, NONE, 1.1 member.py, NONE, 1.1 slots.py, NONE, 1.1 struct.py, NONE, 1.1 structparse.py, NONE, 1.1 template.py, NONE, 1.1 util.py, NONE, 1.1

Christian Tismer tismer at centera.de
Sat May 1 02:54:49 CEST 2004


Update of /home/cvs/slpdev/src/2.2/src/Tools/framer/framer
In directory centera.de:/home/tismer/slpdev/src/2.2/src/Tools/framer/framer

Added Files:
	__init__.py bases.py function.py member.py slots.py struct.py 
	structparse.py template.py util.py 
Log Message:
added files

--- NEW FILE: __init__.py ---
"""A tool to generate basic framework for C extension types.

The basic ideas is the same as modulator, but the code generates code
using many of the new features introduced in Python 2.2.  It also
takes a more declarative approach to generating code.
"""



--- NEW FILE: bases.py ---
"""Provides the Module and Type base classes that user code inherits from."""

__all__ = ["Module", "Type", "member"]

from framer import struct, template
from framer.function import Function, Method
from framer.member import member
from framer.slots import *
from framer.util import cstring, unindent

from types import FunctionType

def sortitems(dict):
    L = dict.items()
    L.sort()
    return L

# The Module and Type classes are implemented using metaclasses,
# because most of the methods are class methods.  It is easier to use
# metaclasses than the cumbersome classmethod() builtin.  They have
# class methods because they are exposed to user code as base classes.

class BaseMetaclass(type):
    """Shared infrastructure for generating modules and types."""

    # just methoddef so far

    def dump_methoddef(self, f, functions, vars):
        def p(templ, vars=vars): # helper function to generate output
            print >> f, templ % vars

        if not functions:
            return
        p(template.methoddef_start)
        for name, func in sortitems(functions):
            if func.__doc__:
                p(template.methoddef_def_doc, func.vars)
            else:
                p(template.methoddef_def, func.vars)
        p(template.methoddef_end)

class ModuleMetaclass(BaseMetaclass):
    """Provides methods for Module class."""

    def gen(self):
        self.analyze()
        self.initvars()
        f = open(self.__filename, "w")
        self.dump(f)
        f.close()

    def analyze(self):
        self.name = getattr(self, "abbrev", self.__name__)
        self.__functions = {}
        self.__types = {}
        self.__members = False

        for name, obj in self.__dict__.iteritems():
            if isinstance(obj, FunctionType):
                self.__functions[name] = Function(obj, self)
            elif isinstance(obj, TypeMetaclass):
                obj._TypeMetaclass__module = self.name
                obj.analyze()
                self.__types[name] = obj
                if obj.has_members():
                    self.__members = True
                    
    def initvars(self):
        v = self.__vars = {}
        filename = getattr(self, "__file__", None)
        if filename is None:
            filename = self.__name__ + "module.c"
        self.__filename = v["FileName"] = filename
        name = v["ModuleName"] = self.__name__
        v["MethodDefName"] = "%s_methods" % name
        v["ModuleDocstring"] = cstring(unindent(self.__doc__))

    def dump(self, f):
        def p(templ, vars=self.__vars): # helper function to generate output
            print >> f, templ % vars

        p(template.module_start)
        if self.__members:
            p(template.member_include)
        print >> f
        
        if self.__doc__:
            p(template.module_doc)

        for name, type in sortitems(self.__types):
            type.dump(f)

        for name, func  in sortitems(self.__functions):
            func.dump(f)

        self.dump_methoddef(f, self.__functions, self.__vars)

        p(template.module_init_start)
        for name, type in sortitems(self.__types):
            type.dump_init(f)
            
        p("}")

class Module:
    __metaclass__ = ModuleMetaclass

class TypeMetaclass(BaseMetaclass):

    def dump(self, f):
        self.initvars()

        # defined after initvars() so that __vars is defined
        def p(templ, vars=self.__vars):
            print >> f, templ % vars

        if self.struct is not None:
            print >> f, unindent(self.struct, False)

        if self.__doc__:
            p(template.docstring)

        for name, func in sortitems(self.__methods):
            func.dump(f)
            
        self.dump_methoddef(f, self.__methods, self.__vars)
        self.dump_memberdef(f)
        self.dump_slots(f)

    def has_members(self):
        if self.__members:
            return True
        else:
            return False

    def analyze(self):
        # called by ModuleMetaclass analyze()
        self.name = getattr(self, "abbrev", self.__name__)
        src = getattr(self, "struct", None)
        if src is not None:
            self.__struct = struct.parse(src)
        else:
            self.__struct = None
        self.__methods = {}
        self.__members = {}
        for cls in self.__mro__:
            for k, v in cls.__dict__.iteritems():
                if isinstance(v, FunctionType):
                    self.__methods[k] = Method(v, self)
                if isinstance(v, member):
                    self.__members[k] = v
                    assert self.__struct is not None
                    v.register(k, self.__struct)
        self.analyze_slots()

    def analyze_slots(self):
        self.__slots = {}
        for s in Slots:
            if s.special is not None:
                meth = self.__methods.get(s.special)
                if meth is not None:
                    self.__slots[s] = meth
        self.__slots[TP_NAME] = '"%s.%s"' % (self.__module, self.__name__)
        if self.__doc__:
            self.__slots[TP_DOC] = "%s_doc" % self.name
        if self.__struct is not None:
            self.__slots[TP_BASICSIZE] = "sizeof(%s)" % self.__struct.name
            self.__slots[TP_DEALLOC] = "%s_dealloc" % self.name
        if self.__methods:
            self.__slots[TP_METHODS] = "%s_methods" % self.name
        if self.__members:
            self.__slots[TP_MEMBERS] = "%s_members" % self.name

    def initvars(self):
        v = self.__vars = {}
        v["TypeName"] = self.__name__
        v["CTypeName"] = "Py%s_Type" % self.__name__
        v["MethodDefName"] = self.__slots[TP_METHODS]
        if self.__doc__:
            v["DocstringVar"] = self.__slots[TP_DOC]
            v["Docstring"] = cstring(unindent(self.__doc__))
        if self.__struct is not None:
            v["StructName"] = self.__struct.name
        if self.__members:
            v["MemberDefName"] = self.__slots[TP_MEMBERS]

    def dump_memberdef(self, f):
        def p(templ, vars=self.__vars):
            print >> f, templ % vars

        if not self.__members:
            return
        p(template.memberdef_start)
        for name, slot in sortitems(self.__members):
            slot.dump(f)
        p(template.memberdef_end)

    def dump_slots(self, f):
        def p(templ, vars=self.__vars):
            print >> f, templ % vars

        if self.struct:
           p(template.dealloc_func, {"name" : self.__slots[TP_DEALLOC]})

        p(template.type_struct_start)
        for s in Slots[:-5]: # XXX
            val = self.__slots.get(s, s.default)
            ntabs = 4 - (4 + len(val)) / 8
            line = "        %s,%s/* %s */" % (val, "\t" * ntabs, s.name)
            print >> f, line
        p(template.type_struct_end)

    def dump_init(self, f):
        def p(templ):
            print >> f, templ % self.__vars

        p(template.type_init_type)
        p(template.module_add_type)

class Type:
    __metaclass__ = TypeMetaclass


--- NEW FILE: function.py ---
"""Functions."""

from framer import template
from framer.util import cstring, unindent

METH_O = "METH_O"
METH_NOARGS = "METH_NOARGS"
METH_VARARGS = "METH_VARARGS"

def parsefmt(fmt):
    for c in fmt:
        if c == '|':
            continue
        yield c

class Argument:

    def __init__(self, name):
        self.name = name
        self.ctype = "PyObject *"
        self.default = None

    def __str__(self):
        return "%s%s" % (self.ctype, self.name)

    def setfmt(self, code):
        self.ctype = self._codes[code]
        if self.ctype[-1] != "*":
            self.ctype += " "

    _codes = {"O": "PyObject *",
              "i": "int",
              }

    def decl(self):
        if self.default is None:
            return str(self) + ";"
        else:
            return "%s = %s;" % (self, self.default)

class _ArgumentList(object):

    # these instance variables should be initialized by subclasses
    ml_meth = None
    fmt = None

    def __init__(self, args):
        self.args = map(Argument, args)

    def __len__(self):
        return len(self.args)

    def __getitem__(self, i):
        return self.args[i]

    def dump_decls(self, f):
        pass
        
class NoArgs(_ArgumentList):

    def __init__(self, args):
        assert len(args) == 0
        super(NoArgs, self).__init__(args)
        self.ml_meth = METH_NOARGS

    def c_args(self):
        return "PyObject *self"

class OneArg(_ArgumentList):
    
    def __init__(self, args):
        assert len(args) == 1
        super(OneArg, self).__init__(args)
        self.ml_meth = METH_O

    def c_args(self):
        return "PyObject *self, %s" % self.args[0]

class VarArgs(_ArgumentList):

    def __init__(self, args, fmt=None):
        super(VarArgs, self).__init__(args)
        self.ml_meth = METH_VARARGS
        if fmt is not None:
            self.fmt = fmt
            i = 0
            for code in parsefmt(fmt):
                self.args[i].setfmt(code)
                i += 1

    def c_args(self):
        return "PyObject *self, PyObject *args"

    def targets(self):
        return ", ".join(["&%s" % a.name for a in self.args])

    def dump_decls(self, f):
        for a in self.args:
            print >> f, "        %s" % a.decl()

def ArgumentList(func, method):
    code = func.func_code
    args = code.co_varnames[:code.co_argcount]
    if method:
        args = args[1:]
    pyarg = getattr(func, "pyarg", None)
    if pyarg is not None:
        args = VarArgs(args, pyarg)
        if func.func_defaults:
            L = list(func.func_defaults)
            ndefault = len(L)
            i = len(args) - ndefault
            while L:
                args[i].default = L.pop(0)
        return args
    else:
        if len(args) == 0:
            return NoArgs(args)
        elif len(args) == 1:
            return OneArg(args)
        else:
            return VarArgs(args)

class Function:

    method = False

    def __init__(self, func, parent):
        self._func = func
        self._parent = parent
        self.analyze()
        self.initvars()

    def dump(self, f):
        def p(templ, vars=None): # helper function to generate output
            if vars is None:
                vars = self.vars
            print >> f, templ % vars

        if self.__doc__:
            p(template.docstring)
            
        d = {"name" : self.vars["CName"],
             "args" : self.args.c_args(),
             }
        p(template.funcdef_start, d)

        self.args.dump_decls(f)

        if self.args.ml_meth == METH_VARARGS:
            p(template.varargs)
        
        p(template.funcdef_end)

    def analyze(self):
        self.__doc__ = self._func.__doc__
        self.args = ArgumentList(self._func, self.method)
        
    def initvars(self):
        v = self.vars = {}
        v["PythonName"] = self._func.__name__
        s = v["CName"] = "%s_%s" % (self._parent.name, self._func.__name__)
        v["DocstringVar"] = s + "_doc"
        v["MethType"] = self.args.ml_meth
        if self.__doc__:
            v["Docstring"] = cstring(unindent(self.__doc__))
        if self.args.fmt is not None:
            v["ArgParse"] = self.args.fmt
            v["ArgTargets"] = self.args.targets()

class Method(Function):

    method = True

--- NEW FILE: member.py ---
from framer import template
from framer.util import cstring, unindent

T_SHORT = "T_SHORT"
T_INT = "T_INT"
T_LONG = "T_LONG"
T_FLOAT = "T_FLOAT"
T_DOUBLE = "T_DOUBLE"
T_STRING = "T_STRING"
T_OBJECT = "T_OBJECT"
T_CHAR = "T_CHAR"
T_BYTE = "T_BYTE"
T_UBYTE = "T_UBYTE"
T_UINT = "T_UINT"
T_ULONG = "T_ULONG"
T_STRING_INPLACE = "T_STRING_INPLACE"
T_OBJECT_EX = "T_OBJECT_EX"

RO = READONLY = "READONLY"
READ_RESTRICTED = "READ_RESTRICTED"
WRITE_RESTRICTED = "WRITE_RESTRICTED"
RESTRICT = "RESTRICTED"

c2t = {"int" : T_INT,
       "unsigned int" : T_UINT,
       "long" : T_LONG,
       "unsigned long" : T_LONG,
       "float" : T_FLOAT,
       "double" : T_DOUBLE,
       "char *" : T_CHAR,
       "PyObject *" : T_OBJECT,
       }

class member(object):

    def __init__(self, cname=None, type=None, flags=None, doc=None):
        self.type = type
        self.flags = flags
        self.cname = cname
        self.doc = doc
        self.name = None
        self.struct = None

    def register(self, name, struct):
        self.name = name
        self.struct = struct
        self.initvars()

    def initvars(self):
        v = self.vars = {}
        v["PythonName"] = self.name
        if self.cname is not None:
            v["CName"] = self.cname
        else:
            v["CName"] = self.name
        v["Flags"] = self.flags or "0"
        v["Type"] = self.get_type()
        if self.doc is not None:
            v["Docstring"] = cstring(unindent(self.doc))
        v["StructName"] = self.struct.name

    def get_type(self):
        """Deduce type code from struct specification if not defined"""
        if self.type is not None:
            return self.type
        ctype = self.struct.get_type(self.name)
        return c2t[ctype]

    def dump(self, f):
        if self.doc is None:
            print >> f, template.memberdef_def % self.vars
        else:
            print >> f, template.memberdef_def_doc % self.vars

--- NEW FILE: slots.py ---
"""Descriptions of all the slots in Python's type objects."""

class Slot(object):
    def __init__(self, name, cast=None, special=None, default="0"):
        self.name = name
        self.cast = cast
        self.special = special
        self.default = default

Slots = (Slot("ob_size"),
         Slot("tp_name"),
         Slot("tp_basicsize"),
         Slot("tp_itemsize"),
         Slot("tp_dealloc", "destructor"),
         Slot("tp_print", "printfunc"),
         Slot("tp_getattr", "getattrfunc"),
         Slot("tp_setattr", "setattrfunc"),
         Slot("tp_compare", "cmpfunc", "__cmp__"),
         Slot("tp_repr", "reprfunc", "__repr__"),
         Slot("tp_as_number"),
         Slot("tp_as_sequence"),
         Slot("tp_as_mapping"),
         Slot("tp_hash", "hashfunc", "__hash__"),
         Slot("tp_call", "ternaryfunc", "__call__"),
         Slot("tp_str", "reprfunc", "__str__"),
         Slot("tp_getattro", "getattrofunc", "__getattr__", # XXX
              "PyObject_GenericGetAttr"),
         Slot("tp_setattro", "setattrofunc", "__setattr__"),
         Slot("tp_as_buffer"),
         Slot("tp_flags", default="Py_TPFLAGS_DEFAULT"),
         Slot("tp_doc"),
         Slot("tp_traverse", "traverseprox"),
         Slot("tp_clear", "inquiry"),
         Slot("tp_richcompare", "richcmpfunc"),
         Slot("tp_weaklistoffset"),
         Slot("tp_iter", "getiterfunc", "__iter__"),
         Slot("tp_iternext", "iternextfunc", "__next__"), # XXX
         Slot("tp_methods"),
         Slot("tp_members"),
         Slot("tp_getset"),
         Slot("tp_base"),
         Slot("tp_dict"),
         Slot("tp_descr_get", "descrgetfunc"),
         Slot("tp_descr_set", "descrsetfunc"),
         Slot("tp_dictoffset"),
         Slot("tp_init", "initproc", "__init__"),
         Slot("tp_alloc", "allocfunc"),
         Slot("tp_new", "newfunc"),
         Slot("tp_free", "freefunc"),
         Slot("tp_is_gc", "inquiry"),
         Slot("tp_bases"),
         Slot("tp_mro"),
         Slot("tp_cache"),
         Slot("tp_subclasses"),
         Slot("tp_weaklist"),
         )

# give some slots symbolic names
TP_NAME = Slots[1]
TP_BASICSIZE = Slots[2]
TP_DEALLOC = Slots[4]
TP_DOC = Slots[20]
TP_METHODS = Slots[27]
TP_MEMBERS = Slots[28]

--- NEW FILE: struct.py ---
"""Rudimentary parser for C struct definitions."""

import re

PyObject_HEAD = "PyObject_HEAD"
PyObject_VAR_HEAD = "PyObject_VAR_HEAD"

rx_name = re.compile("} (\w+);")

class Struct:
    def __init__(self, name, head, members):
        self.name = name
        self.head = head
        self.members = members

    def get_type(self, name):
        for _name, type in self.members:
            if name == _name:
                return type
        raise ValueError, "no member named %s" % name

def parse(s):
    """Parse a C struct definition.

    The parser is very restricted in what it will accept.
    """

    lines = filter(None, s.split("\n")) # get non-empty lines
    assert lines[0].strip() == "typedef struct {"
    pyhead = lines[1].strip()
    assert (pyhead.startswith("PyObject") and
            pyhead.endswith("HEAD"))
    members = []
    for line in lines[2:]:
        line = line.strip()
        if line.startswith("}"):
            break
        
        assert line.endswith(";")
        line = line[:-1]
        words = line.split()
        name = words[-1]
        type = " ".join(words[:-1])
        if name[0] == "*":
            name = name[1:]
            type += " *"
        members.append((name, type))
    name = None
    mo = rx_name.search(line)
    assert mo is not None
    name = mo.group(1)
    return Struct(name, pyhead, members)

--- NEW FILE: structparse.py ---
"""Rudimentary parser for C struct definitions."""

import re

PyObject_HEAD = "PyObject_HEAD"
PyObject_VAR_HEAD = "PyObject_VAR_HEAD"

rx_name = re.compile("} (\w+);")

class Struct:
    def __init__(self, name, head, members):
        self.name = name
        self.head = head
        self.members = members

def parse(s):
    """Parse a C struct definition.

    The parser is very restricted in what it will accept.
    """

    lines = filter(None, s.split("\n")) # get non-empty lines
    assert lines[0].strip() == "typedef struct {"
    pyhead = lines[1].strip()
    assert (pyhead.startswith("PyObject") and
            pyhead.endswith("HEAD"))
    members = []
    for line in lines[2:]:
        line = line.strip()
        if line.startswith("}"):
            break
        
        assert line.endswith(";")
        line = line[:-1]
        words = line.split()
        name = words[-1]
        type = " ".join(words[:-1])
        if name[0] == "*":
            name = name[1:]
            type += " *"
        members.append((name, type))
    name = None
    mo = rx_name.search(line)
    assert mo is not None
    name = mo.group(1)
    return Struct(name, pyhead, members)

--- NEW FILE: template.py ---
"""framer's C code templates.

Templates use the following variables:

FileName: name of the file that contains the C source code
ModuleName: name of the module, as in "import ModuleName"
ModuleDocstring: C string containing the module doc string
"""

module_start = '#include "Python.h"'
member_include = '#include "structmember.h"'

module_doc = """\
PyDoc_STRVAR(%(ModuleName)s_doc,
%(ModuleDocstring)s);
"""

methoddef_start = """\
static struct PyMethodDef %(MethodDefName)s[] = {"""

methoddef_def = """\
        {"%(PythonName)s", (PyCFunction)%(CName)s, %(MethType)s},"""
        
methoddef_def_doc = """\
        {"%(PythonName)s", (PyCFunction)%(CName)s, %(MethType)s,
         %(DocstringVar)s},"""
        
methoddef_end = """\
        {NULL, NULL}
};
"""

memberdef_start = """\
#define OFF(X) offsetof(%(StructName)s, X)

static struct PyMemberDef %(MemberDefName)s[] = {"""

memberdef_def_doc = """\
        {"%(PythonName)s", %(Type)s, OFF(%(CName)s), %(Flags)s,
         %(Docstring)s},"""

memberdef_def = """\
        {"%(PythonName)s", %(Type)s, OFF(%(CName)s), %(Flags)s},"""

memberdef_end = """\
        {NULL}
};

#undef OFF
"""

dealloc_func = """static void
%(name)s(PyObject *ob)
{
}
"""

docstring = """\
PyDoc_STRVAR(%(DocstringVar)s,
%(Docstring)s);
"""

funcdef_start = """\
static PyObject *
%(name)s(%(args)s)
{"""

funcdef_end = """\
}
"""

varargs = """\
        if (!PyArg_ParseTuple(args, \"%(ArgParse)s:%(PythonName)s\",
                              %(ArgTargets)s))
                return NULL;"""

module_init_start = """\
PyMODINIT_FUNC
init%(ModuleName)s(void)
{
        PyObject *mod;

        mod = Py_InitModule3("%(ModuleName)s", %(MethodDefName)s,
                             %(ModuleName)s_doc);
        if (mod == NULL)
                return;
"""

type_init_type = "        %(CTypeName)s.ob_type = &PyType_Type;"
module_add_type = """\
        if (!PyObject_SetAttrString(mod, "%(TypeName)s",
                                    (PyObject *)&%(CTypeName)s))
                return;
"""

type_struct_start = """\
static PyTypeObject %(CTypeName)s = {
        PyObject_HEAD_INIT(0)"""
        
type_struct_end = """\
};
"""

--- NEW FILE: util.py ---
def cstring(s, width=70):
    """Return C string representation of a Python string.

    width specifies the maximum width of any line of the C string.
    """
    L = []
    for l in s.split("\n"):
        if len(l) < width:
            L.append(r'"%s\n"' % l)

    return "\n".join(L)

def unindent(s, skipfirst=True):
    """Return an unindented version of a docstring.

    Removes indentation on lines following the first one, using the
    leading whitespace of the first indented line that is not blank
    to determine the indentation.
    """

    lines = s.split("\n")
    if skipfirst:
        first = lines.pop(0)
        L = [first]
    else:
        L = []
    indent = None
    for l in lines:
        ls = l.strip()
        if ls:
            indent = len(l) - len(ls)
            break
    L += [l[indent:] for l in lines]

    return "\n".join(L)


_______________________________________________
Stackless-checkins mailing list
Stackless-checkins at stackless.com
http://www.stackless.com/mailman/listinfo/stackless-checkins



More information about the Stackless-checkins mailing list