From 898d730638181ae5b08e86eadd6dcc57760690b0 Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Sat, 2 Jul 2022 19:17:33 -0300 Subject: [PATCH 01/33] Use PrettyTables.jl as HTML backend --- src/abstractdataframe/io.jl | 218 ++++++++++++-------------- src/abstractdataframe/prettytables.jl | 3 + 2 files changed, 106 insertions(+), 115 deletions(-) diff --git a/src/abstractdataframe/io.jl b/src/abstractdataframe/io.jl index d997b3c05b..9cc48d92e1 100755 --- a/src/abstractdataframe/io.jl +++ b/src/abstractdataframe/io.jl @@ -144,15 +144,6 @@ Base.show(io::IO, mime::MIME"text/plain", df::AbstractDataFrame; kwargs...) = # ############################################################################## -function digitsep(value::Integer) - # Adapted from https://github.com/IainNZ/Humanize.jl - value = string(abs(value)) - group_ends = reverse(collect(length(value):-3:1)) - groups = [value[max(end_index - 2, 1):end_index] - for end_index in group_ends] - return join(groups, ',') -end - function html_escape(cell::AbstractString) cell = replace(cell, "&"=>"&") cell = replace(cell, "<"=>"<") @@ -164,128 +155,131 @@ function html_escape(cell::AbstractString) return cell end -function _show(io::IO, ::MIME"text/html", df::AbstractDataFrame; - summary::Bool=true, eltypes::Bool=true, rowid::Union{Int, Nothing}=nothing) +function _show(io::IO, + ::MIME"text/html", + df::AbstractDataFrame; + summary::Bool=true, + eltypes::Bool=true, + rowid::Union{Int, Nothing}=nothing, + title::String="", + kwargs...) _check_consistency(df) - # we will pass around this buffer to avoid its reallocation in ourstrwidth - buffer = IOBuffer(Vector{UInt8}(undef, 80), read=true, write=true) + names_str = names(df) + types = Any[eltype(c) for c in eachcol(df)] + types_str = batch_compacttype(types, 9) + types_str_complete = batch_compacttype(types, 256) - if rowid !== nothing - if size(df, 2) == 0 - rowid = nothing - elseif size(df, 1) != 1 - throw(ArgumentError("rowid may be passed only with a single row data frame")) + # For consistency, if `kwargs` has `compact_printng`, we must use it. + compact_printing::Bool = get(kwargs, :compact_printing, get(io, :compact, true)) + + num_rows, num_cols = size(df) + + # By default, we align the columns to the left unless they are numbers, + # which is checked in the following. + alignment = fill(:l, num_cols) + + for i = 1:num_cols + type_i = nonmissingtype(types[i]) + + if type_i <: Number + alignment[i] = :r end end - mxrow, mxcol = size(df) + mxrow, mxcol = num_rows, num_cols + if get(io, :limit, false) + buffer = IOBuffer(Vector{UInt8}(undef, 80), read=true, write=true) tty_rows, tty_cols = displaysize(io) mxrow = min(mxrow, tty_rows) maxwidths = getmaxwidths(df, io, 1:mxrow, 0:-1, :X, nothing, true, buffer, 0) .+ 2 mxcol = min(mxcol, searchsortedfirst(cumsum(maxwidths), tty_cols)) end - cnames = _names(df)[1:mxcol] - write(io, "
") + # Check if the user wants to display a summary about the DataFrame that is + # being printed. This will be shown using the `title` option of + # `pretty_table`. if summary - write(io, "

$(digitsep(nrow(df))) rows × $(digitsep(ncol(df))) columns") - if mxcol < size(df, 2) - write(io, " (omitted printing of $(size(df, 2)-mxcol) columns)") + if isempty(title) + title = Base.summary(df) end - write(io, "

") - end - write(io, "") - write(io, "") - write(io, "") - write(io, "") - for column_name in cnames - write(io, "") - end - write(io, "") - if eltypes - write(io, "") - write(io, "") - # We put a longer string for the type into the title argument of the ") - end - write(io, "") + else + title = "" end - write(io, "") - write(io, "") - for row in 1:mxrow - write(io, "") - if rowid === nothing - write(io, "") + + # If `rowid` is not `nothing`, then we are printing a data row. In this + # case, we will add this information using the row name column of + # PrettyTables.jl. Otherwise, we can just use the row number column. + if (rowid === nothing) || (ncol(df) == 0) + show_row_number::Bool = get(kwargs, :show_row_number, true) + row_names = nothing + + # If the columns with row numbers is not shown, then we should not + # display a vertical line after the first column. + vlines = fill(1, show_row_number) + else + nrow(df) != 1 && + throw(ArgumentError("rowid may be passed only with a single row data frame")) + + # In this case, if the user does not want to show the row number, then + # we must hide the row name column, which is used to display the + # `rowid`. + if !get(kwargs, :show_row_number, true) + row_names = nothing + vlines = Int[] else - write(io, "") - end - for column_name in cnames - if isassigned(df[!, column_name], row) - cell_val = df[row, column_name] - if ismissing(cell_val) - write(io, "") - elseif cell_val isa Markdown.MD - write(io, "") - elseif cell_val isa SHOW_TABULAR_TYPES - write(io, "") - else - cell = sprint(ourshow, cell_val, 0) - write(io, "") - end - else - write(io, "") - end + row_names = [string(rowid)] + vlines = Int[1] end - write(io, "") - end - if size(df, 1) > mxrow - write(io, "") - write(io, "") - for column_name in cnames - write(io, "") - end - write(io, "") + + show_row_number = false end - write(io, "") - write(io, "
$(html_escape(String(column_name)))
element, - # which the users can hover over. The limit of 256 characters is arbitrary, but - # we want some maximum limit, since the types can sometimes get really-really long. - types = Any[eltype(df[!, idx]) for idx in 1:mxcol] - ct, ct_title = batch_compacttype(types, 9), batch_compacttype(types, 256) - for j in 1:mxcol - s = html_escape(ct[j]) - title = html_escape(ct_title[j]) - write(io, "$s
$row$rowidmissing") - show(io, "text/html", cell_val) - write(io, "") - cell = sprint(ourshow, cell_val, 0) - write(io, html_escape(cell)) - write(io, "$(html_escape(cell))#undef
") - write(io, "
") + + pretty_table(io, df; + alignment = alignment, + backend = Val(:html), + compact_printing = compact_printing, + formatters = (_pretty_tables_general_formatter,), + header = (names_str, types_str), + header_alignment = :l, + header_cell_titles = (nothing, types_str_complete), + highlighters = (_PRETTY_TABLES_HTML_HIGHLIGHTER,), + max_num_of_columns = mxcol, + max_num_of_rows = mxrow, + minify = true, + nosubheader = !eltypes, + row_name_column_title = "Row", + row_names = row_names, + row_number_alignment = :r, + row_number_column_title = "Row", + show_omitted_cell_summary = true, + show_row_number = show_row_number, + standalone = false, + top_left_str = title, + top_right_str_decoration = HtmlDecoration(font_style = "italic"), + vcrop_mode = :middle, + kwargs...) + + return nothing end -function Base.show(io::IO, mime::MIME"text/html", dfr::DataFrameRow; - summary::Bool=true, eltypes::Bool=true) +function Base.show(io::IO, mime::MIME"text/html", dfr::DataFrameRow; kwargs...) r, c = parentindices(dfr) - summary && write(io, "

DataFrameRow ($(length(dfr)) columns)

") - _show(io, mime, view(parent(dfr), [r], c), summary=false, eltypes=eltypes, rowid=r) + title = "DataFrameRow ($(length(dfr)) columns)" + _show(io, mime, view(parent(dfr), [r], c); rowid=r, title=title, kwargs...) end -function Base.show(io::IO, mime::MIME"text/html", dfrs::DataFrameRows; - summary::Bool=true, eltypes::Bool=true) +function Base.show(io::IO, mime::MIME"text/html", dfrs::DataFrameRows; kwargs...) df = parent(dfrs) - summary && write(io, "

$(nrow(df))×$(ncol(df)) DataFrameRows

") - _show(io, mime, df, summary=false, eltypes=eltypes) + title = "$(nrow(df))×$(ncol(df)) DataFrameRows" + _show(io, mime, df; title=title, kwargs...) end -function Base.show(io::IO, mime::MIME"text/html", dfcs::DataFrameColumns; - summary::Bool=true, eltypes::Bool=true) +function Base.show(io::IO, mime::MIME"text/html", dfcs::DataFrameColumns; kwargs...) df = parent(dfcs) - if summary - write(io, "

$(nrow(df))×$(ncol(df)) DataFrameColumns

") - end - _show(io, mime, df, summary=false, eltypes=eltypes) + title = "$(nrow(df))×$(ncol(df)) DataFrameColumns" + _show(io, mime, df; title=title, kwargs...) end function Base.show(io::IO, mime::MIME"text/html", gd::GroupedDataFrame) @@ -298,28 +292,22 @@ function Base.show(io::IO, mime::MIME"text/html", gd::GroupedDataFrame) nrows = size(gd[1], 1) rows = nrows > 1 ? "rows" : "row" - identified_groups = [html_escape(string(col, " = ", - repr(first(gd[1][!, col])))) + identified_groups = [string(col, " = ", repr(first(gd[1][!, col]))) for col in gd.cols] - write(io, "

First Group ($nrows $rows): ") - join(io, identified_groups, ", ") - write(io, "

") - show(io, mime, gd[1], summary=false) + title = "First Group ($nrows $rows): " * join(identified_groups, ", ") + _show(io, mime, gd[1], title=title) end if N > 1 nrows = size(gd[N], 1) rows = nrows > 1 ? "rows" : "row" - identified_groups = [html_escape(string(col, " = ", - repr(first(gd[N][!, col])))) + identified_groups = [string(col, " = ", repr(first(gd[N][!, col]))) for col in gd.cols] write(io, "

") - write(io, "

Last Group ($nrows $rows): ") - join(io, identified_groups, ", ") - write(io, "

") - show(io, mime, gd[N], summary=false) + title = "Last Group ($nrows $rows): " * join(identified_groups, ", ") + _show(io, mime, gd[N], title=title) end end diff --git a/src/abstractdataframe/prettytables.jl b/src/abstractdataframe/prettytables.jl index 0779eec137..032a963bdb 100644 --- a/src/abstractdataframe/prettytables.jl +++ b/src/abstractdataframe/prettytables.jl @@ -28,6 +28,9 @@ end const _PRETTY_TABLES_HIGHLIGHTER = Highlighter(_pretty_tables_highlighter_func, Crayon(foreground = :dark_gray)) +const _PRETTY_TABLES_HTML_HIGHLIGHTER = HtmlHighlighter(_pretty_tables_highlighter_func, + HtmlDecoration(font_style = "italic")) + # Default DataFrames formatter for text backend. # # This formatter changes how the following types are presented when rendering From 11e3c2096dfe492c91bbdbc334fbb34741239e77 Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Sun, 3 Jul 2022 14:25:13 -0300 Subject: [PATCH 02/33] Limit the num of rows and cols based on ENV vars --- src/abstractdataframe/io.jl | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/abstractdataframe/io.jl b/src/abstractdataframe/io.jl index 9cc48d92e1..85e2ca8f1d 100755 --- a/src/abstractdataframe/io.jl +++ b/src/abstractdataframe/io.jl @@ -187,14 +187,23 @@ function _show(io::IO, end end - mxrow, mxcol = num_rows, num_cols - if get(io, :limit, false) - buffer = IOBuffer(Vector{UInt8}(undef, 80), read=true, write=true) - tty_rows, tty_cols = displaysize(io) - mxrow = min(mxrow, tty_rows) - maxwidths = getmaxwidths(df, io, 1:mxrow, 0:-1, :X, nothing, true, buffer, 0) .+ 2 - mxcol = min(mxcol, searchsortedfirst(cumsum(maxwidths), tty_cols)) + # Obtain the maximum number of rows and columns that we can print from + # environments variables. + mxrow = tryparse(Int, get(ENV, "DATAFRAMES_ROWS", "100")) + + if isnothing(mxrow) + mxrow = 100 + end + + mxcol = tryparse(Int, get(ENV, "DATAFRAMES_COLUMNS", "500")) + + if isnothing(mxcol) + mxcol = 500 + end + else + mxrow = -1 + mxcol = -1 end # Check if the user wants to display a summary about the DataFrame that is From e7185fa4dedce0db04df39cfbb64372067f559a4 Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Sun, 3 Jul 2022 14:29:02 -0300 Subject: [PATCH 03/33] Wrap table inside a div --- src/abstractdataframe/io.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/abstractdataframe/io.jl b/src/abstractdataframe/io.jl index 85e2ca8f1d..30a656f7c1 100755 --- a/src/abstractdataframe/io.jl +++ b/src/abstractdataframe/io.jl @@ -265,9 +265,12 @@ function _show(io::IO, show_omitted_cell_summary = true, show_row_number = show_row_number, standalone = false, + table_class = "data-frame", + table_div_class = "data-frame", top_left_str = title, top_right_str_decoration = HtmlDecoration(font_style = "italic"), vcrop_mode = :middle, + wrap_table_in_div = true, kwargs...) return nothing From 4129e96404596d9c5d537ea9bb53a07c1fcf0240 Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Sat, 20 Aug 2022 11:07:41 -0300 Subject: [PATCH 04/33] Redirect kwargs to PrettyTables.jl --- src/abstractdataframe/io.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/abstractdataframe/io.jl b/src/abstractdataframe/io.jl index 30a656f7c1..c5ea0b80c4 100755 --- a/src/abstractdataframe/io.jl +++ b/src/abstractdataframe/io.jl @@ -127,8 +127,8 @@ julia> show(stdout, MIME("text/csv"), DataFrame(A=1:3, B=["x", "y", "z"])) """ Base.show(io::IO, mime::MIME, df::AbstractDataFrame) Base.show(io::IO, mime::MIME"text/html", df::AbstractDataFrame; - summary::Bool=true, eltypes::Bool=true) = - _show(io, mime, df, summary=summary, eltypes=eltypes) + summary::Bool=true, eltypes::Bool=true, kwargs...) = + _show(io, mime, df; summary=summary, eltypes=eltypes, kwargs...) Base.show(io::IO, mime::MIME"text/latex", df::AbstractDataFrame; eltypes::Bool=true) = _show(io, mime, df, eltypes=eltypes) Base.show(io::IO, mime::MIME"text/csv", df::AbstractDataFrame) = From 6688343236efa0dcd8d19164fe32da0cd424813c Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Sat, 20 Aug 2022 19:01:57 -0300 Subject: [PATCH 05/33] Update tests related to HTML backend --- test/dataframerow.jl | 67 ++- test/grouping.jl | 125 ++++- test/io.jl | 1104 +++++++++++++++++++++++++++++++++++------- 3 files changed, 1085 insertions(+), 211 deletions(-) diff --git a/test/dataframerow.jl b/test/dataframerow.jl index 978e4fd222..b6dc705178 100644 --- a/test/dataframerow.jl +++ b/test/dataframerow.jl @@ -431,12 +431,38 @@ end str2 = String(take!(io.io)) @test str1 == str2 - @test sprint(show, "text/html", dfr) == "

DataFrameRow (2 columns)

" * - "
" * - "" * - "" * - "" * - "
bc
StringInt64
2b0
" + str = sprint(show, "text/html", dfr) + @test str == "
" * + "
" * + "DataFrameRow (2 columns)" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
Rowbc
" * + "StringInt64
2b0
" * + "
" @test sprint(show, "text/latex", dfr) == """ \\begin{tabular}{r|cc} @@ -479,10 +505,31 @@ end io = IOBuffer() show(io, MIME("text/html"), dfr, eltypes=false) str = String(take!(io)) - @test str == "

DataFrameRow (2 columns)

" * - "
" * - "" * - "
bc
2b0
" + @test str == "
" * + "
" * + "DataFrameRow (2 columns)" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
Rowbc
2b0
" * + "
" io = IOBuffer() show(io, MIME("text/latex"), dfr, eltypes=false) diff --git a/test/grouping.jl b/test/grouping.jl index b608f31c46..56c1a448a9 100644 --- a/test/grouping.jl +++ b/test/grouping.jl @@ -1470,17 +1470,79 @@ end str2 = String(take!(io.io)) @test str1 == str2 - - @test sprint(show, "text/html", gd) == - "

GroupedDataFrame with 4 groups based on key: A

" * - "

First Group (1 row): A = 1

" * - "" * - "" * - "" * - "
ABC
Int64StringFloat32
11x"1.0

Last Group (1 row): A = 4

" * - "
" * - "" * - "
ABC
Int64StringFloat32
14A\\nC4.0
" + str = sprint(show, "text/html", gd) + @test str == "

" * + "GroupedDataFrame with 4 groups based on key: A" * + "

" * + "
" * + "
" * + "First Group (1 row): A = 1" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
RowABC
" * + "Int64StringFloat32
11x"1.0
" * + "
" * + "

" * + "
" * + "
" * + "Last Group (1 row): A = 4" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
RowABC
" * + "Int64StringFloat32
14A\\nC4.0
" * + "
" @test sprint(show, "text/latex", gd) == """ GroupedDataFrame with 4 groups based on key: A @@ -1519,12 +1581,41 @@ end ─────┼──────────────── 1 │ & &""" - @test sprint(show, "text/html", gd) == - "

$summary_str

" * - "First Group (1 row): a = :&, b = "&"

" * - "
" * - "" * - "
ab
SymbolString
1&&
" + str = sprint(show, "text/html", gd) + @test str == "

" * + "GroupedDataFrame with 1 group based on keys: a, b" * + "

" * + "
" * + "
" * + "First Group (1 row): a = :&, b = "&"" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
Rowab
" * + "SymbolString
1&&
" * + "
" @test sprint(show, "text/latex", gd) == """ $summary_str diff --git a/test/io.jl b/test/io.jl index 0cf304f19b..21dce80bd8 100644 --- a/test/io.jl +++ b/test/io.jl @@ -56,101 +56,369 @@ end io = IOBuffer() show(io, "text/html", df) str = String(take!(io)) - @test str == "
" * - "

2 rows × 2 columns

" * - "" * - "" * - "" * - "" * - "" * - "" * - "
FishMass
StringFloat64?
1Suzy1.5
2Amirmissing
" + @test str == "
" * + "
" * + "2×2 DataFrame" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
RowFishMass
" * + "StringFloat64?
1Suzy1.5
2Amirmissing
" * + "
" df = DataFrame(Fish=Vector{String}(undef, 2), Mass=[1.5, missing]) io = IOBuffer() show(io, "text/html", df) str = String(take!(io)) - @test str == "
" * - "

2 rows × 2 columns

" * - "" * - "" * - "" * - "" * - "" * - "" * - "
FishMass
StringFloat64?
1#undef1.5
2#undefmissing
" + @test str == "
" * + "
" * + "2×2 DataFrame" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
RowFishMass
" * + "StringFloat64?
1#undef1.5
2#undefmissing
" * + "
" io = IOBuffer() show(io, "text/html", eachrow(df)) str = String(take!(io)) - @test str == "

2×2 DataFrameRows

" * - "
" * - "" * - "" * - "
" * - "FishMass
StringFloat64?
1#undef1.5
2#undefmissing
" + @test str == "
" * + "
" * + "2×2 DataFrameRows" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
RowFishMass
" * + "StringFloat64?
1#undef1.5
2#undefmissing
" * + "
" io = IOBuffer() show(io, "text/html", eachcol(df)) str = String(take!(io)) - @test str == "

2×2 DataFrameColumns

" * - "
" * - "" * - "" * - "
" * - "FishMass
StringFloat64?
1#undef1.5
2#undefmissing
" + @test str == "
" * + "
" * + "2×2 DataFrameColumns" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
RowFishMass
" * + "StringFloat64?
1#undef1.5
2#undefmissing
" * + "
" io = IOBuffer() show(io, "text/html", df[1, :]) str = String(take!(io)) - @test str == "

DataFrameRow (2 columns)

" * - "
" * - "" * - "" * - "
FishMass
StringFloat64?
1#undef1.5
" + @test str == "
" * + "
" * + "DataFrameRow (2 columns)" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
RowFishMass
" * + "StringFloat64?
1#undef1.5
" * + "
" io = IOBuffer() show(io, MIME"text/html"(), df, summary=false) str = String(take!(io)) - @test str == "
" * - "" * - "" * - "
" * - "FishMass
StringFloat64?
1#undef1.5
2#undefmissing
" + @test str == "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
RowFishMass
" * + "StringFloat64?
1#undef1.5
2#undefmissing
" * + "
" io = IOBuffer() show(io, MIME"text/html"(), eachrow(df), summary=false) str = String(take!(io)) - @test str == "
" * - "" * - "" * - "
" * - "FishMass
StringFloat64?
1#undef1.5
2#undefmissing
" + @test str == "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
RowFishMass
" * + "StringFloat64?
1#undef1.5
2#undefmissing
" * + "
" io = IOBuffer() show(io, MIME"text/html"(), eachcol(df), summary=false) str = String(take!(io)) - @test str == "
" * - "" * - "" * - "
" * - "FishMass
StringFloat64?
1#undef1.5
2#undefmissing
" + @test str == "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
RowFishMass
" * + "StringFloat64?
1#undef1.5
2#undefmissing
" * + "
" io = IOBuffer() show(io, MIME"text/html"(), df[1, :], summary=false) str = String(take!(io)) - @test str == "
" * - "" * - "
FishMass
StringFloat64?
1#undef1.5
" + @test str == "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
RowFishMass
" * + "StringFloat64?
1#undef1.5
" * + "
" io = IOBuffer() - show(IOContext(io, :limit => true, :displaysize => (10, 10)), MIME"text/html"(), + ENV["DATAFRAMES_COLUMNS"] = 2 + ENV["DATAFRAMES_ROWS"] = 2 + show(IOContext(io, :limit => true), MIME"text/html"(), DataFrame(Int64[1 2 3 4 5 6 7 8 9], :auto)) str = String(take!(io)) - @test str == "

1 rows × 9 columns (omitted printing of 7 columns)

" * - "" * - "" * - "
x1x2
Int64Int64
112
" + @test str == "
" * + "
" * + "1×9 DataFrame" * + "
" * + "
" * + "7 columns omitted" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
Rowx1x2
" * + "Int64Int64
112
" * + "
" @test_throws ArgumentError DataFrames._show(stdout, MIME("text/html"), DataFrame(ones(2,2), :auto), rowid=10) @@ -164,16 +432,73 @@ end md"*A*b**A**" ] ) - @test repr(MIME("text/html"), df) == - "

4 rows × 2 columns

" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "
AB
Int64MD
11
24

###A

\n
39

$\\frac{A}{B}$

\n
416

AbA

\n
" + str = repr(MIME("text/html"), df) + @test str == "
" * + "
" * + "4×2 DataFrame" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
RowAB
" * + "Int64MD
11" * + "
" * + "

" * + "DataFrames.jl" * + "

" * + "
" * + "
24" * + "
" * + "

###A

" * + "
" * + "
39" * + "
" * + "

$\\frac{A}{B}$

" * + "
" * + "
416" * + "
" * + "

" * + "AbA" * + "

" * + "
" * + "
" * + "
" # Test that single and double quotes get escaped properly df = DataFrame( @@ -184,38 +509,52 @@ end io = IOBuffer() show(io, "text/html", df) str = String(take!(io)) - @test str == - "

3 rows × 3 columns

" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "
xsyszs
StringAnyQuoteTes…
1'QuoteTestType{'\\\\''}()QuoteTestType{'"'}()
2"QuoteTestType{'"'}QuoteTestType{'"'}()
3<foo>'</bar>QuoteTestType{Symbol("\\\\"'")}()QuoteTestType{'"'}()
" + @test str == "
" * + "
" * + "3×3 DataFrame" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
Rowxsyszs
" * + "StringAnyQuoteTes…
1'QuoteTestType{'\\\\''}()QuoteTestType{'"'}()
2"QuoteTestType{'"'}QuoteTestType{'"'}()
3<foo>'</bar>QuoteTestType{Symbol("\\\\"'")}()QuoteTestType{'"'}()
" * + "
" end # test limit attribute of IOContext is used @@ -335,33 +674,126 @@ end " * γ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0"), ] ) - @test sprint(show,"text/html",df) == - "

8 rows × 2 columns

" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "
AB
Int64MD
11
" * - "

DataFrames.jl

\n
24

$\\frac{x^2}{x^2+y^2}$

\n
39

Header

\n
416
" * - "

This is very, very, very, very, very, very, very, very, very long line

\n" * - "
525
636
" * - "

∫αγ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0∫αγ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0

\n" * - "
749
" * - "

∫αγ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ

\n
    \n
  • ∞7∫αγ

    \n
  • \n
  • ∞8∫αγ

    \n
  • \n
  • ∞9∫αγ∞0∫α

    \n
  • \n
\n

γ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0

\n" * - "
864
" * - "

∫αγ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0∫α

" * - "\n
    \n" * - "
  • γ∞1∫α

    \n
  • \n" * - "
  • γ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0

    \n
  • \n" * - "
\n" * "
" + str = sprint(show,"text/html",df) + @test str == "
" * + "
" * + "8×2 DataFrame" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
RowAB
" * + "Int64MD
11" * + "
" * + "

" * + "DataFrames.jl" * + "

" * + "
" * + "
24" * + "
" * + "

$\\frac{x^2}{x^2+y^2}$

" * + "
" * + "
39" * + "
" * + "

Header

" * + "
" * + "
416" * + "
" * + "

This is very, very, very, very, very, very, very, very, very long line

" * + "
" * + "
525" * + "
" * + "
" * + "
636" * + "
" * + "

∫αγ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0∫αγ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0

" * + "
" * + "
749" * + "
" * + "

∫αγ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ

" * + "
    " * + "
  • " * + "

    ∞7∫αγ

    " * + "
  • " * + "
  • " * + "

    ∞8∫αγ

    " * + "
  • " * + "
  • " * + "

    ∞9∫αγ∞0∫α

    " * + "
  • " * + "
" * + "

γ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0

" * + "
" * + "
864" * + "
" * + "

∫αγ∞1∫αγ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0∫α

" * + "
    " * + "
  • " * + "

    γ∞1∫α

    " * + "
  • " * + "
  • " * + "

    γ∞2∫αγ∞3∫αγ∞4∫αγ∞5∫αγ∞6∫αγ∞7∫αγ∞8∫αγ∞9∫αγ∞0

    " * + "
  • " * + "
" * + "
" * + "
" * + "
" end @testset "empty data frame and DataFrameRow" begin @@ -370,26 +802,51 @@ end @test sprint(show, "text/csv", df[:, 2:1]) == "" @test sprint(show, "text/tab-separated-values", df[:, 2:1]) == "" @test sprint(show, "text/html", df[:, 2:1]) == - "

0 rows × 0 columns

" * - "" * - "
" + "
" * + "
" * + "0×0 DataFrame" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "
" * + "
" @test sprint(show, "text/latex", df[:, 2:1]) == "\\begin{tabular}{r|}\n\t& \\\\\n\t\\hline\n\t& \\\\\n\t\\hline\n\\end{tabular}\n" @test sprint(show, "text/csv", @view df[:, 2:1]) == "" @test sprint(show, "text/tab-separated-values", @view df[:, 2:1]) == "" @test sprint(show, "text/html", @view df[:, 2:1]) == - "

0 rows × 0 columns

" * - "" * - "
" + "
" * + "
" * + "0×0 SubDataFrame" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "
" * + "
" @test sprint(show, "text/latex", @view df[:, 2:1]) == "\\begin{tabular}{r|}\n\t& \\\\\n\t\\hline\n\t& \\\\\n\t\\hline\n\\end{tabular}\n" @test sprint(show, "text/csv", df[1, 2:1]) == "" @test sprint(show, "text/tab-separated-values", df[1, 2:1]) == "" @test sprint(show, "text/html", df[1, 2:1]) == - "

DataFrameRow (0 columns)

" * - "
" + "
" * + "
" * + "DataFrameRow (0 columns)" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "
" * + "
" @test sprint(show, "text/latex", df[1, 2:1]) == "\\begin{tabular}{r|}\n\t& \\\\\n\t\\hline\n\t& \\\\\n\t\\hline\n\\end{tabular}\n" end @@ -503,55 +960,254 @@ end io = IOBuffer() show(io, MIME("text/html"), df, eltypes=true) str = String(take!(io)) - @test str == "

3 rows × 2 columns

" * - "" * - "" * - "" * - "
" * - "AB
Int32String
11x
22y
33z
" + @test str == "
" * + "
" * + "3×2 DataFrame" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
RowAB
" * + "Int32String
11x
22y
33z
" * + "
" io = IOBuffer() show(io, MIME("text/html"), eachcol(df), eltypes=true) str = String(take!(io)) - @test str == "

3×2 DataFrameColumns

" * - "" * - "" * - "
" * - "AB
Int32String
11x
22y
33z
" + @test str == "
" * + "
" * + "3×2 DataFrameColumns" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
RowAB
" * + "Int32String
11x
22y
33z
" * + "
" io = IOBuffer() show(io, MIME("text/html"), eachrow(df), eltypes=true) str = String(take!(io)) - @test str == "

3×2 DataFrameRows

" * - "" * - "" * - "
" * - "AB
Int32String
11x
22y
33z
" + @test str == "
" * + "
" * + "3×2 DataFrameRows" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
RowAB
" * + "Int32String
11x
22y
33z
" * + "
" io = IOBuffer() show(io, MIME("text/html"), df, eltypes=false) str = String(take!(io)) - @test str == "

3 rows × 2 columns

" * - "" * - "" * - "
" * - "AB
11x
22y
33z
" + @test str == "
" * + "
" * + "3×2 DataFrame" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
RowAB
11x
22y
33z
" * + "
" io = IOBuffer() show(io, MIME("text/html"), eachcol(df), eltypes=false) str = String(take!(io)) - @test str == "

3×2 DataFrameColumns

" * - "" * - "
" * - "AB
11x
22y
33z
" + @test str == "
" * + "
" * + "3×2 DataFrameColumns" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
RowAB
11x
22y
33z
" * + "
" io = IOBuffer() show(io, MIME("text/html"), eachrow(df), eltypes=false) str = String(take!(io)) - @test str == "

3×2 DataFrameRows

" * - "" * - "
" * - "AB
11x
22y
33z
" + @test str == "
" * + "
" * + "3×2 DataFrameRows" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
RowAB
11x
22y
33z
" * + "
" for x in [df, eachcol(df), eachrow(df)] io = IOBuffer() @@ -622,7 +1278,7 @@ end @test str == """ \e[1m9×2 DataFrame\e[0m \e[1m Row \e[0m│\e[1m A \e[0m\e[1m B \e[0m - \e[1m \e[0m│\e[90m Int64 \e[0m\e[90m Any \e[0m + │\e[90m Int64 \e[0m\e[90m Any \e[0m ─────┼────────────────────────────────────────── 1 │ 1 \e[90m 9×2 DataFrame \e[0m 2 │ 2 \e[90m 2-element DataFrameRow \e[0m @@ -638,19 +1294,78 @@ end io = IOBuffer() show(io, MIME("text/html"), df) str = String(take!(io)) - @test str == "

9 rows × 2 columns

" * - "" * - "" * + @test str == "
" * + "
" * + "9×2 DataFrame" * + "
" * + "
" * + "
" * + "
" * + "
" * + "
AB
Int64Any
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "" * - "
RowAB
" * + "Int64Any
119×2 DataFrame
222-element DataFrameRow
331×2 SubDataFrame
449-element DataFrameRows
552-element DataFrameColumns
66GroupedDataFrame with 9 groups based on key: A
77missing
88
99#undef
" + "" * + "1" * + "1" * + "9×2 DataFrame" * + "" * + "" * + "2" * + "2" * + "2-element DataFrameRow" * + "" * + "" * + "3" * + "3" * + "1×2 SubDataFrame" * + "" * + "" * + "4" * + "4" * + "9-element DataFrameRows" * + "" * + "" * + "5" * + "5" * + "2-element DataFrameColumns" * + "" * + "" * + "6" * + "6" * + "GroupedDataFrame with 9 groups based on key: A" * + "" * + "" * + "7" * + "7" * + "missing" * + "" * + "" * + "8" * + "8" * + "" * + "" * + "" * + "" * + "9" * + "9" * + "#undef" * + "" * + "" * + "" * + "" io = IOBuffer() show(io, MIME("text/latex"), df) @@ -724,13 +1439,34 @@ end io = IOBuffer() show(io, MIME("text/html"), df) str = String(take!(io)) - @test str == "

1 rows × 1 columns

" * - "" * - "" * - "" * - ""* - "
x
String
101234567890123456789012345678901234567890123456789" * - "01234567890123456789012345678901234567890123456789
" + @test str == "
" * + "
" * + "1×1 DataFrame" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
Rowx
" * + "String
10123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
" * + "
" # no truncation io = IOBuffer() From adfe1b883a288e02177586f87b78fc0e5c8f7ba9 Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Sat, 20 Aug 2022 19:02:11 -0300 Subject: [PATCH 06/33] Fix test related to the text backend --- test/show.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/show.jl b/test/show.jl index 77ea77c34d..f0187f6638 100644 --- a/test/show.jl +++ b/test/show.jl @@ -221,7 +221,7 @@ end @test sprint(show, df, context=:color=>true) == """ \e[1m2×2 DataFrame\e[0m \e[1m Row \e[0m│\e[1m Fish \e[0m\e[1m Mass \e[0m - \e[1m \e[0m│\e[90m String \e[0m\e[90m Float64? \e[0m + │\e[90m String \e[0m\e[90m Float64? \e[0m ─────┼─────────────────── 1 │ Suzy 1.5 2 │ Amir \e[90m missing \e[0m""" @@ -232,7 +232,7 @@ end @test sprint(show, df, context=:color=>true) == """ \e[1m3×3 DataFrame\e[0m \e[1m Row \e[0m│\e[1m A \e[0m\e[1m B \e[0m\e[1m C \e[0m - \e[1m \e[0m│\e[90m Symbol? \e[0m\e[90m String? \e[0m\e[90m Any \e[0m + │\e[90m Symbol? \e[0m\e[90m String? \e[0m\e[90m Any \e[0m ─────┼─────────────────────────── 1 │ Symbol \e[90m missing \e[0m missing 2 │\e[90m missing \e[0m String missing From d7d3169f8c894c594b87d986c9aba55984ecaf29 Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Sun, 21 Aug 2022 18:05:02 -0300 Subject: [PATCH 07/33] Do not allow kwargs rowid and title in show (HTML) --- src/abstractdataframe/io.jl | 25 ++++++++++++++++++++++--- test/io.jl | 10 ++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/abstractdataframe/io.jl b/src/abstractdataframe/io.jl index c5ea0b80c4..415930ca65 100755 --- a/src/abstractdataframe/io.jl +++ b/src/abstractdataframe/io.jl @@ -126,9 +126,12 @@ julia> show(stdout, MIME("text/csv"), DataFrame(A=1:3, B=["x", "y", "z"])) ``` """ Base.show(io::IO, mime::MIME, df::AbstractDataFrame) -Base.show(io::IO, mime::MIME"text/html", df::AbstractDataFrame; - summary::Bool=true, eltypes::Bool=true, kwargs...) = - _show(io, mime, df; summary=summary, eltypes=eltypes, kwargs...) +function Base.show(io::IO, mime::MIME"text/html", df::AbstractDataFrame; + summary::Bool=true, eltypes::Bool=true, kwargs...) + _verify_kwargs_for_html(;kwargs...) + return _show(io, mime, df; summary=summary, eltypes=eltypes, kwargs...) +end + Base.show(io::IO, mime::MIME"text/latex", df::AbstractDataFrame; eltypes::Bool=true) = _show(io, mime, df, eltypes=eltypes) Base.show(io::IO, mime::MIME"text/csv", df::AbstractDataFrame) = @@ -277,24 +280,28 @@ function _show(io::IO, end function Base.show(io::IO, mime::MIME"text/html", dfr::DataFrameRow; kwargs...) + _verify_kwargs_for_html(;kwargs...) r, c = parentindices(dfr) title = "DataFrameRow ($(length(dfr)) columns)" _show(io, mime, view(parent(dfr), [r], c); rowid=r, title=title, kwargs...) end function Base.show(io::IO, mime::MIME"text/html", dfrs::DataFrameRows; kwargs...) + _verify_kwargs_for_html(;kwargs...) df = parent(dfrs) title = "$(nrow(df))×$(ncol(df)) DataFrameRows" _show(io, mime, df; title=title, kwargs...) end function Base.show(io::IO, mime::MIME"text/html", dfcs::DataFrameColumns; kwargs...) + _verify_kwargs_for_html(;kwargs...) df = parent(dfcs) title = "$(nrow(df))×$(ncol(df)) DataFrameColumns" _show(io, mime, df; title=title, kwargs...) end function Base.show(io::IO, mime::MIME"text/html", gd::GroupedDataFrame) + _verify_kwargs_for_html(;kwargs...) N = length(gd) keys = html_escape(join(string.(groupcols(gd)), ", ")) keystr = length(gd.cols) > 1 ? "keys" : "key" @@ -323,6 +330,18 @@ function Base.show(io::IO, mime::MIME"text/html", gd::GroupedDataFrame) end end +# Internal function to verify the keywords in show functions using the HTML +# backend. +function _verify_kwargs_for_html(; kwargs...) + haskey(kwargs, :rowid) && + throw(ArgumentError("The keyword `rowid` is reserved and must not be used.")) + + haskey(kwargs, :title) && + throw(ArgumentError("Use the keyword `top_left_str` instead of `title` to change the label above the data frame.")) + + return nothing +end + ############################################################################## # # LaTeX output diff --git a/test/io.jl b/test/io.jl index 21dce80bd8..d20da8c2e7 100644 --- a/test/io.jl +++ b/test/io.jl @@ -555,6 +555,16 @@ end "" * "" * "" + + # Test invalid keywords when printing to HTML. + @test_throws ArgumentError show(stdout, MIME("text/html"), df, rowid=10) + @test_throws ArgumentError show(stdout, MIME("text/html"), df, title="title") + @test_throws ArgumentError show(stdout, MIME("text/html"), eachcol(df), rowid=10) + @test_throws ArgumentError show(stdout, MIME("text/html"), eachcol(df), title="title") + @test_throws ArgumentError show(stdout, MIME("text/html"), eachrow(df), rowid=10) + @test_throws ArgumentError show(stdout, MIME("text/html"), eachrow(df), title="title") + @test_throws ArgumentError show(stdout, MIME("text/html"), df[1, :], rowid=10) + @test_throws ArgumentError show(stdout, MIME("text/html"), df[1, :], title="title") end # test limit attribute of IOContext is used From 0952200c6db5673a3966747dd843656cfb8300ef Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Sun, 21 Aug 2022 18:15:31 -0300 Subject: [PATCH 08/33] Add support to truncate in HTML show --- src/abstractdataframe/io.jl | 10 ++++++++-- test/io.jl | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/abstractdataframe/io.jl b/src/abstractdataframe/io.jl index 415930ca65..3857026cc6 100755 --- a/src/abstractdataframe/io.jl +++ b/src/abstractdataframe/io.jl @@ -127,9 +127,11 @@ julia> show(stdout, MIME("text/csv"), DataFrame(A=1:3, B=["x", "y", "z"])) """ Base.show(io::IO, mime::MIME, df::AbstractDataFrame) function Base.show(io::IO, mime::MIME"text/html", df::AbstractDataFrame; - summary::Bool=true, eltypes::Bool=true, kwargs...) + summary::Bool=true, eltypes::Bool=true, truncate::Int=0, + kwargs...) _verify_kwargs_for_html(;kwargs...) - return _show(io, mime, df; summary=summary, eltypes=eltypes, kwargs...) + return _show(io, mime, df; summary=summary, eltypes=eltypes, + truncate=truncate, kwargs...) end Base.show(io::IO, mime::MIME"text/latex", df::AbstractDataFrame; eltypes::Bool=true) = @@ -165,6 +167,7 @@ function _show(io::IO, eltypes::Bool=true, rowid::Union{Int, Nothing}=nothing, title::String="", + truncate::Int=0, kwargs...) _check_consistency(df) @@ -248,6 +251,8 @@ function _show(io::IO, show_row_number = false end + maximum_columns_width = truncate <= 0 ? "" : string(truncate) * "px" + pretty_table(io, df; alignment = alignment, backend = Val(:html), @@ -259,6 +264,7 @@ function _show(io::IO, highlighters = (_PRETTY_TABLES_HTML_HIGHLIGHTER,), max_num_of_columns = mxcol, max_num_of_rows = mxrow, + maximum_columns_width = maximum_columns_width, minify = true, nosubheader = !eltypes, row_name_column_title = "Row", diff --git a/test/io.jl b/test/io.jl index d20da8c2e7..75d68c0a72 100644 --- a/test/io.jl +++ b/test/io.jl @@ -1478,6 +1478,39 @@ end "" * "" + # With truncation + io = IOBuffer() + show(io, MIME("text/html"), df, truncate=100) + str = String(take!(io)) + @test str == "
" * + "
" * + "1×1 DataFrame" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
Rowx
" * + "String
10123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
" * + "
" + # no truncation io = IOBuffer() show(io, MIME("text/latex"), df) From 2b19705a9f5ffb36ea671aec8d841f7fbadb89f3 Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Sun, 21 Aug 2022 18:29:29 -0300 Subject: [PATCH 09/33] Update documentation --- docs/src/man/getting_started.md | 17 ++++++++--------- src/abstractdataframe/io.jl | 9 +++++++-- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/docs/src/man/getting_started.md b/docs/src/man/getting_started.md index 91bf9ab9d5..3eef3159d2 100644 --- a/docs/src/man/getting_started.md +++ b/docs/src/man/getting_started.md @@ -15,17 +15,16 @@ relevant variables into your current namespace. !!! note - By default Jupyter Notebook will limit the number of rows and columns when displaying a data frame to roughly - fit the screen size (like in the REPL). - - You can override this behavior by changing the values of the `ENV["COLUMNS"]` and `ENV["LINES"]` - variables to hold the maximum width and height of output in characters respectively. + By default DataFrames.jl limits the number of rows and columns when displaying a data frame in a Jupyter + Notebook to 100 and 500, respectively. You can override this behavior by changing the values of the + `ENV["DATAFRAMES_COLUMNS"]` and `ENV["DATAFRAMES_ROWS"]` variables to hold the maximum number of columns + and rows of the output. Alternatively, you may want to set the maximum number of data frame rows to print to `100` and the maximum - output width in characters to `1000` for every Julia session using some Jupyter kernel file (numbers `100` - and `1000` are only examples and can be adjusted). In such case add a `"COLUMNS": "1000", "LINES": "100"` - entry to the `"env"` variable in this Jupyter kernel file. - See [here](https://jupyter-client.readthedocs.io/en/stable/kernels.html) for information about location + number of columns to print to `1000` for every Julia session using some Jupyter kernel file (numbers `100` + and `1000` are only examples and can be adjusted). In such case add a + `"DATAFRAME_COLUMNS": "1000", "DATAFRAMES_ROWS": "100"` entry to the `"env"` variable in this Jupyter kernel + file. See [here](https://jupyter-client.readthedocs.io/en/stable/kernels.html) for information about location and specification of Jupyter kernels. ## The `DataFrame` Type diff --git a/src/abstractdataframe/io.jl b/src/abstractdataframe/io.jl index 3857026cc6..7ef1fdd4ce 100755 --- a/src/abstractdataframe/io.jl +++ b/src/abstractdataframe/io.jl @@ -101,8 +101,13 @@ Render a data frame to an I/O stream in MIME type `mime`. Additionally selected MIME types support passing the following keyword arguments: - MIME type `"text/plain"` accepts all listed keyword arguments and their behavior is identical as for `show(::IO, ::AbstractDataFrame)` -- MIME type `"text/html"` accepts `summary` keyword argument which - allows to choose whether to print a brief string summary of the data frame. +- MIME type `"text/html"` accepts the following keywords: + - `eltypes::Bool = true`: Whether to print the column types under column names. + - `summary::Bool = true`: Whether to print a brief string summary of the data frame. + - `truncate::Int = 0`: If this value is grater then 0, the cells larger than + this number in pixels will be cropped during rendering. + - `kwargs...`: Any keyword argument supported by the function `pretty_table` + of PrettyTables.jl can be passed here to customize the output. # Examples ```jldoctest From ba81066f6260081fd0dd4a873a30d979f960f83e Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Sun, 21 Aug 2022 18:35:12 -0300 Subject: [PATCH 10/33] Add tests for DataFrameRow --- test/dataframerow.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/dataframerow.jl b/test/dataframerow.jl index b6dc705178..37160ad628 100644 --- a/test/dataframerow.jl +++ b/test/dataframerow.jl @@ -464,6 +464,9 @@ end "" * "" + @test_throws ArgumentError show(stdout, MIME("text/html"), dfr, rowid=10) + @test_throws ArgumentError show(stdout, MIME("text/html"), dfr, title="title") + @test sprint(show, "text/latex", dfr) == """ \\begin{tabular}{r|cc} \t& b & c\\\\ From 95c8816ef7ac56e6ffbd296e7f32070a3898aa1b Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Sun, 21 Aug 2022 18:39:25 -0300 Subject: [PATCH 11/33] Fix bug --- src/abstractdataframe/io.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/abstractdataframe/io.jl b/src/abstractdataframe/io.jl index 7ef1fdd4ce..47d3b69a0b 100755 --- a/src/abstractdataframe/io.jl +++ b/src/abstractdataframe/io.jl @@ -312,7 +312,6 @@ function Base.show(io::IO, mime::MIME"text/html", dfcs::DataFrameColumns; kwargs end function Base.show(io::IO, mime::MIME"text/html", gd::GroupedDataFrame) - _verify_kwargs_for_html(;kwargs...) N = length(gd) keys = html_escape(join(string.(groupcols(gd)), ", ")) keystr = length(gd.cols) > 1 ? "keys" : "key" From cdfa0972cb9e2fc7716120e0bbd24188ec695e03 Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Sat, 27 Aug 2022 18:42:22 -0300 Subject: [PATCH 12/33] Update the code to the new PrettyTables interface --- src/abstractdataframe/io.jl | 12 ++++++------ src/abstractdataframe/show.jl | 18 +++++++++--------- test/dataframerow.jl | 10 +++++----- test/io.jl | 12 ++++++------ 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/abstractdataframe/io.jl b/src/abstractdataframe/io.jl index 47d3b69a0b..5e97664005 100755 --- a/src/abstractdataframe/io.jl +++ b/src/abstractdataframe/io.jl @@ -233,7 +233,7 @@ function _show(io::IO, # PrettyTables.jl. Otherwise, we can just use the row number column. if (rowid === nothing) || (ncol(df) == 0) show_row_number::Bool = get(kwargs, :show_row_number, true) - row_names = nothing + row_labels = nothing # If the columns with row numbers is not shown, then we should not # display a vertical line after the first column. @@ -246,10 +246,10 @@ function _show(io::IO, # we must hide the row name column, which is used to display the # `rowid`. if !get(kwargs, :show_row_number, true) - row_names = nothing + row_labels = nothing vlines = Int[] else - row_names = [string(rowid)] + row_labels = [string(rowid)] vlines = Int[1] end @@ -271,13 +271,13 @@ function _show(io::IO, max_num_of_rows = mxrow, maximum_columns_width = maximum_columns_width, minify = true, - nosubheader = !eltypes, - row_name_column_title = "Row", - row_names = row_names, + row_label_column_title = "Row", + row_labels = row_labels, row_number_alignment = :r, row_number_column_title = "Row", show_omitted_cell_summary = true, show_row_number = show_row_number, + show_subheader = eltypes, standalone = false, table_class = "data-frame", table_div_class = "data-frame", diff --git a/src/abstractdataframe/show.jl b/src/abstractdataframe/show.jl index 0cb92dc858..16c349f1bd 100644 --- a/src/abstractdataframe/show.jl +++ b/src/abstractdataframe/show.jl @@ -223,7 +223,7 @@ function _show(io::IO, # PrettyTables.jl. Otherwise, we can just use the row number column. if (rowid === nothing) || (ncol(df) == 0) show_row_number::Bool = get(kwargs, :show_row_number, true) - row_names = nothing + row_labels = nothing # If the columns with row numbers is not shown, then we should not # display a vertical line after the first column. @@ -236,10 +236,10 @@ function _show(io::IO, # we must hide the row name column, which is used to display the # `rowid`. if !get(kwargs, :show_row_number, true) - row_names = nothing + row_labels = nothing vlines = Int[] else - row_names = [string(rowid)] + row_labels = [string(rowid)] vlines = Int[1] end @@ -253,7 +253,6 @@ function _show(io::IO, alignment_anchor_regex = alignment_anchor_regex, compact_printing = compact_printing, crop = crop, - crop_num_lines_at_beginning = 2, ellipsis_line_skip = 3, formatters = (_pretty_tables_general_formatter,), header = (names_str, types_str), @@ -262,14 +261,15 @@ function _show(io::IO, highlighters = (_PRETTY_TABLES_HIGHLIGHTER,), maximum_columns_width = maximum_columns_width, newline_at_end = false, - nosubheader = !eltypes, - row_name_alignment = :r, - row_name_crayon = Crayon(), - row_name_column_title = string(rowlabel), - row_names = row_names, + reserved_display_lines = 2, + row_label_alignment = :r, + row_label_crayon = Crayon(), + row_label_column_title = string(rowlabel), + row_labels = row_labels, row_number_alignment = :r, row_number_column_title = string(rowlabel), show_row_number = show_row_number, + show_subheader = eltypes, title = title, vcrop_mode = :middle, vlines = vlines, diff --git a/test/dataframerow.jl b/test/dataframerow.jl index 37160ad628..4fb1c14760 100644 --- a/test/dataframerow.jl +++ b/test/dataframerow.jl @@ -443,12 +443,12 @@ end "" * "" * "" * - "" * + "" * "" * "" * "" * "" * - "" * "" * "" * @@ -456,7 +456,7 @@ end "" * "" * "" * - "" * + "" * "" * "" * "" * @@ -519,14 +519,14 @@ end "
RowRowbc
" * + "" * "StringInt64
22b0
" * "" * "" * - "" * + "" * "" * "" * "" * "" * "" * "" * - "" * + "" * "" * "" * "" * diff --git a/test/io.jl b/test/io.jl index 75d68c0a72..fe5cacb838 100644 --- a/test/io.jl +++ b/test/io.jl @@ -228,12 +228,12 @@ end "
RowRowbc
22b0
" * "" * "" * - "" * + "" * "" * "" * "" * "" * - "" * "" * "" * @@ -241,7 +241,7 @@ end "" * "" * "" * - "" * + "" * "" * "" * "" * @@ -355,12 +355,12 @@ end "
RowRowFishMass
" * + "" * "StringFloat64?
11#undef1.5
" * "" * "" * - "" * + "" * "" * "" * "" * "" * - "" * "" * "" * @@ -368,7 +368,7 @@ end "" * "" * "" * - "" * + "" * "" * "" * "" * From 98a849bf50eef92b24e0b3c479c90edf24f2b1e5 Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Sun, 4 Sep 2022 14:47:44 -0300 Subject: [PATCH 13/33] Add info about output customization in Jupyter --- docs/src/man/getting_started.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/src/man/getting_started.md b/docs/src/man/getting_started.md index 3eef3159d2..bba4ceeab3 100644 --- a/docs/src/man/getting_started.md +++ b/docs/src/man/getting_started.md @@ -27,6 +27,15 @@ relevant variables into your current namespace. file. See [here](https://jupyter-client.readthedocs.io/en/stable/kernels.html) for information about location and specification of Jupyter kernels. + The package [PrettyTables.jl](https://github.com/ronisbr/PrettyTables.jl) renders the `DataFrame` in the + Jupyter notebook. If the user wants to customize the output, they can pass keywords (`kwargs...`) to the + function `show`: `show(stdout, MIME("text/html"), df; kwargs...)`, where `df` is the `DataFrame`. Any + argument supported by PrettyTables.jl in the HTML backend can be used here. Hence, for example, if the user + wants to change the color of all numbers smaller than 0 to red in Jupyter, they can execute: + `show(stdout, MIME("text/html"), df; highlighters = hl_lt(0, HtmlDecoration(color = "red")))` after + `using PrettyTables`. For more information about the available options, check + [PrettyTables.jl documentation](https://ronisbr.github.io/PrettyTables.jl/stable/man/usage/). + ## The `DataFrame` Type Objects of the `DataFrame` type represent a data table as a series of vectors, From ea7ec0c1db285de92149f8f6223162c6df9ac262 Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Sun, 4 Sep 2022 15:10:09 -0300 Subject: [PATCH 14/33] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bogumił Kamiński --- docs/src/man/getting_started.md | 2 +- src/abstractdataframe/io.jl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/man/getting_started.md b/docs/src/man/getting_started.md index bba4ceeab3..72e8bac558 100644 --- a/docs/src/man/getting_started.md +++ b/docs/src/man/getting_started.md @@ -16,7 +16,7 @@ relevant variables into your current namespace. !!! note By default DataFrames.jl limits the number of rows and columns when displaying a data frame in a Jupyter - Notebook to 100 and 500, respectively. You can override this behavior by changing the values of the + Notebook to 25 and 100, respectively. You can override this behavior by changing the values of the `ENV["DATAFRAMES_COLUMNS"]` and `ENV["DATAFRAMES_ROWS"]` variables to hold the maximum number of columns and rows of the output. diff --git a/src/abstractdataframe/io.jl b/src/abstractdataframe/io.jl index 5e97664005..33be680f8a 100755 --- a/src/abstractdataframe/io.jl +++ b/src/abstractdataframe/io.jl @@ -201,13 +201,13 @@ function _show(io::IO, if get(io, :limit, false) # Obtain the maximum number of rows and columns that we can print from # environments variables. - mxrow = tryparse(Int, get(ENV, "DATAFRAMES_ROWS", "100")) + mxrow = tryparse(Int, get(ENV, "DATAFRAMES_ROWS", "25")) if isnothing(mxrow) mxrow = 100 end - mxcol = tryparse(Int, get(ENV, "DATAFRAMES_COLUMNS", "500")) + mxcol = tryparse(Int, get(ENV, "DATAFRAMES_COLUMNS", "100")) if isnothing(mxcol) mxcol = 500 From da1d1f86647b701bcef4aa97d8d595f8d194227f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogumi=C5=82=20Kami=C5=84ski?= Date: Sun, 4 Sep 2022 20:14:44 +0200 Subject: [PATCH 15/33] Apply suggestions from code review --- src/abstractdataframe/io.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/abstractdataframe/io.jl b/src/abstractdataframe/io.jl index 33be680f8a..732d3f3799 100755 --- a/src/abstractdataframe/io.jl +++ b/src/abstractdataframe/io.jl @@ -204,13 +204,13 @@ function _show(io::IO, mxrow = tryparse(Int, get(ENV, "DATAFRAMES_ROWS", "25")) if isnothing(mxrow) - mxrow = 100 + mxrow = 25 end mxcol = tryparse(Int, get(ENV, "DATAFRAMES_COLUMNS", "100")) if isnothing(mxcol) - mxcol = 500 + mxcol = 100 end else mxrow = -1 From 6a9b814b1d265bac28b2dbfe7cd01de21b1f4fcf Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Wed, 7 Sep 2022 22:21:33 -0300 Subject: [PATCH 16/33] Bump PrettyTables.jl version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index f44e458782..2296cff222 100644 --- a/Project.toml +++ b/Project.toml @@ -31,7 +31,7 @@ InvertedIndices = "1" IteratorInterfaceExtensions = "0.1.1, 1" Missings = "0.4.2, 1" PooledArrays = "1.4.2" -PrettyTables = "0.12, 1" +PrettyTables = "2" Reexport = "0.1, 0.2, 1" ShiftedArrays = "1" SortingAlgorithms = "0.1, 0.2, 0.3, 1" From 987844d78b6d4d1f74ade7163057bd9b6e405ed1 Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Sat, 10 Sep 2022 17:24:54 -0300 Subject: [PATCH 17/33] Add the requested tests --- test/io.jl | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/test/io.jl b/test/io.jl index fe5cacb838..6569944636 100644 --- a/test/io.jl +++ b/test/io.jl @@ -376,6 +376,72 @@ end "
RowRowFishMass
" * + "" * "StringFloat64?
11#undef1.5
" * "" + io = IOBuffer() + show(io, MIME"text/html"(), df, show_row_number=false) + str = String(take!(io)) + @test str == "
" * + "
" * + "2×2 DataFrame" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
FishMass
StringFloat64?
#undef1.5
#undefmissing
" * + "
" + + io = IOBuffer() + show(io, MIME"text/html"(), df[2, :], show_row_number=false) + str = String(take!(io)) + @test str == "
" * + "
" * + "DataFrameRow (2 columns)" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
FishMass
StringFloat64?
#undefmissing
" * + "
" + io = IOBuffer() ENV["DATAFRAMES_COLUMNS"] = 2 ENV["DATAFRAMES_ROWS"] = 2 From 2e9e5d457839dc285b1b914a1473a9403575b28b Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Tue, 13 Sep 2022 13:02:44 -0300 Subject: [PATCH 18/33] Add the information asked by the reviewer --- docs/src/man/getting_started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/man/getting_started.md b/docs/src/man/getting_started.md index 72e8bac558..957b978dd8 100644 --- a/docs/src/man/getting_started.md +++ b/docs/src/man/getting_started.md @@ -18,7 +18,7 @@ relevant variables into your current namespace. By default DataFrames.jl limits the number of rows and columns when displaying a data frame in a Jupyter Notebook to 25 and 100, respectively. You can override this behavior by changing the values of the `ENV["DATAFRAMES_COLUMNS"]` and `ENV["DATAFRAMES_ROWS"]` variables to hold the maximum number of columns - and rows of the output. + and rows of the output. All columns or rows will be printed if those numbers are equal or lower than 0. Alternatively, you may want to set the maximum number of data frame rows to print to `100` and the maximum number of columns to print to `1000` for every Julia session using some Jupyter kernel file (numbers `100` From 384b7b9ffaae095ad5d0d120b33f7697bf523564 Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Tue, 13 Sep 2022 14:15:57 -0300 Subject: [PATCH 19/33] Bump PrettyTables version to 2.1 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 2296cff222..6b0430b16f 100644 --- a/Project.toml +++ b/Project.toml @@ -31,7 +31,7 @@ InvertedIndices = "1" IteratorInterfaceExtensions = "0.1.1, 1" Missings = "0.4.2, 1" PooledArrays = "1.4.2" -PrettyTables = "2" +PrettyTables = "2.1" Reexport = "0.1, 0.2, 1" ShiftedArrays = "1" SortingAlgorithms = "0.1, 0.2, 0.3, 1" From 608f6ed5cdaf16edb3b7f5f049dede8b29aa080e Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Tue, 13 Sep 2022 14:16:43 -0300 Subject: [PATCH 20/33] Add margin to the bottom of the table in HTML --- src/abstractdataframe/io.jl | 1 + src/abstractdataframe/prettytables.jl | 2 + test/dataframerow.jl | 4 +- test/grouping.jl | 6 +-- test/io.jl | 54 +++++++++++++-------------- 5 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/abstractdataframe/io.jl b/src/abstractdataframe/io.jl index 732d3f3799..5f298e82fb 100755 --- a/src/abstractdataframe/io.jl +++ b/src/abstractdataframe/io.jl @@ -281,6 +281,7 @@ function _show(io::IO, standalone = false, table_class = "data-frame", table_div_class = "data-frame", + table_style = _PRETTY_TABLES_HTML_TABLE_STYLE, top_left_str = title, top_right_str_decoration = HtmlDecoration(font_style = "italic"), vcrop_mode = :middle, diff --git a/src/abstractdataframe/prettytables.jl b/src/abstractdataframe/prettytables.jl index 032a963bdb..a16d728fde 100644 --- a/src/abstractdataframe/prettytables.jl +++ b/src/abstractdataframe/prettytables.jl @@ -31,6 +31,8 @@ const _PRETTY_TABLES_HIGHLIGHTER = Highlighter(_pretty_tables_highlighter_func, const _PRETTY_TABLES_HTML_HIGHLIGHTER = HtmlHighlighter(_pretty_tables_highlighter_func, HtmlDecoration(font_style = "italic")) +const _PRETTY_TABLES_HTML_TABLE_STYLE = Dict("margin-bottom" => "6px") + # Default DataFrames formatter for text backend. # # This formatter changes how the following types are presented when rendering diff --git a/test/dataframerow.jl b/test/dataframerow.jl index 4fb1c14760..492d1d1aa3 100644 --- a/test/dataframerow.jl +++ b/test/dataframerow.jl @@ -440,7 +440,7 @@ end "" * "" * "
" * - "" * + "
" * "" * "" * "" * @@ -516,7 +516,7 @@ end "" * "" * "
" * - "
Row
" * + "
" * "" * "" * "" * diff --git a/test/grouping.jl b/test/grouping.jl index 56c1a448a9..6e7d02ed47 100644 --- a/test/grouping.jl +++ b/test/grouping.jl @@ -1482,7 +1482,7 @@ end "" * "" * "
" * - "
Row
" * + "
" * "" * "" * "" * @@ -1517,7 +1517,7 @@ end "" * "" * "
" * - "
Row
" * + "
" * "" * "" * "" * @@ -1593,7 +1593,7 @@ end "" * "" * "
" * - "
Row
" * + "
" * "" * "" * "" * diff --git a/test/io.jl b/test/io.jl index 6569944636..d6419894c3 100644 --- a/test/io.jl +++ b/test/io.jl @@ -64,7 +64,7 @@ end "" * "" * "
" * - "
Row
" * + "
" * "" * "" * "" * @@ -105,7 +105,7 @@ end "" * "" * "
" * - "
Row
" * + "
" * "" * "" * "" * @@ -145,7 +145,7 @@ end "" * "" * "
" * - "
Row
" * + "
" * "" * "" * "" * @@ -185,7 +185,7 @@ end "" * "" * "
" * - "
Row
" * + "
" * "" * "" * "" * @@ -225,7 +225,7 @@ end "" * "" * "
" * - "
Row
" * + "
" * "" * "" * "" * @@ -253,7 +253,7 @@ end show(io, MIME"text/html"(), df, summary=false) str = String(take!(io)) @test str == "
" * - "
Row
" * + "
" * "" * "" * "" * @@ -286,7 +286,7 @@ end show(io, MIME"text/html"(), eachrow(df), summary=false) str = String(take!(io)) @test str == "
" * - "
Row
" * + "
" * "" * "" * "" * @@ -319,7 +319,7 @@ end show(io, MIME"text/html"(), eachcol(df), summary=false) str = String(take!(io)) @test str == "
" * - "
Row
" * + "
" * "" * "" * "" * @@ -352,7 +352,7 @@ end show(io, MIME"text/html"(), df[1, :], summary=false) str = String(take!(io)) @test str == "
" * - "
Row
" * + "
" * "" * "" * "" * @@ -387,7 +387,7 @@ end "" * "" * "
" * - "
Row
" * + "
" * "" * "" * "" * @@ -422,7 +422,7 @@ end "" * "" * "
" * - "
Fish
" * + "
" * "" * "" * "" * @@ -459,7 +459,7 @@ end "" * "" * "
" * - "
Fish
" * + "
" * "" * "" * "" * @@ -507,7 +507,7 @@ end "" * "" * "
" * - "
Row
" * + "
" * "" * "" * "" * @@ -583,7 +583,7 @@ end "" * "" * "
" * - "
Row
" * + "
" * "" * "" * "" * @@ -759,7 +759,7 @@ end "" * "" * "
" * - "
Row
" * + "
" * "" * "" * "" * @@ -886,7 +886,7 @@ end "" * "" * "
" * - "
Row
" * + "
" * "
" * "
" @test sprint(show, "text/latex", df[:, 2:1]) == @@ -903,7 +903,7 @@ end "" * "" * "
" * - "" * + "
" * "
" * "
" @test sprint(show, "text/latex", @view df[:, 2:1]) == @@ -920,7 +920,7 @@ end "" * "" * "
" * - "" * + "
" * "
" * "
" @test sprint(show, "text/latex", df[1, 2:1]) == @@ -1044,7 +1044,7 @@ end "" * "" * "
" * - "" * + "
" * "" * "" * "" * @@ -1089,7 +1089,7 @@ end "" * "" * "
" * - "
Row
" * + "
" * "" * "" * "" * @@ -1134,7 +1134,7 @@ end "" * "" * "
" * - "
Row
" * + "
" * "" * "" * "" * @@ -1179,7 +1179,7 @@ end "" * "" * "
" * - "
Row
" * + "
" * "" * "" * "" * @@ -1218,7 +1218,7 @@ end "" * "" * "
" * - "
Row
" * + "
" * "" * "" * "" * @@ -1257,7 +1257,7 @@ end "" * "" * "
" * - "
Row
" * + "
" * "" * "" * "" * @@ -1378,7 +1378,7 @@ end "" * "" * "
" * - "
Row
" * + "
" * "" * "" * "" * @@ -1523,7 +1523,7 @@ end "" * "" * "
" * - "
Row
" * + "
" * "" * "" * "" * @@ -1556,7 +1556,7 @@ end "" * "" * "
" * - "
Row
" * + "
" * "" * "" * "" * From 2d6f2db24171b25efa9ca98228fc6bce1d152709 Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Wed, 14 Sep 2022 10:28:24 -0300 Subject: [PATCH 21/33] Add tests for invalid data in ENV vars. --- test/io.jl | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/test/io.jl b/test/io.jl index d6419894c3..240c48273f 100644 --- a/test/io.jl +++ b/test/io.jl @@ -622,6 +622,145 @@ end "
Row
" * "
" + # Test invalid data in ENV variables. + io = IOBuffer() + ENV["DATAFRAMES_COLUMNS"] = "String" + ENV["DATAFRAMES_ROWS"] = "String" + show(IOContext(io, :limit => true), MIME"text/html"(), + DataFrame(a = Int64.(1:26 |> collect))) + str = String(take!(io)) + @test str == "
" * + "
" * + "26×1 DataFrame" * + "
" * + "
" * + "1 row omitted" * + "
" * + "
" * + "
" * + "
" * + "
" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "" * + "
Rowa
" * + "Int64
11
22
33
44
55
66
77
88
99
1010
1111
1212
1313
1515
1616
1717
1818
1919
2020
2121
2222
2323
2424
2525
2626
" * + "
" + # Test invalid keywords when printing to HTML. @test_throws ArgumentError show(stdout, MIME("text/html"), df, rowid=10) @test_throws ArgumentError show(stdout, MIME("text/html"), df, title="title") From e2b1da679d59bf3cbc36b8eb3b37ac0a229b8365 Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Mon, 19 Sep 2022 10:36:17 -0300 Subject: [PATCH 22/33] Apply suggestions from code review Co-authored-by: Milan Bouchet-Valat --- docs/src/man/getting_started.md | 2 +- src/abstractdataframe/io.jl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/man/getting_started.md b/docs/src/man/getting_started.md index 957b978dd8..27985e1544 100644 --- a/docs/src/man/getting_started.md +++ b/docs/src/man/getting_started.md @@ -28,7 +28,7 @@ relevant variables into your current namespace. and specification of Jupyter kernels. The package [PrettyTables.jl](https://github.com/ronisbr/PrettyTables.jl) renders the `DataFrame` in the - Jupyter notebook. If the user wants to customize the output, they can pass keywords (`kwargs...`) to the + Jupyter notebook. Users can customize the output by passing keywords arguments `kwargs...` to the function `show`: `show(stdout, MIME("text/html"), df; kwargs...)`, where `df` is the `DataFrame`. Any argument supported by PrettyTables.jl in the HTML backend can be used here. Hence, for example, if the user wants to change the color of all numbers smaller than 0 to red in Jupyter, they can execute: diff --git a/src/abstractdataframe/io.jl b/src/abstractdataframe/io.jl index 5f298e82fb..43b21d805d 100755 --- a/src/abstractdataframe/io.jl +++ b/src/abstractdataframe/io.jl @@ -104,7 +104,7 @@ Additionally selected MIME types support passing the following keyword arguments - MIME type `"text/html"` accepts the following keywords: - `eltypes::Bool = true`: Whether to print the column types under column names. - `summary::Bool = true`: Whether to print a brief string summary of the data frame. - - `truncate::Int = 0`: If this value is grater then 0, the cells larger than + - `truncate::Int = 0`: If greater than 0, the cells larger than this number in pixels will be cropped during rendering. - `kwargs...`: Any keyword argument supported by the function `pretty_table` of PrettyTables.jl can be passed here to customize the output. @@ -134,7 +134,7 @@ Base.show(io::IO, mime::MIME, df::AbstractDataFrame) function Base.show(io::IO, mime::MIME"text/html", df::AbstractDataFrame; summary::Bool=true, eltypes::Bool=true, truncate::Int=0, kwargs...) - _verify_kwargs_for_html(;kwargs...) + _verify_kwargs_for_html(; kwargs...) return _show(io, mime, df; summary=summary, eltypes=eltypes, truncate=truncate, kwargs...) end From 75c3604260ce77208209cd83c869f263d099f42d Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Mon, 19 Sep 2022 10:36:35 -0300 Subject: [PATCH 23/33] Apply suggestions from code review Co-authored-by: Milan Bouchet-Valat --- src/abstractdataframe/io.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/abstractdataframe/io.jl b/src/abstractdataframe/io.jl index 43b21d805d..557281653c 100755 --- a/src/abstractdataframe/io.jl +++ b/src/abstractdataframe/io.jl @@ -181,7 +181,7 @@ function _show(io::IO, types_str = batch_compacttype(types, 9) types_str_complete = batch_compacttype(types, 256) - # For consistency, if `kwargs` has `compact_printng`, we must use it. + # For consistency, if `kwargs` has `compact_printing`, we must use it. compact_printing::Bool = get(kwargs, :compact_printing, get(io, :compact, true)) num_rows, num_cols = size(df) @@ -200,7 +200,7 @@ function _show(io::IO, if get(io, :limit, false) # Obtain the maximum number of rows and columns that we can print from - # environments variables. + # environment variables. mxrow = tryparse(Int, get(ENV, "DATAFRAMES_ROWS", "25")) if isnothing(mxrow) From d375572aeee2afd3b6944f67e6e33b1932e46e96 Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Mon, 19 Sep 2022 10:36:55 -0300 Subject: [PATCH 24/33] Apply suggestions from code review Co-authored-by: Milan Bouchet-Valat --- src/abstractdataframe/io.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/abstractdataframe/io.jl b/src/abstractdataframe/io.jl index 557281653c..6f2f44a3d1 100755 --- a/src/abstractdataframe/io.jl +++ b/src/abstractdataframe/io.jl @@ -292,14 +292,14 @@ function _show(io::IO, end function Base.show(io::IO, mime::MIME"text/html", dfr::DataFrameRow; kwargs...) - _verify_kwargs_for_html(;kwargs...) + _verify_kwargs_for_html(; kwargs...) r, c = parentindices(dfr) title = "DataFrameRow ($(length(dfr)) columns)" _show(io, mime, view(parent(dfr), [r], c); rowid=r, title=title, kwargs...) end function Base.show(io::IO, mime::MIME"text/html", dfrs::DataFrameRows; kwargs...) - _verify_kwargs_for_html(;kwargs...) + _verify_kwargs_for_html(; kwargs...) df = parent(dfrs) title = "$(nrow(df))×$(ncol(df)) DataFrameRows" _show(io, mime, df; title=title, kwargs...) From 63af286226484f690962cad966a3fff418f63af2 Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Mon, 19 Sep 2022 10:37:24 -0300 Subject: [PATCH 25/33] Apply suggestions from code review Co-authored-by: Milan Bouchet-Valat --- src/abstractdataframe/io.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/abstractdataframe/io.jl b/src/abstractdataframe/io.jl index 6f2f44a3d1..4468795cd1 100755 --- a/src/abstractdataframe/io.jl +++ b/src/abstractdataframe/io.jl @@ -306,7 +306,7 @@ function Base.show(io::IO, mime::MIME"text/html", dfrs::DataFrameRows; kwargs... end function Base.show(io::IO, mime::MIME"text/html", dfcs::DataFrameColumns; kwargs...) - _verify_kwargs_for_html(;kwargs...) + _verify_kwargs_for_html(; kwargs...) df = parent(dfcs) title = "$(nrow(df))×$(ncol(df)) DataFrameColumns" _show(io, mime, df; title=title, kwargs...) @@ -345,10 +345,11 @@ end # backend. function _verify_kwargs_for_html(; kwargs...) haskey(kwargs, :rowid) && - throw(ArgumentError("The keyword `rowid` is reserved and must not be used.")) + throw(ArgumentError("Keyword argument `rowid` is reserved and must not be used.")) haskey(kwargs, :title) && - throw(ArgumentError("Use the keyword `top_left_str` instead of `title` to change the label above the data frame.")) + throw(ArgumentError("Use the `top_left_str` keyword argument instead of `title`" * + "to change the label above the data frame.")) return nothing end From b4f0e159a607ffed13d7e8bd0ac1eb835cebfaf4 Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Mon, 19 Sep 2022 10:43:25 -0300 Subject: [PATCH 26/33] Apply suggestion from code reviewer --- src/abstractdataframe/io.jl | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/abstractdataframe/io.jl b/src/abstractdataframe/io.jl index 4468795cd1..28e35329e2 100755 --- a/src/abstractdataframe/io.jl +++ b/src/abstractdataframe/io.jl @@ -201,17 +201,8 @@ function _show(io::IO, if get(io, :limit, false) # Obtain the maximum number of rows and columns that we can print from # environment variables. - mxrow = tryparse(Int, get(ENV, "DATAFRAMES_ROWS", "25")) - - if isnothing(mxrow) - mxrow = 25 - end - - mxcol = tryparse(Int, get(ENV, "DATAFRAMES_COLUMNS", "100")) - - if isnothing(mxcol) - mxcol = 100 - end + mxrow = something(tryparse(Int, get(ENV, "DATAFRAMES_ROWS", "25")), 25) + mxcol = something(tryparse(Int, get(ENV, "DATAFRAMES_COLUMNS", "100")), 100) else mxrow = -1 mxcol = -1 From 5ca32f141d61887bd07c844ae2e74f681688cddb Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Tue, 20 Sep 2022 13:10:26 -0300 Subject: [PATCH 27/33] Change truncate to max_column_width --- src/abstractdataframe/io.jl | 22 +++++++++++++--------- src/abstractdataframe/show.jl | 25 ++++++++++++++++--------- test/io.jl | 10 ++++++++-- 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/abstractdataframe/io.jl b/src/abstractdataframe/io.jl index 28e35329e2..f6a96bee84 100755 --- a/src/abstractdataframe/io.jl +++ b/src/abstractdataframe/io.jl @@ -104,8 +104,10 @@ Additionally selected MIME types support passing the following keyword arguments - MIME type `"text/html"` accepts the following keywords: - `eltypes::Bool = true`: Whether to print the column types under column names. - `summary::Bool = true`: Whether to print a brief string summary of the data frame. - - `truncate::Int = 0`: If greater than 0, the cells larger than - this number in pixels will be cropped during rendering. + - `max_column_width::String = ""`: The maximum column width. It must be a string + containing a valid CSS length. For example, passing "100px" will limit the + width of all columns to 100 pixels. If empty, the columns will be rendered + without limits. - `kwargs...`: Any keyword argument supported by the function `pretty_table` of PrettyTables.jl can be passed here to customize the output. @@ -132,11 +134,11 @@ julia> show(stdout, MIME("text/csv"), DataFrame(A=1:3, B=["x", "y", "z"])) """ Base.show(io::IO, mime::MIME, df::AbstractDataFrame) function Base.show(io::IO, mime::MIME"text/html", df::AbstractDataFrame; - summary::Bool=true, eltypes::Bool=true, truncate::Int=0, + summary::Bool=true, eltypes::Bool=true, max_column_width::String="", kwargs...) _verify_kwargs_for_html(; kwargs...) return _show(io, mime, df; summary=summary, eltypes=eltypes, - truncate=truncate, kwargs...) + max_column_width=max_column_width, kwargs...) end Base.show(io::IO, mime::MIME"text/latex", df::AbstractDataFrame; eltypes::Bool=true) = @@ -172,7 +174,7 @@ function _show(io::IO, eltypes::Bool=true, rowid::Union{Int, Nothing}=nothing, title::String="", - truncate::Int=0, + max_column_width::String="", kwargs...) _check_consistency(df) @@ -247,8 +249,6 @@ function _show(io::IO, show_row_number = false end - maximum_columns_width = truncate <= 0 ? "" : string(truncate) * "px" - pretty_table(io, df; alignment = alignment, backend = Val(:html), @@ -260,7 +260,7 @@ function _show(io::IO, highlighters = (_PRETTY_TABLES_HTML_HIGHLIGHTER,), max_num_of_columns = mxcol, max_num_of_rows = mxrow, - maximum_columns_width = maximum_columns_width, + maximum_columns_width = max_column_width, minify = true, row_label_column_title = "Row", row_labels = row_labels, @@ -339,9 +339,13 @@ function _verify_kwargs_for_html(; kwargs...) throw(ArgumentError("Keyword argument `rowid` is reserved and must not be used.")) haskey(kwargs, :title) && - throw(ArgumentError("Use the `top_left_str` keyword argument instead of `title`" * + throw(ArgumentError("Use the `top_left_str` keyword argument instead of `title` " * "to change the label above the data frame.")) + haskey(kwargs, :truncate) && + throw(ArgumentError("`truncate` is not supported in HTML. " * + "Use `max_column_width` to limit the size of the columns in this case.")) + return nothing end diff --git a/src/abstractdataframe/show.jl b/src/abstractdataframe/show.jl index 16c349f1bd..b1c881f616 100644 --- a/src/abstractdataframe/show.jl +++ b/src/abstractdataframe/show.jl @@ -333,17 +333,24 @@ julia> show(df, show_row_number=false) 3 z ``` """ -Base.show(io::IO, - df::AbstractDataFrame; - allrows::Bool = !get(io, :limit, false), - allcols::Bool = !get(io, :limit, false), - rowlabel::Symbol = :Row, - summary::Bool = true, - eltypes::Bool = true, - truncate::Int = 32, - kwargs...) = +function Base.show(io::IO, + df::AbstractDataFrame; + allrows::Bool = !get(io, :limit, false), + allcols::Bool = !get(io, :limit, false), + rowlabel::Symbol = :Row, + summary::Bool = true, + eltypes::Bool = true, + truncate::Int = 32, + kwargs...) + + # Check for keywords that are valid in other backends but not here. + haskey(kwargs, :max_column_width) && + throw(ArgumentError("`max_column_width` is not supported in text mode. " * + "Use `truncate` to limit the maximum number of characters in the columns.")) + _show(io, df; allrows=allrows, allcols=allcols, rowlabel=rowlabel, summary=summary, eltypes=eltypes, truncate=truncate, kwargs...) +end Base.show(df::AbstractDataFrame; allrows::Bool = !get(stdout, :limit, true), diff --git a/test/io.jl b/test/io.jl index 240c48273f..4c60248c33 100644 --- a/test/io.jl +++ b/test/io.jl @@ -1647,7 +1647,7 @@ end """ end -@testset "check truncate keyword argument" begin +@testset "check keywords that limit the column width" begin df = DataFrame(x="0123456789"^10) # no truncation @@ -1685,7 +1685,7 @@ end # With truncation io = IOBuffer() - show(io, MIME("text/html"), df, truncate=100) + show(io, MIME("text/html"), df, max_column_width="100px") str = String(take!(io)) @test str == "
" * "
" * @@ -1716,6 +1716,9 @@ end "" * "
" + # Using truncate in HTML must return an error. + @test_throws ArgumentError show(stdout, MIME("text/html"), df, truncate=100) + # no truncation io = IOBuffer() show(io, MIME("text/latex"), df) @@ -1799,6 +1802,9 @@ end ───────────────────────┼─────────────────────── 1 │ 01234567890123456789…""" + # Using `max_column_width` in text must return an error. + @test_throws ArgumentError show(stdout, df, max_column_width="100px") + @test_throws ArgumentError show(stdout, MIME("text/plain"), df, max_column_width="100px") end end # module From 2a4126a0b395adb573b518cc99c5c4a9289bf796 Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Wed, 21 Sep 2022 12:29:55 -0300 Subject: [PATCH 28/33] Update src/abstractdataframe/io.jl Co-authored-by: Milan Bouchet-Valat --- src/abstractdataframe/io.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/abstractdataframe/io.jl b/src/abstractdataframe/io.jl index f6a96bee84..4c50629016 100755 --- a/src/abstractdataframe/io.jl +++ b/src/abstractdataframe/io.jl @@ -101,7 +101,7 @@ Render a data frame to an I/O stream in MIME type `mime`. Additionally selected MIME types support passing the following keyword arguments: - MIME type `"text/plain"` accepts all listed keyword arguments and their behavior is identical as for `show(::IO, ::AbstractDataFrame)` -- MIME type `"text/html"` accepts the following keywords: +- MIME type `"text/html"` accepts the following keyword arguments: - `eltypes::Bool = true`: Whether to print the column types under column names. - `summary::Bool = true`: Whether to print a brief string summary of the data frame. - `max_column_width::String = ""`: The maximum column width. It must be a string From 5d6497473f20974689eb436e84e363ed79081b2a Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Wed, 21 Sep 2022 12:31:35 -0300 Subject: [PATCH 29/33] Change max_column_with type to AbstractString --- src/abstractdataframe/io.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/abstractdataframe/io.jl b/src/abstractdataframe/io.jl index 4c50629016..27d69d3eb7 100755 --- a/src/abstractdataframe/io.jl +++ b/src/abstractdataframe/io.jl @@ -104,10 +104,10 @@ Additionally selected MIME types support passing the following keyword arguments - MIME type `"text/html"` accepts the following keyword arguments: - `eltypes::Bool = true`: Whether to print the column types under column names. - `summary::Bool = true`: Whether to print a brief string summary of the data frame. - - `max_column_width::String = ""`: The maximum column width. It must be a string - containing a valid CSS length. For example, passing "100px" will limit the - width of all columns to 100 pixels. If empty, the columns will be rendered - without limits. + - `max_column_width::AbstractString = ""`: The maximum column width. It must + be a string containing a valid CSS length. For example, passing + "100px" will limit the width of all columns to 100 pixels. If empty, + the columns will be rendered without limits. - `kwargs...`: Any keyword argument supported by the function `pretty_table` of PrettyTables.jl can be passed here to customize the output. @@ -134,8 +134,8 @@ julia> show(stdout, MIME("text/csv"), DataFrame(A=1:3, B=["x", "y", "z"])) """ Base.show(io::IO, mime::MIME, df::AbstractDataFrame) function Base.show(io::IO, mime::MIME"text/html", df::AbstractDataFrame; - summary::Bool=true, eltypes::Bool=true, max_column_width::String="", - kwargs...) + summary::Bool=true, eltypes::Bool=true, + max_column_width::AbstractString="", kwargs...) _verify_kwargs_for_html(; kwargs...) return _show(io, mime, df; summary=summary, eltypes=eltypes, max_column_width=max_column_width, kwargs...) @@ -174,7 +174,7 @@ function _show(io::IO, eltypes::Bool=true, rowid::Union{Int, Nothing}=nothing, title::String="", - max_column_width::String="", + max_column_width::AbstractString="", kwargs...) _check_consistency(df) From caf27a91c06b87955bde9559908965c6f7548214 Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Wed, 21 Sep 2022 13:05:39 -0300 Subject: [PATCH 30/33] Add kwargs check in all types of text backend --- src/abstractdataframe/show.jl | 13 ++++++++++--- src/dataframerow/show.jl | 3 +++ src/groupeddataframe/show.jl | 3 +++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/abstractdataframe/show.jl b/src/abstractdataframe/show.jl index b1c881f616..3cf1b45034 100644 --- a/src/abstractdataframe/show.jl +++ b/src/abstractdataframe/show.jl @@ -344,9 +344,7 @@ function Base.show(io::IO, kwargs...) # Check for keywords that are valid in other backends but not here. - haskey(kwargs, :max_column_width) && - throw(ArgumentError("`max_column_width` is not supported in text mode. " * - "Use `truncate` to limit the maximum number of characters in the columns.")) + _verify_kwargs_for_text(; kwargs...) _show(io, df; allrows=allrows, allcols=allcols, rowlabel=rowlabel, summary=summary, eltypes=eltypes, truncate=truncate, kwargs...) @@ -363,3 +361,12 @@ Base.show(df::AbstractDataFrame; show(stdout, df; allrows=allrows, allcols=allcols, rowlabel=rowlabel, summary=summary, eltypes=eltypes, truncate=truncate, kwargs...) + +# Internal function to verify the keywords in show functions using the text +# backend. +function _verify_kwargs_for_text(; kwargs...) + haskey(kwargs, :max_column_width) && + throw(ArgumentError("`max_column_width` is not supported in text mode. " * + "Use `truncate` to limit the maximum number of characters in the columns.")) + return nothing +end diff --git a/src/dataframerow/show.jl b/src/dataframerow/show.jl index e69d7ff5aa..fbad91a3a0 100644 --- a/src/dataframerow/show.jl +++ b/src/dataframerow/show.jl @@ -4,6 +4,9 @@ function Base.show(io::IO, dfr::DataFrameRow; eltypes::Bool = true, truncate::Int = 32, kwargs...) + # Check for keywords that are valid in other backends but not here. + _verify_kwargs_for_text(; kwargs...) + r, c = parentindices(dfr) _show(io, view(parent(dfr), [r], c); allcols=allcols, rowlabel=rowlabel, summary=false, rowid=r, eltypes=eltypes, truncate=truncate, diff --git a/src/groupeddataframe/show.jl b/src/groupeddataframe/show.jl index 7f9be19b76..083c30f2d9 100644 --- a/src/groupeddataframe/show.jl +++ b/src/groupeddataframe/show.jl @@ -14,6 +14,9 @@ function Base.show(io::IO, gd::GroupedDataFrame; summary::Bool = true, truncate::Int = 32, kwargs...) + # Check for keywords that are valid in other backends but not here. + _verify_kwargs_for_text(; kwargs...) + N = length(gd) summary && Base.summary(io, gd) From e8637b0d41249eef125cec40928afa3bbf0d9d97 Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Wed, 21 Sep 2022 13:06:07 -0300 Subject: [PATCH 31/33] Improve test coverage --- test/dataframerow.jl | 7 ++++--- test/grouping.jl | 3 +++ test/io.jl | 10 +++------- test/show.jl | 5 +++++ 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/test/dataframerow.jl b/test/dataframerow.jl index 492d1d1aa3..0f6626778f 100644 --- a/test/dataframerow.jl +++ b/test/dataframerow.jl @@ -431,6 +431,10 @@ end str2 = String(take!(io.io)) @test str1 == str2 + # Test error when invalid keyword arguments are passed in text backend. + @test_throws ArgumentError show(stdout, dfr, max_column_width="100px") + @test_throws ArgumentError show(stdout, MIME("text/plain"), dfr, max_column_width="100px") + str = sprint(show, "text/html", dfr) @test str == "
" * "
" * @@ -464,9 +468,6 @@ end "" * "
" - @test_throws ArgumentError show(stdout, MIME("text/html"), dfr, rowid=10) - @test_throws ArgumentError show(stdout, MIME("text/html"), dfr, title="title") - @test sprint(show, "text/latex", dfr) == """ \\begin{tabular}{r|cc} \t& b & c\\\\ diff --git a/test/grouping.jl b/test/grouping.jl index 6e7d02ed47..d77add65be 100644 --- a/test/grouping.jl +++ b/test/grouping.jl @@ -1470,6 +1470,9 @@ end str2 = String(take!(io.io)) @test str1 == str2 + # Test error when invalid keyword arguments are passed in text backend. + @test_throws ArgumentError show(stdout, gd, max_column_width="100px") + str = sprint(show, "text/html", gd) @test str == "

" * "GroupedDataFrame with 4 groups based on key: A" * diff --git a/test/io.jl b/test/io.jl index 4c60248c33..31bae4f0fa 100644 --- a/test/io.jl +++ b/test/io.jl @@ -764,12 +764,15 @@ end # Test invalid keywords when printing to HTML. @test_throws ArgumentError show(stdout, MIME("text/html"), df, rowid=10) @test_throws ArgumentError show(stdout, MIME("text/html"), df, title="title") + @test_throws ArgumentError show(stdout, MIME("text/html"), df, truncate=100) @test_throws ArgumentError show(stdout, MIME("text/html"), eachcol(df), rowid=10) @test_throws ArgumentError show(stdout, MIME("text/html"), eachcol(df), title="title") @test_throws ArgumentError show(stdout, MIME("text/html"), eachrow(df), rowid=10) @test_throws ArgumentError show(stdout, MIME("text/html"), eachrow(df), title="title") + @test_throws ArgumentError show(stdout, MIME("text/html"), eachrow(df), truncate=100) @test_throws ArgumentError show(stdout, MIME("text/html"), df[1, :], rowid=10) @test_throws ArgumentError show(stdout, MIME("text/html"), df[1, :], title="title") + @test_throws ArgumentError show(stdout, MIME("text/html"), df[1, :], truncate=100) end # test limit attribute of IOContext is used @@ -1716,9 +1719,6 @@ end "" * "

" - # Using truncate in HTML must return an error. - @test_throws ArgumentError show(stdout, MIME("text/html"), df, truncate=100) - # no truncation io = IOBuffer() show(io, MIME("text/latex"), df) @@ -1801,10 +1801,6 @@ end │ String ───────────────────────┼─────────────────────── 1 │ 01234567890123456789…""" - - # Using `max_column_width` in text must return an error. - @test_throws ArgumentError show(stdout, df, max_column_width="100px") - @test_throws ArgumentError show(stdout, MIME("text/plain"), df, max_column_width="100px") end end # module diff --git a/test/show.jl b/test/show.jl index f0187f6638..612cb68074 100644 --- a/test/show.jl +++ b/test/show.jl @@ -727,6 +727,11 @@ end 12 │ 1.3123e-10+1.123e-5im 100000 1.0e8 -1.0e6+1.0e-7im""" end +@testset "Invalid keywords in text mode" begin + @test_throws ArgumentError show(stdout, df, max_column_width="100px") + @test_throws ArgumentError show(stdout, MIME("text/plain"), df, max_column_width="100px") +end + @testset "Issue #2673 - Vertical line when not showing row numbers" begin df = DataFrame(a=Int64[10, 20], b=Int64[30, 40], c=Int64[50, 60]) From 42d51fefef1353750c86860868a95b7d64b4a6b5 Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Wed, 21 Sep 2022 15:44:36 -0300 Subject: [PATCH 32/33] Fix tests --- test/show.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/show.jl b/test/show.jl index 612cb68074..42218972d9 100644 --- a/test/show.jl +++ b/test/show.jl @@ -728,6 +728,7 @@ end end @testset "Invalid keywords in text mode" begin + df = DataFrame(a=[1, 1, 2, 2], b=[5, 6, 7, 8], c=1:4) @test_throws ArgumentError show(stdout, df, max_column_width="100px") @test_throws ArgumentError show(stdout, MIME("text/plain"), df, max_column_width="100px") end From 732a54692babd4950cd54141d9586347e2c11892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogumi=C5=82=20Kami=C5=84ski?= Date: Fri, 23 Sep 2022 09:00:09 +0200 Subject: [PATCH 33/33] Apply suggestions from code review --- src/abstractdataframe/io.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/abstractdataframe/io.jl b/src/abstractdataframe/io.jl index 27d69d3eb7..15669383d2 100755 --- a/src/abstractdataframe/io.jl +++ b/src/abstractdataframe/io.jl @@ -173,7 +173,7 @@ function _show(io::IO, summary::Bool=true, eltypes::Bool=true, rowid::Union{Int, Nothing}=nothing, - title::String="", + title::AbstractString="", max_column_width::AbstractString="", kwargs...) _check_consistency(df) @@ -273,7 +273,7 @@ function _show(io::IO, table_class = "data-frame", table_div_class = "data-frame", table_style = _PRETTY_TABLES_HTML_TABLE_STYLE, - top_left_str = title, + top_left_str = String(title), top_right_str_decoration = HtmlDecoration(font_style = "italic"), vcrop_mode = :middle, wrap_table_in_div = true,