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
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "DataAPI"
uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
authors = ["quinnj <quinn.jacobd@gmail.com>"]
version = "1.12.0"
version = "1.13.0"

[compat]
julia = "1"
Expand Down
65 changes: 61 additions & 4 deletions src/DataAPI.jl
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,28 @@ mapping for `key` is missing. If `style=true` return `(default, :default)`.
"""
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 must not be modified.
"""
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 All @@ -356,10 +378,10 @@ Throw an error if `x` does not support reading metadata.
function metadatakeys end

"""
metadata!(x, key::AbstractString, value; style)
metadata!(x, key::AbstractString, value; style::Symbol=:default)

Set metadata for object `x` for key `key` to have value `value`
and style `style` and return `x`.
and style `style` (`:default` by default) and return `x`.
Throw an error if `x` does not support setting metadata.

$STYLE_INFO
Expand Down Expand Up @@ -404,6 +426,41 @@ If `style=true` return `(default, :default)`.
"""
function colmetadata end

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

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

If `col` is passed 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

The returned dictionary may be freshly allocated on each call to `colmetadata`
and is considered to be owned by `x` so it must not be modified.
"""
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 All @@ -421,10 +478,10 @@ If `x` does not support column metadata return `()`.
function colmetadatakeys end

"""
colmetadata!(x, col, key::AbstractString, value; style)
colmetadata!(x, col, key::AbstractString, value; style::Symbol=:default)

Set metadata for table `x` for column `col` for key `key` to have value `value`
and style `style` and return `x`.
and style `style` (`:default` by default) and return `x`.
Throw an error if `x` does not support setting metadata for column `col`.

$COL_INFO
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