Skip to content

Commit 89308d5

Browse files
added typing to multidecks
1 parent 4d68943 commit 89308d5

File tree

3 files changed

+42
-21
lines changed

3 files changed

+42
-21
lines changed

src/model/match.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -648,7 +648,7 @@ def create_deck(self, data):
648648

649649
# Create multidecks
650650
for type in self._deck:
651-
self._multidecks[type] = MultiDeck(self._deck[type])
651+
self._multidecks[type] = MultiDeck[Card, int](self._deck[type])
652652

653653
return True, "OK"
654654

src/model/multideck.py

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,35 +27,56 @@
2727

2828
from random import shuffle
2929
from threading import RLock
30+
from typing import Generic, List, Optional, Set, TypeVar, cast
3031

3132
from nussschale.util.locks import mutex
3233

3334

34-
class MultiDeck:
35+
T = TypeVar("T")
36+
U = TypeVar("U")
37+
38+
39+
class MultiDeck(Generic[T, U]):
3540
"""One (refilling) deck used to make selection seem more 'random'."""
3641

37-
def __init__(self, deck):
42+
def __init__(self, deck: List[T]) -> None:
3843
"""Constructor.
3944
4045
Args:
41-
deck (list): A list of objects having an 'id' property. The deck
46+
deck: A list of objects having an 'id' property. The deck
4247
should be safe for random access at any given time.
4348
"""
4449
self._lock = RLock()
45-
self._backing = deck
46-
self._queue = []
47-
self._contained = set()
50+
self._backing = deck # type: List[T]
51+
self._queue = [] # type: List[T]
52+
self._contained = set() # type: Set[U]
53+
54+
@staticmethod
55+
def _id_of(o: T) -> U:
56+
"""Fetches the ID of the given object.
57+
58+
This method only exists to enable type checking to work while
59+
structural subtyping is not supported.
60+
61+
Args:
62+
o: The object to get the ID from.
63+
64+
Returns:
65+
The ID of the object.
66+
"""
67+
id = o.id # type: ignore
68+
return cast(U, id)
4869

4970
@mutex
50-
def request(self, banned_ids):
71+
def request(self, banned_ids: Set[U]) -> Optional[T]:
5172
"""Requests a card from the multideck.
5273
5374
Args:
54-
banned_ids (set): A set of IDs that may not be chosen.
75+
banned_ids: A set of IDs that may not be chosen.
5576
5677
Returns:
57-
obj: The object that was selected. This might be None if the
58-
request can't be fulfilled.
78+
The object that was selected. This might be None if the
79+
request can't be fulfilled.
5980
6081
Contract:
6182
This method locks the deck's lock.
@@ -65,8 +86,8 @@ def request(self, banned_ids):
6586
# Try to find a viable object
6687
while ptr < len(self._queue):
6788
obj = self._queue[ptr]
68-
if obj.id not in banned_ids:
69-
self._contained.remove(obj.id)
89+
if MultiDeck._id_of(obj) not in banned_ids:
90+
self._contained.remove(MultiDeck._id_of(obj))
7091
del self._queue[ptr]
7192
return obj
7293
ptr += 1
@@ -78,18 +99,18 @@ def request(self, banned_ids):
7899
# by at least factor 2.
79100
pool = []
80101
for obj in self._backing:
81-
if obj.id not in self._contained:
102+
if MultiDeck._id_of(obj) not in self._contained:
82103
pool.append(obj)
83104
shuffle(pool)
84105
for obj in pool:
85-
self._contained.add(obj.id)
106+
self._contained.add(MultiDeck._id_of(obj))
86107
self._queue.append(obj)
87108

88109
# Try to find a viable object again
89110
while ptr < len(self._queue):
90111
obj = self._queue[ptr]
91-
if obj.id not in banned_ids:
92-
self._contained.remove(obj.id)
112+
if MultiDeck._id_of(obj) not in banned_ids:
113+
self._contained.remove(MultiDeck._id_of(obj))
93114
del self._queue[ptr]
94115
return obj
95116
ptr += 1

src/test/test_multideck.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def __init__(self, id: int) -> None:
4747

4848
def test_multideck_period() -> None:
4949
"""Tests the multideck's period with no restrictions placed on it."""
50-
md = MultiDeck(deck)
50+
md = MultiDeck[MockCard, int](deck)
5151
ids = set()
5252
for i in range(deck_n):
5353
obj = md.request(set())
@@ -58,7 +58,7 @@ def test_multideck_period() -> None:
5858

5959
def test_multideck_deplete() -> None:
6060
"""Tests depleting the multideck by making restrictions more strict."""
61-
md = MultiDeck(deck)
61+
md = MultiDeck[MockCard, int](deck)
6262
ids = set() # type: Set[int]
6363
for i in range(deck_n):
6464
obj = md.request(ids)
@@ -70,7 +70,7 @@ def test_multideck_deplete() -> None:
7070
def test_multideck_draw() -> None:
7171
"""Tests drawing objects from the multideck."""
7272
# Draw 5 times hands of deck_n/4 cards (must lead to duplicates)
73-
md = MultiDeck(deck)
73+
md = MultiDeck[MockCard, int](deck)
7474
hands = [] # type: List[Set[int]]
7575
for i in range(5):
7676
hands.append(set())
@@ -89,7 +89,7 @@ def test_multideck_draw() -> None:
8989

9090
def test_multideck_no_duplicate() -> None:
9191
"""Tests the drawing for duplicates."""
92-
md = MultiDeck(deck)
92+
md = MultiDeck[MockCard, int](deck)
9393
x = md.request(set())
9494
for i in range(deck_n - 1):
9595
obj = md.request(set())

0 commit comments

Comments
 (0)