Initial prototype of framer: a tool to build the frame for extension modules.
This commit is contained in:
parent
f4d32df19d
commit
53d527ad18
|
@ -0,0 +1,8 @@
|
|||
framer is a tool to generate boilerplate code for C extension types.
|
||||
|
||||
The boilerplate is generated from a specification object written in
|
||||
Python. The specification uses the class statement to describe the
|
||||
extension module and any extension types it contains. From the
|
||||
specification, framer can generate all the boilerplate C code,
|
||||
including function definitions, argument handling code, and type
|
||||
objects.
|
|
@ -0,0 +1,6 @@
|
|||
Add spec for getsets.
|
||||
Generate a distutils setup script.
|
||||
Handle operator overloading.
|
||||
Generate traverse and clear methods for GC.
|
||||
Handle mapping, sequence, buffer protocols.
|
||||
Finish the todo list.
|
|
@ -0,0 +1,127 @@
|
|||
"""Generate the skeleton for cStringIO as an example of framer."""
|
||||
|
||||
from framer.bases import Module, Type
|
||||
from framer.member import member
|
||||
|
||||
class cStringIO(Module):
|
||||
"""A simple fast partial StringIO replacement.
|
||||
|
||||
This module provides a simple useful replacement for the StringIO
|
||||
module that is written in C. It does not provide the full
|
||||
generality of StringIO, but it provides enough for most
|
||||
applications and is especially useful in conjunction with the
|
||||
pickle module.
|
||||
|
||||
Usage:
|
||||
|
||||
from cStringIO import StringIO
|
||||
|
||||
an_output_stream = StringIO()
|
||||
an_output_stream.write(some_stuff)
|
||||
...
|
||||
value = an_output_stream.getvalue()
|
||||
|
||||
an_input_stream = StringIO(a_string)
|
||||
spam = an_input_stream.readline()
|
||||
spam = an_input_stream.read(5)
|
||||
an_input_stream.seek(0) # OK, start over
|
||||
spam = an_input_stream.read() # and read it all
|
||||
"""
|
||||
|
||||
__file__ = "cStringIO.c"
|
||||
|
||||
def StringIO(o):
|
||||
"""Return a StringIO-like stream for reading or writing"""
|
||||
StringIO.pyarg = "|O"
|
||||
|
||||
class InputType(Type):
|
||||
"Simple type for treating strings as input file streams"
|
||||
|
||||
abbrev = "input"
|
||||
|
||||
struct = """\
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
char *buf;
|
||||
int pos;
|
||||
int size;
|
||||
PyObject *pbuf;
|
||||
} InputObject;
|
||||
"""
|
||||
|
||||
def flush(self):
|
||||
"""Does nothing"""
|
||||
|
||||
def getvalue(self):
|
||||
"""Get the string value.
|
||||
|
||||
If use_pos is specified and is a true value, then the
|
||||
string returned will include only the text up to the
|
||||
current file position.
|
||||
"""
|
||||
|
||||
def isatty(self):
|
||||
"""Always returns False"""
|
||||
|
||||
def read(self, s):
|
||||
"""Return s characters or the rest of the string."""
|
||||
read.pyarg = "|i"
|
||||
|
||||
def readline(self):
|
||||
"""Read one line."""
|
||||
|
||||
def readlines(self, hint):
|
||||
"""Read all lines."""
|
||||
readlines.pyarg = "|i"
|
||||
|
||||
def reset(self):
|
||||
"""Reset the file position to the beginning."""
|
||||
|
||||
def tell(self):
|
||||
"""Get the current position."""
|
||||
|
||||
def truncate(self, pos):
|
||||
"""Truncate the file at the current position."""
|
||||
truncate.pyarg = "|i"
|
||||
|
||||
def seek(self, position, mode=0):
|
||||
"""Set the current position.
|
||||
|
||||
The optional mode argument can be 0 for absolute, 1 for relative,
|
||||
and 2 for relative to EOF. The default is absolute.
|
||||
"""
|
||||
seek.pyarg = "i|i"
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
class OutputType(InputType):
|
||||
"Simple type for output strings."
|
||||
|
||||
abbrev = "output"
|
||||
|
||||
struct = """\
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
char *buf;
|
||||
int pos;
|
||||
int size;
|
||||
int softspace;
|
||||
} OutputObject;
|
||||
"""
|
||||
|
||||
softspace = member()
|
||||
|
||||
def close(self):
|
||||
"""Explicitly release resources."""
|
||||
|
||||
def write(self, s):
|
||||
"""Write a string to the file."""
|
||||
# XXX Hack: writing None resets the buffer
|
||||
|
||||
def writelines(self, lines):
|
||||
"""Write each string in lines."""
|
||||
|
||||
|
||||
cStringIO.gen()
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
"""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.
|
||||
"""
|
||||
|
||||
|
|
@ -0,0 +1,221 @@
|
|||
"""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
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
"""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
|
|
@ -0,0 +1,73 @@
|
|||
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
|
|
@ -0,0 +1,64 @@
|
|||
"""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]
|
|
@ -0,0 +1,52 @@
|
|||
"""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)
|
|
@ -0,0 +1,46 @@
|
|||
"""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)
|
|
@ -0,0 +1,102 @@
|
|||
"""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 = """\
|
||||
};
|
||||
"""
|
|
@ -0,0 +1,35 @@
|
|||
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)
|
Loading…
Reference in New Issue