from __future__ import unicode_literals import copy import pickle import sys from unittest import TestCase from django.utils import six from django.utils.functional import SimpleLazyObject, empty class _ComplexObject(object): def __init__(self, name): self.name = name def __eq__(self, other): return self.name == other.name def __hash__(self): return hash(self.name) if six.PY3: def __bytes__(self): return ("I am _ComplexObject(%r)" % self.name).encode("utf-8") def __str__(self): return self.name else: def __str__(self): return b"I am _ComplexObject(%r)" % str(self.name) def __unicode__(self): return self.name def __repr__(self): return "_ComplexObject(%r)" % self.name complex_object = lambda: _ComplexObject("joe") class TestUtilsSimpleLazyObject(TestCase): """ Tests for SimpleLazyObject """ # Note that concrete use cases for SimpleLazyObject are also found in the # auth context processor tests (unless the implementation of that function # is changed). def test_equality(self): self.assertEqual(complex_object(), SimpleLazyObject(complex_object)) self.assertEqual(SimpleLazyObject(complex_object), complex_object()) def test_hash(self): # hash() equality would not be true for many objects, but it should be # for _ComplexObject self.assertEqual(hash(complex_object()), hash(SimpleLazyObject(complex_object))) def test_repr(self): # First, for an unevaluated SimpleLazyObject x = SimpleLazyObject(complex_object) # __repr__ contains __repr__ of setup function and does not evaluate # the SimpleLazyObject self.assertEqual("" % complex_object, repr(x)) self.assertEqual(empty, x._wrapped) # Second, for an evaluated SimpleLazyObject name = x.name # evaluate self.assertIsInstance(x._wrapped, _ComplexObject) # __repr__ contains __repr__ of wrapped object self.assertEqual("" % x._wrapped, repr(x)) def test_bytes(self): self.assertEqual(b"I am _ComplexObject('joe')", bytes(SimpleLazyObject(complex_object))) def test_text(self): self.assertEqual("joe", six.text_type(SimpleLazyObject(complex_object))) def test_class(self): # This is important for classes that use __class__ in things like # equality tests. self.assertEqual(_ComplexObject, SimpleLazyObject(complex_object).__class__) def test_deepcopy(self): # Check that we *can* do deep copy, and that it returns the right # objects. # First, for an unevaluated SimpleLazyObject s = SimpleLazyObject(complex_object) self.assertIs(s._wrapped, empty) s2 = copy.deepcopy(s) # something has gone wrong is s is evaluated self.assertIs(s._wrapped, empty) self.assertEqual(s2, complex_object()) # Second, for an evaluated SimpleLazyObject name = s.name # evaluate self.assertIsNot(s._wrapped, empty) s3 = copy.deepcopy(s) self.assertEqual(s3, complex_object()) def test_none(self): i = [0] def f(): i[0] += 1 return None x = SimpleLazyObject(f) self.assertEqual(str(x), "None") self.assertEqual(i, [1]) self.assertEqual(str(x), "None") self.assertEqual(i, [1]) def test_bool(self): x = SimpleLazyObject(lambda: 3) self.assertTrue(x) x = SimpleLazyObject(lambda: 0) self.assertFalse(x) def test_pickle_complex(self): # See ticket #16563 x = SimpleLazyObject(complex_object) pickled = pickle.dumps(x) unpickled = pickle.loads(pickled) self.assertEqual(unpickled, x) self.assertEqual(six.text_type(unpickled), six.text_type(x)) self.assertEqual(unpickled.name, x.name) def test_dict(self): # See ticket #18447 lazydict = SimpleLazyObject(lambda: {'one': 1}) self.assertEqual(lazydict['one'], 1) lazydict['one'] = -1 self.assertEqual(lazydict['one'], -1) self.assertTrue('one' in lazydict) self.assertFalse('two' in lazydict) self.assertEqual(len(lazydict), 1) del lazydict['one'] with self.assertRaises(KeyError): lazydict['one'] def test_trace(self): # See ticket #19456 old_trace_func = sys.gettrace() try: def trace_func(frame, event, arg): frame.f_locals['self'].__class__ if old_trace_func is not None: old_trace_func(frame, event, arg) sys.settrace(trace_func) SimpleLazyObject(None) finally: sys.settrace(old_trace_func) def test_not_equal(self): lazy1 = SimpleLazyObject(lambda: 2) lazy2 = SimpleLazyObject(lambda: 2) lazy3 = SimpleLazyObject(lambda: 3) self.assertEqual(lazy1, lazy2) self.assertNotEqual(lazy1, lazy3) self.assertTrue(lazy1 != lazy3) self.assertFalse(lazy1 != lazy2) def test_pickle_py2_regression(self): from django.contrib.auth.models import User # See ticket #20212 user = User.objects.create_user('johndoe', 'john@example.com', 'pass') x = SimpleLazyObject(lambda: user) # This would fail with "TypeError: can't pickle instancemethod objects", # only on Python 2.X. pickled = pickle.dumps(x) # Try the variant protocol levels. pickled = pickle.dumps(x, 0) pickled = pickle.dumps(x, 1) pickled = pickle.dumps(x, 2) if six.PY2: import cPickle # This would fail with "TypeError: expected string or Unicode object, NoneType found". pickled = cPickle.dumps(x) def test_list_set(self): lazy_list = SimpleLazyObject(lambda: [1, 2, 3, 4, 5]) lazy_set = SimpleLazyObject(lambda: set([1, 2, 3, 4])) self.assertTrue(1 in lazy_list) self.assertTrue(1 in lazy_set) self.assertFalse(6 in lazy_list) self.assertFalse(6 in lazy_set) self.assertEqual(len(lazy_list), 5) self.assertEqual(len(lazy_set), 4)