From 29d43623379739d79bff3558c182de206d1f4c29 Mon Sep 17 00:00:00 2001 From: Christopher Rowley Date: Fri, 6 Feb 2026 22:53:36 +0000 Subject: [PATCH] fix compatibility with pandas 3 --- CHANGELOG.md | 1 + CondaPkg.toml | 1 + src/Wrap/Wrap.jl | 28 ++++++++++++++++------------ test/Compat.jl | 8 +++++--- test/Convert.jl | 13 +++++++++++++ test/Wrap.jl | 33 +++++++++++++++------------------ 6 files changed, 51 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56609490..80df01fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased * Added `juliacall.TypeValue.__numpy_dtype__` attribute to allow converting Julia types to the corresponding NumPy dtype, like `numpy.dtype(jl.Int)`. +* Bug fixes. ## 0.9.31 (2025-12-17) * Restore support for Python 3.14+. diff --git a/CondaPkg.toml b/CondaPkg.toml index 80243bd9..555e81b3 100644 --- a/CondaPkg.toml +++ b/CondaPkg.toml @@ -16,3 +16,4 @@ version = ">=3.10,!=3.14.0,!=3.14.1,<4" matplotlib = "" numpy = "" pyside6 = "" +pandas = "" diff --git a/src/Wrap/Wrap.jl b/src/Wrap/Wrap.jl index e67129cb..d3b30ff2 100644 --- a/src/Wrap/Wrap.jl +++ b/src/Wrap/Wrap.jl @@ -55,18 +55,22 @@ function __init__() pyconvert_add_rule("collections.abc:Mapping", PyDict, pyconvert_rule_mapping, priority) pyconvert_add_rule("io:IOBase", PyIO, pyconvert_rule_io, priority) pyconvert_add_rule("_io:_IOBase", PyIO, pyconvert_rule_io, priority) - pyconvert_add_rule( - "pandas.core.frame:DataFrame", - PyPandasDataFrame, - pyconvert_rule_pandasdataframe, - priority, - ) - pyconvert_add_rule( - "pandas.core.arrays.base:ExtensionArray", - PyList, - pyconvert_rule_sequence, - priority, - ) + for typename in ["pandas.core.frame:DataFrame", "pandas:DataFrame"] + pyconvert_add_rule( + typename, + PyPandasDataFrame, + pyconvert_rule_pandasdataframe, + priority, + ) + end + for typename in ["pandas.core.arrays.base:ExtensionArray", "pandas.api.extensions:ExtensionArray"] + pyconvert_add_rule( + typename, + PyList, + pyconvert_rule_sequence, + priority, + ) + end priority = PYCONVERT_PRIORITY_NORMAL pyconvert_add_rule("", Array, pyconvert_rule_array, priority) diff --git a/test/Compat.jl b/test/Compat.jl index 00c93ac6..4ad57a5c 100644 --- a/test/Compat.jl +++ b/test/Compat.jl @@ -104,12 +104,14 @@ end end end -@testitem "Tables.jl" begin +@testitem "Tables.jl" setup=[Setup] begin @testset "pytable" begin x = (x = [1, 2, 3], y = ["a", "b", "c"]) # pandas - # TODO: install pandas and test properly - @test_throws PyException pytable(x, :pandas) + if Setup.devdeps + y = pytable(x, :pandas) + @test pyeq(Bool, pytype(y), pyimport("pandas").DataFrame) + end # columns y = pytable(x, :columns) @test pyeq(Bool, y, pydict(x = [1, 2, 3], y = ["a", "b", "c"])) diff --git a/test/Convert.jl b/test/Convert.jl index 4f137459..2765440f 100644 --- a/test/Convert.jl +++ b/test/Convert.jl @@ -305,6 +305,19 @@ end @test_throws Exception pyconvert(Second, td(microseconds = 1000)) end +@testitem "pandas.DataFrame → PyPandasDataFrame" setup=[Setup] begin + if Setup.devdeps + pd = pyimport("pandas") + df = pd.DataFrame() + df2 = pyconvert(PyPandasDataFrame, df) + @test df2 isa PyPandasDataFrame + @test pyis(df2, df) + df3 = pyconvert(PyTable, df) + @test df3 isa PyPandasDataFrame + @test pyis(df3, df) + end +end + @testitem "pyconvert_add_rule (#364)" begin id = string(rand(UInt128), base = 16) pyexec( diff --git a/test/Wrap.jl b/test/Wrap.jl index 9e23f8a3..c42d45ab 100644 --- a/test/Wrap.jl +++ b/test/Wrap.jl @@ -458,26 +458,23 @@ end end end -@testitem "PyPandasDataFrame" begin +@testitem "PyPandasDataFrame" setup=[Setup] begin using Tables @test PyPandasDataFrame isa Type - # TODO: figure out how to get pandas into the test environment - # for now use some dummy type and take advantage of the fact that the code doesn't actually check it's a real dataframe - @pyexec """ - class DataFrame: - def __init__(self, **kw): - self.__dict__.update(kw) - """ => DataFrame - df = DataFrame(shape = (4, 3), columns = pylist(["foo", "bar", "baz"])) - x = PyPandasDataFrame(df) - @test ispy(x) - @test Py(x) === df - @test Tables.istable(x) - @test Tables.columnaccess(x) - @test_throws Exception Tables.columns(x) - @test_throws Exception pyconvert(PyPandasDataFrame, 1) - str = sprint(show, MIME("text/plain"), x) - @test occursin(r"4×3 .*PyPandasDataFrame", str) + if Setup.devdeps + pd = pyimport("pandas") + df = pd.DataFrame(pydict(foo=pylist([1,2,3,4]), bar=pylist([2,3,4,5]), baz=pylist([3,4,5,6]))) + x = PyPandasDataFrame(df) + @test ispy(x) + @test Py(x) === df + @test Tables.istable(x) + @test Tables.columnaccess(x) + # TODO: test the tables interface more fully + Tables.columns(x) + @test_throws Exception pyconvert(PyPandasDataFrame, 1) + str = sprint(show, MIME("text/plain"), x) + @test occursin(r"4×3 .*PyPandasDataFrame", str) + end end @testitem "PySet" begin