File: abstractinst.py

package info (click to toggle)
pypy3 7.0.0%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 111,848 kB
  • sloc: python: 1,291,746; ansic: 74,281; asm: 5,187; cpp: 3,017; sh: 2,533; makefile: 544; xml: 243; lisp: 45; csh: 21; awk: 4
file content (226 lines) | stat: -rw-r--r-- 9,051 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
"""
Implementation of the 'abstract instance and subclasses' protocol:
objects can return pseudo-classes as their '__class__' attribute, and
pseudo-classes can have a '__bases__' attribute with a tuple of other
pseudo-classes.  The standard built-in functions isinstance() and
issubclass() follow and trust these attributes is they are present, in
addition to checking for instances and subtypes in the normal way.
"""

from rpython.rlib import jit

from pypy.interpreter.baseobjspace import ObjSpace as BaseObjSpace
from pypy.interpreter.error import OperationError

def _get_bases(space, w_cls):
    """Returns 'cls.__bases__'.  Returns None if there is
    no __bases__ or if cls.__bases__ is not a tuple.
    """
    try:
        w_bases = space.getattr(w_cls, space.newtext('__bases__'))
    except OperationError as e:
        if not e.match(space, space.w_AttributeError):
            raise       # propagate other errors
        return None
    if space.isinstance_w(w_bases, space.w_tuple):
        return w_bases
    else:
        return None

def abstract_isclass_w(space, w_obj):
    return _get_bases(space, w_obj) is not None

def check_class(space, w_obj, msg):
    if not abstract_isclass_w(space, w_obj):
        raise OperationError(space.w_TypeError, space.newtext(msg))


def abstract_getclass(space, w_obj):
    try:
        return space.getattr(w_obj, space.newtext('__class__'))
    except OperationError as e:
        if not e.match(space, space.w_AttributeError):
            raise       # propagate other errors
        return space.type(w_obj)


# ---------- isinstance ----------


def p_recursive_isinstance_w(space, w_inst, w_cls):
    # Copied straight from CPython 2.7.  Does not handle 'cls' being a tuple.
    if space.isinstance_w(w_cls, space.w_type):
        return p_recursive_isinstance_type_w(space, w_inst, w_cls)

    check_class(space, w_cls, "isinstance() arg 2 must be a class, type,"
                              " or tuple of classes and types")
    try:
        w_abstractclass = space.getattr(w_inst, space.newtext('__class__'))
    except OperationError as e:
        if not e.match(space, space.w_AttributeError):
            raise       # propagate other errors
        return False
    else:
        return p_abstract_issubclass_w(space, w_abstractclass, w_cls)


def p_recursive_isinstance_type_w(space, w_inst, w_type):
    # subfunctionality of p_recursive_isinstance_w(): assumes that w_type is
    # a type object.  Copied straight from CPython 2.7.
    if space.isinstance_w(w_inst, w_type):
        return True
    try:
        w_abstractclass = space.getattr(w_inst, space.newtext('__class__'))
    except OperationError as e:
        if not e.match(space, space.w_AttributeError):
            raise       # propagate other errors
    else:
        if w_abstractclass is not space.type(w_inst):
            if space.isinstance_w(w_abstractclass, space.w_type):
                return space.issubtype_w(w_abstractclass, w_type)
    return False


@jit.unroll_safe
def abstract_isinstance_w(space, w_obj, w_klass_or_tuple, allow_override=False):
    """Implementation for the full 'isinstance(obj, klass_or_tuple)'."""
    # Copied from CPython 2.7's PyObject_Isinstance().  Additionally,
    # if 'allow_override' is False (the default), then don't try to
    # use a custom __instancecheck__ method.

    # WARNING: backward compatibility function name here.  CPython
    # uses the name "abstract" to refer to the logic of handling
    # class-like objects, with a "__bases__" attribute.  This function
    # here is not related to that and implements the full
    # PyObject_IsInstance() logic.

    # Quick test for an exact match
    if space.type(w_obj) is w_klass_or_tuple:
        return True

    # -- case (anything, tuple)
    # XXX it might be risky that the JIT sees this
    if space.isinstance_w(w_klass_or_tuple, space.w_tuple):
        for w_klass in space.fixedview(w_klass_or_tuple):
            if abstract_isinstance_w(space, w_obj, w_klass, allow_override):
                return True
        return False

    # -- case (anything, type)
    if allow_override:
        w_check = space.lookup(w_klass_or_tuple, "__instancecheck__")
        if w_check is not None:
            # this is the common case: all type objects have a method
            # __instancecheck__.  The one in the base 'type' type calls
            # back p_recursive_isinstance_type_w() from the present module.
            return space.is_true(space.get_and_call_function(
                w_check, w_klass_or_tuple, w_obj))

    return p_recursive_isinstance_w(space, w_obj, w_klass_or_tuple)


# ---------- issubclass ----------


@jit.unroll_safe
def p_abstract_issubclass_w(space, w_derived, w_cls):
    # Copied straight from CPython 2.7, function abstract_issubclass().
    # Don't confuse this with the function abstract_issubclass_w() below.
    # Here, w_cls cannot be a tuple.
    while True:
        if space.is_w(w_derived, w_cls):
            return True
        w_bases = _get_bases(space, w_derived)
        if w_bases is None:
            return False
        bases_w = space.fixedview(w_bases)
        last_index = len(bases_w) - 1
        if last_index < 0:
            return False
        # Avoid recursivity in the single inheritance case; in general,
        # don't recurse on the last item in the tuple (loop instead).
        for i in range(last_index):
            if p_abstract_issubclass_w(space, bases_w[i], w_cls):
                return True
        w_derived = bases_w[last_index]


def p_recursive_issubclass_w(space, w_derived, w_cls):
    # From CPython's function of the same name (which as far as I can tell
    # is not recursive).  Copied straight from CPython 2.7.
    if (space.isinstance_w(w_cls, space.w_type) and
        space.isinstance_w(w_derived, space.w_type)):
        return space.issubtype_w(w_derived, w_cls)
    #
    check_class(space, w_derived, "issubclass() arg 1 must be a class")
    check_class(space, w_cls, "issubclass() arg 2 must be a class"
                              " or tuple of classes")
    return p_abstract_issubclass_w(space, w_derived, w_cls)


@jit.unroll_safe
def abstract_issubclass_w(space, w_derived, w_klass_or_tuple,
                          allow_override=False):
    """Implementation for the full 'issubclass(derived, klass_or_tuple)'."""

    # WARNING: backward compatibility function name here.  CPython
    # uses the name "abstract" to refer to the logic of handling
    # class-like objects, with a "__bases__" attribute.  This function
    # here is not related to that and implements the full
    # PyObject_IsSubclass() logic.  There is also p_abstract_issubclass_w().

    # -- case (anything, tuple-of-classes)
    if space.isinstance_w(w_klass_or_tuple, space.w_tuple):
        for w_klass in space.fixedview(w_klass_or_tuple):
            if abstract_issubclass_w(space, w_derived, w_klass, allow_override):
                return True
        return False

    # -- case (anything, type)
    if allow_override:
        w_check = space.lookup(w_klass_or_tuple, "__subclasscheck__")
        if w_check is not None:
            # this is the common case: all type objects have a method
            # __subclasscheck__.  The one in the base 'type' type calls
            # back p_recursive_issubclass_w() from the present module.
            return space.is_true(space.get_and_call_function(
                w_check, w_klass_or_tuple, w_derived))

    return p_recursive_issubclass_w(space, w_derived, w_klass_or_tuple)


# ------------------------------------------------------------
# Exception helpers

def exception_is_valid_obj_as_class_w(space, w_obj):
    return BaseObjSpace.exception_is_valid_obj_as_class_w(space, w_obj)

def exception_is_valid_class_w(space, w_cls):
    return BaseObjSpace.exception_is_valid_class_w(space, w_cls)

def exception_getclass(space, w_obj):
    return BaseObjSpace.exception_getclass(space, w_obj)

def exception_issubclass_w(space, w_cls1, w_cls2):
    return BaseObjSpace.exception_issubclass_w(space, w_cls1, w_cls2)

# ____________________________________________________________
# App-level interface

def issubclass(space, w_cls, w_klass_or_tuple):
    """Check whether a class 'cls' is a subclass (i.e., a derived class) of
another class.  When using a tuple as the second argument, check whether
'cls' is a subclass of any of the classes listed in the tuple."""
    result = abstract_issubclass_w(space, w_cls, w_klass_or_tuple, True)
    return space.newbool(result)

def isinstance(space, w_obj, w_klass_or_tuple):
    """Check whether an object is an instance of a class (or of a subclass
thereof).  When using a tuple as the second argument, check whether 'obj'
is an instance of any of the classes listed in the tuple."""
    result = abstract_isinstance_w(space, w_obj, w_klass_or_tuple, True)
    return space.newbool(result)

# avoid namespace pollution
app_issubclass = issubclass; del issubclass
app_isinstance = isinstance; del isinstance