From b1b1090bd9f62fc5695b54512d48cb5827e6f18b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 16 Jun 2024 11:40:20 -0500 Subject: [PATCH] Only calculate hash once for Marker objects (#513) --- voluptuous/schema_builder.py | 20 +++++++++++++------- voluptuous/tests/tests.py | 1 + 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/voluptuous/schema_builder.py b/voluptuous/schema_builder.py index dba71ba..ca134ca 100644 --- a/voluptuous/schema_builder.py +++ b/voluptuous/schema_builder.py @@ -9,7 +9,7 @@ import typing from collections.abc import Generator from contextlib import contextmanager -from functools import wraps +from functools import cache, wraps from voluptuous import error as er from voluptuous.error import Error @@ -1026,6 +1026,8 @@ class Marker(object): introspected by any external tool, for example to generate schema documentation. """ + __slots__ = ('schema', '_schema', 'msg', 'description', '__hash__') + def __init__( self, schema_: Schemable, @@ -1036,6 +1038,7 @@ def __init__( self._schema = Schema(schema_) self.msg = msg self.description = description + self.__hash__ = cache(lambda: hash(schema_)) # type: ignore[method-assign] def __call__(self, v): try: @@ -1056,9 +1059,6 @@ def __lt__(self, other): return self.schema < other.schema return self.schema < other - def __hash__(self): - return hash(self.schema) - def __eq__(self, other): return self.schema == other @@ -1244,6 +1244,15 @@ class Remove(Marker): [1, 2, 3, 5, '7'] """ + def __init__( + self, + schema_: Schemable, + msg: typing.Optional[str] = None, + description: typing.Optional[str] = None, + ) -> None: + super().__init__(schema_, msg, description) + self.__hash__ = cache(lambda: object.__hash__(self)) # type: ignore[method-assign] + def __call__(self, schema: Schemable): super(Remove, self).__call__(schema) return self.__class__ @@ -1251,9 +1260,6 @@ def __call__(self, schema: Schemable): def __repr__(self): return "Remove(%r)" % (self.schema,) - def __hash__(self): - return object.__hash__(self) - def message( default: typing.Optional[str] = None, diff --git a/voluptuous/tests/tests.py b/voluptuous/tests/tests.py index 7286b31..f6d6784 100644 --- a/voluptuous/tests/tests.py +++ b/voluptuous/tests/tests.py @@ -1072,6 +1072,7 @@ def test_marker_hashable(): assert definition.get('y') == float assert Required('x') == Required('x') assert Required('x') != Required('y') + assert hash(Required('x').schema) == hash(Required('x')) # Remove markers are not hashable assert definition.get('j') is None