diff --git a/.github/workflows/packaging_wheels.yml b/.github/workflows/packaging_wheels.yml index 8e656abd..23a16af7 100644 --- a/.github/workflows/packaging_wheels.yml +++ b/.github/workflows/packaging_wheels.yml @@ -33,6 +33,7 @@ jobs: python: [ cp39, cp310, cp311, cp312, cp313, cp314 ] platform: - { os: windows-2025, arch: amd64, cibw_system: win } + - { os: windows-11-arm, arch: ARM64, cibw_system: win } # cibw requires ARM64 to be uppercase - { os: ubuntu-24.04, arch: x86_64, cibw_system: manylinux } - { os: ubuntu-24.04-arm, arch: aarch64, cibw_system: manylinux } - { os: macos-15, arch: arm64, cibw_system: macosx } @@ -46,6 +47,8 @@ jobs: - { minimal: true, python: cp312 } - { minimal: true, python: cp313 } - { minimal: true, platform: { arch: universal2 } } + - { python: cp39, platform: { os: windows-11-arm, arch: ARM64 } } # too many dependency problems for win arm64 + - { python: cp310, platform: { os: windows-11-arm, arch: ARM64 } } # too many dependency problems for win arm64 runs-on: ${{ matrix.platform.os }} env: ### cibuildwheel configuration diff --git a/.github/workflows/targeted_test.yml b/.github/workflows/targeted_test.yml index 812bb9c5..13ae9566 100644 --- a/.github/workflows/targeted_test.yml +++ b/.github/workflows/targeted_test.yml @@ -9,6 +9,7 @@ on: type: choice options: - 'windows-2025' + - 'windows-11-arm' - 'ubuntu-24.04' - 'ubuntu-24.04-arm' - 'macos-15' @@ -36,6 +37,11 @@ on: description: 'Custom test path (must be in tests/ directory, overrides testsuite)' required: false type: string + verbose-uv: + description: 'Let uv generate verbose output (pytest verbosity is always on)' + required: false + type: boolean + default: true jobs: test: @@ -83,4 +89,4 @@ jobs: - name: Run tests shell: bash run: | - uv run pytest -vv ${{ steps.test_path.outputs.test_path }} + uv ${{ inputs.verbose-uv && 'run -v' || 'run' }} pytest -vv ${{ steps.test_path.outputs.test_path }} diff --git a/pyproject.toml b/pyproject.toml index 6895b12e..3bd54543 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -184,7 +184,8 @@ exclude = [ # - numpy: tensorflow doesn't play nice with numpy>2 so for every platform that can run tensorflow (cp39-cp311) we use # numpy<2. numpy<2 has no wheels for cp31[2|3], meaning an sdist will be used. However, on Windows amd64 + # cp313 this results in a segfault / access violation. To get around this, we install numpy>=2 on all >=cp312 -# platforms. +# platforms. Then for windows arm64, for which there is no tensorflow, we only allow numpy>=2.3 because that +# ships arm64 win32 wheels. ###################################################################################################### [tool.uv] @@ -197,6 +198,7 @@ environments = [ # no need to resolve packages beyond these platforms with uv... "python_version >= '3.9' and sys_platform == 'darwin' and platform_machine == 'arm64'", "python_version >= '3.9' and sys_platform == 'darwin' and platform_machine == 'x86_64'", "python_version >= '3.9' and sys_platform == 'win32' and platform_machine == 'AMD64'", + "python_version >= '3.11' and sys_platform == 'win32' and platform_machine == 'ARM64'", "python_version >= '3.9' and sys_platform == 'linux' and platform_machine == 'x86_64'", "python_version >= '3.9' and sys_platform == 'linux' and platform_machine == 'aarch64'", ] @@ -204,6 +206,7 @@ required-environments = [ # ... but do always resolve for all of them "python_version >= '3.9' and sys_platform == 'darwin' and platform_machine == 'arm64'", "python_version >= '3.9' and sys_platform == 'darwin' and platform_machine == 'x86_64'", "python_version >= '3.9' and sys_platform == 'win32' and platform_machine == 'AMD64'", + "python_version >= '3.11' and sys_platform == 'win32' and platform_machine == 'ARM64'", "python_version >= '3.9' and sys_platform == 'linux' and platform_machine == 'x86_64'", "python_version >= '3.9' and sys_platform == 'linux' and platform_machine == 'aarch64'", ] @@ -219,6 +222,7 @@ explicit = true torch = [ { index = "pytorch-cpu" } ] torchvision = [ { index = "pytorch-cpu" } ] +# todo: adjust for windows arm64 while test dependencies become available [dependency-groups] # used for development only, requires pip >=25.1.0 stubdeps = [ # dependencies used for typehints in the stubs "pybind11-stubgen", @@ -226,17 +230,18 @@ stubdeps = [ # dependencies used for typehints in the stubs "fsspec", "pandas", "polars", - "pyarrow", + "pyarrow; sys_platform != 'win32' or platform_machine != 'ARM64'", + "typing-extensions", ] test = [ # dependencies used for running tests - "adbc-driver-manager", + "adbc-driver-manager; sys_platform != 'win32' or platform_machine != 'ARM64'", "pytest", "pytest-reraise", "pytest-timeout", "pytest-timestamper", "coverage", "gcovr", - "gcsfs", + "gcsfs; sys_platform != 'win32' or platform_machine != 'ARM64'", "packaging", "polars", "psutil", @@ -246,16 +251,18 @@ test = [ # dependencies used for running tests "pytz", "requests", "urllib3", - "fsspec>=2022.11.0", + "fsspec>=2022.11.0; sys_platform != 'win32' or platform_machine != 'ARM64'", "pandas>=2.0.0", - "pyarrow>=18.0.0", - "torch>=2.2.2; python_version < '3.14' and ( sys_platform != 'darwin' or platform_machine != 'x86_64' or python_version < '3.13' )", + "pyarrow>=18.0.0; sys_platform != 'win32' or platform_machine != 'ARM64'", + "torch>=2.2.2; python_version < '3.14' and ( sys_platform != 'darwin' or platform_machine != 'x86_64' or python_version < '3.13' ) and ( sys_platform != 'win32' or platform_machine != 'ARM64' or python_version > '3.11' )", "tensorflow==2.14.0; sys_platform == 'darwin' and python_version < '3.12'", "tensorflow-cpu>=2.14.0; sys_platform == 'linux' and platform_machine != 'aarch64' and python_version < '3.12'", - "tensorflow-cpu>=2.14.0; sys_platform == 'win32' and python_version < '3.12'", + "tensorflow-cpu>=2.14.0; sys_platform == 'win32' and platform_machine != 'ARM64' and python_version < '3.12'", "tensorflow-cpu-aws==2.15.1; sys_platform == 'linux' and platform_machine == 'aarch64' and python_version < '3.12'", - "numpy<2; python_version < '3.12'", - "numpy>=2; python_version >= '3.12'", + "typing-extensions", + "numpy<2; ( sys_platform != 'win32' or platform_machine != 'ARM64' ) and python_version < '3.12'", + "numpy>=2; ( sys_platform != 'win32' or platform_machine != 'ARM64' ) and python_version >= '3.12'", + "numpy>=2.3; sys_platform == 'win32' and platform_machine == 'ARM64' and python_version >= '3.11'", ] scripts = [ # dependencies used for running scripts "cxxheaderparser", @@ -265,7 +272,7 @@ scripts = [ # dependencies used for running scripts "pandas", "pcpp", "polars", - "pyarrow", + "pyarrow; sys_platform != 'win32' or platform_machine != 'ARM64'", "pytz" ] pypi = [ # dependencies used by the pypi cleanup script diff --git a/tests/fast/adbc/test_adbc.py b/tests/fast/adbc/test_adbc.py index c20d2a0e..42ba8199 100644 --- a/tests/fast/adbc/test_adbc.py +++ b/tests/fast/adbc/test_adbc.py @@ -1,12 +1,12 @@ import datetime from pathlib import Path -import adbc_driver_manager.dbapi import numpy as np -import pyarrow import pytest -import adbc_driver_duckdb.dbapi +adbc_driver_manager = pytest.importorskip("adbc_driver_manager") +adbc_driver_duckdb = pytest.importorskip("adbc_driver_duckdb") +pyarrow = pytest.importorskip("pyarrow") xfail = pytest.mark.xfail driver_path = adbc_driver_duckdb.driver_path() diff --git a/tests/fast/adbc/test_connection_get_info.py b/tests/fast/adbc/test_connection_get_info.py index aa2b3d32..cd6298ed 100644 --- a/tests/fast/adbc/test_connection_get_info.py +++ b/tests/fast/adbc/test_connection_get_info.py @@ -1,19 +1,22 @@ -import pyarrow as pa +import pytest -import adbc_driver_duckdb.dbapi import duckdb +pa = pytest.importorskip("pyarrow") +pytest.importorskip("adbc_driver_manager") +adbc_driver_duckdb_dbapi = pytest.importorskip("adbc_driver_duckdb.dbapi") + class TestADBCConnectionGetInfo: def test_connection_basic(self): - con = adbc_driver_duckdb.dbapi.connect() + con = adbc_driver_duckdb_dbapi.connect() with con.cursor() as cursor: cursor.execute("select 42") res = cursor.fetchall() assert res == [(42,)] def test_connection_get_info_all(self): - con = adbc_driver_duckdb.dbapi.connect() + con = adbc_driver_duckdb_dbapi.connect() adbc_con = con.adbc_connection res = adbc_con.get_info() reader = pa.RecordBatchReader._import_from_c(res.address) @@ -37,7 +40,7 @@ def test_connection_get_info_all(self): assert string_values == expected_result def test_empty_result(self): - con = adbc_driver_duckdb.dbapi.connect() + con = adbc_driver_duckdb_dbapi.connect() adbc_con = con.adbc_connection res = adbc_con.get_info([1337]) reader = pa.RecordBatchReader._import_from_c(res.address) @@ -48,7 +51,7 @@ def test_empty_result(self): assert values.num_chunks == 0 def test_unrecognized_codes(self): - con = adbc_driver_duckdb.dbapi.connect() + con = adbc_driver_duckdb_dbapi.connect() adbc_con = con.adbc_connection res = adbc_con.get_info([0, 1000, 4, 2000]) reader = pa.RecordBatchReader._import_from_c(res.address) diff --git a/tests/fast/adbc/test_statement_bind.py b/tests/fast/adbc/test_statement_bind.py index d35693ff..e8df14c7 100644 --- a/tests/fast/adbc/test_statement_bind.py +++ b/tests/fast/adbc/test_statement_bind.py @@ -1,10 +1,10 @@ import sys -import adbc_driver_manager -import pyarrow as pa import pytest -import adbc_driver_duckdb.dbapi +pa = pytest.importorskip("pyarrow") +adbc_driver_manager = pytest.importorskip("adbc_driver_manager") +adbc_driver_duckdb_dbapi = pytest.importorskip("adbc_driver_duckdb.dbapi") xfail = pytest.mark.xfail @@ -35,7 +35,7 @@ def test_bind_multiple_rows(self): names=["ints"], ) - con = adbc_driver_duckdb.dbapi.connect() + con = adbc_driver_duckdb_dbapi.connect() with con.cursor() as cursor: statement = cursor.adbc_statement statement.set_sql_query("select ? * 2 as i") @@ -57,7 +57,7 @@ def test_bind_single_row(self): names=["ints"], ) - con = adbc_driver_duckdb.dbapi.connect() + con = adbc_driver_duckdb_dbapi.connect() with con.cursor() as cursor: statement = cursor.adbc_statement statement.set_sql_query("select ? * 2 as i") @@ -93,7 +93,7 @@ def test_multiple_parameters(self): names=["ints", "strings", "bools"], ) - con = adbc_driver_duckdb.dbapi.connect() + con = adbc_driver_duckdb_dbapi.connect() with con.cursor() as cursor: statement = cursor.adbc_statement statement.set_sql_query("select ? as a, ? as b, ? as c") @@ -123,7 +123,7 @@ def test_bind_composite_type(self): # Create the RecordBatch record_batch = pa.RecordBatch.from_arrays([struct_array], schema=schema) - con = adbc_driver_duckdb.dbapi.connect() + con = adbc_driver_duckdb_dbapi.connect() with con.cursor() as cursor: statement = cursor.adbc_statement statement.set_sql_query("select ? as a") @@ -146,7 +146,7 @@ def test_too_many_parameters(self): names=["ints", "strings"], ) - con = adbc_driver_duckdb.dbapi.connect() + con = adbc_driver_duckdb_dbapi.connect() with con.cursor() as cursor: statement = cursor.adbc_statement statement.set_sql_query("select ? as a") @@ -175,7 +175,7 @@ def test_not_enough_parameters(self): names=["strings"], ) - con = adbc_driver_duckdb.dbapi.connect() + con = adbc_driver_duckdb_dbapi.connect() with con.cursor() as cursor: statement = cursor.adbc_statement statement.set_sql_query("select ? as a, ? as b") diff --git a/tests/fast/arrow/test_2426.py b/tests/fast/arrow/test_2426.py index f43284d3..6f76613f 100644 --- a/tests/fast/arrow/test_2426.py +++ b/tests/fast/arrow/test_2426.py @@ -1,5 +1,9 @@ +import pytest + import duckdb +pytest.importorskip("pyarrow") + try: can_run = True except Exception: diff --git a/tests/fast/arrow/test_arrow_fetch.py b/tests/fast/arrow/test_arrow_fetch.py index 0547020f..ba5d13a4 100644 --- a/tests/fast/arrow/test_arrow_fetch.py +++ b/tests/fast/arrow/test_arrow_fetch.py @@ -1,5 +1,10 @@ +import pytest + import duckdb +pytest.importorskip("pyarrow") + + try: can_run = True except Exception: diff --git a/tests/fast/test_all_types.py b/tests/fast/test_all_types.py index 77074fdc..c4ba0e55 100644 --- a/tests/fast/test_all_types.py +++ b/tests/fast/test_all_types.py @@ -534,6 +534,7 @@ def test_fetchnumpy(self, cur_type): @pytest.mark.parametrize("cur_type", all_types) def test_arrow(self, cur_type): + pytest.importorskip("pyarrow") try: pass except Exception: