Skip to content

Commit ca91305

Browse files
authored
Update metadata API (#53)
1 parent 32ef840 commit ca91305

File tree

3 files changed

+112
-79
lines changed

3 files changed

+112
-79
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "DataAPI"
22
uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
33
authors = ["quinnj <[email protected]>"]
4-
version = "1.11.0"
4+
version = "1.12.0"
55

66
[compat]
77
julia = "1"

src/DataAPI.jl

Lines changed: 67 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -298,72 +298,97 @@ performed). All types supporting metadata allow at least this style.
298298
const COL_INFO = """
299299
`col` must have a type that is supported by table `x` for column indexing.
300300
Following the Tables.jl contract `Symbol` and `Int` are always allowed.
301-
Passing `col` that is not a column of `x` throws an error.
301+
Throw an error if `col` is not a column of `x`.
302302
"""
303303

304304
"""
305-
metadata(x, key::AbstractString; style::Bool=false)
305+
metadatasupport(T::Type)
306306
307-
Return metadata value associated with object `x` for key `key`.
308-
If `x` does not support metadata throw `ArgumentError`.
309-
If `x` supports metadata, but does not have a mapping for `key` throw
310-
`KeyError`.
307+
Return a `NamedTuple{(:read, :write), Tuple{Bool, Bool}}` indicating whether
308+
values of type `T` support metadata.
309+
310+
The `read` field indicates whether reading metadata with the [`metadata`](@ref)
311+
and [`metadatakeys`]](@ref) functions is supported.
312+
313+
The `write` field indicates whether modifying metadata with the [`metadata!`](@ref),
314+
[`deletemetadata!`](@ref), and [`emptymetadata!`](@ref) functions is supported.
315+
"""
316+
metadatasupport(::Type) = (read=false, write=false)
317+
318+
"""
319+
colmetadatasupport(T::Type)
320+
321+
Return a `NamedTuple{(:read, :write), Tuple{Bool, Bool}}` indicating whether
322+
values of type `T` support column metadata.
323+
324+
The `read` field indicates whether reading metadata with the [`colmetadata`](@ref)
325+
and [`colmetadatakeys`](@ref) functions is supported.
326+
327+
The `write` field indicates whether modifying metadata with the [`colmetadata!`](@ref),
328+
[`deletecolmetadata!`](@ref), and [`emptycolmetadata!`](@ref) functions is supported.
329+
"""
330+
colmetadatasupport(::Type) = (read=false, write=false)
331+
332+
"""
333+
metadata(x, key::AbstractString, [default]; style::Bool=false)
334+
335+
Return metadata value associated with object `x` for key `key`. Throw an error
336+
if `x` does not support reading metadata or does not have a mapping for `key`.
311337
312338
If `style=true` return a tuple of metadata value and metadata style. Metadata
313-
style is an additional information about the kind of metadata that is stored
314-
for the `key`.
339+
style is an additional information about the kind of metadata that is stored for
340+
the `key`.
315341
316342
$STYLE_INFO
343+
344+
If `default` is passed then return it if reading metadata is supported but
345+
mapping for `key` is missing. If `style=true` return `(default, :default)`.
317346
"""
318-
metadata(::T, ::AbstractString; style::Bool=false) where {T} =
319-
throw(ArgumentError("Objects of type $T do not support getting metadata"))
347+
function metadata end
320348

321349
"""
322350
metadatakeys(x)
323351
324352
Return an iterator of metadata keys for which `metadata(x, key)` returns a
325-
metadata value. If `x` does not support metadata return `()`.
353+
metadata value.
354+
Throw an error if `x` does not support reading metadata.
326355
"""
327-
metadatakeys(::Any) = ()
356+
function metadatakeys end
328357

329358
"""
330359
metadata!(x, key::AbstractString, value; style)
331360
332361
Set metadata for object `x` for key `key` to have value `value`
333362
and style `style` and return `x`.
334-
If `x` does not support setting metadata throw `ArgumentError`.
363+
Throw an error if `x` does not support setting metadata.
335364
336365
$STYLE_INFO
337366
"""
338-
metadata!(::T, ::AbstractString, ::Any; style) where {T} =
339-
throw(ArgumentError("Objects of type $T do not support setting metadata"))
367+
function metadata! end
340368

341369
"""
342370
deletemetadata!(x, key::AbstractString)
343371
344372
Delete metadata for object `x` for key `key` and return `x`
345373
(if metadata for `key` is not present do not perform any action).
346-
If `x` does not support metadata deletion throw `ArgumentError`.
374+
Throw an error if `x` does not support metadata deletion.
347375
"""
348-
deletemetadata!(::T, ::AbstractString) where {T} =
349-
throw(ArgumentError("Objects of type $T do not support metadata deletion"))
376+
function deletemetadata! end
350377

351378
"""
352379
emptymetadata!(x)
353380
354381
Delete all metadata for object `x`.
355-
If `x` does not support metadata deletion throw `ArgumentError`.
382+
Throw an error if `x` does not support metadata deletion.
356383
"""
357-
emptymetadata!(::T) where {T} =
358-
throw(ArgumentError("Objects of type $T do not support metadata deletion"))
384+
function emptymetadata! end
359385

360386
"""
361-
colmetadata(x, col, key::AbstractString; style::Bool=false)
387+
colmetadata(x, col, key::AbstractString, [default]; style::Bool=false)
362388
363389
Return metadata value associated with table `x` for column `col` and key `key`.
364-
If `x` does not support metadata for column `col` throw `ArgumentError`. If `x`
365-
supports metadata, but does not have a mapping for column `col` for `key` throw
366-
`KeyError`.
390+
Throw an error if `x` does not support reading metadata for column `col` or `x`
391+
supports reading metadata, but does not have a mapping for column `col` for `key`.
367392
368393
$COL_INFO
369394
@@ -372,73 +397,58 @@ style is an additional information about the kind of metadata that is stored for
372397
the `key`.
373398
374399
$STYLE_INFO
400+
401+
If `default` is passed then return it if `x` supports reading metadata and has
402+
column `col` but mapping for `key` is missing.
403+
If `style=true` return `(default, :default)`.
375404
"""
376-
colmetadata(::T, ::Int, ::AbstractString; style::Bool=false) where {T} =
377-
throw(ArgumentError("Objects of type $T do not support getting column metadata"))
378-
colmetadata(::T, ::Symbol, ::AbstractString; style::Bool=false) where {T} =
379-
throw(ArgumentError("Objects of type $T do not support getting column metadata"))
405+
function colmetadata end
380406

381407
"""
382408
colmetadatakeys(x, [col])
383409
384-
If `col` is passed return an iterator of metadata keys for which `metadata(x,
385-
col, key)` returns a metadata value. If `x` does not support metadata for column
386-
`col` return `()`.
410+
If `col` is passed return an iterator of metadata keys for which
411+
`metadata(x, col, key)` returns a metadata value. Throw an error if `x` does not
412+
support reading column metadata or if `col` is not a column of `x`.
387413
388414
`col` must have a type that is supported by table `x` for column indexing.
389-
Following the Tables.jl contract `Symbol` and `Int` are always allowed. Passing
390-
`col` that is not a column of `x` either throws an error (this is a
391-
preferred behavior if it is possible) or returns `()` (this duality is allowed
392-
as some Tables.jl tables do not have a schema).
415+
Following the Tables.jl contract `Symbol` and `Int` are always allowed.
393416
394417
If `col` is not passed return an iterator of `col => colmetadatakeys(x, col)`
395418
pairs for all columns that have metadata, where `col` are `Symbol`.
396419
If `x` does not support column metadata return `()`.
397420
"""
398-
colmetadatakeys(::Any, ::Int) = ()
399-
colmetadatakeys(::Any, ::Symbol) = ()
400-
colmetadatakeys(::Any) = ()
421+
function colmetadatakeys end
401422

402423
"""
403424
colmetadata!(x, col, key::AbstractString, value; style)
404425
405426
Set metadata for table `x` for column `col` for key `key` to have value `value`
406427
and style `style` and return `x`.
407-
If `x` does not support setting metadata for column `col` throw `ArgumentError`.
428+
Throw an error if `x` does not support setting metadata for column `col`.
408429
409430
$COL_INFO
410431
411432
$STYLE_INFO
412433
"""
413-
colmetadata!(::T, ::Int, ::AbstractString, ::Any; style) where {T} =
414-
throw(ArgumentError("Objects of type $T do not support setting metadata"))
415-
colmetadata!(::T, ::Symbol, ::AbstractString, ::Any; style) where {T} =
416-
throw(ArgumentError("Objects of type $T do not support setting metadata"))
434+
function colmetadata! end
417435

418436
"""
419437
deletecolmetadata!(x, col, key::AbstractString)
420438
421439
Delete metadata for table `x` for column `col` for key `key` and return `x`
422440
(if metadata for `key` is not present do not perform any action).
423-
If `x` does not support metadata deletion for column `col` throw `ArgumentError`.
441+
Throw an error if `x` does not support metadata deletion for column `col`.
424442
"""
425-
deletecolmetadata!(::T, ::Symbol, ::AbstractString) where {T} =
426-
throw(ArgumentError("Objects of type $T do not support metadata deletion"))
427-
deletecolmetadata!(::T, ::Int, ::AbstractString) where {T} =
428-
throw(ArgumentError("Objects of type $T do not support metadata deletion"))
443+
function deletecolmetadata! end
429444

430445
"""
431446
emptycolmetadata!(x, [col])
432447
433448
Delete all metadata for table `x` for column `col`.
434449
If `col` is not passed delete all column level metadata for table `x`.
435-
If `x` does not support metadata deletion for column `col` throw `ArgumentError`.
436-
"""
437-
emptycolmetadata!(::T, ::Symbol) where {T} =
438-
throw(ArgumentError("Objects of type $T do not support metadata deletion"))
439-
emptycolmetadata!(::T, ::Int) where {T} =
440-
throw(ArgumentError("Objects of type $T do not support metadata deletion"))
441-
emptycolmetadata!(::T) where {T} =
442-
throw(ArgumentError("Objects of type $T do not support metadata deletion"))
450+
Throw an error if `x` does not support metadata deletion for column `col`.
451+
"""
452+
function emptycolmetadata! end
443453

444454
end # module

test/runtests.jl

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,18 @@ struct TestMeta
2121
TestMeta() = new(Dict{String, Any}(), Dict{Symbol, Dict{String, Any}}())
2222
end
2323

24+
DataAPI.metadatasupport(::Type{TestMeta}) = (read=true, write=true)
25+
DataAPI.colmetadatasupport(::Type{TestMeta}) = (read=true, write=true)
26+
2427
function DataAPI.metadata(x::TestMeta, key::AbstractString; style::Bool=false)
2528
return style ? x.table[key] : x.table[key][1]
2629
end
2730

31+
function DataAPI.metadata(x::TestMeta, key::AbstractString, default; style::Bool=false)
32+
haskey(x.table, key) && return DataAPI.metadata(x, key, style=style)
33+
return style ? (default, :default) : default
34+
end
35+
2836
DataAPI.metadatakeys(x::TestMeta) = keys(x.table)
2937

3038
function DataAPI.metadata!(x::TestMeta, key::AbstractString, value; style)
@@ -44,6 +52,11 @@ function DataAPI.colmetadata(x::TestMeta, col::Symbol, key::AbstractString; styl
4452
return style ? x.col[col][key] : x.col[col][key][1]
4553
end
4654

55+
function DataAPI.colmetadata(x::TestMeta, col::Symbol, key::AbstractString, default; style::Bool=false)
56+
haskey(x.table, col) && haskey(x.table[col], key) && return DataAPI.metadata(x, key, style=style)
57+
return style ? (default, :default) : default
58+
end
59+
4760
function DataAPI.colmetadatakeys(x::TestMeta, col::Symbol)
4861
haskey(x.col, col) && return keys(x.col[col])
4962
return ()
@@ -247,34 +260,42 @@ end
247260
end
248261

249262
@testset "metadata" begin
250-
@test_throws ArgumentError DataAPI.metadata!(1, "a", 10, style=:default)
251-
@test_throws ArgumentError DataAPI.deletemetadata!(1, "a")
252-
@test_throws ArgumentError DataAPI.emptymetadata!(1)
253-
@test_throws ArgumentError DataAPI.metadata(1, "a")
254-
@test_throws ArgumentError DataAPI.metadata(1, "a", style=true)
255-
@test DataAPI.metadatakeys(1) == ()
256-
257-
@test_throws ArgumentError DataAPI.colmetadata!(1, :col, "a", 10, style=:default)
258-
@test_throws ArgumentError DataAPI.deletecolmetadata!(1, :col, "a")
259-
@test_throws ArgumentError DataAPI.emptycolmetadata!(1, :col)
260-
@test_throws ArgumentError DataAPI.deletecolmetadata!(1, 1, "a")
261-
@test_throws ArgumentError DataAPI.emptycolmetadata!(1, 1)
262-
@test_throws ArgumentError DataAPI.emptycolmetadata!(1)
263-
@test_throws ArgumentError DataAPI.colmetadata(1, :col, "a")
264-
@test_throws ArgumentError DataAPI.colmetadata(1, :col, "a", style=true)
265-
@test_throws ArgumentError DataAPI.colmetadata!(1, 1, "a", 10, style=:default)
266-
@test_throws ArgumentError DataAPI.colmetadata(1, 1, "a")
267-
@test_throws ArgumentError DataAPI.colmetadata(1, 1, "a", style=true)
268-
@test DataAPI.colmetadatakeys(1, :col) == ()
269-
@test DataAPI.colmetadatakeys(1, 1) == ()
270-
@test DataAPI.colmetadatakeys(1) == ()
263+
@test_throws MethodError DataAPI.metadata!(1, "a", 10, style=:default)
264+
@test_throws MethodError DataAPI.deletemetadata!(1, "a")
265+
@test_throws MethodError DataAPI.emptymetadata!(1)
266+
@test_throws MethodError DataAPI.metadata(1, "a")
267+
@test_throws MethodError DataAPI.metadata(1, "a", style=true)
268+
@test_throws MethodError DataAPI.metadatakeys(1)
269+
270+
@test_throws MethodError DataAPI.colmetadata!(1, :col, "a", 10, style=:default)
271+
@test_throws MethodError DataAPI.deletecolmetadata!(1, :col, "a")
272+
@test_throws MethodError DataAPI.emptycolmetadata!(1, :col)
273+
@test_throws MethodError DataAPI.deletecolmetadata!(1, 1, "a")
274+
@test_throws MethodError DataAPI.emptycolmetadata!(1, 1)
275+
@test_throws MethodError DataAPI.emptycolmetadata!(1)
276+
@test_throws MethodError DataAPI.colmetadata(1, :col, "a")
277+
@test_throws MethodError DataAPI.colmetadata(1, :col, "a", style=true)
278+
@test_throws MethodError DataAPI.colmetadata!(1, 1, "a", 10, style=:default)
279+
@test_throws MethodError DataAPI.colmetadata(1, 1, "a")
280+
@test_throws MethodError DataAPI.colmetadata(1, 1, "a", style=true)
281+
@test_throws MethodError DataAPI.colmetadatakeys(1, :col)
282+
@test_throws MethodError DataAPI.colmetadatakeys(1, 1)
283+
@test_throws MethodError DataAPI.colmetadatakeys(1)
284+
285+
@test DataAPI.metadatasupport(Int) == (read=false, write=false)
286+
@test DataAPI.colmetadatasupport(Int) == (read=false, write=false)
271287

272288
tm = TestMeta()
289+
@test DataAPI.metadatasupport(TestMeta) == (read=true, write=true)
290+
@test DataAPI.colmetadatasupport(TestMeta) == (read=true, write=true)
291+
273292
@test isempty(DataAPI.metadatakeys(tm))
274293
@test DataAPI.metadata!(tm, "a", "100", style=:note) == tm
275294
@test collect(DataAPI.metadatakeys(tm)) == ["a"]
276295
@test_throws KeyError DataAPI.metadata(tm, "b")
296+
@test DataAPI.metadata(tm, "b", 123) == 123
277297
@test_throws KeyError DataAPI.metadata(tm, "b", style=true)
298+
@test DataAPI.metadata(tm, "b", 123, style=true) == (123, :default)
278299
@test DataAPI.metadata(tm, "a") == "100"
279300
@test DataAPI.metadata(tm, "a", style=true) == ("100", :note)
280301
DataAPI.deletemetadata!(tm, "a")
@@ -289,7 +310,9 @@ end
289310
@test [k => collect(v) for (k, v) in DataAPI.colmetadatakeys(tm)] == [:col => ["a"]]
290311
@test collect(DataAPI.colmetadatakeys(tm, :col)) == ["a"]
291312
@test_throws KeyError DataAPI.colmetadata(tm, :col, "b")
313+
@test DataAPI.colmetadata(tm, :col, "b", 123) == 123
292314
@test_throws KeyError DataAPI.colmetadata(tm, :col, "b", style=true)
315+
@test DataAPI.colmetadata(tm, :col, "b", 123, style=true) == (123, :default)
293316
@test_throws KeyError DataAPI.colmetadata(tm, :col2, "a")
294317
@test_throws KeyError DataAPI.colmetadata(tm, :col2, "a", style=true)
295318
@test DataAPI.colmetadata(tm, :col, "a") == "100"

0 commit comments

Comments
 (0)