#
# This module is used to generate the header and pseudo-c-extension-module
# body for an extension module c-api library.
#
# See "Extending and Embedding the Python Interpeter",
# "1.12 Providing a C API for an Extension Module"
#
# The purpose of a c-api pseudo-extension-module is to provide a platform
# independent mechanism for accessing c-api functions which are built in
# a shared library without polluting the global namespace.
#
# Towards this end, the c-api functions are compiled "static" but pointers
# to them are placed in a jump table.
#
# The header defines "proxy-macros" which provide a typechecked means of
# calling the API functions through the jump table.
#
# The header also provides an "import_module" macro which is used to initialize
# a pointer to the API jump table. So, including the library header generates
# a single pointer of static data in the including file. Calling the import
# macro fills in the pointer at runtime.
#
# The generated output includes header and an pseudo-c-extension body code.
# It is presumed that the API functions themselves already exist in external
# files which either have global linkage *or* are included directly into
# the pseudo-c-extension as extra_headers.
#
import imp
file, path, descr = imp.find_module("template", ["Lib/codegenerator"])
template = imp.load_module("template", file, path, descr)
# import template # for doing <name> --> %(name)s transformations
PROTO = """
static <rval> <function> <proto>;
"""
PSEUDO = """
#define <function> (<module>_API ? (*(<rval> (*) <proto> ) <module>_API[ <num> ]) : (*(<rval> (*) <proto> ) <module>_FatalApiError))
"""
HDR_BODY = """
/* W W AAA RRRR N N III N N GGG !!!
** W W A A R R NN N I NN N G G !!!
** W W W AAAAA RRRR N N N I N N N G !
** W W W A A R R N NN I N NN G GG
** W W A A R R N N III N N GGG !!!
**
** WARNING: This file is program generated by genapi.py.
**
** DO NOT EDIT THIS FILE! Any changes made to this file will be lost!
*/
#ifndef _<module>
#define _<module>
<headers>
#ifdef __cplusplus
extern "C" {
#endif
/* Header file for <module> */
#if !defined(_<module>_MODULE)
/*
Extensions constructed from seperate compilation units can access the
C-API defined here by defining "<module>_UNIQUE_SYMBOL" to a global
name unique to the extension. Doing this circumvents the requirement
to import <module> into each compilation unit, but is nevertheless
mildly discouraged as "outside the Python norm" and potentially
leading to problems. Looking around at "existing Python art", most
extension modules are monolithic C files, and likely for good reason.
*/
#if defined(<module>_UNIQUE_SYMBOL)
#define <module>_API <module>_UNIQUE_SYMBOL
#endif
/* C API address pointer */
#if defined(NO_IMPORT) || defined(NO_IMPORT_ARRAY)
extern void **<module>_API;
#else
#if defined(<module>_UNIQUE_SYMBOL)
void **<module>_API;
#else
static void **<module>_API;
#endif
#endif
#define _import_<module>() \\
{ \\
PyObject *module = PyImport_ImportModule("<qualified_module>"); \\
if (module != NULL) { \\
PyObject *module_dict = PyModule_GetDict(module); \\
PyObject *c_api_object = \\
PyDict_GetItemString(module_dict, "_C_API"); \\
if (c_api_object && PyCObject_Check(c_api_object)) { \\
<module>_API = (void **)PyCObject_AsVoidPtr(c_api_object); \\
} else { \\
PyErr_Format(PyExc_ImportError, \\
"Can't get API for module '<qualified_module>'"); \\
} \\
} \\
}
#define import_<module>() _import_<module>(); if (PyErr_Occurred()) { PyErr_Print(); Py_FatalError("<qualified_module> failed to import... exiting.\\n"); }
#endif
#define <module>_FatalApiError (Py_FatalError("Call to API function without first calling import_<module>() in " __FILE__), NULL)
/* Macros defining components of function prototypes */
<macros>
#ifdef _<module>_MODULE
/* This section is used when compiling <module> */
static PyObject *_Error;
<protos>
#else
/* This section is used in modules that use <module> */
<pseudos>
#endif
/* Total number of C API pointers */
#define <module>_API_pointers <num>
#ifdef __cplusplus
}
#endif
#endif /* !defined(_<module>) */
"""
def genhdr(module, qualified_module, file, header_includes):
functions = open(file).readlines()
headers = mkhdrs(header_includes)
macros, protos, pseudos = "", "", ""
num = 0
fps = []
for l in functions:
rval, function, proto = l.rstrip().split("@")
rval = rval.rstrip()
function = function.rstrip()
fname = module + "_" + function
protos += PROTO % locals()
pseudos += PSEUDO % locals()
fps.append(function)
num += 1
hdr = open(module +".h","w")
print >>hdr, HDR_BODY % locals()
return fps
MOD_BODY = """
/* W W AAA RRRR N N III N N GGG !!!
** W W A A R R NN N I NN N G G !!!
** W W W AAAAA RRRR N N N I N N N G !
** W W W A A R R N NN I N NN G GG
** W W A A R R N N III N N GGG !!!
**
** WARNING: This file is program generated by genapi.py.
**
** DO NOT EDIT THIS FILE! Any changes made to this file will be lost!
*/
#include <Python.h>
#define _<module>_MODULE
<body_code>
static PyObject *_Error;
void *<module>_API[] = {
<fps>
};
#if (!defined(METHOD_TABLE_EXISTS))
static PyMethodDef _<module>Methods[] = {
{NULL, NULL} /* Sentinel */
};
#endif
/* platform independent*/
#ifdef MS_WIN32
__declspec(dllexport)
#endif
/* boiler plate API init */
void init<module>(void)
{
PyObject *m = Py_InitModule("<module>", _<module>Methods);
PyObject *c_api_object;
_Error = PyErr_NewException("<qualified_module>.error", NULL, NULL);
/* Create a CObject containing the API pointer array's address */
c_api_object = PyCObject_FromVoidPtr((void *)<module>_API, NULL);
if (c_api_object != NULL) {
/* Create a name for this object in the module's namespace */
PyObject *d = PyModule_GetDict(m);
PyDict_SetItemString(d, "_C_API", c_api_object);
PyDict_SetItemString(d, "error", _Error);
Py_DECREF(c_api_object);
} else {
return;
}
ADD_VERSION(m);
<module>_init(); /* module customized init */
}
"""
import re
def mkhdrs(hdr_list):
headers = map(lambda x: '#include "%s"' % x, hdr_list)
headers = "\n".join(headers)
return headers
def bodystring(files):
s = ""
for filename in files:
# Add the whole file but the emacs Local Variables section.
# This assumes the Local Variables are in a single C-style comment,
# each line starting with " *" (as opposed to each line beeing it's
# own comment).
s += re.compile("/\*.?\n.?\*.*Local Variables:.*\*/", re.DOTALL).sub("", open(filename).read()+"\n\n")
return s
def genmod(module, qualified_module, fps, body_files):
body_code = bodystring(body_files)
fps = "\t(void*)" + ",\n\t(void*)".join(fps)
mod = open(module +".c","w")
print >>mod, MOD_BODY % locals()
def main(module, functions, header_includes, body_files,
qualified_module, extra_dependencies=[]):
print "generating new API module '%s' .c & .h" % module
fps = genhdr(module, qualified_module, functions, header_includes)
genmod(module, qualified_module, fps, body_files)
template.sugar_dict(globals())
syntax highlighted by Code2HTML, v. 0.9.1