python-interface
python-interface copied to clipboard
Default implementation for Interface properties
Hello! Thanks for a great Python module.
I'm using this to define an interface with class properties (member variables). I would like to provide a default value for these variables (e.g. None
), but I can't see a way to do this.
My attempt;
import interface
class IMyInterface(interface.Interface):
@property
def member_variable(self):
pass
@property
@interface.default
def optional_member_variable(self):
return None
class Concrete(interface.implements(IMyInterface)):
@property
def member_variable(self):
return 1.0
if __name__ == "__main__":
c = Concrete()
Gives the error traceback;
Traceback (most recent call last):
File "C:\Users\uqasnosw\AppData\Local\Continuum\Miniconda3\envs\python36\lib\site-packages\interface\interface.py", line 114, in __new__
signature = TypedSignature(v)
File "C:\Users\uqasnosw\AppData\Local\Continuum\Miniconda3\envs\python36\lib\site-packages\interface\typed_signature.py", line 28, in __init__
self._signature = signature(extract_func(obj))
File "C:\Users\uqasnosw\AppData\Local\Continuum\Miniconda3\envs\python36\lib\inspect.py", line 3065, in signature
return Signature.from_callable(obj, follow_wrapped=follow_wrapped)
File "C:\Users\uqasnosw\AppData\Local\Continuum\Miniconda3\envs\python36\lib\inspect.py", line 2815, in from_callable
follow_wrapper_chains=follow_wrapped)
File "C:\Users\uqasnosw\AppData\Local\Continuum\Miniconda3\envs\python36\lib\inspect.py", line 2193, in _signature_from_callable
raise TypeError('{!r} is not a callable object'.format(obj))
TypeError: default(<function IMyInterface.optional_member_variable at 0x00000168036D5BF8>) is not a callable object
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "D:/Development/unimodal-irl/unimodal_irl/envs/explicit_env.py", line 3, in <module>
class IMyInterface(interface.Interface):
File "C:\Users\uqasnosw\AppData\Local\Continuum\Miniconda3\envs\python36\lib\site-packages\interface\interface.py", line 122, in __new__
raise_from(TypeError(errmsg), e)
File "<string>", line 1, in raise_from
TypeError: Couldn't parse signature for field IMyInterface.optional_member_variable of type property.
If you can provide a little guidance on what form a fix might take, I'm happy to attempt a fix and submit a pull request!
Thank you,
@aaronsnoswell apologies for the late response, but I think the issue here is just the order in which you're applying the @property
and @default
decorators. If you change your first definition to:
In [5]: class IFace(interface.Interface):
class IFace(interface.Interface):
@interface.default
@property
def optional_member(self):
return 3
The reason order matters here is that when you create a new interface, the interface
machinery goes through all the function or property-like attributes of your class and tries to extract function signatures from the attributes using the standard library inspect
module. In your example, you have a property
that wraps a default
object, and when we go to extract the signature from the default
, inspect
blows up because default
isn't actually callable: it's a sentinel object that we unwrap during class creation.
If you switch the order of the decorators, then you end up with a default
wrapping a property
instead of a property
wrapping a default, which means interface sees the default
first, and knows how to handle it.