From: Lisandro Dalcin Date: Thu, 31 Mar 2011 06:57:12 +0000 (-0300) Subject: public/capi: fixes and cleanups, implement exporting cdef variables X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=0a56c99d77fe003e803e719643c40608edb2dfcf;p=cython.git public/capi: fixes and cleanups, implement exporting cdef variables --- diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index ae5c90bf..bb4768e4 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -73,7 +73,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): self.generate_c_code(env, options, result) self.generate_h_code(env, options, result) self.generate_api_code(env, result) - + def has_imported_c_functions(self): for module in self.referenced_modules: for entry in module.cfunc_entries: @@ -96,14 +96,16 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): f.close() def generate_h_code(self, env, options, result): - def h_entries(entries, pxd = 0): + def h_entries(entries, api=0, pxd=0): return [entry for entry in entries - if entry.visibility == 'public' or pxd and entry.defined_in_pxd] - h_types = h_entries(env.type_entries) + if ((entry.visibility == 'public') or + (api and entry.api) or + (pxd and entry.defined_in_pxd))] + h_types = h_entries(env.type_entries, api=1) h_vars = h_entries(env.var_entries) h_funcs = h_entries(env.cfunc_entries) h_extension_types = h_entries(env.c_class_entries) - if h_types or h_vars or h_funcs or h_extension_types: + if (h_types or h_vars or h_funcs or h_extension_types): result.h_file = replace_suffix(result.c_file, ".h") h_code = Code.CCodeWriter() Code.GlobalState(h_code) @@ -115,31 +117,37 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): h_guard = Naming.h_guard_prefix + self.api_name(env) h_code.put_h_guard(h_guard) - self.generate_extern_c_macro_definition(h_code) + h_code.putln("") self.generate_type_header_code(h_types, h_code) h_code.putln("") api_guard = Naming.api_guard_prefix + self.api_name(env) h_code.putln("#ifndef %s" % api_guard) - if h_vars: - h_code.putln("") - for entry in h_vars: - self.generate_public_declaration(entry, h_code, i_code) - if h_funcs: - h_code.putln("") - for entry in h_funcs: - self.generate_public_declaration(entry, h_code, i_code) + h_code.putln("") + self.generate_extern_c_macro_definition(h_code) if h_extension_types: h_code.putln("") for entry in h_extension_types: self.generate_cclass_header_code(entry.type, h_code) if i_code: self.generate_cclass_include_code(entry.type, i_code) + if h_funcs: + h_code.putln("") + for entry in h_funcs: + self.generate_public_declaration(entry, h_code, i_code) + if h_vars: + h_code.putln("") + for entry in h_vars: + self.generate_public_declaration(entry, h_code, i_code) h_code.putln("") - h_code.putln("#endif") + h_code.putln("#endif /* !%s */" % api_guard) h_code.putln("") + h_code.putln("#if PY_MAJOR_VERSION < 3") h_code.putln("PyMODINIT_FUNC init%s(void);" % env.module_name) - h_code.putln("") + h_code.putln("#else") + h_code.putln("PyMODINIT_FUNC PyInit_%s(void);" % env.module_name) h_code.putln("#endif") + h_code.putln("") + h_code.putln("#endif /* !%s */" % h_guard) f = open_new_file(result.h_file) try: @@ -158,65 +166,70 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): def api_name(self, env): return env.qualified_name.replace(".", "__") - + def generate_api_code(self, env, result): - api_funcs = [] - public_extension_types = [] - has_api_extension_types = 0 - for entry in env.cfunc_entries: - if entry.api: - api_funcs.append(entry) - for entry in env.c_class_entries: - if entry.visibility == 'public': - public_extension_types.append(entry) - if entry.api: - has_api_extension_types = 1 - if api_funcs or has_api_extension_types: + def api_entries(entries, pxd=1): + return [entry for entry in entries + if entry.api or (pxd and entry.defined_in_pxd)] + api_vars = api_entries(env.var_entries) + api_funcs = api_entries(env.cfunc_entries) + api_extension_types = api_entries(env.c_class_entries) + if api_vars or api_funcs or api_extension_types: result.api_file = replace_suffix(result.c_file, "_api.h") h_code = Code.CCodeWriter() Code.GlobalState(h_code) - name = self.api_name(env) - guard = Naming.api_guard_prefix + name - h_code.put_h_guard(guard) + api_guard = Naming.api_guard_prefix + self.api_name(env) + h_code.put_h_guard(api_guard) h_code.putln('#include "Python.h"') if result.h_file: h_code.putln('#include "%s"' % os.path.basename(result.h_file)) - for entry in public_extension_types: - type = entry.type + if api_extension_types: h_code.putln("") - h_code.putln("static PyTypeObject *%s;" % type.typeptr_cname) - h_code.putln("#define %s (*%s)" % ( - type.typeobj_cname, type.typeptr_cname)) + for entry in api_extension_types: + type = entry.type + h_code.putln("static PyTypeObject *%s = 0;" % type.typeptr_cname) + h_code.putln("#define %s (*%s)" % ( + type.typeobj_cname, type.typeptr_cname)) if api_funcs: h_code.putln("") for entry in api_funcs: type = CPtrType(entry.type) - h_code.putln("static %s;" % type.declaration_code(entry.cname)) - h_code.putln("") - h_code.put_h_guard(Naming.api_func_guard + "import_module") + cname = env.mangle(Naming.func_prefix, entry.name) + h_code.putln("static %s = 0;" % type.declaration_code(cname)) + h_code.putln("#define %s %s" % (entry.name, cname)) + if api_vars: + h_code.putln("") + for entry in api_vars: + type = CPtrType(entry.type) + cname = env.mangle(Naming.var_prefix, entry.name) + h_code.putln("static %s = 0;" % type.declaration_code(cname)) + h_code.putln("#define %s (*%s)" % (entry.name, cname)) h_code.put(import_module_utility_code.impl) - h_code.putln("") - h_code.putln("#endif") + if api_vars: + h_code.put(voidptr_import_utility_code.impl) if api_funcs: - h_code.putln("") h_code.put(function_import_utility_code.impl) - if public_extension_types: - h_code.putln("") + if api_extension_types: h_code.put(type_import_utility_code.impl) h_code.putln("") - h_code.putln("static int import_%s(void) {" % name) + h_code.putln("static int import_%s(void) {" % self.api_name(env)) h_code.putln("PyObject *module = 0;") h_code.putln('module = __Pyx_ImportModule("%s");' % env.qualified_name) h_code.putln("if (!module) goto bad;") for entry in api_funcs: + cname = env.mangle(Naming.func_prefix, entry.name) sig = entry.type.signature_string() h_code.putln( - 'if (__Pyx_ImportFunction(module, "%s", (void (**)(void))&%s, "%s") < 0) goto bad;' % ( - entry.name, - entry.cname, - sig)) + 'if (__Pyx_ImportFunction(module, "%s", (void (**)(void))&%s, "%s") < 0) goto bad;' + % (entry.name, cname, sig)) + for entry in api_vars: + cname = env.mangle(Naming.var_prefix, entry.name) + sig = entry.type.declaration_code("") + h_code.putln( + 'if (__Pyx_ImportVoidPtr(module, "%s", (void **)&%s, "%s") < 0) goto bad;' + % (entry.name, cname, sig)) h_code.putln("Py_DECREF(module); module = 0;") - for entry in public_extension_types: + for entry in api_extension_types: self.generate_type_import_call( entry.type, h_code, "if (!%s) goto bad;" % entry.type.typeptr_cname) @@ -226,7 +239,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): h_code.putln("return -1;") h_code.putln("}") h_code.putln("") - h_code.putln("#endif") + h_code.putln("#endif /* !%s */" % api_guard) f = open_new_file(result.api_file) try: @@ -235,8 +248,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): f.close() def generate_cclass_header_code(self, type, h_code): - h_code.putln("%s DL_IMPORT(PyTypeObject) %s;" % ( - Naming.extern_c_macro, + h_code.putln("%s %s %s;" % ( + Naming.extern_c_macro, + PyrexTypes.public_decl("PyTypeObject", "DL_IMPORT"), type.typeobj_cname)) def generate_cclass_include_code(self, type, i_code): @@ -426,8 +440,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): self.generate_objstruct_definition(type, code) for entry in vtabslot_list: self.generate_objstruct_definition(entry.type, code) + self.generate_typeobj_predeclaration(entry, code) for entry in vtab_list: - self.generate_typeobject_predeclaration(entry, code) + self.generate_typeobj_predeclaration(entry, code) self.generate_exttype_vtable_struct(entry, code) self.generate_exttype_vtabptr_declaration(entry, code) @@ -724,10 +739,12 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): def generate_extern_c_macro_definition(self, code): name = Naming.extern_c_macro - code.putln("#ifdef __cplusplus") - code.putln('#define %s extern "C"' % name) - code.putln("#else") - code.putln("#define %s extern" % name) + code.putln("#ifndef %s" % name) + code.putln(" #ifdef __cplusplus") + code.putln(' #define %s extern "C"' % name) + code.putln(" #else") + code.putln(" #define %s extern" % name) + code.putln(" #endif") code.putln("#endif") def generate_includes(self, env, cimported_modules, code): @@ -789,10 +806,13 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): def generate_typedef(self, entry, code): base_type = entry.type.typedef_base_type if base_type.is_numeric: - writer = code.globalstate['numeric_typedefs'] + try: + writer = code.globalstate['numeric_typedefs'] + except KeyError: + writer = code else: writer = code - writer.putln("") + writer.mark_pos(entry.pos) writer.putln("typedef %s;" % base_type.declaration_code(entry.cname)) def sue_header_footer(self, type, kind, name): @@ -816,7 +836,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): code.globalstate.use_utility_code(packed_struct_utility_code) header, footer = \ self.sue_header_footer(type, kind, type.cname) - code.putln("") if packed: code.putln("#if defined(__SUNPRO_C)") code.putln(" #pragma pack(1)") @@ -847,7 +866,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): name = entry.cname or entry.name or "" header, footer = \ self.sue_header_footer(type, "enum", name) - code.putln("") code.putln(header) enum_values = entry.enum_values if not enum_values: @@ -873,21 +891,23 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): code.putln(value_code) code.putln(footer) - def generate_typeobject_predeclaration(self, entry, code): + def generate_typeobj_predeclaration(self, entry, code): code.putln("") name = entry.type.typeobj_cname if name: if entry.visibility == 'extern' and not entry.in_cinclude: - code.putln("%s DL_IMPORT(PyTypeObject) %s;" % ( + code.putln("%s %s %s;" % ( Naming.extern_c_macro, + PyrexTypes.public_decl("PyTypeObject", "DL_IMPORT"), name)) elif entry.visibility == 'public': - code.putln("%s DL_EXPORT(PyTypeObject) %s;" % ( + code.putln("%s %s %s;" % ( Naming.extern_c_macro, + PyrexTypes.public_decl("PyTypeObject", "DL_EXPORT"), name)) # ??? Do we really need the rest of this? ??? #else: - # code.putln("staticforward PyTypeObject %s;" % name) + # code.putln("static PyTypeObject %s;" % name) def generate_exttype_vtable_struct(self, entry, code): code.mark_pos(entry.pos) @@ -927,7 +947,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): return # Forward declared but never defined header, footer = \ self.sue_header_footer(type, "struct", type.objstruct_cname) - code.putln("") code.putln(header) base_type = type.base_type if base_type: @@ -966,24 +985,26 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): for entry in env.cfunc_entries: if entry.inline_func_in_pxd or (not entry.in_cinclude and (definition or entry.defined_in_pxd or entry.visibility == 'extern')): - if entry.visibility in ('public', 'extern'): + if entry.visibility == 'public': + storage_class = "%s " % Naming.extern_c_macro dll_linkage = "DL_EXPORT" + elif entry.visibility == 'extern': + storage_class = "%s " % Naming.extern_c_macro + dll_linkage = "DL_IMPORT" + elif entry.visibility == 'private': + storage_class = "static " + dll_linkage = None else: + storage_class = "static " dll_linkage = None type = entry.type + if not definition and entry.defined_in_pxd: type = CPtrType(type) header = type.declaration_code(entry.cname, - dll_linkage = dll_linkage) - if entry.visibility == 'private': - storage_class = "static " - elif entry.visibility == 'public': - storage_class = "" - else: - storage_class = "%s " % Naming.extern_c_macro + dll_linkage = dll_linkage) if entry.func_modifiers: - modifiers = '%s ' % ' '.join([ - modifier.upper() for modifier in entry.func_modifiers]) + modifiers = "%s " % ' '.join(entry.func_modifiers).upper() else: modifiers = '' code.putln("%s%s%s; /*proto*/" % ( @@ -1806,6 +1827,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): code.putln("/*--- Global init code ---*/") self.generate_global_init_code(env, code) + code.putln("/*--- Variable export code ---*/") + self.generate_c_variable_export_code(env, code) + code.putln("/*--- Function export code ---*/") self.generate_c_function_export_code(env, code) @@ -1993,6 +2017,18 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): if entry.type.is_pyobject and entry.used: code.put_init_var_to_py_none(entry, nanny=False) + def generate_c_variable_export_code(self, env, code): + # Generate code to create PyCFunction wrappers for exported C functions. + for entry in env.var_entries: + if entry.api or entry.defined_in_pxd: + env.use_utility_code(voidptr_export_utility_code) + signature = entry.type.declaration_code("") + code.putln('if (__Pyx_ExportVoidPtr("%s", (void *)&%s, "%s") < 0) %s' % ( + entry.name, + entry.cname, + signature, + code.error_goto(self.pos))) + def generate_c_function_export_code(self, env, code): # Generate code to create PyCFunction wrappers for exported C functions. for entry in env.cfunc_entries: @@ -2336,6 +2372,45 @@ bad: #------------------------------------------------------------------------------------ +voidptr_export_utility_code = UtilityCode( +proto = """ +static int __Pyx_ExportVoidPtr(const char *name, void *p, const char *sig); /*proto*/ +""", +impl = r""" +static int __Pyx_ExportVoidPtr(const char *name, void *p, const char *sig) { + PyObject *d = 0; + PyObject *cobj = 0; + + d = PyObject_GetAttrString(%(MODULE)s, (char *)"%(API)s"); + if (!d) { + PyErr_Clear(); + d = PyDict_New(); + if (!d) + goto bad; + Py_INCREF(d); + if (PyModule_AddObject(%(MODULE)s, (char *)"%(API)s", d) < 0) + goto bad; + } +#if PY_VERSION_HEX >= 0x02070000 && !(PY_MAJOR_VERSION==3&&PY_MINOR_VERSION==0) + cobj = PyCapsule_New(p, sig, 0); +#else + cobj = PyCObject_FromVoidPtrAndDesc(p, (void *)sig, 0); +#endif + if (!cobj) + goto bad; + if (PyDict_SetItemString(d, name, cobj) < 0) + goto bad; + Py_DECREF(cobj); + Py_DECREF(d); + return 0; +bad: + Py_XDECREF(cobj); + Py_XDECREF(d); + return -1; +} +""" % {'MODULE': Naming.module_cname, 'API': Naming.api_name} +) + function_export_utility_code = UtilityCode( proto = """ static int __Pyx_ExportFunction(const char *name, void (*f)(void), const char *sig); /*proto*/ @@ -2380,6 +2455,62 @@ bad: """ % {'MODULE': Naming.module_cname, 'API': Naming.api_name} ) +voidptr_import_utility_code = UtilityCode( +proto = """ +static int __Pyx_ImportVoidPtr(PyObject *module, const char *name, void **p, const char *sig); /*proto*/ +""", +impl = """ +#ifndef __PYX_HAVE_RT_ImportVoidPtr +#define __PYX_HAVE_RT_ImportVoidPtr +static int __Pyx_ImportVoidPtr(PyObject *module, const char *name, void **p, const char *sig) { + PyObject *d = 0; + PyObject *cobj = 0; + + d = PyObject_GetAttrString(module, (char *)"%(API)s"); + if (!d) + goto bad; + cobj = PyDict_GetItemString(d, name); + if (!cobj) { + PyErr_Format(PyExc_ImportError, + "%%s does not export expected C variable %%s", + PyModule_GetName(module), name); + goto bad; + } +#if PY_VERSION_HEX >= 0x02070000 && !(PY_MAJOR_VERSION==3&&PY_MINOR_VERSION==0) + if (!PyCapsule_IsValid(cobj, sig)) { + PyErr_Format(PyExc_TypeError, + "C variable %%s.%%s has wrong signature (expected %%s, got %%s)", + PyModule_GetName(module), name, sig, PyCapsule_GetName(cobj)); + goto bad; + } + *p = PyCapsule_GetPointer(cobj, sig); +#else + {const char *desc, *s1, *s2; + desc = (const char *)PyCObject_GetDesc(cobj); + if (!desc) + goto bad; + s1 = desc; s2 = sig; + while (*s1 != '\\0' && *s1 == *s2) { s1++; s2++; } + if (*s1 != *s2) { + PyErr_Format(PyExc_TypeError, + "C variable %%s.%%s has wrong signature (expected %%s, got %%s)", + PyModule_GetName(module), name, sig, desc); + goto bad; + } + *p = PyCObject_AsVoidPtr(cobj);} +#endif + if (!(*p)) + goto bad; + Py_DECREF(d); + return 0; +bad: + Py_XDECREF(d); + return -1; +} +#endif +""" % dict(API = Naming.api_name) +) + function_import_utility_code = UtilityCode( proto = """ static int __Pyx_ImportFunction(PyObject *module, const char *funcname, void (**f)(void), const char *sig); /*proto*/ diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 609489f1..7b91add2 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -970,7 +970,7 @@ class CVarDefNode(StatNode): error(self.pos, "Only 'extern' C variable declaration allowed in .pxd file") entry = dest_scope.declare_var(name, type, declarator.pos, - cname = cname, visibility = visibility, is_cdef = 1) + cname=cname, visibility=visibility, api=self.api, is_cdef=1) entry.needs_property = need_property @@ -980,6 +980,7 @@ class CStructOrUnionDefNode(StatNode): # kind "struct" or "union" # typedef_flag boolean # visibility "public" or "private" + # api boolean # in_pxd boolean # attributes [CVarDefNode] or None # entry Entry @@ -995,7 +996,8 @@ class CStructOrUnionDefNode(StatNode): scope = StructOrUnionScope(self.name) self.entry = env.declare_struct_or_union( self.name, self.kind, scope, self.typedef_flag, self.pos, - self.cname, visibility = self.visibility, packed = self.packed) + self.cname, visibility = self.visibility, api = self.api, + packed = self.packed) if self.attributes is not None: if self.in_pxd and not env.in_cinclude: self.entry.defined_in_pxd = 1 @@ -1078,15 +1080,16 @@ class CEnumDefNode(StatNode): # items [CEnumDefItemNode] # typedef_flag boolean # visibility "public" or "private" + # api boolean # in_pxd boolean # entry Entry - + child_attrs = ["items"] def analyse_declarations(self, env): self.entry = env.declare_enum(self.name, self.pos, cname = self.cname, typedef_flag = self.typedef_flag, - visibility = self.visibility) + visibility = self.visibility, api = self.api) if self.items is not None: if self.in_pxd and not env.in_cinclude: self.entry.defined_in_pxd = 1 @@ -1097,7 +1100,7 @@ class CEnumDefNode(StatNode): pass def generate_execution_code(self, code): - if self.visibility == 'public': + if self.visibility == 'public' or self.api: temp = code.funcstate.allocate_temp(PyrexTypes.py_object_type, manage_ref=True) for item in self.entry.enum_values: code.putln("%s = PyInt_FromLong(%s); %s" % ( @@ -1127,9 +1130,9 @@ class CEnumDefItemNode(StatNode): if not self.value.type.is_int: self.value = self.value.coerce_to(PyrexTypes.c_int_type, env) self.value.analyse_const_expression(env) - entry = env.declare_const(self.name, enum_entry.type, + entry = env.declare_const(self.name, enum_entry.type, self.value, self.pos, cname = self.cname, - visibility = enum_entry.visibility) + visibility = enum_entry.visibility, api = enum_entry.api) enum_entry.enum_values.append(entry) @@ -1137,6 +1140,7 @@ class CTypeDefNode(StatNode): # base_type CBaseTypeNode # declarator CDeclaratorNode # visibility "public" or "private" + # api boolean # in_pxd boolean child_attrs = ["base_type", "declarator"] @@ -1147,10 +1151,10 @@ class CTypeDefNode(StatNode): name = name_declarator.name cname = name_declarator.cname entry = env.declare_typedef(name, type, self.pos, - cname = cname, visibility = self.visibility) + cname = cname, visibility = self.visibility, api = self.api) if self.in_pxd and not env.in_cinclude: entry.defined_in_pxd = 1 - + def analyse_expressions(self, env): pass def generate_execution_code(self, code): @@ -1740,7 +1744,6 @@ class CFuncDefNode(FuncDefNode): def generate_function_header(self, code, with_pymethdef, with_opt_args = 1, with_dispatch = 1, cname = None): arg_decls = [] type = self.type - visibility = self.entry.visibility for arg in type.args[:len(type.args)-type.optional_arg_count]: arg_decls.append(arg.declaration_code()) if with_dispatch and self.overridable: @@ -1754,24 +1757,20 @@ class CFuncDefNode(FuncDefNode): if cname is None: cname = self.entry.func_cname entity = type.function_header_code(cname, ', '.join(arg_decls)) - if visibility == 'public': - dll_linkage = "DL_EXPORT" + if self.entry.visibility == 'private': + storage_class = "static " else: - dll_linkage = None - header = self.return_type.declaration_code(entity, - dll_linkage = dll_linkage) - if visibility == 'extern': - storage_class = "%s " % Naming.extern_c_macro - elif visibility == 'public': storage_class = "" - else: - storage_class = "static " + dll_linkage = None + modifiers = "" if 'inline' in self.modifiers: self.modifiers[self.modifiers.index('inline')] = 'cython_inline' - code.putln("%s%s %s {" % ( - storage_class, - ' '.join(self.modifiers).upper(), # macro forms - header)) + if self.modifiers: + modifiers = "%s " % ' '.join(self.modifiers).upper() + + header = self.return_type.declaration_code(entity, dll_linkage=dll_linkage) + #print (storage_class, modifiers, header) + code.putln("%s%s%s {" % (storage_class, modifiers, header)) def generate_argument_declarations(self, env, code): for arg in self.args: diff --git a/Cython/Compiler/Parsing.py b/Cython/Compiler/Parsing.py index 91ddf6a7..fcd0a4e6 100644 --- a/Cython/Compiler/Parsing.py +++ b/Cython/Compiler/Parsing.py @@ -2420,7 +2420,7 @@ def p_c_enum_definition(s, pos, ctx): return Nodes.CEnumDefNode( pos, name = name, cname = cname, items = items, typedef_flag = ctx.typedef_flag, visibility = ctx.visibility, - in_pxd = ctx.level == 'module_pxd') + api = ctx.api, in_pxd = ctx.level == 'module_pxd') def p_c_enum_line(s, ctx, items): if s.sy != 'pass': @@ -2478,10 +2478,10 @@ def p_c_struct_or_union_definition(s, pos, ctx): s.expect_dedent() else: s.expect_newline("Syntax error in struct or union definition") - return Nodes.CStructOrUnionDefNode(pos, + return Nodes.CStructOrUnionDefNode(pos, name = name, cname = cname, kind = kind, attributes = attributes, typedef_flag = ctx.typedef_flag, visibility = ctx.visibility, - in_pxd = ctx.level == 'module_pxd', packed = packed) + api = ctx.api, in_pxd = ctx.level == 'module_pxd', packed = packed) def p_visibility(s, prev_visibility): pos = s.position() @@ -2566,7 +2566,8 @@ def p_ctypedef_statement(s, ctx): s.expect_newline("Syntax error in ctypedef statement") return Nodes.CTypeDefNode( pos, base_type = base_type, - declarator = declarator, visibility = visibility, + declarator = declarator, + visibility = visibility, api = api, in_pxd = ctx.level == 'module_pxd') def p_decorators(s): @@ -2693,8 +2694,8 @@ def p_c_class_definition(s, pos, ctx): base_class_module = ".".join(base_class_path[:-1]) base_class_name = base_class_path[-1] if s.sy == '[': - if ctx.visibility not in ('public', 'extern'): - error(s.position(), "Name options only allowed for 'public' or 'extern' C class") + if ctx.visibility not in ('public', 'extern') and not ctx.api: + error(s.position(), "Name options only allowed for 'public', 'api', or 'extern' C class") objstruct_name, typeobj_name = p_c_class_options(s) if s.sy == ':': if ctx.level == 'module_pxd': @@ -2718,7 +2719,10 @@ def p_c_class_definition(s, pos, ctx): error(pos, "Type object name specification required for 'public' C class") elif ctx.visibility == 'private': if ctx.api: - error(pos, "Only 'public' C class can be declared 'api'") + if not objstruct_name: + error(pos, "Object struct name specification required for 'api' C class") + if not typeobj_name: + error(pos, "Type object name specification required for 'api' C class") else: error(pos, "Invalid class visibility '%s'" % ctx.visibility) return Nodes.CClassDefNode(pos, diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index a5bbb787..b62a0764 100755 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -1190,8 +1190,8 @@ class CComplexType(CNumericType): visibility="extern") scope.parent_type = self scope.directives = {} - scope.declare_var("real", self.real_type, None, "real", is_cdef=True) - scope.declare_var("imag", self.real_type, None, "imag", is_cdef=True) + scope.declare_var("real", self.real_type, None, cname="real", is_cdef=True) + scope.declare_var("imag", self.real_type, None, cname="imag", is_cdef=True) entry = scope.declare_cfunction( "conjugate", CFuncType(self, [CFuncTypeArg("self", self, None)], nogil=True), diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index d5c8dd20..5b7edc2f 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -361,10 +361,10 @@ class Scope(object): def qualify_name(self, name): return EncodedString("%s.%s" % (self.qualified_name, name)) - def declare_const(self, name, type, value, pos, cname = None, visibility = 'private'): + def declare_const(self, name, type, value, pos, cname = None, visibility = 'private', api = 0): # Add an entry for a named constant. if not cname: - if self.in_cinclude or visibility == 'public': + if self.in_cinclude or (visibility == 'public' or api): cname = name else: cname = self.mangle(Naming.enum_prefix, name) @@ -374,21 +374,22 @@ class Scope(object): return entry def declare_type(self, name, type, pos, - cname = None, visibility = 'private', defining = 1, shadow = 0): + cname = None, visibility = 'private', api = 0, defining = 1, shadow = 0): # Add an entry for a type definition. if not cname: cname = name entry = self.declare(name, cname, type, pos, visibility, shadow) entry.is_type = 1 + entry.api = api if defining: self.type_entries.append(entry) # here we would set as_variable to an object representing this type return entry def declare_typedef(self, name, base_type, pos, cname = None, - visibility = 'private'): + visibility = 'private', api = 0): if not cname: - if self.in_cinclude or visibility == 'public': + if self.in_cinclude or (visibility == 'public' or api): cname = name else: cname = self.mangle(Naming.type_prefix, name) @@ -398,16 +399,18 @@ class Scope(object): except ValueError, e: error(pos, e.args[0]) type = PyrexTypes.error_type - entry = self.declare_type(name, type, pos, cname, visibility) + entry = self.declare_type(name, type, pos, cname, + visibility = visibility, api = api) type.qualified_name = entry.qualified_name return entry - - def declare_struct_or_union(self, name, kind, scope, - typedef_flag, pos, cname = None, visibility = 'private', - packed = False): + + def declare_struct_or_union(self, name, kind, scope, + typedef_flag, pos, cname = None, + visibility = 'private', api = 0, + packed = False): # Add an entry for a struct or union definition. if not cname: - if self.in_cinclude or visibility == 'public': + if self.in_cinclude or (visibility == 'public' or api): cname = name else: cname = self.mangle(Naming.type_prefix, name) @@ -416,7 +419,8 @@ class Scope(object): type = PyrexTypes.CStructOrUnionType( name, kind, scope, typedef_flag, cname, packed) entry = self.declare_type(name, type, pos, cname, - visibility = visibility, defining = scope is not None) + visibility = visibility, api = api, + defining = scope is not None) self.sue_entries.append(entry) type.entry = entry else: @@ -482,12 +486,12 @@ class Scope(object): if entry.visibility != visibility: error(pos, "'%s' previously declared as '%s'" % ( entry.name, entry.visibility)) - + def declare_enum(self, name, pos, cname, typedef_flag, - visibility = 'private'): + visibility = 'private', api = 0): if name: if not cname: - if self.in_cinclude or visibility == 'public': + if self.in_cinclude or (visibility == 'public' or api): cname = name else: cname = self.mangle(Naming.type_prefix, name) @@ -495,13 +499,13 @@ class Scope(object): else: type = PyrexTypes.c_anon_enum_type entry = self.declare_type(name, type, pos, cname = cname, - visibility = visibility) + visibility = visibility, api = api) entry.enum_values = [] self.sue_entries.append(entry) - return entry + return entry def declare_var(self, name, type, pos, - cname = None, visibility = 'private', is_cdef = 0): + cname = None, visibility = 'private', api = 0, is_cdef = 0): # Add an entry for a variable. if not cname: if visibility != 'private': @@ -514,6 +518,7 @@ class Scope(object): error(pos, "C++ class must have a default constructor to be stack allocated") entry = self.declare(name, cname, type, pos, visibility) entry.is_variable = 1 + entry.api = api self.control_flow.set_state((), (name, 'initialized'), False) return entry @@ -995,13 +1000,13 @@ class ModuleScope(Scope): return entry def declare_var(self, name, type, pos, - cname = None, visibility = 'private', is_cdef = 0): + cname = None, visibility = 'private', api = 0, is_cdef = 0): # Add an entry for a global variable. If it is a Python # object type, and not declared with cdef, it will live # in the module dictionary, otherwise it will be a C # global variable. entry = Scope.declare_var(self, name, type, pos, - cname, visibility, is_cdef) + cname=cname, visibility=visibility, api=api, is_cdef=is_cdef) if not visibility in ('private', 'public', 'extern'): error(pos, "Module-level variable cannot be declared %s" % visibility) if not is_cdef: @@ -1033,10 +1038,10 @@ class ModuleScope(Scope): buffer_defaults = None, shadow = 0): # If this is a non-extern typedef class, expose the typedef, but use # the non-typedef struct internally to avoid needing forward - # declarations for anonymous structs. + # declarations for anonymous structs. if typedef_flag and visibility != 'extern': - if visibility != 'public': - warning(pos, "ctypedef only valid for public and extern classes", 2) + if not (visibility == 'public' or api): + warning(pos, "ctypedef only valid for 'extern' , 'public', and 'api'", 2) objtypedef_cname = objstruct_cname typedef_flag = 0 else: @@ -1283,12 +1288,12 @@ class LocalScope(Scope): return entry def declare_var(self, name, type, pos, - cname = None, visibility = 'private', is_cdef = 0): + cname = None, visibility = 'private', api = 0, is_cdef = 0): # Add an entry for a local variable. if visibility in ('public', 'readonly'): error(pos, "Local variable cannot be declared %s" % visibility) entry = Scope.declare_var(self, name, type, pos, - cname, visibility, is_cdef) + cname=cname, visibility=visibility, api=api, is_cdef=is_cdef) if type.is_pyobject and not Options.init_local_none: entry.init = "0" entry.init_to_none = (type.is_pyobject or type.is_unspecified) and Options.init_local_none @@ -1354,7 +1359,7 @@ class GeneratorExpressionScope(Scope): return '%s%s' % (self.genexp_prefix, self.parent_scope.mangle(prefix, name)) def declare_var(self, name, type, pos, - cname = None, visibility = 'private', is_cdef = True): + cname = None, visibility = 'private', api = 0, is_cdef = True): if type is unspecified_type: # if the outer scope defines a type for this variable, inherit it outer_entry = self.outer_scope.lookup(name) @@ -1397,7 +1402,7 @@ class StructOrUnionScope(Scope): Scope.__init__(self, name, None, None) def declare_var(self, name, type, pos, - cname = None, visibility = 'private', is_cdef = 0, allow_pyobject = 0): + cname = None, visibility = 'private', api = 0, is_cdef = 0, allow_pyobject = 0): # Add an entry for an attribute. if not cname: cname = name @@ -1419,7 +1424,8 @@ class StructOrUnionScope(Scope): def declare_cfunction(self, name, type, pos, cname = None, visibility = 'private', defining = 0, api = 0, in_pxd = 0, modifiers = ()): # currently no utility code ... - return self.declare_var(name, type, pos, cname, visibility) + return self.declare_var(name, type, pos, + cname=cname, visibility=visibility) class ClassScope(Scope): # Abstract base class for namespace of @@ -1463,12 +1469,12 @@ class PyClassScope(ClassScope): is_py_class_scope = 1 def declare_var(self, name, type, pos, - cname = None, visibility = 'private', is_cdef = 0): + cname = None, visibility = 'private', api = 0, is_cdef = 0): if type is unspecified_type: type = py_object_type # Add an entry for a class attribute. entry = Scope.declare_var(self, name, type, pos, - cname, visibility, is_cdef) + cname=cname, visibility=visibility, api=api, is_cdef=is_cdef) entry.is_pyglobal = 1 entry.is_pyclass_attr = 1 return entry @@ -1513,7 +1519,7 @@ class CClassScope(ClassScope): self.parent_type.base_type.scope.needs_gc()) def declare_var(self, name, type, pos, - cname = None, visibility = 'private', is_cdef = 0): + cname = None, visibility = 'private', api = 0, is_cdef = 0): if is_cdef: # Add an entry for an attribute. if self.defined: @@ -1553,7 +1559,7 @@ class CClassScope(ClassScope): type = py_object_type # Add an entry for a class attribute. entry = Scope.declare_var(self, name, type, pos, - cname, visibility, is_cdef) + cname=cname, visibility=visibility, api=api, is_cdef=is_cdef) entry.is_member = 1 entry.is_pyglobal = 1 # xxx: is_pyglobal changes behaviour in so many places that # I keep it in for now. is_member should be enough @@ -1569,7 +1575,8 @@ class CClassScope(ClassScope): if name == "__new__": error(pos, "__new__ method of extension type will change semantics " "in a future version of Pyrex and Cython. Use __cinit__ instead.") - entry = self.declare_var(name, py_object_type, pos, visibility='extern') + entry = self.declare_var(name, py_object_type, pos, + visibility='extern') special_sig = get_special_method_signature(name) if special_sig: # Special methods get put in the method table with a particular @@ -1694,7 +1701,8 @@ class CppClassScope(Scope): self.inherited_var_entries = [] def declare_var(self, name, type, pos, - cname = None, visibility = 'extern', is_cdef = 0, allow_pyobject = 0): + cname = None, visibility = 'extern', api = 0, + is_cdef = 0, allow_pyobject = 0): # Add an entry for an attribute. if not cname: cname = name @@ -1735,15 +1743,16 @@ class CppClassScope(Scope): error(pos, "no matching function for call to %s::%s()" % (self.default_constructor, self.default_constructor)) - def declare_cfunction(self, name, type, pos, - cname = None, visibility = 'extern', defining = 0, - api = 0, in_pxd = 0, modifiers = (), utility_code = None): + def declare_cfunction(self, name, type, pos, cname = None, + visibility = 'extern', api = 0, defining = 0, + in_pxd = 0, modifiers = (), utility_code = None): if name == self.name.split('::')[-1] and cname is None: self.check_base_default_constructor(pos) name = '' type.return_type = self.lookup(self.name).type prev_entry = self.lookup_here(name) - entry = self.declare_var(name, type, pos, cname, visibility) + entry = self.declare_var(name, type, pos, + cname=cname, visibility=visibility) if prev_entry: entry.overloaded_alternatives = prev_entry.all_alternatives() entry.utility_code = utility_code @@ -1767,8 +1776,9 @@ class CppClassScope(Scope): self.inherited_var_entries.append(entry) for base_entry in base_scope.cfunc_entries: entry = self.declare_cfunction(base_entry.name, base_entry.type, - base_entry.pos, base_entry.cname, - base_entry.visibility, base_entry.func_modifiers, + base_entry.pos, base_entry.cname, + base_entry.visibility, 0, + modifiers = base_entry.func_modifiers, utility_code = base_entry.utility_code) entry.is_inherited = 1 diff --git a/tests/compile/a_capi.pyx b/tests/compile/a_capi.pyx index 710a7095..d4acf9c3 100644 --- a/tests/compile/a_capi.pyx +++ b/tests/compile/a_capi.pyx @@ -11,6 +11,12 @@ cdef public class C[type C_Type, object C_Obj]: cdef public Zax *blarg +cdef public C c_pub = C() +cdef api C c_api = C() + +cdef public dict o_pub = C() +cdef api list o_api = C() + cdef api float f(Foo *x): pass diff --git a/tests/compile/publicapi_api.pyx b/tests/compile/publicapi_api.pyx new file mode 100644 index 00000000..e65e47a3 --- /dev/null +++ b/tests/compile/publicapi_api.pyx @@ -0,0 +1,65 @@ +# -- + +ctypedef int Int0 +ctypedef api int Int1 + +ctypedef enum EnumA0: EA0 +ctypedef api enum EnumA1: EA1 + +cdef enum EnumB0: EB0=0 +cdef api enum EnumB1: EB1=1 + +cdef Int0 i0 = 0 +cdef EnumA0 ea0 = EA0 +cdef EnumB0 eb0 = EB0 + +cdef api Int1 i1 = 0 +cdef api EnumA1 ea1 = EA1 +cdef api EnumB1 eb1 = EB1 + +# -- + +ctypedef struct StructA0: + int SA0 +ctypedef api struct StructA1: + int SA1 + +cdef struct StructB0: + int SB0 +cdef api struct StructB1: + int SB1 + +cdef StructA0 sa0 = {'SA0':0} +cdef StructB0 sb0 = {'SB0':2} + +cdef api StructA1 sa1 = {'SA1':1} +cdef api StructB1 sb1 = {'SB1':3} + +# -- + +ctypedef class Foo0: pass +ctypedef api class Foo1 [type PyFoo1_Type, object PyFoo1_Object]: pass + +cdef class Bar0: pass +cdef api class Bar1 [type PyBar1_Type, object PyBar1_Object]: pass + +cdef Foo0 f0 = None +cdef Bar0 b0 = None + +cdef api Foo1 f1 = None +cdef api Bar1 b1 = None + +# -- + +cdef void bar0(): pass +cdef api void bar1(): pass + +cdef void* spam0(object o) except NULL: return NULL +cdef api void* spam1(object o) except NULL: return NULL + +bar0() +bar1() +spam0(None) +spam1(None) + +# -- diff --git a/tests/compile/publicapi_mix.pyx b/tests/compile/publicapi_mix.pyx new file mode 100644 index 00000000..93bd29e2 --- /dev/null +++ b/tests/compile/publicapi_mix.pyx @@ -0,0 +1,77 @@ +# -- + +ctypedef int Int0 +ctypedef public int Int1 +ctypedef api int Int2 +ctypedef public api int Int3 + +ctypedef enum EnumA0: EA0 +ctypedef public enum EnumA1: EA1 +ctypedef api enum EnumA2: EA2 +ctypedef public api enum EnumA3: EA3 + +cdef enum EnumB0: EB0=0 +cdef public enum EnumB1: EB1=1 +cdef api enum EnumB2: EB2=2 +cdef public api enum EnumB3: EB3=3 + +# -- + +ctypedef struct StructA0: + int SA0 +ctypedef public struct StructA1: + int SA1 +ctypedef api struct StructA2: + int SA2 +ctypedef public api struct StructA3: + int SA3 + +cdef struct StructB0: + int SB0 +cdef public struct StructB1: + int SB1 +cdef api struct StructB2: + int SB2 +cdef public api struct StructB3: + int SB3 + +# -- + +ctypedef class Foo0: pass +ctypedef public class Foo1 [type PyFoo1_Type, object PyFoo1_Object]: pass +ctypedef api class Foo2 [type PyFoo2_Type, object PyFoo2_Object]: pass +ctypedef public api class Foo3 [type PyFoo3_Type, object PyFoo3_Object]: pass + +cdef class Bar0: pass +cdef public class Bar1 [type PyBar1_Type, object PyBar1_Object]: pass +cdef api class Bar2 [type PyBar2_Type, object PyBar2_Object]: pass +cdef public api class Bar3 [type PyBar3_Type, object PyBar3_Object]: pass + +# -- + +cdef void bar0(): pass +cdef public void bar1(): pass +cdef api void bar2(): pass +cdef public api void bar3(): pass + +cdef void* spam0(object o) except NULL: return NULL +cdef public void* spam1(object o) except NULL: return NULL +cdef api void* spam2(object o) except NULL: return NULL +cdef public api void* spam3(object o) except NULL: return NULL + +bar0() +spam0(None) + +# -- + +cdef double d0 = 0 +cdef public double d1 = 1 +cdef api double d2 = 2 +cdef public api double d3 = 3 + +cdef object o0 = None +cdef public object o1 = None +cdef api object o2 = None +cdef public api object o3 = None + +# -- diff --git a/tests/compile/publicapi_pub.pyx b/tests/compile/publicapi_pub.pyx new file mode 100644 index 00000000..2ff473af --- /dev/null +++ b/tests/compile/publicapi_pub.pyx @@ -0,0 +1,65 @@ +# -- + +ctypedef int Int0 +ctypedef public int Int1 + +ctypedef enum EnumA0: EA0 +ctypedef public enum EnumA1: EA1 + +cdef enum EnumB0: EB0=0 +cdef public enum EnumB1: EB1=1 + +cdef Int0 i0 = 0 +cdef EnumA0 ea0 = EA0 +cdef EnumB0 eb0 = EB0 + +cdef public Int1 i1 = 0 +cdef public EnumA1 ea1 = EA1 +cdef public EnumB1 eb1 = EB1 + +# -- + +ctypedef struct StructA0: + int SA0 +ctypedef public struct StructA1: + int SA1 + +cdef struct StructB0: + int SB0 +cdef public struct StructB1: + int SB1 + +cdef StructA0 sa0 = {'SA0':0} +cdef StructB0 sb0 = {'SB0':2} + +cdef public StructA1 sa1 = {'SA1':1} +cdef public StructB1 sb1 = {'SB1':3} + +# -- + +ctypedef class Foo0: pass +ctypedef public class Foo1 [type PyFoo1_Type, object PyFoo1_Object]: pass + +cdef class Bar0: pass +cdef public class Bar1 [type PyBar1_Type, object PyBar1_Object]: pass + +cdef Foo0 f0 = None +cdef Bar0 b0 = None + +cdef public Foo1 f1 = None +cdef public Bar1 b1 = None + +# -- + +cdef void bar0(): pass +cdef public void bar1(): pass + +cdef void* spam0(object o) except NULL: return NULL +cdef public void* spam1(object o) except NULL: return NULL + +bar0() +bar1() +spam0(None) +spam1(None) + +# -- diff --git a/tests/compile/publicapi_pxd_mix.pxd b/tests/compile/publicapi_pxd_mix.pxd new file mode 100644 index 00000000..1810c104 --- /dev/null +++ b/tests/compile/publicapi_pxd_mix.pxd @@ -0,0 +1,68 @@ +# -- + +ctypedef int Int0 +ctypedef public int Int1 +ctypedef api int Int2 +ctypedef public api int Int3 + +ctypedef enum EnumA0: EA0 +ctypedef public enum EnumA1: EA1 +ctypedef api enum EnumA2: EA2 +ctypedef public api enum EnumA3: EA3 + +cdef enum EnumB0: EB0=0 +cdef public enum EnumB1: EB1=1 +cdef api enum EnumB2: EB2=2 +cdef public api enum EnumB3: EB3=3 + +# -- + +ctypedef struct StructA0: + int SA0 +ctypedef public struct StructA1: + int SA1 +ctypedef api struct StructA2: + int SA2 +ctypedef public api struct StructA3: + int SA3 + +cdef struct StructB0: + int SB0 +cdef public struct StructB1: + int SB1 +cdef api struct StructB2: + int SB2 +cdef public api struct StructB3: + int SB3 + +# -- + +ctypedef class Foo0: pass +ctypedef public class Foo1 [type PyFoo1_Type, object PyFoo1_Object]: pass +ctypedef api class Foo2 [type PyFoo2_Type, object PyFoo2_Object]: pass +ctypedef public api class Foo3 [type PyFoo3_Type, object PyFoo3_Object]: pass + +cdef class Bar0: pass +cdef public class Bar1 [type PyBar1_Type, object PyBar1_Object]: pass +cdef api class Bar2 [type PyBar2_Type, object PyBar2_Object]: pass +cdef public api class Bar3 [type PyBar3_Type, object PyBar3_Object]: pass + +# -- + +cdef inline void bar0(): pass +cdef public void bar1() +cdef api void bar2() +cdef public api void bar3() + +cdef inline void* spam0(object o) except NULL: return NULL +cdef public void* spam1(object o) except NULL +cdef api void* spam2(object o) nogil except NULL +cdef public api void* spam3(object o) except NULL with gil + +# -- + +#cdef public int i1 +#cdef api int i2 +#cdef public api int i3 + +# -- diff --git a/tests/compile/publicapi_pxd_mix.pyx b/tests/compile/publicapi_pxd_mix.pyx new file mode 100644 index 00000000..242743ff --- /dev/null +++ b/tests/compile/publicapi_pxd_mix.pyx @@ -0,0 +1,15 @@ +cdef class Foo1: pass +cdef class Foo2: pass +cdef class Foo3: pass + +cdef class Bar1: pass +cdef class Bar2: pass +cdef class Bar3: pass + +cdef public void bar1(): pass +cdef api void bar2(): pass +cdef public api void bar3(): pass + +cdef public void* spam1(object o) except NULL: return NULL +cdef api void* spam2(object o) nogil except NULL: return NULL +cdef public api void* spam3(object o) except NULL with gil: return NULL diff --git a/tests/run/capiimpl.pyx b/tests/run/capiimpl.pyx index e7ea7e2b..4efe0582 100644 --- a/tests/run/capiimpl.pyx +++ b/tests/run/capiimpl.pyx @@ -2,8 +2,20 @@ __doc__ = u""" >>> import sys >>> sys.getrefcount(Foo.__pyx_vtable__) 2 ->>> sys.getrefcount(__pyx_capi__['spam']) +>>> sys.getrefcount(__pyx_capi__['ten']) 2 +>>> sys.getrefcount(__pyx_capi__['pi']) +2 +>>> sys.getrefcount(__pyx_capi__['obj']) +2 +>>> sys.getrefcount(__pyx_capi__['dct']) +2 +>>> sys.getrefcount(__pyx_capi__['one']) +2 +>>> sys.getrefcount(__pyx_capi__['two']) +Traceback (most recent call last): + ... +KeyError: 'two' """ cdef public api class Foo [type FooType, object FooObject]: @@ -12,3 +24,12 @@ cdef public api class Foo [type FooType, object FooObject]: cdef api void spam(): pass + +cdef api int ten = 10 +cdef api double pi = 3.14 +cdef api object obj = object() +cdef api dict dct = {} + +cdef public api int one = 1 +cdef public int two = 2 +