Commit ee80d4b8 authored by Robert Bradshaw's avatar Robert Bradshaw

Static cdef methods.

parent e406de9e
......@@ -9,6 +9,10 @@ Latest
Features added
--------------
* Allow @staticmethod decorator to declare static cdef methods.
This is especially useful for declaring "constructors" for
cdef classes that can take non-Python arguments.
* Generators have new properties ``__name__`` and ``__qualname__``
that provide the plain/qualified name of the generator function
(following CPython 3.5). See http://bugs.python.org/issue21205
......
......@@ -4449,7 +4449,7 @@ class SimpleCallNode(CallNode):
self.type = error_type
return
if self.self:
if self.self and not self.function.type.is_static_method:
args = [self.self] + self.args
else:
args = self.args
......@@ -4475,6 +4475,7 @@ class SimpleCallNode(CallNode):
else:
alternatives = overloaded_entry.all_alternatives()
print self.function.type, args
entry = PyrexTypes.best_match(args, alternatives, self.pos, env)
if not entry:
......@@ -4504,7 +4505,7 @@ class SimpleCallNode(CallNode):
self.is_temp = 1
# check 'self' argument
if entry and entry.is_cmethod and func_type.args:
if entry and entry.is_cmethod and func_type.args and not func_type.is_static_method:
formal_arg = func_type.args[0]
arg = args[0]
if formal_arg.not_none:
......
......@@ -616,7 +616,9 @@ class CFuncDeclaratorNode(CDeclaratorNode):
func_type_args = []
for i, arg_node in enumerate(self.args):
name_declarator, type = arg_node.analyse(
env, nonempty=nonempty, is_self_arg=(i == 0 and env.is_c_class_scope))
env, nonempty=nonempty, is_self_arg=(i == 0 and env.is_c_class_scope and 'staticmethod' not in env.directives))
print arg_node
print name_declarator, type, (i == 0 and env.is_c_class_scope and 'staticmethod' not in env.directives)
name = name_declarator.name
if name in directive_locals:
type_node = directive_locals[name]
......@@ -795,6 +797,7 @@ class CArgDeclNode(Node):
is_dynamic = 0
def analyse(self, env, nonempty = 0, is_self_arg = False):
print "is_self_arg", is_self_arg, "type", self.type, "self.base_type", self.base_type, "self.base_type.is_self_arg", self.base_type.is_self_arg
if is_self_arg:
self.base_type.is_self_arg = self.is_self_arg = True
if self.type is None:
......@@ -815,6 +818,7 @@ class CArgDeclNode(Node):
could_be_name = False
self.base_type.is_arg = True
base_type = self.base_type.analyse(env, could_be_name=could_be_name)
print "base_type", base_type
if hasattr(self.base_type, 'arg_name') and self.base_type.arg_name:
self.declarator.name = self.base_type.arg_name
......@@ -2140,6 +2144,7 @@ class CFuncDefNode(FuncDefNode):
# inline_in_pxd whether this is an inline function in a pxd file
# template_declaration String or None Used for c++ class methods
# is_const_method whether this is a const method
# is_static_method whether this is a static method
child_attrs = ["base_type", "declarator", "body", "py_func"]
......@@ -2165,6 +2170,7 @@ class CFuncDefNode(FuncDefNode):
base_type = PyrexTypes.error_type
else:
base_type = self.base_type.analyse(env)
self.is_static_method = 'staticmethod' in env.directives
# The 2 here is because we need both function and argument names.
if isinstance(self.declarator, CFuncDeclaratorNode):
name_declarator, type = self.declarator.analyse(base_type, env,
......@@ -2179,6 +2185,7 @@ class CFuncDefNode(FuncDefNode):
# written here, because the type in the symbol table entry
# may be different if we're overriding a C method inherited
# from the base type of an extension type.
print "type", type
self.type = type
type.is_overridable = self.overridable
declarator = self.declarator
......@@ -2226,6 +2233,7 @@ class CFuncDefNode(FuncDefNode):
cname = name_declarator.cname
type.is_const_method = self.is_const_method
type.is_static_method = self.is_static_method
self.entry = env.declare_cfunction(
name, type, self.pos,
cname = cname, visibility = self.visibility, api = self.api,
......@@ -2238,7 +2246,7 @@ class CFuncDefNode(FuncDefNode):
if self.return_type.is_cpp_class:
self.return_type.check_nullary_constructor(self.pos, "used as a return value")
if self.overridable and not env.is_module_scope:
if self.overridable and not env.is_module_scope and not self.is_static_method:
if len(self.args) < 1 or not self.args[0].type.is_pyobject:
# An error will be produced in the cdef function
self.overridable = False
......
......@@ -209,6 +209,7 @@ directive_types = {
'cfunc' : None, # decorators do not take directive value
'ccall' : None,
'inline' : None,
'staticmethod' : None,
'cclass' : None,
'returns' : type,
'set_initial_path': str,
......@@ -225,6 +226,7 @@ directive_scopes = { # defaults to available everywhere
# 'module', 'function', 'class', 'with statement'
'final' : ('cclass', 'function'),
'inline' : ('function',),
'staticmethod' : ('function',), # FIXME: analysis currently lacks more specific function scope
'no_gc_clear' : ('cclass',),
'internal' : ('cclass',),
'autotestdict' : ('module',),
......
......@@ -674,11 +674,13 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
for key, value in compilation_directive_defaults.items():
self.compilation_directive_defaults[unicode(key)] = copy.deepcopy(value)
self.cython_module_names = set()
self.directive_names = {}
self.directive_names = {'staticmethod': 'staticmethod'}
self.parallel_directives = {}
def check_directive_scope(self, pos, directive, scope):
print 'check_directive_scope', pos, directive, scope
legal_scopes = Options.directive_scopes.get(directive, None)
print legal_scopes
if legal_scopes and scope not in legal_scopes:
self.context.nonfatal_error(PostParseError(pos, 'The %s compiler directive '
'is not allowed in %s scope' % (directive, scope)))
......@@ -979,18 +981,23 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
# Split the decorators into two lists -- real decorators and directives
directives = []
realdecs = []
both = []
for dec in node.decorators:
new_directives = self.try_to_parse_directives(dec.decorator)
if new_directives is not None:
for directive in new_directives:
if self.check_directive_scope(node.pos, directive[0], scope_name):
directives.append(directive)
name, value = directive
if self.directives.get(name, object()) != value:
directives.append(directive)
if directive[0] == 'staticmethod':
both.append(dec)
else:
realdecs.append(dec)
if realdecs and isinstance(node, (Nodes.CFuncDefNode, Nodes.CClassDefNode, Nodes.CVarDefNode)):
raise PostParseError(realdecs[0].pos, "Cdef functions/classes cannot take arbitrary decorators.")
else:
node.decorators = realdecs
node.decorators = realdecs + both
# merge or override repeated directives
optdict = {}
directives.reverse() # Decorators coming first take precedence
......@@ -2705,6 +2712,8 @@ class TransformBuiltinMethods(EnvTransform):
node.cdivision = True
elif function == u'set':
node.function = ExprNodes.NameNode(node.pos, name=EncodedString('set'))
elif function == u'staticmethod':
node.function = ExprNodes.NameNode(node.pos, name=EncodedString('staticmethod'))
elif self.context.cython_scope.lookup_qualified_name(function):
pass
else:
......
......@@ -2315,6 +2315,7 @@ class CFuncType(CType):
# is_strict_signature boolean function refuses to accept coerced arguments
# (used for optimisation overrides)
# is_const_method boolean
# is_static_method boolean
is_cfunction = 1
original_sig = None
......@@ -2327,7 +2328,8 @@ class CFuncType(CType):
def __init__(self, return_type, args, has_varargs = 0,
exception_value = None, exception_check = 0, calling_convention = "",
nogil = 0, with_gil = 0, is_overridable = 0, optional_arg_count = 0,
is_const_method = False, templates = None, is_strict_signature = False):
is_const_method = False, is_static_method=False,
templates = None, is_strict_signature = False):
self.return_type = return_type
self.args = args
self.has_varargs = has_varargs
......@@ -2339,6 +2341,7 @@ class CFuncType(CType):
self.with_gil = with_gil
self.is_overridable = is_overridable
self.is_const_method = is_const_method
self.is_static_method = is_static_method
self.templates = templates
self.is_strict_signature = is_strict_signature
......@@ -2563,6 +2566,7 @@ class CFuncType(CType):
is_overridable = self.is_overridable,
optional_arg_count = self.optional_arg_count,
is_const_method = self.is_const_method,
is_static_method = self.is_static_method,
templates = self.templates)
result.from_fused = self.is_fused
......
......@@ -1945,11 +1945,12 @@ class CClassScope(ClassScope):
if get_special_method_signature(name) and not self.parent_type.is_builtin_type:
error(pos, "Special methods must be declared with 'def', not 'cdef'")
args = type.args
if not args:
error(pos, "C method has no self argument")
elif not self.parent_type.assignable_from(args[0].type):
error(pos, "Self argument (%s) of C method '%s' does not match parent type (%s)" %
(args[0].type, name, self.parent_type))
if not type.is_static_method:
if not args:
error(pos, "C method has no self argument")
elif not self.parent_type.assignable_from(args[0].type):
error(pos, "Self argument (%s) of C method '%s' does not match parent type (%s)" %
(args[0].type, name, self.parent_type))
entry = self.lookup_here(name)
if cname is None:
cname = c_safe_identifier(name)
......
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