From cd558daf5a601f64e98ad4ae2e7f6351be702632 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Mon, 12 Jul 2021 13:35:31 -0700 Subject: [PATCH] Add decorator for tracking save compat. Used to decorate functions or methods that have save compat code for a given major version. ``` @has_save_compat_for(5) def foo() -> None: ... ``` This function will raise an error at startup if it is decorated as having save compat for a version other than the current major version of the game. A new major version is the definition of a save compat break, so keeping around the old compat code serves no purpose other than hiding initialization bugs. The compat code and the decorator should be removed in the branch raising the error. --- game/savecompat.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++ game/version.py | 9 ++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 game/savecompat.py diff --git a/game/savecompat.py b/game/savecompat.py new file mode 100644 index 00000000..5388dd24 --- /dev/null +++ b/game/savecompat.py @@ -0,0 +1,48 @@ +"""Tools for aiding in save compat removal after compatibility breaks.""" +from collections import Callable +from typing import TypeVar + +from game.version import MAJOR_VERSION + +ReturnT = TypeVar("ReturnT") + + +class DeprecatedSaveCompatError(RuntimeError): + def __init__(self, function_name: str) -> None: + super().__init__( + f"{function_name} has save compat code for a different major version." + ) + + +def has_save_compat_for( + major: int, +) -> Callable[[Callable[..., ReturnT]], Callable[..., ReturnT]]: + """Declares a function or method as having save compat code for a given version. + + If the function has save compatibility for the current major version, there is no + change in behavior. + + If the function has save compatibility for a *different* (future or past) major + version, DeprecatedSaveCompatError will be raised during startup. Since a break in + save compatibility is the definition of a major version break, there's no need to + keep around old save compat code; it only serves to mask initialization bugs. + + Args: + major: The major version for which the decorated function has save + compatibility. + + Returns: + The decorated function or method. + + Raises: + DeprecatedSaveCompatError: The decorated function has save compat code for + another version of liberation, and that code (and the decorator declaring it) + should be removed from this branch. + """ + + def decorator(func: Callable[..., ReturnT]) -> Callable[..., ReturnT]: + if major != MAJOR_VERSION: + raise DeprecatedSaveCompatError(func.__name__) + return func + + return decorator diff --git a/game/version.py b/game/version.py index c25490e0..7c989e2a 100644 --- a/game/version.py +++ b/game/version.py @@ -1,8 +1,15 @@ from pathlib import Path +MAJOR_VERSION = 5 +MINOR_VERSION = 0 +MICRO_VERSION = 0 + + def _build_version_string() -> str: - components = ["5.0.0"] + components = [ + ".".join(str(v) for v in (MAJOR_VERSION, MINOR_VERSION, MICRO_VERSION)) + ] build_number_path = Path("resources/buildnumber") if build_number_path.exists(): with build_number_path.open("r") as build_number_file: