Merge pull request #2507 from daschw/no-aliases

Warn on aliases in recipes
This commit is contained in:
Daniel Schwabeneder 2020-03-27 19:39:16 +01:00 committed by GitHub
commit 5fc02f7f58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 75 additions and 11 deletions

View File

@ -460,6 +460,29 @@ is_axis_attr_noletter(k) = haskey(_axis_defaults, k)
RecipesBase.is_key_supported(k::Symbol) = is_attr_supported(k) RecipesBase.is_key_supported(k::Symbol) = is_attr_supported(k)
const _internal_args =
[:plot_object, :series_plotindex, :markershape_to_add, :letter, :idxfilter]
const _magic_axis_args = [:axis, :tickfont, :guidefont]
const _magic_subplot_args = [:titlefont, :legendfont, :legendtitlefont, ]
const _magic_series_args = [:line, :marker, :fill]
function is_noletter_attribute(k)
is_axis_attr_noletter(k) && return true
k in _magic_axis_args && return true
return false
end
function is_default_attribute(k)
k in _internal_args && return true
k in _all_args && return true
is_axis_attr(k) && return true
is_noletter_attribute(k) && return true
k in _magic_subplot_args && return true
k in _magic_series_args && return true
Symbol(chop(string(k); head = 1, tail = 0)) in _magic_axis_args && return true
return false
end
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
makeplural(s::Symbol) = Symbol(string(s,"s")) makeplural(s::Symbol) = Symbol(string(s,"s"))
@ -1017,6 +1040,16 @@ function preprocessArgs!(plotattributes::AKW)
end end
end end
end end
# handle axes guides
if haskey(plotattributes, :guide)
guide = pop!(plotattributes, :guide)
for letter in (:x, :y, :z)
guide_sym = Symbol(letter, :guide)
if !is_explicit(plotattributes, guide_sym)
plotattributes[guide_sym] = guide
end
end
end
# handle line args # handle line args
for arg in wraptuple(pop_kw!(plotattributes, :line, ())) for arg in wraptuple(pop_kw!(plotattributes, :line, ()))

View File

@ -1,4 +1,26 @@
# Error for aliases used in recipes
function warn_on_recipe_aliases!(plotattributes, recipe_type, args...)
for k in keys(plotattributes)
if !is_default_attribute(k)
dk = get(_keyAliases, k, k)
if k !== dk
@warn "Attribute alias `$k` detected in the $recipe_type recipe defined for the signature $(signature_string(Val{recipe_type}, args...)). To ensure expected behavior it is recommended to use the default attribute `$dk`."
end
plotattributes[dk] = pop_kw!(plotattributes, k)
end
end
end
warn_on_recipe_aliases!(v::AbstractVector, recipe_type, args) =
foreach(x -> warn_on_recipe_aliases!(x, recipe_type, args), v)
warn_on_recipe_aliases!(rd::RecipeData, recipe_type, args) =
warn_on_recipe_aliases!(rd.plotattributes, recipe_type, args)
function signature_string(::Type{Val{:user}}, args...)
return string("(::", join(string.(typeof.(args)), ", ::"), ")")
end
signature_string(::Type{Val{:type}}, T) = "(::Type{$T}, ::$T)"
signature_string(::Type{Val{:plot}}, st) = "(::Type{Val{:$st}}, ::AbstractPlot)"
signature_string(::Type{Val{:series}}, st) = "(::Type{Val{:$st}}, x, y, z)"
# ------------------------------------------------------------------ # ------------------------------------------------------------------
# preprocessing # preprocessing
@ -82,7 +104,11 @@ function _process_userrecipes(plt::Plot, plotattributes::AKW, args)
if isempty(next_series.args) if isempty(next_series.args)
_process_userrecipe(plt, kw_list, next_series) _process_userrecipe(plt, kw_list, next_series)
else else
rd_list = RecipesBase.apply_recipe(next_series.plotattributes, next_series.args...) rd_list = RecipesBase.apply_recipe(
next_series.plotattributes,
next_series.args...
)
warn_on_recipe_aliases!(rd_list, :user, next_series.args)
prepend!(still_to_process,rd_list) prepend!(still_to_process,rd_list)
end end
end end
@ -184,6 +210,7 @@ function _process_plotrecipe(plt::Plot, kw::AKW, kw_list::Vector{KW}, still_to_p
st = kw[:seriestype] st = kw[:seriestype]
st = kw[:seriestype] = get(_typeAliases, st, st) st = kw[:seriestype] = get(_typeAliases, st, st)
datalist = RecipesBase.apply_recipe(kw, Val{st}, plt) datalist = RecipesBase.apply_recipe(kw, Val{st}, plt)
warn_on_recipe_aliases!(datalist, :plot, st)
for data in datalist for data in datalist
preprocessArgs!(data.plotattributes) preprocessArgs!(data.plotattributes)
if data.plotattributes[:seriestype] == st if data.plotattributes[:seriestype] == st
@ -408,7 +435,9 @@ function _process_seriesrecipe(plt::Plot, plotattributes::AKW)
else else
# get a sub list of series for this seriestype # get a sub list of series for this seriestype
datalist = RecipesBase.apply_recipe(plotattributes, Val{st}, plotattributes[:x], plotattributes[:y], plotattributes[:z]) x, y, z = plotattributes[:x], plotattributes[:y], plotattributes[:z]
datalist = RecipesBase.apply_recipe(plotattributes, Val{st}, x, y, z)
warn_on_recipe_aliases!(datalist, :series, st)
# assuming there was no error, recursively apply the series recipes # assuming there was no error, recursively apply the series recipes
for data in datalist for data in datalist

View File

@ -1261,8 +1261,8 @@ end
yflip := true yflip := true
aspect_ratio := 1 aspect_ratio := 1
rs, cs, zs = findnz(z.surf) rs, cs, zs = findnz(z.surf)
xlim := ignorenan_extrema(cs) xlims := ignorenan_extrema(cs)
ylim := ignorenan_extrema(rs) ylims := ignorenan_extrema(rs)
if plotattributes[:markershape] == :none if plotattributes[:markershape] == :none
markershape := :circle markershape := :circle
end end

View File

@ -174,6 +174,7 @@ end
function _apply_type_recipe(plotattributes, v, letter) function _apply_type_recipe(plotattributes, v, letter)
_preprocess_axis_args!(plotattributes, letter) _preprocess_axis_args!(plotattributes, letter)
rdvec = RecipesBase.apply_recipe(plotattributes, typeof(v), v) rdvec = RecipesBase.apply_recipe(plotattributes, typeof(v), v)
warn_on_recipe_aliases!(plotattributes, :type, typeof(v))
_postprocess_axis_args!(plotattributes, letter) _postprocess_axis_args!(plotattributes, letter)
return rdvec[1].args[1] return rdvec[1].args[1]
end end
@ -185,11 +186,13 @@ function _apply_type_recipe(plotattributes, v::AbstractArray, letter)
_preprocess_axis_args!(plotattributes, letter) _preprocess_axis_args!(plotattributes, letter)
# First we try to apply an array type recipe. # First we try to apply an array type recipe.
w = RecipesBase.apply_recipe(plotattributes, typeof(v), v)[1].args[1] w = RecipesBase.apply_recipe(plotattributes, typeof(v), v)[1].args[1]
warn_on_recipe_aliases!(plotattributes, :type, typeof(v))
# If the type did not change try it element-wise # If the type did not change try it element-wise
if typeof(v) == typeof(w) if typeof(v) == typeof(w)
isempty(skipmissing(v)) && return Float64[] isempty(skipmissing(v)) && return Float64[]
x = first(skipmissing(v)) x = first(skipmissing(v))
args = RecipesBase.apply_recipe(plotattributes, typeof(x), x)[1].args args = RecipesBase.apply_recipe(plotattributes, typeof(x), x)[1].args
warn_on_recipe_aliases!(plotattributes, :type, typeof(x))
_postprocess_axis_args!(plotattributes, letter) _postprocess_axis_args!(plotattributes, letter)
if length(args) == 2 && all(arg -> arg isa Function, args) if length(args) == 2 && all(arg -> arg isa Function, args)
numfunc, formatter = args numfunc, formatter = args
@ -223,9 +226,8 @@ _apply_type_recipe(plotattributes, v::AbstractArray{<:DataPoint}, letter) = v
# axis args before type recipes should still be mapped to all axes # axis args before type recipes should still be mapped to all axes
function _preprocess_axis_args!(plotattributes) function _preprocess_axis_args!(plotattributes)
replaceAliases!(plotattributes, _keyAliases)
for (k, v) in plotattributes for (k, v) in plotattributes
if haskey(_axis_defaults, k) if is_noletter_attribute(k)
pop!(plotattributes, k) pop!(plotattributes, k)
for l in (:x, :y, :z) for l in (:x, :y, :z)
lk = Symbol(l, k) lk = Symbol(l, k)
@ -243,9 +245,8 @@ end
function _postprocess_axis_args!(plotattributes, letter) function _postprocess_axis_args!(plotattributes, letter)
pop!(plotattributes, :letter) pop!(plotattributes, :letter)
if letter in (:x, :y, :z) if letter in (:x, :y, :z)
replaceAliases!(plotattributes, _keyAliases)
for (k, v) in plotattributes for (k, v) in plotattributes
if haskey(_axis_defaults, k) if is_noletter_attribute(k)
pop!(plotattributes, k) pop!(plotattributes, k)
lk = Symbol(letter, k) lk = Symbol(letter, k)
haskey(plotattributes, lk) || (plotattributes[lk] = v) haskey(plotattributes, lk) || (plotattributes[lk] = v)

View File

@ -26,8 +26,9 @@ struct Attr <: AbstractDict{Symbol,Any}
defaults::KW defaults::KW
end end
Base.getindex(attr::Attr, k) = haskey(attr.explicit,k) ? function Base.getindex(attr::Attr, k)
attr.explicit[k] : attr.defaults[k] return haskey(attr.explicit, k) ? attr.explicit[k] : attr.defaults[k]
end
Base.haskey(attr::Attr, k) = haskey(attr.explicit,k) || haskey(attr.defaults,k) Base.haskey(attr::Attr, k) = haskey(attr.explicit,k) || haskey(attr.defaults,k)
Base.get(attr::Attr, k, default) = haskey(attr, k) ? attr[k] : default Base.get(attr::Attr, k, default) = haskey(attr, k) ? attr[k] : default
function Base.get!(attr::Attr, k, default) function Base.get!(attr::Attr, k, default)