From 82f9bb989e726c88e84265fc2399093cdcdfecf5 Mon Sep 17 00:00:00 2001 From: teocns <59549574+teocns@users.noreply.github.com> Date: Sun, 29 Dec 2024 15:30:00 -0600 Subject: [PATCH] =?UTF-8?q?Replace=20usage=20of=20functools.cached=5Fprope?= =?UTF-8?q?rty=20with=20DIY=20class=20for=20backwar=E2=80=A6=20(#613)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Replace usage of functools.cached_property with DIY class for backwards compatibility reasons Signed-off-by: Teo * ruff Signed-off-by: Teo --------- Signed-off-by: Teo --- agentops/client.py | 2 +- agentops/helpers.py | 19 ++++++++++++ tests/test_helpers.py | 67 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 tests/test_helpers.py diff --git a/agentops/client.py b/agentops/client.py index fb3e17937..8e0b6de52 100644 --- a/agentops/client.py +++ b/agentops/client.py @@ -14,7 +14,6 @@ import threading import traceback from decimal import Decimal -from functools import cached_property from typing import List, Optional, Tuple, Union from uuid import UUID, uuid4 @@ -28,6 +27,7 @@ from .meta_client import MetaClient from .session import Session, active_sessions from .singleton import conditional_singleton +from .helpers import cached_property @conditional_singleton diff --git a/agentops/helpers.py b/agentops/helpers.py index ca0c4f0e3..3e8df36e1 100644 --- a/agentops/helpers.py +++ b/agentops/helpers.py @@ -174,3 +174,22 @@ def wrapper(self, *args, **kwargs): return func(self, *args, **kwargs) return wrapper + + +class cached_property: + """ + Decorator that converts a method with a single self argument into a + property cached on the instance. + See: https://github.com/AgentOps-AI/agentops/issues/612 + """ + + def __init__(self, func): + self.func = func + self.__doc__ = func.__doc__ + + def __get__(self, instance, cls=None): + if instance is None: + return self + value = self.func(instance) + setattr(instance, self.func.__name__, value) + return value diff --git a/tests/test_helpers.py b/tests/test_helpers.py new file mode 100644 index 000000000..401bb5665 --- /dev/null +++ b/tests/test_helpers.py @@ -0,0 +1,67 @@ +import pytest +from agentops.helpers import cached_property + + +def test_cached_property(): + class TestClass: + def __init__(self): + self.compute_count = 0 + + @cached_property + def expensive_computation(self): + self.compute_count += 1 + return 42 + + # Create instance + obj = TestClass() + + # First access should compute the value + assert obj.expensive_computation == 42 + assert obj.compute_count == 1 + + # Second access should use cached value + assert obj.expensive_computation == 42 + assert obj.compute_count == 1 # Count shouldn't increase + + # Third access should still use cached value + assert obj.expensive_computation == 42 + assert obj.compute_count == 1 # Count shouldn't increase + + +def test_cached_property_different_instances(): + class TestClass: + def __init__(self): + self.compute_count = 0 + + @cached_property + def expensive_computation(self): + self.compute_count += 1 + return id(self) # Return unique id for each instance + + # Create two different instances + obj1 = TestClass() + obj2 = TestClass() + + # Each instance should compute its own value + val1 = obj1.expensive_computation + val2 = obj2.expensive_computation + + assert val1 != val2 # Values should be different + assert obj1.compute_count == 1 + assert obj2.compute_count == 1 + + # Accessing again should use cached values + assert obj1.expensive_computation == val1 + assert obj2.expensive_computation == val2 + assert obj1.compute_count == 1 # Counts shouldn't increase + assert obj2.compute_count == 1 + + +def test_cached_property_class_access(): + class TestClass: + @cached_property + def expensive_computation(self): + return 42 + + # Accessing via class should return the descriptor + assert isinstance(TestClass.expensive_computation, cached_property)