From beee0f63baeb1ed9eec26e06e382cb0405549ead Mon Sep 17 00:00:00 2001 From: Vince Knight Date: Fri, 11 May 2018 16:53:15 +0100 Subject: [PATCH] Fix type hints for mypy 2.1 Closes #1176 Various things I've needed to do: - In a number of places include that the type is `Optional[]`. - In a number of places include an `assert` so that `mypy` knows that the type is no longer None. - I have made a minor refactor to the Moran process, I could have used asserts but this actually pointed out a spot where I believe the tests were incorrect: we were testing a specific situation where `MoranProcess.birth` or `MoranProcess.death` should have been called with default `index=None` but was actually being tested with `index=0`. - **I have ignored** a type hint check in `fingerprint.py`: I was completely unable to get this one happy To ensure that any new PRs don't break CI let's merge this under bug fix. --- axelrod/fingerprint.py | 8 +++++--- axelrod/moran.py | 8 ++++---- axelrod/strategies/apavlov.py | 6 ++++-- axelrod/strategies/darwin.py | 5 ++++- axelrod/strategies/hunter.py | 4 ++-- axelrod/strategies/memoryone.py | 1 + axelrod/tests/unit/test_moran.py | 10 +++++----- axelrod/tournament.py | 7 ++++--- 8 files changed, 29 insertions(+), 20 deletions(-) diff --git a/axelrod/fingerprint.py b/axelrod/fingerprint.py index 1b1a289a1..c2b8f0e1f 100644 --- a/axelrod/fingerprint.py +++ b/axelrod/fingerprint.py @@ -268,7 +268,7 @@ def construct_tournament_elements(self, step: float, def fingerprint( self, turns: int = 50, repetitions: int = 10, step: float = 0.01, - processes: int=None, filename: str = None, + processes: int = None, filename: str = None, progress_bar: bool = True ) -> dict: """Build and play the spatial tournament. @@ -305,7 +305,7 @@ def fingerprint( temp_file_descriptor = None if filename is None: - temp_file_descriptor, filename = mkstemp() + temp_file_descriptor, filename = mkstemp() # type: ignore edges, tourn_players = self.construct_tournament_elements( step, progress_bar=progress_bar) @@ -323,6 +323,7 @@ def fingerprint( filename, progress_bar=progress_bar) if temp_file_descriptor is not None: + assert filename is not None os.close(temp_file_descriptor) os.remove(filename) @@ -443,7 +444,7 @@ def fingerprint(self, turns: int = 50, repetitions: int = 1000, temp_file_descriptor = None if filename is None: - temp_file_descriptor, filename = mkstemp() + temp_file_descriptor, filename = mkstemp() # type: ignore edges = [(0, k + 1) for k in range(len(self.opponents))] tournament = axl.Tournament(players=players, @@ -455,6 +456,7 @@ def fingerprint(self, turns: int = 50, repetitions: int = 1000, self.data = self.analyse_cooperation_ratio(filename) if temp_file_descriptor is not None: + assert filename is not None os.close(temp_file_descriptor) os.remove(filename) diff --git a/axelrod/moran.py b/axelrod/moran.py index 5ae7f0525..ba7268180 100644 --- a/axelrod/moran.py +++ b/axelrod/moran.py @@ -12,7 +12,7 @@ from .match import Match from .random_ import randrange -from typing import List, Tuple, Set +from typing import List, Tuple, Set, Optional def fitness_proportionate_selection(scores: List) -> int: @@ -102,7 +102,7 @@ def __init__(self, players: List[Player], turns: int = DEFAULT_TURNS, self.populations = [] # type: List self.set_players() self.score_history = [] # type: List - self.winning_strategy_name = None # type: str + self.winning_strategy_name = None # type: Optional[str] self.mutation_rate = mutation_rate assert (mutation_rate >= 0) and (mutation_rate <= 1) assert (noise >= 0) and (noise <= 1) @@ -184,7 +184,7 @@ def death(self, index: int = None) -> int: index: The index of the player to be removed """ - if self.mode == "db": + if index is None: # Select a player to be replaced globally i = randrange(0, len(self.players)) # Record internally for use in _matchup_indices @@ -207,7 +207,7 @@ def birth(self, index: int = None) -> int: """ # Compute necessary fitnesses. scores = self.score_all() - if self.mode == "db": + if index is not None: # Death has already occurred, so remove the dead player from the # possible choices scores.pop(index) diff --git a/axelrod/strategies/apavlov.py b/axelrod/strategies/apavlov.py index 4f8a7858c..19bfacbdc 100644 --- a/axelrod/strategies/apavlov.py +++ b/axelrod/strategies/apavlov.py @@ -1,6 +1,8 @@ from axelrod.action import Action from axelrod.player import Player +from typing import Optional + C, D = Action.C, Action.D @@ -29,7 +31,7 @@ class APavlov2006(Player): def __init__(self) -> None: super().__init__() - self.opponent_class = None # type: str + self.opponent_class = None # type: Optional[str] def strategy(self, opponent: Player) -> Action: # TFT for six rounds @@ -93,7 +95,7 @@ class APavlov2011(Player): def __init__(self) -> None: super().__init__() - self.opponent_class = None # type: str + self.opponent_class = None # type: Optional[str] def strategy(self, opponent: Player) -> Action: # TFT for six rounds diff --git a/axelrod/strategies/darwin.py b/axelrod/strategies/darwin.py index 3be0a7185..9d3c58ebd 100644 --- a/axelrod/strategies/darwin.py +++ b/axelrod/strategies/darwin.py @@ -8,6 +8,8 @@ from axelrod.action import Action from axelrod.player import Player +from typing import Optional + C, D = Action.C, Action.D @@ -48,7 +50,7 @@ class Darwin(Player): valid_callers = ["play"] # What functions may invoke our strategy. def __init__(self) -> None: - self.outcomes = None # type: dict + self.outcomes = None # type: Optional[dict] self.response = Darwin.genome[0] super().__init__() @@ -64,6 +66,7 @@ def strategy(self, opponent: Player) -> Action: trial = len(self.history) if trial > 0: + assert self.outcomes is not None outcome = self.outcomes[(self.history[-1], opponent.history[-1])] self.mutate(outcome, trial) # Update genome with selected response diff --git a/axelrod/strategies/hunter.py b/axelrod/strategies/hunter.py index dc717f23c..57d0f2900 100644 --- a/axelrod/strategies/hunter.py +++ b/axelrod/strategies/hunter.py @@ -2,7 +2,7 @@ from axelrod.player import Player from axelrod._strategy_utils import detect_cycle -from typing import List, Tuple +from typing import List, Tuple, Optional C, D = Action.C, Action.D @@ -120,7 +120,7 @@ class CycleHunter(Player): def __init__(self) -> None: super().__init__() - self.cycle = None # type: Tuple[Action] + self.cycle = None # type: Optional[Tuple[Action]] def strategy(self, opponent: Player) -> Action: if self.cycle: diff --git a/axelrod/strategies/memoryone.py b/axelrod/strategies/memoryone.py index 5a3133d9c..27ccd33ed 100644 --- a/axelrod/strategies/memoryone.py +++ b/axelrod/strategies/memoryone.py @@ -193,6 +193,7 @@ def receive_match_attributes(self): self.set_four_vector(four_vector) def __repr__(self) -> str: + assert self.p is not None return "%s: %s" % (self.name, round(self.p, 2)) diff --git a/axelrod/tests/unit/test_moran.py b/axelrod/tests/unit/test_moran.py index 410297a9c..2b0a8c0f5 100644 --- a/axelrod/tests/unit/test_moran.py +++ b/axelrod/tests/unit/test_moran.py @@ -77,13 +77,13 @@ def test_death_in_db(self): players = axelrod.Cooperator(), axelrod.Defector(), axelrod.TitForTat() mp = MoranProcess(players, mutation_rate=0.5, mode="db") axelrod.seed(1) - self.assertEqual(mp.death(0), 0) + self.assertEqual(mp.death(), 0) self.assertEqual(mp.dead, 0) axelrod.seed(5) - self.assertEqual(mp.death(0), 1) + self.assertEqual(mp.death(), 1) self.assertEqual(mp.dead, 1) axelrod.seed(2) - self.assertEqual(mp.death(0), 2) + self.assertEqual(mp.death(), 2) self.assertEqual(mp.dead, 2) def test_death_in_bd(self): @@ -102,14 +102,14 @@ def test_birth_in_db(self): players = axelrod.Cooperator(), axelrod.Defector(), axelrod.TitForTat() mp = MoranProcess(players, mode="db") axelrod.seed(1) - self.assertEqual(mp.death(0), 0) + self.assertEqual(mp.death(), 0) self.assertEqual(mp.birth(0), 2) def test_birth_in_bd(self): players = axelrod.Cooperator(), axelrod.Defector(), axelrod.TitForTat() mp = MoranProcess(players, mode="bd") axelrod.seed(1) - self.assertEqual(mp.birth(0), 0) + self.assertEqual(mp.birth(), 0) def test_fixation_check(self): players = axelrod.Cooperator(), axelrod.Cooperator() diff --git a/axelrod/tournament.py b/axelrod/tournament.py index bd3c3ef5e..44b54bd76 100644 --- a/axelrod/tournament.py +++ b/axelrod/tournament.py @@ -21,7 +21,7 @@ C, D = Action.C, Action.D -from typing import List, Tuple +from typing import List, Tuple, Optional class Tournament(object): @@ -83,8 +83,8 @@ def __init__(self, players: List[Player], self._logger = logging.getLogger(__name__) self.use_progress_bar = True - self.filename = None # type: str - self._temp_file_descriptor = None # type: int + self.filename = None # type: Optional[str] + self._temp_file_descriptor = None # type: Optional[int] def setup_output(self, filename=None): """assign/create `filename` to `self`. If file should be deleted once @@ -142,6 +142,7 @@ def play(self, build_results: bool = True, filename: str = None, processes=processes, progress_bar=progress_bar) if self._temp_file_descriptor is not None: + assert self.filename is not None os.close(self._temp_file_descriptor) os.remove(self.filename)