[pypy-svn] r57193 - in pypy/dist/pypy: interpreter interpreter/test module/__builtin__ module/__builtin__/test objspace/flow objspace/flow/test
arigo at codespeak.net
arigo at codespeak.net
Mon Aug 11 12:47:00 CEST 2008
Author: arigo
Date: Mon Aug 11 12:46:58 2008
New Revision: 57193
Added:
pypy/dist/pypy/module/__builtin__/abstractinst.py
- copied unchanged from r57133, pypy/branch/isinstance-refactor/pypy/module/__builtin__/abstractinst.py
pypy/dist/pypy/module/__builtin__/test/test_abstractinst.py
- copied unchanged from r57133, pypy/branch/isinstance-refactor/pypy/module/__builtin__/test/test_abstractinst.py
Modified:
pypy/dist/pypy/interpreter/baseobjspace.py
pypy/dist/pypy/interpreter/error.py
pypy/dist/pypy/interpreter/function.py
pypy/dist/pypy/interpreter/test/test_function.py
pypy/dist/pypy/module/__builtin__/__init__.py
pypy/dist/pypy/module/__builtin__/interp_classobj.py
pypy/dist/pypy/module/__builtin__/operation.py
pypy/dist/pypy/module/__builtin__/test/test_classobj.py
pypy/dist/pypy/objspace/flow/objspace.py
pypy/dist/pypy/objspace/flow/test/test_objspace.py
Log:
Merge the isinstance-refactor branch:
* merge the two variants of "abstract isinstance" checking.
* add tests for, hopefully, all the various obscure cases of
isinstance() and issubclass()
* implement them in a new module, abstractinst.py, following
the CPython logic closely
* the BaseObjSpace class only provides minimal implementations
by default now as space.abstract_isxxx_w(), but the __buitin__
module patches the space to install the full version. A bit
hackish but it looks less messy overall - no flow space hacks
needed, and no need for strange imports from BaseObjSpace
to pypy.module.__builtin__.interp_classobj.
* the overzealous test compliance hack is gone, so I guess
we'll need to do a small edit in one of CPython's tests.
* minor speed-up in old-style instances' __getattribute__().
* one more test for the flow space.
Modified: pypy/dist/pypy/interpreter/baseobjspace.py
==============================================================================
--- pypy/dist/pypy/interpreter/baseobjspace.py (original)
+++ pypy/dist/pypy/interpreter/baseobjspace.py Mon Aug 11 12:46:58 2008
@@ -654,16 +654,13 @@
def exception_match(self, w_exc_type, w_check_class):
"""Checks if the given exception type matches 'w_check_class'."""
if self.is_w(w_exc_type, w_check_class):
- return True
- if self.is_true(self.abstract_issubclass(w_exc_type, w_check_class)):
- return True
-
- if self.is_true(self.isinstance(w_check_class, self.w_tuple)):
- exclst_w = self.unpacktuple(w_check_class)
- for w_e in exclst_w:
- if self.exception_match(w_exc_type, w_e):
- return True
- return False
+ return True # fast path (also here to handle string exceptions)
+ try:
+ return self.abstract_issubclass_w(w_exc_type, w_check_class)
+ except OperationError, e:
+ if e.match(self, self.w_TypeError): # string exceptions maybe
+ return False
+ raise
def call_obj_args(self, w_callable, w_obj, args):
if not self.config.objspace.disable_call_speedhacks:
@@ -690,8 +687,8 @@
func = w_func.w_function
if isinstance(func, Function):
return func.funccall(w_inst, *args_w)
- elif args_w and self.is_true(
- self.abstract_isinstance(args_w[0], w_func.w_class)):
+ elif args_w and (
+ self.abstract_isinstance_w(args_w[0], w_func.w_class)):
w_func = w_func.w_function
if isinstance(w_func, Function):
@@ -713,9 +710,9 @@
# reuse callable stack place for w_inst
frame.settopvalue(w_inst, nargs)
nargs += 1
- elif nargs > 0 and self.is_true(
- self.abstract_isinstance(frame.peekvalue(nargs-1), # :-(
- w_func.w_class)):
+ elif nargs > 0 and (
+ self.abstract_isinstance_w(frame.peekvalue(nargs-1), # :-(
+ w_func.w_class)):
w_func = w_func.w_function
if isinstance(w_func, Function):
@@ -767,61 +764,34 @@
w_objtype = self.type(w_obj)
return self.issubtype(w_objtype, w_type)
- def abstract_issubclass(self, w_obj, w_cls, failhard=False):
- try:
- return self.issubtype(w_obj, w_cls)
- except OperationError, e:
- if not e.match(self, self.w_TypeError):
- raise
- try:
- self.getattr(w_cls, self.wrap('__bases__')) # type sanity check
- return self.recursive_issubclass(w_obj, w_cls)
- except OperationError, e:
- if failhard or not (e.match(self, self.w_TypeError) or
- e.match(self, self.w_AttributeError)):
- raise
- else:
- return self.w_False
-
- def recursive_issubclass(self, w_obj, w_cls):
- if self.is_w(w_obj, w_cls):
- return self.w_True
- for w_base in self.unpackiterable(self.getattr(w_obj,
- self.wrap('__bases__'))):
- if self.is_true(self.recursive_issubclass(w_base, w_cls)):
- return self.w_True
- return self.w_False
-
- def abstract_isinstance(self, w_obj, w_cls):
- try:
- return self.isinstance(w_obj, w_cls)
- except OperationError, e:
- if not e.match(self, self.w_TypeError):
- raise
- try:
- w_objcls = self.getattr(w_obj, self.wrap('__class__'))
- return self.abstract_issubclass(w_objcls, w_cls)
- except OperationError, e:
- if not (e.match(self, self.w_TypeError) or
- e.match(self, self.w_AttributeError)):
- raise
- return self.w_False
-
- def abstract_isclass(self, w_obj):
- if self.is_true(self.isinstance(w_obj, self.w_type)):
- return self.w_True
- if self.findattr(w_obj, self.wrap('__bases__')) is not None:
- return self.w_True
- else:
- return self.w_False
+ def abstract_issubclass_w(self, w_cls1, w_cls2):
+ # Equivalent to 'issubclass(cls1, cls2)'. The code below only works
+ # for the simple case (new-style class, new-style class).
+ # This method is patched with the full logic by the __builtin__
+ # module when it is loaded.
+ return self.is_true(self.issubtype(w_cls1, w_cls2))
+
+ def abstract_isinstance_w(self, w_obj, w_cls):
+ # Equivalent to 'isinstance(obj, cls)'. The code below only works
+ # for the simple case (new-style instance, new-style class).
+ # This method is patched with the full logic by the __builtin__
+ # module when it is loaded.
+ return self.is_true(self.isinstance(w_obj, w_cls))
+
+ def abstract_isclass_w(self, w_obj):
+ # Equivalent to 'isinstance(obj, type)'. The code below only works
+ # for the simple case (new-style instance without special stuff).
+ # This method is patched with the full logic by the __builtin__
+ # module when it is loaded.
+ return self.is_true(self.isinstance(w_obj, self.w_type))
def abstract_getclass(self, w_obj):
- try:
- return self.getattr(w_obj, self.wrap('__class__'))
- except OperationError, e:
- if e.match(self, self.w_TypeError) or e.match(self, self.w_AttributeError):
- return self.type(w_obj)
- raise
+ # Equivalent to 'obj.__class__'. The code below only works
+ # for the simple case (new-style instance without special stuff).
+ # This method is patched with the full logic by the __builtin__
+ # module when it is loaded.
+ return self.type(w_obj)
+
def eval(self, expression, w_globals, w_locals):
"NOT_RPYTHON: For internal debugging."
Modified: pypy/dist/pypy/interpreter/error.py
==============================================================================
--- pypy/dist/pypy/interpreter/error.py (original)
+++ pypy/dist/pypy/interpreter/error.py Mon Aug 11 12:46:58 2008
@@ -151,15 +151,14 @@
while space.is_true(space.isinstance(w_type, space.w_tuple)):
w_type = space.getitem(w_type, space.wrap(0))
- if space.is_true(space.abstract_isclass(w_type)):
+ if space.abstract_isclass_w(w_type):
if space.is_w(w_value, space.w_None):
# raise Type: we assume we have to instantiate Type
w_value = space.call_function(w_type)
w_type = space.abstract_getclass(w_value)
else:
w_valuetype = space.abstract_getclass(w_value)
- if space.is_true(space.abstract_issubclass(w_valuetype,
- w_type)):
+ if space.abstract_issubclass_w(w_valuetype, w_type):
# raise Type, Instance: let etype be the exact type of value
w_type = w_valuetype
else:
Modified: pypy/dist/pypy/interpreter/function.py
==============================================================================
--- pypy/dist/pypy/interpreter/function.py (original)
+++ pypy/dist/pypy/interpreter/function.py Mon Aug 11 12:46:58 2008
@@ -322,8 +322,8 @@
# unbound method
w_firstarg = args.firstarg()
- if w_firstarg is not None and space.is_true(
- space.abstract_isinstance(w_firstarg, self.w_class)):
+ if w_firstarg is not None and (
+ space.abstract_isinstance_w(w_firstarg, self.w_class)):
pass # ok
else:
myname = self.getname(space,"")
@@ -351,7 +351,7 @@
# only allow binding to a more specific class than before
if (w_cls is not None and
not space.is_w(w_cls, space.w_None) and
- not space.is_true(space.abstract_issubclass(w_cls, self.w_class))):
+ not space.abstract_issubclass_w(w_cls, self.w_class)):
return space.wrap(self) # subclass test failed
else:
return descr_function_get(space, self.w_function, w_obj, w_cls)
Modified: pypy/dist/pypy/interpreter/test/test_function.py
==============================================================================
--- pypy/dist/pypy/interpreter/test/test_function.py (original)
+++ pypy/dist/pypy/interpreter/test/test_function.py Mon Aug 11 12:46:58 2008
@@ -356,6 +356,46 @@
__metaclass__ = A().foo
assert Fun[:2] == ('Fun', ())
+ def test_unbound_abstract_typecheck(self):
+ import new
+ def f(*args):
+ return args
+ m = new.instancemethod(f, None, "foobar")
+ raises(TypeError, m)
+ raises(TypeError, m, None)
+ raises(TypeError, m, "egg")
+
+ m = new.instancemethod(f, None, (str, int)) # really obscure...
+ assert m(4) == (4,)
+ assert m("uh") == ("uh",)
+ raises(TypeError, m, [])
+
+ class MyBaseInst(object):
+ pass
+ class MyInst(MyBaseInst):
+ def __init__(self, myclass):
+ self.myclass = myclass
+ def __class__(self):
+ if self.myclass is None:
+ raise AttributeError
+ return self.myclass
+ __class__ = property(__class__)
+ class MyClass(object):
+ pass
+ BBase = MyClass()
+ BSub1 = MyClass()
+ BSub2 = MyClass()
+ BBase.__bases__ = ()
+ BSub1.__bases__ = (BBase,)
+ BSub2.__bases__ = (BBase,)
+ x = MyInst(BSub1)
+ m = new.instancemethod(f, None, BSub1)
+ assert m(x) == (x,)
+ raises(TypeError, m, MyInst(BBase))
+ raises(TypeError, m, MyInst(BSub2))
+ raises(TypeError, m, MyInst(None))
+ raises(TypeError, m, MyInst(42))
+
class TestMethod:
def setup_method(self, method):
Modified: pypy/dist/pypy/module/__builtin__/__init__.py
==============================================================================
--- pypy/dist/pypy/module/__builtin__/__init__.py (original)
+++ pypy/dist/pypy/module/__builtin__/__init__.py Mon Aug 11 12:46:58 2008
@@ -87,8 +87,8 @@
'coerce' : 'operation.coerce',
'divmod' : 'operation.divmod',
'_issubtype' : 'operation._issubtype',
- 'issubclass' : 'operation.issubclass',
- 'isinstance' : 'operation.isinstance',
+ 'issubclass' : 'abstractinst.app_issubclass',
+ 'isinstance' : 'abstractinst.app_isinstance',
'getattr' : 'operation.getattr',
'setattr' : 'operation.setattr',
'delattr' : 'operation.delattr',
@@ -151,6 +151,12 @@
# xxx hide the installer
space.delitem(self.w_dict, space.wrap(name))
del self.loaders[name]
+ # install the more general version of isinstance() & co. in the space
+ from pypy.module.__builtin__ import abstractinst as ab
+ space.abstract_isinstance_w = ab.abstract_isinstance_w.__get__(space)
+ space.abstract_issubclass_w = ab.abstract_issubclass_w.__get__(space)
+ space.abstract_isclass_w = ab.abstract_isclass_w.__get__(space)
+ space.abstract_getclass = ab.abstract_getclass.__get__(space)
def startup(self, space):
# install zipimport hook if --withmod-zipimport is used
Modified: pypy/dist/pypy/module/__builtin__/interp_classobj.py
==============================================================================
--- pypy/dist/pypy/module/__builtin__/interp_classobj.py (original)
+++ pypy/dist/pypy/module/__builtin__/interp_classobj.py Mon Aug 11 12:46:58 2008
@@ -85,6 +85,16 @@
space.wrap("__bases__ items must be classes"))
self.bases_w = bases_w
+ def is_subclass_of(self, other):
+ assert isinstance(other, W_ClassObject)
+ if self is other:
+ return True
+ for base in self.bases_w:
+ assert isinstance(base, W_ClassObject)
+ if base.is_subclass_of(other):
+ return True
+ return False
+
def lookup(self, space, w_attr):
# returns w_value or interplevel None
w_result = space.finditem(self.w_dict, w_attr)
@@ -225,6 +235,7 @@
except OperationError, e:
if e.match(space, space.w_AttributeError):
return space.w_NotImplemented
+ raise
else:
if w_meth is None:
return space.w_NotImplemented
@@ -314,11 +325,6 @@
def getattr(self, space, w_name, exc=True):
- name = space.str_w(w_name)
- if name == "__dict__":
- return self.w_dict
- elif name == "__class__":
- return self.w_class
w_result = space.finditem(self.w_dict, w_name)
if w_result is not None:
return w_result
@@ -328,7 +334,7 @@
raise OperationError(
space.w_AttributeError,
space.wrap("%s instance has no attribute %s" % (
- self.w_class.name, name)))
+ self.w_class.name, space.str_w(w_name))))
else:
return None
w_descr_get = space.lookup(w_value, '__get__')
@@ -337,7 +343,12 @@
return space.call_function(w_descr_get, w_value, self, self.w_class)
def descr_getattribute(self, space, w_attr):
- #import pdb; pdb.set_trace()
+ name = space.str_w(w_attr)
+ if len(name) >= 8 and name[0] == '_':
+ if name == "__dict__":
+ return self.w_dict
+ elif name == "__class__":
+ return self.w_class
try:
return self.getattr(space, w_attr)
except OperationError, e:
Modified: pypy/dist/pypy/module/__builtin__/operation.py
==============================================================================
--- pypy/dist/pypy/module/__builtin__/operation.py (original)
+++ pypy/dist/pypy/module/__builtin__/operation.py Mon Aug 11 12:46:58 2008
@@ -220,95 +220,3 @@
function). Note that classes are callable."""
return space.callable(w_object)
-
-
-def _recursive_issubclass(space, w_cls, w_klass_or_tuple): # returns interp-level bool
- if space.is_w(w_cls, w_klass_or_tuple):
- return True
- try:
- w_bases = space.getattr(w_cls, space.wrap("__bases__"))
- except OperationError, e:
- if e.match(space, space.w_AttributeError):
- return False
- else:
- raise
- w_iterator = space.iter(w_bases)
- while True:
- try:
- w_base = space.next(w_iterator)
- except OperationError, e:
- if not e.match(space, space.w_StopIteration):
- raise
- break
- if _recursive_issubclass(space, w_base, w_klass_or_tuple):
- return True
- return False
-
-def _issubclass(space, w_cls, w_klass_or_tuple, check_cls, depth): # returns interp-level bool
- if depth == 0:
- # XXX overzealous test compliance hack
- raise OperationError(space.w_RuntimeError, space.wrap("maximum recursion depth exceeded"))
- if space.is_true(space.issubtype(space.type(w_klass_or_tuple), space.w_tuple)):
- w_iter = space.iter(w_klass_or_tuple)
- while True:
- try:
- w_klass = space.next(w_iter)
- except OperationError, e:
- if not e.match(space, space.w_StopIteration):
- raise
- break
- if _issubclass(space, w_cls, w_klass, True, depth - 1):
- return True
- return False
-
- try:
- return space.is_true(space.issubtype(w_cls, w_klass_or_tuple))
- except OperationError, e:
- if e.match(space, space.w_TypeError):
- w_bases = space.wrap('__bases__')
- if check_cls:
- try:
- space.getattr(w_cls, w_bases)
- except OperationError, e:
- if not e.match(space, space.w_AttributeError):
- raise
- raise OperationError(space.w_TypeError, space.wrap('arg 1 must be a class or type'))
- try:
- space.getattr(w_klass_or_tuple, w_bases)
- except OperationError, e:
- if not e.match(space, space.w_AttributeError):
- raise
- raise OperationError(space.w_TypeError, space.wrap('arg 2 must be a class or type or a tuple thereof'))
- return _recursive_issubclass(space, w_cls, w_klass_or_tuple)
- else:
- raise
-
-
-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."""
- return space.wrap(issubclass_w(space, w_cls, w_klass_or_tuple))
-
-def issubclass_w(space, w_cls, w_klass_or_tuple):
- return _issubclass(space, w_cls, w_klass_or_tuple, True, space.sys.recursionlimit)
-
-
-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."""
- w_objtype = space.type(w_obj)
- if issubclass_w(space, w_objtype, w_klass_or_tuple):
- return space.w_True
- try:
- w_objcls = space.getattr(w_obj, space.wrap("__class__"))
- except OperationError, e:
- if e.match(space, space.w_AttributeError):
- return space.w_False
- else:
- raise
- if space.is_w(w_objcls, w_objtype):
- return space.w_False
- else:
- return space.wrap(_issubclass(space, w_objcls, w_klass_or_tuple, False, space.sys.recursionlimit))
Modified: pypy/dist/pypy/module/__builtin__/test/test_classobj.py
==============================================================================
--- pypy/dist/pypy/module/__builtin__/test/test_classobj.py (original)
+++ pypy/dist/pypy/module/__builtin__/test/test_classobj.py Mon Aug 11 12:46:58 2008
@@ -15,6 +15,35 @@
assert a.__class__ is A
assert a.__dict__ == {'b': 2}
+ def test_isinstance(self):
+ class A:
+ pass
+ class B(A):
+ pass
+ class C(A):
+ pass
+ assert isinstance(B(), A)
+ assert isinstance(B(), B)
+ assert not isinstance(B(), C)
+ assert not isinstance(A(), B)
+ assert isinstance(B(), (A, C))
+ assert isinstance(B(), (C, (), (C, B)))
+ assert not isinstance(B(), ())
+
+ def test_issubclass(self):
+ class A:
+ pass
+ class B(A):
+ pass
+ class C(A):
+ pass
+ assert issubclass(A, A)
+ assert not issubclass(A, B)
+ assert not issubclass(A, C)
+ assert issubclass(B, A)
+ assert issubclass(B, B)
+ assert not issubclass(B, C)
+
def test_mutate_class_special(self):
class A:
a = 1
@@ -415,6 +444,21 @@
raises(TypeError, "a + 1.1")
assert l == [1, 1.1]
+ def test_binaryop_raises(self):
+ class A:
+ def __add__(self, other):
+ raise this_exception
+ def __iadd__(self, other):
+ raise this_exception
+
+ a = A()
+ this_exception = ValueError
+ raises(ValueError, "a + 1")
+ raises(ValueError, "a += 1")
+ this_exception = AttributeError
+ raises(AttributeError, "a + 1")
+ raises(AttributeError, "a += 1")
+
def test_iadd(self):
class A:
def __init__(self):
@@ -618,13 +662,14 @@
def test_catch_attributeerror_of_descriptor(self):
def booh(self):
- raise AttributeError, "booh"
+ raise this_exception, "booh"
class E:
__eq__ = property(booh)
__iadd__ = property(booh)
e = E()
+ this_exception = AttributeError
raises(TypeError, "e += 1")
# does not crash
E() == E()
@@ -632,6 +677,9 @@
__init__ = property(booh)
raises(AttributeError, I)
+ this_exception = ValueError
+ raises(ValueError, "e += 1")
+
def test_multiple_inheritance_more(self):
l = []
class A: # classic class
@@ -686,6 +734,10 @@
assert Y() != X()
def test_assignment_to_del(self):
+ import sys
+ if not hasattr(sys, 'pypy_objspaceclass'):
+ skip("assignment to __del__ doesn't give a warning in CPython")
+
import warnings
warnings.simplefilter('error', RuntimeWarning)
Modified: pypy/dist/pypy/objspace/flow/objspace.py
==============================================================================
--- pypy/dist/pypy/objspace/flow/objspace.py (original)
+++ pypy/dist/pypy/objspace/flow/objspace.py Mon Aug 11 12:46:58 2008
@@ -218,18 +218,6 @@
return ecls
return None
- def abstract_issubclass(self, w_obj, w_cls, failhard=False):
- return self.issubtype(w_obj, w_cls)
-
- def abstract_isinstance(self, w_obj, w_cls):
- return self.isinstance(w_obj, w_cls)
-
- def abstract_isclass(self, w_obj):
- return self.isinstance(w_obj, self.w_type)
-
- def abstract_getclass(self, w_obj):
- return self.type(w_obj)
-
def build_flow(self, func, constargs={}):
"""
Modified: pypy/dist/pypy/objspace/flow/test/test_objspace.py
==============================================================================
--- pypy/dist/pypy/objspace/flow/test/test_objspace.py (original)
+++ pypy/dist/pypy/objspace/flow/test/test_objspace.py Mon Aug 11 12:46:58 2008
@@ -1,7 +1,7 @@
import new
import py
from pypy.objspace.flow.model import Constant, Block, Link, Variable, traverse
-from pypy.objspace.flow.model import flatten
+from pypy.objspace.flow.model import flatten, mkentrymap
from pypy.interpreter.argument import Arguments
from pypy.translator.simplify import simplify_graph
from pypy.objspace.flow.objspace import FlowObjSpace
@@ -416,6 +416,27 @@
x = self.codetest(self.catch_simple_call)
#__________________________________________________________
+ def multiple_catch_simple_call():
+ try:
+ user_defined_function()
+ except (IndexError, OSError):
+ return -1
+ return 0
+
+ def test_multiple_catch_simple_call(self):
+ graph = self.codetest(self.multiple_catch_simple_call)
+ simplify_graph(graph)
+ assert self.all_operations(graph) == {'simple_call': 1}
+ entrymap = mkentrymap(graph)
+ links = entrymap[graph.returnblock]
+ assert len(links) == 3
+ assert (dict.fromkeys([link.exitcase for link in links]) ==
+ dict.fromkeys([None, IndexError, OSError]))
+ links = entrymap[graph.exceptblock]
+ assert len(links) == 1
+ assert links[0].exitcase is Exception
+
+ #__________________________________________________________
def dellocal():
x = 1
del x
More information about the Pypy-commit
mailing list