diff --git a/CHANGELOG.md b/CHANGELOG.md index 09c528ba..5463422f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ * Comparisons like `==`, `<` and `isless` between `Py`s now return `Bool` instead of `Py`. * Removed comparisons between `Py` and `Number` (like `Py(3) < 5`). * Removed arithmetic between `Py` and `Number` (like `Py(2) * 10`). + * Removed unsafe API: `getptr`, `pycopy!`, `pyisnull`, `pynew`, `PyNULL`, `unsafe_pynext`. + A `Py` is now always a valid, fixed, non-NULL Python object in the documented API. * Python errors no longer automatically set `sys.last_traceback` etc. when displayed from Julia. * Removed `CONFIG.auto_sys_last_traceback`. * Changes to `PythonCall.GC` (now more like `Base.GC`): diff --git a/docs/src/pythoncall-reference.md b/docs/src/pythoncall-reference.md index 27b0d1b8..3dfc8ff3 100644 --- a/docs/src/pythoncall-reference.md +++ b/docs/src/pythoncall-reference.md @@ -250,12 +250,7 @@ The functions here are not exported. They are mostly unsafe in the sense that yo crash Julia by using them incorrectly. ```@docs -PythonCall.pynew -PythonCall.pyisnull -PythonCall.pycopy! -PythonCall.getptr PythonCall.pydel! -PythonCall.unsafe_pynext ``` ## NumpyDates diff --git a/docs/src/v1-migration-guide.md b/docs/src/v1-migration-guide.md index 7fa2cc09..cef13097 100644 --- a/docs/src/v1-migration-guide.md +++ b/docs/src/v1-migration-guide.md @@ -24,6 +24,41 @@ between Python and Julia is explicit. * Instead of `Py(5) * 6` use `Py(5) * Py(6)` or `pymul(Py(5), 6)`. * Instead of `np.array([1,2,3]) < 3` use `pylt(np.array([1,2,3]), 3)`. +The unsafe API has been removed: `getptr`, `pycopy!`, `pyisnull`, `pynew`, `PyNULL`, +`unsafe_pynext`. This means that a `Py` is immutable and non-NULL in the documented API +for PythonCall (mutability and NULLs are internal implementation details). These functions +were primarily used in packages to have global `Py` objects with this pattern: + +``` +module Foo + using PythonCall + + const np = PythonCall.pynew() + + function __init__() + PythonCall.pycopy!(np, pyimport("numpy")) + end +end +``` + +We have recommended this alternate, safe pattern for a while now: + +``` +module Foo + using PythonCall + + const np = Ref{Py}() + + function __init__() + np[] = pyimport("numpy") + end +end +``` + +* Instead of `PythonCall.pynew()` use `Ref{Py}()`. +* Instead of `PythonCall.pycopy!(x, y)` use `x[] = y`. +* Instead of `PythonCall.unsafe_pynext(x)` (and check for `pyisnull`) use `pynext(x, nothing)` (and check for `nothing`). + When a Python error is displayed in Julia, PythonCall no longer sets `sys.last_traceback` and friends. This means that the Python post-mortem debugger `pdb.pm()` will no longer work. diff --git a/src/API/functions.jl b/src/API/functions.jl index 0b8689bc..74ccdcb0 100644 --- a/src/API/functions.jl +++ b/src/API/functions.jl @@ -5,7 +5,6 @@ function python_library_path end function python_version end # Core -function getptr end function ispy end function pyabs end function pyadd end @@ -21,7 +20,6 @@ function pycollist end function pycompile end function pycomplex end function pycontains end -function pycopy! end function pydate end function pydatetime end function pydel! end @@ -62,7 +60,6 @@ function pyipow end function pyirshift end function pyis end function pyisinstance end -function pyisnull end function pyissubclass end function pyisub end function pyiter end @@ -78,7 +75,6 @@ function pymod end function pymul end function pyne end function pyneg end -function pynew end function pynext end function pynot end function pyor end @@ -102,7 +98,6 @@ function pytuple end function pytype end function pywith end function pyxor end -function unsafe_pynext end # Convert function pyconvert end diff --git a/src/API/publics.jl b/src/API/publics.jl index cc15d516..a8c4cdd1 100644 --- a/src/API/publics.jl +++ b/src/API/publics.jl @@ -13,13 +13,7 @@ if Base.VERSION ≥ v"1.11" # Core CONFIG, - getptr, - pycopy!, pydel!, - pyisnull, - pynew, - PyNULL, - unsafe_pynext, # Compat event_loop_off, diff --git a/src/Core/Core.jl b/src/Core/Core.jl index cc74234e..de144bf3 100644 --- a/src/Core/Core.jl +++ b/src/Core/Core.jl @@ -34,7 +34,6 @@ import ..PythonCall: @pyconst, @pyeval, @pyexec, - getptr, ispy, Py, pyabs, @@ -53,7 +52,6 @@ import ..PythonCall: pycomplex, pycontains, pyconvert, - pycopy!, pydate, pydatetime, pydel!, @@ -95,7 +93,6 @@ import ..PythonCall: pyirshift, pyis, pyisinstance, - pyisnull, pyissubclass, pyisub, pyiter, @@ -111,7 +108,6 @@ import ..PythonCall: pymul, pyne, pyneg, - pynew, pynext, pynot, pyor, @@ -134,8 +130,7 @@ import ..PythonCall: pytuple, pytype, pywith, - pyxor, - unsafe_pynext + pyxor export _base_datetime, diff --git a/src/PythonCall.jl b/src/PythonCall.jl index e8c7597f..a689e43d 100644 --- a/src/PythonCall.jl +++ b/src/PythonCall.jl @@ -16,7 +16,7 @@ include("JlWrap/JlWrap.jl") include("Compat/Compat.jl") # non-exported API -using .Core: PyNULL, CONFIG +using .Core: CONFIG # not API but used in tests for k in [ diff --git a/test/Core.jl b/test/Core.jl index e7895594..b1eccd15 100644 --- a/test/Core.jl +++ b/test/Core.jl @@ -2,7 +2,7 @@ import Markdown @testset "pyis" begin x = pylist() - y = PythonCall.pynew(x) + y = PythonCall.Core.pynew(x) z = pylist() @test pyis(x, x) @test pyis(x, y) @@ -218,7 +218,7 @@ @test Base.Docs.getdoc(Py(nothing)) isa Markdown.MD @test Base.Docs.getdoc(Py(12)) isa Markdown.MD @test Base.Docs.getdoc(pybuiltins.int) isa Markdown.MD - @test Base.Docs.getdoc(PythonCall.PyNULL) === nothing + @test Base.Docs.getdoc(PythonCall.Core.PyNULL) === nothing end @testset "comparisons" begin @testset "Py vs Py" begin @@ -255,14 +255,14 @@ end @test_throws PyException pyiter(pybuiltins.True) # unsafe_pynext it = pyiter(pyrange(2)) - x = PythonCall.unsafe_pynext(it) - @test !PythonCall.pyisnull(x) + x = PythonCall.Core.unsafe_pynext(it) + @test !PythonCall.Core.pyisnull(x) @test pyeq(Bool, x, 0) - x = PythonCall.unsafe_pynext(it) - @test !PythonCall.pyisnull(x) + x = PythonCall.Core.unsafe_pynext(it) + @test !PythonCall.Core.pyisnull(x) @test pyeq(Bool, x, 1) - x = PythonCall.unsafe_pynext(it) - @test PythonCall.pyisnull(x) + x = PythonCall.Core.unsafe_pynext(it) + @test PythonCall.Core.pyisnull(x) # pynext it = pyiter(pyrange(2)) x = pynext(it) @@ -825,18 +825,18 @@ end @test showable(MIME("text/plain"), Py(nothing)) @test showable(MIME("text/plain"), Py(12)) # https://github.com/JuliaPy/PythonCall.jl/issues/522 - @test showable(MIME("text/plain"), PythonCall.pynew()) - @test !showable(MIME("text/html"), PythonCall.pynew()) + @test showable(MIME("text/plain"), PythonCall.Core.pynew()) + @test !showable(MIME("text/html"), PythonCall.Core.pynew()) end @testset "show" begin @test sprint(show, MIME("text/plain"), Py(nothing)) == "Python: None" @test sprint(show, MIME("text/plain"), Py(12)) == "Python: 12" # https://github.com/JuliaPy/PythonCall.jl/issues/522 - @test sprint(show, MIME("text/plain"), PythonCall.pynew()) == "Python: NULL" + @test sprint(show, MIME("text/plain"), PythonCall.Core.pynew()) == "Python: NULL" # test compact printing @test sprint(show, MIME("text/plain"), Py(String('A':'Z')), context=(:compact=>true, :displaysize=>(50, 20))) == "Py: 'ABCDE ... WXYZ'" @test sprint(show, MIME("text/plain"), Py(String('A':'Z')), context=(:compact=>true, :limit=>false, :displaysize=>(50, 20))) == "Py: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" - @test_throws MethodError sprint(show, MIME("text/html"), PythonCall.pynew()) + @test_throws MethodError sprint(show, MIME("text/html"), PythonCall.Core.pynew()) end end