Skip to content

Commit bed29bc

Browse files
authored
feat: Julia helper functions using PyCall (#42)
* 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
1 parent c46f83a commit bed29bc

File tree

5 files changed

+144
-30
lines changed

5 files changed

+144
-30
lines changed

.github/workflows/CI.yml

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,25 +50,20 @@ jobs:
5050
- name: Build Julia package
5151
uses: julia-actions/julia-buildpkg@v1
5252

53-
- name: Install PyCall dependencies
54-
run: julia -e 'import Pkg; Pkg.add("PyCall"); Pkg.build("PyCall")'
55-
5653
- name: Run Julia tests
5754
uses: julia-actions/julia-runtest@v1
5855
with:
5956
project: .
6057

61-
- name: Set up Python
62-
uses: actions/setup-python@v2
63-
with:
64-
python-version: 3.12
65-
6658
- name: Install Python dependencies
6759
run: |
6860
python -m pip install -r requirements/pycall.txt
6961
70-
# - name: Run Python tests
71-
# run: python test/test.py
62+
- name: Install PyCall dependencies
63+
run: julia -e 'import Pkg; Pkg.add("PyCall"); Pkg.build("PyCall")'
64+
65+
- name: Run PyCall tests
66+
run: julia --code-coverage --project=tests test/runpytests.jl
7267

7368
- name: Generate and upload code coverage
7469
uses: julia-actions/julia-processcoverage@v1

src/pycall/AwkwardPyCall.jl

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
module AwkwardPyCall
2+
3+
using PyCall
4+
using JSON
5+
using AwkwardArray
6+
7+
const ak = pyimport("awkward")
8+
const np = pyimport("numpy")
9+
10+
function _as_numpy(array::AbstractVector{UInt8})
11+
py_array = PyObject(array)
12+
np.asarray(py_array, dtype = np.uint8)
13+
end
14+
15+
function julia_array_to_python(array)
16+
form, len, containers = AwkwardArray.to_buffers(array)
17+
18+
py_buffers = Dict{String,Any}()
19+
20+
for (key, buffer) in containers
21+
py_buffers[key] = _as_numpy(buffer)
22+
end
23+
24+
return ak.from_buffers(form, len, py_buffers)
25+
end
26+
27+
function _as_julia(py_buffer)
28+
uint8_buffer = reinterpret(UInt8, py_buffer)
29+
return uint8_buffer
30+
end
31+
32+
function python_array_to_julia(py_array)
33+
form, len, containers = ak.to_buffers(py_array)
34+
35+
julia_buffers = Dict{String,AbstractVector{UInt8}}()
36+
for (key, buffer) in containers
37+
julia_buffers[key] = _as_julia(buffer)
38+
end
39+
40+
return AwkwardArray.from_buffers(form.to_json(), len, julia_buffers)
41+
end
42+
43+
end # module

src/tables.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ Tables.columnaccess(::Type{RecordArray}) = true
44
Tables.columns(x::RecordArray) = x.contents
55
Tables.columnnames(x::RecordArray) = keys(x.contents)
66

7-
Tables.schema(x::RecordArray) = Tables.Schema(Tables.columnnames(x), eltype.(values(Tables.columns(x))))
7+
Tables.schema(x::RecordArray) =
8+
Tables.Schema(Tables.columnnames(x), eltype.(values(Tables.columns(x))))
89

910
function from_table(input)
1011
sch = Tables.schema(input)
11-
NT = NamedTuple{sch.names, Base.Tuple{sch.types...}}
12+
NT = NamedTuple{sch.names,Base.Tuple{sch.types...}}
1213
AwkwardType = layout_for(NT)
1314
out = AwkwardType()
1415

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

1920
return out
2021
end
21-

test/runpytests.jl

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using PyCall
2+
using Test
3+
4+
using Pkg
5+
6+
# FIXME: remove after AwkwardArray is released
7+
function add_unreleased_AwkwardArray_package()
8+
# Specify the Git URL of the AwkwardArray package
9+
git_url = "https://github.com/JuliaHEP/AwkwardArray.jl"
10+
11+
# Use Pkg to add the package
12+
Pkg.add(PackageSpec(url = git_url))
13+
end
14+
15+
# Call the function to add the unreleased AwkwardArray package
16+
add_unreleased_AwkwardArray_package()
17+
18+
using AwkwardArray
19+
20+
Pkg.add("JSON")
21+
using JSON
22+
23+
include("../src/pycall/AwkwardPyCall.jl")
24+
25+
import Main.AwkwardPyCall: ak
26+
27+
import Main.AwkwardPyCall: julia_array_to_python
28+
29+
# Test julia_array_to_python function
30+
@testset "julia_array_to_python tests" begin
31+
array = AwkwardArray.ListOffsetArray(
32+
[0, 3, 3, 5],
33+
AwkwardArray.PrimitiveArray([1.1, 2.2, 3.3, 4.4, 5.5]),
34+
)
35+
# Test case 1: Check if the function returns an awkward array
36+
@test isa(julia_array_to_python(array), PyObject)
37+
38+
# Test case 2: Check if the awkward array has the correct layout
39+
py_array = julia_array_to_python(array)
40+
@test typeof(py_array) == PyObject
41+
@test ak.to_list(py_array) == [[1.1, 2.2, 3.3], [], [4.4, 5.5]]
42+
end
43+
44+
import Main.AwkwardPyCall: python_array_to_julia
45+
46+
# Test python_array_to_julia function
47+
@testset "python_array_to_julia tests" begin
48+
py_array = ak.Array([[1.1, 2.2, 3.3], [], [4.4, 5.5]])
49+
50+
# Test case 1: Check if the function returns an awkward array
51+
@test isa(
52+
python_array_to_julia(py_array),
53+
AwkwardArray.ListOffsetArray{
54+
SubArray{Int64,1,Vector{Int64},Tuple{UnitRange{Int64}},true},
55+
AwkwardArray.PrimitiveArray{
56+
Float64,
57+
SubArray{Float64,1,Vector{Float64},Tuple{UnitRange{Int64}},true},
58+
:default,
59+
},
60+
:default,
61+
},
62+
)
63+
64+
# Test case 2: Check if the awkward array has the correct layout
65+
array = python_array_to_julia(py_array)
66+
@test typeof(array) == AwkwardArray.ListOffsetArray{
67+
SubArray{Int64,1,Vector{Int64},Tuple{UnitRange{Int64}},true},
68+
AwkwardArray.PrimitiveArray{
69+
Float64,
70+
SubArray{Float64,1,Vector{Float64},Tuple{UnitRange{Int64}},true},
71+
:default,
72+
},
73+
:default,
74+
}
75+
@test array == [[1.1, 2.2, 3.3], [], [4.4, 5.5]]
76+
end

test/runtests.jl

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ using JSON
33
using Test
44
using Tables
55

6-
### PrimitiveArray #######################################################
6+
### PrimitiveArray #######################################################
77
@testset "PrimitiveArray" begin
88

99
begin
@@ -116,7 +116,7 @@ end
116116
end
117117
end
118118

119-
### ListOffsetArray ######################################################
119+
### ListOffsetArray ######################################################
120120
@testset "ListOffsetArray" begin
121121

122122
begin
@@ -233,7 +233,7 @@ end
233233
end
234234
end
235235

236-
### ListArray ######################################################
236+
### ListArray ######################################################
237237
@testset "ListArray" begin
238238

239239
begin
@@ -355,7 +355,7 @@ end
355355
end
356356
end
357357

358-
### RegularArray #########################################################
358+
### RegularArray #########################################################
359359

360360
@testset "RegularArray" begin
361361
begin
@@ -543,7 +543,7 @@ end
543543
end
544544
end
545545

546-
### ListType with behavior = :string #####################################
546+
### ListType with behavior = :string #####################################
547547

548548
@testset "ListType with behavior = :string" begin
549549
begin
@@ -900,7 +900,7 @@ end
900900
end
901901

902902
end
903-
### ListType with other parameters #######################################
903+
### ListType with other parameters #######################################
904904

905905
@testset "ListType with other parameters" begin
906906
begin
@@ -984,7 +984,7 @@ end
984984
end
985985
end
986986

987-
### RecordArray ##########################################################
987+
### RecordArray ##########################################################
988988

989989
@testset "RecordArray" begin
990990
begin
@@ -1282,7 +1282,7 @@ end
12821282
)
12831283
end
12841284
end
1285-
### TupleArray ##########################################################
1285+
### TupleArray ##########################################################
12861286

12871287
@testset "TupleArray" begin
12881288
begin
@@ -1540,7 +1540,7 @@ end
15401540
end
15411541
end
15421542

1543-
### IndexedArray #########################################################
1543+
### IndexedArray #########################################################
15441544

15451545
@testset "IndexedArray" begin
15461546
begin
@@ -1709,7 +1709,7 @@ end
17091709
end
17101710
end
17111711

1712-
### IndexedOptionArray ###################################################
1712+
### IndexedOptionArray ###################################################
17131713

17141714
@testset "IndexedOptionArray" begin
17151715
begin
@@ -1826,7 +1826,7 @@ end
18261826
end
18271827
end
18281828

1829-
### ByteMaskedArray ######################################################
1829+
### ByteMaskedArray ######################################################
18301830

18311831
@testset "ByteMaskedArray" begin
18321832
begin
@@ -1984,7 +1984,7 @@ end
19841984
end
19851985
end
19861986

1987-
### BitMaskedArray #######################################################
1987+
### BitMaskedArray #######################################################
19881988

19891989
@testset "BitMaskedArray" begin
19901990
begin
@@ -2141,7 +2141,7 @@ end
21412141
end
21422142
end
21432143

2144-
### UnmaskedArray ########################################################
2144+
### UnmaskedArray ########################################################
21452145

21462146
@testset "UnmaskedArray" begin
21472147
begin
@@ -2270,7 +2270,7 @@ end
22702270
)
22712271
end
22722272
end
2273-
### UnionArray ###########################################################
2273+
### UnionArray ###########################################################
22742274

22752275
@testset "UnionArray" begin
22762276
begin
@@ -2437,7 +2437,7 @@ end
24372437
)
24382438
end
24392439
end
2440-
### from_iter ############################################################
2440+
### from_iter ############################################################
24412441

24422442
@testset "from_iter" begin
24432443
begin
@@ -2517,7 +2517,7 @@ end
25172517
end
25182518
end
25192519

2520-
### from_buffers #########################################################
2520+
### from_buffers #########################################################
25212521

25222522
@testset "from_buffers" begin
25232523
begin
@@ -3159,7 +3159,7 @@ end
31593159
end
31603160

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

0 commit comments

Comments
 (0)