Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make it easier to work with metadata by providing more default behaviors #56

Merged
merged 10 commits into from
Nov 9, 2022
64 changes: 56 additions & 8 deletions src/DataAPI.jl
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ The `write` field indicates whether modifying metadata with the [`colmetadata!`]
colmetadatasupport(::Type) = (read=false, write=false)

"""
metadata(x, [key::AbstractString], [default]; style::Bool=false)
metadata(x, key::AbstractString, [default]; style::Bool=false)

Return metadata value associated with object `x` for key `key`. Throw an error
if `x` does not support reading metadata or does not have a mapping for `key`.
Expand All @@ -343,12 +343,31 @@ $STYLE_INFO

If `default` is passed then return it if reading metadata is supported but
mapping for `key` is missing. If `style=true` return `(default, :default)`.

If `key` is not passed return a dictionary mapping all metadata keys to
metadata values associated with object `x`.
"""
function metadata end

"""
metadata(x; style::Bool=false)

Return a dictionary mapping all metadata keys to metadata values associated
with object `x`. Throw an error if `x` does not support reading metadata.

If `style=true` values are tuples of metadata value and metadata style. Metadata
style is an additional information about the kind of metadata that is stored for
the `key`.

$STYLE_INFO

The returned dictionary may be freshly allocated on each call to `metadata` and
is considered to be owned by `x` so it should be only used for reading data.
bkamins marked this conversation as resolved.
Show resolved Hide resolved
"""
function metadata(x::T; style::Bool=false) where {T}
if !metadatasupport(T).read
throw(ArgumentError("Objects of type $T do not support reading metadata"))
end
return Dict(key => metadata(x, key, style=style) for key in metadatakeys(x))
end

"""
metadatakeys(x)

Expand Down Expand Up @@ -387,7 +406,7 @@ Throw an error if `x` does not support metadata deletion.
function emptymetadata! end

"""
colmetadata(x, [col], [key::AbstractString], [default]; style::Bool=false)
colmetadata(x, col, key::AbstractString, [default]; style::Bool=false)

Return metadata value associated with table `x` for column `col` and key `key`.
Throw an error if `x` does not support reading metadata for column `col` or `x`
Expand All @@ -404,15 +423,44 @@ $STYLE_INFO
If `default` is passed then return it if `x` supports reading metadata and has
column `col` but mapping for `key` is missing.
If `style=true` return `(default, :default)`.
"""
function colmetadata end

If `key` is not passed return a dictionary mapping all metadata keys to
metadata values associated with table `x` for column `col`.
"""
colmetadata(x, [col]; style::Bool=false)

Return a dictionary mapping all column metadata keys to metadata values
associated with column `col` of table `x`. Throw an error if `x` does not
support reading metadata for column `col` or column `col` is not present in `x`.

If `style=true` values are tuples of metadata value and metadata style. Metadata
style is an additional information about the kind of metadata that is stored for
the `key`.

$STYLE_INFO

If `col` is not passed return a dictionary mapping columns represented as
`Symbol` that have associated metadata to dictionaries dictionary mapping all
bkamins marked this conversation as resolved.
Show resolved Hide resolved
metadata keys to metadata values associated with table `x` for a given column.

The returned dictionary may be freshly allocated on each call to `colmetadata`
and is considered to be owned by `x` so it should be only used for reading
data.
bkamins marked this conversation as resolved.
Show resolved Hide resolved
"""
function colmetadata end
function colmetadata(x::T, col; style::Bool=false) where {T}
if !colmetadatasupport(T).read
throw(ArgumentError("Objects of type $T do not support reading column metadata"))
end
return Dict(key => colmetadata(x, col, key, style=style) for key in colmetadatakeys(x, col))
end

function colmetadata(x::T; style::Bool=false) where {T}
if !colmetadatasupport(T).read
throw(ArgumentError("Objects of type $T do not support reading column metadata"))
end
return Dict(col => Dict(key => colmetadata(x, col, key, style=style) for key in keys)
for (col, keys) in colmetadatakeys(x))
end

"""
colmetadatakeys(x, [col])
Expand Down
28 changes: 20 additions & 8 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,7 @@ end

DataAPI.metadatakeys(x::TestMeta) = keys(x.table)

function DataAPI.metadata!(x::TestMeta, key::AbstractString, value; style)
x.table[key] = (value, style)
return x
end

function DataAPI.metadata!(x::TestMeta, key::AbstractString, value; style)
function DataAPI.metadata!(x::TestMeta, key::AbstractString, value; style::Symbol=:default)
x.table[key] = (value, style)
return x
end
Expand All @@ -52,7 +47,8 @@ function DataAPI.colmetadata(x::TestMeta, col::Symbol, key::AbstractString; styl
return style ? x.col[col][key] : x.col[col][key][1]
end

function DataAPI.colmetadata(x::TestMeta, col::Symbol, key::AbstractString, default; style::Bool=false)
function DataAPI.colmetadata(x::TestMeta, col::Symbol, key::AbstractString, default;
style::Bool=false)
haskey(x.table, col) && haskey(x.table[col], key) && return DataAPI.metadata(x, key, style=style)
return style ? (default, :default) : default
end
Expand All @@ -67,7 +63,8 @@ function DataAPI.colmetadatakeys(x::TestMeta)
return (col => keys(x.col[col]) for col in keys(x.col))
end

function DataAPI.colmetadata!(x::TestMeta, col::Symbol, key::AbstractString, value; style)
function DataAPI.colmetadata!(x::TestMeta, col::Symbol, key::AbstractString, value;
style::Symbol=:default)
if haskey(x.col, col)
x.col[col][key] = (value, style)
else
Expand Down Expand Up @@ -264,7 +261,9 @@ end
@test_throws MethodError DataAPI.deletemetadata!(1, "a")
@test_throws MethodError DataAPI.emptymetadata!(1)
@test_throws MethodError DataAPI.metadata(1, "a")
@test_throws ArgumentError DataAPI.metadata(1)
@test_throws MethodError DataAPI.metadata(1, "a", style=true)
@test_throws ArgumentError DataAPI.metadata(1, style=true)
@test_throws MethodError DataAPI.metadatakeys(1)

@test_throws MethodError DataAPI.colmetadata!(1, :col, "a", 10, style=:default)
Expand All @@ -274,7 +273,11 @@ end
@test_throws MethodError DataAPI.emptycolmetadata!(1, 1)
@test_throws MethodError DataAPI.emptycolmetadata!(1)
@test_throws MethodError DataAPI.colmetadata(1, :col, "a")
@test_throws ArgumentError DataAPI.colmetadata(1, :col)
@test_throws ArgumentError DataAPI.colmetadata(1)
@test_throws MethodError DataAPI.colmetadata(1, :col, "a", style=true)
@test_throws ArgumentError DataAPI.colmetadata(1, :col, style=true)
@test_throws ArgumentError DataAPI.colmetadata(1, style=true)
@test_throws MethodError DataAPI.colmetadata!(1, 1, "a", 10, style=:default)
@test_throws MethodError DataAPI.colmetadata(1, 1, "a")
@test_throws MethodError DataAPI.colmetadata(1, 1, "a", style=true)
Expand All @@ -290,14 +293,18 @@ end
@test DataAPI.colmetadatasupport(TestMeta) == (read=true, write=true)

@test isempty(DataAPI.metadatakeys(tm))
@test DataAPI.metadata(tm) == Dict()
@test DataAPI.metadata(tm, style=true) == Dict()
@test DataAPI.metadata!(tm, "a", "100", style=:note) == tm
@test collect(DataAPI.metadatakeys(tm)) == ["a"]
@test_throws KeyError DataAPI.metadata(tm, "b")
@test DataAPI.metadata(tm, "b", 123) == 123
@test_throws KeyError DataAPI.metadata(tm, "b", style=true)
@test DataAPI.metadata(tm, "b", 123, style=true) == (123, :default)
@test DataAPI.metadata(tm, "a") == "100"
@test DataAPI.metadata(tm) == Dict("a" => "100")
@test DataAPI.metadata(tm, "a", style=true) == ("100", :note)
@test DataAPI.metadata(tm, style=true) == Dict("a" => ("100", :note))
DataAPI.deletemetadata!(tm, "a")
@test isempty(DataAPI.metadatakeys(tm))
@test DataAPI.metadata!(tm, "a", "100", style=:note) == tm
Expand All @@ -306,6 +313,7 @@ end

@test DataAPI.colmetadatakeys(tm) == ()
@test DataAPI.colmetadatakeys(tm, :col) == ()
@test DataAPI.colmetadata(tm) == Dict()
@test DataAPI.colmetadata!(tm, :col, "a", "100", style=:note) == tm
@test [k => collect(v) for (k, v) in DataAPI.colmetadatakeys(tm)] == [:col => ["a"]]
@test collect(DataAPI.colmetadatakeys(tm, :col)) == ["a"]
Expand All @@ -316,7 +324,11 @@ end
@test_throws KeyError DataAPI.colmetadata(tm, :col2, "a")
@test_throws KeyError DataAPI.colmetadata(tm, :col2, "a", style=true)
@test DataAPI.colmetadata(tm, :col, "a") == "100"
@test DataAPI.colmetadata(tm, :col) == Dict("a" => "100")
@test DataAPI.colmetadata(tm) == Dict(:col => Dict("a" => "100"))
@test DataAPI.colmetadata(tm, :col, "a", style=true) == ("100", :note)
@test DataAPI.colmetadata(tm, :col, style=true) == Dict("a" => ("100", :note))
@test DataAPI.colmetadata(tm, style=true) == Dict(:col => Dict("a" => ("100", :note)))
DataAPI.deletecolmetadata!(tm, :col, "a")
@test isempty(DataAPI.colmetadatakeys(tm, :col))
@test DataAPI.colmetadata!(tm, :col, "a", "100", style=:note) == tm
Expand Down