mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
* vm: drop trailing space from RecursionError text Format Python-limit and native-stack RecursionError messages through one helper so empty context is not a trailing space or an empty string. Assisted-by: Grok 4.6 * test: deepen nested hash snippets for release stack frames The cold RecursionError helper thins with_recursion's hot frame, so 100_000 nested hashes no longer trip the native stack guard in release. Assisted-by: Grok 4.6 * vm: let RecursionError callers own the where-clause space The helper concatenates an empty suffix or one that already starts with a space. Call sites supply that space. Assisted-by: Grok 4.6
77 lines
1.8 KiB
Python
77 lines
1.8 KiB
Python
import sys
|
|
|
|
from testutils import assert_raises
|
|
|
|
|
|
class A:
|
|
pass
|
|
|
|
|
|
assert type(hash(None)) is int
|
|
assert type(hash(object())) is int
|
|
assert type(hash(A())) is int
|
|
assert type(hash(1)) is int
|
|
assert type(hash(1.1)) is int
|
|
assert type(hash("")) is int
|
|
|
|
|
|
class Evil:
|
|
def __hash__(self):
|
|
return 1 << 63
|
|
|
|
|
|
assert hash(Evil()) == 4
|
|
|
|
with assert_raises(TypeError):
|
|
hash({})
|
|
|
|
with assert_raises(TypeError):
|
|
hash(set())
|
|
|
|
with assert_raises(TypeError):
|
|
hash([])
|
|
|
|
# Hashing a deeply nested tuple must not run off the native stack: the hash
|
|
# slot dispatch is what recurses, so that is where the depth is checked.
|
|
|
|
if sys.implementation.name == "rustpython":
|
|
# Deep enough to reach the native stack guard in release; CPython, which
|
|
# also runs this snippet, dies on the same value.
|
|
deep_tuple = ()
|
|
for _ in range(500_000):
|
|
deep_tuple = (deep_tuple,)
|
|
with assert_raises(RecursionError):
|
|
hash(deep_tuple)
|
|
# a dict key and a set member are hashed on insertion, same dispatch
|
|
with assert_raises(RecursionError):
|
|
{deep_tuple: 1}
|
|
with assert_raises(RecursionError):
|
|
{deep_tuple}
|
|
|
|
|
|
def test_unhashable_names_the_type_as_written():
|
|
# The message names the type the way `tp_name` does, so a type defined in a
|
|
# module carries the module with it.
|
|
import array
|
|
import collections
|
|
|
|
for value, name in [
|
|
(array.array("B", b"a"), "array.array"),
|
|
(collections.deque(), "collections.deque"),
|
|
([], "list"),
|
|
(bytearray(), "bytearray"),
|
|
(Unhashable(), "Unhashable"),
|
|
]:
|
|
try:
|
|
hash(value)
|
|
raise AssertionError(f"hashed {name}")
|
|
except TypeError as e:
|
|
assert str(e) == f"unhashable type: '{name}'", e
|
|
|
|
|
|
class Unhashable:
|
|
__hash__ = None
|
|
|
|
|
|
test_unhashable_names_the_type_as_written()
|