Commit 7148a1a7 authored by Robert Bradshaw's avatar Robert Bradshaw

Merge closures into main development branch.

parents 8d4fdaad 48d7588a
......@@ -415,7 +415,7 @@ def init_builtins():
init_builtin_structs()
global list_type, tuple_type, dict_type, set_type, frozenset_type
global bytes_type, str_type, unicode_type
global float_type, bool_type, type_type
global float_type, bool_type, type_type, complex_type
type_type = builtin_scope.lookup('type').type
list_type = builtin_scope.lookup('list').type
tuple_type = builtin_scope.lookup('tuple').type
......@@ -427,5 +427,6 @@ def init_builtins():
unicode_type = builtin_scope.lookup('unicode').type
float_type = builtin_scope.lookup('float').type
bool_type = builtin_scope.lookup('bool').type
complex_type = builtin_scope.lookup('complex').type
init_builtins()
......@@ -407,6 +407,7 @@ class GlobalState(object):
code_layout = [
'h_code',
'filename_table',
'utility_code_proto_before_types',
'numeric_typedefs', # Let these detailed individual parts stay!,
'complex_type_declarations', # as the proper solution is to make a full DAG...
......@@ -425,7 +426,6 @@ class GlobalState(object):
'cleanup_globals',
'cleanup_module',
'main_method',
'filename_table',
'utility_code_def',
'end'
]
......@@ -484,11 +484,6 @@ class GlobalState(object):
code.write('\n#line 1 "cython_utility"\n')
code.putln("")
code.putln("/* Runtime support code */")
code.putln("")
code.putln("static void %s(void) {" % Naming.fileinit_cname)
code.putln("%s = %s;" %
(Naming.filetable_cname, Naming.filenames_cname))
code.putln("}")
def finalize_main_c_code(self):
self.close_global_decls()
......
......@@ -70,7 +70,7 @@ class ControlFlow(object):
if current is None:
return (None, None)
state = current._get_pos_state_local(item, pos)
while state is None and current.incoming is not None:
while (state is None or state == (None, None)) and current.incoming is not None:
current = current.incoming
state = current._get_pos_state_local(item, pos)
if state is None:
......
This diff is collapsed.
......@@ -14,6 +14,7 @@ def make_lexicon():
letter = Any("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_")
digit = Any("0123456789")
bindigit = Any("01")
octdigit = Any("01234567")
hexdigit = Any("0123456789ABCDEFabcdef")
indentation = Bol + Rep(Any(" \t"))
......@@ -24,7 +25,9 @@ def make_lexicon():
decimal_fract = (decimal + dot + Opt(decimal)) | (dot + decimal)
name = letter + Rep(letter | digit)
intconst = decimal | (Str("0x") + Rep1(hexdigit))
intconst = decimal | (Str("0") + ((Any("Xx") + Rep1(hexdigit)) |
(Any("Oo") + Rep1(octdigit)) |
(Any("Bb") + Rep1(bindigit)) ))
intsuffix = (Opt(Any("Uu")) + Opt(Any("Ll")) + Opt(Any("Ll"))) | (Opt(Any("Ll")) + Opt(Any("Ll")) + Opt(Any("Uu")))
intliteral = intconst + intsuffix
fltconst = (decimal_fract + Opt(exponent)) | (decimal + exponent)
......@@ -72,7 +75,7 @@ def make_lexicon():
punct = Any(":,;+-*/|&<>=.%`~^?")
diphthong = Str("==", "<>", "!=", "<=", ">=", "<<", ">>", "**", "//",
"+=", "-=", "*=", "/=", "%=", "|=", "^=", "&=",
"<<=", ">>=", "**=", "//=")
"<<=", ">>=", "**=", "//=", "->")
spaces = Rep1(Any(" \t\f"))
escaped_newline = Str("\\\n")
lineterm = Eol + Opt(Str("\n"))
......
......@@ -136,11 +136,13 @@ class Context(object):
_specific_post_parse,
InterpretCompilerDirectives(self, self.compiler_directives),
_align_function_definitions,
MarkClosureVisitor(self),
ConstantFolding(),
FlattenInListTransform(),
WithTransform(self),
DecoratorTransform(self),
AnalyseDeclarationsTransform(self),
CreateClosureClasses(self),
AutoTestDictTransform(self),
EmbedSignature(self),
EarlyReplaceBuiltinCalls(self),
......
......@@ -270,11 +270,14 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code = globalstate['all_the_rest']
self.generate_cached_builtins_decls(env, code)
# generate lambda function definitions
for node in env.lambda_defs:
node.generate_function_definitions(env, code)
# generate normal function definitions
self.body.generate_function_definitions(env, code)
code.mark_pos(None)
self.generate_typeobj_definitions(env, code)
self.generate_method_table(env, code)
self.generate_filename_init_prototype(code)
if env.has_import_star:
self.generate_import_star(env, code)
self.generate_pymoduledef_struct(env, code)
......@@ -545,6 +548,12 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
#define PyBytes_Repr PyString_Repr
#define PyBytes_Concat PyString_Concat
#define PyBytes_ConcatAndDel PyString_ConcatAndDel
#define PySet_Check(obj) PyObject_TypeCheck(obj, &PySet_Type)
#define PyFrozenSet_Check(obj) PyObject_TypeCheck(obj, &PyFrozenSet_Type)
#endif
#ifndef PySet_CheckExact
# define PySet_CheckExact(obj) (Py_TYPE(obj) == &PySet_Type)
#endif
#if PY_MAJOR_VERSION >= 3
......@@ -580,7 +589,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.put("""
#if PY_MAJOR_VERSION >= 3
#define PyMethod_New(func, self, klass) PyInstanceMethod_New(func)
#define PyMethod_New(func, self, klass) ((self) ? PyMethod_New(func, self) : PyInstanceMethod_New(func))
#endif
#if PY_VERSION_HEX < 0x02050000
......@@ -631,7 +640,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln('static int %s = 0;' % Naming.clineno_cname)
code.putln('static const char * %s= %s;' % (Naming.cfilenm_cname, Naming.file_c_macro))
code.putln('static const char *%s;' % Naming.filename_cname)
code.putln('static const char **%s;' % Naming.filetable_cname)
# XXX this is a mess
for utility_code in PyrexTypes.c_int_from_py_function.specialize_list:
......@@ -658,13 +666,12 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
def generate_filename_table(self, code):
code.putln("")
code.putln("static const char *%s[] = {" % Naming.filenames_cname)
code.putln("static const char *%s[] = {" % Naming.filetable_cname)
if code.globalstate.filename_list:
for source_desc in code.globalstate.filename_list:
filename = os.path.basename(source_desc.get_filenametable_entry())
escaped_filename = filename.replace("\\", "\\\\").replace('"', r'\"')
code.putln('"%s",' %
escaped_filename)
code.putln('"%s",' % escaped_filename)
else:
# Some C compilers don't like an empty array
code.putln("0")
......@@ -1003,7 +1010,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
type.vtabslot_cname,
struct_type_cast, type.vtabptr_cname))
for entry in py_attrs:
if entry.name == "__weakref__":
if scope.is_internal or entry.name == "__weakref__":
# internal classes do not need None inits
code.putln("p->%s = 0;" % entry.cname)
else:
code.put_init_var_to_py_none(entry, "p->%s", nanny=False)
......@@ -1584,10 +1592,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln(
"};")
def generate_filename_init_prototype(self, code):
code.putln("");
code.putln("static void %s(void); /*proto*/" % Naming.fileinit_cname)
def generate_import_star(self, env, code):
env.use_utility_code(streq_utility_code)
code.putln()
......@@ -1678,11 +1682,13 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln("__pyx_refnanny = __Pyx_RefNanny->SetupContext(\"%s\", __LINE__, __FILE__);"% header3)
code.putln("#endif")
self.generate_filename_init_call(code)
code.putln("%s = PyTuple_New(0); %s" % (Naming.empty_tuple, code.error_goto_if_null(Naming.empty_tuple, self.pos)));
code.putln("%s = PyBytes_FromStringAndSize(\"\", 0); %s" % (Naming.empty_bytes, code.error_goto_if_null(Naming.empty_bytes, self.pos)));
code.putln("#ifdef %s_USED" % Naming.binding_cfunc)
code.putln("if (%s_init() < 0) %s" % (Naming.binding_cfunc, code.error_goto(self.pos)))
code.putln("#endif")
code.putln("/*--- Library function declarations ---*/")
env.generate_library_function_declarations(code)
......@@ -1816,9 +1822,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
module_is_main = "%s%s" % (Naming.module_is_main, self.full_module_name.replace('.', '__'))
code.globalstate.use_utility_code(main_method.specialize(module_name=env.module_name, module_is_main=module_is_main))
def generate_filename_init_call(self, code):
code.putln("%s();" % Naming.fileinit_cname)
def generate_pymoduledef_struct(self, env, code):
if env.doc:
doc = "__Pyx_DOCSTR(%s)" % code.get_string_const(env.doc)
......
......@@ -44,6 +44,9 @@ vtabptr_prefix = pyrex_prefix + "vtabptr_"
vtabstruct_prefix = pyrex_prefix + "vtabstruct_"
opt_arg_prefix = pyrex_prefix + "opt_args_"
convert_func_prefix = pyrex_prefix + "convert_"
closure_scope_prefix = pyrex_prefix + "scope_"
closure_class_prefix = pyrex_prefix + "scope_struct_"
lambda_func_prefix = pyrex_prefix + "lambda_"
module_is_main = pyrex_prefix + "module_is_main_"
args_cname = pyrex_prefix + "args"
......@@ -56,8 +59,6 @@ dummy_cname = pyrex_prefix + "dummy"
filename_cname = pyrex_prefix + "filename"
modulename_cname = pyrex_prefix + "modulename"
filetable_cname = pyrex_prefix + "f"
filenames_cname = pyrex_prefix + "filenames"
fileinit_cname = pyrex_prefix + "init_filenames"
intern_tab_cname = pyrex_prefix + "intern_tab"
kwds_cname = pyrex_prefix + "kwds"
lineno_cname = pyrex_prefix + "lineno"
......@@ -83,10 +84,14 @@ pymoduledef_cname = pyrex_prefix + "moduledef"
optional_args_cname = pyrex_prefix + "optional_args"
import_star = pyrex_prefix + "import_star"
import_star_set = pyrex_prefix + "import_star_set"
outer_scope_cname= pyrex_prefix + "outer_scope"
cur_scope_cname = pyrex_prefix + "cur_scope"
enc_scope_cname = pyrex_prefix + "enc_scope"
frame_cname = pyrex_prefix + "frame"
frame_code_cname = pyrex_prefix + "frame_code"
binding_cfunc = pyrex_prefix + "binding_PyCFunctionType"
genexpr_id_ref = 'genexpr'
line_c_macro = "__LINE__"
......
This diff is collapsed.
This diff is collapsed.
......@@ -74,6 +74,9 @@ directive_defaults = {
# test support
'test_assert_path_exists' : [],
'test_fail_if_path_exists' : [],
# experimental, subject to change
'binding': False,
}
# Override types possibilities above, if needed
......
......@@ -177,6 +177,7 @@ class PostParse(CythonTransform):
def visit_ModuleNode(self, node):
self.scope_type = 'module'
self.scope_node = node
self.lambda_counter = 1
self.visitchildren(node)
return node
......@@ -197,6 +198,25 @@ class PostParse(CythonTransform):
def visit_CStructOrUnionDefNode(self, node):
return self.visit_scope(node, 'struct')
def visit_LambdaNode(self, node):
# unpack a lambda expression into the corresponding DefNode
if self.scope_type != 'function':
error(node.pos,
"lambda functions are currently only supported in functions")
lambda_id = self.lambda_counter
self.lambda_counter += 1
node.lambda_name = EncodedString(u'lambda%d' % lambda_id)
body = Nodes.ReturnStatNode(
node.result_expr.pos, value = node.result_expr)
node.def_node = Nodes.DefNode(
node.pos, name=node.name, lambda_name=node.lambda_name,
args=node.args, star_arg=node.star_arg,
starstar_arg=node.starstar_arg,
body=body)
self.visitchildren(node)
return node
# cdef variables
def handle_bufferdefaults(self, decl):
if not isinstance(decl.default, DictNode):
......@@ -983,7 +1003,12 @@ property NAME:
self.visitchildren(node)
self.seen_vars_stack.pop()
return node
def visit_LambdaNode(self, node):
node.analyse_declarations(self.env_stack[-1])
self.visitchildren(node)
return node
def visit_ClassDefNode(self, node):
self.env_stack.append(node.scope)
self.visitchildren(node)
......@@ -1014,6 +1039,23 @@ property NAME:
node.analyse_declarations(self.env_stack[-1])
return node
def visit_ScopedExprNode(self, node):
node.analyse_declarations(self.env_stack[-1])
if self.seen_vars_stack:
self.seen_vars_stack.append(set(self.seen_vars_stack[-1]))
else:
self.seen_vars_stack.append(set())
self.env_stack.append(node.expr_scope)
self.visitchildren(node)
self.env_stack.pop()
self.seen_vars_stack.pop()
return node
def visit_TempResultFromStatNode(self, node):
self.visitchildren(node)
node.analyse_declarations(self.env_stack[-1])
return node
# Some nodes are no longer needed after declaration
# analysis and can be dropped. The analysis was performed
# on these nodes in a seperate recursive process from the
......@@ -1110,6 +1152,13 @@ class AnalyseExpressionsTransform(CythonTransform):
node.body.analyse_expressions(node.local_scope)
self.visitchildren(node)
return node
def visit_ScopedExprNode(self, node):
if node.expr_scope is not None:
node.expr_scope.infer_types()
node.analyse_scoped_expressions(node.expr_scope)
self.visitchildren(node)
return node
class AlignFunctionDefinitions(CythonTransform):
"""
......@@ -1175,15 +1224,26 @@ class MarkClosureVisitor(CythonTransform):
node.needs_closure = self.needs_closure
self.needs_closure = True
return node
def visit_ClassDefNode(self, node):
def visit_CFuncDefNode(self, node):
self.visit_FuncDefNode(node)
if node.needs_closure:
error(node.pos, "closures inside cdef functions not yet supported")
return node
def visit_LambdaNode(self, node):
self.needs_closure = False
self.visitchildren(node)
node.needs_closure = self.needs_closure
self.needs_closure = True
return node
def visit_YieldNode(self, node):
def visit_ClassDefNode(self, node):
self.visitchildren(node)
self.needs_closure = True
return node
class CreateClosureClasses(CythonTransform):
# Output closure classes in module scope for all functions
# that need it.
......@@ -1194,21 +1254,39 @@ class CreateClosureClasses(CythonTransform):
return node
def create_class_from_scope(self, node, target_module_scope):
as_name = temp_name_handle("closure")
as_name = "%s%s" % (Naming.closure_class_prefix, node.entry.cname)
func_scope = node.local_scope
entry = target_module_scope.declare_c_class(name = as_name,
pos = node.pos, defining = True, implementing = True)
func_scope.scope_class = entry
class_scope = entry.type.scope
for entry in func_scope.entries.values():
class_scope.is_internal = True
if node.entry.scope.is_closure_scope:
class_scope.declare_var(pos=node.pos,
name=Naming.outer_scope_cname, # this could conflict?
cname=Naming.outer_scope_cname,
type=node.entry.scope.scope_class.type,
is_cdef=True)
entries = func_scope.entries.items()
entries.sort()
for name, entry in entries:
# This is wasteful--we should do this later when we know
# which vars are actually being used inside...
#
# Also, this happens before type inference and type
# analysis, so the entries created here may end up having
# incorrect or at least unspecified types.
class_scope.declare_var(pos=entry.pos,
name=entry.name,
cname=entry.cname,
type=entry.type,
is_cdef=True)
def visit_FuncDefNode(self, node):
self.create_class_from_scope(node, self.module_scope)
if node.needs_closure:
self.create_class_from_scope(node, self.module_scope)
self.visitchildren(node)
return node
......
......@@ -6,17 +6,21 @@ from Cython.Compiler.Scanning cimport PyrexScanner
cpdef p_ident(PyrexScanner s, message =*)
cpdef p_ident_list(PyrexScanner s)
cpdef p_binop_operator(PyrexScanner s)
cpdef p_binop_expr(PyrexScanner s, ops, p_sub_expr)
cpdef p_simple_expr(PyrexScanner s)
cpdef p_lambdef(PyrexScanner s, bint allow_conditional=*)
cpdef p_lambdef_nocond(PyrexScanner s)
cpdef p_test(PyrexScanner s)
cpdef p_test_nocond(PyrexScanner s)
cpdef p_or_test(PyrexScanner s)
cpdef p_rassoc_binop_expr(PyrexScanner s, ops, p_subexpr)
cpdef p_and_test(PyrexScanner s)
cpdef p_not_test(PyrexScanner s)
cpdef p_comparison(PyrexScanner s)
cpdef p_test_or_starred_expr(PyrexScanner s)
cpdef p_starred_expr(PyrexScanner s)
cpdef p_cascaded_cmp(PyrexScanner s)
cpdef p_cmp_op(PyrexScanner s)
cpdef p_starred_expr(PyrexScanner s)
cpdef p_bit_expr(PyrexScanner s)
cpdef p_xor_expr(PyrexScanner s)
cpdef p_and_expr(PyrexScanner s)
......@@ -27,6 +31,7 @@ cpdef p_factor(PyrexScanner s)
cpdef p_typecast(PyrexScanner s)
cpdef p_sizeof(PyrexScanner s)
cpdef p_yield_expression(PyrexScanner s)
cpdef p_yield_statement(PyrexScanner s)
cpdef p_power(PyrexScanner s)
cpdef p_new_expr(PyrexScanner s)
cpdef p_trailer(PyrexScanner s, node1)
......@@ -44,14 +49,17 @@ cpdef p_cat_string_literal(PyrexScanner s)
cpdef p_opt_string_literal(PyrexScanner s)
cpdef p_string_literal(PyrexScanner s, kind_override=*)
cpdef p_list_maker(PyrexScanner s)
cpdef p_list_iter(PyrexScanner s, body)
cpdef p_list_for(PyrexScanner s, body)
cpdef p_list_if(PyrexScanner s, body)
cpdef p_comp_iter(PyrexScanner s, body)
cpdef p_comp_for(PyrexScanner s, body)
cpdef p_comp_if(PyrexScanner s, body)
cpdef p_dict_or_set_maker(PyrexScanner s)
cpdef p_backquote_expr(PyrexScanner s)
cpdef p_simple_expr_list(PyrexScanner s)
cpdef p_expr(PyrexScanner s)
cpdef p_simple_expr_list(PyrexScanner s, expr=*)
cpdef p_test_or_starred_expr_list(s, expr=*)
cpdef p_testlist(PyrexScanner s)
cpdef p_testlist_star_expr(PyrexScanner s)
cpdef p_testlist_comp(PyrexScanner s)
cpdef p_genexp(PyrexScanner s, expr)
#-------------------------------------------------------
#
......@@ -80,12 +88,12 @@ cpdef p_if_clause(PyrexScanner s)
cpdef p_else_clause(PyrexScanner s)
cpdef p_while_statement(PyrexScanner s)
cpdef p_for_statement(PyrexScanner s)
cpdef p_for_bounds(PyrexScanner s)
cpdef p_for_bounds(PyrexScanner s, bint allow_testlist = *)
cpdef p_for_from_relation(PyrexScanner s)
cpdef p_for_from_step(PyrexScanner s)
cpdef p_target(PyrexScanner s, terminator)
cpdef p_for_target(PyrexScanner s)
cpdef p_for_iterator(PyrexScanner s)
cpdef p_for_iterator(PyrexScanner s, bint allow_testlist = *)
cpdef p_try_statement(PyrexScanner s)
cpdef p_except_clause(PyrexScanner s)
cpdef p_include_statement(PyrexScanner s, ctx)
......@@ -122,23 +130,24 @@ cpdef p_nogil(PyrexScanner s)
cpdef p_with_gil(PyrexScanner s)
cpdef p_exception_value_clause(PyrexScanner s)
cpdef p_c_arg_list(PyrexScanner s, ctx = *, bint in_pyfunc = *, bint cmethod_flag = *,
bint nonempty_declarators = *, bint kw_only = *)
bint nonempty_declarators = *, bint kw_only = *, bint annotated = *)
cpdef p_optional_ellipsis(PyrexScanner s)
cpdef p_c_arg_decl(PyrexScanner s, ctx, in_pyfunc, bint cmethod_flag = *, bint nonempty = *, bint kw_only = *)
cpdef p_c_arg_decl(PyrexScanner s, ctx, in_pyfunc, bint cmethod_flag = *, bint nonempty = *, bint kw_only = *, bint annotated = *)
cpdef p_api(PyrexScanner s)
cpdef p_cdef_statement(PyrexScanner s, ctx)
cpdef p_cdef_block(PyrexScanner s, ctx)
cpdef p_cdef_extern_block(PyrexScanner s, pos, ctx)
cpdef p_c_enum_definition(PyrexScanner s, pos, ctx)
cpdef p_c_enum_line(PyrexScanner s, ctx, items)
cpdef p_c_enum_item(PyrexScanner s, ctx, items)
cpdef p_c_enum_line(PyrexScanner s, ctx, list items)
cpdef p_c_enum_item(PyrexScanner s, ctx, list items)
cpdef p_c_struct_or_union_definition(PyrexScanner s, pos, ctx)
cpdef p_visibility(PyrexScanner s, prev_visibility)
cpdef p_c_modifiers(PyrexScanner s)
cpdef p_c_func_or_var_declaration(PyrexScanner s, pos, ctx)
cpdef p_ctypedef_statement(PyrexScanner s, ctx)
cpdef p_decorators(PyrexScanner s)
cpdef p_def_statement(PyrexScanner s, decorators = *)
cpdef p_def_statement(PyrexScanner s, list decorators = *)
cpdef p_varargslist(PyrexScanner s, terminator=*, bint annotated = *)
cpdef p_py_arg_decl(PyrexScanner s)
cpdef p_class_statement(PyrexScanner s, decorators)
cpdef p_c_class_definition(PyrexScanner s, pos, ctx)
......
This diff is collapsed.
......@@ -416,20 +416,27 @@ class BuiltinObjectType(PyObjectType):
def subtype_of(self, type):
return type.is_pyobject and self.assignable_from(type)
def type_test_code(self, arg, notnone=False):
def type_check_function(self, exact=True):
type_name = self.name
if type_name == 'bool':
return 'PyBool_Check'
if type_name == 'str':
type_check = 'PyString_CheckExact'
elif type_name == 'set':
type_check = 'PyAnySet_CheckExact'
type_check = 'PyString_Check'
elif type_name == 'frozenset':
type_check = 'PyFrozenSet_CheckExact'
elif type_name == 'bool':
type_check = 'PyBool_Check'
type_check = 'PyFrozenSet_Check'
else:
type_check = 'Py%s_CheckExact' % type_name.capitalize()
type_check = 'Py%s_Check' % type_name.capitalize()
if exact:
type_check += 'Exact'
return type_check
def isinstance_code(self, arg):
return '%s(%s)' % (self.type_check_function(exact=False), arg)
def type_test_code(self, arg, notnone=False):
type_check = self.type_check_function(exact=True)
check = 'likely(%s(%s))' % (type_check, arg)
if not notnone:
check = check + ('||((%s) == Py_None)' % arg)
......
......@@ -20,6 +20,7 @@ try:
set
except NameError:
from sets import Set as set
import copy
possible_identifier = re.compile(ur"(?![0-9])\w+$", re.U).match
nice_identifier = re.compile('^[a-zA-Z0-0_]+$').match
......@@ -146,6 +147,7 @@ class Entry(object):
is_arg = 0
is_local = 0
in_closure = 0
from_closure = 0
is_declared_generic = 0
is_readonly = 0
func_cname = None
......@@ -207,6 +209,7 @@ class Scope(object):
# return_type PyrexType or None Return type of function owning scope
# is_py_class_scope boolean Is a Python class scope
# is_c_class_scope boolean Is an extension type scope
# is_closure_scope boolean
# is_cpp_class_scope boolean Is a C++ class scope
# is_property_scope boolean Is a extension type property scope
# scope_prefix string Disambiguator for C names
......@@ -218,12 +221,15 @@ class Scope(object):
# nogil boolean In a nogil section
# directives dict Helper variable for the recursive
# analysis, contains directive values.
# is_internal boolean Is only used internally (simpler setup)
is_py_class_scope = 0
is_c_class_scope = 0
is_closure_scope = 0
is_cpp_class_scope = 0
is_property_scope = 0
is_module_scope = 0
is_internal = 0
scope_prefix = ""
in_cinclude = 0
nogil = 0
......@@ -260,9 +266,11 @@ class Scope(object):
self.obj_to_entry = {}
self.pystring_entries = []
self.buffer_entries = []
self.lambda_defs = []
self.control_flow = ControlFlow.LinearControlFlow()
self.return_type = None
self.id_counters = {}
def start_branching(self, pos):
self.control_flow = self.control_flow.start_branch(pos)
......@@ -290,7 +298,19 @@ class Scope(object):
prefix = "%s%s_" % (Naming.pyrex_prefix, name)
return self.mangle(prefix)
#return self.parent_scope.mangle(prefix, self.name)
def next_id(self, name=None):
# Return a cname fragment that is unique for this scope.
try:
count = self.id_counters[name] + 1
except KeyError:
count = 0
self.id_counters[name] = count
if name:
return '%s%d' % (name, count)
else:
return '%d' % count
def global_scope(self):
# Return the module-level scope containing this scope.
return self.outer_scope.global_scope()
......@@ -482,7 +502,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
self.control_flow.set_state((), (name, 'initalized'), False)
self.control_flow.set_state((), (name, 'initialized'), False)
return entry
def declare_builtin(self, name, pos):
......@@ -498,7 +518,20 @@ class Scope(object):
entry.signature = pyfunction_signature
self.pyfunc_entries.append(entry)
return entry
def declare_lambda_function(self, func_cname, pos):
# Add an entry for an anonymous Python function.
entry = self.declare_var(None, py_object_type, pos,
cname=func_cname, visibility='private')
entry.name = EncodedString(func_cname)
entry.func_cname = func_cname
entry.signature = pyfunction_signature
self.pyfunc_entries.append(entry)
return entry
def add_lambda_def(self, def_node):
self.lambda_defs.append(def_node)
def register_pyfunction(self, entry):
self.pyfunc_entries.append(entry)
......@@ -577,14 +610,7 @@ class Scope(object):
# Look up name in this scope or an enclosing one.
# Return None if not found.
return (self.lookup_here(name)
or (self.outer_scope and self.outer_scope.lookup_from_inner(name))
or None)
def lookup_from_inner(self, name):
# Look up name in this scope or an enclosing one.
# This is only called from enclosing scopes.
return (self.lookup_here(name)
or (self.outer_scope and self.outer_scope.lookup_from_inner(name))
or (self.outer_scope and self.outer_scope.lookup(name))
or None)
def lookup_here(self, name):
......@@ -1157,7 +1183,7 @@ class ModuleScope(Scope):
from TypeInference import PyObjectTypeInferer
PyObjectTypeInferer().infer_types(self)
class LocalScope(Scope):
class LocalScope(Scope):
def __init__(self, name, outer_scope, parent_scope = None):
if parent_scope is None:
......@@ -1202,31 +1228,94 @@ class LocalScope(Scope):
entry = self.global_scope().lookup_target(name)
self.entries[name] = entry
def lookup_from_inner(self, name):
entry = self.lookup_here(name)
if entry:
entry.in_closure = 1
return entry
else:
return (self.outer_scope and self.outer_scope.lookup_from_inner(name)) or None
def lookup(self, name):
# Look up name in this scope or an enclosing one.
# Return None if not found.
entry = Scope.lookup(self, name)
if entry is not None:
if entry.scope is not self and entry.scope.is_closure_scope:
# The actual c fragment for the different scopes differs
# on the outside and inside, so we make a new entry
entry.in_closure = True
# Would it be better to declare_var here?
inner_entry = Entry(entry.name, entry.cname, entry.type, entry.pos)
inner_entry.scope = self
inner_entry.is_variable = True
inner_entry.outer_entry = entry
inner_entry.from_closure = True
self.entries[name] = inner_entry
return inner_entry
return entry
def mangle_closure_cnames(self, scope_var):
def mangle_closure_cnames(self, outer_scope_cname):
for entry in self.entries.values():
if entry.in_closure:
if not hasattr(entry, 'orig_cname'):
entry.orig_cname = entry.cname
entry.cname = scope_var + "->" + entry.cname
if entry.from_closure:
cname = entry.outer_entry.cname
if cname.startswith(Naming.cur_scope_cname):
cname = cname[len(Naming.cur_scope_cname)+2:]
entry.cname = "%s->%s" % (outer_scope_cname, cname)
elif entry.in_closure:
entry.original_cname = entry.cname
entry.cname = "%s->%s" % (Naming.cur_scope_cname, entry.cname)
class GeneratorExpressionScope(LocalScope):
"""Scope for generator expressions and comprehensions. As opposed
to generators, these can be easily inlined in some cases, so all
we really need is a scope that holds the loop variable(s).
"""
def __init__(self, outer_scope):
name = outer_scope.global_scope().next_id(Naming.genexpr_id_ref)
LocalScope.__init__(self, name, outer_scope)
self.directives = outer_scope.directives
self.genexp_prefix = "%s%d%s" % (Naming.pyrex_prefix, len(name), name)
def mangle(self, prefix, name):
return '%s%s' % (self.genexp_prefix, self.outer_scope.mangle(self, prefix, name))
def declare_var(self, name, type, pos,
cname = None, visibility = 'private', 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)
if outer_entry and outer_entry.is_variable:
type = outer_entry.type # may still be 'unspecified_type' !
# the outer scope needs to generate code for the variable, but
# this scope must hold its name exclusively
cname = '%s%s' % (self.genexp_prefix, self.outer_scope.mangle(Naming.var_prefix, name))
entry = self.outer_scope.declare_var(None, type, pos, cname, visibility, is_cdef = True)
self.entries[name] = entry
return entry
class GeneratorLocalScope(LocalScope):
class ClosureScope(LocalScope):
def mangle_closure_cnames(self, scope_var):
is_closure_scope = True
def __init__(self, name, scope_name, outer_scope):
LocalScope.__init__(self, name, outer_scope)
self.closure_cname = "%s%s" % (Naming.closure_scope_prefix, scope_name)
# def mangle_closure_cnames(self, scope_var):
# for entry in self.entries.values() + self.temp_entries:
# entry.in_closure = 1
LocalScope.mangle_closure_cnames(self, scope_var)
# LocalScope.mangle_closure_cnames(self, scope_var)
# def mangle(self, prefix, name):
# return "%s->%s" % (Naming.scope_obj_cname, name)
# return "%s->%s" % (self.cur_scope_cname, name)
# return "%s->%s" % (self.closure_cname, name)
def declare_pyfunction(self, name, pos):
# Add an entry for a Python function.
entry = self.lookup_here(name)
if entry and not entry.type.is_cfunction:
# This is legal Python, but for now may produce invalid C.
error(pos, "'%s' already declared" % name)
entry = self.declare_var(name, py_object_type, pos)
entry.signature = pyfunction_signature
self.pyfunc_entries.append(entry)
return entry
class StructOrUnionScope(Scope):
# Namespace of a C struct or union.
......
......@@ -196,7 +196,7 @@ class MarkOverflowingArithmetic(CythonTransform):
self.visitchildren(node)
return node
class PyObjectTypeInferer:
class PyObjectTypeInferer(object):
"""
If it's not declared, it's a PyObject.
"""
......@@ -208,14 +208,14 @@ class PyObjectTypeInferer:
if entry.type is unspecified_type:
entry.type = py_object_type
class SimpleAssignmentTypeInferer:
class SimpleAssignmentTypeInferer(object):
"""
Very basic type inference.
"""
# TODO: Implement a real type inference algorithm.
# (Something more powerful than just extending this one...)
def infer_types(self, scope):
enabled = scope.directives['infer_types']
enabled = not scope.is_closure_scope and scope.directives['infer_types']
verbose = scope.directives['infer_types.verbose']
if enabled == True:
spanning_type = aggressive_spanning_type
......@@ -225,6 +225,8 @@ class SimpleAssignmentTypeInferer:
for entry in scope.entries.values():
if entry.type is unspecified_type:
entry.type = py_object_type
if scope.is_closure_scope:
fix_closure_entries(scope)
return
dependancies_by_entry = {} # entry -> dependancies
......@@ -286,6 +288,19 @@ class SimpleAssignmentTypeInferer:
entry.type = py_object_type
if verbose:
message(entry.pos, "inferred '%s' to be of type '%s' (default)" % (entry.name, entry.type))
#if scope.is_closure_scope:
# fix_closure_entries(scope)
def fix_closure_entries(scope):
"""Temporary work-around to fix field types in the closure class
that were unknown at the time of creation and only determined
during type inference.
"""
closure_entries = scope.scope_class.type.scope.entries
for name, entry in scope.entries.iteritems():
if name in closure_entries:
closure_entry = closure_entries[name]
closure_entry.type = entry.type
def find_spanning_type(type1, type2):
if type1 is type2:
......
......@@ -119,22 +119,36 @@ class ResultRefNode(AtomicExprNode):
subexprs = []
lhs_of_first_assignment = False
def __init__(self, expression):
self.pos = expression.pos
def __init__(self, expression=None, pos=None, type=None):
self.expression = expression
if hasattr(expression, "type"):
self.type = expression.type
self.pos = None
if expression is not None:
self.pos = expression.pos
if hasattr(expression, "type"):
self.type = expression.type
if pos is not None:
self.pos = pos
if type is not None:
self.type = type
assert self.pos is not None
def analyse_types(self, env):
self.type = self.expression.type
if self.expression is not None:
self.type = self.expression.type
def infer_type(self, env):
return self.expression.infer_type(env)
if self.expression is not None:
return self.expression.infer_type(env)
def is_simple(self):
return True
def result(self):
try:
return self.result_code
except AttributeError:
if self.expression is not None:
self.result_code = self.expression.result()
return self.result_code
def generate_evaluation_code(self, code):
......@@ -258,9 +272,6 @@ class TempResultFromStatNode(ExprNodes.ExprNode):
# body. Requires a ResultRefNode that it sets up to refer to its
# own temp result. The StatNode must assign a value to the result
# node, which then becomes the result of this node.
#
# This can only be used in/after type analysis.
#
subexprs = []
child_attrs = ['body']
......@@ -272,6 +283,12 @@ class TempResultFromStatNode(ExprNodes.ExprNode):
self.type = result_ref.type
self.is_temp = 1
def analyse_declarations(self, env):
self.body.analyse_declarations(env)
def analyse_types(self, env):
self.body.analyse_expressions(env)
def generate_result_code(self, code):
self.result_ref.result_code = self.result()
self.body.generate_execution_code(code)
......@@ -46,7 +46,7 @@ class BasicVisitor(object):
if self.access_path:
print self.access_path[-1][0].pos
print self.access_path[-1][0].__dict__
raise RuntimeError("Visitor does not accept object: %s" % obj)
raise RuntimeError("Visitor %r does not accept object: %s" % (self, obj))
#print "Caching " + cls.__name__
return handler_method
......@@ -260,7 +260,7 @@ class VisitorTransform(TreeVisitor):
class CythonTransform(VisitorTransform):
"""
Certain common conventions and utilitues for Cython transforms.
Certain common conventions and utilities for Cython transforms.
- Sets up the context of the pipeline in self.context
- Tracks directives in effect in self.current_directives
......@@ -352,7 +352,9 @@ class RecursiveNodeReplacer(VisitorTransform):
else:
return node
def recursively_replace_node(tree, old_node, new_node):
replace_in = RecursiveNodeReplacer(old_node, new_node)
replace_in(tree)
# Utils
......
......@@ -23,19 +23,19 @@ cdef extern from "Python.h":
bint PyBytes_CheckExact(object o)
# Return true if the object o is a string object, but not an instance of a subtype of the string type.
object PyBytes_FromString(char *v)
bytes PyBytes_FromString(char *v)
# Return value: New reference.
# Return a new string object with the value v on success, and NULL
# on failure. The parameter v must not be NULL; it will not be
# checked.
object PyBytes_FromStringAndSize(char *v, Py_ssize_t len)
bytes PyBytes_FromStringAndSize(char *v, Py_ssize_t len)
# Return value: New reference.
# Return a new string object with the value v and length len on
# success, and NULL on failure. If v is NULL, the contents of the
# string are uninitialized.
object PyBytes_FromFormat(char *format, ...)
bytes PyBytes_FromFormat(char *format, ...)
# Return value: New reference.
# Take a C printf()-style format string and a variable number of
# arguments, calculate the size of the resulting Python string and
......@@ -64,7 +64,7 @@ cdef extern from "Python.h":
# format string to be copied as-is to the result string, and any
# extra arguments discarded.
object PyBytes_FromFormatV(char *format, va_list vargs)
bytes PyBytes_FromFormatV(char *format, va_list vargs)
# Return value: New reference.
# Identical to PyBytes_FromFormat() except that it takes exactly two arguments.
......@@ -134,7 +134,7 @@ cdef extern from "Python.h":
# *string is set to NULL, a memory exception is set, and -1 is
# returned.
object PyBytes_Format(object format, object args)
bytes PyBytes_Format(object format, object args)
# Return value: New reference. Return a new string object from
# format and args. Analogous to format % args. The args argument
# must be a tuple.
......@@ -152,7 +152,7 @@ cdef extern from "Python.h":
# reference-count-neutral; you own the object after the call if
# and only if you owned it before the call.)
object PyBytes_InternFromString(char *v)
bytes PyBytes_InternFromString(char *v)
# Return value: New reference.
# A combination of PyBytes_FromString() and
# PyBytes_InternInPlace(), returning either a new string object
......
......@@ -188,16 +188,31 @@ def open_source_from_loader(loader,
stream = NormalisedNewlineStream(stream)
return stream
def long_literal(value):
if isinstance(value, basestring):
if len(value) < 2:
value = int(value)
elif value[0] == 0:
value = int(value, 8)
elif value[1] in 'xX':
def str_to_number(value):
# note: this expects a string as input that was accepted by the
# parser already
if len(value) < 2:
value = int(value, 0)
elif value[0] == '0':
if value[1] in 'xX':
# hex notation ('0x1AF')
value = int(value[2:], 16)
elif value[1] in 'oO':
# Py3 octal notation ('0o136')
value = int(value[2:], 8)
elif value[1] in 'bB':
# Py3 binary notation ('0b101')
value = int(value[2:], 2)
else:
value = int(value)
# Py2 octal notation ('0136')
value = int(value, 8)
else:
value = int(value, 0)
return value
def long_literal(value):
if isinstance(value, basestring):
value = str_to_number(value)
return not -2**31 <= value < 2**31
def none_or_sub(s, data):
......
......@@ -51,6 +51,9 @@ EXT_DEP_INCLUDES = [
VER_DEP_MODULES = {
# tests are excluded if 'CurrentPythonVersion OP VersionTuple', i.e.
# (2,4) : (operator.le, ...) excludes ... when PyVer <= 2.4.x
(2,5) : (operator.lt, lambda x: x in ['run.any',
'run.all',
]),
(2,4) : (operator.le, lambda x: x in ['run.extern_builtins_T258'
]),
(2,6) : (operator.lt, lambda x: x in ['run.print_function',
......@@ -786,7 +789,6 @@ if __name__ == '__main__':
if sys.version_info[0] >= 3:
options.doctests = False
options.pyregr = False
if options.with_cython:
try:
# try if Cython is installed in a Py3 version
......
......@@ -11,8 +11,10 @@ cascaded_list_unpacking_T467
compile.cpp_operators
cpp_templated_ctypedef
cpp_structs
genexpr_T491
with_statement_module_level_T536
function_as_method_T494
closure_inside_cdef_T554
# CPython regression tests that don't current work:
pyregr.test_threadsignals
......
if x:
def h():
pass
_ERRORS = u"""
2:1: def statement not allowed here
"""
def f():
def g():
pass
_ERRORS = u"""
2:1: def statement not allowed here
"""
cdef class VerboseGetItem(object):
cdef object sequence
def __init__(self, seq):
self.sequence = seq
def __getitem__(self, i):
print i
return self.sequence[i] # may raise IndexError
cimport cython
@cython.test_assert_path_exists("//SimpleCallNode")
@cython.test_fail_if_path_exists("//ForInStatNode")
def all_item(x):
"""
>>> all_item([1,1,1,1,1])
True
>>> all_item([1,1,1,1,0])
False
>>> all_item([0,1,1,1,0])
False
>>> all(VerboseGetItem([1,1,1,0,0]))
0
1
2
3
False
>>> all_item(VerboseGetItem([1,1,1,0,0]))
0
1
2
3
False
>>> all(VerboseGetItem([1,1,1,1,1]))
0
1
2
3
4
5
True
>>> all_item(VerboseGetItem([1,1,1,1,1]))
0
1
2
3
4
5
True
"""
return all(x)
@cython.test_assert_path_exists("//ForInStatNode",
"//InlinedGeneratorExpressionNode")
@cython.test_fail_if_path_exists("//SimpleCallNode",
"//YieldExprNode")
def all_in_simple_gen(seq):
"""
>>> all_in_simple_gen([1,1,1])
True
>>> all_in_simple_gen([1,1,0])
False
>>> all_in_simple_gen([1,0,1])
False
>>> all_in_simple_gen(VerboseGetItem([1,1,1,1,1]))
0
1
2
3
4
5
True
>>> all_in_simple_gen(VerboseGetItem([1,1,0,1,1]))
0
1
2
False
"""
return all(x for x in seq)
@cython.test_assert_path_exists("//ForInStatNode",
"//InlinedGeneratorExpressionNode")
@cython.test_fail_if_path_exists("//SimpleCallNode",
"//YieldExprNode")
def all_in_simple_gen_scope(seq):
"""
>>> all_in_simple_gen_scope([1,1,1])
True
>>> all_in_simple_gen_scope([1,1,0])
False
>>> all_in_simple_gen_scope([1,0,1])
False
>>> all_in_simple_gen_scope(VerboseGetItem([1,1,1,1,1]))
0
1
2
3
4
5
True
>>> all_in_simple_gen_scope(VerboseGetItem([1,1,0,1,1]))
0
1
2
False
"""
x = 'abc'
result = all(x for x in seq)
assert x == 'abc'
return result
@cython.test_assert_path_exists("//ForInStatNode",
"//InlinedGeneratorExpressionNode")
@cython.test_fail_if_path_exists("//SimpleCallNode",
"//YieldExprNode")
def all_in_conditional_gen(seq):
"""
>>> all_in_conditional_gen([3,6,9])
False
>>> all_in_conditional_gen([0,3,7])
False
>>> all_in_conditional_gen([1,0,1])
True
>>> all_in_conditional_gen(VerboseGetItem([1,1,1,1,1]))
0
1
2
3
4
5
True
>>> all_in_conditional_gen(VerboseGetItem([1,1,0,1,1]))
0
1
2
3
4
5
True
"""
return all(x%3 for x in seq if x%2 == 1)
mixed_ustring = u'AbcDefGhIjKlmnoP'
lower_ustring = mixed_ustring.lower()
upper_ustring = mixed_ustring.upper()
@cython.test_assert_path_exists('//PythonCapiCallNode',
'//ForFromStatNode')
@cython.test_fail_if_path_exists('//SimpleCallNode',
'//ForInStatNode')
def all_lower_case_characters(unicode ustring):
"""
>>> all_lower_case_characters(mixed_ustring)
False
>>> all_lower_case_characters(upper_ustring)
False
>>> all_lower_case_characters(lower_ustring)
True
"""
return all(uchar.islower() for uchar in ustring)
@cython.test_assert_path_exists("//ForInStatNode",
"//InlinedGeneratorExpressionNode",
"//InlinedGeneratorExpressionNode//IfStatNode")
@cython.test_fail_if_path_exists("//SimpleCallNode",
"//YieldExprNode",
"//IfStatNode//CoerceToBooleanNode")
def all_in_typed_gen(seq):
"""
>>> all_in_typed_gen([1,1,1])
True
>>> all_in_typed_gen([1,0,0])
False
>>> all_in_typed_gen(VerboseGetItem([1,1,1,1,1]))
0
1
2
3
4
5
True
>>> all_in_typed_gen(VerboseGetItem([1,1,1,1,0]))
0
1
2
3
4
False
"""
cdef int x
return all(x for x in seq)
@cython.test_assert_path_exists("//ForInStatNode",
"//InlinedGeneratorExpressionNode",
"//InlinedGeneratorExpressionNode//IfStatNode")
@cython.test_fail_if_path_exists("//SimpleCallNode",
"//YieldExprNode",
"//IfStatNode//CoerceToBooleanNode")
def all_in_double_gen(seq):
"""
>>> all(x for L in [[1,1,1],[1,1,1],[1,1,1]] for x in L)
True
>>> all_in_double_gen([[1,1,1],[1,1,1],[1,1,1]])
True
>>> all(x for L in [[1,1,1],[1,1,1],[1,1,0]] for x in L)
False
>>> all_in_double_gen([[1,1,1],[1,1,1],[1,1,0]])
False
>>> all(x for L in [[1,1,1],[0,1,1],[1,1,1]] for x in L)
False
>>> all_in_double_gen([[1,1,1],[0,1,1],[1,1,1]])
False
>>> all_in_double_gen([VerboseGetItem([1,1,1]), VerboseGetItem([1,1,1,1,1])])
0
1
2
3
0
1
2
3
4
5
True
>>> all_in_double_gen([VerboseGetItem([1,1,1]),VerboseGetItem([1,1]),VerboseGetItem([1,1,0])])
0
1
2
3
0
1
2
0
1
2
False
"""
cdef int x
return all(x for L in seq for x in L)
cdef class VerboseGetItem(object):
cdef object sequence
def __init__(self, seq):
self.sequence = seq
def __getitem__(self, i):
print i
return self.sequence[i] # may raise IndexError
cimport cython
@cython.test_assert_path_exists("//SimpleCallNode")
@cython.test_fail_if_path_exists("//ForInStatNode")
def any_item(x):
"""
>>> any_item([0,0,1,0,0])
True
>>> any_item([0,0,0,0,1])
True
>>> any_item([0,0,0,0,0])
False
>>> any(VerboseGetItem([0,0,1,0,0]))
0
1
2
True
>>> any_item(VerboseGetItem([0,0,1,0,0]))
0
1
2
True
>>> any(VerboseGetItem([0,0,0,0,0]))
0
1
2
3
4
5
False
>>> any_item(VerboseGetItem([0,0,0,0,0]))
0
1
2
3
4
5
False
"""
return any(x)
@cython.test_assert_path_exists("//ForInStatNode",
"//InlinedGeneratorExpressionNode")
@cython.test_fail_if_path_exists("//SimpleCallNode",
"//YieldExprNode")
def any_in_simple_gen(seq):
"""
>>> any_in_simple_gen([0,1,0])
True
>>> any_in_simple_gen([0,0,0])
False
>>> any_in_simple_gen(VerboseGetItem([0,0,1,0,0]))
0
1
2
True
>>> any_in_simple_gen(VerboseGetItem([0,0,0,0,0]))
0
1
2
3
4
5
False
"""
return any(x for x in seq)
@cython.test_assert_path_exists("//ForInStatNode",
"//InlinedGeneratorExpressionNode")
@cython.test_fail_if_path_exists("//SimpleCallNode",
"//YieldExprNode")
def any_in_simple_gen_scope(seq):
"""
>>> any_in_simple_gen_scope([0,1,0])
True
>>> any_in_simple_gen_scope([0,0,0])
False
>>> any_in_simple_gen_scope(VerboseGetItem([0,0,1,0,0]))
0
1
2
True
>>> any_in_simple_gen_scope(VerboseGetItem([0,0,0,0,0]))
0
1
2
3
4
5
False
"""
x = 'abc'
result = any(x for x in seq)
assert x == 'abc'
return result
@cython.test_assert_path_exists("//ForInStatNode",
"//InlinedGeneratorExpressionNode")
@cython.test_fail_if_path_exists("//SimpleCallNode",
"//YieldExprNode")
def any_in_conditional_gen(seq):
"""
>>> any_in_conditional_gen([3,6,9])
False
>>> any_in_conditional_gen([0,3,7])
True
>>> any_in_conditional_gen([1,0,1])
True
>>> any_in_conditional_gen(VerboseGetItem([0,0,3,0,0]))
0
1
2
3
4
5
False
>>> any_in_conditional_gen(VerboseGetItem([0,3,0,1,1]))
0
1
2
3
True
"""
return any(x%3 for x in seq if x%2 == 1)
mixed_ustring = u'AbcDefGhIjKlmnoP'
lower_ustring = mixed_ustring.lower()
upper_ustring = mixed_ustring.upper()
@cython.test_assert_path_exists('//PythonCapiCallNode',
'//ForFromStatNode',
"//InlinedGeneratorExpressionNode")
@cython.test_fail_if_path_exists('//SimpleCallNode',
'//ForInStatNode')
def any_lower_case_characters(unicode ustring):
"""
>>> any_lower_case_characters(upper_ustring)
False
>>> any_lower_case_characters(mixed_ustring)
True
>>> any_lower_case_characters(lower_ustring)
True
"""
return any(uchar.islower() for uchar in ustring)
@cython.test_assert_path_exists("//ForInStatNode",
"//InlinedGeneratorExpressionNode",
"//InlinedGeneratorExpressionNode//IfStatNode")
@cython.test_fail_if_path_exists("//SimpleCallNode",
"//YieldExprNode",
"//IfStatNode//CoerceToBooleanNode")
def any_in_typed_gen(seq):
"""
>>> any_in_typed_gen([0,1,0])
True
>>> any_in_typed_gen([0,0,0])
False
>>> any_in_typed_gen(VerboseGetItem([0,0,1,0,0]))
0
1
2
True
>>> any_in_typed_gen(VerboseGetItem([0,0,0,0,0]))
0
1
2
3
4
5
False
"""
cdef int x
return any(x for x in seq)
@cython.test_assert_path_exists("//ForInStatNode",
"//InlinedGeneratorExpressionNode",
"//InlinedGeneratorExpressionNode//IfStatNode")
@cython.test_fail_if_path_exists("//SimpleCallNode",
"//YieldExprNode")
def any_in_gen_builtin_name(seq):
"""
>>> any_in_gen_builtin_name([0,1,0])
True
>>> any_in_gen_builtin_name([0,0,0])
False
>>> any_in_gen_builtin_name(VerboseGetItem([0,0,1,0,0]))
0
1
2
True
>>> any_in_gen_builtin_name(VerboseGetItem([0,0,0,0,0]))
0
1
2
3
4
5
False
"""
return any(type for type in seq)
@cython.test_assert_path_exists("//ForInStatNode",
"//InlinedGeneratorExpressionNode",
"//InlinedGeneratorExpressionNode//IfStatNode")
@cython.test_fail_if_path_exists("//SimpleCallNode",
"//YieldExprNode",
"//IfStatNode//CoerceToBooleanNode")
def any_in_double_gen(seq):
"""
>>> any(x for L in [[0,0,0],[0,0,1],[0,0,0]] for x in L)
True
>>> any_in_double_gen([[0,0,0],[0,0,1],[0,0,0]])
True
>>> any(x for L in [[0,0,0],[0,0,0],[0,0,0]] for x in L)
False
>>> any_in_double_gen([[0,0,0],[0,0,0],[0,0,0]])
False
>>> any_in_double_gen([VerboseGetItem([0,0,0]), VerboseGetItem([0,0,1,0,0])])
0
1
2
3
0
1
2
True
>>> any_in_double_gen([VerboseGetItem([0,0,0]),VerboseGetItem([0,0]),VerboseGetItem([0,0,0])])
0
1
2
3
0
1
2
0
1
2
3
False
"""
cdef int x
return any(x for L in seq for x in L)
def f(dict d, x=4):
"""
>>> f({1:1, 2:2})
[1, 2]
"""
cdef dict d_new = {}
l = []
for k in d:
d = d_new
l.append(k)
l.sort()
return l
cimport cython
# min()
@cython.test_assert_path_exists("//CondExprNode")
@cython.test_fail_if_path_exists("//SimpleCallNode")
def min3(a,b,c):
"""
>>> min3(1,2,3)
1
>>> min3(2,3,1)
1
>>> min3(2,1,3)
1
>>> min3(3,1,2)
1
>>> min3(3,2,1)
1
"""
return min(a,b,c)
@cython.test_assert_path_exists("//CondExprNode")
@cython.test_fail_if_path_exists("//SimpleCallNode")
def min3_typed(int a, int b, int c):
"""
>>> min3_typed(1,2,3)
1
>>> min3_typed(2,3,1)
1
>>> min3_typed(2,1,3)
1
>>> min3_typed(3,1,2)
1
>>> min3_typed(3,2,1)
1
"""
return min(a,b,c)
@cython.test_assert_path_exists("//CondExprNode")
@cython.test_fail_if_path_exists("//SimpleCallNode")
def literal_min3():
"""
>>> literal_min3()
(1, 1, 1, 1, 1)
"""
return min(1,2,3), min(2,1,3), min(2,3,1), min(3,1,2), min(3,2,1)
# max()
@cython.test_assert_path_exists("//CondExprNode")
@cython.test_fail_if_path_exists("//SimpleCallNode")
def max3(a,b,c):
"""
>>> max3(1,2,3)
3
>>> max3(2,3,1)
3
>>> max3(2,1,3)
3
>>> max3(3,1,2)
3
>>> max3(3,2,1)
3
"""
return max(a,b,c)
@cython.test_assert_path_exists("//CondExprNode")
@cython.test_fail_if_path_exists("//SimpleCallNode")
def max3_typed(int a, int b, int c):
"""
>>> max3_typed(1,2,3)
3
>>> max3_typed(2,3,1)
3
>>> max3_typed(2,1,3)
3
>>> max3_typed(3,1,2)
3
>>> max3_typed(3,2,1)
3
"""
return max(a,b,c)
@cython.test_assert_path_exists("//CondExprNode")
@cython.test_fail_if_path_exists("//SimpleCallNode")
def literal_max3():
"""
>>> literal_max3()
(3, 3, 3, 3, 3)
"""
return max(1,2,3), max(2,1,3), max(2,3,1), max(3,1,2), max(3,2,1)
# The arguments in f() are put into the closure one after the other,
# so the reference of 'o' is filled in before the type errors are
# found. This leaves a reference in the closure instance on error
# return, which must be properly ref-counted to facilitate generic
# closure deallocation. In the case of an argument type error, it's
# actually best to just Py_CLEAR() the already handled references, as
# this frees them as early as possible.
# This test doesn't really check the ref-counting itself, it just
# reproduces the problem.
def func_with_typed_args(object o, int i, tuple t, double d):
"""
>>> g = func_with_typed_args(1, 2, (), 3.0)
>>> g()
(1, 2, (), 3.0)
>>> g = func_with_typed_args(1, 'x', (), 3.0)
Traceback (most recent call last):
TypeError: an integer is required
>>> g = func_with_typed_args(1, 2, 3, 3.0)
Traceback (most recent call last):
TypeError: Argument 't' has incorrect type (expected tuple, got int)
"""
def g():
return o, i, t, d
return g
__doc__ = """
>>> Num(13).is_prime()
args (Num(13),) kwds {}
True
>>> Num(13).is_prime(True)
args (Num(13), True) kwds {}
True
>>> Num(15).is_prime(print_factors=True)
args (Num(15),) kwds {'print_factors': True}
3 5
False
"""
def print_args(func):
def f(*args, **kwds):
print "args", args, "kwds", kwds
return func(*args, **kwds)
return f
cdef class Num:
cdef int n
def __init__(self, n):
self.n = n
def __repr__(self):
return "Num(%s)" % self.n
@print_args
def is_prime(self, bint print_factors=False):
if self.n == 2:
return True
elif self.n < 2:
return False
elif self.n % 2 == 0:
if print_factors:
print 2, self.n // 2
cdef int i = 3
while i*i <= self.n:
if self.n % i == 0:
if print_factors:
print i, self.n // i
return False
i += 2
return True
def call_f(x):
"""
>>> call_f(2)
4
"""
return f(x)
cdef f(x): # def here => works fine
def g(y): return y*x # cdef here => compile error
return g(x) # faults@ INCREF(.*cur_scope->.*v_x
#
# closure_tests_1.pyx
#
# Battery of tests for closures in Cython. Based on the collection of
# compiler tests from P423/B629 at Indiana University, Spring 1999 and
# Fall 2000. Special thanks to R. Kent Dybvig, Dan Friedman, Kevin
# Millikin, and everyone else who helped to generate the original
# tests. Converted into a collection of Python/Cython tests by Craig
# Citro.
#
# Note: This set of tests is split (somewhat randomly) into several
# files, simply because putting all the tests in a single file causes
# gcc and g++ to buckle under the load.
#
def g1425():
"""
>>> g1425()
142
"""
if (True):
def g1424():
if (True):
return 122
return (20)+(g1424())
else:
return 10000
def g1432():
"""
>>> g1432()
[0, []]
"""
def g1431():
return [0,[]]
x_1056 = g1431()
if (x_1056):
def g1430():
def g1429():
return (x_1056[0])
def g1428():
return (x_1056[0])
return (g1429())+(g1428())
x_1056[0] = g1430()
return x_1056
def g1435():
"""
>>> g1435()
4000
"""
def g1434():
def g1433(y_1057):
return y_1057
return g1433
return g1434()(4000)
def g1438():
"""
>>> g1438()
1
"""
def g1437():
def g1436(x_1058):
return x_1058
return g1436
f_1059 = g1437()
return (f_1059(0)+1)
def g1441():
"""
>>> g1441()
4
"""
def g1440():
def g1439(y_1060):
return y_1060
return g1439
f_1061 = g1440()
return f_1061(f_1061(4))
def g1446():
"""
>>> g1446()
4
"""
def g1445():
def g1444(f_1063):
return f_1063(f_1063(4))
return g1444
def g1443():
def g1442(y_1062):
return y_1062
return g1442
return g1445()(g1443())
def g1449():
"""
>>> g1449()
9000
"""
def g1448():
a_1064 = 4000
def g1447(b_1065):
return (a_1064)+(b_1065)
return g1447
return g1448()(5000)
def g1454():
"""
>>> g1454()
9000
"""
def g1453():
def g1452():
def g1450(a_1066):
def g1451(b_1067):
return (a_1066)+(b_1067)
return g1451
return g1450
return g1452()(4000)
return g1453()(5000)
def g1459():
"""
>>> g1459()
2
"""
def g1458():
def g1457(f_1069):
return f_1069(f_1069(0))
return g1457
def g1456():
def g1455(x_1068):
return (x_1068+1)
return g1455
return g1458()(g1456())
def g1462():
"""
>>> g1462()
0
"""
x_1072 = 0
def g1461():
def g1460(x_1070):
return x_1070
return g1460
f_1071 = g1461()
a_1075 = f_1071(x_1072)
b_1074 = f_1071(x_1072)
c_1073 = f_1071(x_1072)
return ((a_1075)+(b_1074))+(c_1073)
def g1465():
"""
>>> g1465()
3
"""
x_1080 = 0
y_1079 = 1
z_1078 = 2
def g1464():
def g1463(x_1076):
return x_1076
return g1463
f_1077 = g1464()
a_1083 = f_1077(x_1080)
b_1082 = f_1077(y_1079)
c_1081 = f_1077(z_1078)
return ((a_1083)+(b_1082))+(c_1081)
def g1468():
"""
>>> g1468()
0
"""
def g1467():
def g1466(x_1085, y_1084):
return x_1085
return g1466
f_1086 = g1467()
a_1087 = f_1086(0, 1)
return f_1086(a_1087, a_1087)
def g1471():
"""
>>> g1471()
0
"""
x_1094 = 0
y_1093 = 1
z_1092 = 2
def g1470():
def g1469(x_1090, y_1089, z_1088):
return x_1090
return g1469
f_1091 = g1470()
a_1097 = f_1091(x_1094, y_1093, z_1092)
b_1096 = y_1093
c_1095 = z_1092
return f_1091(a_1097, b_1096, c_1095)
def g1474():
"""
>>> g1474()
3
"""
def g1473():
def g1472(a_1101, b_1100, c_1099, d_1098):
return (a_1101)+(d_1098)
return g1472
f_1102 = g1473()
return f_1102(0, 1, 2, 3)
def g1478():
"""
>>> g1478()
3
"""
def g1477():
def g1476(x_1103):
return x_1103
return g1476
f_1104 = g1477()
def g1475():
a_1107 = 0
b_1106 = 1
c_1105 = 2
return (f_1104(a_1107))+((f_1104(b_1106))+(f_1104(c_1105)))
return (f_1104(0))+(g1475())
def g1483():
"""
>>> g1483()
"""
a_1108 = 0
def g1482():
def g1481():
return 0
return g1481
a_1110 = g1482()
def g1480():
def g1479():
return 11
return g1479
b_1109 = g1480()
a_1110 = 11
def g1486():
"""
>>> g1486()
"""
a_1111 = 0
def g1485():
def g1484():
a_1113 = 0
return g1484
a_1113 = g1485()
b_1112 = 11
return a_1113()
def g1491():
"""
>>> g1491()
0
"""
def g1490():
def g1489():
return 0
return g1489
a_1115 = g1490()
def g1488():
def g1487():
return 11
return g1487
b_1114 = g1488()
return a_1115()
def g1494():
"""
>>> g1494()
2
"""
def g1493():
x_1116 = 1
def g1492(y_1117):
return (x_1116)+(y_1117)
return g1492
f_1118 = g1493()
x_1119 = 0
return f_1118(f_1118(x_1119))
def g1501():
"""
>>> g1501()
3050
"""
def g1500():
def g1499():
def g1498(x_1121):
return (x_1121)+(50)
return g1498
t_1122 = g1499()
def g1497(f_1123):
return t_1122(f_1123(1000))
return g1497
def g1496():
def g1495(y_1120):
return (y_1120)+(2000)
return g1495
return g1500()(g1496())
def g1508():
"""
>>> g1508()
60
"""
def g1507():
def g1506():
def g1505():
def g1502(a_1124):
def g1503(b_1125):
def g1504(c_1126):
return (a_1124)+((b_1125)+(c_1126))
return g1504
return g1503
return g1502
return g1505()(10)
return g1506()(20)
return g1507()(30)
def g1513():
"""
>>> g1513()
5
"""
def g1512():
def g1509(b_1127):
def g1511():
def g1510(a_1128):
return (b_1127)+(a_1128)
return g1510
return g1511()(2)
return g1509
return g1512()(3)
def g1518():
"""
>>> g1518()
5
"""
def g1517():
def g1516(f_1130):
return f_1130(f_1130(5))
return g1516
def g1515():
def g1514(x_1129):
return x_1129
return g1514
return g1517()(g1515())
def g1523():
"""
>>> g1523()
8000
"""
def g1522():
def g1521():
def g1520(x_1131):
return (x_1131)+(3000)
return g1520
f_1132 = g1521()
def g1519(y_1133):
return f_1132(f_1132(y_1133))
return g1519
return g1522()(2000)
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
__doc__ = u"""
>>> f = add_n(3)
>>> f(2)
5
>>> f = add_n(1000000)
>>> f(1000000), f(-1000000)
(2000000, 0)
>>> a(5)()
8
>>> local_x(1)(2)(4)
4 2 1
15
# this currently crashes Cython due to redefinition
#>>> x(1)(2)(4)
#15
>>> x2(1)(2)(4)
4 2 1
15
>>> inner_override(2,4)()
5
>>> reassign(4)(2)
3
>>> reassign_int(4)(2)
3
>>> reassign_int_int(4)(2)
3
>>> def py_twofuncs(x):
... def f(a):
... return g(x) + a
... def g(b):
... return x + b
... return f
>>> py_twofuncs(1)(2) == cy_twofuncs(1)(2)
True
>>> py_twofuncs(3)(5) == cy_twofuncs(3)(5)
True
>>> inner_funcs = more_inner_funcs(1)(2,4,8)
>>> inner_funcs[0](16), inner_funcs[1](32), inner_funcs[2](64)
(19, 37, 73)
>>> switch_funcs([1,2,3], [4,5,6], 0)([10])
[1, 2, 3, 10]
>>> switch_funcs([1,2,3], [4,5,6], 1)([10])
[4, 5, 6, 10]
>>> switch_funcs([1,2,3], [4,5,6], 2) is None
True
>>> call_ignore_func()
"""
def add_n(int n):
def f(int x):
return x+n
return f
def a(int x):
def b():
def c():
return 3+x
return c()
return b
def local_x(int arg_x):
cdef int local_x = arg_x
def y(arg_y):
y = arg_y
def z(long arg_z):
cdef long z = arg_z
print z, y, local_x
return 8+z+y+local_x
return z
return y
# currently crashes Cython due to name redefinitions (see local_x())
## def x(int x):
## def y(y):
## def z(long z):
## return 8+z+y+x
## return z
## return y
def x2(int x2):
def y2(y2):
def z2(long z2):
print z2, y2, x2
return 8+z2+y2+x2
return z2
return y2
def inner_override(a,b):
def f():
a = 1
return a+b
return f
def reassign(x):
def f(a):
return a+x
x = 1
return f
def reassign_int(x):
def f(int a):
return a+x
x = 1
return f
def reassign_int_int(int x):
def f(int a):
return a+x
x = 1
return f
def cy_twofuncs(x):
def f(a):
return g(x) + a
def g(b):
return x + b
return f
def switch_funcs(a, b, int ix):
def f(x):
return a + x
def g(x):
return b + x
if ix == 0:
return f
elif ix == 1:
return g
else:
return None
def ignore_func(x):
def f():
return x
return None
def call_ignore_func():
ignore_func((1,2,3))
def more_inner_funcs(x):
# called with x==1
def f(a):
def g(b):
# called with 16
return a+b+x
return g
def g(b):
def f(a):
# called with 32
return a+b+x
return f
def h(b):
def f(a):
# called with 64
return a+b+x
return f
def resolve(a_f, b_g, b_h):
# called with (2,4,8)
return f(a_f), g(b_g), h(b_h)
return resolve
# cython: language_level=3
try:
sorted
except NameError:
def sorted(seq):
seq = list(seq)
seq.sort()
return seq
def print_function(*args):
"""
>>> print_function(1,2,3)
......@@ -17,3 +25,33 @@ def unicode_literals():
"""
print(isinstance(ustring, unicode) or type(ustring))
return ustring
def list_comp():
"""
>>> list_comp()
[0, 4, 8]
"""
x = 'abc'
result = [x*2 for x in range(5) if x % 2 == 0]
assert x == 'abc' # don't leak in Py3 code
return result
def set_comp():
"""
>>> sorted(set_comp())
[0, 4, 8]
"""
x = 'abc'
result = {x*2 for x in range(5) if x % 2 == 0}
assert x == 'abc' # don't leak
return result
def dict_comp():
"""
>>> sorted(dict_comp().items())
[(0, 0), (2, 4), (4, 8)]
"""
x = 'abc'
result = {x:x*2 for x in range(5) if x % 2 == 0}
assert x == 'abc' # don't leak
return result
__doc__ = u"""
>>> type(smoketest_dict()) is dict
True
>>> type(smoketest_list()) is dict
True
>>> sorted(smoketest_dict().items())
[(2, 0), (4, 4), (6, 8)]
>>> sorted(smoketest_list().items())
[(2, 0), (4, 4), (6, 8)]
>>> list(typed().items())
[(A, 1), (A, 1), (A, 1)]
>>> sorted(iterdict().items())
[(1, 'a'), (2, 'b'), (3, 'c')]
"""
cimport cython
def smoketest_dict():
return { x+2:x*2
for x in range(5)
if x % 2 == 0 }
def dictcomp():
"""
>>> sorted(dictcomp().items())
[(2, 0), (4, 4), (6, 8)]
>>> sorted(dictcomp().items())
[(2, 0), (4, 4), (6, 8)]
"""
x = 'abc'
result = { x+2:x*2
for x in range(5)
if x % 2 == 0 }
assert x == 'abc' # do not leak!
return result
@cython.test_fail_if_path_exists(
"//ComprehensionNode//ComprehensionAppendNode",
"//SimpleCallNode//ComprehensionNode")
"//GeneratorExpressionNode",
"//SimpleCallNode")
@cython.test_assert_path_exists(
"//ComprehensionNode",
"//ComprehensionNode//DictComprehensionAppendNode")
def smoketest_list():
return dict([ (x+2,x*2)
for x in range(5)
if x % 2 == 0 ])
def genexpr():
"""
>>> type(genexpr()) is dict
True
>>> type(genexpr()) is dict
True
"""
x = 'abc'
result = dict( (x+2,x*2)
for x in range(5)
if x % 2 == 0 )
assert x == 'abc'
return result
cdef class A:
def __repr__(self): return u"A"
def __richcmp__(one, other, op): return one is other
def __hash__(self): return id(self) % 65536
def typed():
def typed_dictcomp():
"""
>>> list(typed_dictcomp().items())
[(A, 1), (A, 1), (A, 1)]
"""
cdef A obj
return {obj:1 for obj in [A(), A(), A()]}
def iterdict():
def iterdict_dictcomp():
"""
>>> sorted(iterdict_dictcomp().items())
[(1, 'a'), (2, 'b'), (3, 'c')]
"""
cdef dict d = dict(a=1,b=2,c=3)
return {d[key]:key for key in d}
......
cimport cython
class SomeNumber(object):
def __init__(self, n):
self._n = n
def __repr__(self):
return "SomeNumber(%s)" % self._n
@cython.binding(True)
def add_to_func(self, x):
"""
>>> add_to_func(SomeNumber(2), 5)
7
>>> SomeNumber(3).add_to(10)
13
>>> SomeNumber.add_to(SomeNumber(22), 7)
29
"""
return self._n + x
@cython.binding(False)
def new_num(n):
"""
>>> new_num(11)
SomeNumber(11)
>>> SomeNumber.new(11)
SomeNumber(11)
>>> SomeNumber(3).new(11)
SomeNumber(11)
"""
return SomeNumber(n)
SomeNumber.add_to = add_to_func
SomeNumber.new = new_num
def test_genexpr():
"""
>>> gen = test_genexpr()
>>> list(gen)
[0, 1, 2, 3, 4]
"""
return (i for i in range(5))
def test_genexpr_typed():
"""
>>> gen = test_genexpr_typed()
>>> list(gen)
[0, 1, 2, 3, 4]
"""
cdef int i
return (i for i in range(5))
def test_genexpr_funccall():
"""
>>> test_genexpr_funccall()
[0, 1, 2, 3, 4]
"""
return list(i for i in range(5))
def test_genexpr_scope():
"""
>>> test_genexpr_scope()
([0, 1, 2, 3, 4], 'abc')
"""
i = 'abc'
gen = (i for i in range(5))
lst = list(gen)
return lst, i
def test_genexpr_closure():
"""
>>> gen = test_genexpr_closure()
>>> list(gen)
['a', 'b', 'c']
"""
abc = 'a' + 'b' + 'c'
return (c for c in abc)
This diff is collapsed.
This diff is collapsed.
#
# Cython version of Knuth's "man or boy" test -- "It separates the man
# Algol 60 compilers from the boy Algol 60 compilers." Here's the
# original (from wikipedia):
#
# begin
# real procedure A (k, x1, x2, x3, x4, x5);
# value k; integer k;
# begin
# real procedure B;
# begin k:= k - 1;
# B:= A := A (k, B, x1, x2, x3, x4);
# end;
# if k <= 0 then A:= x4 + x5 else B;
# end;
# outreal (A (10, 1, -1, -1, 1, 0));
# end;
#
# and a table of values:
#
# k A
# 0 1
# 1 0
# 2 -2
# 3 0
# 4 1
# 5 0
# 6 1
# 7 -1
# 8 -10
# 9 -30
# 10 -67
#
# Past 10 or so, we blow the C stack -- can't just set a higher recursion limit
# to get around that one.
#
def compute(val):
if isinstance(val, int):
return val
else:
return val()
def a(in_k, x1, x2, x3, x4, x5):
"""
>>> import sys
>>> sys.setrecursionlimit(1350)
>>> a(10, 1, -1, -1, 1, 0)
-67
"""
k = [in_k]
def b():
k[0] -= 1
return a(k[0], b, x1, x2, x3, x4)
return compute(x4) + compute(x5) if k[0] <= 0 else b()
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -20,7 +20,8 @@ def get_locals_items(x, *args, **kwds):
return locals().items()
def get_locals_items_listcomp(x, *args, **kwds):
# FIXME: 'item' should *not* appear in locals() !
# FIXME: 'item' should *not* appear in locals() yet, as locals()
# is evaluated before assigning to item !
cdef int z = 5
y = "hi"
return [ item for item in locals().items() ]
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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