Skip to content

Commit 92ba2e3

Browse files
authored
ci: test pi-thon (#3218)
1 parent ed709b7 commit 92ba2e3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+308
-90
lines changed

.github/workflows/pytest.yml

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,13 @@ jobs:
5252
cache-suffix: pytest-windows-${{ matrix.python-version }}
5353
cache-dependency-glob: "pyproject.toml"
5454
- name: install-reqs
55-
# we are not testing pyspark on Windows here because it is very slow
56-
run: uv pip install -e ".[dask, modin, ibis]" --group core-tests --group extra --system
55+
# we are not testing pyspark, modin, or dask on Windows here because nobody got time for that
56+
run: uv pip install -e ".[ibis]" --group core-tests --group extra --system
5757
- name: show-deps
5858
run: uv pip freeze
5959
- name: Run pytest
6060
run: |
61-
pytest tests --cov=narwhals --cov=tests --runslow --cov-fail-under=95 --constructors=pandas,pandas[nullable],pandas[pyarrow],pyarrow,modin[pyarrow],polars[eager],polars[lazy],dask,duckdb,sqlframe,ibis --durations=30
61+
pytest tests --cov=narwhals --cov=tests --runslow --cov-fail-under=95 --constructors=pandas,pandas[nullable],pandas[pyarrow],pyarrow,polars[eager],polars[lazy],duckdb,sqlframe,ibis --durations=30
6262
6363
pytest-full-coverage:
6464
strategy:
@@ -112,8 +112,73 @@ jobs:
112112
cache-suffix: pytest-narrower-deps-${{ matrix.python-version }}
113113
cache-dependency-glob: "pyproject.toml"
114114
- name: install-reqs
115-
run: uv pip install -e ".[pandas, pyarrow]" --group tests --system
115+
run: uv pip install -e ".[pandas]" --group tests --system
116+
- name: show-deps
117+
run: uv pip freeze
118+
- name: Run pytest
119+
run: pytest tests --runslow --constructors=pandas,pandas[nullable]
120+
- name: install-more-reqs
121+
run: uv pip install -U pyarrow --system
122+
- name: show-deps
123+
run: uv pip freeze
124+
- name: Run pytest
125+
run: pytest tests --runslow --constructors=pandas[pyarrow],pyarrow
126+
127+
python-314:
128+
strategy:
129+
matrix:
130+
python-version: ["3.14"]
131+
os: [ubuntu-latest]
132+
runs-on: ${{ matrix.os }}
133+
steps:
134+
- uses: actions/checkout@v5
135+
- uses: actions/setup-python@v6
136+
with:
137+
python-version: ${{ matrix.python-version }}
138+
- name: Install uv
139+
uses: astral-sh/setup-uv@v6
140+
with:
141+
enable-cache: "true"
142+
cache-suffix: python-314-${{ matrix.python-version }}
143+
cache-dependency-glob: "pyproject.toml"
144+
- name: install pyarrow nightly
145+
run: |
146+
uv pip uninstall pyarrow --system
147+
uv pip install -i https://pypi.anaconda.org/scientific-python-nightly-wheels/simple pyarrow -U --system
148+
- name: install-reqs
149+
# Use `--pre` as duckdb stable not compatible with 3.14
150+
run: uv pip install -e . --group tests --pre pandas polars duckdb sqlframe --system
151+
- name: show-deps
152+
run: uv pip freeze
153+
- name: Run pytest
154+
run: pytest tests --cov=narwhals --cov=tests --runslow --durations=30 --constructors=pandas,pandas[nullable],pandas[pyarrow],pyarrow,polars[eager],polars[lazy],duckdb,sqlframe --cov-fail-under=50
155+
156+
python-314t:
157+
strategy:
158+
matrix:
159+
python-version: ["3.14t"]
160+
os: [ubuntu-latest]
161+
runs-on: ${{ matrix.os }}
162+
env:
163+
PYTHON_GIL: 0
164+
steps:
165+
- uses: actions/checkout@v5
166+
- uses: actions/setup-python@v6
167+
with:
168+
python-version: ${{ matrix.python-version }}
169+
- name: Install uv
170+
uses: astral-sh/setup-uv@v6
171+
with:
172+
enable-cache: "true"
173+
cache-suffix: python-314t-${{ matrix.python-version }}
174+
cache-dependency-glob: "pyproject.toml"
175+
- name: install pyarrow nightly
176+
run: |
177+
uv pip uninstall pyarrow --system
178+
uv pip install -i https://pypi.anaconda.org/scientific-python-nightly-wheels/simple pyarrow -U --system
179+
- name: install-reqs
180+
run: uv pip install -e . --group tests --pre pandas --system
116181
- name: show-deps
117182
run: uv pip freeze
118183
- name: Run pytest
119-
run: pytest tests --runslow --constructors=pandas,pandas[nullable],pandas[pyarrow],pyarrow
184+
run: pytest tests --cov=narwhals --cov=tests --runslow --durations=30 --constructors=pandas,pandas[nullable],pandas[pyarrow],pyarrow --cov-fail-under=50

pyproject.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,23 +246,34 @@ filterwarnings = [
246246
'ignore:.*defaulting to pandas implementation:Warning:modin',
247247
'ignore:.*implementation has mismatches with pandas:Warning:modin',
248248
'ignore:.*You are using pyarrow version',
249+
250+
# https://discuss.python.org/t/types-uniontype-was-merged-with-wrong-class-not-even-a-class/102275/31
251+
"ignore:.*'_UnionGenericAlias' is deprecated:DeprecationWarning",
252+
249253
# This warning was temporarily raised by pandas but then reverted.
250254
'ignore:.*Passing a BlockManager to DataFrame:DeprecationWarning',
255+
251256
# This warning was temporarily raised by Polars but then reverted.
252257
'ignore:.*The default coalesce behavior of left join will change:DeprecationWarning',
258+
253259
'ignore: unclosed <socket.socket',
254260
'ignore:.*The distutils package is deprecated and slated for removal in Python 3.12:DeprecationWarning:pyspark',
255261
'ignore:.*distutils Version classes are deprecated. Use packaging.version instead.*:DeprecationWarning',
256262
'ignore:.*is_datetime64tz_dtype is deprecated and will be removed in a future version.*:DeprecationWarning:pyspark',
263+
257264
# Warning raised by PyArrow nightly just by importing pandas
258265
'ignore:.*Python binding for RankQuantileOptions not exposed:RuntimeWarning:pyarrow',
266+
259267
'ignore:.*pandas only supports SQLAlchemy:UserWarning:sqlframe',
260268
'ignore:.*numpy.core is deprecated and has been renamed to numpy._core.*:DeprecationWarning:sqlframe',
261269
"ignore:.*__array__ implementation doesn't accept a copy keyword, so passing copy=False failed:DeprecationWarning:modin",
270+
262271
# raised internally by pandas
263272
"ignore:.*np.find_common_type is deprecated:DeprecationWarning:pandas",
273+
264274
# Warning raised when calling PandasLikeNamespace.from_arrow with old pyarrow
265275
"ignore:.*is_sparse is deprecated and will be removed in a future version.*:DeprecationWarning:pyarrow",
276+
266277
'ignore:.*invalid value encountered in cast:RuntimeWarning:pandas',
267278
]
268279
xfail_strict = true

tests/conftest.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ def pandas_nullable_constructor(obj: Data) -> pd.DataFrame:
102102

103103

104104
def pandas_pyarrow_constructor(obj: Data) -> pd.DataFrame:
105+
pytest.importorskip("pyarrow")
105106
import pandas as pd
106107

107108
return pd.DataFrame(obj).convert_dtypes(dtype_backend="pyarrow")
@@ -143,6 +144,8 @@ def polars_lazy_constructor(obj: Data) -> pl.LazyFrame:
143144

144145

145146
def duckdb_lazy_constructor(obj: Data) -> duckdb.DuckDBPyRelation:
147+
pytest.importorskip("duckdb")
148+
pytest.importorskip("pyarrow")
146149
import duckdb
147150
import polars as pl
148151

@@ -165,6 +168,7 @@ def dask_lazy_p2_constructor(obj: Data) -> NativeLazyFrame: # pragma: no cover
165168

166169

167170
def pyarrow_table_constructor(obj: dict[str, Any]) -> pa.Table:
171+
pytest.importorskip("pyarrow")
168172
import pyarrow as pa
169173

170174
return pa.table(obj)
@@ -201,6 +205,8 @@ def _constructor(obj: Data) -> PySparkDataFrame:
201205

202206

203207
def sqlframe_pyspark_lazy_constructor(obj: Data) -> SQLFrameDataFrame: # pragma: no cover
208+
pytest.importorskip("sqlframe")
209+
pytest.importorskip("duckdb")
204210
session = sqlframe_session()
205211
return session.createDataFrame([*zip(*obj.values())], schema=[*obj.keys()])
206212

@@ -214,6 +220,8 @@ def _ibis_backend() -> IbisDuckDBBackend: # pragma: no cover
214220

215221

216222
def ibis_lazy_constructor(obj: Data) -> ibis.Table: # pragma: no cover
223+
pytest.importorskip("ibis")
224+
pytest.importorskip("polars")
217225
import polars as pl
218226

219227
ldf = pl.from_dict(obj).lazy()

tests/dtypes_test.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
import numpy as np
99
import pandas as pd
10-
import pyarrow as pa
1110
import pytest
1211

1312
import narwhals as nw
@@ -159,6 +158,8 @@ def test_2d_array(constructor: Constructor, request: pytest.FixtureRequest) -> N
159158
for condition, reason in version_conditions:
160159
if condition:
161160
pytest.skip(reason)
161+
if "pandas" in str(constructor):
162+
pytest.importorskip("pyarrow")
162163

163164
if any(x in str(constructor) for x in ("dask", "cudf", "pyspark")):
164165
request.applymarker(
@@ -176,6 +177,9 @@ def test_2d_array(constructor: Constructor, request: pytest.FixtureRequest) -> N
176177

177178

178179
def test_second_time_unit() -> None:
180+
pytest.importorskip("pyarrow")
181+
import pyarrow as pa
182+
179183
s: IntoSeries = pd.Series(np.array([np.datetime64("2020-01-01", "s")]))
180184
result = nw.from_native(s, series_only=True)
181185
expected_unit: Literal["ns", "us", "ms", "s"] = (
@@ -216,6 +220,8 @@ def test_pandas_inplace_modification_1267() -> None:
216220

217221

218222
def test_pandas_fixed_offset_1302() -> None:
223+
pytest.importorskip("pyarrow")
224+
219225
result = nw.from_native(
220226
pd.Series(pd.to_datetime(["2020-01-01T00:00:00.000000000+01:00"])),
221227
series_only=True,
@@ -398,9 +404,10 @@ def test_huge_int_to_native() -> None:
398404
def test_cast_decimal_to_native() -> None:
399405
pytest.importorskip("duckdb")
400406
pytest.importorskip("polars")
401-
407+
pytest.importorskip("pyarrow")
402408
import duckdb
403409
import polars as pl
410+
import pyarrow as pa
404411

405412
data = {"a": [1, 2, 3]}
406413

tests/expr_and_series/cast_test.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,10 @@ def test_cast_struct(request: pytest.FixtureRequest, constructor: Constructor) -
268268
if any(backend in str(constructor) for backend in ("dask", "cudf", "sqlframe")):
269269
request.applymarker(pytest.mark.xfail)
270270

271-
if "pandas" in str(constructor) and PANDAS_VERSION < (2, 2):
272-
pytest.skip()
271+
if "pandas" in str(constructor):
272+
if PANDAS_VERSION < (2, 2):
273+
pytest.skip()
274+
pytest.importorskip("pyarrow")
273275

274276
data = {
275277
"a": [{"movie ": "Cars", "rating": 4.5}, {"movie ": "Toy Story", "rating": 4.9}]
@@ -318,8 +320,10 @@ def test_raise_if_polars_dtype(constructor: Constructor) -> None:
318320

319321

320322
def test_cast_time(request: pytest.FixtureRequest, constructor: Constructor) -> None:
321-
if "pandas" in str(constructor) and PANDAS_VERSION < (2, 2):
322-
pytest.skip()
323+
if "pandas" in str(constructor):
324+
if PANDAS_VERSION < (2, 2):
325+
pytest.skip()
326+
pytest.importorskip("pyarrow")
323327

324328
if any(backend in str(constructor) for backend in ("dask", "pyspark", "cudf")):
325329
request.applymarker(pytest.mark.xfail)
@@ -331,8 +335,10 @@ def test_cast_time(request: pytest.FixtureRequest, constructor: Constructor) ->
331335

332336

333337
def test_cast_binary(request: pytest.FixtureRequest, constructor: Constructor) -> None:
334-
if "pandas" in str(constructor) and PANDAS_VERSION < (2, 2):
335-
pytest.skip()
338+
if "pandas" in str(constructor):
339+
if PANDAS_VERSION < (2, 2):
340+
pytest.skip()
341+
pytest.importorskip("pyarrow")
336342

337343
if any(backend in str(constructor) for backend in ("cudf", "dask")):
338344
request.applymarker(pytest.mark.xfail)
@@ -402,6 +408,7 @@ def test_cast_typing_invalid() -> None:
402408

403409
@pytest.mark.skipif(PANDAS_VERSION < (2,), reason="too old for pyarrow")
404410
def test_pandas_pyarrow_dtypes() -> None:
411+
pytest.importorskip("pyarrow")
405412
s = nw.from_native(
406413
pd.Series([123, None]).convert_dtypes(dtype_backend="pyarrow"), series_only=True
407414
).cast(nw.String)

tests/expr_and_series/cat/get_categories_test.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from __future__ import annotations
22

3-
import pyarrow as pa
43
import pytest
54

65
import narwhals as nw
@@ -25,6 +24,9 @@ def test_get_categories(constructor_eager: ConstructorEager) -> None:
2524

2625

2726
def test_get_categories_pyarrow() -> None:
27+
pytest.importorskip("pyarrow")
28+
import pyarrow as pa
29+
2830
# temporary test until we have `cast` in pyarrow - later, fuse
2931
# this with test above
3032
table = pa.table(

tests/expr_and_series/concat_str_test.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
from typing import Callable
44

5-
import pyarrow as pa
65
import pytest
76

87
import narwhals as nw
98
from tests.utils import POLARS_VERSION, Constructor, assert_equal_data
109

10+
pytest.importorskip("pyarrow")
11+
import pyarrow as pa
12+
1113
data = {"a": [1, 2, 3], "b": ["dogs", "cats", None], "c": ["play", "swim", "walk"]}
1214

1315

tests/expr_and_series/dt/datetime_duration_test.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
from typing import Literal
55

66
import numpy as np
7-
import pyarrow as pa
8-
import pyarrow.compute as pc
97
import pytest
108

119
import narwhals as nw
@@ -102,6 +100,10 @@ def test_duration_attributes_series(
102100
def test_pyarrow_units(
103101
unit: Literal["s", "ms", "us", "ns"], attribute: str, expected: int
104102
) -> None:
103+
pytest.importorskip("pyarrow")
104+
import pyarrow as pa
105+
import pyarrow.compute as pc
106+
105107
data = [None, timedelta(minutes=1, seconds=10)]
106108
arr = pc.cast(pa.array(data), pa.duration(unit))
107109
df = nw.from_native(pa.table({"a": arr}), eager_only=True)

tests/expr_and_series/dt/offset_by_test.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ def test_offset_by_invalid_interval(constructor: Constructor) -> None:
250250
@pytest.mark.skipif(PANDAS_VERSION < (2, 2), reason="too old for pyarrow date type")
251251
def test_offset_by_date_pandas() -> None:
252252
pytest.importorskip("pandas")
253+
pytest.importorskip("pyarrow")
253254
import pandas as pd
254255

255256
df = nw.from_native(pd.DataFrame({"a": [date(2020, 1, 1)]}, dtype="date32[pyarrow]"))

tests/expr_and_series/dt/ordinal_day_test.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
@pytest.mark.skipif(PANDAS_VERSION < (2, 0, 0), reason="pyarrow dtype not available")
1818
@pytest.mark.slow
1919
def test_ordinal_day(dates: datetime) -> None:
20+
pytest.importorskip("pyarrow")
2021
result_pd = nw.from_native(pd.Series([dates]), series_only=True).dt.ordinal_day()[0]
2122
result_pdms = nw.from_native(
2223
pd.Series([dates]).dt.as_unit("ms"), series_only=True

0 commit comments

Comments
 (0)