Python 3.10 is the minimum supported version. Code cleanup.

This commit is contained in:
Josh W 2024-11-27 12:03:41 -05:00
parent c31d676a4f
commit 344a7cbae4
8 changed files with 123 additions and 137 deletions

View file

@ -7,7 +7,7 @@ readme = "README.md"
packages = [{include = "dnidatetime", from = "src"}]
[tool.poetry.dependencies]
python = "^3.11"
python = ">=3.10"
[tool.poetry.group.dev.dependencies]
pylint = "^3.1.0"

View file

@ -2,11 +2,10 @@
from collections import OrderedDict
from datetime import datetime, timezone
from typing import Tuple, TypedDict
from typing import TypedDict
#: Type alias for a seven-integer tuple used in a datetime timestamp.
DniDateTimeTuple = Tuple[int, int, int, int, int, int, int]
DniDateTimeTuple = tuple[int, int, int, int, int, int, int]
class DniUnitType(TypedDict):

View file

@ -2,10 +2,8 @@
from datetime import datetime, timezone
from math import floor
from typing import Dict, Optional, Tuple
from .constants import (
DniDateTimeTuple,
DNI_EPOCH_EDT,
DNI_EPOCH_HAHR,
DNI_UNITS,
@ -13,17 +11,16 @@ from .constants import (
MIN_DNI_ORDINAL,
MS_PER_HAHR,
MS_PER_PRORAHN,
YAHRTEE_BEFORE_VAILEE
YAHRTEE_BEFORE_VAILEE,
DniDateTimeTuple,
)
from .utils import (
check_dni_date_fields,
get_adj_ms_from_epoch,
get_ms_from_epoch,
is_timezone_aware
is_timezone_aware,
)
# Number of yahrtee (days) in a hahr (year) == 290.
_YAHR_IN_HAHR = DNI_UNITS["yahr"]["max"] * DNI_UNITS["vailee"]["max"]
@ -35,7 +32,7 @@ def hvy2ord(hahr: int, vailee: int, yahr: int) -> int:
return yahr_before_hahr + YAHRTEE_BEFORE_VAILEE[ok_vailee] + ok_yahr
def ord2hvy(ordinal: int) -> Tuple[int, int, int]:
def ord2hvy(ordinal: int) -> tuple[int, int, int]:
"""Find the exact hahr, vailee, and yahr from an ordinal value of
yahrtee.
"""
@ -53,19 +50,14 @@ def ord2hvy(ordinal: int) -> Tuple[int, int, int]:
vailee = DNI_UNITS["vailee"]["max"]
yahr = DNI_UNITS["yahr"]["max"]
else:
vailee, yahr = divmod(
rem + DNI_UNITS["yahr"]["max"],
DNI_UNITS["yahr"]["max"]
)
vailee, yahr = divmod(rem + DNI_UNITS["yahr"]["max"], DNI_UNITS["yahr"]["max"])
if yahr == 0:
vailee -= 1
yahr = DNI_UNITS["yahr"]["max"]
return hahr, vailee, yahr
def earth_to_dni(
date_time: Optional[datetime] = None
) -> DniDateTimeTuple:
def earth_to_dni(date_time: datetime | None = None) -> DniDateTimeTuple:
"""Converts an Earth gregorian datetime to a D'Ni datetime."""
if date_time is None:
moment = datetime.now(timezone.utc)
@ -75,7 +67,7 @@ def earth_to_dni(
else:
raise ValueError("Supplied datetime must be timezone aware.")
dni_dt: Dict[str, int] = {}
dni_dt: dict[str, int] = {}
stamp = get_adj_ms_from_epoch(moment)
dni_epoch = get_adj_ms_from_epoch(DNI_EPOCH_EDT)
@ -125,13 +117,13 @@ def dni_to_earth(dni_date_time: DniDateTimeTuple) -> datetime:
"D'Ni datetime must be in the form of a timetuple. "
"See DniDateTime.timetuple()."
),
dni_date_time
dni_date_time,
)
if len(dni_date_time) != 7:
raise ValueError(
"The D'Ni timetuple is malformed. See DniDateTime.timetuple().",
dni_date_time
dni_date_time,
)
temp_units = list(dni_date_time)

View file

@ -1,8 +1,10 @@
"""The DniDate class."""
from typing import Any, Optional, TYPE_CHECKING, Union
from __future__ import annotations
from .constants import DniDateTimeTuple, DNI_UNITS, MAX_DNI_ORDINAL
from typing import TYPE_CHECKING, Any
from .constants import DNI_UNITS, MAX_DNI_ORDINAL, DniDateTimeTuple
from .conversions import hvy2ord, ord2hvy
from .dnitimedelta import DniTimedelta
from .utils import check_dni_date_fields, cmp
@ -19,8 +21,8 @@ class DniDate:
_yahr: int
# Class properties
min: "DniDate"
max: "DniDate"
min: DniDate
max: DniDate
resolution: DniTimedelta
def __new__(
@ -28,7 +30,7 @@ class DniDate:
hahr: int = DNI_UNITS["hahr"]["min"],
vailee: int = DNI_UNITS["vailee"]["min"],
yahr: int = DNI_UNITS["yahr"]["min"],
) -> "DniDate":
) -> DniDate:
if isinstance(hahr, int):
hahr, vailee, yahr = check_dni_date_fields(hahr, vailee, yahr)
self = object.__new__(cls)
@ -39,7 +41,7 @@ class DniDate:
return NotImplemented
@classmethod
def from_dnitimestamp(cls, prorahntee: int) -> "DniDate":
def from_dnitimestamp(cls, prorahntee: int) -> DniDate:
"""Construct a D'Ni date from a D'Ni timestamp."""
yahrtee = prorahntee // DNI_UNITS["yahr"]["total_pro"]
return cls.from_ordinal(yahrtee)
@ -50,7 +52,7 @@ class DniDate:
return NotImplemented
@classmethod
def from_isoformat(cls, date_str: str) -> "DniDate":
def from_isoformat(cls, date_str: str) -> DniDate:
if not isinstance(date_str, str):
raise TypeError("from_isoformat: argument must be str.")
@ -64,7 +66,7 @@ class DniDate:
raise ValueError(f"Invalid isoformat string: {date_str!r}") from exc
@classmethod
def from_ordinal(cls, yahrtee: int) -> "DniDate":
def from_ordinal(cls, yahrtee: int) -> DniDate:
"""Construct a D'ni date from a D'ni ordinal."""
hahr, vailee, yahr = ord2hvy(yahrtee)
return cls(hahr, vailee, yahr)
@ -105,10 +107,10 @@ class DniDate:
def replace(
self,
hahr: Optional[int] = None,
vailee: Optional[int] = None,
yahr: Optional[int] = None,
) -> "DniDate":
hahr: int | None = None,
vailee: int | None = None,
yahr: int | None = None,
) -> DniDate:
"""Return a new D'ni date with new values for the specified fields."""
if hahr is None:
hahr = self._hahr
@ -119,7 +121,7 @@ class DniDate:
return type(self)(hahr, vailee, yahr)
# Arithmetic
def __add__(self, other: Any) -> "DniDate":
def __add__(self, other: Any) -> DniDate:
if isinstance(other, DniTimedelta):
ordinal = self.to_ordinal() + other._yahrtee
if 0 < ordinal <= MAX_DNI_ORDINAL:
@ -129,7 +131,7 @@ class DniDate:
__radd__ = __add__
def __sub__(self, other: Any) -> Union["DniDate", DniTimedelta]:
def __sub__(self, other: Any) -> DniDate | DniTimedelta:
if isinstance(other, DniTimedelta):
return self + DniTimedelta(-other._yahrtee)
if isinstance(other, DniDate):

View file

@ -1,14 +1,11 @@
"""The DniDatetime class."""
from datetime import datetime, timedelta, timezone
from typing import Any, Optional, TYPE_CHECKING, Union
from __future__ import annotations
from .constants import (
DniDateTimeTuple,
DNI_EPOCH_HAHR,
DNI_UNITS,
MAX_DNI_ORDINAL
)
from datetime import datetime, timedelta, timezone
from typing import TYPE_CHECKING, Any
from .constants import DNI_EPOCH_HAHR, DNI_UNITS, MAX_DNI_ORDINAL, DniDateTimeTuple
from .conversions import dni_to_earth, earth_to_dni, hvy2ord
from .dnidate import DniDate
from .dnitime import DniTime
@ -31,8 +28,8 @@ class DniDatetime:
_prorahn: int
# Class properties
min: "DniDatetime"
max: "DniDatetime"
min: DniDatetime
max: DniDatetime
resolution: DniTimedelta
def __new__(
@ -44,7 +41,7 @@ class DniDatetime:
tahvo: int = DNI_UNITS["tahvo"]["min"],
gorahn: int = DNI_UNITS["gorahn"]["min"],
prorahn: int = DNI_UNITS["prorahn"]["min"],
) -> "DniDatetime":
) -> DniDatetime:
if isinstance(hahr, int):
hahr, vailee, yahr = check_dni_date_fields(hahr, vailee, yahr)
gahrtahvo, tahvo, gorahn, prorahn = check_dni_time_fields(
@ -121,13 +118,13 @@ class DniDatetime:
def replace(
self,
hahr: Optional[int] = None,
vailee: Optional[int] = None,
yahr: Optional[int] = None,
gahrtahvo: Optional[int] = None,
tahvo: Optional[int] = None,
gorahn: Optional[int] = None,
prorahn: Optional[int] = None,
hahr: int | None = None,
vailee: int | None = None,
yahr: int | None = None,
gahrtahvo: int | None = None,
tahvo: int | None = None,
gorahn: int | None = None,
prorahn: int | None = None,
) -> "DniDatetime":
"""Return a new D'ni datetime with new values for the specified fields."""
if hahr is None:
@ -147,7 +144,7 @@ class DniDatetime:
return type(self)(hahr, vailee, yahr, gahrtahvo, tahvo, gorahn, prorahn)
# Arithmetic
def __add__(self, other: Any) -> "DniDatetime":
def __add__(self, other: Any) -> DniDatetime:
if not isinstance(other, (timedelta, DniTimedelta)):
return NotImplemented
if isinstance(other, timedelta):
@ -173,7 +170,7 @@ class DniDatetime:
__radd__ = __add__
def __sub__(self, other: Any) -> Union["DniDatetime", DniTimedelta]:
def __sub__(self, other: Any) -> DniDatetime | DniTimedelta:
if not isinstance(other, DniDatetime):
if isinstance(other, timedelta):
return self + -other
@ -277,7 +274,7 @@ class DniDatetime:
self._gahrtahvo,
self._tahvo,
self._gorahn,
self._prorahn
self._prorahn,
)
def to_ordinal(self) -> int:
@ -285,7 +282,7 @@ class DniDatetime:
return hvy2ord(self._hahr, self._vailee, self._yahr)
@classmethod
def combine(cls, date_obj: DniDate, time_obj: DniTime) -> "DniDatetime":
def combine(cls, date_obj: DniDate, time_obj: DniTime) -> DniDatetime:
"""Construct a D'ni datetime from a given DniDate and DniTime."""
# pylint: disable=protected-access
if not isinstance(date_obj, DniDate):
@ -302,12 +299,12 @@ class DniDatetime:
time_obj._prorahn,
)
def to_earth(self, tz: Optional[timezone] = timezone.utc) -> datetime:
def to_earth(self, tz: timezone | None = timezone.utc) -> datetime:
"""Create an earth datetime from a DniDatetime."""
return dni_to_earth(self.timetuple()).astimezone(tz)
@classmethod
def from_earth(cls, date_time: datetime) -> "DniDatetime":
def from_earth(cls, date_time: datetime) -> DniDatetime:
"""Create a DniDatetime from an earth datetime."""
ddt = earth_to_dni(date_time)
return cls(*ddt)

View file

@ -1,6 +1,8 @@
"""The DniTime class."""
from typing import Any, Optional, TYPE_CHECKING
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from .constants import DNI_UNITS
from .dnitimedelta import DniTimedelta
@ -19,8 +21,8 @@ class DniTime:
_prorahn: int
# Class properties
min: "DniTime"
max: "DniTime"
min: DniTime
max: DniTime
resolution: DniTimedelta
def __new__(
@ -29,7 +31,7 @@ class DniTime:
tahvo: int = DNI_UNITS["tahvo"]["min"],
gorahn: int = DNI_UNITS["gorahn"]["min"],
prorahn: int = DNI_UNITS["prorahn"]["min"],
) -> "DniTime":
) -> DniTime:
gahrtahvo, tahvo, gorahn, prorahn = check_dni_time_fields(
gahrtahvo, tahvo, gorahn, prorahn
)
@ -41,7 +43,7 @@ class DniTime:
return self
@classmethod
def from_isoformat(cls, time_str: str) -> "DniTime":
def from_isoformat(cls, time_str: str) -> DniTime:
if not isinstance(time_str, str):
raise TypeError("from_isoformat: argument must be str.")
@ -95,11 +97,11 @@ class DniTime:
def replace(
self,
gahrtahvo: Optional[int] = None,
tahvo: Optional[int] = None,
gorahn: Optional[int] = None,
prorahn: Optional[int] = None,
) -> "DniTime":
gahrtahvo: int | None = None,
tahvo: int | None = None,
gorahn: int | None = None,
prorahn: int | None = None,
) -> DniTime:
"""Return a new D'ni time with new values for the specified fields."""
if gahrtahvo is None:
gahrtahvo = self._gahrtahvo

View file

@ -1,8 +1,10 @@
"""The DniTimedelta class."""
from __future__ import annotations
from datetime import timedelta
from math import modf
from typing import Any, Tuple, TYPE_CHECKING, Union
from typing import TYPE_CHECKING, Any, Self
from .constants import DNI_UNITS, MAX_DNI_DELTA_YAHRTEE, MS_PER_PRORAHN
from .utils import cmp, divide_and_round
@ -19,19 +21,19 @@ class DniTimedelta:
_prorahntee: int
# Class properties
min: "DniTimedelta"
max: "DniTimedelta"
resolution: "DniTimedelta"
min: DniTimedelta
max: DniTimedelta
resolution: DniTimedelta
def __new__(
cls,
yahrtee: Union[float, int] = 0,
pahrtahvotee: Union[float, int] = 0,
prorahntee: Union[float, int] = 0,
gorahntee: Union[float, int] = 0,
tahvotee: Union[float, int] = 0,
gahrtahvotee: Union[float, int] = 0,
vaileetee: Union[float, int] = 0,
yahrtee: float | int = 0,
pahrtahvotee: float | int = 0,
prorahntee: float | int = 0,
gorahntee: float | int = 0,
tahvotee: float | int = 0,
gahrtahvotee: float | int = 0,
vaileetee: float | int = 0,
) -> "DniTimedelta":
# Final values are integer.
yahr: int = 0
@ -165,7 +167,7 @@ class DniTimedelta:
return self._prorahntee
# Arithmetic functions
def __add__(self, other: Any) -> "DniTimedelta":
def __add__(self, other: Any) -> DniTimedelta:
if isinstance(other, (timedelta, DniTimedelta)):
if isinstance(other, timedelta):
other = self.from_timedelta(other)
@ -178,7 +180,7 @@ class DniTimedelta:
__radd__ = __add__
def __sub__(self, other: Any) -> "DniTimedelta":
def __sub__(self, other: Any) -> DniTimedelta:
if isinstance(other, (timedelta, DniTimedelta)):
if isinstance(other, timedelta):
other = self.from_timedelta(other)
@ -189,25 +191,25 @@ class DniTimedelta:
)
return NotImplemented
def __rsub__(self, other: Any) -> "DniTimedelta":
def __rsub__(self, other: Any) -> DniTimedelta:
if isinstance(other, (timedelta, DniTimedelta)):
if isinstance(other, timedelta):
other = self.from_timedelta(other)
return -self + other
return NotImplemented
def __neg__(self) -> "DniTimedelta":
def __neg__(self) -> DniTimedelta:
return DniTimedelta(-self._yahrtee, -self._pahrtahvotee, -self._prorahntee)
def __pos__(self) -> "DniTimedelta":
def __pos__(self) -> DniTimedelta:
return self
def __abs__(self) -> "DniTimedelta":
def __abs__(self) -> DniTimedelta:
if self._yahrtee < 0:
return -self
return self
def __mul__(self, other: Any) -> "DniTimedelta":
def __mul__(self, other: Any) -> DniTimedelta:
if isinstance(other, int):
return DniTimedelta(
self._yahrtee * other,
@ -222,7 +224,7 @@ class DniTimedelta:
__rmul__ = __mul__
def __floordiv__(self, other: Any) -> Union["DniTimedelta", int]:
def __floordiv__(self, other: Any) -> DniTimedelta | int:
if not isinstance(other, (int, DniTimedelta)):
return NotImplemented
pro = self.total_prorahntee()
@ -231,7 +233,7 @@ class DniTimedelta:
if isinstance(other, int):
return DniTimedelta(0, 0, pro // other)
def __truediv__(self, other: Any) -> Union["DniTimedelta", float]:
def __truediv__(self, other: Any) -> DniTimedelta | float:
if not isinstance(other, (float, int, DniTimedelta)):
return NotImplemented
pro = self.total_prorahntee()
@ -243,13 +245,13 @@ class DniTimedelta:
if isinstance(other, int):
return DniTimedelta(0, 0, divide_and_round(pro, other))
def __mod__(self, other: Any) -> "DniTimedelta":
def __mod__(self, other: Any) -> DniTimedelta:
if isinstance(other, DniTimedelta):
rem = self.total_prorahntee() % other.total_prorahntee()
return DniTimedelta(0, 0, rem)
return NotImplemented
def __divmod__(self, other: Any) -> Tuple[int, "DniTimedelta"]:
def __divmod__(self, other: Any) -> tuple[int, DniTimedelta]:
if isinstance(other, DniTimedelta):
quot, rem = divmod(self.total_prorahntee(), other.total_prorahntee())
return quot, DniTimedelta(0, 0, rem)
@ -290,7 +292,7 @@ class DniTimedelta:
return self._yahrtee != 0 or self._pahrtahvotee != 0 or self._prorahntee != 0
@classmethod
def from_timedelta(cls, delta: timedelta) -> "DniTimedelta":
def from_timedelta(cls, delta: timedelta) -> DniTimedelta:
"""Converts a normal timedelta to a D'Ni timedelta."""
prorahntee = delta.total_seconds() * 1000 / MS_PER_PRORAHN
return cls(prorahntee=prorahntee)

View file

@ -3,7 +3,7 @@
# pylint: disable=line-too-long
from datetime import datetime, timedelta, timezone
from typing import Any, Optional, Tuple, Union
from typing import Any
from .constants import DNI_UNITS, EARTH_EPOCH, LEAP_MS_FROM_EPOCH
@ -18,7 +18,7 @@ def cmp(x: Any, y: Any) -> int:
return 0 if x == y else 1 if x > y else -1
def divide_and_round(a: Union[float, int], b: Union[float, int]) -> int:
def divide_and_round(a: float | int, b: float | int) -> int:
"""Divide a by b and round result to the nearest integer. When the ratio
is exactly half-way between two integers, the even integer is returned.
"""
@ -35,7 +35,7 @@ def check_dni_date_fields(
hahr: int = DNI_UNITS["hahr"]["min"],
vailee: int = DNI_UNITS["vailee"]["min"],
yahr: int = DNI_UNITS["yahr"]["min"],
) -> Tuple[int, int, int]:
) -> tuple[int, int, int]:
"""Verifies the D'Ni date fields are within proper boundaries."""
if not DNI_UNITS["hahr"]["min"] <= hahr <= DNI_UNITS["hahr"]["max"]:
raise ValueError(
@ -60,7 +60,7 @@ def check_dni_time_fields(
tahvo: int = DNI_UNITS["tahvo"]["min"],
gorahn: int = DNI_UNITS["gorahn"]["min"],
prorahn: int = DNI_UNITS["prorahn"]["min"],
) -> Tuple[int, int, int, int]:
) -> tuple[int, int, int, int]:
"""Verifies the D'Ni time fields are within proper boundaries."""
if (
not DNI_UNITS["gahrtahvo"]["min"]
@ -76,20 +76,12 @@ def check_dni_time_fields(
f"Tahvo must be in {DNI_UNITS['tahvo']['min']}..{DNI_UNITS['tahvo']['max'] - 1}",
tahvo,
)
if (
not DNI_UNITS["gorahn"]["min"]
<= gorahn
<= DNI_UNITS["gorahn"]["max"] - 1
):
if not DNI_UNITS["gorahn"]["min"] <= gorahn <= DNI_UNITS["gorahn"]["max"] - 1:
raise ValueError(
f"Gorahn must be in {DNI_UNITS['gorahn']['min']}..{DNI_UNITS['gorahn']['max'] - 1}",
gorahn,
)
if (
not DNI_UNITS["prorahn"]["min"]
<= prorahn
<= DNI_UNITS["prorahn"]["max"] - 1
):
if not DNI_UNITS["prorahn"]["min"] <= prorahn <= DNI_UNITS["prorahn"]["max"] - 1:
raise ValueError(
f"Prorahn must be in {DNI_UNITS['prorahn']['min']}..{DNI_UNITS['prorahn']['max'] - 1}",
prorahn,
@ -111,7 +103,7 @@ def add_leap_seconds(timestamp: int) -> int:
return timestamp + (leap_seconds * 1000)
def get_ms_from_epoch(date_time: Optional[datetime] = None) -> int:
def get_ms_from_epoch(date_time: datetime | None = None) -> int:
"""
Returns the amount of milliseconds since the UNIX epoch (1-1-1970).
"""
@ -124,7 +116,7 @@ def get_ms_from_epoch(date_time: Optional[datetime] = None) -> int:
return ((date - EARTH_EPOCH) // timedelta(microseconds=1)) // 1000
def get_adj_ms_from_epoch(date_time: Optional[datetime] = None) -> int:
def get_adj_ms_from_epoch(date_time: datetime | None = None) -> int:
"""
Returns the amount of milliseconds since the UNIX epoch (1-1-1970),
but also accounting for leap seconds.