From 47dc5488d04ad54b8e79fbbb6e4bd5821269adce Mon Sep 17 00:00:00 2001 From: Christopher Doris Date: Thu, 23 Oct 2025 15:29:29 +0100 Subject: [PATCH 1/2] comparisons now return Bool not Py --- docs/src/releasenotes.md | 7 +++- src/Core/Py.jl | 36 +++++++++--------- test/Core.jl | 80 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 19 deletions(-) diff --git a/docs/src/releasenotes.md b/docs/src/releasenotes.md index 4f10247a..9d7e3333 100644 --- a/docs/src/releasenotes.md +++ b/docs/src/releasenotes.md @@ -1,7 +1,10 @@ # Release Notes ## Unreleased (v1) -* `PythonCall.GC` is now more like `Base.GC`: `enable(true)` replaces `enable()`, `enable(false)` replaces `disable()`, and `gc()` is added. +* Breaking changes to `PythonCall.GC`, which is now more like `Base.GC`: + * `enable(true)` replaces `enable()`. + * `enable(false)` replaces `disable()`. + * `gc()` added. * Breaking changes to Julia wrapper types: * Classes renamed: `ValueBase` to `JlBase`, `AnyValue` to `Jl`, `ArrayValue` to `JlArray`, etc. * Classes removed: `RawValue`, `ModuleValue`, `TypeValue`, `NumberValue`, `ComplexValue`, `RealValue`, `RationalValue`, `IntegerValue`. @@ -12,6 +15,8 @@ * Methods removed: `_jl_raw()`. * `pyjl(x)` now always returns a `juliacall.Jl` (it used to select a wrapper type if possible). * `pyjltype(x)` removed. +* Other breaking changes: + * Comparisons like `==(::Py, ::Py)`, `<(::Py, ::Number)`, `isless(::Number, ::Py)` now return `Bool` instead of `Py`. * New functions: `pyjlarray`, `pyjldict`, `pyjlset`. ## 0.9.28 (2025-09-17) diff --git a/src/Core/Py.jl b/src/Core/Py.jl index 9faea130..002e435a 100644 --- a/src/Core/Py.jl +++ b/src/Core/Py.jl @@ -334,31 +334,31 @@ Base.broadcastable(x::Py) = Ref(x) (f::Py)(args...; kwargs...) = pycall(f, args...; kwargs...) # comparisons -Base.:(==)(x::Py, y::Py) = pyeq(x, y) -Base.:(!=)(x::Py, y::Py) = pyne(x, y) -Base.:(<=)(x::Py, y::Py) = pyle(x, y) -Base.:(<)(x::Py, y::Py) = pylt(x, y) -Base.:(>=)(x::Py, y::Py) = pyge(x, y) -Base.:(>)(x::Py, y::Py) = pygt(x, y) +Base.:(==)(x::Py, y::Py) = pyeq(Bool, x, y) +Base.:(!=)(x::Py, y::Py) = pyne(Bool, x, y) +Base.:(<=)(x::Py, y::Py) = pyle(Bool, x, y) +Base.:(<)(x::Py, y::Py) = pylt(Bool, x, y) +Base.:(>=)(x::Py, y::Py) = pyge(Bool, x, y) +Base.:(>)(x::Py, y::Py) = pygt(Bool, x, y) Base.isless(x::Py, y::Py) = pylt(Bool, x, y) Base.isequal(x::Py, y::Py) = pyeq(Bool, x, y) # we also allow comparison with numbers -Base.:(==)(x::Py, y::Number) = pyeq(x, y) -Base.:(!=)(x::Py, y::Number) = pyne(x, y) -Base.:(<=)(x::Py, y::Number) = pyle(x, y) -Base.:(<)(x::Py, y::Number) = pylt(x, y) -Base.:(>=)(x::Py, y::Number) = pyge(x, y) -Base.:(>)(x::Py, y::Number) = pygt(x, y) +Base.:(==)(x::Py, y::Number) = pyeq(Bool, x, y) +Base.:(!=)(x::Py, y::Number) = pyne(Bool, x, y) +Base.:(<=)(x::Py, y::Number) = pyle(Bool, x, y) +Base.:(<)(x::Py, y::Number) = pylt(Bool, x, y) +Base.:(>=)(x::Py, y::Number) = pyge(Bool, x, y) +Base.:(>)(x::Py, y::Number) = pygt(Bool, x, y) Base.isless(x::Py, y::Number) = pylt(Bool, x, y) Base.isequal(x::Py, y::Number) = pyeq(Bool, x, y) -Base.:(==)(x::Number, y::Py) = pyeq(x, y) -Base.:(!=)(x::Number, y::Py) = pyne(x, y) -Base.:(<=)(x::Number, y::Py) = pyle(x, y) -Base.:(<)(x::Number, y::Py) = pylt(x, y) -Base.:(>=)(x::Number, y::Py) = pyge(x, y) -Base.:(>)(x::Number, y::Py) = pygt(x, y) +Base.:(==)(x::Number, y::Py) = pyeq(Bool, x, y) +Base.:(!=)(x::Number, y::Py) = pyne(Bool, x, y) +Base.:(<=)(x::Number, y::Py) = pyle(Bool, x, y) +Base.:(<)(x::Number, y::Py) = pylt(Bool, x, y) +Base.:(>=)(x::Number, y::Py) = pyge(Bool, x, y) +Base.:(>)(x::Number, y::Py) = pygt(Bool, x, y) Base.isless(x::Number, y::Py) = pylt(Bool, x, y) Base.isequal(x::Number, y::Py) = pyeq(Bool, x, y) diff --git a/test/Core.jl b/test/Core.jl index 32b4d59f..c1e1727e 100644 --- a/test/Core.jl +++ b/test/Core.jl @@ -220,6 +220,86 @@ @test Base.Docs.getdoc(pybuiltins.int) isa Markdown.MD @test Base.Docs.getdoc(PythonCall.PyNULL) === nothing end + @testset "comparisons" begin + @testset "Py vs Py" begin + # == + @test Py(1) == Py(1) + @test !(Py(1) == Py(2)) + @test !(Py(1) == Py(0)) + # != + @test Py(2) != Py(1) + @test Py(2) != Py(3) + @test !(Py(2) != Py(2)) + # < + @test Py(3) < Py(4) + @test !(Py(3) < Py(3)) + @test !(Py(3) < Py(2)) + # <= + @test Py(4) <= Py(5) + @test Py(4) <= Py(4) + @test !(Py(4) <= Py(3)) + # > + @test Py(5) > Py(4) + @test !(Py(5) > Py(5)) + @test !(Py(5) > Py(6)) + # >= + @test Py(5) >= Py(4) + @test Py(5) >= Py(5) + @test !(Py(5) >= Py(6)) + end + @testset "Py vs Number" begin + # == + @test Py(1) == 1 + @test !(Py(1) == 2) + @test !(Py(1) == 0) + # != + @test Py(2) != 1 + @test Py(2) != 3 + @test !(Py(2) != 2) + # < + @test Py(3) < 4 + @test !(Py(3) < 3) + @test !(Py(3) < 2) + # <= + @test Py(4) <= 5 + @test Py(4) <= 4 + @test !(Py(4) <= 3) + # > + @test Py(5) > 4 + @test !(Py(5) > 5) + @test !(Py(5) > 6) + # >= + @test Py(5) >= 4 + @test Py(5) >= 5 + @test !(Py(5) >= 6) + end + @testset "Number vs Py" begin + # == + @test 1 == Py(1) + @test !(1 == Py(2)) + @test !(1 == Py(0)) + # != + @test 2 != Py(1) + @test 2 != Py(3) + @test !(2 != Py(2)) + # < + @test 3 < Py(4) + @test !(3 < Py(3)) + @test !(3 < Py(2)) + # <= + @test 4 <= Py(5) + @test 4 <= Py(4) + @test !(4 <= Py(3)) + # > + @test 5 > Py(4) + @test !(5 > Py(5)) + @test !(5 > Py(6)) + # >= + @test 5 >= Py(4) + @test 5 >= Py(5) + @test !(5 >= Py(6)) + end + end end @testitem "iter" begin From 459a79fbcdd9fef0d792c2061041e1faa8f00461 Mon Sep 17 00:00:00 2001 From: Christopher Doris Date: Thu, 23 Oct 2025 15:30:40 +0100 Subject: [PATCH 2/2] fix bug in JlIO.fileno() from change of return type of fd() --- src/JlWrap/io.jl | 2 +- test/JlWrap.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/JlWrap/io.jl b/src/JlWrap/io.jl index f220d214..3070e3fe 100644 --- a/src/JlWrap/io.jl +++ b/src/JlWrap/io.jl @@ -10,7 +10,7 @@ pyjlio_closed(io::IO) = Py(!isopen(io)) pyjl_handle_error_type(::typeof(pyjlio_closed), io, exc) = exc isa MethodError && exc.f === isopen ? pybuiltins.ValueError : PyNULL -pyjlio_fileno(io::IO) = Py(fd(io)) +pyjlio_fileno(io::IO) = Py(Base.cconvert(Cint, fd(io))::Cint) pyjl_handle_error_type(::typeof(pyjlio_fileno), io, exc) = exc isa MethodError && exc.f === fd ? pybuiltins.ValueError : PyNULL diff --git a/test/JlWrap.jl b/test/JlWrap.jl index 693769d9..b1497931 100644 --- a/test/JlWrap.jl +++ b/test/JlWrap.jl @@ -718,7 +718,7 @@ end y = pytextio(x) z = y.fileno() @test pyisinstance(z, pybuiltins.int) - @test pyeq(Bool, z, fd(x)) + @test z == Base.cconvert(Cint, fd(x)) end end @testset "flush" begin