Issue #29200: Add test for lru cache only calling __hash__ once

This commit is contained in:
Raymond Hettinger 2017-01-07 20:44:48 -08:00
parent 4f5c6a27d8
commit d191ef25c1
1 changed files with 36 additions and 0 deletions

View File

@ -8,6 +8,7 @@
import sys import sys
from test import support from test import support
import unittest import unittest
import unittest.mock
from weakref import proxy from weakref import proxy
import contextlib import contextlib
try: try:
@ -1190,6 +1191,41 @@ def f(x):
self.assertEqual(misses, 4) self.assertEqual(misses, 4)
self.assertEqual(currsize, 2) self.assertEqual(currsize, 2)
def test_lru_hash_only_once(self):
# To protect against weird reentrancy bugs and to improve
# efficiency when faced with slow __hash__ methods, the
# LRU cache guarantees that it will only call __hash__
# only once per use as an argument to the cached function.
@self.module.lru_cache(maxsize=1)
def f(x, y):
return x * 3 + y
# Simulate the integer 5
mock_int = unittest.mock.Mock()
mock_int.__mul__ = unittest.mock.Mock(return_value=15)
mock_int.__hash__ = unittest.mock.Mock(return_value=999)
# Add to cache: One use as an argument gives one call
assert f(mock_int, 1) == 16
assert mock_int.__hash__.call_count == 1
assert f.cache_info() == (0, 1, 1, 1)
# Cache hit: One use as an argument gives one additional call
assert f(mock_int, 1) == 16
assert mock_int.__hash__.call_count == 2
assert f.cache_info() == (1, 1, 1, 1)
# Cache eviction: No use as an argument gives no additonal call
assert f(6, 2) == 20
assert mock_int.__hash__.call_count == 2
assert f.cache_info() == (1, 2, 1, 1)
# Cache miss: One use as an argument gives one additional call
assert f(mock_int, 1) == 16
assert mock_int.__hash__.call_count == 3
assert f.cache_info() == (1, 3, 1, 1)
def test_lru_reentrancy_with_len(self): def test_lru_reentrancy_with_len(self):
# Test to make sure the LRU cache code isn't thrown-off by # Test to make sure the LRU cache code isn't thrown-off by
# caching the built-in len() function. Since len() can be # caching the built-in len() function. Since len() can be