Commit 18933f25 authored by Stefan Behnel's avatar Stefan Behnel

automatically replace calls to builtin.__contains__() with the corresponding...

automatically replace calls to builtin.__contains__() with the corresponding C-API function and enable optimisation of unbound builtin methods for subtypes
parent 2608935e
...@@ -268,20 +268,26 @@ builtin_types_table = [ ...@@ -268,20 +268,26 @@ builtin_types_table = [
BuiltinAttribute('imag', 'cval.imag', field_type = PyrexTypes.c_double_type), BuiltinAttribute('imag', 'cval.imag', field_type = PyrexTypes.c_double_type),
]), ]),
("bytes", "PyBytes_Type", []), ("bytes", "PyBytes_Type", [BuiltinMethod("__contains__", "TO", "b", "PySequence_Contains"),
("str", "PyString_Type", []), ]),
("unicode", "PyUnicode_Type", [BuiltinMethod("join", "TO", "T", "PyUnicode_Join"), ("str", "PyString_Type", [BuiltinMethod("__contains__", "TO", "b", "PySequence_Contains"),
]),
("unicode", "PyUnicode_Type", [BuiltinMethod("__contains__", "TO", "b", "PyUnicode_Contains"),
BuiltinMethod("join", "TO", "T", "PyUnicode_Join"),
]), ]),
("tuple", "PyTuple_Type", []), ("tuple", "PyTuple_Type", [BuiltinMethod("__contains__", "TO", "b", "PySequence_Contains"),
]),
("list", "PyList_Type", [BuiltinMethod("insert", "TzO", "r", "PyList_Insert"), ("list", "PyList_Type", [BuiltinMethod("__contains__", "TO", "b", "PySequence_Contains"),
BuiltinMethod("insert", "TzO", "r", "PyList_Insert"),
BuiltinMethod("reverse", "T", "r", "PyList_Reverse"), BuiltinMethod("reverse", "T", "r", "PyList_Reverse"),
BuiltinMethod("append", "TO", "r", "__Pyx_PyList_Append", BuiltinMethod("append", "TO", "r", "__Pyx_PyList_Append",
utility_code=UtilityCode.load("ListAppend", "Optimize.c")), utility_code=UtilityCode.load("ListAppend", "Optimize.c")),
]), ]),
("dict", "PyDict_Type", [BuiltinMethod("items", "T", "O", "__Pyx_PyDict_Items", ("dict", "PyDict_Type", [BuiltinMethod("__contains__", "TO", "b", "PyDict_Contains"),
BuiltinMethod("items", "T", "O", "__Pyx_PyDict_Items",
utility_code=UtilityCode.load("py_dict_items", "Builtins.c")), utility_code=UtilityCode.load("py_dict_items", "Builtins.c")),
BuiltinMethod("keys", "T", "O", "__Pyx_PyDict_Keys", BuiltinMethod("keys", "T", "O", "__Pyx_PyDict_Keys",
utility_code=UtilityCode.load("py_dict_keys", "Builtins.c")), utility_code=UtilityCode.load("py_dict_keys", "Builtins.c")),
...@@ -309,7 +315,8 @@ builtin_types_table = [ ...@@ -309,7 +315,8 @@ builtin_types_table = [
]), ]),
# ("file", "PyFile_Type", []), # not in Py3 # ("file", "PyFile_Type", []), # not in Py3
("set", "PySet_Type", [BuiltinMethod("clear", "T", "r", "PySet_Clear", ("set", "PySet_Type", [BuiltinMethod("__contains__", "TO", "b", "PySequence_Contains"),
BuiltinMethod("clear", "T", "r", "PySet_Clear",
utility_code = py_set_utility_code), utility_code = py_set_utility_code),
# discard() and remove() have a special treatment for unhashable values # discard() and remove() have a special treatment for unhashable values
# BuiltinMethod("discard", "TO", "r", "PySet_Discard", # BuiltinMethod("discard", "TO", "r", "PySet_Discard",
......
...@@ -1494,7 +1494,7 @@ class NameNode(AtomicExprNode): ...@@ -1494,7 +1494,7 @@ class NameNode(AtomicExprNode):
if not entry: if not entry:
entry = env.lookup(self.name) entry = env.lookup(self.name)
if entry and entry.is_type: if entry and entry.is_type:
if entry.type.is_extension_type: # or entry.type.is_builtin_type: if entry.type.is_extension_type or entry.type.is_builtin_type:
return entry.type return entry.type
return None return None
...@@ -4001,6 +4001,11 @@ class SimpleCallNode(CallNode): ...@@ -4001,6 +4001,11 @@ class SimpleCallNode(CallNode):
else: else:
arg = CloneNode(self.self) arg = CloneNode(self.self)
arg = self.coerced_self = arg.coerce_to(formal_arg.type, env) arg = self.coerced_self = arg.coerce_to(formal_arg.type, env)
elif formal_arg.type.is_builtin_type:
# special case: unbound methods of builtins accept subtypes
arg = arg.coerce_to(formal_arg.type, env)
if arg.type.is_builtin_type and isinstance(arg, PyTypeTestNode):
arg.exact_builtin_type = False
args[0] = arg args[0] = arg
# Coerce arguments # Coerce arguments
...@@ -4763,6 +4768,9 @@ class AttributeNode(ExprNode): ...@@ -4763,6 +4768,9 @@ class AttributeNode(ExprNode):
entry = type.scope.lookup_here(self.attribute) entry = type.scope.lookup_here(self.attribute)
if entry and entry.is_cmethod: if entry and entry.is_cmethod:
if type.is_builtin_type: if type.is_builtin_type:
if not self.is_called:
# must handle this as Python object
return None
ubcm_entry = entry ubcm_entry = entry
else: else:
# Create a temporary entry describing the C method # Create a temporary entry describing the C method
...@@ -4793,7 +4801,7 @@ class AttributeNode(ExprNode): ...@@ -4793,7 +4801,7 @@ class AttributeNode(ExprNode):
if module_scope: if module_scope:
entry = module_scope.lookup_here(self.attribute) entry = module_scope.lookup_here(self.attribute)
if entry and entry.is_type: if entry and entry.is_type:
if entry.type.is_extension_type: # or entry.type.is_builtin_type: if entry.type.is_extension_type or entry.type.is_builtin_type:
return entry.type return entry.type
return None return None
...@@ -9719,6 +9727,8 @@ class PyTypeTestNode(CoercionNode): ...@@ -9719,6 +9727,8 @@ class PyTypeTestNode(CoercionNode):
# object is an instance of a particular extension type. # object is an instance of a particular extension type.
# This node borrows the result of its argument node. # This node borrows the result of its argument node.
exact_builtin_type = True
def __init__(self, arg, dst_type, env, notnone=False): def __init__(self, arg, dst_type, env, notnone=False):
# The arg is know to be a Python object, and # The arg is know to be a Python object, and
# the dst_type is known to be an extension type. # the dst_type is known to be an extension type.
...@@ -9760,12 +9770,17 @@ class PyTypeTestNode(CoercionNode): ...@@ -9760,12 +9770,17 @@ class PyTypeTestNode(CoercionNode):
def generate_result_code(self, code): def generate_result_code(self, code):
if self.type.typeobj_is_available(): if self.type.typeobj_is_available():
if not self.type.is_builtin_type: if self.type.is_builtin_type:
code.globalstate.use_utility_code(UtilityCode.load_cached("ExtTypeTest", "ObjectHandling.c")) type_test = self.type.type_test_code(
code.putln( self.arg.py_result(),
"if (!(%s)) %s" % ( self.notnone, exact=self.exact_builtin_type)
self.type.type_test_code(self.arg.py_result(), self.notnone), else:
code.error_goto(self.pos))) type_test = self.type.type_test_code(
self.arg.py_result(), self.notnone)
code.globalstate.use_utility_code(
UtilityCode.load_cached("ExtTypeTest", "ObjectHandling.c"))
code.putln("if (!(%s)) %s" % (
type_test, code.error_goto(self.pos)))
else: else:
error(self.pos, "Cannot test type of extern C class " error(self.pos, "Cannot test type of extern C class "
"without type object name specification") "without type object name specification")
......
...@@ -984,8 +984,8 @@ class BuiltinObjectType(PyObjectType): ...@@ -984,8 +984,8 @@ class BuiltinObjectType(PyObjectType):
def isinstance_code(self, arg): def isinstance_code(self, arg):
return '%s(%s)' % (self.type_check_function(exact=False), arg) return '%s(%s)' % (self.type_check_function(exact=False), arg)
def type_test_code(self, arg, notnone=False): def type_test_code(self, arg, notnone=False, exact=True):
type_check = self.type_check_function(exact=True) type_check = self.type_check_function(exact=exact)
check = 'likely(%s(%s))' % (type_check, arg) check = 'likely(%s(%s))' % (type_check, arg)
if not notnone: if not notnone:
check += '||((%s) == Py_None)' % arg check += '||((%s) == Py_None)' % arg
......
...@@ -1891,7 +1891,7 @@ class CClassScope(ClassScope): ...@@ -1891,7 +1891,7 @@ class CClassScope(ClassScope):
def declare_cfunction(self, name, type, pos, def declare_cfunction(self, name, type, pos,
cname = None, visibility = 'private', api = 0, in_pxd = 0, cname = None, visibility = 'private', api = 0, in_pxd = 0,
defining = 0, modifiers = (), utility_code = None): defining = 0, modifiers = (), utility_code = None):
if get_special_method_signature(name): 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'") error(pos, "Special methods must be declared with 'def', not 'cdef'")
args = type.args args = type.args
if not args: if not args:
......
# mode: run
# tag: special_method
cimport cython
text = u'ab jd sdflk as sa sadas asdas fsdf '
@cython.test_fail_if_path_exists(
"//CoerceFromPyTypeNode")
@cython.test_assert_path_exists(
"//CoerceToPyTypeNode",
"//AttributeNode",
"//AttributeNode[@entry.cname = 'PyUnicode_Contains']")
def unicode_contains(unicode s, substring):
"""
>>> unicode_contains(text, 'fl')
True
>>> unicode_contains(text, 'XYZ')
False
>>> unicode_contains(None, 'XYZ')
Traceback (most recent call last):
AttributeError: 'NoneType' object has no attribute '__contains__'
"""
return s.__contains__(substring)
@cython.test_fail_if_path_exists(
"//CoerceFromPyTypeNode")
@cython.test_assert_path_exists(
# "//CoerceToPyTypeNode",
"//NameNode[@entry.cname = 'PyUnicode_Contains']")
def unicode_contains_unbound(unicode s, substring):
"""
>>> unicode_contains_unbound(text, 'fl')
True
>>> unicode_contains_unbound(text, 'XYZ')
False
>>> unicode_contains_unbound(None, 'XYZ') # doctest: +ELLIPSIS
Traceback (most recent call last):
TypeError: descriptor '__contains__' requires a '...' object but received a 'NoneType'
"""
return unicode.__contains__(s, substring)
cdef class UnicodeSubclass(unicode):
"""
>>> u = UnicodeSubclass(text)
>>> 'fl' in u
False
>>> 'XYZ' in u
True
>>> u.method('fl')
False
>>> u.method('XYZ')
True
>>> u.operator('fl')
False
>>> u.operator('XYZ')
True
"""
def __contains__(self, substring):
return substring not in (self + u'x')
def method(self, other):
return self.__contains__(other)
def operator(self, other):
return other in self
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