Skip to content

Commit 042b176

Browse files
authored
Merge pull request #18 from JuliaAI/issue17
Fix issue #17 - add support for mlflow 1.22
2 parents cf69ecc + 7cb13cf commit 042b176

File tree

8 files changed

+289
-247
lines changed

8 files changed

+289
-247
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "MLFlowClient"
22
uuid = "64a0f543-368b-4a9a-827a-e71edb2a0b83"
33
authors = ["@deyandyankov and contributors"]
4-
version = "0.3.0"
4+
version = "0.3.1"
55

66
[deps]
77
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@
99
Julia client for [MLFlow](https://www.mlflow.org/)
1010

1111
This package is still under development and interfaces may change. See the documentation for current features and limitations.
12+
13+
Tested against `mlflow==1.21.0` and `mlflow==1.22.0`.

src/runs.jl

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ function createrun(mlf::MLFlow, experiment_id; start_time=missing, tags=missing)
1919
if ismissing(start_time)
2020
start_time = Int(trunc(datetime2unix(now()) * 1000))
2121
end
22-
result = mlfpost(mlf, endpoint; experiment_id=experiment_id, start_time=string(start_time), tags=tags)
22+
result = mlfpost(mlf, endpoint; experiment_id=experiment_id, start_time=start_time, tags=tags)
2323
MLFlowRun(result["run"]["info"], result["run"]["data"])
2424
end
2525
"""
@@ -356,7 +356,12 @@ function listartifacts(mlf::MLFlow, run_id::String; path::String="", maxdepth::I
356356
for resultentry httpresult["files"]
357357
if resultentry["is_dir"] == false
358358
filepath = joinpath(root_uri, resultentry["path"])
359-
filesize = parse(Int, resultentry["file_size"])
359+
file_size = resultentry["file_size"]
360+
if typeof(file_size) <: Int
361+
filesize = file_size
362+
else
363+
filesize = parse(Int, file_size)
364+
end
360365
push!(result, MLFlowArtifactFileInfo(filepath, filesize))
361366
elseif resultentry["is_dir"] == true
362367
dirpath = joinpath(root_uri, resultentry["path"])

src/types.jl

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,14 @@ function MLFlowRunInfo(info::Dict{String,Any})
123123

124124
experiment_id = ismissing(experiment_id) ? experiment_id : parse(Int64, experiment_id)
125125
status = ismissing(status) ? status : MLFlowRunStatus(status)
126-
start_time = ismissing(start_time) ? start_time : parse(Int64, start_time)
127-
end_time = ismissing(end_time) ? end_time : parse(Int64, end_time)
128126

127+
# support for mlflow 1.21.0
128+
if !ismissing(start_time) && !(typeof(start_time) <: Int)
129+
start_time = parse(Int64, start_time)
130+
end
131+
if !ismissing(end_time) && !(typeof(end_time) <: Int)
132+
end_time = parse(Int64, end_time)
133+
end
129134
MLFlowRunInfo(run_id, experiment_id, status, start_time, end_time, artifact_uri, lifecycle_stage)
130135
end
131136
Base.show(io::IO, t::MLFlowRunInfo) = show(io, ShowCase(t, new_lines=true))
@@ -156,8 +161,16 @@ end
156161
function MLFlowRunDataMetric(d::Dict{String,Any})
157162
key = d["key"]
158163
value = d["value"]
159-
step = parse(Int64, d["step"])
160-
timestamp = parse(Int64, d["timestamp"])
164+
if typeof(d["step"]) <: Int
165+
step = d["step"]
166+
else
167+
step = parse(Int64, d["step"])
168+
end
169+
if typeof(d["timestamp"]) <: Int
170+
timestamp = d["timestamp"]
171+
else
172+
timestamp = parse(Int64, d["timestamp"])
173+
end
161174
MLFlowRunDataMetric(key, value, step, timestamp)
162175
end
163176
Base.show(io::IO, t::MLFlowRunDataMetric) = show(io, ShowCase(t, new_lines=true))

test/runtests.jl

Lines changed: 2 additions & 240 deletions
Original file line numberDiff line numberDiff line change
@@ -1,240 +1,2 @@
1-
using MLFlowClient
2-
using Test
3-
using UUIDs
4-
using Dates
5-
6-
function mlflow_server_is_running(mlf::MLFlow)
7-
try
8-
response = MLFlowClient.mlfget(mlf, "experiments/list")
9-
return isa(response, Dict)
10-
catch e
11-
return false
12-
end
13-
end
14-
15-
# creates an instance of mlf
16-
# skips test if mlflow is not available on default location, http://localhost:5000
17-
macro ensuremlf()
18-
e = quote
19-
mlf = MLFlow()
20-
mlflow_server_is_running(mlf) || return nothing
21-
end
22-
eval(e)
23-
end
24-
25-
@testset "MLFlow" begin
26-
mlf = MLFlow()
27-
@test mlf.baseuri == "http://localhost:5000"
28-
@test mlf.apiversion == 2.0
29-
mlf = MLFlow("https://localhost:5001", apiversion=3.0)
30-
@test mlf.baseuri == "https://localhost:5001"
31-
@test mlf.apiversion == 3.0
32-
end
33-
34-
@testset "createexperiment" begin
35-
@ensuremlf
36-
exp = createexperiment(mlf)
37-
@test isa(exp, MLFlowExperiment)
38-
@test deleteexperiment(mlf, exp)
39-
experiment = getexperiment(mlf, exp.experiment_id)
40-
@test experiment.experiment_id == exp.experiment_id
41-
@test experiment.lifecycle_stage == "deleted"
42-
end
43-
44-
@testset "createrun" begin
45-
@ensuremlf
46-
expname = "getorcreate-$(UUIDs.uuid4())"
47-
e = getorcreateexperiment(mlf, expname)
48-
r = createrun(mlf, e.experiment_id)
49-
@test isa(r, MLFlowRun)
50-
@test deleterun(mlf, r)
51-
rr = createrun(mlf, e)
52-
@test isa(rr, MLFlowRun)
53-
@test deleterun(mlf, rr)
54-
@test deleteexperiment(mlf, e)
55-
end
56-
57-
@testset "getorcreateexperiment" begin
58-
@ensuremlf
59-
expname = "getorcreate"
60-
e = getorcreateexperiment(mlf, expname)
61-
@test isa(e, MLFlowExperiment)
62-
ee = getorcreateexperiment(mlf, expname)
63-
@test isa(ee, MLFlowExperiment)
64-
@test e === ee
65-
@test deleteexperiment(mlf, ee)
66-
@test deleteexperiment(mlf, ee)
67-
end
68-
69-
@testset "generatefilterfromparama" begin
70-
filter_params = Dict("k1" => "v1")
71-
filter = generatefilterfromparams(filter_params)
72-
@test filter == "param.\"k1\" = \"v1\""
73-
filter_params = Dict("k1" => "v1", "started" => Date("2020-01-01"))
74-
filter = generatefilterfromparams(filter_params)
75-
@test occursin("param.\"k1\" = \"v1\"", filter)
76-
@test occursin("param.\"started\" = \"2020-01-01\"", filter)
77-
@test occursin(" and ", filter)
78-
end
79-
80-
@testset "searchruns" begin
81-
@ensuremlf
82-
exp = createexperiment(mlf)
83-
expid = exp.experiment_id
84-
exprun = createrun(mlf, exp)
85-
@test exprun.info.experiment_id == expid
86-
@test exprun.info.lifecycle_stage == "active"
87-
@test exprun.info.status == MLFlowRunStatus("RUNNING")
88-
exprunid = exprun.info.run_id
89-
90-
runparams = Dict(
91-
"k1" => "v1",
92-
"started" => Date("2020-01-01")
93-
)
94-
logparam(mlf, exprun, runparams)
95-
96-
findrun = searchruns(mlf, exp; filter_params=runparams)
97-
@test length(findrun) == 1
98-
r = only(findrun)
99-
@test get_run_id(get_info(r)) == exprun.info.run_id
100-
@test get_run_id(r) == get_run_id(get_info(r))
101-
@test sort(collect(keys(get_params(get_data(r))))) == sort(string.(keys(runparams)))
102-
@test sort(collect(values(get_params(get_data(r))))) == sort(string.(values(runparams)))
103-
@test get_params(r) == get_params(get_data(r))
104-
@test deleteexperiment(mlf, exp)
105-
end
106-
107-
@testset "artifacts" begin
108-
@ensuremlf
109-
exp = createexperiment(mlf)
110-
@test isa(exp, MLFlowExperiment)
111-
exprun = createrun(mlf, exp)
112-
@test isa(exprun, MLFlowRun)
113-
# only run the below if artifact_uri is a local directory
114-
# i.e. when running mlflow server as a separate process next to the testset
115-
# when running mlflow in a container, the below tests will be skipped
116-
# this is what happens in github actions - mlflow runs in a container, the artifact_uri is not immediately available, and tests are skipped
117-
artifact_uri = exprun.info.artifact_uri
118-
if isdir(artifact_uri)
119-
@test_throws SystemError logartifact(mlf, exprun, "/etc/shadow")
120-
121-
tmpfiletoupload = "sometempfilename.txt"
122-
f = open(tmpfiletoupload, "w")
123-
write(f, "samplecontents")
124-
close(f)
125-
artifactpath = logartifact(mlf, exprun, tmpfiletoupload)
126-
@test isfile(artifactpath)
127-
rm(tmpfiletoupload)
128-
artifactpath = logartifact(mlf, exprun, "randbytes.bin", b"some rand bytes here")
129-
@test isfile(artifactpath)
130-
131-
mkdir(joinpath(artifact_uri, "newdir"))
132-
artifactpath = logartifact(mlf, exprun, joinpath("newdir", "randbytesindir.bin"), b"bytes here")
133-
artifactpath = logartifact(mlf, exprun, joinpath("newdir", "randbytesindir2.bin"), b"bytes here")
134-
mkdir(joinpath(artifact_uri, "newdir", "new2"))
135-
artifactpath = logartifact(mlf, exprun, joinpath("newdir", "new2", "randbytesindir.bin"), b"bytes here")
136-
artifactpath = logartifact(mlf, exprun, joinpath("newdir", "new2", "randbytesindir2.bin"), b"bytes here")
137-
mkdir(joinpath(artifact_uri, "newdir", "new2", "new3"))
138-
artifactpath = logartifact(mlf, exprun, joinpath("newdir", "new2", "new3", "randbytesindir.bin"), b"bytes here")
139-
artifactpath = logartifact(mlf, exprun, joinpath("newdir", "new2", "new3", "randbytesindir2.bin"), b"bytes here")
140-
mkdir(joinpath(artifact_uri, "newdir", "new2", "new3", "new4"))
141-
artifactpath = logartifact(mlf, exprun, joinpath("newdir", "new2", "new3", "new4", "randbytesindir.bin"), b"bytes here")
142-
artifactpath = logartifact(mlf, exprun, joinpath("newdir", "new2", "new3", "new4", "randbytesindir2.bin"), b"bytes here")
143-
144-
# artifact tree should now look like this:
145-
#
146-
# ├── newdir
147-
# │   ├── new2
148-
# │   │   ├── new3
149-
# │   │   │   ├── new4
150-
# │   │   │   │   ├── randbytesindir2.bin
151-
# │   │   │   │   └── randbytesindir.bin
152-
# │   │   │   ├── randbytesindir2.bin
153-
# │   │   │   └── randbytesindir.bin
154-
# │   │   ├── randbytesindir2.bin
155-
# │   │   └── randbytesindir.bin
156-
# │   ├── randbytesindir2.bin
157-
# │   └── randbytesindir.bin
158-
# ├── randbytes.bin
159-
# └── sometempfilename.txt
160-
161-
# 4 directories, 10 files
162-
163-
artifactlist = listartifacts(mlf, exprun)
164-
@test sort(basename.(get_path.(artifactlist))) == ["newdir", "randbytes.bin", "sometempfilename.txt"]
165-
@test sort(get_size.(artifactlist)) == [0, 14, 20]
166-
167-
ald2 = listartifacts(mlf, exprun, maxdepth=2)
168-
@test length(ald2) == 6
169-
@test sort(basename.(get_path.(ald2))) == ["new2", "newdir", "randbytes.bin", "randbytesindir.bin", "randbytesindir2.bin", "sometempfilename.txt"]
170-
aldrecursion = listartifacts(mlf, exprun, maxdepth=-1)
171-
@test length(aldrecursion) == 14 # 4 directories, 10 files
172-
@test sum(typeof.(aldrecursion) .== MLFlowArtifactDirInfo) == 4 # 4 directories
173-
@test sum(typeof.(aldrecursion) .== MLFlowArtifactFileInfo) == 10 # 10 files
174-
end
175-
deleterun(mlf, exprun)
176-
deleteexperiment(mlf, exp)
177-
end
178-
179-
@testset "MLFlowClient.jl" begin
180-
@ensuremlf
181-
exp = createexperiment(mlf)
182-
@test isa(exp, MLFlowExperiment)
183-
184-
exptags = [:key => "val"]
185-
expname = "expname-$(UUIDs.uuid4())"
186-
187-
@test ismissing(getexperiment(mlf, "$(UUIDs.uuid4()) - $(UUIDs.uuid4())"))
188-
189-
experiment = createexperiment(mlf; name=expname, tags=exptags)
190-
experiment_id = experiment.experiment_id
191-
experimentbyname = getexperiment(mlf, expname)
192-
@test experimentbyname.name == experiment.name
193-
194-
exprun = createrun(mlf, experiment_id)
195-
@test exprun.info.experiment_id == experiment_id
196-
@test exprun.info.lifecycle_stage == "active"
197-
@test exprun.info.status == MLFlowRunStatus("RUNNING")
198-
exprunid = exprun.info.run_id
199-
200-
logparam(mlf, exprunid, "paramkey", "paramval")
201-
logparam(mlf, exprunid, Dict("k" => "v", "k1" => "v1"))
202-
logparam(mlf, exprun, Dict("test1" => "test2"))
203-
204-
logmetric(mlf, exprun, "metrickeyrun", 1.0)
205-
logmetric(mlf, exprun.info, "metrickeyrun", 2.0)
206-
logmetric(mlf, exprun.info, "metrickeyrun", [2.5, 3.5])
207-
logmetric(mlf, exprunid, "metrickey", 1.0)
208-
logmetric(mlf, exprunid, "metrickey2", [1.0, 1.5, 2.0])
209-
210-
retrieved_run = getrun(mlf, exprunid)
211-
@test exprun.info == retrieved_run.info
212-
213-
running_run = updaterun(mlf, exprunid, "RUNNING")
214-
@test running_run.info.experiment_id == experiment_id
215-
@test running_run.info.status == MLFlowRunStatus("RUNNING")
216-
finished_run = updaterun(mlf, exprun, MLFlowRunStatus("FINISHED"))
217-
finishedrun = getrun(mlf, finished_run.info.run_id)
218-
219-
@test !ismissing(finishedrun.info.end_time)
220-
221-
exprun2 = createrun(mlf, experiment_id)
222-
exprun2id = exprun.info.run_id
223-
logparam(mlf, exprun2, "param2", "key2")
224-
logmetric(mlf, exprun2, "metric2", [1.0, 2.0])
225-
updaterun(mlf, exprun2, "FINISHED")
226-
227-
runs = searchruns(mlf, experiment_id)
228-
@test length(runs) == 2
229-
runs = searchruns(mlf, experiment_id; filter="param.param2 = \"key2\"")
230-
@test length(runs) == 1
231-
@test_throws ErrorException searchruns(mlf, experiment_id; run_view_type="ERR")
232-
runs = searchruns(mlf, experiment_id; filter="param.param2 = \"key3\"")
233-
@test length(runs) == 0
234-
runs = searchruns(mlf, experiment_id; max_results=1) # test paging functionality
235-
@test length(runs) == 2
236-
deleterun(mlf, exprunid)
237-
deleterun(mlf, exprun2)
238-
239-
deleteexperiment(mlf, exp)
240-
end
1+
include("test_functional.jl")
2+
include("test_issue17.jl")

test/test_base.jl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using MLFlowClient
2+
using Test
3+
using UUIDs
4+
using Dates
5+
6+
function mlflow_server_is_running(mlf::MLFlow)
7+
try
8+
response = MLFlowClient.mlfget(mlf, "experiments/list")
9+
return isa(response, Dict)
10+
catch e
11+
return false
12+
end
13+
end
14+
15+
# creates an instance of mlf
16+
# skips test if mlflow is not available on default location, http://localhost:5000
17+
macro ensuremlf()
18+
e = quote
19+
mlf = MLFlow()
20+
mlflow_server_is_running(mlf) || return nothing
21+
end
22+
eval(e)
23+
end

0 commit comments

Comments
 (0)