Skip to content

Commit cbea824

Browse files
authored
Fix conftest dependency breaking nightly wheel build (#3078)
<!-- Thanks for opening a pull request! --> <!-- In the case this PR will resolve an issue, please replace ${GITHUB_ISSUE_ID} below with the actual Github issue id. --> <!-- Closes #${GITHUB_ISSUE_ID} --> # Rationale for this change Context: #2982 (comment) Adding imports to `conftest.py` may break the nightly build pipeline. (For example BigQuery in #2982). This is because [nightly wheel build tests](https://github.com/apache/iceberg-python/blob/95f6273b23524c6238aafb57fa06e693ef83d6ef/.github/workflows/pypi-build-artifacts.yml#L74-L75) run in a narrower dependency set (`--only-group dev`), so new imports could cause test collection to fail. This PR inlines the imports in `conftest.py` and also include a smoke test in CI to catch this problem going forward ## Are these changes tested? yes, nightly build works again https://github.com/apache/iceberg-python/actions/runs/22285169782 ## Are there any user-facing changes? <!-- In the case of user-facing changes, please add the changelog label. -->
1 parent 95f6273 commit cbea824

File tree

5 files changed

+87
-12
lines changed

5 files changed

+87
-12
lines changed

.github/workflows/pypi-build-artifacts.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ jobs:
7171
# Ignore 32 bit architectures
7272
CIBW_ARCHS: "auto64"
7373
CIBW_PROJECT_REQUIRES_PYTHON: ">=3.10,<3.14"
74+
# Keep these in sync with Python CI job `cibw-dev-env-smoke-test`
75+
# in .github/workflows/python-ci.yml to catch import-time regressions early.
7476
CIBW_BEFORE_TEST: "uv sync --directory {project} --only-group dev --no-install-project"
7577
CIBW_TEST_COMMAND: "uv run --directory {project} pytest tests/avro/test_decoder.py"
7678
# Skip free-threaded (PEP 703) builds until we evaluate decoder_fast support

.github/workflows/python-ci.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,26 @@ jobs:
200200
merge-multiple: true
201201
- name: Generate coverage report (75%) # Coverage threshold should only increase over time — never decrease it!
202202
run: COVERAGE_FAIL_UNDER=75 make coverage-report
203+
204+
cibw-dev-env-smoke-test:
205+
runs-on: ubuntu-latest
206+
steps:
207+
- uses: actions/checkout@v6
208+
- uses: actions/setup-python@v6
209+
with:
210+
python-version: '3.12'
211+
- name: Install UV
212+
uses: astral-sh/setup-uv@v7
213+
with:
214+
enable-cache: true
215+
# Why this exists:
216+
# Catch import-time regressions (e.g., global conftest optional deps)
217+
# in the same dev-only environment used by cibuildwheel wheel tests.
218+
# Keep this in sync with wheel build test setup in
219+
# .github/workflows/pypi-build-artifacts.yml:
220+
# CIBW_BEFORE_TEST: uv sync --directory {project} --only-group dev --no-install-project
221+
# CIBW_TEST_COMMAND: uv run --directory {project} pytest tests/avro/test_decoder.py
222+
- name: Mirror wheel CIBW_BEFORE_TEST
223+
run: uv sync --directory . --only-group dev --no-install-project
224+
- name: Mirror wheel CIBW_TEST_COMMAND
225+
run: uv run --directory . pytest tests/avro/test_decoder.py

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ dev = [
118118
"docutils!=0.21.post1",
119119
"mypy-boto3-glue>=1.28.18",
120120
"mypy-boto3-dynamodb>=1.28.18",
121+
"google-cloud-bigquery>=3.33.0,<4",
121122
"pyarrow-stubs>=20.0.0.20251107", # Remove when pyarrow >= 23.0.0 https://github.com/apache/arrow/pull/47609
122123
"sqlalchemy>=2.0.18,<3",
123124
]

tests/conftest.py

Lines changed: 59 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,6 @@
4949
from pytest_lazy_fixtures import lf
5050

5151
from pyiceberg.catalog import Catalog, load_catalog
52-
from pyiceberg.catalog.bigquery_metastore import BigQueryMetastoreCatalog
53-
from pyiceberg.catalog.dynamodb import DynamoDbCatalog
54-
from pyiceberg.catalog.glue import GlueCatalog
55-
from pyiceberg.catalog.hive import HiveCatalog
56-
from pyiceberg.catalog.memory import InMemoryCatalog
57-
from pyiceberg.catalog.noop import NoopCatalog
58-
from pyiceberg.catalog.rest import RestCatalog
59-
from pyiceberg.catalog.sql import SqlCatalog
6052
from pyiceberg.expressions import BoundReference
6153
from pyiceberg.io import (
6254
ADLS_ACCOUNT_KEY,
@@ -2497,6 +2489,8 @@ def warehouse(tmp_path_factory: pytest.TempPathFactory) -> Path:
24972489

24982490
@pytest.fixture
24992491
def table_v1(example_table_metadata_v1: dict[str, Any]) -> Table:
2492+
from pyiceberg.catalog.noop import NoopCatalog
2493+
25002494
table_metadata = TableMetadataV1(**example_table_metadata_v1)
25012495
return Table(
25022496
identifier=("database", "table"),
@@ -2509,6 +2503,8 @@ def table_v1(example_table_metadata_v1: dict[str, Any]) -> Table:
25092503

25102504
@pytest.fixture
25112505
def table_v2(example_table_metadata_v2: dict[str, Any]) -> Table:
2506+
from pyiceberg.catalog.noop import NoopCatalog
2507+
25122508
table_metadata = TableMetadataV2(**example_table_metadata_v2)
25132509
return Table(
25142510
identifier=("database", "table"),
@@ -2521,6 +2517,8 @@ def table_v2(example_table_metadata_v2: dict[str, Any]) -> Table:
25212517

25222518
@pytest.fixture
25232519
def table_v3(example_table_metadata_v3: dict[str, Any]) -> Table:
2520+
from pyiceberg.catalog.noop import NoopCatalog
2521+
25242522
table_metadata = TableMetadataV3(**example_table_metadata_v3)
25252523
return Table(
25262524
identifier=("database", "table"),
@@ -2535,6 +2533,8 @@ def table_v3(example_table_metadata_v3: dict[str, Any]) -> Table:
25352533
def table_v2_orc(example_table_metadata_v2: dict[str, Any]) -> Table:
25362534
import copy
25372535

2536+
from pyiceberg.catalog.noop import NoopCatalog
2537+
25382538
metadata_dict = copy.deepcopy(example_table_metadata_v2)
25392539
if not metadata_dict["properties"]:
25402540
metadata_dict["properties"] = {}
@@ -2553,6 +2553,8 @@ def table_v2_orc(example_table_metadata_v2: dict[str, Any]) -> Table:
25532553
def table_v2_with_fixed_and_decimal_types(
25542554
table_metadata_v2_with_fixed_and_decimal_types: dict[str, Any],
25552555
) -> Table:
2556+
from pyiceberg.catalog.noop import NoopCatalog
2557+
25562558
table_metadata = TableMetadataV2(
25572559
**table_metadata_v2_with_fixed_and_decimal_types,
25582560
)
@@ -2567,6 +2569,8 @@ def table_v2_with_fixed_and_decimal_types(
25672569

25682570
@pytest.fixture
25692571
def table_v2_with_extensive_snapshots(example_table_metadata_v2_with_extensive_snapshots: dict[str, Any]) -> Table:
2572+
from pyiceberg.catalog.noop import NoopCatalog
2573+
25702574
table_metadata = TableMetadataV2(**example_table_metadata_v2_with_extensive_snapshots)
25712575
return Table(
25722576
identifier=("database", "table"),
@@ -2579,6 +2583,8 @@ def table_v2_with_extensive_snapshots(example_table_metadata_v2_with_extensive_s
25792583

25802584
@pytest.fixture
25812585
def table_v2_with_statistics(table_metadata_v2_with_statistics: dict[str, Any]) -> Table:
2586+
from pyiceberg.catalog.noop import NoopCatalog
2587+
25822588
table_metadata = TableMetadataV2(**table_metadata_v2_with_statistics)
25832589
return Table(
25842590
identifier=("database", "table"),
@@ -3000,11 +3006,15 @@ def ray_session() -> Generator[Any, None, None]:
30003006
# Catalog fixtures
30013007

30023008

3003-
def _create_memory_catalog(name: str, warehouse: Path) -> InMemoryCatalog:
3009+
def _create_memory_catalog(name: str, warehouse: Path) -> Catalog:
3010+
from pyiceberg.catalog.memory import InMemoryCatalog
3011+
30043012
return InMemoryCatalog(name, warehouse=f"file://{warehouse}")
30053013

30063014

3007-
def _create_sql_catalog(name: str, warehouse: Path) -> SqlCatalog:
3015+
def _create_sql_catalog(name: str, warehouse: Path) -> Catalog:
3016+
from pyiceberg.catalog.sql import SqlCatalog
3017+
30083018
catalog = SqlCatalog(
30093019
name,
30103020
uri="sqlite:///:memory:",
@@ -3014,7 +3024,9 @@ def _create_sql_catalog(name: str, warehouse: Path) -> SqlCatalog:
30143024
return catalog
30153025

30163026

3017-
def _create_sql_without_rowcount_catalog(name: str, warehouse: Path) -> SqlCatalog:
3027+
def _create_sql_without_rowcount_catalog(name: str, warehouse: Path) -> Catalog:
3028+
from pyiceberg.catalog.sql import SqlCatalog
3029+
30183030
props = {
30193031
"uri": f"sqlite:////{warehouse}/sql-catalog",
30203032
"warehouse": f"file://{warehouse}",
@@ -3152,48 +3164,83 @@ def test_table_properties() -> dict[str, str]:
31523164

31533165

31543166
def does_support_purge_table(catalog: Catalog) -> bool:
3167+
from pyiceberg.catalog.noop import NoopCatalog
3168+
from pyiceberg.catalog.rest import RestCatalog
3169+
31553170
if isinstance(catalog, RestCatalog):
31563171
return property_as_bool(catalog.properties, "supports_purge_table", True)
3172+
from pyiceberg.catalog.hive import HiveCatalog
3173+
31573174
if isinstance(catalog, (HiveCatalog, NoopCatalog)):
31583175
return False
31593176
return True
31603177

31613178

31623179
def does_support_atomic_concurrent_updates(catalog: Catalog) -> bool:
3180+
from pyiceberg.catalog.noop import NoopCatalog
3181+
from pyiceberg.catalog.rest import RestCatalog
3182+
31633183
if isinstance(catalog, RestCatalog):
31643184
return property_as_bool(catalog.properties, "supports_atomic_concurrent_updates", True)
3185+
from pyiceberg.catalog.hive import HiveCatalog
3186+
31653187
if isinstance(catalog, (HiveCatalog, NoopCatalog)):
31663188
return False
31673189
return True
31683190

31693191

31703192
def does_support_nested_namespaces(catalog: Catalog) -> bool:
3193+
from pyiceberg.catalog.dynamodb import DynamoDbCatalog
3194+
from pyiceberg.catalog.glue import GlueCatalog
3195+
from pyiceberg.catalog.noop import NoopCatalog
3196+
from pyiceberg.catalog.rest import RestCatalog
3197+
31713198
if isinstance(catalog, RestCatalog):
31723199
return property_as_bool(catalog.properties, "supports_nested_namespaces", True)
3173-
if isinstance(catalog, (HiveCatalog, NoopCatalog, GlueCatalog, BigQueryMetastoreCatalog, DynamoDbCatalog)):
3200+
from pyiceberg.catalog.bigquery_metastore import BigQueryMetastoreCatalog
3201+
from pyiceberg.catalog.hive import HiveCatalog
3202+
3203+
if isinstance(catalog, (HiveCatalog, BigQueryMetastoreCatalog, NoopCatalog, GlueCatalog, DynamoDbCatalog)):
31743204
return False
31753205
return True
31763206

31773207

31783208
def does_support_schema_evolution(catalog: Catalog) -> bool:
3209+
from pyiceberg.catalog.noop import NoopCatalog
3210+
from pyiceberg.catalog.rest import RestCatalog
3211+
31793212
if isinstance(catalog, RestCatalog):
31803213
return property_as_bool(catalog.properties, "supports_schema_evolution", True)
3214+
from pyiceberg.catalog.hive import HiveCatalog
3215+
31813216
if isinstance(catalog, (HiveCatalog, NoopCatalog)):
31823217
return False
31833218
return True
31843219

31853220

31863221
def does_support_slash_in_identifier(catalog: Catalog) -> bool:
3222+
from pyiceberg.catalog.noop import NoopCatalog
3223+
from pyiceberg.catalog.rest import RestCatalog
3224+
from pyiceberg.catalog.sql import SqlCatalog
3225+
31873226
if isinstance(catalog, RestCatalog):
31883227
return property_as_bool(catalog.properties, "supports_slash_in_identifier", True)
3228+
from pyiceberg.catalog.hive import HiveCatalog
3229+
31893230
if isinstance(catalog, (HiveCatalog, NoopCatalog, SqlCatalog)):
31903231
return False
31913232
return True
31923233

31933234

31943235
def does_support_dot_in_identifier(catalog: Catalog) -> bool:
3236+
from pyiceberg.catalog.noop import NoopCatalog
3237+
from pyiceberg.catalog.rest import RestCatalog
3238+
from pyiceberg.catalog.sql import SqlCatalog
3239+
31953240
if isinstance(catalog, RestCatalog):
31963241
return property_as_bool(catalog.properties, "supports_dot_in_identifier", True)
3242+
from pyiceberg.catalog.hive import HiveCatalog
3243+
31973244
if isinstance(catalog, (HiveCatalog, NoopCatalog, SqlCatalog)):
31983245
return False
31993246
return True

uv.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)