diff --git a/.github/workflows/python-build.yml b/.github/workflows/python-build.yml index 089b016..d1249c0 100644 --- a/.github/workflows/python-build.yml +++ b/.github/workflows/python-build.yml @@ -1,6 +1,3 @@ -# This workflow will install Python dependencies, run tests and lint with a variety of Python versions -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions - name: Build on: @@ -12,24 +9,29 @@ jobs: strategy: fail-fast: false matrix: - python-version: [ '3.11', '3.12' ] - os: [ macos-15, windows-2025 ] + cfg: + - { os: windows-2025, python-version: '3.14', architecture: x64 } + - { os: windows-11-arm, python-version: '3.14', architecture: arm64 } + - { os: macos-15, python-version: '3.14', architecture: arm64 } - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.cfg.os }} defaults: run: shell: bash steps: - name: Clone - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: 'true' + - name: Setup cmake + uses: jwlawson/actions-setup-cmake@v2 + - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: - python-version: ${{ matrix.python-version }} + python-version: ${{ matrix.cfg.python-version }} - name: Install dependencies shell: bash diff --git a/.github/workflows/python-stylecheck.yml b/.github/workflows/python-stylecheck.yml index b2a92a1..6b8a19d 100644 --- a/.github/workflows/python-stylecheck.yml +++ b/.github/workflows/python-stylecheck.yml @@ -18,16 +18,15 @@ jobs: steps: - name: Clone - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: - python-version: 3.12 + python-version: 3.14 - name: Install dependencies run: | - python -m pip install --upgrade pip pip install black - name: run stylecheck diff --git a/.github/workflows/python-unittests.yml b/.github/workflows/python-unittests.yml index 0af33c0..3c7845a 100644 --- a/.github/workflows/python-unittests.yml +++ b/.github/workflows/python-unittests.yml @@ -1,6 +1,3 @@ -# This workflow will install Python dependencies, run tests and lint with a variety of Python versions -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions - name: Unittests on: @@ -17,25 +14,33 @@ jobs: strategy: fail-fast: false matrix: - python-version: [ '3.11', '3.12' ] - os: [ macos-15, windows-2025, ubuntu-24.04 ] + cfg: + - { os: windows-2025, python-version: '3.14', architecture: x64 } + - { os: windows-11-arm, python-version: '3.14', architecture: arm64 } + - { os: macos-15-intel, python-version: '3.14', architecture: x64 } + - { os: macos-15, python-version: '3.14', architecture: arm64 } + - { os: ubuntu-24.04, python-version: '3.14', architecture: x64 } - runs-on: ${{ matrix.os }} - timeout-minutes: 30 + runs-on: ${{ matrix.cfg.os }} + timeout-minutes: 45 defaults: run: shell: bash steps: - name: Clone - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: 'true' + - name: Setup cmake + uses: jwlawson/actions-setup-cmake@v2 + - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: - python-version: ${{ matrix.python-version }} + python-version: ${{ matrix.cfg.python-version }} + architecture: ${{ matrix.cfg.architecture }} - name: Build run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 3dee961..84bd3ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.13) +cmake_minimum_required(VERSION 4.1) project(amulet_game LANGUAGES CXX) @@ -26,6 +26,8 @@ if (MSVC) add_definitions("/MP") endif() +find_package(Python3 COMPONENTS Interpreter Development REQUIRED) + # Find libraries if (NOT TARGET pybind11::module) find_package(pybind11 CONFIG REQUIRED) @@ -57,6 +59,7 @@ list(REMOVE_ITEM HEADERS ${EXTENSION_HEADERS}) # Add implementation add_library(amulet_game SHARED) +set_target_properties(amulet_game PROPERTIES CXX_VISIBILITY_PRESET hidden) set_target_properties(amulet_game PROPERTIES FOLDER "CPP") target_compile_definitions(amulet_game PRIVATE ExportAmuletGame) target_link_libraries(amulet_game PUBLIC pybind11::module) @@ -76,6 +79,7 @@ endforeach() # Add python extension pybind11_add_module(_amulet_game) +set_target_properties(_amulet_game PROPERTIES CXX_VISIBILITY_PRESET hidden) set_target_properties(_amulet_game PROPERTIES FOLDER "Python") target_link_libraries(_amulet_game PRIVATE amulet_pybind11_extensions) target_link_libraries(_amulet_game PRIVATE amulet_game) diff --git a/build_requires.py b/build_requires.py index d3eecb1..a295137 100644 --- a/build_requires.py +++ b/build_requires.py @@ -13,7 +13,6 @@ def get_requires_for_build_wheel( ) -> list[str]: return [ *build_meta.get_requires_for_build_wheel(config_settings), - "wheel", *requirements.get_build_dependencies(), ] diff --git a/get_compiler/CMakeLists.txt b/get_compiler/CMakeLists.txt index dc80a97..a664476 100644 --- a/get_compiler/CMakeLists.txt +++ b/get_compiler/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.13) +cmake_minimum_required(VERSION 4.1) project(get_compiler LANGUAGES CXX) diff --git a/mypy.ini b/mypy.ini index 99ccf96..3f9b947 100644 --- a/mypy.ini +++ b/mypy.ini @@ -2,7 +2,7 @@ disallow_untyped_defs = True check_untyped_defs = True warn_return_any = True -python_version = 3.12 +python_version = 3.14 explicit_package_bases = True mypy_path = $MYPY_CONFIG_FILE_DIR/src,$MYPY_CONFIG_FILE_DIR/tests files = diff --git a/pyproject.toml b/pyproject.toml index b79639f..c7b2049 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ authors = [ ] description = "A Minecraft metadata and low level translation library." dynamic = ["version", "readme", "dependencies"] -requires-python = ">=3.11" +requires-python = ">=3.14" classifiers = [ "Programming Language :: Python :: 3", "Operating System :: OS Independent", @@ -27,7 +27,6 @@ dev = [ "versioneer", "types-versioneer", "packaging", - "wheel", "pybind11_stubgen>=2.5.4", "black>=22.3", "isort", @@ -53,7 +52,7 @@ include-package-data = false "**/*.so", "**/*.dylib", "**/*.lib", - "**/*.pkl.gz" + "**/*.pkl.gz", ] [tool.setuptools.dynamic] diff --git a/requirements.py b/requirements.py index 6e3a501..c039a27 100644 --- a/requirements.py +++ b/requirements.py @@ -7,10 +7,10 @@ PYBIND11_REQUIREMENT = "==3.0.1" AMULET_PYBIND11_EXTENSIONS_REQUIREMENT = "~=1.2.0.0a2" AMULET_IO_REQUIREMENT = "~=2.0.0.0a0" -AMULET_UTILS_REQUIREMENT = "~=1.1.3.0a6" -AMULET_ZLIB_REQUIREMENT = "~=1.0.8.0a0" -AMULET_NBT_REQUIREMENT = "~=5.0.3.0a0" -AMULET_CORE_REQUIREMENT = "~=2.0.8.0a0" +AMULET_UTILS_REQUIREMENT = "~=1.1.4.0a" +AMULET_ZLIB_REQUIREMENT = "~=1.0.9.0a0" +AMULET_NBT_REQUIREMENT = "~=5.0.4.0a0" +AMULET_CORE_REQUIREMENT = "~=2.0.9.0a0" if os.environ.get("AMULET_PYBIND11_EXTENSIONS_REQUIREMENT", None): AMULET_PYBIND11_EXTENSIONS_REQUIREMENT = f"{AMULET_PYBIND11_EXTENSIONS_REQUIREMENT},{os.environ['AMULET_PYBIND11_EXTENSIONS_REQUIREMENT']}" diff --git a/setup.py b/setup.py index 03564a3..38e7cfc 100644 --- a/setup.py +++ b/setup.py @@ -9,6 +9,7 @@ import pickle from tempfile import TemporaryDirectory from typing import TypeAlias, TYPE_CHECKING +import sysconfig from setuptools import setup, Extension, Command from setuptools.command.build import build @@ -52,10 +53,14 @@ def build_extension(self, ext: Extension) -> None: platform_args = [] if sys.platform == "win32": platform_args.extend(["-G", "Visual Studio 17 2022"]) - if sys.maxsize > 2**32: + if sysconfig.get_platform() == "win-amd64": platform_args.extend(["-A", "x64"]) - else: + elif sysconfig.get_platform() == "win32": platform_args.extend(["-A", "Win32"]) + elif sysconfig.get_platform() == "win-arm64": + platform_args.extend(["-A", "ARM64"]) + else: + raise RuntimeError(f"Unsupported platform: {sysconfig.get_platform()}") platform_args.extend(["-T", "v143"]) elif sys.platform == "darwin": if platform.machine() == "arm64": @@ -68,7 +73,7 @@ def build_extension(self, ext: Extension) -> None: [ "cmake", *platform_args, - f"-DPYTHON_EXECUTABLE={sys.executable}", + f"-DPython3_EXECUTABLE={fix_path(sys.executable)}", f"-Dpybind11_DIR={fix_path(pybind11.get_cmake_dir())}", f"-Damulet_pybind11_extensions_DIR={fix_path(amulet.pybind11_extensions.__path__[0])}", f"-Damulet_io_DIR={fix_path(amulet.io.__path__[0])}", @@ -102,6 +107,9 @@ def finalize_options(self) -> None: self.set_undefined_options("build_py", ("build_lib", "build_lib")) def run(self) -> None: + if os.environ.get("AMULET_SKIP_COMPILE", None): + return + # This is rather janky but it is a stop-gap until the whole library can be ported to C++ if self.editable_mode: src_dir = os.path.abspath("src") diff --git a/src/amulet/game/__init__.py b/src/amulet/game/__init__.py index 4fe7cd9..93a66d1 100644 --- a/src/amulet/game/__init__.py +++ b/src/amulet/game/__init__.py @@ -1,8 +1,5 @@ -""" -A module to store data about the game including state enumerations and translations between different game versions. -""" - import logging as _logging +import os from . import _version @@ -17,6 +14,9 @@ def _init() -> None: import sys import ctypes + if os.environ.get("AMULET_SKIP_COMPILE", None): + return + if sys.platform == "win32": lib_path = os.path.join(os.path.dirname(__file__), "amulet_game.dll") elif sys.platform == "darwin": @@ -41,7 +41,10 @@ def _init() -> None: _init() +del _init + -from .game import get_game_platforms, get_game_versions, get_game_version -from .java import JavaGameVersion -from .bedrock import BedrockGameVersion +if not os.environ.get("AMULET_SKIP_COMPILE", None): + from .game import get_game_platforms, get_game_versions, get_game_version + from .java import JavaGameVersion + from .bedrock import BedrockGameVersion diff --git a/src/amulet/game/_amulet_game.py.cpp b/src/amulet/game/_amulet_game.py.cpp index 4af89a7..c532605 100644 --- a/src/amulet/game/_amulet_game.py.cpp +++ b/src/amulet/game/_amulet_game.py.cpp @@ -5,20 +5,19 @@ namespace py = pybind11; namespace pyext = Amulet::pybind11_extensions; -void init_java(py::module); +void init_amulet_game(py::module); -void init_module(py::module m) +static void _init_amulet_game(py::module m) { pyext::init_compiler_config(m); pyext::check_compatibility(py::module::import("amulet.utils"), m); pyext::check_compatibility(py::module::import("amulet.zlib"), m); pyext::check_compatibility(py::module::import("amulet.nbt"), m); pyext::check_compatibility(py::module::import("amulet.core"), m); - - init_java(m); + init_amulet_game(m); } PYBIND11_MODULE(_amulet_game, m) { - m.def("init", &init_module, py::arg("m")); + m.def("init", &_init_amulet_game, py::arg("m")); } diff --git a/src/amulet/game/_amulet_game_.py.cpp b/src/amulet/game/_amulet_game_.py.cpp new file mode 100644 index 0000000..907ca3d --- /dev/null +++ b/src/amulet/game/_amulet_game_.py.cpp @@ -0,0 +1,9 @@ +#include + +namespace py = pybind11; + +void init_java(py::module); + +void init_amulet_game(py::module m){ + init_java(m); +} diff --git a/src/amulet/game/abc/biome.py b/src/amulet/game/abc/biome.py index ca115f5..6abb062 100644 --- a/src/amulet/game/abc/biome.py +++ b/src/amulet/game/abc/biome.py @@ -12,7 +12,6 @@ from .game_version_container import GameVersionContainer - if TYPE_CHECKING: from .version import GameVersion diff --git a/src/amulet/game/dll.hpp b/src/amulet/game/dll.hpp index fbe63a0..7906735 100644 --- a/src/amulet/game/dll.hpp +++ b/src/amulet/game/dll.hpp @@ -8,14 +8,6 @@ #define AMULET_GAME_EXPORT __declspec(dllimport) #endif #else - #define AMULET_GAME_EXPORT - #endif -#endif - -#if !defined(AMULET_GAME_EXPORT_EXCEPTION) - #if defined(_LIBCPP_EXCEPTION) - #define AMULET_GAME_EXPORT_EXCEPTION __attribute__((visibility("default"))) - #else - #define AMULET_GAME_EXPORT_EXCEPTION + #define AMULET_GAME_EXPORT __attribute__((visibility("default"))) #endif #endif diff --git a/src/amulet/game/game.py b/src/amulet/game/game.py index ade708d..d0f0ea9 100644 --- a/src/amulet/game/game.py +++ b/src/amulet/game/game.py @@ -12,7 +12,6 @@ from amulet.core.version import VersionNumber from amulet.utils.cast import dynamic_cast - if TYPE_CHECKING: from .abc import GameVersion from .universal import UniversalVersion diff --git a/src/amulet/game/translate/_functions/_code_functions/bedrock_sign.py b/src/amulet/game/translate/_functions/_code_functions/bedrock_sign.py index b48b65b..b5c4c22 100644 --- a/src/amulet/game/translate/_functions/_code_functions/bedrock_sign.py +++ b/src/amulet/game/translate/_functions/_code_functions/bedrock_sign.py @@ -6,7 +6,6 @@ from ._text import RawTextComponent, ExtendedBedrockSectionParser from .._state import SrcData, StateData, DstData - BedrockFrontText = str(uuid4()) BedrockBackText = str(uuid4()) diff --git a/src/amulet/game/translate/_functions/_code_functions/custom_name.py b/src/amulet/game/translate/_functions/_code_functions/custom_name.py index d798732..e0a10f5 100644 --- a/src/amulet/game/translate/_functions/_code_functions/custom_name.py +++ b/src/amulet/game/translate/_functions/_code_functions/custom_name.py @@ -12,7 +12,6 @@ JavaSectionParser, ) - JavaSectionText = str(uuid4()) JavaRawText = str(uuid4()) BedrockSectionText = str(uuid4()) diff --git a/src/amulet/game/translate/_functions/code.py b/src/amulet/game/translate/_functions/code.py index 9cd268d..797dc30 100644 --- a/src/amulet/game/translate/_functions/code.py +++ b/src/amulet/game/translate/_functions/code.py @@ -38,7 +38,6 @@ to_universal as bedrock_skull_rotation_2u, ) - T = TypeVar("T") diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e6a4b4e..f562dd9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.13) +cmake_minimum_required(VERSION 4.1) project(amulet_game_tests LANGUAGES CXX) @@ -19,6 +19,8 @@ else() message( FATAL_ERROR "Unsupported platform. Please submit a pull request to support this platform." ) endif() +find_package(Python3 COMPONENTS Interpreter Development REQUIRED) + # Find dependencies if (NOT TARGET pybind11::module) find_package(pybind11 CONFIG REQUIRED) @@ -37,6 +39,7 @@ endif() file(GLOB_RECURSE SOURCES LIST_DIRECTORIES false "${CMAKE_CURRENT_LIST_DIR}/*.py.cpp") pybind11_add_module(_test_amulet_game) +set_target_properties(_test_amulet_game PROPERTIES CXX_VISIBILITY_PRESET hidden) set_target_properties(_test_amulet_game PROPERTIES FOLDER "Tests") target_compile_definitions(_test_amulet_game PRIVATE PYBIND11_DETAILED_ERROR_MESSAGES) target_compile_definitions(_test_amulet_game PRIVATE PYBIND11_VERSION="${pybind11_VERSION}") diff --git a/tests/test_amulet_game/_test_amulet_game.py.cpp b/tests/test_amulet_game/_test_amulet_game.py.cpp index bf91c35..d1f86c7 100644 --- a/tests/test_amulet_game/_test_amulet_game.py.cpp +++ b/tests/test_amulet_game/_test_amulet_game.py.cpp @@ -5,15 +5,14 @@ namespace py = pybind11; namespace pyext = Amulet::pybind11_extensions; -void init_test_java(py::module); +void init_test_amulet_game(py::module); -void init_module(py::module m){ +static void _init_test_amulet_game(py::module m){ pyext::init_compiler_config(m); pyext::check_compatibility(py::module::import("amulet.game"), m); - - init_test_java(m); + init_test_amulet_game(m); } PYBIND11_MODULE(_test_amulet_game, m) { - m.def("init", &init_module, py::arg("m")); + m.def("init", &_init_test_amulet_game, py::arg("m")); } diff --git a/tests/test_amulet_game/_test_amulet_game_.py.cpp b/tests/test_amulet_game/_test_amulet_game_.py.cpp new file mode 100644 index 0000000..f3cea4c --- /dev/null +++ b/tests/test_amulet_game/_test_amulet_game_.py.cpp @@ -0,0 +1,9 @@ +#include + +namespace py = pybind11; + +void init_test_java(py::module); + +void init_test_amulet_game(py::module m){ + init_test_java(m); +} diff --git a/tools/cmake_generate.py b/tools/cmake_generate.py index a5a9856..25f54fa 100644 --- a/tools/cmake_generate.py +++ b/tools/cmake_generate.py @@ -2,6 +2,7 @@ import subprocess import os import shutil +import sysconfig import pybind11 import amulet.pybind11_extensions @@ -23,10 +24,14 @@ def main() -> None: platform_args = [] if sys.platform == "win32": platform_args.extend(["-G", "Visual Studio 17 2022"]) - if sys.maxsize > 2**32: + if sysconfig.get_platform() == "win-amd64": platform_args.extend(["-A", "x64"]) - else: + elif sysconfig.get_platform() == "win32": platform_args.extend(["-A", "Win32"]) + elif sysconfig.get_platform() == "win-arm64": + platform_args.extend(["-A", "ARM64"]) + else: + raise RuntimeError(f"Unsupported platform: {sysconfig.get_platform()}") platform_args.extend(["-T", "v143"]) os.chdir(RootDir) @@ -38,7 +43,7 @@ def main() -> None: [ "cmake", *platform_args, - f"-DPYTHON_EXECUTABLE={sys.executable}", + f"-DPython3_EXECUTABLE={fix_path(sys.executable)}", f"-Dpybind11_DIR={fix_path(pybind11.get_cmake_dir())}", f"-Damulet_pybind11_extensions_DIR={fix_path(amulet.pybind11_extensions.__path__[0])}", f"-Damulet_io_DIR={fix_path(amulet.io.__path__[0])}", diff --git a/tools/compile_tests.py b/tools/compile_tests.py index 4ae0b26..355cf23 100644 --- a/tools/compile_tests.py +++ b/tools/compile_tests.py @@ -2,6 +2,7 @@ import sys import shutil import os +import sysconfig import pybind11 import amulet.pybind11_extensions @@ -25,10 +26,14 @@ def main() -> None: platform_args = [] if sys.platform == "win32": platform_args.extend(["-G", "Visual Studio 17 2022"]) - if sys.maxsize > 2**32: + if sysconfig.get_platform() == "win-amd64": platform_args.extend(["-A", "x64"]) - else: + elif sysconfig.get_platform() == "win32": platform_args.extend(["-A", "Win32"]) + elif sysconfig.get_platform() == "win-arm64": + platform_args.extend(["-A", "ARM64"]) + else: + raise RuntimeError(f"Unsupported platform: {sysconfig.get_platform()}") platform_args.extend(["-T", "v143"]) os.chdir(TestsDir) @@ -40,7 +45,7 @@ def main() -> None: [ "cmake", *platform_args, - f"-DPYTHON_EXECUTABLE={sys.executable}", + f"-DPython3_EXECUTABLE={fix_path(sys.executable)}", f"-Dpybind11_DIR={fix_path(pybind11.get_cmake_dir())}", f"-Damulet_pybind11_extensions_DIR={fix_path(amulet.pybind11_extensions.__path__[0])}", f"-Damulet_io_DIR={fix_path(amulet.io.__path__[0])}", diff --git a/tools/generate_pybind_stubs.py b/tools/generate_pybind_stubs.py index f106c12..dfba4da 100644 --- a/tools/generate_pybind_stubs.py +++ b/tools/generate_pybind_stubs.py @@ -8,7 +8,6 @@ from pybind11_stubgen.structs import Identifier from pybind11_stubgen.parser.mixins.filter import FilterClassMembers - ForwardRefPattern = re.compile(r"ForwardRef\('(?P[a-zA-Z_][a-zA-Z0-9_]*)'\)") QuotePattern = re.compile(r"'(?P[a-zA-Z_][a-zA-Z0-9_]*)'")