diff --git a/__init__.py b/__init__.py index 7b1eebe..3abff74 100644 --- a/__init__.py +++ b/__init__.py @@ -2,23 +2,63 @@ C64 Emulator Package """ -__version__ = "0.1.0" +from __future__ import annotations -from .emulator import C64 -from .cpu import CPU6502 -from .memory import MemoryMap -from .cpu_state import CPUState, CIATimer -from .debug import UdpDebugLogger -from .ui import TextualInterface -from .server import EmulatorServer +from typing import TYPE_CHECKING, Any + +__version__ = "1.0.1" __all__ = [ - 'C64', - 'CPU6502', - 'MemoryMap', - 'CPUState', - 'CIATimer', - 'UdpDebugLogger', - 'TextualInterface', - 'EmulatorServer', + "C64", + "CPU6502", + "MemoryMap", + "CPUState", + "CIATimer", + "UdpDebugLogger", + "TextualInterface", + "EmulatorServer", ] + +if TYPE_CHECKING: + from .cpu import CPU6502 + from .cpu_state import CPUState, CIATimer + from .debug import UdpDebugLogger + from .emulator import C64 + from .server import EmulatorServer + from .ui import TextualInterface + + +def __getattr__(name: str) -> Any: + if name == "C64": + from .emulator import C64 + + return C64 + if name == "CPU6502": + from .cpu import CPU6502 + + return CPU6502 + if name == "MemoryMap": + from .memory import MemoryMap + + return MemoryMap + if name == "CPUState": + from .cpu_state import CPUState + + return CPUState + if name == "CIATimer": + from .cpu_state import CIATimer + + return CIATimer + if name == "UdpDebugLogger": + from .debug import UdpDebugLogger + + return UdpDebugLogger + if name == "TextualInterface": + from .ui import TextualInterface + + return TextualInterface + if name == "EmulatorServer": + from .server import EmulatorServer + + return EmulatorServer + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/emulator.py b/emulator.py index 2718ddf..c4ebbac 100644 --- a/emulator.py +++ b/emulator.py @@ -48,7 +48,6 @@ from .debug import UdpDebugLogger from .memory import MemoryMap from .roms import REQUIRED_ROMS -from .ui import TextualInterface from .iec_bus import IECBus from .drives.tcp_drive_client import TcpDriveClient from .drives.iec_backend import IECDriveBackend @@ -121,6 +120,8 @@ def __init__( rust_hybrid_vic = vic_emulation == "accurate-rust" self.memory = MemoryMap() if interface_factory is None: + from .ui import TextualInterface + self.interface = TextualInterface(self) else: self.interface = interface_factory(self) diff --git a/pyproject.toml b/pyproject.toml index f09fbd3..6e1fac5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "c64py" -version = "0.1.0" +version = "1.0.1" description = "A Commodore 64 emulator implemented in Python with a text-based interface." readme = "README.md" requires-python = ">=3.9" @@ -18,7 +18,7 @@ dependencies = [ "tomli>=2.0.0; python_version<\"3.11\"", ] classifiers = [ - "Development Status :: 3 - Alpha", + "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", @@ -29,7 +29,7 @@ classifiers = [ ] [project.urls] -Homepage = "https://github.com/luar/c64py" +Homepage = "https://github.com/cyberplant/c64py" [project.scripts] c64py = "c64py.C64:main" diff --git a/rust/c64py-core/Cargo.lock b/rust/c64py-core/Cargo.lock index 5fe7c54..f0148c2 100644 --- a/rust/c64py-core/Cargo.lock +++ b/rust/c64py-core/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "c64py_rust_core" -version = "0.1.0" +version = "1.0.1" dependencies = [ "lazy_static", "libloading", @@ -32,9 +32,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.184" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libloading" diff --git a/rust/c64py-core/Cargo.toml b/rust/c64py-core/Cargo.toml index 230a778..fffc874 100644 --- a/rust/c64py-core/Cargo.toml +++ b/rust/c64py-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "c64py_rust_core" -version = "0.1.0" +version = "1.0.1" edition = "2021" rust-version = "1.83" description = "Optional native fast-path core for c64py (PyO3 extension)" diff --git a/test/test_headless_imports.py b/test/test_headless_imports.py new file mode 100644 index 0000000..f13debf --- /dev/null +++ b/test/test_headless_imports.py @@ -0,0 +1,27 @@ +"""Headless import guards (PyPy Docker smoke test has no textual/pygame).""" + +from __future__ import annotations + +import builtins +import importlib + + +def _block_textual_import(name, *args, **kwargs): + if name == "textual" or name.startswith("textual."): + raise AssertionError(f"unexpected textual import: {name!r}") + return _REAL_IMPORT(name, *args, **kwargs) + + +_REAL_IMPORT = builtins.__import__ + + +def test_c64py_config_import_without_textual(monkeypatch) -> None: + monkeypatch.setattr(builtins, "__import__", _block_textual_import) + importlib.import_module("c64py.config") + + +def test_emulator_headless_factory_without_textual(monkeypatch) -> None: + monkeypatch.setattr(builtins, "__import__", _block_textual_import) + from c64py.emulator import C64 + + C64(interface_factory=lambda _emu: None, vic_emulation="fast")