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 @@ from random import choice
import sys
from test import support
import unittest
import unittest.mock
from weakref import proxy
import contextlib
try:
@ -1190,6 +1191,41 @@ class TestLRU:
self.assertEqual(misses, 4)
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):
# Test to make sure the LRU cache code isn't thrown-off by
# caching the built-in len() function. Since len() can be