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 = [
BuiltinAttribute('imag', 'cval.imag', field_type = PyrexTypes.c_double_type),
]),
("bytes", "PyBytes_Type", []),
("str", "PyString_Type", []),
("unicode", "PyUnicode_Type", [BuiltinMethod("join", "TO", "T", "PyUnicode_Join"),
("bytes", "PyBytes_Type", [BuiltinMethod("__contains__", "TO", "b", "PySequence_Contains"),
]),
("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("append", "TO", "r", "__Pyx_PyList_Append",
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")),
BuiltinMethod("keys", "T", "O", "__Pyx_PyDict_Keys",
utility_code=UtilityCode.load("py_dict_keys", "Builtins.c")),
......@@ -309,7 +315,8 @@ builtin_types_table = [
]),
# ("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),
# discard() and remove() have a special treatment for unhashable values
# BuiltinMethod("discard", "TO", "r", "PySet_Discard",
......
......@@ -1494,7 +1494,7 @@ class NameNode(AtomicExprNode):
if not entry:
entry = env.lookup(self.name)
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 None
......@@ -4001,6 +4001,11 @@ class SimpleCallNode(CallNode):
else:
arg = CloneNode(self.self)
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
# Coerce arguments
......@@ -4763,6 +4768,9 @@ class AttributeNode(ExprNode):
entry = type.scope.lookup_here(self.attribute)
if entry and entry.is_cmethod:
if type.is_builtin_type:
if not self.is_called:
# must handle this as Python object
return None
ubcm_entry = entry
else:
# Create a temporary entry describing the C method
......@@ -4793,7 +4801,7 @@ class AttributeNode(ExprNode):
if module_scope:
entry = module_scope.lookup_here(self.attribute)
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 None
......@@ -9719,6 +9727,8 @@ class PyTypeTestNode(CoercionNode):
# object is an instance of a particular extension type.
# This node borrows the result of its argument node.
exact_builtin_type = True
def __init__(self, arg, dst_type, env, notnone=False):
# The arg is know to be a Python object, and
# the dst_type is known to be an extension type.
......@@ -9760,12 +9770,17 @@ class PyTypeTestNode(CoercionNode):
def generate_result_code(self, code):
if self.type.typeobj_is_available():
if not self.type.is_builtin_type:
code.globalstate.use_utility_code(UtilityCode.load_cached("ExtTypeTest", "ObjectHandling.c"))
code.putln(
"if (!(%s)) %s" % (
self.type.type_test_code(self.arg.py_result(), self.notnone),
code.error_goto(self.pos)))
if self.type.is_builtin_type:
type_test = self.type.type_test_code(
self.arg.py_result(),
self.notnone, exact=self.exact_builtin_type)
else:
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:
error(self.pos, "Cannot test type of extern C class "
"without type object name specification")
......
......@@ -984,8 +984,8 @@ class BuiltinObjectType(PyObjectType):
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)
def type_test_code(self, arg, notnone=False, exact=True):
type_check = self.type_check_function(exact=exact)
check = 'likely(%s(%s))' % (type_check, arg)
if not notnone:
check += '||((%s) == Py_None)' % arg
......
......@@ -1891,7 +1891,7 @@ class CClassScope(ClassScope):
def declare_cfunction(self, name, type, pos,
cname = None, visibility = 'private', api = 0, in_pxd = 0,
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'")
args = type.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