From 932d0da73de46dc271947a73299479f5b182a0ec Mon Sep 17 00:00:00 2001 From: Daniel Schwabeneder Date: Thu, 26 Mar 2020 15:09:31 +0100 Subject: [PATCH 1/4] move `replaceAliases!` out of recipe processing --- src/args.jl | 4 ++-- src/pipeline.jl | 6 +++--- src/series.jl | 2 -- src/types.jl | 7 ++++--- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/args.jl b/src/args.jl index 82ca09d8..b388c641 100644 --- a/src/args.jl +++ b/src/args.jl @@ -916,8 +916,8 @@ function _add_markershape(plotattributes::AKW) end "Handle all preprocessing of args... break out colors/sizes/etc and replace aliases." -function preprocessArgs!(plotattributes::AKW) - replaceAliases!(plotattributes, _keyAliases) +function preprocessArgs!(plotattributes::AKW, replace_aliases = true) + replace_aliases && replaceAliases!(plotattributes, _keyAliases) # clear all axis stuff # if haskey(plotattributes, :axis) && plotattributes[:axis] in (:none, nothing, false) diff --git a/src/pipeline.jl b/src/pipeline.jl index 1e9cd991..383ac82a 100644 --- a/src/pipeline.jl +++ b/src/pipeline.jl @@ -96,7 +96,7 @@ function _process_userrecipe(plt::Plot, kw_list::Vector{KW}, recipedata::RecipeD # when the arg tuple is empty, that means there's nothing left to recursively # process... finish up and add to the kw_list kw = recipedata.plotattributes - preprocessArgs!(kw) + preprocessArgs!(kw, false) _preprocess_userrecipe(kw) warnOnUnsupported_scales(plt.backend, kw) @@ -185,7 +185,7 @@ function _process_plotrecipe(plt::Plot, kw::AKW, kw_list::Vector{KW}, still_to_p st = kw[:seriestype] = get(_typeAliases, st, st) datalist = RecipesBase.apply_recipe(kw, Val{st}, plt) for data in datalist - preprocessArgs!(data.plotattributes) + preprocessArgs!(data.plotattributes, false) if data.plotattributes[:seriestype] == st error("Plot recipe $st returned the same seriestype: $(data.plotattributes)") end @@ -413,7 +413,7 @@ function _process_seriesrecipe(plt::Plot, plotattributes::AKW) # assuming there was no error, recursively apply the series recipes for data in datalist if isa(data, RecipeData) - preprocessArgs!(data.plotattributes) + preprocessArgs!(data.plotattributes, false) if data.plotattributes[:seriestype] == st error("The seriestype didn't change in series recipe $st. This will cause a StackOverflow.") end diff --git a/src/series.jl b/src/series.jl index afebde8e..3e76d93a 100644 --- a/src/series.jl +++ b/src/series.jl @@ -227,7 +227,6 @@ _apply_type_recipe(plotattributes, v::AbstractArray{<:DataPoint}, letter) = v # axis args before type recipes should still be mapped to all axes function _preprocess_axis_args!(plotattributes) - replaceAliases!(plotattributes, _keyAliases) for (k, v) in plotattributes if haskey(_axis_defaults, k) pop!(plotattributes, k) @@ -247,7 +246,6 @@ end function _postprocess_axis_args!(plotattributes, letter) pop!(plotattributes, :letter) if letter in (:x, :y, :z) - replaceAliases!(plotattributes, _keyAliases) for (k, v) in plotattributes if haskey(_axis_defaults, k) pop!(plotattributes, k) diff --git a/src/types.jl b/src/types.jl index 4ee8e9e0..a866e59f 100644 --- a/src/types.jl +++ b/src/types.jl @@ -26,8 +26,9 @@ struct Attr <: AbstractDict{Symbol,Any} defaults::KW end -Base.getindex(attr::Attr, k) = haskey(attr.explicit,k) ? - attr.explicit[k] : attr.defaults[k] +function Base.getindex(attr::Attr, 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.get(attr::Attr, k, default) = haskey(attr, k) ? attr[k] : default function Base.get!(attr::Attr, k, default) @@ -41,7 +42,7 @@ end function Base.delete!(attr::Attr, k) haskey(attr.explicit, k) && delete!(attr.explicit, k) haskey(attr.defaults, k) && delete!(attr.defaults, k) -end +end Base.length(attr::Attr) = length(union(keys(attr.explicit), keys(attr.defaults))) function Base.iterate(attr::Attr) exp_keys = keys(attr.explicit) From 0c2d80841c0f4035fd2cd18edaf56a90be7a6919 Mon Sep 17 00:00:00 2001 From: Daniel Schwabeneder Date: Fri, 27 Mar 2020 13:44:39 +0100 Subject: [PATCH 2/4] warn on recipe aliases --- src/args.jl | 28 ++++++++++++++++++++++++++++ src/pipeline.jl | 33 +++++++++++++++++++++++++++++++-- src/recipes.jl | 4 ++-- src/series.jl | 3 +++ 4 files changed, 64 insertions(+), 4 deletions(-) diff --git a/src/args.jl b/src/args.jl index b388c641..f32bfe42 100644 --- a/src/args.jl +++ b/src/args.jl @@ -460,6 +460,24 @@ is_axis_attr_noletter(k) = haskey(_axis_defaults, 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_default_attribute(k::Symbol) + k in _internal_args && return true + k in _all_args && return true + is_axis_attr(k) && return true + is_axis_attr_noletter(k) && return true + k in _magic_axis_args && 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")) @@ -1017,6 +1035,16 @@ function preprocessArgs!(plotattributes::AKW, replace_aliases = true) 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 for arg in wraptuple(pop_kw!(plotattributes, :line, ())) diff --git a/src/pipeline.jl b/src/pipeline.jl index 383ac82a..5b4d9bf5 100644 --- a/src/pipeline.jl +++ b/src/pipeline.jl @@ -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 @@ -82,7 +104,11 @@ function _process_userrecipes(plt::Plot, plotattributes::AKW, args) if isempty(next_series.args) _process_userrecipe(plt, kw_list, next_series) 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) 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] = get(_typeAliases, st, st) datalist = RecipesBase.apply_recipe(kw, Val{st}, plt) + warn_on_recipe_aliases!(datalist, :plot, st) for data in datalist preprocessArgs!(data.plotattributes, false) if data.plotattributes[:seriestype] == st @@ -408,7 +435,9 @@ function _process_seriesrecipe(plt::Plot, plotattributes::AKW) else # 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 for data in datalist diff --git a/src/recipes.jl b/src/recipes.jl index dc94c1a9..79b4176d 100644 --- a/src/recipes.jl +++ b/src/recipes.jl @@ -1261,8 +1261,8 @@ end yflip := true aspect_ratio := 1 rs, cs, zs = findnz(z.surf) - xlim := ignorenan_extrema(cs) - ylim := ignorenan_extrema(rs) + xlims := ignorenan_extrema(cs) + ylims := ignorenan_extrema(rs) if plotattributes[:markershape] == :none markershape := :circle end diff --git a/src/series.jl b/src/series.jl index 3e76d93a..5df68409 100644 --- a/src/series.jl +++ b/src/series.jl @@ -178,6 +178,7 @@ end function _apply_type_recipe(plotattributes, v, letter) _preprocess_axis_args!(plotattributes, letter) rdvec = RecipesBase.apply_recipe(plotattributes, typeof(v), v) + warn_on_recipe_aliases!(plotattributes, :type, typeof(v)) _postprocess_axis_args!(plotattributes, letter) return rdvec[1].args[1] end @@ -189,11 +190,13 @@ function _apply_type_recipe(plotattributes, v::AbstractArray, letter) _preprocess_axis_args!(plotattributes, letter) # First we try to apply an array type recipe. 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 typeof(v) == typeof(w) isempty(skipmissing(v)) && return Float64[] x = first(skipmissing(v)) args = RecipesBase.apply_recipe(plotattributes, typeof(x), x)[1].args + warn_on_recipe_aliases!(plotattributes, :type, typeof(x)) _postprocess_axis_args!(plotattributes, letter) if length(args) == 2 && all(arg -> arg isa Function, args) numfunc, formatter = args From 8594926b67f1010c05cd499b34dee1a31fca8ec3 Mon Sep 17 00:00:00 2001 From: Daniel Schwabeneder Date: Fri, 27 Mar 2020 16:31:06 +0100 Subject: [PATCH 3/4] move `replaceAliases!` back to `preprocessArgs!` --- src/args.jl | 4 ++-- src/pipeline.jl | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/args.jl b/src/args.jl index f32bfe42..a2b70326 100644 --- a/src/args.jl +++ b/src/args.jl @@ -934,8 +934,8 @@ function _add_markershape(plotattributes::AKW) end "Handle all preprocessing of args... break out colors/sizes/etc and replace aliases." -function preprocessArgs!(plotattributes::AKW, replace_aliases = true) - replace_aliases && replaceAliases!(plotattributes, _keyAliases) +function preprocessArgs!(plotattributes::AKW) + replaceAliases!(plotattributes, _keyAliases) # clear all axis stuff # if haskey(plotattributes, :axis) && plotattributes[:axis] in (:none, nothing, false) diff --git a/src/pipeline.jl b/src/pipeline.jl index 5b4d9bf5..ef846dd1 100644 --- a/src/pipeline.jl +++ b/src/pipeline.jl @@ -122,7 +122,7 @@ function _process_userrecipe(plt::Plot, kw_list::Vector{KW}, recipedata::RecipeD # when the arg tuple is empty, that means there's nothing left to recursively # process... finish up and add to the kw_list kw = recipedata.plotattributes - preprocessArgs!(kw, false) + preprocessArgs!(kw) _preprocess_userrecipe(kw) warnOnUnsupported_scales(plt.backend, kw) @@ -212,7 +212,7 @@ function _process_plotrecipe(plt::Plot, kw::AKW, kw_list::Vector{KW}, still_to_p datalist = RecipesBase.apply_recipe(kw, Val{st}, plt) warn_on_recipe_aliases!(datalist, :plot, st) for data in datalist - preprocessArgs!(data.plotattributes, false) + preprocessArgs!(data.plotattributes) if data.plotattributes[:seriestype] == st error("Plot recipe $st returned the same seriestype: $(data.plotattributes)") end @@ -442,7 +442,7 @@ function _process_seriesrecipe(plt::Plot, plotattributes::AKW) # assuming there was no error, recursively apply the series recipes for data in datalist if isa(data, RecipeData) - preprocessArgs!(data.plotattributes, false) + preprocessArgs!(data.plotattributes) if data.plotattributes[:seriestype] == st error("The seriestype didn't change in series recipe $st. This will cause a StackOverflow.") end From e9ef5c852b8841e3a4dca4507dea4dac573ead85 Mon Sep 17 00:00:00 2001 From: Daniel Schwabeneder Date: Fri, 27 Mar 2020 17:09:07 +0100 Subject: [PATCH 4/4] fix magic axis args for recipes --- src/args.jl | 11 ++++++++--- src/series.jl | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/args.jl b/src/args.jl index a2b70326..cebc5344 100644 --- a/src/args.jl +++ b/src/args.jl @@ -466,12 +466,17 @@ const _magic_axis_args = [:axis, :tickfont, :guidefont] const _magic_subplot_args = [:titlefont, :legendfont, :legendtitlefont, ] const _magic_series_args = [:line, :marker, :fill] -function is_default_attribute(k::Symbol) +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_axis_attr_noletter(k) && return true - k in _magic_axis_args && 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 diff --git a/src/series.jl b/src/series.jl index 5df68409..43cedc8b 100644 --- a/src/series.jl +++ b/src/series.jl @@ -231,7 +231,7 @@ _apply_type_recipe(plotattributes, v::AbstractArray{<:DataPoint}, letter) = v # axis args before type recipes should still be mapped to all axes function _preprocess_axis_args!(plotattributes) for (k, v) in plotattributes - if haskey(_axis_defaults, k) + if is_noletter_attribute(k) pop!(plotattributes, k) for l in (:x, :y, :z) lk = Symbol(l, k) @@ -250,7 +250,7 @@ function _postprocess_axis_args!(plotattributes, letter) pop!(plotattributes, :letter) if letter in (:x, :y, :z) for (k, v) in plotattributes - if haskey(_axis_defaults, k) + if is_noletter_attribute(k) pop!(plotattributes, k) lk = Symbol(letter, k) haskey(plotattributes, lk) || (plotattributes[lk] = v)