Skip to content

Commit f4ae4ca

Browse files
committed
Add Python integration
1 parent 902c584 commit f4ae4ca

File tree

7 files changed

+131
-0
lines changed

7 files changed

+131
-0
lines changed

Diff for: Project.toml

+3
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@ Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
3232
GraphViz = "f526b714-d49f-11e8-06ff-31ed36ee7ee0"
3333
JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
3434
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
35+
PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d"
3536

3637
[extensions]
3738
DistributionsExt = "Distributions"
3839
GraphVizExt = "GraphViz"
3940
GraphVizSimpleExt = "Colors"
4041
JSON3Ext = "JSON3"
4142
PlotsExt = ["DataFrames", "Plots"]
43+
PythonExt = "PythonCall"
4244

4345
[compat]
4446
Adapt = "4.0.4"
@@ -69,3 +71,4 @@ Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
6971
GraphViz = "f526b714-d49f-11e8-06ff-31ed36ee7ee0"
7072
JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
7173
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
74+
PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d"

Diff for: docs/make.jl

+3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ makedocs(;
3232
"Logging: Visualization" => "logging-visualization.md",
3333
"Logging: Advanced" => "logging-advanced.md",
3434
],
35+
"External Languages" => [
36+
"Python" => "external-languages/python.md",
37+
],
3538
"Checkpointing" => "checkpointing.md",
3639
"Benchmarking" => "benchmarking.md",
3740
"Dynamic Scheduler Control" => "dynamic.md",

Diff for: docs/src/external-languages/python.md

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Python Integration
2+
3+
If you're using Python as your main programming language, Dagger can be easily
4+
integrated into your workflow. Dagger has built-in support for Python, which
5+
you can easily access through the `pydaggerjl` library (accessible through
6+
`pip`). This library provides a Pythonic interface to Dagger, allowing you to
7+
spawn Dagger tasks that run Python functions on Python arguments.
8+
9+
Here's a simple example of interfacing between Python's `numpy` library and
10+
Dagger:
11+
12+
```python
13+
import numpy as np
14+
from pydaggerjl import daggerjl
15+
16+
# Create a Dagger DTask to sum the elements of an array
17+
task = daggerjl.spawn(np.sum, np.array([1, 2, 3]))
18+
19+
# Wait on task to finish
20+
# This is purely educational, as fetch will wait for the task to finish
21+
daggerjl.wait(task)
22+
23+
# Fetch the result
24+
result = daggerjl.fetch(task)
25+
26+
print(f"The sum is: {result}")
27+
28+
# Create two numpy arrays
29+
a = np.array([1, 2, 3])
30+
b = np.array([4, 5, 6])
31+
32+
# Element-wise sum of the two arrays
33+
task = daggerjl.spawn(np.add, a, b)
34+
35+
# Fetch the result
36+
result = daggerjl.fetch(task)
37+
38+
print(f"The element-wise sum is: {result}")
39+
40+
# Element-wise sum of last result with itself
41+
task2 = daggerjl.spawn(np.add, task, task)
42+
43+
# Fetch the result
44+
result2 = daggerjl.fetch(task2)
45+
46+
print(f"The element-wise sum of the last result with itself is: {result2}")
47+
```
48+
49+
Keep an eye on Dagger and pydaggerjl - new features are soon to come!

Diff for: ext/PythonExt.jl

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
module PythonExt
2+
3+
if isdefined(Base, :get_extension)
4+
using PythonCall
5+
else
6+
using ..PythonCall
7+
end
8+
9+
import Dagger
10+
import Dagger: Processor, OSProc, ThreadProc, Chunk
11+
import Distributed: myid
12+
13+
const CPUProc = Union{OSProc, ThreadProc}
14+
15+
struct PythonProcessor <: Processor
16+
owner::Int
17+
end
18+
19+
Dagger.root_worker_id(proc::PythonProcessor) = proc.owner
20+
Dagger.get_parent(proc::PythonProcessor) = OSProc(proc.owner)
21+
Dagger.default_enabled(::PythonProcessor) = true
22+
23+
Dagger.iscompatible_func(::ThreadProc, opts, ::Type{Py}) = false
24+
Dagger.iscompatible_func(::PythonProcessor, opts, ::Type{Py}) = true
25+
Dagger.iscompatible_arg(::PythonProcessor, opts, ::Type{Py}) = true
26+
Dagger.iscompatible_arg(::PythonProcessor, opts, ::Type{<:PyArray}) = true
27+
28+
Dagger.move(from_proc::CPUProc, to_proc::PythonProcessor, x::Chunk) =
29+
Dagger.move(from_proc, to_proc, Dagger.move(from_proc, Dagger.get_parent(to_proc), x))
30+
Dagger.move(::CPUProc, ::PythonProcessor, x) = Py(x)
31+
Dagger.move(::CPUProc, ::PythonProcessor, x::Py) = x
32+
Dagger.move(::CPUProc, ::PythonProcessor, x::PyArray) = x
33+
# FIXME: Conversion from Python to Julia
34+
35+
function Dagger.execute!(::PythonProcessor, f, args...; kwargs...)
36+
@assert f isa Py "Function must be a Python object"
37+
return f(args...; kwargs...)
38+
end
39+
40+
function __init__()
41+
Dagger.add_processor_callback!(:pythonproc) do
42+
return PythonProcessor(myid())
43+
end
44+
end
45+
46+
end # module PythonExt

Diff for: test/Project.toml

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[deps]
22
ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63"
33
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
4+
CondaPkg = "992eb4ea-22a4-4c89-a5bb-47a3300528ab"
45
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
56
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
67
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
@@ -12,6 +13,7 @@ MemPool = "f9f48841-c794-520a-933b-121f7ba6ed94"
1213
OnlineStats = "a15396b6-48d5-5d58-9928-6d29437db91e"
1314
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
1415
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
16+
PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d"
1517
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
1618
Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
1719
SharedArrays = "1a1011a3-84de-559e-8e89-a11a2f7dc383"

Diff for: test/extlang/python.jl

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using PythonCall
2+
using CondaPkg
3+
4+
CondaPkg.add("numpy")
5+
6+
np = pyimport("numpy")
7+
8+
@testset "spawn" begin
9+
a = np.array([1, 2, 3])
10+
11+
t = Dagger.@spawn np.sum(a)
12+
result = fetch(t)
13+
@test result isa Py
14+
@test pyconvert(Int, result) == 6
15+
16+
b = np.array([4, 5, 6])
17+
18+
t = Dagger.@spawn np.add(a, b)
19+
result = fetch(t)
20+
@test result isa Py
21+
@test pyconvert(Array, result) == [5, 7, 9]
22+
23+
t2 = Dagger.@spawn np.add(t, b)
24+
result = fetch(t2)
25+
@test result isa Py
26+
@test pyconvert(Array, result) == [9, 12, 15]
27+
end

Diff for: test/runtests.jl

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ tests = [
2323
("Caching", "cache.jl"),
2424
("Disk Caching", "diskcaching.jl"),
2525
("File IO", "file-io.jl"),
26+
("External Languages - Python", "extlang/python.jl"),
2627
#("Fault Tolerance", "fault-tolerance.jl"),
2728
]
2829
all_test_names = map(test -> replace(last(test), ".jl"=>""), tests)

0 commit comments

Comments
 (0)