Skip to content

Commit 649f332

Browse files
authored
Merge branch 'main' into fix-types
2 parents 1080a67 + a0d4ee8 commit 649f332

File tree

17 files changed

+157
-24
lines changed

17 files changed

+157
-24
lines changed

.github/workflows/benchmark_pr.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: Benchmark PR
2+
on:
3+
pull_request:
4+
5+
jobs:
6+
bench:
7+
runs-on: ubuntu-latest
8+
steps:
9+
- uses: MilesCranmer/AirspeedVelocity.jl@action-v1
10+
with:
11+
julia-version: "1"
12+
tune: "true"
13+
# Post to "summary" tab of workflow run:
14+
job-summary: "true"
15+
# Run benchmark using PR's version of the script:
16+
bench-on: ${{ github.event.pull_request.head.sha }}

.github/workflows/tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ jobs:
9393
uv run pytest -s --nbval --cov=pysrc ./pytest/
9494
env:
9595
PYTHON_JULIACALL_THREADS: '2'
96+
PYTHON_JULIACALL_HANDLE_SIGNALS: 'yes'
9697

9798
- name: Upload coverage to Codecov
9899
uses: codecov/codecov-action@v5

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "PythonCall"
22
uuid = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d"
33
authors = ["Christopher Doris <github.com/cjdoris>"]
4-
version = "0.9.25"
4+
version = "0.9.26"
55

66
[deps]
77
CondaPkg = "992eb4ea-22a4-4c89-a5bb-47a3300528ab"

benchmark/Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[deps]
2+
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"

benchmark/benchmarks.jl

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
using BenchmarkTools
2+
using PythonCall
3+
using PythonCall: pydel!, pyimport, pydict, pystr, pyrange
4+
5+
const SUITE = BenchmarkGroup()
6+
7+
function test_pydict_init()
8+
random = pyimport("random").random
9+
x = pydict()
10+
for i in pyrange(1000)
11+
x[pystr(i)] = i + random()
12+
end
13+
return x
14+
end
15+
16+
SUITE["basic"]["julia"]["pydict"]["init"] = @benchmarkable test_pydict_init()
17+
18+
function test_pydict_pydel()
19+
random = pyimport("random").random
20+
x = pydict()
21+
for i in pyrange(1000)
22+
k = pystr(i)
23+
r = random()
24+
v = i + r
25+
x[k] = v
26+
pydel!(k)
27+
pydel!(r)
28+
pydel!(v)
29+
pydel!(i)
30+
end
31+
return x
32+
end
33+
34+
SUITE["basic"]["julia"]["pydict"]["pydel"] = @benchmarkable test_pydict_pydel()
35+
36+
@generated function test_atpy(::Val{use_pydel}) where {use_pydel}
37+
quote
38+
@py begin
39+
import random: random
40+
x = {}
41+
for i in range(1000)
42+
x[str(i)] = i + random()
43+
$(use_pydel ? :(@jl PythonCall.pydel!(i)) : :(nothing))
44+
end
45+
x
46+
end
47+
end
48+
end
49+
50+
SUITE["basic"]["@py"]["pydict"]["init"] = @benchmarkable test_atpy(Val(false))
51+
SUITE["basic"]["@py"]["pydict"]["pydel"] = @benchmarkable test_atpy(Val(true))
52+
53+
54+
include("gcbench.jl")
55+
using .GCBench: append_lots
56+
57+
SUITE["gc"]["full"] = @benchmarkable(
58+
GC.gc(true),
59+
setup=(GC.gc(true); append_lots(size=159)),
60+
seconds=30,
61+
evals=1,
62+
)

benchmark/gcbench.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module GCBench
2+
3+
using PythonCall
4+
5+
function append_lots(; iters=100 * 1024, size=1596)
6+
v = pylist()
7+
for i = 1:iters
8+
v.append(pylist(rand(size)))
9+
end
10+
return v
11+
end
12+
13+
end

docs/src/faq.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,19 @@ To force PythonCall to use the same Python interpreter as PyCall, set the enviro
1010

1111
Alternatively, to force PyCall to use the same interpreter as PythonCall, set the environment variable `PYTHON` to [`PythonCall.python_executable_path()`](@ref) and then `Pkg.build("PyCall")`. You will need to do this each time you change project, because PythonCall by default uses a different Python for each project.
1212

13-
## Is PythonCall/JuliaCall thread safe?
13+
## [Is PythonCall/JuliaCall thread safe?](@id faq-multi-threading)
1414

1515
Yes, as of v0.9.22, provided you handle the GIL correctly. See the guides for
1616
[PythonCall](@ref jl-multi-threading) and [JuliaCall](@ref py-multi-threading).
1717

1818
Before, tricks such as disabling the garbage collector were required. See the
1919
[old docs](https://juliapy.github.io/PythonCall.jl/v0.9.21/faq/#Is-PythonCall/JuliaCall-thread-safe?).
2020

21+
When starting a Julia REPL with multiple threads, there must be exactly one interactive thread,
22+
to avoid triggering a segmentation fault on tab completion (issue [#586](https://github.com/JuliaPy/PythonCall.jl/issues/586)).
23+
Check this with `Threads.nthreads(:interactive)` or `versioninfo()`, set it with `JULIA_NUM_THREADS=X,1`,
24+
where `X` is the number of default threads, or use the Julia `--threads` CLI flag, see `julia --help`.
25+
2126
Related issues:
2227
[#201](https://github.com/JuliaPy/PythonCall.jl/issues/201),
2328
[#202](https://github.com/JuliaPy/PythonCall.jl/issues/202),

docs/src/pythoncall.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,8 @@ Both `@unlock` and `@lock` are important. If the GIL were not unlocked, then a d
392392
would occur when attempting to lock the already-locked GIL from the threads. If the GIL
393393
were not re-locked, then Python would crash when interacting with it.
394394

395+
With multiple Julia threads you need exactly one interactive thread, see the [FAQ](@ref faq-multi-threading).
396+
395397
You can also use [multi-threading from Python](@ref py-multi-threading).
396398

397399
### Caveat: Garbage collection

docs/src/releasenotes.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Release Notes
22

3+
## 0.9.26 (2025-07-15)
4+
* Added PySide6 support to the GUI compatibility layer.
5+
* Added FAQ on interactive threads.
6+
* Added CI benchmarking suite.
7+
* Bug fixes.
8+
39
## 0.9.25 (2025-05-13)
410
* Added `PYTHON_JULIACALL_HEAP_SIZE_HINT` option to configure initial Julia heap size.
511
* `Base.elsize` now defined for `PyArray`.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "juliacall"
7-
version = "0.9.25"
7+
version = "0.9.26"
88
description = "Julia and Python in seamless harmony"
99
readme = { file = "README.md", content-type = "text/markdown" }
1010
classifiers = [

0 commit comments

Comments
 (0)