Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions BENCHMARKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |

Expand Down Expand Up @@ -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

Expand All @@ -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
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
12 changes: 6 additions & 6 deletions benchmark/benchmarks.jl
Original file line number Diff line number Diff line change
@@ -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()

Expand All @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/src/pythoncall-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 6 additions & 1 deletion docs/src/v1-migration-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions src/API/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export pytuple
export pytype
export pywith
export pyxor
export unsafe_pydel

# Convert
export @pyconvert
Expand Down
2 changes: 1 addition & 1 deletion src/API/functions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -98,6 +97,7 @@ function pytuple end
function pytype end
function pywith end
function pyxor end
function unsafe_pydel end

# Convert
function pyconvert end
Expand Down
1 change: 0 additions & 1 deletion src/API/publics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ if Base.VERSION ≥ v"1.11"

# Core
CONFIG,
pydel!,

# Compat
event_loop_off,
Expand Down
2 changes: 1 addition & 1 deletion src/Convert/pyconvert.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
26 changes: 13 additions & 13 deletions src/Convert/rules.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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_)
Expand Down Expand Up @@ -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_)
Expand Down Expand Up @@ -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_)
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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 +
Expand Down
4 changes: 2 additions & 2 deletions src/Core/Core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ import ..PythonCall:
pyconvert,
pydate,
pydatetime,
pydel!,
unsafe_pydel,
pydelattr,
pydelitem,
pydict,
Expand Down Expand Up @@ -153,7 +153,7 @@ export
pycopy!,
pydatetime_isaware,
pydatetimetype,
pydel!,
unsafe_pydel,
pydict_setitem,
pyfloat_asdouble,
pyisbytes,
Expand Down
8 changes: 4 additions & 4 deletions src/Core/Py.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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`.

Expand All @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down
Loading
Loading