From 53d527ad1828d484e336626ee59d7d5606b58499 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Mon, 5 Aug 2002 18:29:45 +0000 Subject: Initial prototype of framer: a tool to build the frame for extension modules. --- Tools/framer/README.txt | 8 ++ Tools/framer/TODO.txt | 6 + Tools/framer/example.py | 127 +++++++++++++++++++++ Tools/framer/framer/__init__.py | 8 ++ Tools/framer/framer/bases.py | 221 +++++++++++++++++++++++++++++++++++++ Tools/framer/framer/function.py | 173 +++++++++++++++++++++++++++++ Tools/framer/framer/member.py | 73 ++++++++++++ Tools/framer/framer/slots.py | 64 +++++++++++ Tools/framer/framer/struct.py | 52 +++++++++ Tools/framer/framer/structparse.py | 46 ++++++++ Tools/framer/framer/template.py | 102 +++++++++++++++++ Tools/framer/framer/util.py | 35 ++++++ 12 files changed, 915 insertions(+) create mode 100644 Tools/framer/README.txt create mode 100644 Tools/framer/TODO.txt create mode 100644 Tools/framer/example.py create mode 100644 Tools/framer/framer/__init__.py create mode 100644 Tools/framer/framer/bases.py create mode 100644 Tools/framer/framer/function.py create mode 100644 Tools/framer/framer/member.py create mode 100644 Tools/framer/framer/slots.py create mode 100644 Tools/framer/framer/struct.py create mode 100644 Tools/framer/framer/structparse.py create mode 100644 Tools/framer/framer/template.py create mode 100644 Tools/framer/framer/util.py diff --git a/Tools/framer/README.txt b/Tools/framer/README.txt new file mode 100644 index 0000000..4a93a4d --- /dev/null +++ b/Tools/framer/README.txt @@ -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. diff --git a/Tools/framer/TODO.txt b/Tools/framer/TODO.txt new file mode 100644 index 0000000..8586c8e --- /dev/null +++ b/Tools/framer/TODO.txt @@ -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. diff --git a/Tools/framer/example.py b/Tools/framer/example.py new file mode 100644 index 0000000..b77f475 --- /dev/null +++ b/Tools/framer/example.py @@ -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() + diff --git a/Tools/framer/framer/__init__.py b/Tools/framer/framer/__init__.py new file mode 100644 index 0000000..ab73a30 --- /dev/null +++ b/Tools/framer/framer/__init__.py @@ -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. +""" + + diff --git a/Tools/framer/framer/bases.py b/Tools/framer/framer/bases.py new file mode 100644 index 0000000..61052ab --- /dev/null +++ b/Tools/framer/framer/bases.py @@ -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 + diff --git a/Tools/framer/framer/function.py b/Tools/framer/framer/function.py new file mode 100644 index 0000000..595cc8d --- /dev/null +++ b/Tools/framer/framer/function.py @@ -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 diff --git a/Tools/framer/framer/member.py b/Tools/framer/framer/member.py new file mode 100644 index 0000000..5faf462 --- /dev/null +++ b/Tools/framer/framer/member.py @@ -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 diff --git a/Tools/framer/framer/slots.py b/Tools/framer/framer/slots.py new file mode 100644 index 0000000..d369c9a --- /dev/null +++ b/Tools/framer/framer/slots.py @@ -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] diff --git a/Tools/framer/framer/struct.py b/Tools/framer/framer/struct.py new file mode 100644 index 0000000..3948740 --- /dev/null +++ b/Tools/framer/framer/struct.py @@ -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) diff --git a/Tools/framer/framer/structparse.py b/Tools/framer/framer/structparse.py new file mode 100644 index 0000000..419228a --- /dev/null +++ b/Tools/framer/framer/structparse.py @@ -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) diff --git a/Tools/framer/framer/template.py b/Tools/framer/framer/template.py new file mode 100644 index 0000000..70e2591 --- /dev/null +++ b/Tools/framer/framer/template.py @@ -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 = """\ +}; +""" diff --git a/Tools/framer/framer/util.py b/Tools/framer/framer/util.py new file mode 100644 index 0000000..73f3309 --- /dev/null +++ b/Tools/framer/framer/util.py @@ -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) -- cgit v0.12