Skip to content

Commit 1ed28d3

Browse files
authored
v1.4.2: Make cleaning exhaustive (#160)
* Add a test with the example from the issue * Move example preferences to data directory Cleans up the test module a fair bit! * Remove players via list comprehension Something must have been going wrong with references in the list using `list.remove(x)`. Oddly enough, the same solution in `Player._forget(other)` makes everything fail... * Sort imports in new example test * Remove old try-except for weird removals Another way the player removal process broke things was for project removal (when removing a supervisor). Now that removals are explicit via comprehension we don't need this. (I wish) This also fixes #161 * Bump version to 1.4.2 * Implement hypothesis profile w/o deadline
1 parent c99b71f commit 1ed28d3

File tree

8 files changed

+201
-14
lines changed

8 files changed

+201
-14
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ htmlcov/
1616

1717

1818
!img/*.pdf
19+
!tests/**/data/*.json
1920

2021
/.quarto/
2122
/_site/

src/matching/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from .matchings import MultipleMatching, SingleMatching
1212
from .players import Hospital, Player, Project, Supervisor
1313

14-
__version__ = "1.4.1"
14+
__version__ = "1.4.2"
1515

1616
__all__ = [
1717
"BaseGame",

src/matching/base.py

+4-7
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,7 @@ def _forget(self, other):
4747
"""Forget another player by removing them from the player's preference
4848
list."""
4949

50-
prefs = self.prefs[:]
51-
prefs.remove(other)
52-
self.prefs = prefs
50+
self.prefs = [p for p in self.prefs if p != other]
5351

5452
def unmatched_message(self):
5553
"""Message to say the player is not matched."""
@@ -134,10 +132,9 @@ def _remove_player(self, player, player_party, other_party):
134132
preference lists, removing their memory from the game.
135133
"""
136134

137-
party = vars(self)[player_party][:]
138-
party.remove(player)
139-
vars(self)[player_party].remove(player)
140-
for other in vars(self)[other_party]:
135+
party = getattr(self, player_party)[:]
136+
setattr(self, player_party, [p for p in party if p != player])
137+
for other in getattr(self, other_party):
141138
if player in other.prefs:
142139
other._forget(player)
143140

src/matching/games/student_allocation.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,7 @@ def _remove_player(self, player, player_party, other_party=None):
8989
if player_party == "supervisors":
9090
self.supervisors.remove(player)
9191
for project in player.projects:
92-
try:
93-
super()._remove_player(project, "projects", "students")
94-
except ValueError:
95-
pass
92+
super()._remove_player(project, "projects", "students")
9693

9794
else:
9895
super()._remove_player(player, player_party, other_party)

tests/conftest.py

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""Configuration for tests."""
2+
3+
from hypothesis import settings
4+
5+
settings.register_profile("ci", deadline=6000)
+156
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
{
2+
"hospitals": {
3+
"0": [0, 51],
4+
"1": [28, 98, 1],
5+
"2": [46, 44, 9, 2],
6+
"3": [],
7+
"4": [4, 11, 71, 79],
8+
"5": [5, 87],
9+
"6": [6],
10+
"7": [],
11+
"8": [13, 26],
12+
"9": [64, 76],
13+
"10": [82],
14+
"11": [11, 72],
15+
"12": [24, 12, 5, 57, 60],
16+
"13": [13, 51, 40, 71],
17+
"14": [14],
18+
"15": [15, 99, 83],
19+
"16": [16],
20+
"17": [47],
21+
"18": [71],
22+
"19": [47, 19, 61, 20, 72],
23+
"20": [20, 59, 65],
24+
"21": [21, 33, 98, 38, 93],
25+
"22": [22, 52],
26+
"23": [4],
27+
"24": [24, 60, 5, 12, 28],
28+
"25": [45],
29+
"26": [26],
30+
"27": [38, 17, 98, 57, 9],
31+
"28": [28, 98, 1, 7, 60],
32+
"29": [70, 90, 29],
33+
"30": [30, 89, 65],
34+
"31": [63, 31, 70, 55, 44],
35+
"32": [33, 32],
36+
"33": [33],
37+
"34": [],
38+
"35": [35, 52, 8, 72],
39+
"36": [36, 44, 91],
40+
"37": [],
41+
"38": [38, 0, 33],
42+
"39": [39, 80, 84, 51, 71],
43+
"40": [],
44+
"41": [41, 80, 73],
45+
"42": [],
46+
"43": [87, 92],
47+
"44": [61, 2, 44, 12, 87],
48+
"45": [72, 45, 0, 10],
49+
"46": [40],
50+
"47": [47],
51+
"48": [98, 48, 60, 5, 85],
52+
"49": [49, 18, 75, 86, 53]
53+
},
54+
"residents": {
55+
"0": [0, 45, 7],
56+
"1": [1],
57+
"2": [2],
58+
"3": [],
59+
"4": [4],
60+
"5": [5],
61+
"6": [6],
62+
"7": [1],
63+
"8": [35],
64+
"9": [],
65+
"10": [45, 37],
66+
"11": [11],
67+
"12": [],
68+
"13": [13, 8],
69+
"14": [14, 13, 37, 11],
70+
"15": [15, 40],
71+
"16": [16],
72+
"17": [],
73+
"18": [],
74+
"19": [19],
75+
"20": [20, 19],
76+
"21": [21],
77+
"22": [22],
78+
"23": [6],
79+
"24": [24],
80+
"25": [],
81+
"26": [26, 8],
82+
"27": [],
83+
"28": [28, 1],
84+
"29": [29],
85+
"30": [30, 46],
86+
"31": [31],
87+
"32": [],
88+
"33": [33, 21],
89+
"34": [],
90+
"35": [35],
91+
"36": [36],
92+
"37": [37],
93+
"38": [38, 27],
94+
"39": [39],
95+
"40": [46, 11, 13, 40],
96+
"41": [41],
97+
"42": [],
98+
"43": [49, 34, 43, 40],
99+
"44": [2],
100+
"45": [25, 45],
101+
"46": [2],
102+
"47": [47],
103+
"48": [48],
104+
"49": [49],
105+
"50": [],
106+
"51": [13],
107+
"52": [],
108+
"53": [],
109+
"54": [],
110+
"55": [],
111+
"56": [],
112+
"57": [12],
113+
"58": [],
114+
"59": [],
115+
"60": [],
116+
"61": [],
117+
"62": [5],
118+
"63": [],
119+
"64": [],
120+
"65": [],
121+
"66": [37],
122+
"67": [],
123+
"68": [6],
124+
"69": [],
125+
"70": [29],
126+
"71": [13],
127+
"72": [45, 11],
128+
"73": [41, 12],
129+
"74": [],
130+
"75": [1],
131+
"76": [9],
132+
"77": [],
133+
"78": [],
134+
"79": [],
135+
"80": [39, 41],
136+
"81": [],
137+
"82": [10],
138+
"83": [],
139+
"84": [39],
140+
"85": [],
141+
"86": [],
142+
"87": [43, 5],
143+
"88": [],
144+
"89": [],
145+
"90": [12],
146+
"91": [],
147+
"92": [43],
148+
"93": [],
149+
"94": [],
150+
"95": [],
151+
"96": [],
152+
"97": [],
153+
"98": [1, 28, 48, 21],
154+
"99": []
155+
}
156+
}

tests/hospital_resident/test_examples.py

+32-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
"""A collection of example tests for HR."""
22

3+
import json
4+
import os
5+
36
from matching.games import HospitalResident
47

58

@@ -31,7 +34,7 @@ def test_readme_example():
3134
assert matching == {M: [L, S], C: [D, A], G: [J]}
3235

3336

34-
def test_example_in_issue():
37+
def test_example_in_issue_67():
3538
"""Test the example given in #67."""
3639

3740
group_prefs = {
@@ -72,3 +75,31 @@ def test_resident_loses_all_preferences():
7275

7376
matching = game.solve()
7477
assert matching == {X: [B], Y: []}
78+
79+
80+
def test_example_in_issue_159():
81+
"""Test the example given in #159.
82+
83+
The example in this is particularly large, so we've moved the
84+
dictionaries to a test data directory.
85+
"""
86+
87+
here = os.path.join(os.path.dirname(__file__))
88+
with open(os.path.join(here, "data", "issue_159.json"), "r") as f:
89+
preferences = json.load(f)
90+
91+
resident_prefs = {
92+
int(res): prefs for res, prefs in preferences["residents"].items()
93+
}
94+
hospital_prefs = {
95+
int(hos): prefs for hos, prefs in preferences["hospitals"].items()
96+
}
97+
98+
capacities = {hospital: 1 for hospital in hospital_prefs}
99+
100+
game = HospitalResident.create_from_dictionaries(
101+
resident_prefs, hospital_prefs, capacities, clean=True
102+
)
103+
104+
for player in game.residents + game.hospitals:
105+
assert player.prefs

tox.ini

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ commands =
2020
python -m pytest docs \
2121
--nbval --nbval-current-env -p no:randomly
2222
python -m pytest tests \
23-
--cov=matching --cov-fail-under=100
23+
--cov=matching --cov-fail-under=100 --hypothesis-profile=ci

0 commit comments

Comments
 (0)