Skip to content

Commit c37f079

Browse files
committed
refactor: use AbstractDict for JSON 1.x support
1 parent d7e5cc3 commit c37f079

File tree

12 files changed

+87
-61
lines changed

12 files changed

+87
-61
lines changed

.github/workflows/CI.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,32 @@ jobs:
6868
with:
6969
token: ${{ secrets.CODECOV_TOKEN }}
7070

71+
test-json-021:
72+
name: Julia 1 - ubuntu-latest - JSON 0.21
73+
runs-on: ubuntu-latest
74+
steps:
75+
- uses: actions/checkout@v6
76+
- uses: julia-actions/setup-julia@v2
77+
with:
78+
version: "1"
79+
- uses: actions/cache@v4
80+
env:
81+
cache-name: cache-artifacts
82+
with:
83+
path: ~/.julia/artifacts
84+
key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }}
85+
restore-keys: |
86+
${{ runner.os }}-test-${{ env.cache-name }}-
87+
${{ runner.os }}-test-
88+
${{ runner.os }}-
89+
- name: Install dependencies with JSON 0.21
90+
shell: julia --color=yes --project {0}
91+
run: |
92+
import Pkg
93+
Pkg.add(name="JSON", version="0.21")
94+
Pkg.instantiate()
95+
- uses: julia-actions/julia-runtest@v1
96+
7197
aqua:
7298
name: Aqua
7399
runs-on: ubuntu-latest

src/applications.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ struct _RegistryInfo
1313
name::String
1414
uuid::UUIDs.UUID
1515

16-
function _RegistryInfo(json::Dict; var="_RegistryInfo")
16+
function _RegistryInfo(json::AbstractDict; var="_RegistryInfo")
1717
id = _json_get(json, "id", Integer; var)
1818
name = _json_get(json, "name", AbstractString; var)
1919
uuid = _json_get(json, "uuid", UUIDs.UUID; var, parse=true)
@@ -23,7 +23,7 @@ end
2323

2424
function _api_registries(auth::Authentication)::Vector{_RegistryInfo}
2525
r = _restcall(auth, :GET, "app", "packages", "registries")
26-
json, _ = _parse_response_json(r, Dict)
26+
json, _ = _parse_response_json(r, AbstractDict)
2727
_json_check_success(json; var="app/packages/registries")
2828
registries = _json_get(json, "registries", Vector; var="app/packages/registries")
2929
# Note: this broadcast can return Any[] if `registries` is empty, hence
@@ -78,7 +78,7 @@ struct DefaultApp <: AbstractJuliaHubApp
7878
_apptype::String
7979
_json::Dict{String, Any}
8080

81-
function DefaultApp(json::Dict, appargs::AbstractVector)
81+
function DefaultApp(json::AbstractDict, appargs::AbstractVector)
8282
apptype = _json_get(json, "appType", AbstractString; var="default app")
8383
name = _json_get(json, "name", AbstractString; var="default app")
8484
new(name, appargs, apptype, json)
@@ -124,7 +124,7 @@ struct PackageApp <: AbstractJuliaHubApp
124124
_registry::_RegistryInfo
125125
_json::Dict{String, Any}
126126

127-
function PackageApp(json::Dict, registries::Vector{_RegistryInfo})
127+
function PackageApp(json::AbstractDict, registries::Vector{_RegistryInfo})
128128
name = _json_get(json, "name", AbstractString; var="registered app")
129129
uuid = _json_get(json, "uuid", UUIDs.UUID; var="registered app", parse=true)
130130
registrymap = _json_get(json, "registrymap", Vector; var="registered app")
@@ -180,7 +180,7 @@ struct UserApp <: AbstractJuliaHubApp
180180
_repository::String
181181
_json::Dict{String, Any}
182182

183-
function UserApp(json::Dict)
183+
function UserApp(json::AbstractDict)
184184
name = _json_get(json, "name", AbstractString; var="user app")
185185
repository_url = _json_get(json, "repourl", AbstractString; var="user app")
186186
new(name, repository_url, json)
@@ -258,7 +258,7 @@ end
258258
function _api_apps_default(auth::Authentication)
259259
r = _restcall(auth, :GET, "app", "applications", "default")
260260
r.status == 200 || _throw_invalidresponse(r; msg="Unable to list default applications.")
261-
return _parse_response_json(r, Dict)
261+
return _parse_response_json(r, AbstractDict)
262262
end
263263

264264
function _apps_default(auth::Authentication)

src/batchimages.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,12 +192,12 @@ end
192192
function _api_product_image_groups(auth::Authentication)
193193
r = _restcall(auth, :GET, "juliaruncloud", "product_image_groups"; query=[("extended", "true")])
194194
r.status == 200 || _throw_invalidresponse(r)
195-
return _parse_response_json(r, Dict)
195+
return _parse_response_json(r, AbstractDict)
196196
end
197197

198198
function _product_image_groups(auth::Authentication)
199199
r_json, r_json_str = _api_product_image_groups(auth)
200-
image_groups = _get_json(r_json, "image_groups", Dict)
200+
image_groups = _get_json(r_json, "image_groups", AbstractDict)
201201
# Double check that the returned JSON is correct
202202
image_groups = map(collect(pairs(image_groups))) do (image_group, images)
203203
if !isa(images, Vector)
@@ -227,7 +227,7 @@ function _group_images(images; image_group::AbstractString)
227227
# which should be unique.
228228
grouped_images = Dict{String, _ImageKeys}()
229229
for image in images
230-
if !isa(image, Dict)
230+
if !isa(image, AbstractDict)
231231
msg = """
232232
Invalid JSON returned by the server: image value is not an object
233233
image_group = $(image_group)
@@ -279,7 +279,7 @@ function _group_images(images; image_group::AbstractString)
279279
sort(grouped_images; by=((display_name, keys)::Pair) -> (!keys.isdefault, display_name))
280280
end
281281

282-
function _parse_image_group_entry_type(image::Dict)
282+
function _parse_image_group_entry_type(image::AbstractDict)
283283
image_type = _get_json(image, "type", String)
284284
m = match(r"(base|option)-(cpu|gpu)", image_type)
285285
if isnothing(m)

src/datasets.jl

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ struct DatasetVersion
5858
timestamp::TimeZones.ZonedDateTime
5959
_blobstore_path::String
6060

61-
function DatasetVersion(json::Dict; owner::AbstractString, name::AbstractString)
61+
function DatasetVersion(json::AbstractDict; owner::AbstractString, name::AbstractString)
6262
msg = "Unable to parse dataset version info for ($owner, $name)"
6363
version = _get_json(json, "version", Int; msg)
6464
size = _get_json(json, "size", Int; msg)
@@ -158,9 +158,9 @@ Base.@kwdef struct Dataset
158158
_json::Dict
159159
end
160160

161-
function Dataset(d::Dict; expected_project::Union{UUID, Nothing}=nothing)
161+
function Dataset(d::AbstractDict; expected_project::Union{UUID, Nothing}=nothing)
162162
owner = _get_json(
163-
_get_json(d, "owner", Dict),
163+
_get_json(d, "owner", AbstractDict),
164164
"username", String,
165165
)
166166
name = _get_json(d, "name", AbstractString)
@@ -169,7 +169,7 @@ function Dataset(d::Dict; expected_project::Union{UUID, Nothing}=nothing)
169169
[DatasetVersion(json; owner, name) for json in versions_json];
170170
by=dsv -> dsv.id,
171171
)
172-
_storage = let storage_json = _get_json(d, "storage", Dict)
172+
_storage = let storage_json = _get_json(d, "storage", AbstractDict)
173173
_DatasetStorage(;
174174
credentials_url=_get_json(d, "credentials_url", AbstractString),
175175
region=_get_json(storage_json, "bucket_region", AbstractString),
@@ -178,7 +178,7 @@ function Dataset(d::Dict; expected_project::Union{UUID, Nothing}=nothing)
178178
)
179179
end
180180
project = if !isnothing(expected_project)
181-
project_json = _get_json(d, "project", Dict)
181+
project_json = _get_json(d, "project", AbstractDict)
182182
project_json_uuid = UUIDs.UUID(
183183
_get_json(project_json, "project_id", String)
184184
)
@@ -723,7 +723,7 @@ end
723723
function _check_dataset_upload_config(
724724
r::_RESTResponse, expected_dtype::AbstractString; newly_created_dataset::Bool
725725
)
726-
upload_config, _ = _parse_response_json(r, Dict)
726+
upload_config, _ = _parse_response_json(r, AbstractDict)
727727
# Verify that the dtype of the remote dataset is what we expect it to be.
728728
if upload_config["dataset_type"] != expected_dtype
729729
if newly_created_dataset
@@ -899,7 +899,7 @@ storage_class =
899899
)
900900
end
901901

902-
function _write_rclone_config(io::IO, upload_config::Dict)
902+
function _write_rclone_config(io::IO, upload_config::AbstractDict)
903903
region = upload_config["location"]["region"]
904904
access_key_id = upload_config["credentials"]["access_key_id"]
905905
secret_access_key = upload_config["credentials"]["secret_access_key"]
@@ -914,7 +914,7 @@ end
914914
function _get_dataset_credentials(auth::Authentication, dataset::Dataset)
915915
r = @_httpcatch HTTP.get(dataset._storage.credentials_url, _authheaders(auth))
916916
r.status == 200 || _throw_invalidresponse(r; msg="Unable get credentials for $(dataset)")
917-
credentials, _ = _parse_response_json(r, Dict)
917+
credentials, _ = _parse_response_json(r, AbstractDict)
918918
return credentials
919919
end
920920

@@ -1143,7 +1143,7 @@ end
11431143

11441144
# Low-level internal function that just takes a dict of params, without caring
11451145
# if they are valid or not, and returns the raw HTTP response.
1146-
function _update_dataset(auth::Authentication, dataset_name::AbstractString, params::Dict)
1146+
function _update_dataset(auth::Authentication, dataset_name::AbstractString, params::AbstractDict)
11471147
_restcall(auth, :PATCH, ("user", "datasets", dataset_name), JSON.json(params))
11481148
end
11491149

src/jobs/jobs.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ struct JobFile
5353
_upload_timestamp::String
5454

5555
function JobFile(jobname::AbstractString, jf::AbstractDict; var)
56-
filehash = let hash = _json_get(jf, "hash", Dict; var)
56+
filehash = let hash = _json_get(jf, "hash", AbstractDict; var)
5757
hash_algorithm = _json_get(hash, "algorithm", Union{String, Nothing}; var)
5858
hash_value = _json_get(hash, "value", Union{String, Nothing}; var)
5959
if isnothing(hash_algorithm) || isnothing(hash_value)
@@ -374,7 +374,7 @@ function job end
374374
function job(id::AbstractString; throw::Bool=true, auth::Authentication=__auth__())
375375
r = _restcall(auth, :GET, "api", "rest", "jobs", id; hasura=true)
376376
r.status == 200 || _throw_invalidresponse(r)
377-
job, json = _parse_response_json(r, Dict)
377+
job, json = _parse_response_json(r, AbstractDict)
378378
details = get(job, "details") do
379379
Base.throw(JuliaHubError("Invalid JSON returned by the server:\n$(json)"))
380380
end
@@ -559,7 +559,7 @@ kill_job(job::Job; auth::Authentication=__auth__()) = kill_job(job.id; auth)
559559
function kill_job(jobname::AbstractString; auth::Authentication=__auth__())
560560
r = _restcall(auth, :GET, "juliaruncloud", "kill_job"; query=(; jobname=string(jobname)))
561561
if r.status == 200
562-
response, json = _parse_response_json(r, Dict)
562+
response, json = _parse_response_json(r, AbstractDict)
563563
# response_json["status"] might not be a Bool
564564
if get(response, "status", false) != true
565565
throw(JuliaHubError("Unexpected JSON returned by the server\n$(json)"))
@@ -601,7 +601,7 @@ function extend_job(jobname::AbstractString, extension::Limit; auth::Authenticat
601601
)
602602
r = _restcall(auth, :POST, ("juliaruncloud", "extend_job_time_limit"), payload)
603603
if r.status == 200
604-
response, json = _parse_response_json(r, Dict)
604+
response, json = _parse_response_json(r, AbstractDict)
605605
success = get(response, "success", nothing)
606606
message = get(response, "message", "")
607607
if success === true

src/jobs/logging-kafka.jl

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
struct _KafkaLogging <: _JobLoggingAPIVersion end
22

3-
function JobLogMessage(::_KafkaLogging, json::Dict)
3+
function JobLogMessage(::_KafkaLogging, json::AbstractDict)
44
offset = _get_json(json, "offset", Int)
55
# The timestamps in Kafka logs are in milliseconds
6-
value = _get_json(json, "value", Dict)
6+
value = _get_json(json, "value", AbstractDict)
77
timestamp = _ms_utc2localtz(_get_json(value, "timestamp", Int))
8-
log = _get_json(value, "log", Dict)
8+
log = _get_json(value, "log", AbstractDict)
99
message = _get_json(log, "message", String)
10-
metadata = _get_json_or(log, "metadata", Dict, Dict{String, Any}())
11-
keywords = _get_json_or(log, "keywords", Dict, Dict{String, Any}())
10+
metadata::Dict = _get_json_or(log, "metadata", AbstractDict, Dict{String, Any}())
11+
keywords::Dict = _get_json_or(log, "keywords", AbstractDict, Dict{String, Any}())
1212
stream = _get_json_or(log, "stream", String, nothing)
1313
JobLogMessage(;
1414
_offset=offset, timestamp, message, _metadata=metadata, _keywords=keywords,
@@ -412,12 +412,12 @@ function _get_logs_kafka_parsed(
412412
@debug "_get_logs_kafka_parsed($jobname): start REST call" _taskstamp() offset consumer_id timeout
413413
r = _get_job_logs_kafka_restcall(auth, jobname; offset, consumer_id, timeout)
414414
r.status == 200 || _throw_invalidresponse(r)
415-
json, json_str = _parse_response_json(r, Dict)
415+
json, json_str = _parse_response_json(r, AbstractDict)
416416
consumer_id = _get_json(json, "consumer_id", Int)
417417
# If the Kafka endpoints want to return an empty list of log messages, it returns it
418418
# as an empty object (i.e. "logs": {}), rather than an empty array.
419-
logs = _get_json(json, "logs", Union{Dict, Vector})
420-
logmessages, jobdone = if isa(logs, Dict)
419+
logs = _get_json(json, "logs", Union{AbstractDict, Vector})
420+
logmessages, jobdone = if isa(logs, AbstractDict)
421421
if !isempty(logs)
422422
throw(JuliaHubError("Non-empty dictionary for logs\n$(json_str)"))
423423
end
@@ -434,7 +434,7 @@ function _get_logs_kafka_parsed(
434434
# also occur in a random place.. or there may be multiple meta messages.
435435
bottom_message = false
436436
for (i, log) in enumerate(logs)
437-
if !isa(log, Dict)
437+
if !isa(log, AbstractDict)
438438
@error "Invalid log message type $(typeof(log)) (at $i / $(length(logs)); omitting)" i log
439439
continue
440440
end
@@ -477,8 +477,8 @@ function _get_logs_kafka_parsed(
477477
return (; consumer_id, logs=logmessages, isdone=jobdone)
478478
end
479479

480-
function _kafka_is_last_message(json::Dict)
481-
value = _get_json_or(json, "value", Dict, Dict())
480+
function _kafka_is_last_message(json::AbstractDict)
481+
value::Dict = _get_json_or(json, "value", AbstractDict, Dict())
482482
return get(value, "meta", nothing) == "bottom"
483483
end
484484

src/jobs/logging-legacy.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
struct _LegacyLogging <: _JobLoggingAPIVersion end
22

3-
function JobLogMessage(::_LegacyLogging, json::Dict, offset::Integer)
3+
function JobLogMessage(::_LegacyLogging, json::AbstractDict, offset::Integer)
44
# The .message property _should_ always be present in the log messages,
55
# but there are a few versions out there where it's sometimes omitted due
66
# to a backend bug. So we default to an empty string in those cases.
77
message = _get_json_or(json, "message", String, "")
8-
keywords = _get_json_or(json, "keywords", Dict, Dict{String, Any}())
9-
metadata = _get_json_or(json, "metadata", Dict, Dict{String, Any}())
8+
keywords::Dict = _get_json_or(json, "keywords", AbstractDict, Dict{String, Any}())
9+
metadata::Dict = _get_json_or(json, "metadata", AbstractDict, Dict{String, Any}())
1010
timestamp = if haskey(json, "timestamp")
1111
# Apparently timestamps are sometimes strings, sometimes integers..
1212
timestamp = _get_json(json, "timestamp", Union{String, Integer})
@@ -273,7 +273,7 @@ end
273273
# The log messages (may) have special _meta messages at the start and at the end.
274274
# These have a `"_meta": true` field, and should have either `"end": "top"` (if first)
275275
# or `"end": "bottom"` (if last message).
276-
function _log_legacy_is_meta(log::Dict, s::AbstractString)
276+
function _log_legacy_is_meta(log::AbstractDict, s::AbstractString)
277277
haskey(log, "_meta") || return false
278278
if log["_meta"] !== true
279279
throw(JuliaHubError("""
@@ -529,7 +529,7 @@ function _job_logs_legacy_start_streaming!(auth::Authentication, buffer::_Legacy
529529
end
530530
# If the message wasn't empty, we assume that it is a valid JSON blob containing
531531
# a log message.
532-
msg, _ = _parse_response_json(msg, Dict)
532+
msg, _ = _parse_response_json(msg, AbstractDict)
533533
if _log_legacy_is_meta(msg, "top")
534534
@error "Unexpected `top` meta message streamed" msg
535535
return nothing

src/jobsubmission.jl

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ struct _JobSubmission1
4343
# User code arguments
4444
appType::Union{AbstractString, Nothing}=nothing,
4545
appArgs::Union{Dict, Nothing}=nothing,
46-
args::Dict,
46+
args::AbstractDict,
4747
projectid::Union{AbstractString, Nothing},
4848
customcode::Bool, usercode::Union{AbstractString, Nothing}=nothing,
4949
projecttoml::Union{AbstractString, Nothing}=nothing,
@@ -158,7 +158,7 @@ end
158158

159159
_string_or_nothing(x) = isnothing(x) ? nothing : string(x)
160160

161-
function _check_key(d::Dict, key, ::Type{T}; varname) where {T}
161+
function _check_key(d::AbstractDict, key, ::Type{T}; varname) where {T}
162162
haskey(d, key) || throw(ArgumentError("Dictionary `$varname` is missing key `$key`."))
163163
isa(d[key], T) ||
164164
throw(ArgumentError("`$varname[\"$key\"]` is not `<: $T` (got `$(typeof(d[key]))`)."))
@@ -185,7 +185,7 @@ function _submit_job(auth::Authentication, j::_JobSubmission1)
185185
"""
186186
r = _restcall(auth, :POST, ("juliaruncloud", "submit_job"), HTTP.Form(params))
187187
if r.status == 200
188-
r_json, _ = _parse_response_json(r, Dict)
188+
r_json, _ = _parse_response_json(r, AbstractDict)
189189
haskey(r_json, "success") && r_json["success"] || throw(JuliaHubError(
190190
"""
191191
Invalid response JSON from JuliaHub:
@@ -868,10 +868,10 @@ function _get_appbundle_upload_url(auth::Authentication, appbundle_tar_path::Abs
868868
)
869869
r = _restcall(auth, :GET, "jobs", "appbundle_upload_url"; query=appbundle_params)
870870
r.status == 200 || _throw_invalidresponse(r; msg="Unable to upload appbundle to JuliaHub.")
871-
r_json, _ = _parse_response_json(r, Dict)
871+
r_json, _ = _parse_response_json(r, AbstractDict)
872872
_get_json(r_json, "success", Bool) ||
873873
_throw_invalidresponse(r; msg="Unable to upload appbundle to JuliaHub.")
874-
message = _get_json(r_json, "message", Dict)
874+
message = _get_json(r_json, "message", AbstractDict)
875875
upload_url = _get_json(message, "upload_url", AbstractString)
876876
return upload_url, appbundle_params
877877
end
@@ -914,9 +914,9 @@ struct PackageJob <: AbstractJobConfig
914914
args::Dict
915915
sysimage::Bool
916916

917-
PackageJob(app::PackageApp; args::Dict=Dict(), sysimage::Bool=_DEFAULT_BatchJob_sysimage) =
917+
PackageJob(app::PackageApp; args::AbstractDict=Dict(), sysimage::Bool=_DEFAULT_BatchJob_sysimage) =
918918
new(app, app.name, app._registry.name, string(app._uuid), args, sysimage)
919-
PackageJob(app::UserApp; args::Dict=Dict(), sysimage::Bool=_DEFAULT_BatchJob_sysimage) =
919+
PackageJob(app::UserApp; args::AbstractDict=Dict(), sysimage::Bool=_DEFAULT_BatchJob_sysimage) =
920920
new(app, app.name, nothing, app._repository, args, sysimage)
921921
end
922922

@@ -926,7 +926,7 @@ function _check_packagebundler_dir(bundlepath::AbstractString)
926926
return nothing
927927
end
928928

929-
function _check_job_args(args::Dict)
929+
function _check_job_args(args::AbstractDict)
930930
for k in keys(args)
931931
isa(k, AbstractString) ||
932932
throw(

0 commit comments

Comments
 (0)