Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test with Python 3.13 #230

Merged
merged 2 commits into from
Oct 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
python-version: "3.12"

- name: Install Python packages
run: python -m pip install --upgrade build
Expand Down Expand Up @@ -57,7 +57,8 @@ jobs:
python:
- { version: "3.10" }
- { version: "3.11" }
- { version: "3.12.0-beta - 3.12.0" }
- { version: "3.12" }
- { version: "3.13" }

steps:
- name: Checkout
Expand Down
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
]
requires-python = ">=3.10"
dynamic = ["version"]
Expand All @@ -27,7 +29,6 @@ dependencies = [
"zstandard",
]


[project.readme]
file = "README.md"
content-type = "text/markdown"
Expand Down Expand Up @@ -67,7 +68,7 @@ lint.select = ["E", "F", "W", "C90", "I", "UP"]
src = ["src"]

# Version to target for generated code.
target-version = "py38"
target-version = "py310"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since 3.9 is still supported for a whole year, any reason why we dont target that for now?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Writing union types as X | Y was introduced in 3.10. I assume those changes needs to be reverted if we would target 3.9.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I thought they were introduced in 3.9. good to know.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That'd be a concern, but meds has requires-python = ">=3.10"


[tool.ruff.lint.mccabe]
# Flag errors (`C901`) whenever the complexity level exceeds 25.
Expand Down
5 changes: 2 additions & 3 deletions src/mercury_engine_data_structures/_dread_data_construct.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import struct
import typing

import construct

Expand Down Expand Up @@ -30,7 +29,7 @@ def __init__(self):
),
)

def _parse(self, stream, context, path) -> typing.Dict[str, int]:
def _parse(self, stream, context, path) -> dict[str, int]:
key_struct = struct.Struct("=H")
value_struct = struct.Struct("=Q")

Expand All @@ -44,7 +43,7 @@ def _parse(self, stream, context, path) -> typing.Dict[str, int]:

return result

def _build(self, obj: typing.Dict[str, int], stream, context, path):
def _build(self, obj: dict[str, int], stream, context, path):
return self._build_construct._build(list(obj.items()), stream, context, path)


Expand Down
3 changes: 1 addition & 2 deletions src/mercury_engine_data_structures/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import typing
from concurrent.futures import ProcessPoolExecutor
from pathlib import Path
from typing import Optional

from mercury_engine_data_structures import formats
from mercury_engine_data_structures.construct_extensions.json import convert_to_raw_python
Expand Down Expand Up @@ -205,7 +204,7 @@
input_path: Path = args.input_path
file_format: str = args.format
game: Game = args.game
limit: Optional[int] = args.limit
limit: int | None = args.limit

Check warning on line 207 in src/mercury_engine_data_structures/cli.py

View check run for this annotation

Codecov / codecov/patch

src/mercury_engine_data_structures/cli.py#L207

Added line #L207 was not covered by tests

def apply_limit(it):
if limit is None:
Expand Down
2 changes: 1 addition & 1 deletion src/mercury_engine_data_structures/common_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ def _emitbuild(code):
return result


def make_enum(values: typing.Union[typing.List[str], typing.Dict[str, int]], *, add_invalid: bool = True):
def make_enum(values: list[str] | dict[str, int], *, add_invalid: bool = True):
if isinstance(values, dict):
mapping = copy.copy(values)
else:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import enum
import typing

import construct


class StrictEnum(construct.Adapter):
def __init__(self, enum_class: typing.Type[enum.IntEnum]):
def __init__(self, enum_class: type[enum.IntEnum]):
super().__init__(construct.Int32ul)
self.enum_class = enum_class

def _decode(self, obj: int, context, path):
return self.enum_class(obj)

def _encode(self, obj: typing.Union[str, enum.IntEnum, int], context, path) -> int:
def _encode(self, obj: str | enum.IntEnum | int, context, path) -> int:
if isinstance(obj, str):
obj = getattr(self.enum_class, obj)

Expand All @@ -39,7 +38,7 @@ def _encode_enum_{i}(obj, io, this):
return f"_encode_enum_{i}(obj, io, this)"


def BitMaskEnum(enum_type: typing.Type[enum.IntEnum]):
def BitMaskEnum(enum_type: type[enum.IntEnum]):
flags = {}
for enumentry in enum_type:
flags[enumentry.name] = 2**enumentry.value
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import re
from typing import Dict, Optional, Type, Union

import construct


def _resolve_id(type_class: Union[construct.Construct, Type[construct.Construct]]) -> int:
def _resolve_id(type_class: construct.Construct | type[construct.Construct]) -> int:
if isinstance(type_class, construct.Renamed):
return _resolve_id(type_class.subcon)
return id(type_class)
Expand All @@ -16,8 +15,8 @@ def _resolve_id(type_class: Union[construct.Construct, Type[construct.Construct]

def emit_switch_cases_parse(
code: construct.CodeGen,
fields: Dict[Union[str, int], Union[construct.Construct, Type[construct.Construct]]],
custom_table_name: Optional[str] = None,
fields: dict[str | int, construct.Construct | type[construct.Construct]],
custom_table_name: str | None = None,
) -> str:
"""Construct codegen helper for handling the switch cases dict in _emitparse."""
table_name = custom_table_name
Expand Down Expand Up @@ -48,8 +47,8 @@ def {code_name}(io, this):

def emit_switch_cases_build(
code: construct.CodeGen,
fields: Dict[Union[str, int], Union[construct.Construct, Type[construct.Construct]]],
custom_table_name: Optional[str] = None,
fields: dict[str | int, construct.Construct | type[construct.Construct]],
custom_table_name: str | None = None,
) -> str:
"""Construct codegen helper for handling the switch cases dict in _emitbuild."""
table_name = custom_table_name
Expand Down
8 changes: 3 additions & 5 deletions src/mercury_engine_data_structures/crc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
Module for calculating CRC hashes with the algorithm and data used by Mercury Engine.
"""

import typing

_crc32_constants = [
0x00000000,
0x77073096,
Expand Down Expand Up @@ -523,7 +521,7 @@
]


def _algorithm(data: typing.Union[bytes, str], constants: typing.List[int], checksum: int) -> int:
def _algorithm(data: bytes | str, constants: list[int], checksum: int) -> int:
if isinstance(data, str):
data = data.encode("utf-8")

Expand All @@ -533,15 +531,15 @@ def _algorithm(data: typing.Union[bytes, str], constants: typing.List[int], chec
return checksum


def crc32(data: typing.Union[bytes, str]) -> int:
def crc32(data: bytes | str) -> int:
return _algorithm(
data,
_crc32_constants,
0xFFFFFFFF,
)


def crc64(data: typing.Union[bytes, str]) -> int:
def crc64(data: bytes | str) -> int:
return _algorithm(
data,
_crc64_constants,
Expand Down
15 changes: 7 additions & 8 deletions src/mercury_engine_data_structures/dread_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,21 @@
import json
import typing
from pathlib import Path
from typing import Dict, Optional

from mercury_engine_data_structures._dread_data_construct import KnownHashes

_root = Path(__file__).parent


@functools.lru_cache
def get_raw_types() -> Dict[str, typing.Any]:
def get_raw_types() -> dict[str, typing.Any]:
path = _root.joinpath("dread_types.json")
with path.open() as f:
return json.load(f)


@functools.lru_cache
def all_name_to_asset_id() -> Dict[str, int]:
def all_name_to_asset_id() -> dict[str, int]:
bin_path = _root.joinpath("dread_resource_names.bin")
if bin_path.exists():
return dict(KnownHashes.parse_file(bin_path))
Expand All @@ -28,16 +27,16 @@ def all_name_to_asset_id() -> Dict[str, int]:


@functools.lru_cache
def all_asset_id_to_name() -> Dict[int, str]:
def all_asset_id_to_name() -> dict[int, str]:
return {asset_id: name for name, asset_id in all_name_to_asset_id().items()}


def name_for_asset_id(asset_id: int) -> Optional[str]:
def name_for_asset_id(asset_id: int) -> str | None:
return all_asset_id_to_name().get(asset_id)


@functools.lru_cache
def all_name_to_property_id() -> Dict[str, int]:
def all_name_to_property_id() -> dict[str, int]:
bin_path = _root.joinpath("dread_property_names.bin")
if bin_path.exists():
return dict(KnownHashes.parse_file(bin_path))
Expand All @@ -48,13 +47,13 @@ def all_name_to_property_id() -> Dict[str, int]:


@functools.lru_cache
def all_property_id_to_name() -> Dict[int, str]:
def all_property_id_to_name() -> dict[int, str]:
names = all_name_to_property_id()

return {asset_id: name for name, asset_id in names.items()}


def all_files_ending_with(ext: str, exclusions: Optional[list[str]] = None) -> list[str]:
def all_files_ending_with(ext: str, exclusions: list[str] | None = None) -> list[str]:
if not ext.startswith("."):
ext = "." + ext

Expand Down
24 changes: 11 additions & 13 deletions src/mercury_engine_data_structures/file_tree_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import logging
import os.path
import typing
from collections.abc import Iterator
from pathlib import Path
from typing import Dict, Iterator, Optional, Set

import construct

Expand Down Expand Up @@ -60,11 +60,11 @@ class FileTreeEditor:
_modified_resources: mapping of asset id to bytes. When saving, these asset ids are replaced
"""

headers: Dict[str, construct.Container]
_files_for_asset_id: Dict[AssetId, Set[Optional[str]]]
_ensured_asset_ids: Dict[str, Set[AssetId]]
_modified_resources: Dict[AssetId, Optional[bytes]]
_in_memory_pkgs: Dict[str, Pkg]
headers: dict[str, construct.Container]
_files_for_asset_id: dict[AssetId, set[str | None]]
_ensured_asset_ids: dict[str, set[AssetId]]
_modified_resources: dict[AssetId, bytes | None]
_in_memory_pkgs: dict[str, Pkg]
_toc: Toc

def __init__(self, root: Path, target_game: Game):
Expand All @@ -78,7 +78,7 @@ def __init__(self, root: Path, target_game: Game):
def path_for_pkg(self, pkg_name: str) -> Path:
return self.root.joinpath(pkg_name)

def _add_pkg_name_for_asset_id(self, asset_id: AssetId, pkg_name: Optional[str]):
def _add_pkg_name_for_asset_id(self, asset_id: AssetId, pkg_name: str | None):
self._files_for_asset_id[asset_id] = self._files_for_asset_id.get(asset_id, set())
self._files_for_asset_id[asset_id].add(pkg_name)

Expand Down Expand Up @@ -154,7 +154,7 @@ def does_asset_exists(self, asset_id: NameOrAssetId) -> bool:

return asset_id in self._files_for_asset_id

def get_raw_asset(self, asset_id: NameOrAssetId, *, in_pkg: Optional[str] = None) -> bytes:
def get_raw_asset(self, asset_id: NameOrAssetId, *, in_pkg: str | None = None) -> bytes:
"""
Gets the bytes data for the given asset name/id, optionally restricting from which pkg.
:raises ValueError if the asset doesn't exist.
Expand Down Expand Up @@ -189,9 +189,7 @@ def get_raw_asset(self, asset_id: NameOrAssetId, *, in_pkg: Optional[str] = None

raise ValueError(f"Unknown asset_id: {original_name}")

def get_parsed_asset(
self, name: str, *, in_pkg: Optional[str] = None, type_hint: typing.Type[_T] = BaseResource
) -> _T:
def get_parsed_asset(self, name: str, *, in_pkg: str | None = None, type_hint: type[_T] = BaseResource) -> _T:
"""
Gets the resource with the given name and decodes it based on the extension.
"""
Expand All @@ -214,7 +212,7 @@ def get_file(self, path: str, type_hint: type[_T] = BaseResource) -> _T:
"""
return self.get_parsed_asset(path, type_hint=type_hint)

def add_new_asset(self, name: str, new_data: typing.Union[bytes, BaseResource], in_pkgs: typing.Iterable[str]):
def add_new_asset(self, name: str, new_data: bytes | BaseResource, in_pkgs: typing.Iterable[str]):
"""
Adds an asset that doesn't already exist.
"""
Expand All @@ -241,7 +239,7 @@ def add_new_asset(self, name: str, new_data: typing.Union[bytes, BaseResource],
for pkg_name in in_pkgs:
self.ensure_present(pkg_name, asset_id)

def replace_asset(self, asset_id: NameOrAssetId, new_data: typing.Union[bytes, BaseResource]):
def replace_asset(self, asset_id: NameOrAssetId, new_data: bytes | BaseResource):
"""
Replaces an existing asset.
See `add_new_asset` for new assets.
Expand Down
4 changes: 1 addition & 3 deletions src/mercury_engine_data_structures/formats/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from typing import Type

from mercury_engine_data_structures.formats.bapd import Bapd
from mercury_engine_data_structures.formats.base_resource import AssetType, BaseResource
from mercury_engine_data_structures.formats.bcmdl import Bcmdl
Expand Down Expand Up @@ -105,5 +103,5 @@
}


def format_for(type_name: AssetType) -> Type[BaseResource]:
def format_for(type_name: AssetType) -> type[BaseResource]:
return ALL_FORMATS[type_name.upper()]
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def raw(self) -> Container:

AssetType = str
AssetId = int
NameOrAssetId = typing.Union[str, AssetId]
NameOrAssetId = str | AssetId


def resolve_asset_id(value: NameOrAssetId, game: Game) -> AssetId:
Expand Down
4 changes: 1 addition & 3 deletions src/mercury_engine_data_structures/formats/bmmdef.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from typing import Tuple

from construct import Construct, Container

from mercury_engine_data_structures.formats import standard_format
Expand All @@ -25,7 +23,7 @@ def add_icon(
uSpriteCol: int,
sInspectorLabel: str,
sDisabledIconId: str = "",
vAnchorOffset: Tuple[int, int] = (0, 0),
vAnchorOffset: tuple[int, int] = (0, 0),
bAutoScale: bool = True,
**kwargs,
):
Expand Down
2 changes: 1 addition & 1 deletion src/mercury_engine_data_structures/formats/bmsad.py
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ def __set__(self, inst: ActorDefFunc, value: T):


Vec3 = list
FieldType = typing.Union[bool, str, float, int, Vec3]
FieldType = bool | str | float | int | Vec3


class ComponentFields:
Expand Down
4 changes: 2 additions & 2 deletions src/mercury_engine_data_structures/formats/bmsld.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import logging
from typing import Iterator, Tuple
from collections.abc import Iterator

import construct
from construct import Const, Construct, Container, Flag, Float32l, Hex, Int32ul, Struct, Switch
Expand Down Expand Up @@ -145,7 +145,7 @@ class Bmsld(BaseResource):
def construct_class(cls, target_game: Game) -> Construct:
return BMSLD

def all_actors(self) -> Iterator[Tuple[int, str, construct.Container]]:
def all_actors(self) -> Iterator[tuple[int, str, construct.Container]]:
for layer in self.raw.actors:
for actor_name, actor in layer.items():
yield layer, actor_name, actor
Expand Down
Loading