Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deprecating a function that is being replaced with a property

Tags:

python

I am refactoring parts of an API and wish to add deprecation warnings to parts that will eventually be removed. However I have stumbled into an issue where I would like to replace a function call with a property sharing a name.

Is there a hack where I can support both calling the .length as a property and as a function? I have thinkered with __getattribute__ and __getattr__ and can't think of a way.

import warnings
class A:
    @property
    def length(self):
        return 1

    def length(self):
        warnings.warn(".length function is deprecated. Use the .length property", DeprecationWarning)
        return 1

P.S.
Preferably I would like the solution to be python 2.7 compatible.

Additional context

The only "kind of" solution I have thought of is to overwrite the return value and skip the properties for now and add them in later when the deprecation warnings are removed. This solution would work for my case, if there really isn't any other way, but I would prefer a solution that is a lot less hacky.

import warnings


class F(float):
    def __init__(self, v):
        self.v = v

    def __new__(cls, value):
        return float.__new__(cls, value)

    def __call__(self, *args, **kwargs):
        warnings.warn(".length function is deprecated. Use the .length property", DeprecationWarning)
        return self.v


class A(object):
    def __getattribute__(self, item):
        if item == "length":
            # This is a hack to enable a deprecation warning when calling .length()
            # Remove this in favor for the @property, when the deprecation warnings are removed.
            return F(1)
        return super(A, self).__getattribute__(item)

    # @property
    # def length(self):
    #     # type: () -> float
    #     return 1.0


like image 230
Jerakin Avatar asked Oct 15 '25 19:10

Jerakin


1 Answers

One workaround is to make the property return a proxy object of a subtype of the value to be returned. The proxy object can then produce the warning when called:

import warnings

def warning_property(message, warning_type=DeprecationWarning):
    class _property(property):
        def __get__(self, obj, obj_type=None):
            value = super().__get__(obj, obj_type)
            class _proxy(type(value)):
                def __call__(self):
                    warnings.warn(message, warning_type)
                    return value
            return _proxy(value)
    return _property

so that:

class A:
    @warning_property(".length function is deprecated. Use the .length property")
    def length(self):
        return 1

print(A().length)
print(A().length())

outputs:

1
1
DeprecationWarning: .length function is deprecated. Use the .length property

Demo: https://ideone.com/PWkLN9

Note that the above assumes that the constructor of the type of the returning value of the property can take an instance as an argument, which is the case for all built-in types. If the constructor has a different signature then you should modify return _proxy(value) accordingly.

like image 130
blhsing Avatar answered Oct 18 '25 08:10

blhsing