smarie/python-autoclass

Enforce + Autoclass: AttributeError: 'functools.partial' object has no attribute '__enforcer__'

smarie opened this issue · 1 comments

This works:

from autoclass import autoclass, setter_override
from numbers import Real
from enforce import runtime_validation, config
config(dict(mode='covariant'))  # to accept subclasses in validation

@runtime_validation
@autoclass
class HouseConfiguration(object):
    def __init__(self, surface: Real):
        pass

    # -- overriden setter for surface
    @setter_override
    def surface(self, surface):
        print('Set surface to {}'.format(surface))
        self._surface = surface

t = HouseConfiguration(12)

While this throws an exception AttributeError: 'functools.partial' object has no attribute '__enforcer__':

from autoclass import autoclass, setter_override
from numbers import Real
from enforce import runtime_validation, config
config(dict(mode='covariant'))  # to accept subclasses in validation

@autoclass
@runtime_validation
class HouseConfiguration(object):
    def __init__(self, surface: Real):
        pass

    # -- overriden setter for surface
    @setter_override
    def surface(self, surface):
        print('Set surface to {}'.format(surface))
        self._surface = surface

t = HouseConfiguration(12)

The only difference is the order of the annotations on class HouseConfiguration.

After investigation, the 'wrong' order should be forbidden, since otherwise this would lead to non-intuitive behaviours. For example if there is a second attribute:

from autoclass import autoclass, setter_override
from numbers import Real
from enforce import runtime_validation, config
config(dict(mode='covariant'))  # to accept subclasses in validation

@autoclass
@runtime_validation
class HouseConfiguration(object):
    def __init__(self, surface: Real, name: str):
        pass

    # -- overriden setter for surface
    @setter_override
    def surface(self, surface):
        print('Set surface to {}'.format(surface))
        self._surface = surface

t = HouseConfiguration(12)

Then the setter for surface would by checked by enforce, but not the setter for name. For this reason we will detect and prevent such usage by raising an exception when the @autoclass, @autodict or @autoprops annotations are used after enforce decorator, rather than before.