""" Tests to make sure the newobject object (which defines Python 2-compatible ``__unicode__`` and ``next`` methods) is working. """ from __future__ import absolute_import, division from future import utils from future.builtins import object, str, next, int, super from future.utils import implements_iterator, python_2_unicode_compatible from future.tests.base import unittest, expectedFailurePY2 class TestNewObject(unittest.TestCase): def test_object_implements_py2_unicode_method(self): my_unicode_str = u'Unicode string: \u5b54\u5b50' class A(object): def __str__(self): return my_unicode_str a = A() self.assertEqual(len(str(a)), 18) if utils.PY2: self.assertTrue(hasattr(a, '__unicode__')) else: self.assertFalse(hasattr(a, '__unicode__')) self.assertEqual(str(a), my_unicode_str) self.assertTrue(isinstance(str(a).encode('utf-8'), bytes)) if utils.PY2: self.assertTrue(type(unicode(a)) == unicode) self.assertEqual(unicode(a), my_unicode_str) # Manual equivalent on Py2 without the decorator: if not utils.PY3: class B(object): def __unicode__(self): return u'Unicode string: \u5b54\u5b50' def __str__(self): return unicode(self).encode('utf-8') b = B() assert str(a) == str(b) def test_implements_py2_iterator(self): class Upper(object): def __init__(self, iterable): self._iter = iter(iterable) def __next__(self): # note the Py3 interface return next(self._iter).upper() def __iter__(self): return self self.assertEqual(list(Upper('hello')), list('HELLO')) # Try combining it with the next() function: class MyIter(object): def __next__(self): return 'Next!' def __iter__(self): return self itr = MyIter() self.assertEqual(next(itr), 'Next!') itr2 = MyIter() for i, item in enumerate(itr2): if i >= 10: break self.assertEqual(item, 'Next!') def test_implements_py2_nonzero(self): class EvenIsTrue(object): """ An integer that evaluates to True if even. """ def __init__(self, my_int): self.my_int = my_int def __bool__(self): return self.my_int % 2 == 0 def __add__(self, other): return type(self)(self.my_int + other) k = EvenIsTrue(5) self.assertFalse(k) self.assertFalse(bool(k)) self.assertTrue(k + 1) self.assertTrue(bool(k + 1)) self.assertFalse(k + 2) def test_int_implements_py2_nonzero(self): """ Tests whether the newint object provides a __nonzero__ method that maps to __bool__ in case the user redefines __bool__ in a subclass of newint. """ class EvenIsTrue(int): """ An integer that evaluates to True if even. """ def __bool__(self): return self % 2 == 0 def __add__(self, other): val = super().__add__(other) return type(self)(val) k = EvenIsTrue(5) self.assertFalse(k) self.assertFalse(bool(k)) self.assertTrue(k + 1) self.assertTrue(bool(k + 1)) self.assertFalse(k + 2) def test_non_iterator(self): """ The default behaviour of next(o) for a newobject o should be to raise a TypeError, as with the corresponding builtin object. """ o = object() with self.assertRaises(TypeError): next(o) def test_bool_empty_object(self): """ The default result of bool(newobject()) should be True, as with builtin objects. """ o = object() self.assertTrue(bool(o)) class MyClass(object): pass obj = MyClass() self.assertTrue(bool(obj)) def test_isinstance_object_subclass(self): """ This was failing before """ class A(object): pass a = A() class B(object): pass b = B() self.assertFalse(isinstance(a, B)) self.assertFalse(isinstance(b, A)) self.assertTrue(isinstance(a, A)) self.assertTrue(isinstance(b, B)) class C(A): pass c = C() self.assertTrue(isinstance(c, A)) self.assertFalse(isinstance(c, B)) self.assertFalse(isinstance(a, C)) self.assertFalse(isinstance(b, C)) self.assertTrue(isinstance(c, C)) @expectedFailurePY2 def test_types_isinstance_newobject(self): a = list() b = dict() c = set() self.assertTrue(isinstance(a, object)) self.assertTrue(isinstance(b, object)) self.assertTrue(isinstance(c, object)) # Old-style class instances on Py2 should still report as an instance # of object as usual on Py2: class D: pass d = D() self.assertTrue(isinstance(d, object)) e = object() self.assertTrue(isinstance(e, object)) class F(object): pass f = F() self.assertTrue(isinstance(f, object)) class G(F): pass g = G() self.assertTrue(isinstance(g, object)) class H(): pass h = H() self.assertTrue(isinstance(h, object)) def test_long_special_method(self): class A(object): def __int__(self): return 0 a = A() self.assertEqual(int(a), 0) if utils.PY2: self.assertEqual(long(a), 0) def test_multiple_inheritance(self): """ Issue #96 """ if utils.PY2: from collections import Container else: from collections.abc import Container class Base(object): pass class Foo(Base, Container): def __contains__(self, item): return False def test_with_metaclass_and_object(self): """ Issue #91 """ from future.utils import with_metaclass class MetaClass(type): pass class TestClass(with_metaclass(MetaClass, object)): pass def test_bool(self): """ Issue #211 """ from builtins import object class ResultSet(object): def __len__(self): return 0 self.assertTrue(bool(ResultSet()) is False) class ResultSet(object): def __len__(self): return 2 self.assertTrue(bool(ResultSet()) is True) def test_bool2(self): """ If __bool__ is defined, the presence or absence of __len__ should be irrelevant. """ from builtins import object class TrueThing(object): def __bool__(self): return True def __len__(self): raise RuntimeError('__len__ should not be called') self.assertTrue(bool(TrueThing())) class FalseThing(object): def __bool__(self): return False def __len__(self): raise RuntimeError('__len__ should not be called') self.assertFalse(bool(FalseThing())) def test_cannot_assign_new_attributes_to_object(self): """ New attributes cannot be assigned to object() instances in Python. The same should apply to newobject. """ from builtins import object with self.assertRaises(AttributeError): object().arbitrary_attribute_name = True if __name__ == '__main__': unittest.main()