[Python-checkins] python/dist/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
jhylton@users.sourceforge.net
jhylton@users.sourceforge.net
Mon, 05 Aug 2002 11:29:48 -0700
Update of /cvsroot/python/python/dist/src/Tools/framer/framer
In directory usw-pr-cvs1:/tmp/cvs-serv10915/framer/framer
Added Files:
__init__.py bases.py function.py member.py slots.py struct.py
structparse.py template.py util.py
Log Message:
Initial prototype of framer: a tool to build the frame for extension modules.
--- 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)