These notes came from the JuliaCon Global 2025 hackathon in Pittsburgh.
https://julialang.slack.com/archives/C097GBGDGCT
Bjorn Paulson started working on this and I eventually joined.
julia> python_command = "import appose.python_worker; appose.python_worker.main()"
"import appose.python_worker; appose.python_worker.main()"
julia> begin
to_python = Pipe()
from_python = Pipe()
err_python = Pipe()
pl = pipeline(`pixi run python -c $python_command`, stdin=to_python, stdout=from_python, stderr=from_python)
p = run(pl, wait=false)
end
Process(`pixi run python -c 'import appose.python_worker; appose.python_worker.main()'`, ProcessRunning)
julia> begin
python_command = "import appose.python_worker; appose.python_worker.main()"
to_python = Pipe()
from_python = Pipe()
err_python = Pipe()
pl = pipeline(`pixi run python -c $python_command`, stdin=to_python, stdout=from_python, stderr=from_python)
p = run(pl, wait=false)
@async while true
readline(from_python) |> println
end
end
julia> begin
using JSON3, UUIDs
python_command = "import appose.python_worker; appose.python_worker.main()"
to_python = Pipe()
from_python = Pipe()
err_python = Pipe()
pl = pipeline(`pixi run python -c $python_command`, stdin=to_python, stdout=from_python, stderr=from_python)
p = run(pl, wait=false)
ch = Channel()
Threads.@spawn while true
#readline(from_python) |> println
push!(ch, JSON3.read(readline(from_python)))
end
end
julia> function appose_execute(script, inputs=Dict{String, Any}())
d = Dict{String, Any}(
"task" => string(uuid4()),
"requestType" => "EXECUTE",
"script" => script,
"inputs" => inputs
)
JSON3.write(d) * "\n"
end
appose_execute (generic function with 2 methods)
julia> python_command = """
import appose;
shm = appose.SharedMemory(create = True, rsize=2*2*20*25)
data = appose.NDArray("uint16", [2,20,25], shm)
data.ndarray()[0,0,0] = 567
task.outputs["result"] = data
"""
"import appose;\nshm = appose.SharedMemory(create = True, rsize=2*2*20*25)\ndata = appose.NDArray(\"uint16\", [2,20,25], shm)\ndata.ndarray()[0,0,0] = 567\ntask.outputs[\"result\"] = data\n"
julia> write(to_python, appose_execute(python_command))
284
julia> result = take!(ch)
JSON3.Object{Base.CodeUnits{UInt8, String}, Vector{UInt64}} with 2 entries:
:task => "a21fcae5-fb03-45f6-acb9-76a8fcf4d60a"
:responseType => "LAUNCH"
julia> result = take!(ch)
JSON3.Object{Base.CodeUnits{UInt8, String}, Vector{UInt64}} with 3 entries:
:outputs => {…
:task => "a21fcae5-fb03-45f6-acb9-76a8fcf4d60a"
:responseType => "COMPLETION"
julia> using SharedArrays
julia> arr = SharedArray{Int16}("/dev/shm/" * result.outputs.result.shm.name, (25, 20, 2));
julia> arr[1]
567
julia> using JSON3, UUIDs
julia> function process_appose_execute(d::Dict{String})
expr = Meta.parse(d["script"])
inputs = d["input"]
mod = Module()
Core.eval(mod, quote
task = (; outputs = Dict{String, Any}(), inputs = $inputs)
end)
for (k, v) in inputs
Core.eval(mod, Expr(:(=), [Symbol(k), Meta.parse(v)]))
end
Core.eval(mod, Meta.parse(d["script"]))
outputs = mod.task.outputs
return Dict{String, Any}(
"task" => d["task"],
"responseType" => "COMPLETION",
"outputs" => outputs
)
end
process_appose_execute (generic function with 2 methods)
julia> process_appose_execute(d::Dict{String}, ::Type{String}) = JSON3.write(process_appose_execute(d))
process_appose_execute (generic function with 2 methods)
julia> process_appose_execute(Dict("task" => string(uuid4()), "script" => "task.outputs[\"result\"] = 5 + 5", "input" => Dict{String,Any}()))
Dict{String, Any} with 3 entries:
"task" => "5de55234-33b8-49fa-af88-7fbd9f422a3e"
"responseType" => "COMPLETION"
"outputs" => Dict{String, Any}("result"=>10)
julia> process_appose_execute(Dict("task" => string(uuid4()), "script" => "task.outputs[\"result\"] = 5 + 5", "input" => Dict{String,Any}()), String)
"{\"task\":\"68490a94-afaf-4349-b8b8-4610a2e3a2ba\",\"responseType\":\"COMPLETION\",\"outputs\":{\"result\":10}}"
These notes came from the JuliaCon Global 2025 hackathon in Pittsburgh.
https://julialang.slack.com/archives/C097GBGDGCT
Bjorn Paulson started working on this and I eventually joined.