From 66c8efd579e585ccd75cd70012029a1529df7115 Mon Sep 17 00:00:00 2001 From: Kevin Wenner Date: Thu, 25 Sep 2025 17:28:04 +0000 Subject: [PATCH] test on osx & windows --- .github/workflows/ci.yml | 42 +++++++++++++++++++++++++++++++++++++++- .readthedocs.yaml | 2 +- README.md | 4 ++-- pyproject.toml | 2 ++ src/tinypg/__init__.py | 1 - src/tinypg/binaries.py | 29 ++++++++++++++++++++------- src/tinypg/core.py | 12 ++++++++---- 7 files changed, 76 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b3bc149..600b547 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,4 +39,44 @@ jobs: - name: Check formatting run: | uv run --python ${{ matrix.python-version }} black --check . - uv run --python ${{ matrix.python-version }} isort --check-only . \ No newline at end of file + uv run --python ${{ matrix.python-version }} isort --check-only . + + test-osx: + name: macOS Python 3.12 + runs-on: macos-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Set up uv + uses: astral-sh/setup-uv@v6 + + - name: Install Python 3.12 + run: uv python install 3.12 + + - name: Install the project + run: uv sync --locked --all-extras --dev + + - name: Run tests + run: uv run --python 3.12 pytest + + test-windows: + name: Windows Python 3.12 + runs-on: windows-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Set up uv + uses: astral-sh/setup-uv@v6 + + - name: Install Python 3.12 + run: uv python install 3.12 + + - name: Install the project + run: uv sync --locked --all-extras --dev + + - name: Run tests + run: uv run --python 3.12 pytest \ No newline at end of file diff --git a/.readthedocs.yaml b/.readthedocs.yaml index f8bcc10..7c38cfc 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -6,4 +6,4 @@ build: python: "3.12" commands: - pip install .[docs] - - python -m pdoc --docformat google --output-directory $READTHEDOCS_OUTPUT/html tinypg \ No newline at end of file + - python -m pdoc --docformat google --output-directory $READTHEDOCS_OUTPUT/html --logo https://iili.io/Klv1Zcx.md.png tinypg \ No newline at end of file diff --git a/README.md b/README.md index 073c7fd..8c6ddbf 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -[![Klv1Zcx.md.png](https://iili.io/Klv1Zcx.md.png)](https://freeimage.host/i/Klv1Zcx) +![logo](https://iili.io/Klv1Zcx.md.png) # TinyPG @@ -9,7 +9,7 @@ A Python package for creating ephemeral PostgreSQL databases, inspired by [ephem TinyPG provides a clean Python API for creating temporary PostgreSQL databases for development and testing. It's designed to be self-contained and work without requiring system-wide PostgreSQL installation. -**Currently only tested on linux, but should work on OSX and Windows hopefully** +**Works on Linux, OSX and Windows** ## Features diff --git a/pyproject.toml b/pyproject.toml index ff33e8c..e7f074e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,8 @@ classifiers = [ "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: POSIX :: Linux", + "Operating System :: MacOS :: MacOS X", + "Operating System :: Microsoft :: Windows", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", diff --git a/src/tinypg/__init__.py b/src/tinypg/__init__.py index 1f704e7..af938b1 100644 --- a/src/tinypg/__init__.py +++ b/src/tinypg/__init__.py @@ -1,5 +1,4 @@ """ -[![Klv1Zcx.md.png](https://iili.io/Klv1Zcx.md.png)](https://freeimage.host/i/Klv1Zcx) TinyPG: Ephemeral PostgreSQL databases for Python development and testing. diff --git a/src/tinypg/binaries.py b/src/tinypg/binaries.py index 79872df..4ced4e4 100644 --- a/src/tinypg/binaries.py +++ b/src/tinypg/binaries.py @@ -22,6 +22,9 @@ class PostgreSQLBinaries: """Manages PostgreSQL binary installation and versioning.""" + PLATFORM = platform.machine().lower() + SYSTEM = platform.system().lower() + # PostgreSQL versions supported (from pg-embed) SUPPORTED_VERSIONS = { "17": "17.2.0", @@ -50,7 +53,7 @@ def __init__(self, cache_dir: Optional[Path] = None): def _detect_os(self) -> str: """Detect the operating system.""" - system = platform.system().lower() + system = self.SYSTEM if system == "darwin": return "darwin" elif system == "windows": @@ -63,7 +66,7 @@ def _detect_os(self) -> str: def _detect_arch(self) -> str: """Detect the CPU architecture.""" - machine = platform.machine().lower() + machine = self.PLATFORM if machine in ["x86_64", "amd64"]: return "amd64" elif machine in ["i386", "i686"]: @@ -81,6 +84,17 @@ def _get_platform_string(self) -> str: """Get the platform string for binary downloads.""" return f"{self.os_name}-{self.arch}" + @staticmethod + def _get_binary_path( + manager: "PostgreSQLBinaries", version: str, binary_name: str + ) -> str: + install_dir = manager._get_install_dir(version) + binary_path = install_dir / "bin" / binary_name + + if manager.SYSTEM == "windows": + binary_path = binary_path.with_suffix(".exe") + return binary_path + @classmethod def ensure_version(cls, version: str) -> Path: """ @@ -142,9 +156,7 @@ def get_binary_path(cls, binary_name: str, version: str = None) -> Path: return Path(system_binary) # Check our installation - install_dir = manager._get_install_dir(version) - binary_path = install_dir / "bin" / binary_name - + binary_path = cls._get_binary_path(manager, version, binary_name) if binary_path.exists() and os.access(binary_path, os.X_OK): return binary_path @@ -311,8 +323,8 @@ def _is_version_installed(self, version: str) -> bool: install_dir = self._get_install_dir(version) # Check if all required binaries exist - for binary in self.REQUIRED_BINARIES: - binary_path = install_dir / "bin" / binary + for binary_name in self.REQUIRED_BINARIES: + binary_path = self._get_binary_path(self, version, binary_name) if not (binary_path.exists() and os.access(binary_path, os.X_OK)): return False @@ -366,6 +378,9 @@ def _verify_installation(self, install_dir: Path) -> None: for binary in self.REQUIRED_BINARIES: binary_path = bin_dir / binary + if self.SYSTEM == "windows": + binary_path = binary_path.with_suffix(".exe") + if not (binary_path.exists() and os.access(binary_path, os.X_OK)): raise BinaryNotFoundError(f"Binary {binary} not found in installation") diff --git a/src/tinypg/core.py b/src/tinypg/core.py index ad34526..269900e 100644 --- a/src/tinypg/core.py +++ b/src/tinypg/core.py @@ -18,7 +18,6 @@ from .config import TinyPGConfig from .exceptions import ( DatabaseStartError, - DatabaseTimeoutError, InitDBError, ProcessError, ) @@ -210,8 +209,13 @@ def load_sql_file(self, file_path: Path) -> None: def _initialize_database(self) -> Path: """Initialize a new PostgreSQL database cluster.""" # Create temporary directory - self._temp_dir = tempfile.mkdtemp(prefix="tinypg.") - temp_path = Path(self._temp_dir) + is_windows = PostgreSQLBinaries.SYSTEM == "windows" + sep = "-" if is_windows else "." + if is_windows: + temp_path = Path(tempfile.gettempdir()) / sep / tempfile.gettempprefix() + else: + self._temp_dir = tempfile.mkdtemp(prefix=f"tinypg{sep}") + temp_path = Path(self._temp_dir) # Data directory for this PostgreSQL version data_dir = temp_path / self.version @@ -233,7 +237,7 @@ def _initialize_database(self) -> Path: ], check=True, capture_output=True, - cwd=temp_path, + cwd=None if is_windows else temp_path, ) # Configure PostgreSQL for ephemeral use