How to get class name from an annotation for static method?

80 views Asked by At

Here is my code:

import os
import pickle
import hashlib

def read_file(path):
    # Implement your read_file logic here
    pass

def write_file(path, content):
    # Implement your write_file logic here
    pass

class CachedStaticMethod:
    path = "cache"
    
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        if instance is None:
            return self  # Return the decorator instance for staticmethod to use
        return self.func.__get__(instance, owner)  # For regular methods

    def __call__(self, *args, **kwargs):
        cache_path = self.__get_path(*args, **kwargs)

        try:
            content = read_file(cache_path)
            result = pickle.loads(content)
        except Exception as e:
            result = self.func(*args, **kwargs)
            content = pickle.dumps(result)
            write_file(cache_path, content)
        return result    

    def __get_path(self, *args, **kwargs):
        class_name = self.func.__qualname__.split('.<locals>', 1)[0]
        function_name = self.func.__name__
        
        hash_input = f"{class_name}.{function_name}({args}, {kwargs})".encode("utf-8")
        hash_value = hashlib.md5(hash_input).hexdigest()
        filename = f"{hash_value}.cache"
        return os.path.join(self.path, filename)

class MyClass:
    @CachedStaticMethod
    @staticmethod
    def my_static_method(param1: int, param2: str) -> float:
        """
        This is a cached static method of MyClass.
        """
        result = 3.14 * param1 + len(param2)
        return result

# Usage
result1 = MyClass.my_static_method(5, "hello")
result2 = MyClass.my_static_method(5, "hello")  # Should use cached result

print(result1)
print(result2)

This is the Error it's throwing:

AttributeError Traceback (most recent call last) in 54 55 # Usage ---> 56 result1 = MyClass.my_static_method(5, "hello") 57 result2 = MyClass.my_static_method(5, "hello") # Should use cached result 58

in call(self, *args, **kwargs) 23 24 def call(self, *args, **kwargs): ---> 25 cache_path = self.__get_path(*args, **kwargs) 26 27 try:

in __get_path(self, *args, **kwargs) 35 36 def __get_path(self, *args, **kwargs): ---> 37 class_name = self.func.qualname.split('.', 1)[0] 38 function_name = self.func.name 39

AttributeError: 'staticmethod' object has no attribute 'qualname'

1

There are 1 answers

5
user2357112 On BEST ANSWER

This is a Python version issue. staticmethods don't have a __qualname__ until Python 3.10.

You could access the underlying function with __func__, but you're already implementing the descriptor protocol yourself, so it'd be easier to just not use staticmethod at all. Handling staticmethod's functionality is straightforward and makes using your decorator simpler:

class CachedStaticMethod:
    def __init__(self, func):
        self.func = func
    def __get__(self, instance, owner=None):
        if instance is None:
            return self
        return self.func
    ...

class MyClass:
    # No @staticmethod
    @CachedStaticMethod
    def my_static_method(...):
        ...

Also, the way you've designed your __get__, you're only caching calls like MyClass.my_static_method(...). Calls on an instance, MyClass().my_static_method(...), bypass the cache, even though they still don't receive self. You might want to just remove your __get__ entirely, so all calls use the cache.

If you really want to keep self.func as a staticmethod, then you could do self.func.__func__.__qualname__ to get the qualname.