Commit 8eb14b3e authored by Robert Bradshaw's avatar Robert Bradshaw

Merge branch 'master' of github.com:cython/cython

parents 8a646021 12e29263
...@@ -3844,6 +3844,8 @@ class AttributeNode(ExprNode): ...@@ -3844,6 +3844,8 @@ class AttributeNode(ExprNode):
#print "...obj_code =", obj_code ### #print "...obj_code =", obj_code ###
if self.entry and self.entry.is_cmethod: if self.entry and self.entry.is_cmethod:
if obj.type.is_extension_type and not self.entry.is_builtin_cmethod: if obj.type.is_extension_type and not self.entry.is_builtin_cmethod:
if self.entry.final_func_cname:
return self.entry.final_func_cname
return "((struct %s *)%s%s%s)->%s" % ( return "((struct %s *)%s%s%s)->%s" % (
obj.type.vtabstruct_cname, obj_code, self.op, obj.type.vtabstruct_cname, obj_code, self.op,
obj.type.vtabslot_cname, self.member) obj.type.vtabslot_cname, self.member)
......
...@@ -457,6 +457,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -457,6 +457,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.generate_typeobj_predeclaration(entry, code) self.generate_typeobj_predeclaration(entry, code)
self.generate_exttype_vtable_struct(entry, code) self.generate_exttype_vtable_struct(entry, code)
self.generate_exttype_vtabptr_declaration(entry, code) self.generate_exttype_vtabptr_declaration(entry, code)
self.generate_exttype_final_methods_declaration(entry, code)
def generate_declarations_for_modules(self, env, modules, globalstate): def generate_declarations_for_modules(self, env, modules, globalstate):
typecode = globalstate['type_declarations'] typecode = globalstate['type_declarations']
...@@ -991,6 +992,23 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -991,6 +992,23 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
type.vtabstruct_cname, type.vtabstruct_cname,
type.vtabptr_cname)) type.vtabptr_cname))
def generate_exttype_final_methods_declaration(self, entry, code):
if not entry.used:
return
code.mark_pos(entry.pos)
# Generate final methods prototypes
type = entry.type
for method_entry in entry.type.scope.cfunc_entries:
if not method_entry.is_inherited and method_entry.final_func_cname:
declaration = method_entry.type.declaration_code(
method_entry.final_func_cname)
if entry.func_modifiers:
modifiers = "%s " % ' '.join(entry.func_modifiers).upper()
else:
modifiers = ''
code.putln("static %s%s;" % (modifiers, declaration))
def generate_objstruct_predeclaration(self, type, code): def generate_objstruct_predeclaration(self, type, code):
if not type.scope: if not type.scope:
return return
......
...@@ -1726,7 +1726,7 @@ class CFuncDefNode(FuncDefNode): ...@@ -1726,7 +1726,7 @@ class CFuncDefNode(FuncDefNode):
defining = self.body is not None, modifiers = self.modifiers) defining = self.body is not None, modifiers = self.modifiers)
self.entry.inline_func_in_pxd = self.inline_in_pxd self.entry.inline_func_in_pxd = self.inline_in_pxd
self.return_type = type.return_type self.return_type = type.return_type
if self.return_type.is_array and visibility != 'extern': if self.return_type.is_array and self.visibility != 'extern':
error(self.pos, error(self.pos,
"Function cannot return an array") "Function cannot return an array")
...@@ -1751,7 +1751,8 @@ class CFuncDefNode(FuncDefNode): ...@@ -1751,7 +1751,8 @@ class CFuncDefNode(FuncDefNode):
self.entry.as_variable = self.py_func.entry self.entry.as_variable = self.py_func.entry
# Reset scope entry the above cfunction # Reset scope entry the above cfunction
env.entries[name] = self.entry env.entries[name] = self.entry
if not env.is_module_scope or Options.lookup_module_cpdef: if (not self.entry.is_final_cmethod and
(not env.is_module_scope or Options.lookup_module_cpdef)):
self.override = OverrideCheckNode(self.pos, py_func = self.py_func) self.override = OverrideCheckNode(self.pos, py_func = self.py_func)
self.body = StatListNode(self.pos, stats=[self.override, self.body]) self.body = StatListNode(self.pos, stats=[self.override, self.body])
self.create_local_scope(env) self.create_local_scope(env)
...@@ -2243,8 +2244,12 @@ class DefNode(FuncDefNode): ...@@ -2243,8 +2244,12 @@ class DefNode(FuncDefNode):
#print "DefNode.declare_pyfunction:", self.name, "in", env ### #print "DefNode.declare_pyfunction:", self.name, "in", env ###
name = self.name name = self.name
entry = env.lookup_here(name) entry = env.lookup_here(name)
if entry and entry.type.is_cfunction and not entry.is_builtin_cmethod and not self.is_wrapper: if entry:
warning(self.pos, "Overriding cdef method with def method.", 5) if entry.is_final_cmethod and not env.parent_type.is_final_type:
error(self.pos, "Only final types can have final Python (def/cpdef) methods")
if (entry.type.is_cfunction and not entry.is_builtin_cmethod
and not self.is_wrapper):
warning(self.pos, "Overriding cdef method with def method.", 5)
entry = env.declare_pyfunction(name, self.pos, allow_redefine=not self.is_wrapper) entry = env.declare_pyfunction(name, self.pos, allow_redefine=not self.is_wrapper)
self.entry = entry self.entry = entry
prefix = env.next_id(env.scope_prefix) prefix = env.next_id(env.scope_prefix)
...@@ -3503,7 +3508,7 @@ class CClassDefNode(ClassDefNode): ...@@ -3503,7 +3508,7 @@ class CClassDefNode(ClassDefNode):
error(self.pos, "Base class '%s' of type '%s' is incomplete" % ( error(self.pos, "Base class '%s' of type '%s' is incomplete" % (
self.base_class_name, self.class_name)) self.base_class_name, self.class_name))
elif base_class_entry.type.scope and base_class_entry.type.scope.directives and \ elif base_class_entry.type.scope and base_class_entry.type.scope.directives and \
base_class_entry.type.scope.directives['final']: base_class_entry.type.is_final_type:
error(self.pos, "Base class '%s' of type '%s' is final" % ( error(self.pos, "Base class '%s' of type '%s' is final" % (
self.base_class_name, self.class_name)) self.base_class_name, self.class_name))
elif base_class_entry.type.is_builtin_type and \ elif base_class_entry.type.is_builtin_type and \
......
...@@ -141,7 +141,7 @@ for key, val in directive_defaults.items(): ...@@ -141,7 +141,7 @@ for key, val in directive_defaults.items():
directive_scopes = { # defaults to available everywhere directive_scopes = { # defaults to available everywhere
# 'module', 'function', 'class', 'with statement' # 'module', 'function', 'class', 'with statement'
'final' : ('cclass',), # add 'method' in the future 'final' : ('cclass', 'function'),
'internal' : ('cclass',), 'internal' : ('cclass',),
'autotestdict' : ('module',), 'autotestdict' : ('module',),
'autotestdict.all' : ('module',), 'autotestdict.all' : ('module',),
......
...@@ -567,7 +567,8 @@ class PxdPostParse(CythonTransform, SkipDeclarations): ...@@ -567,7 +567,8 @@ class PxdPostParse(CythonTransform, SkipDeclarations):
err = None # allow these slots err = None # allow these slots
if isinstance(node, Nodes.CFuncDefNode): if isinstance(node, Nodes.CFuncDefNode):
if u'inline' in node.modifiers and self.scope_type == 'pxd': if (u'inline' in node.modifiers and
self.scope_type in ('pxd', 'cclass')):
node.inline_in_pxd = True node.inline_in_pxd = True
if node.visibility != 'private': if node.visibility != 'private':
err = self.ERR_NOGO_WITH_INLINE % node.visibility err = self.ERR_NOGO_WITH_INLINE % node.visibility
...@@ -1943,9 +1944,9 @@ class CreateClosureClasses(CythonTransform): ...@@ -1943,9 +1944,9 @@ class CreateClosureClasses(CythonTransform):
objstruct_cname='__pyx_Generator_object', objstruct_cname='__pyx_Generator_object',
typeobj_cname='__pyx_Generator_type', typeobj_cname='__pyx_Generator_type',
pos=pos, defining=True, implementing=True) pos=pos, defining=True, implementing=True)
entry.type.is_final_type = True
klass = entry.type.scope klass = entry.type.scope
klass.is_internal = True klass.is_internal = True
klass.directives = {'final': True}
body_type = PyrexTypes.create_typedef_type('generator_body', body_type = PyrexTypes.create_typedef_type('generator_body',
PyrexTypes.c_void_ptr_type, PyrexTypes.c_void_ptr_type,
...@@ -2041,11 +2042,11 @@ class CreateClosureClasses(CythonTransform): ...@@ -2041,11 +2042,11 @@ class CreateClosureClasses(CythonTransform):
entry = target_module_scope.declare_c_class( entry = target_module_scope.declare_c_class(
name=as_name, pos=node.pos, defining=True, name=as_name, pos=node.pos, defining=True,
implementing=True, base_type=base_type) implementing=True, base_type=base_type)
entry.type.is_final_type = True
func_scope.scope_class = entry func_scope.scope_class = entry
class_scope = entry.type.scope class_scope = entry.type.scope
class_scope.is_internal = True class_scope.is_internal = True
class_scope.directives = {'final': True}
if from_closure: if from_closure:
assert cscope.is_closure_scope assert cscope.is_closure_scope
......
...@@ -39,6 +39,7 @@ class PyrexType(BaseType): ...@@ -39,6 +39,7 @@ class PyrexType(BaseType):
# #
# is_pyobject boolean Is a Python object type # is_pyobject boolean Is a Python object type
# is_extension_type boolean Is a Python extension type # is_extension_type boolean Is a Python extension type
# is_final_type boolean Is a final extension type
# is_numeric boolean Is a C numeric type # is_numeric boolean Is a C numeric type
# is_int boolean Is a C integer type # is_int boolean Is a C integer type
# is_float boolean Is a C floating point type # is_float boolean Is a C floating point type
...@@ -90,6 +91,7 @@ class PyrexType(BaseType): ...@@ -90,6 +91,7 @@ class PyrexType(BaseType):
is_pyobject = 0 is_pyobject = 0
is_unspecified = 0 is_unspecified = 0
is_extension_type = 0 is_extension_type = 0
is_final_type = 0
is_builtin_type = 0 is_builtin_type = 0
is_numeric = 0 is_numeric = 0
is_int = 0 is_int = 0
......
...@@ -70,6 +70,8 @@ class Entry(object): ...@@ -70,6 +70,8 @@ class Entry(object):
# is_cmethod boolean Is a C method of an extension type # is_cmethod boolean Is a C method of an extension type
# is_builtin_cmethod boolean Is a C method of a builtin type (implies is_cmethod) # is_builtin_cmethod boolean Is a C method of a builtin type (implies is_cmethod)
# is_unbound_cmethod boolean Is an unbound C method of an extension type # is_unbound_cmethod boolean Is an unbound C method of an extension type
# is_final_cmethod boolean Is non-overridable C method
# is_inline_cmethod boolean Is inlined C method
# is_anonymous boolean Is a anonymous pyfunction entry # is_anonymous boolean Is a anonymous pyfunction entry
# is_type boolean Is a type definition # is_type boolean Is a type definition
# is_cclass boolean Is an extension class # is_cclass boolean Is an extension class
...@@ -139,6 +141,8 @@ class Entry(object): ...@@ -139,6 +141,8 @@ class Entry(object):
is_cmethod = 0 is_cmethod = 0
is_builtin_cmethod = False is_builtin_cmethod = False
is_unbound_cmethod = 0 is_unbound_cmethod = 0
is_final_cmethod = 0
is_inline_cmethod = 0
is_anonymous = 0 is_anonymous = 0
is_type = 0 is_type = 0
is_cclass = 0 is_cclass = 0
...@@ -157,6 +161,7 @@ class Entry(object): ...@@ -157,6 +161,7 @@ class Entry(object):
is_readonly = 0 is_readonly = 0
func_cname = None func_cname = None
func_modifiers = [] func_modifiers = []
final_func_cname = None
doc = None doc = None
as_variable = None as_variable = None
xdecref_cleanup = 0 xdecref_cleanup = 0
...@@ -829,7 +834,7 @@ class BuiltinScope(Scope): ...@@ -829,7 +834,7 @@ class BuiltinScope(Scope):
scope = CClassScope(name, outer_scope=None, visibility='extern') scope = CClassScope(name, outer_scope=None, visibility='extern')
scope.directives = {} scope.directives = {}
if name == 'bool': if name == 'bool':
scope.directives['final'] = True type.is_final_type = True
type.set_scope(scope) type.set_scope(scope)
self.type_names[name] = 1 self.type_names[name] = 1
entry = self.declare_type(name, type, None, visibility='extern') entry = self.declare_type(name, type, None, visibility='extern')
...@@ -1245,6 +1250,9 @@ class ModuleScope(Scope): ...@@ -1245,6 +1250,9 @@ class ModuleScope(Scope):
error(pos, "Type object name differs from previous declaration") error(pos, "Type object name differs from previous declaration")
type.typeobj_cname = typeobj_cname type.typeobj_cname = typeobj_cname
if self.directives.get('final'):
entry.type.is_final_type = True
# cdef classes are always exported, but we need to set it to # cdef classes are always exported, but we need to set it to
# distinguish between unused Cython utility code extension classes # distinguish between unused Cython utility code extension classes
entry.used = True entry.used = True
...@@ -1775,7 +1783,7 @@ class CClassScope(ClassScope): ...@@ -1775,7 +1783,7 @@ class CClassScope(ClassScope):
# Otherwise, subtypes may choose to override the # Otherwise, subtypes may choose to override the
# method, but the optimisation would prevent the # method, but the optimisation would prevent the
# subtype method from being called. # subtype method from being called.
if not self.directives['final']: if not self.parent_type.is_final_type:
return None return None
return entry return entry
...@@ -1798,7 +1806,9 @@ class CClassScope(ClassScope): ...@@ -1798,7 +1806,9 @@ class CClassScope(ClassScope):
if defining and entry.func_cname: if defining and entry.func_cname:
error(pos, "'%s' already defined" % name) error(pos, "'%s' already defined" % name)
#print "CClassScope.declare_cfunction: checking signature" ### #print "CClassScope.declare_cfunction: checking signature" ###
if type.same_c_signature_as(entry.type, as_cmethod = 1) and type.nogil == entry.type.nogil: if entry.is_final_cmethod:
error(pos, "Overriding final methods is not allowed")
elif type.same_c_signature_as(entry.type, as_cmethod = 1) and type.nogil == entry.type.nogil:
pass pass
elif type.compatible_signature_with(entry.type, as_cmethod = 1) and type.nogil == entry.type.nogil: elif type.compatible_signature_with(entry.type, as_cmethod = 1) and type.nogil == entry.type.nogil:
entry = self.add_cfunction(name, type, pos, cname or name, visibility='ignore', modifiers=modifiers) entry = self.add_cfunction(name, type, pos, cname or name, visibility='ignore', modifiers=modifiers)
...@@ -1816,6 +1826,12 @@ class CClassScope(ClassScope): ...@@ -1816,6 +1826,12 @@ class CClassScope(ClassScope):
if defining: if defining:
entry.func_cname = self.mangle(Naming.func_prefix, name) entry.func_cname = self.mangle(Naming.func_prefix, name)
entry.utility_code = utility_code entry.utility_code = utility_code
if u'inline' in modifiers:
entry.is_inline_cmethod = True
if (self.parent_type.is_final_type or entry.is_inline_cmethod or
self.directives.get('final')):
entry.is_final_cmethod = True
entry.final_func_cname = entry.func_cname
return entry return entry
def add_cfunction(self, name, type, pos, cname, visibility, modifiers): def add_cfunction(self, name, type, pos, cname, visibility, modifiers):
...@@ -1874,6 +1890,12 @@ class CClassScope(ClassScope): ...@@ -1874,6 +1890,12 @@ class CClassScope(ClassScope):
base_entry.pos, cname, base_entry.pos, cname,
base_entry.visibility, base_entry.func_modifiers) base_entry.visibility, base_entry.func_modifiers)
entry.is_inherited = 1 entry.is_inherited = 1
if base_entry.is_final_cmethod:
entry.is_final_cmethod = True
entry.is_inline_cmethod = base_entry.is_inline_cmethod
if (self.parent_scope == base_scope.parent_scope or
entry.is_inline_cmethod):
entry.final_func_cname = base_entry.final_func_cname
if is_builtin: if is_builtin:
entry.is_builtin_cmethod = True entry.is_builtin_cmethod = True
entry.as_variable = var_entry entry.as_variable = var_entry
......
...@@ -343,7 +343,7 @@ class TypeFlagsSlot(SlotDescriptor): ...@@ -343,7 +343,7 @@ class TypeFlagsSlot(SlotDescriptor):
def slot_code(self, scope): def slot_code(self, scope):
value = "Py_TPFLAGS_DEFAULT|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER" value = "Py_TPFLAGS_DEFAULT|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER"
if not scope.directives['final']: if not scope.parent_type.is_final_type:
value += "|Py_TPFLAGS_BASETYPE" value += "|Py_TPFLAGS_BASETYPE"
if scope.needs_gc(): if scope.needs_gc():
value += "|Py_TPFLAGS_HAVE_GC" value += "|Py_TPFLAGS_HAVE_GC"
......
"""Pyrex.Distutils.extension """Pyrex.Distutils.extension
Provides a modified Extension class, that understands hou to describe Provides a modified Extension class, that understands how to describe
Pyrex extension modules in setup scripts.""" Pyrex extension modules in setup scripts."""
__revision__ = "$Id:$" __revision__ = "$Id:$"
import os, sys import os
import sys
from types import * from types import *
import distutils.extension as _Extension import distutils.extension as _Extension
...@@ -23,7 +24,7 @@ class Extension(_Extension.Extension): ...@@ -23,7 +24,7 @@ class Extension(_Extension.Extension):
dict of compiler directives dict of compiler directives
pyrex_create_listing_file : boolean pyrex_create_listing_file : boolean
write pyrex error messages to a listing (.lis) file. write pyrex error messages to a listing (.lis) file.
pyrex_line_directivess : boolean pyrex_line_directives : boolean
emit pyx line numbers for debugging/profiling emit pyx line numbers for debugging/profiling
pyrex_cplus : boolean pyrex_cplus : boolean
use the C++ compiler for compiling and linking. use the C++ compiler for compiling and linking.
......
# mode: error
cimport cython
cdef class BaseClass:
@cython.final
cdef cdef_method(self):
pass
@cython.final
cpdef cpdef_method(self):
pass
cdef class SubType(BaseClass):
cdef cdef_method(self):
pass
_ERRORS = """
11:10: Only final types can have final Python (def/cpdef) methods
16:9: Overriding final methods is not allowed
"""
cdef class TestInlineMethod(object):
cdef inline int cdef_inline_method(self):
return 0
# mode: run
# ticket: 474
cimport cython
cdef class TestInlineMethod(object):
"""
>>> test = TestInlineMethod()
>>> test.test_cdef_method()
0
"""
@cython.test_assert_path_exists("//AttributeNode[@entry.is_final_cmethod=True]")
@cython.test_assert_path_exists("//AttributeNode[@entry.is_inline_cmethod=True]")
def test_cdef_method(self):
return self.cdef_inline_method()
cdef class Subtyping(TestInlineMethod):
"""
>>> test = Subtyping()
>>> test.test_cdef_subtyping()
0
"""
@cython.test_assert_path_exists("//AttributeNode[@entry.is_final_cmethod=True]")
@cython.test_assert_path_exists("//AttributeNode[@entry.is_inline_cmethod=True]")
def test_cdef_subtyping(self):
return self.cdef_inline_method()
# mode: run
# ticket: 568
cimport cython
@cython.final
cdef class FinalType(object):
"""
>>> obj = FinalType()
>>> obj.test_cdef()
>>> obj.test_cpdef()
"""
@cython.test_assert_path_exists("//CFuncDefNode[@entry.is_final_cmethod=True]")
cdef cdef_method(self):
pass
@cython.test_assert_path_exists("//CFuncDefNode[@entry.is_final_cmethod=True]")
@cython.test_fail_if_path_exists("//CFuncDefNode//OverrideCheckNode")
cpdef cpdef_method(self):
pass
@cython.test_assert_path_exists("//AttributeNode[@entry.is_final_cmethod=True]")
def test_cdef(self):
self.cdef_method()
@cython.test_assert_path_exists("//AttributeNode[@entry.is_final_cmethod=True]")
def test_cpdef(self):
self.cpdef_method()
cdef class BaseTypeWithFinalMethods(object):
"""
>>> obj = BaseTypeWithFinalMethods()
>>> obj.test_cdef()
"""
@cython.test_assert_path_exists("//CFuncDefNode[@entry.is_final_cmethod=True]")
@cython.final
cdef cdef_method(self):
pass
@cython.test_assert_path_exists("//AttributeNode[@entry.is_final_cmethod=True]")
def test_cdef(self):
self.cdef_method()
cdef class SubType(BaseTypeWithFinalMethods):
"""
>>> obj = SubType()
>>> obj.test_cdef()
"""
@cython.test_assert_path_exists("//AttributeNode[@entry.is_final_cmethod=True]")
def test_cdef(self):
self.cdef_method()
...@@ -338,3 +338,14 @@ def s(a): ...@@ -338,3 +338,14 @@ def s(a):
""" """
cdef int result = a in [1,2,3,4] in [[1,2,3],[2,3,4],[1,2,3,4]] cdef int result = a in [1,2,3,4] in [[1,2,3],[2,3,4],[1,2,3,4]]
return result return result
@cython.test_assert_path_exists("//ReturnStatNode//BoolNode")
@cython.test_fail_if_path_exists("//SwitchStatNode")
def constant_empty_sequence(a):
"""
>>> constant_empty_sequence(1)
False
>>> constant_empty_sequence(5)
False
"""
return a in ()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment