From a88f9866a5efeb8de41306945e885dae975b7a6f Mon Sep 17 00:00:00 2001 From: Christopher Doris Date: Thu, 23 Oct 2025 21:05:23 +0100 Subject: [PATCH 1/4] add a migration guide for v1 --- docs/src/releasenotes.md | 11 ++--- docs/src/v1-migration-guide.md | 77 ++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 docs/src/v1-migration-guide.md diff --git a/docs/src/releasenotes.md b/docs/src/releasenotes.md index f5aa5ff3..9c7214e2 100644 --- a/docs/src/releasenotes.md +++ b/docs/src/releasenotes.md @@ -1,11 +1,14 @@ # Release Notes ## Unreleased (v1) -* Breaking changes to `PythonCall.GC`, which is now more like `Base.GC`: +* The vast majority of these changes are breaking, see the [Migration Guide](@ref v1-migration-guide) for how to upgrade to v1. +* Changes to core functionality: + * Comparisons like `==(::Py, ::Py)`, `<(::Py, ::Number)`, `isless(::Number, ::Py)` now return `Bool` instead of `Py`. +* Changes to `PythonCall.GC` (now more like `Base.GC`): * `enable(true)` replaces `enable()`. * `enable(false)` replaces `disable()`. * `gc()` added. -* Breaking changes to Julia wrapper types: +* 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`. * `Jl` now behaves similar to how `RawValue` behaved before. In particular, most methods on `Jl` now return a `Jl` instead of an arbitrary Python object. @@ -15,9 +18,7 @@ * 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`. + * New functions: `pyjlarray`, `pyjldict`, `pyjlset`. ## Unreleased * Minimum supported Python version is now 3.10. diff --git a/docs/src/v1-migration-guide.md b/docs/src/v1-migration-guide.md new file mode 100644 index 00000000..3ef1bb29 --- /dev/null +++ b/docs/src/v1-migration-guide.md @@ -0,0 +1,77 @@ +# [v0.9 to v1 Migration Guide](@id v1-migration-guide) + +## Core functionality + +Comparisons (`==`, `<`, etc.) between Python objects `Py`, or between `Py` and `Number`, +used to return `Py` but now return `Bool`. The old behaviour was a pun but broke the +Base API behaviour of these functions. These comparisons will now raise an error if the +underlying Python operation does not return `bool`. + +* Instead of `pytruth(Py(3) < Py(5))` use `Py(3) < Py(5)`. +* Instead of `Py(3) < Py(5)` use `Py(Py(3) < Py(5))`. +* Instead of `np.array([1,2,3]) < Py(3)` use `pylt(np.array([1,2,3]), Py(3))`. This is + because comparisons on numpy arrays return arrays of `bool` rather than a single + `bool`. +* Instead of `pylt(Bool, Py(3), Py(5))` you can use `Py(3) < Py(5)`. + +## `PythonCall.GC` + +This submodule has been changed to closer mimic the `Base.GC` API. + +* Instead of `PythonCall.GC.enable()` use `PythonCall.GC.enable(true)`. +* Instead of `PythonCall.GC.disable()` use `PythonCall.GC.enable(false)`. + +## Julia wrappers (JlDict, etc.) + +The wrapper types have been renamed. + +* Instead of `juliacall.AnyValue` use `juliacall.Jl` (but see below). +* Instead of `juliacall.ArrayValue` use `juliacall.JlArray`. +* Instead of `juliacall.DictValue` use `juliacall.JlDict`. + +Most methods on the `Jl` class return a `Jl` now instead of an arbitrary Python object +converted from the Julia return value. This makes generic programming easier and +more closely reflects the behaviour of `Py`. + +* Instead of `jl.seval("1+2")` use `jl.jl_eval("1+2").jl_to_py()`. +* Instead of `jl.rand(5)[0]` use `jl.rand(5)[1].jl_to_py()`. Note the shift from 0-based + to 1-based indexing - previously `jl.rand(5)` was a `juliacall.VectorValue` which + supported Python 0-based indexing, but now `jl.rand(5)` is a `juliacall.Jl` which + supports indexing by passing the arguments directly to Julia, which is 1-based. + +Some wrapper types have been removed and can mostly be replaced with `Jl`. + +* Instead of `juliacall.RawValue` use `juliacall.Jl`, since this behaves much the same + now. +* Instead of `juliacall.IntegerValue` (and other number types) use `int`, `float`, + `complex` or other numeric types as appropriate. Alternatively use `juliacall.Jl` + which supports the basic arithmetic and comparison operators, but is not strictly a + number. +* Instead of `juliacall.ModuleValue` use `juliacall.Jl`. The only benefit of + `ModuleValue` was its `seval` method, which is now `Jl.jl_eval`. +* Instead of `juliacall.TypeValue` use `juliacall.Jl`. The only benefit of `TypeValue` + was that indexing syntax (`jl.Vector[jl.Type]`) was converted to Julia's curly syntax + (`Vector{Type}`) but `Jl` does this now (for types). + +Methods with the `_jl_` prefix are renamed with the `jl_` prefix: +* Instead of `x._jl_help()` use `x.jl_help()`. +* Instead of `x._jl_display()` use `x.jl_display()`. + +The `seval` function is now called `jl_eval`: +* Instead of `juliacall.Main.seval("1+2")` use `juliacall.Main.jl_eval("1+2")`. + +Other methods, functions and attributes removed: +* Instead of `x._jl_raw()` use `x` (if already a `Jl`) or `Jl(x)`. This is because the + old `AnyValue` and `RawValue` are replaced by `Jl`. +* Instead of `juliacall.convert(type, value)` use `juliacall.Jl(value, type)`. +* Instead of `juliacall.Pkg` you must import import it yourself, such as + `juliacall.Main.jl_eval("using Pkg; Pkg")`. + +On the Julia side, the `pyjl` function now always returns a `Jl`, whereas before it +would return one of the more specific wrappers (now called `JlDict`, `JlArray`, etc.). + +* Instead of `pyjl([1, 2, 3])` use `pyjlarray([1, 2, 3])` if you need a `JlArray`. +* Instead of `pyjl(Dict())` use `pyjldict(Dict())` if you need a `JlDict`. +* Instead of `pyjl(Set())` use `pyjlset(Set())` if you need a `JlSet`. +* Continue to use `pyjl` if you are OK with the result being a `Jl`. +* Note that `Py([1, 2, 3])` still returns a `JlArray`, etc., only `pyjl` itself changed. From 7dcb9fe453d156c5298d0de401bc13191568d2e5 Mon Sep 17 00:00:00 2001 From: Christopher Doris Date: Thu, 23 Oct 2025 21:06:35 +0100 Subject: [PATCH 2/4] actually add the migration guide to the docs --- docs/make.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/make.jl b/docs/make.jl index 7b04641e..9966e378 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -20,6 +20,7 @@ makedocs( "compat.md", "faq.md", "releasenotes.md", + "v1-migration-guide.md", ], ) From 5eba854c8c4dbb1e2c5c843f23ab2e5f7193b629 Mon Sep 17 00:00:00 2001 From: Christopher Doris Date: Thu, 23 Oct 2025 21:20:51 +0100 Subject: [PATCH 3/4] add PyArray to migration guide --- docs/src/v1-migration-guide.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/docs/src/v1-migration-guide.md b/docs/src/v1-migration-guide.md index 3ef1bb29..1300d42b 100644 --- a/docs/src/v1-migration-guide.md +++ b/docs/src/v1-migration-guide.md @@ -21,7 +21,29 @@ This submodule has been changed to closer mimic the `Base.GC` API. * Instead of `PythonCall.GC.enable()` use `PythonCall.GC.enable(true)`. * Instead of `PythonCall.GC.disable()` use `PythonCall.GC.enable(false)`. -## Julia wrappers (JlDict, etc.) +## Python wrappers (`PyArray`, etc.) + +`PyArray` has been reparametrised from `PyArray{T,N,L,M,R}` to `PyArray{T,N,F}` where +`F` is a `Tuple` of `Symbol` flags replacing `L` (now `:linear`) and `M` +(now `:mutable`). The `R` parameter (the underlying raw type) is removed and now implied +by `T`. + +* Instead of `PyArray{Int,2,true,true,Int}` use `PyArray{Int,2,(:mutable,:linear)}`. +* Instead of `PyArray{Bool,1,false,false,Bool}` use `PyArray{Bool,1,()}`. +* Instead of `PyArray{Py,2,false,false,PythonCall.Wrap.UnsafePyObject}` use `PyArray{Py,2,()}`. + +Because the `R` parameter is removed, if the underlying array is of Python objects, the +`PyArray` must have eltype `Py`. Previously you could construct a `PyArray{String}` from +such a thing and the elements would be automatically `pyconvert(String, element)`-ed for +you. + +* Instead of `PyArray{String}(x)` use `pyconvert.(String, PyArray{Py}(x))` if you are + OK with taking a copy. Or use `mappedarray(x->pyconvert(String, x), PyArray{Py}(x))` + from [MappedArrays.jl](https://github.com/JuliaArrays/MappedArrays.jl) to emulate the + old behaviour. +* Same comments for `pyconvert(PyArray{String}, x)`. + +## Julia wrappers (`JlDict`, etc.) The wrapper types have been renamed. From bac7e38597953835107018f7500d84d7643ca95c Mon Sep 17 00:00:00 2001 From: Christopher Doris Date: Thu, 23 Oct 2025 21:51:09 +0100 Subject: [PATCH 4/4] tweaks --- docs/src/releasenotes.md | 2 +- docs/src/v1-migration-guide.md | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/src/releasenotes.md b/docs/src/releasenotes.md index 4ab9d32f..bde8d317 100644 --- a/docs/src/releasenotes.md +++ b/docs/src/releasenotes.md @@ -1,7 +1,7 @@ # Release Notes ## Unreleased (v1) -* The vast majority of these changes are breaking, see the [Migration Guide](@ref v1-migration-guide) for how to upgrade to v1. +* The vast majority of these changes are breaking, see the [v1 Migration Guide](@ref) for how to upgrade. * Changes to core functionality: * Comparisons like `==(::Py, ::Py)`, `<(::Py, ::Number)`, `isless(::Number, ::Py)` now return `Bool` instead of `Py`. * Changes to `PythonCall.GC` (now more like `Base.GC`): diff --git a/docs/src/v1-migration-guide.md b/docs/src/v1-migration-guide.md index 1300d42b..87950289 100644 --- a/docs/src/v1-migration-guide.md +++ b/docs/src/v1-migration-guide.md @@ -1,4 +1,6 @@ -# [v0.9 to v1 Migration Guide](@id v1-migration-guide) +# v1 Migration Guide + +Use this guide to help with migrating code from v0.9 to v1. ## Core functionality