From adc63dbedaddb71e27885a078bb230e727c32096 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 5 Feb 2023 11:13:28 +0100 Subject: [PATCH] add `UnitfulExt` --- CHANGELOG.md | 4 ++ Project.toml | 6 ++- ext/FreeTypeExt.jl | 34 +++++++-------- ext/ImageInTerminalExt.jl | 2 +- ext/UnitfulExt.jl | 84 ++++++++++++++++++++++++++++++++++++ src/UnicodePlots.jl | 17 ++++---- src/common.jl | 16 ------- src/interface/lineplot.jl | 29 ------------- src/interface/scatterplot.jl | 29 ------------- test/runtests.jl | 2 +- test/tst_common.jl | 22 ++++++---- test/tst_quality.jl | 7 ++- 12 files changed, 140 insertions(+), 112 deletions(-) create mode 100644 ext/UnitfulExt.jl diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d95330b..ac4ad9f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## [4.0] - 2023-02-xx +### Changed +- Rework conditional dependencies (`Unitful`, `FreeType`, `FileIO`) through weak deps to improve latency + ## [3.3] - 2022-11-17 ### Added - Group digits on integer values (thousands) for readability (configured using `thousands_separator`). diff --git a/Project.toml b/Project.toml index 51693086..a1916217 100644 --- a/Project.toml +++ b/Project.toml @@ -20,7 +20,6 @@ SnoopPrecompile = "66db9d55-30c0-4569-8b51-7e840670fc0c" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" -Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" [compat] ColorSchemes = "^3.19" @@ -43,10 +42,12 @@ julia = "1.6" FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" FreeType = "b38be410-82b0-50bf-ab77-7b57e271db43" ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254" +Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" [extensions] FreeTypeExt = ["FileIO", "FreeType"] ImageInTerminalExt = "ImageInTerminal" +UnitfulExt = "Unitful" [extras] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" @@ -61,6 +62,7 @@ StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" TestImages = "5e47fb64-e119-507b-a336-dd2b206d9990" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" +Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" [targets] -test = ["Aqua", "DataFrames", "FileIO", "FreeType", "ImageInTerminal", "ImageMagick", "Random", "ReferenceTests", "StableRNGs", "Test", "TestImages", "TimerOutputs"] +test = ["Aqua", "DataFrames", "FileIO", "FreeType", "ImageInTerminal", "ImageMagick", "Random", "ReferenceTests", "StableRNGs", "Test", "TestImages", "TimerOutputs", "Unitful"] diff --git a/ext/FreeTypeExt.jl b/ext/FreeTypeExt.jl index edbcace5..6cb1c259 100644 --- a/ext/FreeTypeExt.jl +++ b/ext/FreeTypeExt.jl @@ -88,23 +88,6 @@ fallback_fonts() = const FT_FONTS = Dict{String,FTFont}() -function UnicodePlots.get_font_face(font = nothing, fallback = fallback_fonts()) - face = nothing - for name ∈ filter(!isnothing, (font, "JuliaMono", fallback...)) - if (face = get(FT_FONTS, name, nothing)) ≡ nothing - if (ft = find_font(name)) ≢ nothing - face = FT_FONTS[name] = ft - break # found new font, cache and return it - end - else - break # found in cache - end - end - face -end - -UnicodePlots.save_image(args...; kw...) = FileIO.save(args...; kw...) - """ Match a font using the user-specified search string. Each part of the search string is searched in the family name first which has to match once to include the font @@ -492,4 +475,21 @@ function __init__() nothing end +function UnicodePlots.get_font_face(font = nothing, fallback = fallback_fonts()) + face = nothing + for name ∈ filter(!isnothing, (font, "JuliaMono", fallback...)) + if (face = get(FT_FONTS, name, nothing)) ≡ nothing + if (ft = find_font(name)) ≢ nothing + face = FT_FONTS[name] = ft + break # found new font, cache and return it + end + else + break # found in cache + end + end + face end + +UnicodePlots.save_image(args...; kw...) = FileIO.save(args...; kw...) + +end # module diff --git a/ext/ImageInTerminalExt.jl b/ext/ImageInTerminalExt.jl index 01f72755..2ce73e87 100644 --- a/ext/ImageInTerminalExt.jl +++ b/ext/ImageInTerminalExt.jl @@ -27,4 +27,4 @@ end UnicodePlots.imageplot(img::AbstractArray{<:Colorant}; kw...) = UnicodePlots.Plot(UnicodePlots.ImageGraphics(img); border = :corners, kw...) -end +end # module diff --git a/ext/UnitfulExt.jl b/ext/UnitfulExt.jl new file mode 100644 index 00000000..164a1f3e --- /dev/null +++ b/ext/UnitfulExt.jl @@ -0,0 +1,84 @@ +module UnitfulExt + +import UnicodePlots +import UnicodePlots: KEYWORDS, Plot, Canvas +@static if isdefined(Base, :get_extension) + import Unitful: Quantity, RealOrRealQuantity, ustrip, unit +else + import ..Unitful: Quantity, RealOrRealQuantity, ustrip, unit +end + +function unit_str(x, fancy) + io = IOContext(PipeBuffer(), :fancy_exponent => fancy) + show(io, unit(x)) + read(io, String) +end + +unit_label(label::AbstractString, unit::AbstractString) = + (lab_strip = rstrip(label)) |> isempty ? unit : "$lab_strip ($unit)" +unit_label(label::AbstractString, unit::Nothing) = rstrip(label) + +number_unit(x::Union{AbstractVector,Number}, args...) = x, nothing +number_unit(x::AbstractVector{<:Quantity}, fancy = true) = + ustrip.(x), unit_str(first(x), fancy) +number_unit(x::Quantity, fancy = true) = ustrip(x), unit_str(x, fancy) + +# ---------------------------------------------------------------------------- # +# lineplot +function UnicodePlots.lineplot( + x::AbstractVector{<:RealOrRealQuantity}, + y::AbstractVector{<:Quantity}; + unicode_exponent::Bool = KEYWORDS.unicode_exponent, + xlabel = KEYWORDS.xlabel, + ylabel = KEYWORDS.ylabel, + kw..., +) + x, ux = number_unit(x, unicode_exponent) + y, uy = number_unit(y, unicode_exponent) + UnicodePlots.lineplot( + ustrip.(x), + ustrip.(y); + xlabel = unit_label(xlabel, ux), + ylabel = unit_label(ylabel, uy), + unicode_exponent, + kw..., + ) +end + +UnicodePlots.lineplot!( + plot::Plot{<:Canvas}, + x::AbstractVector{<:RealOrRealQuantity}, + y::AbstractVector{<:Quantity}; + kw..., +) = UnicodePlots.lineplot!(plot, ustrip.(x), ustrip.(y); kw...) + +# ---------------------------------------------------------------------------- # +# scatterplot +function UnicodePlots.scatterplot( + x::AbstractVector{<:RealOrRealQuantity}, + y::AbstractVector{<:Quantity}; + unicode_exponent::Bool = KEYWORDS.unicode_exponent, + xlabel = KEYWORDS.xlabel, + ylabel = KEYWORDS.ylabel, + kw..., +) + x, ux = number_unit(x, unicode_exponent) + y, uy = number_unit(y, unicode_exponent) + UnicodePlots.scatterplot( + ustrip.(x), + ustrip.(y); + xlabel = unit_label(xlabel, ux), + ylabel = unit_label(ylabel, uy), + unicode_exponent, + kw..., + ) +end + +UnicodePlots.scatterplot!( + plot::Plot{<:Canvas}, + x::AbstractVector{<:RealOrRealQuantity}, + y::AbstractVector{<:Quantity}; + kw..., +) = UnicodePlots.scatterplot!(plot, ustrip.(x), ustrip.(y); kw...) + +end # module diff --git a/src/UnicodePlots.jl b/src/UnicodePlots.jl index 0d2533c6..d5ae1e3a 100644 --- a/src/UnicodePlots.jl +++ b/src/UnicodePlots.jl @@ -8,14 +8,12 @@ using Crayons using Printf using Dates -import Unitful: Quantity, RealOrRealQuantity, ustrip, unit import StatsBase: Histogram, fit, percentile, sturges import SparseArrays: AbstractSparseMatrix, findnz import Base: RefValue import MarchingCubes import ColorSchemes -import Requires import NaNMath import Contour @@ -112,7 +110,7 @@ include("interface/boxplot.jl") include("interface/polarplot.jl") include("interface/imageplot.jl") -isdefined(Base, :get_extension) || import Requires +isdefined(Base, :get_extension) || import Requires: @require function __init__() if (terminal_24bit() || forced_24bit()) && !forced_8bit() @@ -123,14 +121,17 @@ function __init__() faintcolors!() end @static if !isdefined(Base, :get_extension) # COV_EXCL_LINE - Requires.@require ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254" include( - "../ext/ImageInTerminalExt.jl", + @require ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254" include( + normpath(@__DIR__, "..", "ext", "ImageInTerminalExt.jl"), ) - Requires.@require FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" begin - Requires.@require FreeType = "b38be410-82b0-50bf-ab77-7b57e271db43" begin - include("../ext/FreeTypeExt.jl") + @require FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" begin + @require FreeType = "b38be410-82b0-50bf-ab77-7b57e271db43" begin + include(normpath(@__DIR__, "..", "ext", "FreeTypeExt.jl")) end end + @require Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" include( + normpath(@__DIR__, "..", "ext", "UnitfulExt.jl"), + ) end nothing end diff --git a/src/common.jl b/src/common.jl index 9b16a8d1..1cd9ccc4 100644 --- a/src/common.jl +++ b/src/common.jl @@ -377,22 +377,6 @@ ceil_base(x, b) = round_base(x, b, RoundUp) round_base(x::T, b, ::RoundingMode{:Down}) where {T} = T(b^floor(log(b, x))) round_base(x::T, b, ::RoundingMode{:Up}) where {T} = T(b^ceil(log(b, x))) -function unit_str(x, fancy) - io = IOContext(PipeBuffer(), :fancy_exponent => fancy) - show(io, unit(x)) - read(io, String) -end - -number_unit(x::AbstractVector{<:Quantity}, fancy = true) = - ustrip.(x), unit_str(first(x), fancy) -number_unit(x::Quantity, fancy = true) = ustrip(x), unit_str(x, fancy) -number_unit(x::AbstractVector, args...) = x, nothing -number_unit(x::Number, args...) = x, nothing - -unit_label(label::AbstractString, unit::AbstractString) = - (lab_strip = rstrip(label)) |> isempty ? unit : "$lab_strip ($unit)" -unit_label(label::AbstractString, unit::Nothing) = rstrip(label) - function superscript(s::AbstractString)::String v = collect(s) for (i, k) ∈ enumerate(v) diff --git a/src/interface/lineplot.jl b/src/interface/lineplot.jl index e8b211d9..038ffee5 100644 --- a/src/interface/lineplot.jl +++ b/src/interface/lineplot.jl @@ -183,35 +183,6 @@ end lineplot!(plot::Plot{<:Canvas}, x::AbstractVector{<:TimeType}, y::AbstractVector; kw...) = lineplot!(plot, Dates.value.(x), y; kw...) -# ---------------------------------------------------------------------------- # -# Unitful -function lineplot( - x::AbstractVector{<:RealOrRealQuantity}, - y::AbstractVector{<:Quantity}; - unicode_exponent::Bool = KEYWORDS.unicode_exponent, - xlabel = KEYWORDS.xlabel, - ylabel = KEYWORDS.ylabel, - kw..., -) - x, ux = number_unit(x, unicode_exponent) - y, uy = number_unit(y, unicode_exponent) - lineplot( - ustrip.(x), - ustrip.(y); - xlabel = unit_label(xlabel, ux), - ylabel = unit_label(ylabel, uy), - unicode_exponent, - kw..., - ) -end - -lineplot!( - plot::Plot{<:Canvas}, - x::AbstractVector{<:RealOrRealQuantity}, - y::AbstractVector{<:Quantity}; - kw..., -) = lineplot!(plot, ustrip.(x), ustrip.(y); kw...) - # ---------------------------------------------------------------------------- # # slope and intercept function lineplot!(plot::Plot{<:Canvas}, intercept::Number, slope::Number; kw...) diff --git a/src/interface/scatterplot.jl b/src/interface/scatterplot.jl index 97c61575..03ab6286 100644 --- a/src/interface/scatterplot.jl +++ b/src/interface/scatterplot.jl @@ -132,32 +132,3 @@ function scatterplot!(plot::Plot{<:Canvas}, x::AbstractVector, y::AbstractMatrix end plot end - -# ---------------------------------------------------------------------------- # -# Unitful -function scatterplot( - x::AbstractVector{<:RealOrRealQuantity}, - y::AbstractVector{<:Quantity}; - unicode_exponent::Bool = KEYWORDS.unicode_exponent, - xlabel = KEYWORDS.xlabel, - ylabel = KEYWORDS.ylabel, - kw..., -) - x, ux = number_unit(x, unicode_exponent) - y, uy = number_unit(y, unicode_exponent) - scatterplot( - ustrip.(x), - ustrip.(y); - xlabel = unit_label(xlabel, ux), - ylabel = unit_label(ylabel, uy), - unicode_exponent, - kw..., - ) -end - -scatterplot!( - plot::Plot{<:Canvas}, - x::AbstractVector{<:RealOrRealQuantity}, - y::AbstractVector{<:Quantity}; - kw..., -) = scatterplot!(plot, ustrip.(x), ustrip.(y); kw...) diff --git a/test/runtests.jl b/test/runtests.jl index d6a0071b..d8dc0f8a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,4 +1,4 @@ -using ImageInTerminal, FreeType, FileIO # weak deps, or @require +using ImageInTerminal, FreeType, FileIO, Unitful # weak deps, or @require using UnicodePlots, Test import UnicodePlots: lines!, points!, pixel!, nrows, ncols diff --git a/test/tst_common.jl b/test/tst_common.jl index e0fc6382..c7fd257f 100644 --- a/test/tst_common.jl +++ b/test/tst_common.jl @@ -285,14 +285,20 @@ end @test UnicodePlots.keywords() isa String end +const UE = if isdefined(Base, :get_extension) + Base.get_extension(UnicodePlots, :UnitfulExt) +else + UnicodePlots.UnitfulExt +end + @testset "units" begin x = [1.0, 2.0, 3.0] - @test UnicodePlots.number_unit(x) == (x, nothing) - @test UnicodePlots.number_unit(1) == (1, nothing) - @test UnicodePlots.number_unit(x * u"°C") == (x, "°C") - @test UnicodePlots.number_unit(1u"°C") == (1, "°C") - - @test UnicodePlots.unit_label(" fancy label ", "Hz") == " fancy label (Hz)" - @test UnicodePlots.unit_label(" ", "dB") == "dB" - @test UnicodePlots.unit_label(" no units ", nothing) == " no units" + @test UE.number_unit(x) == (x, nothing) + @test UE.number_unit(1) == (1, nothing) + @test UE.number_unit(x * u"°C") == (x, "°C") + @test UE.number_unit(1u"°C") == (1, "°C") + + @test UE.unit_label(" fancy label ", "Hz") == " fancy label (Hz)" + @test UE.unit_label(" ", "dB") == "dB" + @test UE.unit_label(" no units ", nothing) == " no units" end diff --git a/test/tst_quality.jl b/test/tst_quality.jl index c4b1616a..3bd7b010 100644 --- a/test/tst_quality.jl +++ b/test/tst_quality.jl @@ -1,5 +1,10 @@ @testset "Aqua" begin # JuliaTesting/Aqua.jl/issues/77 - Aqua.test_all(UnicodePlots; ambiguities = false, project_toml_formatting = false) + Aqua.test_all( + UnicodePlots; + ambiguities = false, + project_toml_formatting = false, # issues since weak deps + stale_deps = !isdefined(Base, :get_extension), # issue with `Requires` not used when weak deps are enabled + ) Aqua.test_ambiguities(UnicodePlots) end