Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python 3.5 type hinting dynamically generated instance attributes

I'd like to add Python 3.5 type hints for dynamically generated object attributes, so that IDEs correctly autocomplete them. Here by "dynamical" I mean that the attribute is not present during class creation or in __init__ or any other method.

E.g. is there a way to add these through comments or other tricks? If not I can fallback to add dummy class attributes.

Example::

 class Request:
      """Example HTTP request object.

      We have `get_user()`  but we do not declare it anyhere.
      """

 ...


 # Pyramid's way of plugging in methods and properties to request, enabled addon capabilities for the framework
 # adds Request.user - done in different part of application lifecycle, not during class creation
 config.add_request_method(auth.get_user, 'user', reify=True)

The goal is to make this work so that PyCharm and other IDEs would complete this attribute.

like image 388
Mikko Ohtamaa Avatar asked Dec 11 '15 18:12

Mikko Ohtamaa


1 Answers

In Python 3.6+ you can use the class-level type hints - these would not generate attributes in the class. I.e.

class Request(_Request):
    user: Optional[User]

This would not create an attribute in the class, only an annotation.

>>> Request.user
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'Request' has no attribute 'user'

>>> Request.__annotations__
{'user': typing.Union[foo.User, NoneType]}

In Python 3.5 it is possible to make a function that returns a non-data descriptor (i.e. a descriptor without __set__); this would be overridable by an instance attribute but it comes with some minimal runtime overhead - the descriptor will be fetched from __dict__ and checked if it defines the __set__ slot - even for all reads. It could then look something like

class Request(_Request):
    user = typed(User)

where the typed is defined as

def typed(type: Type[T]) -> T:
    ... return a dummy non-data-descriptor...

This should be enough for PyCharm to infer the types correctly.

like image 179