From 234a8819ff70aebe04d8dc6cce6f90be3ba47ec7 Mon Sep 17 00:00:00 2001 From: Christopher Rowley Date: Thu, 4 Jun 2026 12:10:15 +0100 Subject: [PATCH] rename pydel! to unsafe_pydel and export --- BENCHMARKS.md | 18 ++++----- CHANGELOG.md | 1 + benchmark/benchmarks.jl | 12 +++--- docs/src/pythoncall-reference.md | 2 +- docs/src/v1-migration-guide.md | 7 +++- src/API/exports.jl | 1 + src/API/functions.jl | 2 +- src/API/publics.jl | 1 - src/Convert/pyconvert.jl | 2 +- src/Convert/rules.jl | 26 ++++++------- src/Core/Core.jl | 4 +- src/Core/Py.jl | 8 ++-- src/Core/builtins.jl | 64 ++++++++++++++++---------------- src/JlWrap/any.jl | 40 ++++++++++---------- src/JlWrap/array.jl | 10 ++--- src/JlWrap/io.jl | 16 ++++---- src/JlWrap/set.jl | 8 ++-- src/JlWrap/vector.jl | 12 +++--- src/PyMacro/PyMacro.jl | 10 ++--- src/Wrap/PyArray.jl | 8 ++-- src/Wrap/PyDict.jl | 2 +- src/Wrap/PyIO.jl | 26 ++++++------- src/Wrap/PyIterable.jl | 2 +- src/Wrap/PyList.jl | 8 ++-- src/Wrap/PySet.jl | 10 ++--- 25 files changed, 153 insertions(+), 147 deletions(-) diff --git a/BENCHMARKS.md b/BENCHMARKS.md index 454888d7..ce8574ae 100644 --- a/BENCHMARKS.md +++ b/BENCHMARKS.md @@ -6,9 +6,9 @@ | ------- | ------ | ------------------- | ----------- | | Python | 1.0x | 280 | ? | | PythonCall | 2.4x | 680 | 5008 | -| PythonCall + `pydel!` | 1.1x | 300 | 1008 | +| PythonCall + `unsafe_pydel` | 1.1x | 300 | 1008 | | PythonCall `@py` | 1.4x | 420 | 1002 | -| PythonCall `@py` + `@pydel!` | 1.1x | 300 | 2 | +| PythonCall `@py` + `unsafe_pydel` | 1.1x | 300 | 2 | | PyCall | 5.4x | 1620 | 10987 | | PyCall (readable but wrong) | 5.9x | 1784 | 11456 | @@ -42,7 +42,7 @@ test (generic function with 1 method) julia> @benchmark test() ``` -PythonCall + `pydel!` code: +PythonCall + `unsafe_pydel` code: ```julia-repl julia> using PythonCall, BenchmarkTools @@ -54,10 +54,10 @@ julia> function test() r = random() v = i + r x[k] = v - pydel!(k) - pydel!(r) - pydel!(v) - pydel!(i) + unsafe_pydel(k) + unsafe_pydel(r) + unsafe_pydel(v) + unsafe_pydel(i) end return x end @@ -75,8 +75,8 @@ julia> test() = @py begin x = {} for i in range(1000) x[str(i)] = i + random() - # Uncomment for pydel! version: - # @jl PythonCall.pydel!(i) + # Uncomment for unsafe_pydel version: + # @jl PythonCall.unsafe_pydel(i) end x end diff --git a/CHANGELOG.md b/CHANGELOG.md index 5463422f..6c5ea33e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * 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`. + * Renamed `pydel!` to `unsafe_pydel` and exported. 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`. diff --git a/benchmark/benchmarks.jl b/benchmark/benchmarks.jl index 38bbfd07..1958f89a 100644 --- a/benchmark/benchmarks.jl +++ b/benchmark/benchmarks.jl @@ -1,6 +1,6 @@ using BenchmarkTools using PythonCall -using PythonCall: pydel!, pyimport, pydict, pystr, pyrange +using PythonCall: unsafe_pydel, pyimport, pydict, pystr, pyrange const SUITE = BenchmarkGroup() @@ -23,10 +23,10 @@ function test_pydict_pydel() r = random() v = i + r x[k] = v - pydel!(k) - pydel!(r) - pydel!(v) - pydel!(i) + unsafe_pydel(k) + unsafe_pydel(r) + unsafe_pydel(v) + unsafe_pydel(i) end return x end @@ -40,7 +40,7 @@ SUITE["basic"]["julia"]["pydict"]["pydel"] = @benchmarkable test_pydict_pydel() x = {} for i in range(1000) x[str(i)] = i + random() - $(use_pydel ? :(@jl PythonCall.pydel!(i)) : :(nothing)) + $(use_pydel ? :(@jl PythonCall.unsafe_pydel(i)) : :(nothing)) end x end diff --git a/docs/src/pythoncall-reference.md b/docs/src/pythoncall-reference.md index 3dfc8ff3..6c13d3fb 100644 --- a/docs/src/pythoncall-reference.md +++ b/docs/src/pythoncall-reference.md @@ -250,7 +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.pydel! +PythonCall.unsafe_pydel ``` ## NumpyDates diff --git a/docs/src/v1-migration-guide.md b/docs/src/v1-migration-guide.md index cef13097..123e6f4c 100644 --- a/docs/src/v1-migration-guide.md +++ b/docs/src/v1-migration-guide.md @@ -24,7 +24,7 @@ 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`, +Most of 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: @@ -59,6 +59,11 @@ end * 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`). +`pydel!` has been renamed to `unsafe_pydel` to highlight that it is an unsafe function. +It is now exported. + +* Instead of `PythonCall.pydel!(x)` use `unsafe_pydel(x)`. + 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/exports.jl b/src/API/exports.jl index 0454e0e1..cdc5ac83 100644 --- a/src/API/exports.jl +++ b/src/API/exports.jl @@ -98,6 +98,7 @@ export pytuple export pytype export pywith export pyxor +export unsafe_pydel # Convert export @pyconvert diff --git a/src/API/functions.jl b/src/API/functions.jl index 74ccdcb0..77492f7a 100644 --- a/src/API/functions.jl +++ b/src/API/functions.jl @@ -22,7 +22,6 @@ function pycomplex end function pycontains end function pydate end function pydatetime end -function pydel! end function pydelattr end function pydelitem end function pydict end @@ -98,6 +97,7 @@ function pytuple end function pytype end function pywith end function pyxor end +function unsafe_pydel end # Convert function pyconvert end diff --git a/src/API/publics.jl b/src/API/publics.jl index a8c4cdd1..74db3538 100644 --- a/src/API/publics.jl +++ b/src/API/publics.jl @@ -13,7 +13,6 @@ if Base.VERSION ≥ v"1.11" # Core CONFIG, - pydel!, # Compat event_loop_off, diff --git a/src/Convert/pyconvert.jl b/src/Convert/pyconvert.jl index 2b03eb72..10435f11 100644 --- a/src/Convert/pyconvert.jl +++ b/src/Convert/pyconvert.jl @@ -347,7 +347,7 @@ function pytryconvert(::Type{T}, x_) where {T} rules = get!(trules, tptr) do t = pynew(incref(tptr)) ans = pyconvert_get_rules(T, t)::Vector{Function} - pydel!(t) + unsafe_pydel(t) ans end diff --git a/src/Convert/rules.jl b/src/Convert/rules.jl index ac104d5e..43e029b8 100644 --- a/src/Convert/rules.jl +++ b/src/Convert/rules.jl @@ -90,7 +90,7 @@ pyconvert_rule_int(::Type{T}, x::Py) where {T<:Number} = begin # try converting -> int -> str -> BigInt -> T x_int = pyint(x) x_str = pystr(String, x_int) - pydel!(x_int) + unsafe_pydel(x_int) v = parse(BigInt, x_str) return pyconvert_tryconvert(T, v) end @@ -203,7 +203,7 @@ function _pyconvert_rule_iterable(ans::Vector{T0}, it::Py, ::Type{T1}) where {T0 @label again x_ = unsafe_pynext(it) if pyisnull(x_) - pydel!(it) + unsafe_pydel(it) return pyconvert_return(ans) end x = @pyconvert(T1, x_) @@ -234,7 +234,7 @@ function _pyconvert_rule_iterable(ans::Set{T0}, it::Py, ::Type{T1}) where {T0,T1 @label again x_ = unsafe_pynext(it) if pyisnull(x_) - pydel!(it) + unsafe_pydel(it) return pyconvert_return(ans) end x = @pyconvert(T1, x_) @@ -271,7 +271,7 @@ function _pyconvert_rule_mapping( @label again k_ = unsafe_pynext(it) if pyisnull(k_) - pydel!(it) + unsafe_pydel(it) return pyconvert_return(ans) end v_ = pygetitem(x, k_) @@ -378,24 +378,24 @@ function pyconvert_rule_iterable( it = pyiter(x) k_ = unsafe_pynext(it) if pyisnull(k_) - pydel!(it) - pydel!(k_) + unsafe_pydel(it) + unsafe_pydel(k_) return pyconvert_unconverted() end k = @pyconvert(K1, k_) v_ = unsafe_pynext(it) if pyisnull(v_) - pydel!(it) - pydel!(v_) + unsafe_pydel(it) + unsafe_pydel(v_) return pyconvert_unconverted() end v = @pyconvert(V1, v_) z_ = unsafe_pynext(it) - pydel!(it) + unsafe_pydel(it) if pyisnull(z_) - pydel!(z_) + unsafe_pydel(z_) else - pydel!(z_) + unsafe_pydel(z_) return pyconvert_unconverted() end K2 = Utils._promote_type_bounded(K0, typeof(k), K1) @@ -421,7 +421,7 @@ function pyconvert_rule_iterable(::Type{R}, x::Py) where {R<:NamedTuple} pyistuple(x) || return pyconvert_unconverted() names2_ = pygetattr(x, "_fields", pybuiltins.None) names2 = @pyconvert(names === nothing ? Tuple{Vararg{Symbol}} : typeof(names), names2_) - pydel!(names2_) + unsafe_pydel(names2_) names === nothing || names === names2 || return pyconvert_unconverted() types2 = types === nothing ? NTuple{length(names2),Any} : types vals = @pyconvert(types2, x) @@ -461,7 +461,7 @@ function pyconvert_rule_datetime(::Type{DateTime}, x::Py) days = pyconvert(Int, d.days) seconds = pyconvert(Int, d.seconds) microseconds = pyconvert(Int, d.microseconds) - pydel!(d) + unsafe_pydel(d) iszero(mod(microseconds, 1000)) || return pyconvert_unconverted() return pyconvert_return( _base_datetime + diff --git a/src/Core/Core.jl b/src/Core/Core.jl index de144bf3..118bec2a 100644 --- a/src/Core/Core.jl +++ b/src/Core/Core.jl @@ -54,7 +54,7 @@ import ..PythonCall: pyconvert, pydate, pydatetime, - pydel!, + unsafe_pydel, pydelattr, pydelitem, pydict, @@ -153,7 +153,7 @@ export pycopy!, pydatetime_isaware, pydatetimetype, - pydel!, + unsafe_pydel, pydict_setitem, pyfloat_asdouble, pyisbytes, diff --git a/src/Core/Py.jl b/src/Core/Py.jl index 1b4f48fe..50ff0eb2 100644 --- a/src/Core/Py.jl +++ b/src/Core/Py.jl @@ -71,7 +71,7 @@ Assumes `dst` is NULL, otherwise a memory leak will occur. pycopy!(dst::Py, src) = Base.GC.@preserve src setptr!(dst, incref(getptr(src))) """ - pydel!(x::Py) + unsafe_pydel(x::Py) Delete the Python object `x`. @@ -84,7 +84,7 @@ This decrements the reference count and sets the pointer to NULL. Use this to eagerly free a Python object, rather than waiting for Julia's GC to finalize it at some indeterminate point in the future. """ -function pydel!(x::Py) +function unsafe_pydel(x::Py) ptr = getptr(x) if ptr != C.PyNULL C.Py_DecRef(ptr) @@ -101,7 +101,7 @@ macro autopy(args...) esc(quote # $([:($t = $ispy($v) ? $v : $Py($v)) for (t, v) in zip(ts, vs)]...) # $ans = $body - # $([:($ispy($v) || $pydel!($t)) for (t, v) in zip(ts, vs)]...) + # $([:($ispy($v) || $unsafe_pydel($t)) for (t, v) in zip(ts, vs)]...) # $ans $([:($t = $Py($v)) for (t, v) in zip(ts, vs)]...) $body @@ -341,7 +341,7 @@ Base.IteratorSize(::Type{Py}) = Base.SizeUnknown() function Base.iterate(x::Py, it::Py = pyiter(x)) v = unsafe_pynext(it) if pyisnull(v) - pydel!(it) + unsafe_pydel(it) nothing else (v, it) diff --git a/src/Core/builtins.jl b/src/Core/builtins.jl index 7e772662..837e2e57 100644 --- a/src/Core/builtins.jl +++ b/src/Core/builtins.jl @@ -15,7 +15,7 @@ pyisnot(x, y) = !pyis(x, y) Equivalent to `repr(x)` in Python. """ pyrepr(x) = pynew(errcheck(@autopy x C.PyObject_Repr(x_))) -pyrepr(::Type{String}, x) = (s = pyrepr(x); ans = pystr_asstring(s); pydel!(s); ans) +pyrepr(::Type{String}, x) = (s = pyrepr(x); ans = pystr_asstring(s); unsafe_pydel(s); ans) """ pyascii(x) @@ -23,7 +23,7 @@ pyrepr(::Type{String}, x) = (s = pyrepr(x); ans = pystr_asstring(s); pydel!(s); Equivalent to `ascii(x)` in Python. """ pyascii(x) = pynew(errcheck(@autopy x C.PyObject_ASCII(x_))) -pyascii(::Type{String}, x) = (s = pyascii(x); ans = pystr_asstring(s); pydel!(s); ans) +pyascii(::Type{String}, x) = (s = pyascii(x); ans = pystr_asstring(s); unsafe_pydel(s); ans) """ pyhasattr(x, k) @@ -205,13 +205,13 @@ pycall(f, args...; kwargs...) = args_ = pytuple_fromiter(args) kwargs_ = pystrdict_fromiter(kwargs) ans = pycallargs(f, args_, kwargs_) - pydel!(args_) - pydel!(kwargs_) + unsafe_pydel(args_) + unsafe_pydel(kwargs_) ans elseif !isempty(args) args_ = pytuple_fromiter(args) ans = pycallargs(f, args_) - pydel!(args_) + unsafe_pydel(args_) ans else pycallargs(f) @@ -565,13 +565,13 @@ pystr(x::SubString{String}) = pystr_fromUTF8(x) pystr(x::Char) = pystr(string(x)) pystr(x::AbstractString) = pystr(convert(String, x)::String) pystr(x::AbstractChar) = pystr(convert(Char, x)::Char) -pystr(::Type{String}, x) = (s = pystr(x); ans = pystr_asstring(s); pydel!(s); ans) +pystr(::Type{String}, x) = (s = pystr(x); ans = pystr_asstring(s); unsafe_pydel(s); ans) pystr_asUTF8bytes(x::Py) = pynew(errcheck(C.PyUnicode_AsUTF8String(x))) pystr_asUTF8vector(x::Py) = - (b = pystr_asUTF8bytes(x); ans = pybytes_asvector(b); pydel!(b); ans) + (b = pystr_asUTF8bytes(x); ans = pybytes_asvector(b); unsafe_pydel(b); ans) pystr_asstring(x::Py) = - (b = pystr_asUTF8bytes(x); ans = pybytes_asUTF8string(b); pydel!(b); ans) + (b = pystr_asUTF8bytes(x); ans = pybytes_asUTF8string(b); unsafe_pydel(b); ans) function pystr_intern!(x::Py) ptr = Ref(getptr(x)) @@ -596,9 +596,9 @@ pybytes(x::Vector{UInt8}) = pybytes_fromdata(x) pybytes(x::Base.CodeUnits{UInt8,String}) = pybytes_fromdata(x) pybytes(x::Base.CodeUnits{UInt8,SubString{String}}) = pybytes_fromdata(x) pybytes(::Type{T}, x) where {Vector{UInt8} <: T <: Vector} = - (b = pybytes(x); ans = pybytes_asvector(b); pydel!(b); ans) + (b = pybytes(x); ans = pybytes_asvector(b); unsafe_pydel(b); ans) pybytes(::Type{T}, x) where {Base.CodeUnits{UInt8,String} <: T <: Base.CodeUnits} = - (b = pybytes(x); ans = Base.CodeUnits(pybytes_asUTF8string(b)); pydel!(b); ans) + (b = pybytes(x); ans = Base.CodeUnits(pybytes_asUTF8string(b)); unsafe_pydel(b); ans) pyisbytes(x) = pytypecheckfast(x, C.Py_TPFLAGS_BYTES_SUBCLASS) @@ -827,7 +827,7 @@ function pytuple_fromiter(xs) # length unknown xs_ = pylist_fromiter(xs) ans = pylist_astuple(xs_) - pydel!(xs_) + unsafe_pydel(xs_) return ans end end @@ -912,7 +912,7 @@ function pycollist(x::AbstractArray{T,N}) where {T,N} for (i, j) in enumerate(ax) y = pycollist(selectdim(x, d, j)) pylist_setitem(ans, i - 1, y) - pydel!(y) + unsafe_pydel(y) end return ans end @@ -930,7 +930,7 @@ function pyrowlist(x::AbstractArray{T,N}) where {T,N} for (i, j) in enumerate(ax) y = pyrowlist(selectdim(x, d, j)) pylist_setitem(ans, i - 1, y) - pydel!(y) + unsafe_pydel(y) end return ans end @@ -1065,7 +1065,7 @@ function pydatetime(x::DateTime) # this accounts for fold d = pytimedeltatype(milliseconds = (x - _base_datetime).value) ans = _base_pydatetime + d - pydel!(d) + unsafe_pydel(d) return ans end pydatetime(x::Date) = pydatetime(year(x), month(x), day(x)) @@ -1073,30 +1073,30 @@ pydatetime(x::Date) = pydatetime(year(x), month(x), day(x)) function pytime_isaware(x) tzinfo = pygetattr(x, "tzinfo") if pyisnone(tzinfo) - pydel!(tzinfo) + unsafe_pydel(tzinfo) return false end utcoffset = tzinfo.utcoffset - pydel!(tzinfo) + unsafe_pydel(tzinfo) o = utcoffset(nothing) - pydel!(utcoffset) + unsafe_pydel(utcoffset) ans = !pyisnone(o) - pydel!(o) + unsafe_pydel(o) return ans end function pydatetime_isaware(x) tzinfo = pygetattr(x, "tzinfo") if pyisnone(tzinfo) - pydel!(tzinfo) + unsafe_pydel(tzinfo) return false end utcoffset = tzinfo.utcoffset - pydel!(tzinfo) + unsafe_pydel(tzinfo) o = utcoffset(x) - pydel!(utcoffset) + unsafe_pydel(utcoffset) ans = !pyisnone(o) - pydel!(o) + unsafe_pydel(o) return ans end @@ -1159,7 +1159,7 @@ pyeval(Float64, "x+y", Main, (x=1.1, y=2.2)) # returns 3.3 function pyeval(::Type{T}, code, globals, locals = nothing) where {T} code_, globals_, locals_ = _pyeval_args(code, globals, locals) ans = pybuiltins.eval(code_, globals_, locals_) - pydel!(locals_) + unsafe_pydel(locals_) return pyconvert(T, ans) end pyeval(code, globals, locals = nothing) = pyeval(Py, code, globals, locals) @@ -1224,9 +1224,9 @@ pyeval(Int, "x", Main) # returns 12 """ function pyexec(::Type{T}, code, globals, locals = nothing) where {T} code_, globals_, locals_ = _pyeval_args(code, globals, locals) - pydel!(pybuiltins.exec(code_, globals_, locals_)) + unsafe_pydel(pybuiltins.exec(code_, globals_, locals_)) ans = _pyexec_ans(T, globals_, locals_) - pydel!(locals_) + unsafe_pydel(locals_) return ans end pyexec(code, globals, locals = nothing) = pyexec(Nothing, code, globals, locals) @@ -1456,9 +1456,9 @@ Import a module `m`, or an attribute `k`, or a tuple of attributes. If several arguments are given, return the results of importing each one in a tuple. """ pyimport(m) = pynew(errcheck(@autopy m C.PyImport_Import(m_))) -pyimport((m, k)::Pair) = (m_ = pyimport(m); k_ = pygetattr(m_, k); pydel!(m_); k_) +pyimport((m, k)::Pair) = (m_ = pyimport(m); k_ = pygetattr(m_, k); unsafe_pydel(m_); k_) pyimport((m, ks)::Pair{<:Any,<:Tuple}) = - (m_ = pyimport(m); ks_ = map(k -> pygetattr(m_, k), ks); pydel!(m_); ks_) + (m_ = pyimport(m); ks_ = map(k -> pygetattr(m_, k), ks); unsafe_pydel(m_); ks_) pyimport(m1, m2, ms...) = map(pyimport, (m1, m2, ms...)) ### builtins not covered elsewhere @@ -1468,11 +1468,11 @@ pyimport(m1, m2, ms...) = map(pyimport, (m1, m2, ms...)) Equivalent to `print(...)` in Python. """ -pyprint(args...; kwargs...) = (pydel!(pybuiltins.print(args...; kwargs...)); nothing) +pyprint(args...; kwargs...) = (unsafe_pydel(pybuiltins.print(args...; kwargs...)); nothing) function _pyhelp(args...) pyisnone(pybuiltins.help) && error("Python help is not available") - pydel!(pybuiltins.help(args...)) + unsafe_pydel(pybuiltins.help(args...)) nothing end """ @@ -1491,7 +1491,7 @@ Equivalent to `all(x)` in Python. function pyall(x) y = pybuiltins.all(x) z = pybool_asbool(y) - pydel!(y) + unsafe_pydel(y) z end @@ -1503,7 +1503,7 @@ Equivalent to `any(x)` in Python. function pyany(x) y = pybuiltins.any(x) z = pybool_asbool(y) - pydel!(y) + unsafe_pydel(y) z end @@ -1515,7 +1515,7 @@ Equivalent to `callable(x)` in Python. function pycallable(x) y = pybuiltins.callable(x) z = pybool_asbool(y) - pydel!(y) + unsafe_pydel(y) z end diff --git a/src/JlWrap/any.jl b/src/JlWrap/any.jl index 1da5b483..a0df83e2 100644 --- a/src/JlWrap/any.jl +++ b/src/JlWrap/any.jl @@ -22,14 +22,14 @@ pyjl_attr_jl2py(k::String) = replace(k, r"!+$" => (x -> "_" * "b"^length(x))) function pyjlany_getattr(self, k_::Py) k = Symbol(pyjl_attr_py2jl(pyconvert(String, k_))) - pydel!(k_) + unsafe_pydel(k_) pyjl(getproperty(self, k)) end pyjl_handle_error_type(::typeof(pyjlany_getattr), self, exc) = pybuiltins.AttributeError function pyjlany_setattr(self, k_::Py, v_::Py) k = Symbol(pyjl_attr_py2jl(pyconvert(String, k_))) - pydel!(k_) + unsafe_pydel(k_) v = pyconvert(Any, v_) if self isa Module && !isdefined(self, k) # Fix for https://github.com/JuliaLang/julia/pull/54678 @@ -65,8 +65,8 @@ function pyjlany_call(self, args_::Py, kwargs_::Py) else ans = pyjl(self()) end - pydel!(args_) - pydel!(kwargs_) + unsafe_pydel(args_) + unsafe_pydel(kwargs_) ans end pyjl_handle_error_type(::typeof(pyjlany_call), self, exc) = @@ -83,8 +83,8 @@ function pyjlany_callback(self, args_::Py, kwargs_::Py) else ans = Py(self()) end - pydel!(args_) - pydel!(kwargs_) + unsafe_pydel(args_) + unsafe_pydel(kwargs_) ans end pyjl_handle_error_type(::typeof(pyjlany_callback), self, exc::MethodError) = @@ -101,8 +101,8 @@ function pyjlany_call_nogil(self, args_::Py, kwargs_::Py) else ans = pyjl(GIL.@unlock self()) end - pydel!(args_) - pydel!(kwargs_) + unsafe_pydel(args_) + unsafe_pydel(kwargs_) ans end pyjl_handle_error_type(::typeof(pyjlany_call_nogil), self, exc::MethodError) = @@ -112,7 +112,7 @@ function pyjlany_getitem(self, k_::Py) if self isa Type if pyistuple(k_) k = pyconvert(Vector{Any}, k_) - pydel!(k_) + unsafe_pydel(k_) pyjl(self{k...}) else k = pyconvert(Any, k_) @@ -121,7 +121,7 @@ function pyjlany_getitem(self, k_::Py) else if pyistuple(k_) k = pyconvert(Vector{Any}, k_) - pydel!(k_) + unsafe_pydel(k_) pyjl(self[k...]) else k = pyconvert(Any, k_) @@ -137,7 +137,7 @@ function pyjlany_setitem(self, k_::Py, v_::Py) v = pyconvert(Any, v_) if pyistuple(k_) k = pyconvert(Vector{Any}, k_) - pydel!(k_) + unsafe_pydel(k_) self[k...] = v else k = pyconvert(Any, k_) @@ -152,7 +152,7 @@ pyjl_handle_error_type(::typeof(pyjlany_setitem), self, exc) = function pyjlany_delitem(self, k_::Py) if pyistuple(k_) k = pyconvert(Vector{Any}, k_) - pydel!(k_) + unsafe_pydel(k_) delete!(self, k...) else k = pyconvert(Any, k_) @@ -176,7 +176,7 @@ end function (op::pyjlany_op)(self, other_::Py) if pyisjl(other_) other = pyjlvalue(other_) - pydel!(other_) + unsafe_pydel(other_) else other = pyconvert(Any, other_) end @@ -185,13 +185,13 @@ end function (op::pyjlany_op)(self, other_::Py, other2_::Py) if pyisjl(other_) other = pyjlvalue(other_) - pydel!(other_) + unsafe_pydel(other_) else other = pyconvert(Any, other) end if pyisjl(other2_) other2 = pyjlvalue(other2_) - pydel!(other2_) + unsafe_pydel(other2_) end pyjl(op.op(self, other, other2)) end @@ -204,7 +204,7 @@ end function (op::pyjlany_rev_op)(self, other_::Py) if pyisjl(other_) other = pyjlvalue(other_) - pydel!(other_) + unsafe_pydel(other_) else other = pyconvert(Any, other_) end @@ -213,13 +213,13 @@ end function (op::pyjlany_rev_op)(self, other_::Py, other2_::Py) if pyisjl(other_) other = pyjlvalue(other_) - pydel!(other_) + unsafe_pydel(other_) else other = pyconvert(Any, other) end if pyisjl(other2_) other2 = pyjlvalue(other2_) - pydel!(other2_) + unsafe_pydel(other2_) end pyjl(op.op(other, self, other2)) end @@ -270,7 +270,7 @@ function pyjlany_mimebundle(self, include::Py, exclude::Py) show(IOContext(io, :limit => true), MIME(m), self) v = take!(io) ans[m] = vo = istextmime(m) ? pystr(String(v)) : pybytes(v) - pydel!(vo) + unsafe_pydel(vo) catch err # silently skip anything that didn't work end @@ -333,7 +333,7 @@ pyjl_handle_error_type(::typeof(pyjlany_ceil), self, exc::MethodError) = pyjlany_round(self) = pyint(round(Integer, self)) function pyjlany_round(self, ndigits_::Py) ndigits = pyconvertarg(Int, ndigits_, "ndigits") - pydel!(ndigits_) + unsafe_pydel(ndigits_) pyjl(round(self; digits = ndigits)) end pyjl_handle_error_type(::typeof(pyjlany_round), self, exc::MethodError) = diff --git a/src/JlWrap/array.jl b/src/JlWrap/array.jl index 712e4b26..ebe4a272 100644 --- a/src/JlWrap/array.jl +++ b/src/JlWrap/array.jl @@ -68,7 +68,7 @@ function pyjl_getarrayindices(x::AbstractArray{T,N}, ks::Py) where {T,N} return ntuple(N) do i k = pytuple_getitem(ks, i - 1) ans = pyjl_getaxisindex(axes(x, i), k) - pydel!(k) + unsafe_pydel(k) return ans end else @@ -84,7 +84,7 @@ end function pyjlarray_getitem(x::AbstractArray{T,N}, k_::Py) where {T,N} k = pyjl_getarrayindices(x, k_) - pydel!(k_) + unsafe_pydel(k_) if k isa NTuple{N,Int} return Py(x[k...]) else @@ -94,7 +94,7 @@ end function pyjlarray_setitem(x::AbstractArray{T,N}, k_::Py, v_::Py) where {T,N} k = pyjl_getarrayindices(x, k_) - pydel!(k_) + unsafe_pydel(k_) if k isa NTuple{N,Int} v = pyconvertarg(T, v_, "value") x[k...] = v @@ -108,7 +108,7 @@ end function pyjlarray_delitem(x::AbstractArray{T,N}, k_::Py) where {T,N} if N == 1 k = pyjl_getarrayindices(x, k_) - pydel!(k_) + unsafe_pydel(k_) deleteat!(x, k...) else errset(pybuiltins.TypeError, "can only delete from 1D arrays") @@ -121,7 +121,7 @@ pyjl_handle_error_type(::typeof(pyjlarray_delitem), x, exc::MethodError) = function pyjlarray_reshape(x::AbstractArray, shape_::Py) shape = pyconvertarg(Union{Int,Vector{Int}}, shape_, "shape") - pydel!(shape_) + unsafe_pydel(shape_) return Py(reshape(x, shape...)) end diff --git a/src/JlWrap/io.jl b/src/JlWrap/io.jl index 3070e3fe..0f7c07d6 100644 --- a/src/JlWrap/io.jl +++ b/src/JlWrap/io.jl @@ -97,20 +97,20 @@ function pyjlbinaryio_readinto(io::IO, b::Py) m = pybuiltins.memoryview(b) c = m.c_contiguous if !pytruth(c) - pydel!(c) + unsafe_pydel(c) errset(pybuiltins.ValueError, "input buffer is not contiguous") return PyNULL end - pydel!(c) + unsafe_pydel(c) buf = unsafe_load(C.PyMemoryView_GET_BUFFER(m)) if buf.readonly != 0 - pydel!(m) + unsafe_pydel(m) errset(pybuiltins.ValueError, "output buffer is read-only") return PyNULL end data = unsafe_wrap(Array, Ptr{UInt8}(buf.buf), buf.len) nb = readbytes!(io, data) - pydel!(m) + unsafe_pydel(m) return Py(nb) end pyjl_handle_error_type(::typeof(pyjlbinaryio_readinto), io, exc) = @@ -120,15 +120,15 @@ function pyjlbinaryio_write(io::IO, b::Py) m = pybuiltins.memoryview(b) c = m.c_contiguous if !pytruth(c) - pydel!(c) + unsafe_pydel(c) errset(pybuiltins.ValueError, "input buffer is not contiguous") return PyNULL end - pydel!(c) + unsafe_pydel(c) buf = unsafe_load(C.PyMemoryView_GET_BUFFER(m)) data = unsafe_wrap(Array, Ptr{UInt8}(buf.buf), buf.len) write(io, data) - pydel!(m) + unsafe_pydel(m) return Py(buf.len) end pyjl_handle_error_type(::typeof(pyjlbinaryio_write), io, exc) = @@ -192,7 +192,7 @@ function pyjltextio_write(io::IO, s_::Py) # get the line separator linesep_ = pyosmodule.linesep linesep = pystr_asstring(linesep_) - pydel!(linesep_) + unsafe_pydel(linesep_) # write the string # translating '\n' to os.linesep i = firstindex(s) diff --git a/src/JlWrap/set.jl b/src/JlWrap/set.jl index 6ace3731..36f19f02 100644 --- a/src/JlWrap/set.jl +++ b/src/JlWrap/set.jl @@ -36,7 +36,7 @@ function pyjlset_update(x::AbstractSet, vs_::Py) for v_ in vs_ v = pyconvert(eltype(x), v_) push!(x, v) - pydel!(v_) + unsafe_pydel(v_) end Py(nothing) end @@ -45,7 +45,7 @@ function pyjlset_difference_update(x::AbstractSet, vs_::Py) for v_ in vs_ v = @pyconvert(eltype(x), v_, continue) delete!(x, v) - pydel!(v_) + unsafe_pydel(v_) end Py(nothing) end @@ -55,7 +55,7 @@ function pyjlset_intersection_update(x::AbstractSet, vs_::Py) for v_ in vs_ v = @pyconvert(eltype(x), v_, continue) push!(vs, v) - pydel!(v_) + unsafe_pydel(v_) end intersect!(x, vs) Py(nothing) @@ -66,7 +66,7 @@ function pyjlset_symmetric_difference_update(x::AbstractSet, vs_::Py) for v_ in vs_ v = pyconvert(eltype(x), v_) push!(vs, v) - pydel!(v_) + unsafe_pydel(v_) end symdiff!(x, vs) Py(nothing) diff --git a/src/JlWrap/vector.jl b/src/JlWrap/vector.jl index e4699d65..8058098c 100644 --- a/src/JlWrap/vector.jl +++ b/src/JlWrap/vector.jl @@ -2,18 +2,18 @@ const pyjlvectortype = pynew() function pyjlvector_resize(x::AbstractVector, size_::Py) size = pyconvertarg(Int, size_, "size") - pydel!(size_) + unsafe_pydel(size_) resize!(x, size) Py(nothing) end function pyjlvector_sort(x::AbstractVector, reverse_::Py, key_::Py) reverse = pyconvertarg(Bool, reverse_, "reverse") - pydel!(reverse_) + unsafe_pydel(reverse_) key = pyconvertarg(Any, key_, "size") if key === nothing sort!(x, rev = reverse) - pydel!(key_) + unsafe_pydel(key_) else sort!(x, rev = reverse, by = key) end @@ -31,7 +31,7 @@ end function pyjlvector_insert(x::AbstractVector, k_::Py, v_::Py) k = pyconvertarg(Int, k_, "index") - pydel!(k_) + unsafe_pydel(k_) a = axes(x, 1) k′ = k < 0 ? (last(a) + 1 + k) : (first(a) + k) if checkbounds(Bool, x, k′) || k′ == last(a) + 1 @@ -55,13 +55,13 @@ function pyjlvector_extend(x::AbstractVector, vs_::Py) v = pyconvert(eltype(x), v_) push!(x, v) end - pydel!(vs_) + unsafe_pydel(vs_) Py(nothing) end function pyjlvector_pop(x::AbstractVector, k_::Py) k = pyconvertarg(Int, k_, "index") - pydel!(k_) + unsafe_pydel(k_) a = axes(x, 1) k′ = k < 0 ? (last(a) + 1 + k) : (first(a) + k) if checkbounds(Bool, x, k′) diff --git a/src/PyMacro/PyMacro.jl b/src/PyMacro/PyMacro.jl index e20f98d9..d8dc45ab 100644 --- a/src/PyMacro/PyMacro.jl +++ b/src/PyMacro/PyMacro.jl @@ -151,7 +151,7 @@ py_macro_assign(body, ans, ex) = push!(body, :($ans = $ex)) py_macro_del(body, var, tmp) = if tmp - push!(body, :($pydel!($var))) + push!(body, :($unsafe_pydel($var))) end ismacroexpr(ex, name) = @@ -366,7 +366,7 @@ function py_macro_lower(st, body, ans, ex; flavour = :expr) end if af === :print # treat print as a special case since it is variadic - push!(body, :($pydel!($ans))) + push!(body, :($unsafe_pydel($ans))) py_macro_assign(body, ans, nothing) return false else @@ -649,7 +649,7 @@ function py_macro_lower(st, body, ans, ex; flavour = :expr) tx = py_macro_lower(st, body, ans, ax) body2 = [] body3 = [] - tx && push!(body2, :($pydel!($ans))) + tx && push!(body2, :($unsafe_pydel($ans))) ty = py_macro_lower(st, body2, ans, ay) t = tx || ty if t @@ -668,7 +668,7 @@ function py_macro_lower(st, body, ans, ex; flavour = :expr) tx = py_macro_lower(st, body, ans, ax) body2 = [] body3 = [] - tx && push!(body3, :($pydel!($ans))) + tx && push!(body3, :($unsafe_pydel($ans))) ty = py_macro_lower(st, body3, ans, ay) t = tx || ty if t @@ -704,7 +704,7 @@ function py_macro_lower(st, body, ans, ex; flavour = :expr) py_macro_del(body, y, ty) body2 = [] push!(body2, :($v = $unsafe_pynext($i))) - push!(body2, Expr(:if, :($pyisnull($v)), Expr(:block, :($pydel!($v)), :(break)))) + push!(body2, Expr(:if, :($pyisnull($v)), Expr(:block, :($unsafe_pydel($v)), :(break)))) py_macro_lower_assign(st, body2, ax, v) py_macro_del(body2, v, true) tz = py_macro_lower(st, body2, z, az) diff --git a/src/Wrap/PyArray.jl b/src/Wrap/PyArray.jl index 98cbaaee..4453a6b3 100644 --- a/src/Wrap/PyArray.jl +++ b/src/Wrap/PyArray.jl @@ -161,11 +161,11 @@ function PyArraySource_ArrayInterface(x::Py, d::Py = x.__array_interface__) if pyistuple(data) ptr = Ptr{Cvoid}(pyconvert(UInt, data[0])) readonly = pyconvert(Bool, data[1]) - pydel!(data) + unsafe_pydel(data) handle = Py((x, d)) else memview = @py memoryview(data === None ? x : data) - pydel!(data) + unsafe_pydel(data) buf = UnsafePtr(C.PyMemoryView_GET_BUFFER(memview)) ptr = buf.buf[!] readonly = buf.readonly[] != 0 @@ -323,7 +323,7 @@ function pyarray_get_R(src::PyArraySource_ArrayInterface) typestr = pyconvert(String, src.dict["typestr"]) descr = @py @jl(src.dict).get("descr") R = pyarray_typestrdescr_to_type(typestr, descr)::DataType - pydel!(descr) + unsafe_pydel(descr) return R end @@ -342,7 +342,7 @@ function pyarray_get_strides( ) where {R,N} @py strides = @jl(src.dict).get("strides") if pyisnone(strides) - pydel!(strides) + unsafe_pydel(strides) return Utils.size_to_cstrides(sizeof(R), size) else return pyconvert(NTuple{N,Int}, strides) diff --git a/src/Wrap/PyDict.jl b/src/Wrap/PyDict.jl index 7ea1ba1b..f2fc9781 100644 --- a/src/Wrap/PyDict.jl +++ b/src/Wrap/PyDict.jl @@ -52,7 +52,7 @@ function Base.delete!(x::PyDict{K,V}, k) where {K,V} end function Base.empty!(x::PyDict) - pydel!(@py x.clear()) + unsafe_pydel(@py x.clear()) return x end diff --git a/src/Wrap/PyIO.jl b/src/Wrap/PyIO.jl index ea3f7de1..9a653f31 100644 --- a/src/Wrap/PyIO.jl +++ b/src/Wrap/PyIO.jl @@ -27,7 +27,7 @@ function PyIO(f::Function, o; opts...) try return f(io) finally - pydel!(io.py) + unsafe_pydel(io.py) end end @@ -66,8 +66,8 @@ function putobuf(io::PyIO) else data = pystr_fromUTF8(view(io.obuf, 1:(n-nskip))) end - pydel!(@py io.write(data)) - pydel!(data) + unsafe_pydel(@py io.write(data)) + unsafe_pydel(data) if nskip == 0 empty!(io.obuf) else @@ -75,8 +75,8 @@ function putobuf(io::PyIO) end else data = pybytes(io.obuf) - pydel!(@py io.write(data)) - pydel!(data) + unsafe_pydel(@py io.write(data)) + unsafe_pydel(data) empty!(io.obuf) end end @@ -94,20 +94,20 @@ function getibuf(io::PyIO) else append!(io.ibuf, pybytes_asvector(data)) end - pydel!(data) + unsafe_pydel(data) end return end function Base.flush(io::PyIO) putobuf(io) - pydel!(@py io.flush()) + unsafe_pydel(@py io.flush()) return end function Base.close(io::PyIO) flush(io) - pydel!(@py io.close()) + unsafe_pydel(@py io.close()) return end @@ -195,13 +195,13 @@ function Base.seek(io::PyIO, pos::Integer) putobuf(io) empty!(io.ibuf) io.eof = false - pydel!(@py io.seek(pos)) + unsafe_pydel(@py io.seek(pos)) return io end function Base.truncate(io::PyIO, pos::Integer) seek(io, position(io)) - pydel!(@py io.truncate(pos)) + unsafe_pydel(@py io.truncate(pos)) return io end @@ -209,7 +209,7 @@ function Base.seekstart(io::PyIO) putobuf(io) empty!(io.ibuf) io.eof = false - pydel!(@py io.seek(0)) + unsafe_pydel(@py io.seek(0)) return io end @@ -217,7 +217,7 @@ function Base.seekend(io::PyIO) putobuf(io) empty!(io.ibuf) io.eof = false - pydel!(@py io.seek(0, 2)) + unsafe_pydel(@py io.seek(0, 2)) return io end @@ -233,7 +233,7 @@ function Base.skip(io::PyIO, n::Integer) if 0 ≤ n ≤ io.ibuflen read(io, n) else - pydel!(@py io.seek(@jl(n - length(io.ibuf)), 1)) + unsafe_pydel(@py io.seek(@jl(n - length(io.ibuf)), 1)) empty!(io.ibuf) io.eof = false end diff --git a/src/Wrap/PyIterable.jl b/src/Wrap/PyIterable.jl index e375b36c..a15fc903 100644 --- a/src/Wrap/PyIterable.jl +++ b/src/Wrap/PyIterable.jl @@ -10,7 +10,7 @@ Base.eltype(::Type{PyIterable{T}}) where {T} = T function Base.iterate(x::PyIterable{T}, it::Py = pyiter(x)) where {T} y = unsafe_pynext(it) if pyisnull(y) - pydel!(it) + unsafe_pydel(it) return nothing else return (pyconvert(T, y), it) diff --git a/src/Wrap/PyList.jl b/src/Wrap/PyList.jl index 59c7c6f1..e958698d 100644 --- a/src/Wrap/PyList.jl +++ b/src/Wrap/PyList.jl @@ -29,12 +29,12 @@ end Base.@propagate_inbounds function Base.insert!(x::PyList{T}, i::Integer, v) where {T} @boundscheck (i == length(x) + 1 || checkbounds(x, i)) - pydel!(@py x.insert(@jl(i - 1), @jl(convert(T, v)))) + unsafe_pydel(@py x.insert(@jl(i - 1), @jl(convert(T, v)))) return x end function Base.push!(x::PyList{T}, v) where {T} - pydel!(@py x.append(@jl(convert(T, v)))) + unsafe_pydel(@py x.append(@jl(convert(T, v)))) return x end @@ -77,12 +77,12 @@ Base.@propagate_inbounds function Base.popfirst!(x::PyList{T}) where {T} end function Base.reverse!(x::PyList) - pydel!(@py x.reverse()) + unsafe_pydel(@py x.reverse()) return x end function Base.empty!(x::PyList) - pydel!(@py x.clear()) + unsafe_pydel(@py x.clear()) return x end diff --git a/src/Wrap/PySet.jl b/src/Wrap/PySet.jl index 61073ff2..df117fae 100644 --- a/src/Wrap/PySet.jl +++ b/src/Wrap/PySet.jl @@ -19,7 +19,7 @@ Base.isempty(x::PySet) = length(x) == 0 function Base.iterate(x::PySet{T}, it::Py = pyiter(x)) where {T} y = unsafe_pynext(it) if pyisnull(y) - pydel!(it) + unsafe_pydel(it) return nothing else return (pyconvert(T, y), it) @@ -40,17 +40,17 @@ function Base.in(v, x::PySet{T}) where {T} end function Base.push!(x::PySet{T}, v) where {T} - pydel!(@py x.add(@jl(convert(T, v)))) + unsafe_pydel(@py x.add(@jl(convert(T, v)))) return x end function Base.delete!(x::PySet{T}, v) where {T} if v isa T - pydel!(@py x.discard(v)) + unsafe_pydel(@py x.discard(v)) else r = pyconvert_tryconvert(T, v) if !pyconvert_isunconverted(r) - pydel!(@py x.discard(@jl pyconvert_result(T, r))) + unsafe_pydel(@py x.discard(@jl pyconvert_result(T, r))) end end return x @@ -80,7 +80,7 @@ function Base.pop!(x::PySet, v, d) end function Base.empty!(x::PySet) - pydel!(@py x.clear()) + unsafe_pydel(@py x.clear()) return x end