def _method_instance_names(desc, env, key, rtn): classnames = [] _class_heirarchy(desc['name'], classnames, env) for classname in classnames: classrtn = env.get(classname, {}).get('methods', {}).get(key, NotImplemented) if rtn != classrtn: continue #class_ctype = cython_ctype(desc['name']) class_ctype = cython_ctype(classname) inst_name = "(<{0} *> self._inst)".format(class_ctype) return inst_name, classname return "(<{0} *> self._inst)".format(cython_ctype(desc['name'])), desc['name']
def genpxd(desc): """Generates a ``*pxd`` Cython header file for exposing C/C++ data to other Cython wrappers based off of a dictionary description. Parameters ---------- desc : dict Class description dictonary. Returns ------- pxd : str Cython ``*.pxd`` header file as in-memory string. """ if 'pxd_filename' not in desc: desc['pxd_filename'] = '{0}.pxd'.format(desc['name'].lower()) pars = ', '.join([cython_cytype(p) for p in desc['parents'] or ()]) d = {'parents': pars if 0 == len(pars) else '('+pars+')'} copy_from_desc = ['name',] for key in copy_from_desc: d[key] = desc[key] cimport_tups = set() for parent in desc['parents'] or (): cython_cimport_tuples(parent, cimport_tups, set(['cy'])) from_cpppxd = desc['cpppxd_filename'].rsplit('.', 1)[0] # This is taken care of in main! #register_class(desc['name'], cython_cimport=from_cpppxd, # cython_c_type="{0}.{1}".format(from_cpppxd, desc['name']),) d['name_type'] = cython_ctype(desc['name']) cython_cimport_tuples(desc['name'], cimport_tups, set(['c'])) parentless_body = ['cdef void * _inst', 'cdef public bint _free_inst'] body = parentless_body if desc['parents'] is None else [] attritems = sorted(desc['attrs'].items()) for aname, atype in attritems: if aname.startswith('_'): continue # skip private _, _, cachename, iscached = cython_c2py(aname, atype, cache_prefix=None) if iscached: cython_cimport_tuples(atype, cimport_tups) cyt = cython_cytype(atype) decl = "cdef public {0} {1}".format(cyt, cachename) body.append(decl) d['cimports'] = "\n".join(sorted(cython_cimports(cimport_tups))) d['body'] = indent(body or ['pass']) d['extra'] = desc.get('extra', {}).get('pxd', '') pxd = _pxd_template.format(**d) return pxd
def _gen_method(name, name_mangled, args, rtn, doc=None, inst_name="self._inst"): argfill = ", ".join(['self'] + [a[0] for a in args if 2 == len(a)] + \ ["{0}={1}".format(a[0], a[2]) for a in args if 3 == len(a)]) lines = ['def {0}({1}):'.format(name_mangled, argfill)] lines += [] if doc is None else indent('\"\"\"{0}\"\"\"'.format(doc), join=False) decls = [] argbodies = [] argrtns = {} for a in args: adecl, abody, artn = cython_py2c(a[0], a[1]) if adecl is not None: decls += indent(adecl, join=False) if abody is not None: argbodies += indent(abody, join=False) argrtns[a[0]] = artn rtype = cython_ctype(rtn) hasrtn = rtype not in set(['None', None, 'NULL', 'void']) argvals = ', '.join([argrtns[a[0]] for a in args]) fcall = '{0}.{1}({2})'.format(inst_name, name, argvals) if hasrtn: fcdecl, fcbody, fcrtn, fccached = cython_c2py('rtnval', rtn, cached=False) decls += indent("cdef {0} {1}".format(rtype, 'rtnval'), join=False) func_call = indent('rtnval = {0}'.format(fcall), join=False) if fcdecl is not None: decls += indent(fcdecl, join=False) if fcbody is not None: func_call += indent(fcbody, join=False) func_rtn = indent("return {0}".format(fcrtn), join=False) else: func_call = indent(fcall, join=False) func_rtn = [] lines += decls lines += argbodies lines += func_call lines += func_rtn lines += ['', ""] return lines
def check_cython_ctype(t, exp): obs = ts.cython_ctype(t) assert_equal(obs, exp)
def gencpppxd(desc, exception_type='+'): """Generates a cpp_*.pxd Cython header file for exposing C/C++ data from to other Cython wrappers based off of a dictionary description. Parameters ---------- desc : dict Class description dictonary. exception_type : str, optional Cython exception annotation. Set to None when exceptions should not be included. Returns ------- cpppxd : str Cython cpp_*.pxd header file as in-memory string. """ pars = ', '.join([cython_ctype(p) for p in desc['parents'] or ()]) d = {'parents': pars if 0 == len(pars) else '('+pars+')'} copy_from_desc = ['name', 'namespace', 'header_filename'] for key in copy_from_desc: d[key] = desc[key] inc = set(['c']) cimport_tups = set() for parent in desc['parents'] or (): cython_cimport_tuples(parent, cimport_tups, inc) alines = [] attritems = sorted(desc['attrs'].items()) for aname, atype in attritems: if aname.startswith('_'): continue alines.append("{0} {1}".format(cython_ctype(atype), aname)) cython_cimport_tuples(atype, cimport_tups, inc) d['attrs_block'] = indent(alines, 8) mlines = [] clines = [] estr = str() if exception_type is None else ' except {0}'.format(exception_type) methitems = sorted(expand_default_args(desc['methods'].items())) for mkey, mrtn in methitems: mname, margs = mkey[0], mkey[1:] if mname.startswith('_') or mname.startswith('~'): continue # private or destructor argfill = ", ".join([cython_ctype(a[1]) for a in margs]) for a in margs: cython_cimport_tuples(a[1], cimport_tups, inc) line = "{0}({1}){2}".format(mname, argfill, estr) if mrtn is None: # this must be a constructor if line not in clines: clines.append(line) else: # this is a normal method rtype = cython_ctype(mrtn) cython_cimport_tuples(mrtn, cimport_tups, inc) line = rtype + " " + line if line not in mlines: mlines.append(line) d['methods_block'] = indent(mlines, 8) d['constructors_block'] = indent(clines, 8) d['cimports'] = "\n".join(sorted(cython_cimports(cimport_tups))) d['extra'] = desc.get('extra', {}).get('cpppxd', '') cpppxd = _cpppxd_template.format(**d) if 'cpppxd_filename' not in desc: desc['cpppxd_filename'] = 'cpp_{0}.pxd'.format(d['name'].lower()) return cpppxd
def genpyx(desc, env=None): """Generates a ``*.pyx`` Cython wrapper implementation for exposing a C/C++ class based off of a dictionary description. The environment is a dictionary of all class names known to their descriptions. Parameters ---------- desc : dict Class description dictonary. env : env, optional Environment dictionary which maps all class names that are required to their own descriptions. This is required for resolved class heirarchy dependencies. Returns ------- pyx : str Cython ``*.pyx`` implementation file as in-memory string. """ if env is None: env = {desc['name']: desc} nodocmsg = "no docstring for {0}, please file a bug report!" pars = ', '.join([cython_cytype(p) for p in desc['parents'] or ()]) d = {'parents': pars if 0 == len(pars) else '('+pars+')'} copy_from_desc = ['name', 'namespace', 'header_filename'] for key in copy_from_desc: d[key] = desc[key] d['module_docstring'] = desc.get('docstrings', {})\ .get('module', nodocmsg.format(desc['name'].lower())) class_doc = desc.get('docstrings', {}).get('class', nodocmsg.format(desc['name'])) d['class_docstring'] = indent('\"\"\"{0}\"\"\"'.format(class_doc)) class_ctype = cython_ctype(desc['name']) inst_name = "(<{0} *> self._inst)".format(class_ctype) import_tups = set() cimport_tups = set() for parent in desc['parents'] or (): cython_import_tuples(parent, import_tups) cython_cimport_tuples(parent, cimport_tups) alines = [] cached_names = [] attritems = sorted(desc['attrs'].items()) for aname, atype in attritems: if aname.startswith('_'): continue # skip private adoc = desc.get('docstrings', {}).get('attrs', {})\ .get(aname, nodocmsg.format(aname)) alines += _gen_property(aname, atype, adoc, cached_names=cached_names, inst_name=inst_name) cython_import_tuples(atype, import_tups) cython_cimport_tuples(atype, cimport_tups) d['attrs_block'] = indent(alines) pd = ["{0} = None".format(n) for n in cached_names] d['property_defaults'] = indent(indent(pd, join=False)) mlines = [] clines = [] methcounts = _count0(desc['methods']) currcounts = {k: 0 for k in methcounts} mangled_mnames = {} methitems = sorted(desc['methods'].items()) for mkey, mrtn in methitems: mname, margs = mkey[0], mkey[1:] if mname.startswith('_'): continue # skip private if 1 < methcounts[mname]: mname_mangled = "_{0}_{1}_{2:0{3}}".format(desc['name'], mname, currcounts[mname], int(math.log(methcounts[mname], 10)+1)).lower() else: mname_mangled = mname currcounts[mname] += 1 mangled_mnames[mkey] = mname_mangled for a in margs: cython_import_tuples(a[1], import_tups) cython_cimport_tuples(a[1], cimport_tups) minst_name, mcname = _method_instance_names(desc, env, mkey, mrtn) if mcname != desc['name']: cython_import_tuples(mcname, import_tups) cython_cimport_tuples(mcname, cimport_tups) if mrtn is None: # this must be a constructor if mname not in (desc['name'], '__init__'): continue # skip destuctors if 1 == methcounts[mname]: mname_mangled = '__init__' mangled_mnames[mkey] = mname_mangled mdoc = desc.get('docstrings', {}).get('methods', {}).get(mname, '') mdoc = _doc_add_sig(mdoc, mname, margs) clines += _gen_constructor(mname, mname_mangled, desc['name'], margs, doc=mdoc, cpppxd_filename=desc['cpppxd_filename'], inst_name=minst_name) if 1 < methcounts[mname] and currcounts[mname] == methcounts[mname]: # write dispatcher nm = {k: v for k, v in mangled_mnames.iteritems() if k[0] == mname} clines += _gen_dispatcher('__init__', nm, doc=mdoc, hasrtn=False) else: # this is a normal method cython_import_tuples(mrtn, import_tups) cython_cimport_tuples(mrtn, cimport_tups) mdoc = desc.get('docstrings', {}).get('methods', {})\ .get(mname, nodocmsg.format(mname)) mdoc = _doc_add_sig(mdoc, mname, margs) mlines += _gen_method(mname, mname_mangled, margs, mrtn, mdoc, inst_name=minst_name) if 1 < methcounts[mname] and currcounts[mname] == methcounts[mname]: # write dispatcher nm = {k: v for k, v in mangled_mnames.iteritems() if k[0] == mname} mlines += _gen_dispatcher(mname, nm, doc=mdoc) if desc['parents'] is None: clines += ["def __dealloc__(self):"] clines += indent("if self._free_inst:", join=False) clines += indent(indent("free(self._inst)", join=False), join=False) cimport_tups.add(('libc.stdlib', 'free')) d['methods_block'] = indent(mlines) d['constructor_block'] = indent(clines) d['imports'] = "\n".join(sorted(cython_imports(import_tups))) d['cimports'] = "\n".join(sorted(cython_cimports(cimport_tups))) if 'numpy' in d['cimports']: d['imports'] += "\n\nnp.import_array()" d['extra'] = desc.get('extra', {}).get('pyx', '') pyx = _pyx_template.format(**d) if 'pyx_filename' not in desc: desc['pyx_filename'] = '{0}.pyx'.format(d['name'].lower()) return pyx