Skip to content

Commit

Permalink
Migrate from pendulum to arrow to get support for Python 3.12
Browse files Browse the repository at this point in the history
  • Loading branch information
pronovic committed Oct 15, 2023
1 parent ea697fc commit d9c7f08
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 74 deletions.
1 change: 1 addition & 0 deletions Changelog
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Version 0.1.45 unreleased
* Upgrade major dependencies.
* Upgrade build process to Poetry v1.6.1.
* Add support for Python v3.12 and drop support for v3.8.
* Migrate from pendulum to arrow to get support for Python 3.12.

Version 0.1.44 07 Jun 2023

Expand Down
87 changes: 41 additions & 46 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ format-jinja = "{% if distance == 0 and not dirty %}{{ base }}{% else %}{{ base
python = ">=3.9,<4"
attrs = "^23.1.0"
cattrs = "^23.1.2"
pendulum = "^2.1.2"
importlib-metadata = { version="^6.8.0", optional=true }
sphinx = { version="^7.2.6", optional=true }
sphinx-autoapi = { version="^3.0.0", optional=true }
arrow = "^1.3.0"

[tool.poetry.extras]
docs = [ "importlib-metadata", "sphinx", "sphinx-autoapi" ]
Expand Down
16 changes: 8 additions & 8 deletions src/apologies/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@
from enum import Enum
from typing import Dict, List, Optional

import pendulum
import arrow
from arrow.arrow import Arrow
from attrs import define, field, frozen
from pendulum.datetime import DateTime

from .util import CattrConverter

# Cattr converter that serializes/deserializes DateTime to an ISO 8601 timestamp
# Cattr converter that serializes/deserializes Arrow date/time to an ISO 8601 timestamp
_CONVERTER = CattrConverter()

# A game consists of 2-4 players
Expand Down Expand Up @@ -425,21 +425,21 @@ class History:
action(str): String describing the action
color(Optional[PlayerColor]): Color of the player associated with the action
card(Optional[CardType]): Card associated with the action
timestamp(DateTime): Timestamp tied to the action (defaults to current time)
timestamp(Arrow): Timestamp tied to the action (defaults to current time)
"""

action: str
color: Optional[PlayerColor] = None
card: Optional[CardType] = None
timestamp: DateTime = field()
timestamp: Arrow = field()

# noinspection PyUnresolvedReferences
@timestamp.default
def _default_timestamp(self) -> DateTime:
return pendulum.now(pendulum.UTC) # type: ignore[attr-defined]
def _default_timestamp(self) -> Arrow:
return arrow.utcnow()

def __str__(self) -> str:
time = self.timestamp.to_time_string() # type: ignore
time = self.timestamp.format("HH:mm:ss")
color = "General" if not self.color else self.color.value
action = self.action
return "[%s] %s - %s" % (time, color, action)
Expand Down
16 changes: 8 additions & 8 deletions src/apologies/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
from itertools import combinations_with_replacement
from typing import Dict, List, Optional, Sequence

import pendulum
import arrow
from arrow.arrow import Arrow
from attrs import frozen
from pendulum import DateTime # type: ignore

from .engine import Character, Engine
from .game import MAX_PLAYERS, MIN_PLAYERS, GameMode, Player
Expand Down Expand Up @@ -60,8 +60,8 @@ class _Result:

"""Result of a single game within a scenario."""

start: DateTime
stop: DateTime
start: Arrow
stop: Arrow
character: Character
player: Player

Expand Down Expand Up @@ -152,12 +152,12 @@ def _run_scenario(prefix: str, iterations: int, engine: Engine) -> List[_Result]
for i in range(0, iterations):
print(" " * 100, end="\r", flush=True)
print("%siteration %d" % (prefix, i), end="\r", flush=True)
start = pendulum.now()
start = arrow.now()
engine.reset()
engine.start_game()
while not engine.completed:
engine.play_next()
stop = pendulum.now()
stop = arrow.now()
character, player = engine.winner() # type: ignore
results.append(_Result(start, stop, character, player))
return results
Expand All @@ -177,7 +177,7 @@ def run_simulation(iterations: int, output: str, sources: List[CharacterInputSou
csvwriter = csv.writer(csvfile, quoting=csv.QUOTE_ALL)
_write_header(csvwriter, sources)

start = pendulum.now()
start = arrow.now()
print("Starting simulation at %s, using %d iterations per scenario" % (start.to_datetime_string(), iterations)) # type: ignore

scenario = 0
Expand All @@ -200,6 +200,6 @@ def run_simulation(iterations: int, output: str, sources: List[CharacterInputSou
_write_scenario(csvwriter, analysis)
print("%sdone" % prefix, end="\r", flush=True)

stop = pendulum.now()
stop = arrow.now()
print(" " * 100, end="\r", flush=True)
print("Simulation completed after %s at %s" % (stop.diff(start).in_words(), stop.to_datetime_string())) # type: ignore
10 changes: 5 additions & 5 deletions src/apologies/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,24 @@
"""
from typing import Generic, List, TypeVar

import arrow
import cattrs
from arrow.arrow import Arrow
from attrs import define, field
from pendulum.datetime import DateTime
from pendulum.parser import parse


class CattrConverter(cattrs.GenConverter):
"""
Cattr converter that knows how to correctly serialize/deserialize DateTime to an ISO 8601 timestamp.
Cattr converter that knows how to correctly serialize/deserialize Arrow date/time to an ISO 8601 timestamp.
"""

# Note: we need to use GenConverter and not Converter because we use PEP563 (postponed) annotations
# See: https://stackoverflow.com/a/72539298/2907667 and https://github.com/python-attrs/cattrs/issues/41

def __init__(self) -> None:
super().__init__()
self.register_unstructure_hook(DateTime, lambda datetime: datetime.isoformat() if datetime else None)
self.register_structure_hook(DateTime, lambda string, _: parse(string) if string else None)
self.register_unstructure_hook(Arrow, lambda datetime: datetime.isoformat() if datetime else None)
self.register_structure_hook(Arrow, lambda string, _: arrow.get(string) if string else None)


T = TypeVar("T")
Expand Down
11 changes: 5 additions & 6 deletions tests/test_game.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@

from unittest.mock import MagicMock

import pendulum
import arrow
import pytest
from pendulum.datetime import DateTime

from apologies.game import (
BOARD_SQUARES,
Expand Down Expand Up @@ -353,10 +352,10 @@ def test_constructor(self):
assert history.action == "action"
assert history.color is color
assert history.card is card
assert history.timestamp <= DateTime.utcnow()
assert history.timestamp <= arrow.utcnow()

def test_str(self):
timestamp = pendulum.parse("2020-03-25T14:02:16")
timestamp = arrow.get("2020-03-25T14:02:16")

history = History("This is an action", color=None, timestamp=timestamp)
assert "%s" % history == "[14:02:16] General - This is an action"
Expand Down Expand Up @@ -473,7 +472,7 @@ def test_track_no_player(self):
assert game.history[0].action == "action"
assert game.history[0].color is None
assert game.history[0].card is None
assert game.history[0].timestamp <= DateTime.utcnow()
assert game.history[0].timestamp <= arrow.utcnow()
assert game.players[PlayerColor.RED].turns == 0
assert game.players[PlayerColor.YELLOW].turns == 0
assert game.players[PlayerColor.BLUE].turns == 0
Expand All @@ -487,7 +486,7 @@ def test_track_with_color(self):
assert game.history[0].action == "action"
assert game.history[0].color is PlayerColor.RED
assert game.history[0].card == CardType.CARD_12
assert game.history[0].timestamp <= DateTime.utcnow()
assert game.history[0].timestamp <= arrow.utcnow()
assert game.players[PlayerColor.RED].turns == 1
assert game.players[PlayerColor.YELLOW].turns == 0
assert game.players[PlayerColor.BLUE].turns == 0
Expand Down

0 comments on commit d9c7f08

Please sign in to comment.