Skip to content

Commit

Permalink
feat: Julia helper functions using PyCall (#42)
Browse files Browse the repository at this point in the history
* create julia_helpers.jl

* a helper to convert Julia Awkward array to Python

* type lock to a PrimitiveArray

* fix: use np.asarray

* convert only vectors of uint8

* use np.asarray instead

* check if PyCall is installed

* check PyCall installation

* check for PyCall in Main?

* move the import in

* Update and rename julia_helpers.jl to AwkwardPyCall.jl

* try reexport AwkwardArray

* add end of module

* feat: add python to julia functions

* fix: add JSON

* fix: check if PyCall is installed

* Update AwkwardPyCall.jl

* test: add tests

* test: add more tests

* fix: add unreleased AwkwardArray package for testing

* fix: add JSON for tests

* fix: reorder

* test: do not install python

* cleanup: remove python installation

* fix: format using JuliaFormatter and add coverage for tests
  • Loading branch information
ianna authored Nov 22, 2023
1 parent c46f83a commit bed29bc
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 30 deletions.
15 changes: 5 additions & 10 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,25 +50,20 @@ jobs:
- name: Build Julia package
uses: julia-actions/julia-buildpkg@v1

- name: Install PyCall dependencies
run: julia -e 'import Pkg; Pkg.add("PyCall"); Pkg.build("PyCall")'

- name: Run Julia tests
uses: julia-actions/julia-runtest@v1
with:
project: .

- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.12

- name: Install Python dependencies
run: |
python -m pip install -r requirements/pycall.txt
# - name: Run Python tests
# run: python test/test.py
- name: Install PyCall dependencies
run: julia -e 'import Pkg; Pkg.add("PyCall"); Pkg.build("PyCall")'

- name: Run PyCall tests
run: julia --code-coverage --project=tests test/runpytests.jl

- name: Generate and upload code coverage
uses: julia-actions/julia-processcoverage@v1
Expand Down
43 changes: 43 additions & 0 deletions src/pycall/AwkwardPyCall.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module AwkwardPyCall

using PyCall
using JSON
using AwkwardArray

const ak = pyimport("awkward")
const np = pyimport("numpy")

function _as_numpy(array::AbstractVector{UInt8})
py_array = PyObject(array)
np.asarray(py_array, dtype = np.uint8)
end

function julia_array_to_python(array)
form, len, containers = AwkwardArray.to_buffers(array)

py_buffers = Dict{String,Any}()

for (key, buffer) in containers
py_buffers[key] = _as_numpy(buffer)
end

return ak.from_buffers(form, len, py_buffers)
end

function _as_julia(py_buffer)
uint8_buffer = reinterpret(UInt8, py_buffer)
return uint8_buffer
end

function python_array_to_julia(py_array)
form, len, containers = ak.to_buffers(py_array)

julia_buffers = Dict{String,AbstractVector{UInt8}}()
for (key, buffer) in containers
julia_buffers[key] = _as_julia(buffer)
end

return AwkwardArray.from_buffers(form.to_json(), len, julia_buffers)
end

end # module
6 changes: 3 additions & 3 deletions src/tables.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ Tables.columnaccess(::Type{RecordArray}) = true
Tables.columns(x::RecordArray) = x.contents
Tables.columnnames(x::RecordArray) = keys(x.contents)

Tables.schema(x::RecordArray) = Tables.Schema(Tables.columnnames(x), eltype.(values(Tables.columns(x))))
Tables.schema(x::RecordArray) =
Tables.Schema(Tables.columnnames(x), eltype.(values(Tables.columns(x))))

function from_table(input)
sch = Tables.schema(input)
NT = NamedTuple{sch.names, Base.Tuple{sch.types...}}
NT = NamedTuple{sch.names,Base.Tuple{sch.types...}}
AwkwardType = layout_for(NT)
out = AwkwardType()

Expand All @@ -18,4 +19,3 @@ function from_table(input)

return out
end

76 changes: 76 additions & 0 deletions test/runpytests.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using PyCall
using Test

using Pkg

# FIXME: remove after AwkwardArray is released
function add_unreleased_AwkwardArray_package()
# Specify the Git URL of the AwkwardArray package
git_url = "https://github.com/JuliaHEP/AwkwardArray.jl"

# Use Pkg to add the package
Pkg.add(PackageSpec(url = git_url))
end

# Call the function to add the unreleased AwkwardArray package
add_unreleased_AwkwardArray_package()

using AwkwardArray

Pkg.add("JSON")
using JSON

include("../src/pycall/AwkwardPyCall.jl")

import Main.AwkwardPyCall: ak

import Main.AwkwardPyCall: julia_array_to_python

# Test julia_array_to_python function
@testset "julia_array_to_python tests" begin
array = AwkwardArray.ListOffsetArray(
[0, 3, 3, 5],
AwkwardArray.PrimitiveArray([1.1, 2.2, 3.3, 4.4, 5.5]),
)
# Test case 1: Check if the function returns an awkward array
@test isa(julia_array_to_python(array), PyObject)

# Test case 2: Check if the awkward array has the correct layout
py_array = julia_array_to_python(array)
@test typeof(py_array) == PyObject
@test ak.to_list(py_array) == [[1.1, 2.2, 3.3], [], [4.4, 5.5]]
end

import Main.AwkwardPyCall: python_array_to_julia

# Test python_array_to_julia function
@testset "python_array_to_julia tests" begin
py_array = ak.Array([[1.1, 2.2, 3.3], [], [4.4, 5.5]])

# Test case 1: Check if the function returns an awkward array
@test isa(
python_array_to_julia(py_array),
AwkwardArray.ListOffsetArray{
SubArray{Int64,1,Vector{Int64},Tuple{UnitRange{Int64}},true},
AwkwardArray.PrimitiveArray{
Float64,
SubArray{Float64,1,Vector{Float64},Tuple{UnitRange{Int64}},true},
:default,
},
:default,
},
)

# Test case 2: Check if the awkward array has the correct layout
array = python_array_to_julia(py_array)
@test typeof(array) == AwkwardArray.ListOffsetArray{
SubArray{Int64,1,Vector{Int64},Tuple{UnitRange{Int64}},true},
AwkwardArray.PrimitiveArray{
Float64,
SubArray{Float64,1,Vector{Float64},Tuple{UnitRange{Int64}},true},
:default,
},
:default,
}
@test array == [[1.1, 2.2, 3.3], [], [4.4, 5.5]]
end
34 changes: 17 additions & 17 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ using JSON
using Test
using Tables

### PrimitiveArray #######################################################
### PrimitiveArray #######################################################
@testset "PrimitiveArray" begin

begin
Expand Down Expand Up @@ -116,7 +116,7 @@ end
end
end

### ListOffsetArray ######################################################
### ListOffsetArray ######################################################
@testset "ListOffsetArray" begin

begin
Expand Down Expand Up @@ -233,7 +233,7 @@ end
end
end

### ListArray ######################################################
### ListArray ######################################################
@testset "ListArray" begin

begin
Expand Down Expand Up @@ -355,7 +355,7 @@ end
end
end

### RegularArray #########################################################
### RegularArray #########################################################

@testset "RegularArray" begin
begin
Expand Down Expand Up @@ -543,7 +543,7 @@ end
end
end

### ListType with behavior = :string #####################################
### ListType with behavior = :string #####################################

@testset "ListType with behavior = :string" begin
begin
Expand Down Expand Up @@ -900,7 +900,7 @@ end
end

end
### ListType with other parameters #######################################
### ListType with other parameters #######################################

@testset "ListType with other parameters" begin
begin
Expand Down Expand Up @@ -984,7 +984,7 @@ end
end
end

### RecordArray ##########################################################
### RecordArray ##########################################################

@testset "RecordArray" begin
begin
Expand Down Expand Up @@ -1282,7 +1282,7 @@ end
)
end
end
### TupleArray ##########################################################
### TupleArray ##########################################################

@testset "TupleArray" begin
begin
Expand Down Expand Up @@ -1540,7 +1540,7 @@ end
end
end

### IndexedArray #########################################################
### IndexedArray #########################################################

@testset "IndexedArray" begin
begin
Expand Down Expand Up @@ -1709,7 +1709,7 @@ end
end
end

### IndexedOptionArray ###################################################
### IndexedOptionArray ###################################################

@testset "IndexedOptionArray" begin
begin
Expand Down Expand Up @@ -1826,7 +1826,7 @@ end
end
end

### ByteMaskedArray ######################################################
### ByteMaskedArray ######################################################

@testset "ByteMaskedArray" begin
begin
Expand Down Expand Up @@ -1984,7 +1984,7 @@ end
end
end

### BitMaskedArray #######################################################
### BitMaskedArray #######################################################

@testset "BitMaskedArray" begin
begin
Expand Down Expand Up @@ -2141,7 +2141,7 @@ end
end
end

### UnmaskedArray ########################################################
### UnmaskedArray ########################################################

@testset "UnmaskedArray" begin
begin
Expand Down Expand Up @@ -2270,7 +2270,7 @@ end
)
end
end
### UnionArray ###########################################################
### UnionArray ###########################################################

@testset "UnionArray" begin
begin
Expand Down Expand Up @@ -2437,7 +2437,7 @@ end
)
end
end
### from_iter ############################################################
### from_iter ############################################################

@testset "from_iter" begin
begin
Expand Down Expand Up @@ -2517,7 +2517,7 @@ end
end
end

### from_buffers #########################################################
### from_buffers #########################################################

@testset "from_buffers" begin
begin
Expand Down Expand Up @@ -3159,7 +3159,7 @@ end
end

@testset "Tables.jl intergration" begin
df = (; x = [[1], [2], [1,2,3]], y = [4.0, 5, 6])
df = (; x = [[1], [2], [1, 2, 3]], y = [4.0, 5, 6])
awt = AwkwardArray.from_table(df)
@test Tables.schema(df) == Tables.schema(awt)

Expand Down

0 comments on commit bed29bc

Please sign in to comment.