Skip to content

Commit

Permalink
Add type stubs for indexing into ListAttribute
Browse files Browse the repository at this point in the history
  • Loading branch information
ikonst committed Apr 29, 2020
1 parent 20032fa commit dc55815
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 4 deletions.
13 changes: 13 additions & 0 deletions docs/release_notes.rst
@@ -1,6 +1,19 @@
Release Notes
=============

v4.3.3
----------

* Add type stubs for indexing into a ``ListAttribute`` for forming conditional expressions (#774)

::

class MyModel(Model):
...
my_list = ListAttribute()

MyModel.query(..., condition=MyModel.my_list[0] == 42)

v4.3.2
----------

Expand Down
2 changes: 1 addition & 1 deletion pynamodb/__init__.py
Expand Up @@ -7,4 +7,4 @@
"""
__author__ = 'Jharrod LaFon'
__license__ = 'MIT'
__version__ = '4.3.2'
__version__ = '4.3.3'
3 changes: 2 additions & 1 deletion pynamodb/attributes.pyi
Expand Up @@ -6,7 +6,7 @@ from pynamodb.expressions.condition import (
BeginsWith, Between, Comparison, Contains, NotExists, Exists, In
)
from pynamodb.expressions.operand import (
_Decrement, _IfNotExists, _Increment, _ListAppend
Path, _Decrement, _IfNotExists, _Increment, _ListAppend
)
from pynamodb.expressions.update import (
AddAction, DeleteAction, RemoveAction, SetAction, ListRemoveAction
Expand Down Expand Up @@ -168,6 +168,7 @@ class ListAttribute(Generic[_T], Attribute[List[_T]]):
def __get__(self: _A, instance: None, owner: Any) -> _A: ...
@overload
def __get__(self, instance: Any, owner: Any) -> List[_T]: ...
def __getitem__(self, idx: int) -> Path: ...
def remove_indexes(self, *indexes: int) -> Union[ListRemoveAction]: ...


Expand Down
1 change: 1 addition & 0 deletions pynamodb/expressions/operand.pyi
Expand Up @@ -79,3 +79,4 @@ class Path(_NumericOperand, _ListAppendOperand, _ConditionOperand):
def set(self, value: Any) -> SetAction: ...
def remove(self) -> RemoveAction: ...
def remove_list_elements(self, *indexes: int) -> ListRemoveAction: ...
def __getitem__(self, idx: Union[int, str]) -> Path: ...
21 changes: 19 additions & 2 deletions tests/test_mypy.py
Expand Up @@ -142,15 +142,32 @@ class MyModel(Model):
reveal_type(MyModel.my_list) # E: Revealed type is 'pynamodb.attributes.ListAttribute[__main__.MyMap]'
reveal_type(MyModel().my_list) # E: Revealed type is 'builtins.list[__main__.MyMap*]'
reveal_type(MyModel.my_list[0]) # E: Value of type "ListAttribute[MyMap]" is not indexable # E: Revealed type is 'Any'
reveal_type(MyModel().my_list[0].my_sub_attr) # E: Revealed type is 'builtins.str'
# Untyped lists are not well supported yet
reveal_type(MyModel.my_untyped_list[0]) # E: Value of type "ListAttribute[Any]" is not indexable # E: Revealed type is 'Any'
reveal_type(MyModel().my_untyped_list[0].my_sub_attr) # E: Revealed type is 'Any'
""")


def test_paths():
assert_mypy_output("""
from pynamodb.attributes import ListAttribute, MapAttribute, UnicodeAttribute
from pynamodb.models import Model
class MyMap(MapAttribute):
my_sub_attr = UnicodeAttribute()
class MyModel(Model):
my_list = ListAttribute(of=MyMap)
my_map = MyMap()
reveal_type(MyModel.my_list[0]) # E: Revealed type is 'pynamodb.expressions.operand.Path'
reveal_type(MyModel.my_list[0] == MyModel()) # E: Revealed type is 'pynamodb.expressions.condition.Comparison'
# the following string indexing is not type checked - not by mypy nor in runtime
reveal_type(MyModel.my_list[0]['my_sub_attr'] == 'foobar') # E: Revealed type is 'pynamodb.expressions.condition.Comparison'
""")


def test_index_query_scan():
assert_mypy_output("""
from pynamodb.attributes import NumberAttribute
Expand Down

0 comments on commit dc55815

Please sign in to comment.