Commit 97cb7314 authored by Stefan Behnel's avatar Stefan Behnel

ticket #607: access inner fields of CPython's builtin objects

--HG--
rename : tests/broken/builtinslice.pyx => tests/run/builtinslice.pyx
parent a51f81b7
......@@ -300,6 +300,22 @@ class _BuiltinOverride(object):
self.func_type, self.sig = func_type, sig
self.utility_code = utility_code
class BuiltinAttribute(object):
def __init__(self, py_name, cname=None, field_type=None, field_type_name=None):
self.py_name = py_name
self.cname = cname or py_name
self.field_type_name = field_type_name # can't do the lookup before the type is declared!
self.field_type = field_type
def declare_in_type(self, self_type):
if self.field_type_name is not None:
# lazy type lookup
field_type = builtin_scope.lookup(self.field_type_name).type
else:
field_type = self.field_type or PyrexTypes.py_object_type
entry = self_type.scope.declare(self.py_name, self.cname, field_type, None, 'private')
entry.is_variable = True
class BuiltinFunction(_BuiltinOverride):
def declare_in_scope(self, scope):
func_type, sig = self.func_type, self.sig
......@@ -426,9 +442,10 @@ builtin_types_table = [
("long", "PyLong_Type", []),
("float", "PyFloat_Type", []),
# Until we have a way to access attributes of a type,
# we don't want to make this one builtin.
# ("complex", "PyComplex_Type", []),
("complex", "PyComplex_Type", [BuiltinAttribute('cval', field_type_name = 'Py_complex'),
BuiltinAttribute('real', 'cval.real', field_type = PyrexTypes.c_double_type),
BuiltinAttribute('imag', 'cval.imag', field_type = PyrexTypes.c_double_type),
]),
("bytes", "PyBytes_Type", []),
("str", "PyString_Type", []),
......@@ -447,7 +464,10 @@ builtin_types_table = [
BuiltinMethod("values","T", "O", "PyDict_Values"), # FIXME: Py3 mode?
BuiltinMethod("copy", "T", "T", "PyDict_Copy")]),
("slice", "PySlice_Type", []),
("slice", "PySlice_Type", [BuiltinAttribute('start'),
BuiltinAttribute('stop'),
BuiltinAttribute('step'),
]),
# ("file", "PyFile_Type", []), # not in Py3
("set", "PySet_Type", [BuiltinMethod("clear", "T", "i", "PySet_Clear"),
......@@ -480,6 +500,10 @@ builtin_structs_table = [
("strides", PyrexTypes.c_py_ssize_t_ptr_type),
("suboffsets", PyrexTypes.c_py_ssize_t_ptr_type),
("internal", PyrexTypes.c_void_ptr_type),
]),
('Py_complex', 'Py_complex',
[('real', PyrexTypes.c_double_type),
('imag', PyrexTypes.c_double_type),
])
]
......@@ -497,7 +521,13 @@ def init_builtin_types():
global builtin_types
for name, cname, methods in builtin_types_table:
utility = builtin_utility_code.get(name)
the_type = builtin_scope.declare_builtin_type(name, cname, utility)
if name == 'frozenset':
objstruct_cname = 'PySetObject'
elif name == 'bool':
objstruct_cname = None
else:
objstruct_cname = 'Py%sObject' % name.capitalize()
the_type = builtin_scope.declare_builtin_type(name, cname, utility, objstruct_cname)
builtin_types[name] = the_type
for method in methods:
method.declare_in_type(the_type)
......@@ -512,9 +542,9 @@ def init_builtin_structs():
name, "struct", scope, 1, None, cname = cname)
def init_builtins():
init_builtin_structs()
init_builtin_funcs()
init_builtin_types()
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, complex_type
......
......@@ -3466,7 +3466,7 @@ class AttributeNode(ExprNode):
if obj_type.is_ptr or obj_type.is_array:
obj_type = obj_type.base_type
self.op = "->"
elif obj_type.is_extension_type:
elif obj_type.is_extension_type or obj_type.is_builtin_type:
self.op = "->"
else:
self.op = "."
......@@ -3558,6 +3558,9 @@ class AttributeNode(ExprNode):
elif obj.type.is_complex:
return "__Pyx_C%s(%s)" % (self.member.upper(), obj_code)
else:
if obj.type.is_builtin_type and self.entry and self.entry.is_variable:
# accessing a field of a builtin type, need to cast better than result_as() does
obj_code = obj.type.cast_code(obj.result(), to_object_struct = True)
return "%s%s%s" % (obj_code, self.op, self.member)
def generate_result_code(self, code):
......
......@@ -373,16 +373,18 @@ class PyObjectType(PyrexType):
return cname
class BuiltinObjectType(PyObjectType):
# objstruct_cname string Name of PyObject struct
is_builtin_type = 1
has_attributes = 1
base_type = None
module_name = '__builtin__'
def __init__(self, name, cname):
def __init__(self, name, cname, objstruct_cname=None):
self.name = name
self.cname = cname
self.typeptr_cname = "&" + cname
self.objstruct_cname = objstruct_cname
def set_scope(self, scope):
self.scope = scope
......@@ -445,6 +447,11 @@ class BuiltinObjectType(PyObjectType):
entity_code = "*%s" % entity_code
return self.base_declaration_code(base_code, entity_code)
def cast_code(self, expr_code, to_object_struct = False):
return "((%s*)%s)" % (
to_object_struct and self.objstruct_cname or "PyObject", # self.objstruct_cname may be None
expr_code)
class PyExtensionType(PyObjectType):
#
......
......@@ -741,9 +741,9 @@ class BuiltinScope(Scope):
entry.as_variable = var_entry
return entry
def declare_builtin_type(self, name, cname, utility_code = None):
def declare_builtin_type(self, name, cname, utility_code = None, objstruct_cname = None):
name = EncodedString(name)
type = PyrexTypes.BuiltinObjectType(name, cname)
type = PyrexTypes.BuiltinObjectType(name, cname, objstruct_cname)
scope = CClassScope(name, outer_scope=None, visibility='extern')
scope.directives = {}
if name == 'bool':
......
cdef int f() except -1:
cdef slice s
cdef object z
cdef int i
z = slice
s = slice(1, 2, 3)
z = slice.indices()
i = s.start
i = s.stop
i = s.step
cimport cython
@cython.test_assert_path_exists('//TupleNode//CoerceToPyTypeNode//AttributeNode')
def complex_attributes():
"""
>>> complex_attributes()
(1.0, 2.0)
"""
cdef complex c = 1+2j
return (c.real, c.imag)
@cython.test_assert_path_exists('//TupleNode//CoerceToPyTypeNode//AttributeNode')
def complex_attributes_assign():
"""
>>> complex_attributes_assign()
(10.0, 20.0)
"""
cdef complex c = 1+2j
c.real, c.imag = 10, 20
return (c.real, c.imag)
@cython.test_assert_path_exists('//TupleNode//CoerceToPyTypeNode//AttributeNode')
def complex_cstruct_assign():
"""
>>> complex_cstruct_assign()
(10.0, 20.0)
"""
cdef complex c = 1+2j
cval = &c.cval
cval.real, cval.imag = 10, 20
return (c.real, c.imag)
cimport cython
def unbound_method_lookup():
"""
>>> unbound_method_lookup()
"""
ignore = slice.indices
@cython.test_assert_path_exists('//SingleAssignmentNode//AttributeNode[@is_py_attr = False]')
@cython.test_fail_if_path_exists('//SingleAssignmentNode//AttributeNode[@is_py_attr = True]')
def typed_slice():
"""
>>> typed_slice()
(1, 2, 3)
"""
cdef slice s
cdef object z
cdef Py_ssize_t a,b,c
z = slice
s = slice(1, 2, 3)
s.indices
a = s.start
b = s.stop
c = s.step
return (a,b,c)
@cython.test_fail_if_path_exists('//SingleAssignmentNode//AttributeNode[@is_py_attr = False]')
def plain_object_slice():
"""
>>> plain_object_slice()
(1, 2, 3)
"""
cdef object s
cdef object z
cdef Py_ssize_t a,b,c
s = slice(1, 2, 3)
s.indices
a = s.start
b = s.stop
c = s.step
return (a,b,c)
......@@ -18,7 +18,6 @@ def test_non_optimised():
assert isinstance(A(), foo)
assert isinstance(0, (int, long))
assert not isinstance(u"xyz", (int, long))
assert isinstance(complex(), complex) # FIXME: this should be optimised, too!
return True
@cython.test_assert_path_exists('//PythonCapiCallNode',
......@@ -46,6 +45,7 @@ def test_optimised():
assert isinstance(dict(), dict)
assert isinstance(set(), set)
assert isinstance(slice(0), slice)
assert isinstance(complex(), complex)
assert not isinstance(u"foo", int)
assert isinstance(A, type)
return True
......
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