Commit 2a6bfe21 authored by Stefan Behnel's avatar Stefan Behnel

merge

parents 017241bf 4fa6d965
...@@ -740,19 +740,22 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -740,19 +740,22 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
arg_tuple = node.positional_args arg_tuple = node.positional_args
if not isinstance(arg_tuple, ExprNodes.TupleNode): if not isinstance(arg_tuple, ExprNodes.TupleNode):
return node return node
args = arg_tuple.args
return self._dispatch_to_handler( return self._dispatch_to_handler(
node, function, arg_tuple, node.keyword_args) node, function, args, node.keyword_args)
def visit_SimpleCallNode(self, node): def visit_SimpleCallNode(self, node):
self.visitchildren(node) self.visitchildren(node)
function = node.function function = node.function
if not function.type.is_pyobject: if function.type.is_pyobject:
return node arg_tuple = node.arg_tuple
arg_tuple = node.arg_tuple if not isinstance(arg_tuple, ExprNodes.TupleNode):
if not isinstance(arg_tuple, ExprNodes.TupleNode): return node
return node args = arg_tuple.args
else:
args = node.args
return self._dispatch_to_handler( return self._dispatch_to_handler(
node, node.function, arg_tuple) node, function, args)
### cleanup to avoid redundant coercions to/from Python types ### cleanup to avoid redundant coercions to/from Python types
...@@ -823,20 +826,25 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -823,20 +826,25 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
handler = getattr(self, '_handle_any_%s' % match_name, None) handler = getattr(self, '_handle_any_%s' % match_name, None)
return handler return handler
def _dispatch_to_handler(self, node, function, arg_tuple, kwargs=None): def _dispatch_to_handler(self, node, function, arg_list, kwargs=None):
if function.is_name: if function.is_name:
match_name = "_function_%s" % function.name # we only consider functions that are either builtin
# Python functions or builtins that were already replaced
# into a C function call (defined in the builtin scope)
is_builtin = function.entry.is_builtin \
or getattr(function.entry, 'scope', None) is Builtin.builtin_scope
if not is_builtin:
return node
function_handler = self._find_handler( function_handler = self._find_handler(
"function_%s" % function.name, kwargs) "function_%s" % function.name, kwargs)
if function_handler is None: if function_handler is None:
return node return node
if kwargs: if kwargs:
return function_handler(node, arg_tuple, kwargs) return function_handler(node, arg_list, kwargs)
else: else:
return function_handler(node, arg_tuple) return function_handler(node, arg_list)
elif function.is_attribute: elif function.is_attribute and function.type.is_pyobject:
attr_name = function.attribute attr_name = function.attribute
arg_list = arg_tuple.args
self_arg = function.obj self_arg = function.obj
obj_type = self_arg.type obj_type = self_arg.type
is_unbound_method = False is_unbound_method = False
...@@ -892,7 +900,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -892,7 +900,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
"""Replace dict(a=b,c=d,...) by the underlying keyword dict """Replace dict(a=b,c=d,...) by the underlying keyword dict
construction which is done anyway. construction which is done anyway.
""" """
if len(pos_args.args) > 0: if len(pos_args) > 0:
return node return node
if not isinstance(kwargs, ExprNodes.DictNode): if not isinstance(kwargs, ExprNodes.DictNode):
return node return node
...@@ -910,9 +918,9 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -910,9 +918,9 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
"""Replace dict(some_dict) by PyDict_Copy(some_dict) and """Replace dict(some_dict) by PyDict_Copy(some_dict) and
dict([ (a,b) for ... ]) by a literal { a:b for ... }. dict([ (a,b) for ... ]) by a literal { a:b for ... }.
""" """
if len(pos_args.args) != 1: if len(pos_args) != 1:
return node return node
arg = pos_args.args[0] arg = pos_args[0]
if arg.type is Builtin.dict_type: if arg.type is Builtin.dict_type:
arg = ExprNodes.NoneCheckNode( arg = ExprNodes.NoneCheckNode(
arg, "PyExc_TypeError", "'NoneType' is not iterable") arg, "PyExc_TypeError", "'NoneType' is not iterable")
...@@ -943,13 +951,13 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -943,13 +951,13 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
"""Replace set([a,b,...]) by a literal set {a,b,...} and """Replace set([a,b,...]) by a literal set {a,b,...} and
set([ x for ... ]) by a literal { x for ... }. set([ x for ... ]) by a literal { x for ... }.
""" """
arg_count = len(pos_args.args) arg_count = len(pos_args)
if arg_count == 0: if arg_count == 0:
return ExprNodes.SetNode(node.pos, args=[], return ExprNodes.SetNode(node.pos, args=[],
type=Builtin.set_type, is_temp=1) type=Builtin.set_type, is_temp=1)
if arg_count > 1: if arg_count > 1:
return node return node
iterable = pos_args.args[0] iterable = pos_args[0]
if isinstance(iterable, (ExprNodes.ListNode, ExprNodes.TupleNode)): if isinstance(iterable, (ExprNodes.ListNode, ExprNodes.TupleNode)):
return ExprNodes.SetNode(node.pos, args=iterable.args, return ExprNodes.SetNode(node.pos, args=iterable.args,
type=Builtin.set_type, is_temp=1) type=Builtin.set_type, is_temp=1)
...@@ -971,20 +979,20 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -971,20 +979,20 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
def _handle_simple_function_tuple(self, node, pos_args): def _handle_simple_function_tuple(self, node, pos_args):
"""Replace tuple([...]) by a call to PyList_AsTuple. """Replace tuple([...]) by a call to PyList_AsTuple.
""" """
if len(pos_args.args) != 1: if len(pos_args) != 1:
return node return node
list_arg = pos_args.args[0] list_arg = pos_args[0]
if list_arg.type is not Builtin.list_type: if list_arg.type is not Builtin.list_type:
return node return node
if not isinstance(list_arg, (ExprNodes.ComprehensionNode, if not isinstance(list_arg, (ExprNodes.ComprehensionNode,
ExprNodes.ListNode)): ExprNodes.ListNode)):
pos_args.args[0] = ExprNodes.NoneCheckNode( pos_args[0] = ExprNodes.NoneCheckNode(
list_arg, "PyExc_TypeError", list_arg, "PyExc_TypeError",
"'NoneType' object is not iterable") "'NoneType' object is not iterable")
return ExprNodes.PythonCapiCallNode( return ExprNodes.PythonCapiCallNode(
node.pos, "PyList_AsTuple", self.PyList_AsTuple_func_type, node.pos, "PyList_AsTuple", self.PyList_AsTuple_func_type,
args = pos_args.args, args = pos_args,
is_temp = node.is_temp is_temp = node.is_temp
) )
...@@ -1004,22 +1012,21 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -1004,22 +1012,21 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
]) ])
def _handle_simple_function_getattr(self, node, pos_args): def _handle_simple_function_getattr(self, node, pos_args):
args = pos_args.args if len(pos_args) == 2:
if len(args) == 2:
node = ExprNodes.PythonCapiCallNode( node = ExprNodes.PythonCapiCallNode(
node.pos, "PyObject_GetAttr", self.PyObject_GetAttr2_func_type, node.pos, "PyObject_GetAttr", self.PyObject_GetAttr2_func_type,
args = args, args = pos_args,
is_temp = node.is_temp is_temp = node.is_temp
) )
elif len(args) == 3: elif len(pos_args) == 3:
node = ExprNodes.PythonCapiCallNode( node = ExprNodes.PythonCapiCallNode(
node.pos, "__Pyx_GetAttr3", self.PyObject_GetAttr3_func_type, node.pos, "__Pyx_GetAttr3", self.PyObject_GetAttr3_func_type,
utility_code = Builtin.getattr3_utility_code, utility_code = Builtin.getattr3_utility_code,
args = args, args = pos_args,
is_temp = node.is_temp is_temp = node.is_temp
) )
else: else:
self._error_wrong_arg_count('getattr', node, args, '2 or 3') self._error_wrong_arg_count('getattr', node, pos_args, '2 or 3')
return node return node
Pyx_Type_func_type = PyrexTypes.CFuncType( Pyx_Type_func_type = PyrexTypes.CFuncType(
...@@ -1028,17 +1035,42 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -1028,17 +1035,42 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
]) ])
def _handle_simple_function_type(self, node, pos_args): def _handle_simple_function_type(self, node, pos_args):
args = pos_args.args if len(pos_args) != 1:
if len(args) != 1:
return node return node
node = ExprNodes.PythonCapiCallNode( node = ExprNodes.PythonCapiCallNode(
node.pos, "__Pyx_Type", self.Pyx_Type_func_type, node.pos, "__Pyx_Type", self.Pyx_Type_func_type,
args = args, args = pos_args,
is_temp = node.is_temp, is_temp = node.is_temp,
utility_code = pytype_utility_code, utility_code = pytype_utility_code,
) )
return node return node
Pyx_strlen_func_type = PyrexTypes.CFuncType(
PyrexTypes.c_size_t_type, [
PyrexTypes.CFuncTypeArg("bytes", PyrexTypes.c_char_ptr_type, None)
])
def _handle_simple_function_len(self, node, pos_args):
# note: this only works because we already replaced len() by
# PyObject_Length() which returns a Py_ssize_t instead of a
# Python object, so we can return a plain size_t instead
# without caring about Python object conversion etc.
if len(pos_args) != 1:
self._error_wrong_arg_count('len', node, pos_args, 1)
return node
arg = pos_args[0]
if isinstance(arg, ExprNodes.CoerceToPyTypeNode):
arg = arg.arg
if not arg.type.is_string:
return node
node = ExprNodes.PythonCapiCallNode(
node.pos, "strlen", self.Pyx_strlen_func_type,
args = [arg],
is_temp = node.is_temp,
utility_code = include_string_h_utility_code,
)
return node
### special methods ### special methods
Pyx_tp_new_func_type = PyrexTypes.CFuncType( Pyx_tp_new_func_type = PyrexTypes.CFuncType(
...@@ -1477,6 +1509,13 @@ static INLINE PyObject* __Pyx_Type(PyObject* o) { ...@@ -1477,6 +1509,13 @@ static INLINE PyObject* __Pyx_Type(PyObject* o) {
) )
include_string_h_utility_code = UtilityCode(
proto = """
#include <string.h>
"""
)
tpnew_utility_code = UtilityCode( tpnew_utility_code = UtilityCode(
proto = """ proto = """
static INLINE PyObject* __Pyx_tp_new(PyObject* type_obj) { static INLINE PyObject* __Pyx_tp_new(PyObject* type_obj) {
......
...@@ -624,6 +624,7 @@ class BuiltinScope(Scope): ...@@ -624,6 +624,7 @@ class BuiltinScope(Scope):
var_entry.is_variable = 1 var_entry.is_variable = 1
var_entry.is_cglobal = 1 var_entry.is_cglobal = 1
var_entry.is_readonly = 1 var_entry.is_readonly = 1
var_entry.is_builtin = 1
var_entry.utility_code = utility_code var_entry.utility_code = utility_code
entry.as_variable = var_entry entry.as_variable = var_entry
...@@ -1184,6 +1185,9 @@ class ClassScope(Scope): ...@@ -1184,6 +1185,9 @@ class ClassScope(Scope):
return self.outer_scope.add_string_const(value, identifier) return self.outer_scope.add_string_const(value, identifier)
def lookup(self, name): def lookup(self, name):
entry = Scope.lookup(self, name)
if entry:
return entry
if name == "classmethod": if name == "classmethod":
# We don't want to use the builtin classmethod here 'cause it won't do the # We don't want to use the builtin classmethod here 'cause it won't do the
# right thing in this scope (as the class memebers aren't still functions). # right thing in this scope (as the class memebers aren't still functions).
...@@ -1197,9 +1201,7 @@ class ClassScope(Scope): ...@@ -1197,9 +1201,7 @@ class ClassScope(Scope):
py_object_type, py_object_type,
[PyrexTypes.CFuncTypeArg("", py_object_type, None)], 0, 0)) [PyrexTypes.CFuncTypeArg("", py_object_type, None)], 0, 0))
entry.is_cfunction = 1 entry.is_cfunction = 1
return entry return entry
else:
return Scope.lookup(self, name)
class PyClassScope(ClassScope): class PyClassScope(ClassScope):
......
cimport cython
def test_file_py(file): def test_file_py(file):
assert isinstance(file, (str, unicode)), \ assert isinstance(file, (str, unicode)), \
u"not a string, found '%s' instead" % file.__class__.__name__ u"not a string, found '%s' instead" % file.__class__.__name__
...@@ -12,19 +14,35 @@ cdef test_file_c(file): ...@@ -12,19 +14,35 @@ cdef test_file_c(file):
def range(arg): def range(arg):
return u'range' + arg return u'range' + arg
def len(arg):
return u'len' + arg
cdef type(arg): cdef type(arg):
return u'type' + arg return u'type' + arg
@cython.test_fail_if_path_exists(
'//SimpleCallNode/NameNode[@name="type" and @entry.is_cfunction]',
'//SimpleCallNode/NameNode[@name="len" and @entry.is_cfunction]',
)
@cython.test_assert_path_exists(
'//SimpleCallNode/NameNode[@name="type"]',
'//SimpleCallNode/NameNode[@name="type" and not(@entry.is_cfunction)]',
'//SimpleCallNode/NameNode[@name="len"]',
)
def test_c(arg): def test_c(arg):
""" """
>>> test_c('abc') >>> test_c('abc')
fileabc fileabc
lenabc
typeabc typeabc
>>> print(test_file_py('abc')) >>> print(test_file_py('abc'))
abc abc
>>> print(range('abc')) >>> print(range('abc'))
rangeabc rangeabc
>>> print(len('abc'))
lenabc
""" """
print test_file_c(arg) print test_file_c(arg)
print len(arg)
print type(arg) print type(arg)
__doc__ = """
>>> lentest_char()
7
>>> lentest_char_c()
7
>>> lentest_uchar()
7
>>> lentest_uchar_c()
7
>>> lentest_py()
7
>>> lentest_py_c()
7
"""
cimport cython
cdef char* s = b"abcdefg"
cdef unsigned char* us = b"abcdefg"
cdef bytes pystr = b"abcdefg"
@cython.test_assert_path_exists(
"//PythonCapiCallNode",
)
def lentest_char():
return len(s)
@cython.test_assert_path_exists(
"//PythonCapiCallNode",
)
def lentest_char_c():
cdef Py_ssize_t l = len(s)
return l
@cython.test_assert_path_exists(
"//PythonCapiCallNode",
)
def lentest_uchar():
return len(us)
@cython.test_assert_path_exists(
"//PythonCapiCallNode",
)
def lentest_uchar_c():
cdef Py_ssize_t l = len(us)
return l
@cython.test_assert_path_exists(
"//SimpleCallNode",
)
def lentest_py():
return len(pystr)
@cython.test_assert_path_exists(
"//SimpleCallNode",
)
def lentest_py_c():
cdef Py_ssize_t l = len(pystr)
return l
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