From aaa86712fa810a4d5a89afe6051df57b9ace2312 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 10 Jun 2024 13:38:52 +0000 Subject: [PATCH] [TOML] remove Dates hack, replace with epxlicit usage This hack will cease functioning soon (#54739), so it must be removed so that packages can stop relying on it. As an aid, now provide some basic conversion and printing support for the Base.TOML variants of this as well such that users can round-trip the contents (if they are well-formed as a date/time). --- base/toml_parser.jl | 35 +++++++++++++++++------------------ stdlib/Dates/src/types.jl | 5 +++++ stdlib/TOML/Project.toml | 3 ++- stdlib/TOML/src/TOML.jl | 21 +++++++++++++++++---- stdlib/TOML/src/print.jl | 6 +++++- 5 files changed, 46 insertions(+), 24 deletions(-) diff --git a/base/toml_parser.jl b/base/toml_parser.jl index b31e256517a1e..d50ca3b423c26 100644 --- a/base/toml_parser.jl +++ b/base/toml_parser.jl @@ -9,8 +9,8 @@ module TOML using Base: IdSet -# In case we do not have the Dates stdlib available # we parse DateTime into these internal structs, +# unless a different DateTime library is passed to the Parser constructor # note that these do not do any argument checking struct Date year::Int @@ -85,12 +85,10 @@ mutable struct Parser # Filled in in case we are parsing a file to improve error messages filepath::Union{String, Nothing} - # Gets populated with the Dates stdlib if it exists + # Optionally populate with the Dates stdlib to change the type of Date types returned Dates::Union{Module, Nothing} end -const DATES_PKGID = Base.PkgId(Base.UUID("ade2ca70-3891-5945-98fb-dc099432e06a"), "Dates") - function Parser(str::String; filepath=nothing) root = TOMLDict() l = Parser( @@ -109,7 +107,7 @@ function Parser(str::String; filepath=nothing) IdSet{TOMLDict}(), # defined_tables root, filepath, - isdefined(Base, :maybe_root_module) ? Base.maybe_root_module(DATES_PKGID) : nothing, + nothing ) startup(l) return l @@ -151,8 +149,6 @@ end # Errors # ########## -throw_internal_error(msg) = error("internal TOML parser error: $msg") - # Many functions return a ParserError. We want this to bubble up # all the way and have this error be returned to the user # if the parse is called with `raise=false`. This macro @@ -900,7 +896,7 @@ end function parse_float(l::Parser, contains_underscore)::Err{Float64} s = take_string_or_substring(l, contains_underscore) v = Base.tryparse(Float64, s) - v === nothing && return(ParserError(ErrGenericValueError)) + v === nothing && return ParserError(ErrGenericValueError) return v end @@ -909,8 +905,8 @@ function parse_int(l::Parser, contains_underscore, base=nothing)::Err{Int64} v = try Base.parse(Int64, s; base=base) catch e - e isa Base.OverflowError && return(ParserError(ErrOverflowError)) - error("internal parser error: did not correctly discredit $(repr(s)) as an int") + e isa Base.OverflowError && return ParserError(ErrOverflowError) + rethrow() end return v end @@ -932,8 +928,8 @@ for (name, T1, T2, n1, n2) in (("integer", Int64, Int128, 17, 33), Base.parse(BigInt, s; base) end catch e - e isa Base.OverflowError && return(ParserError(ErrOverflowError)) - error("internal parser error: did not correctly discredit $(repr(s)) as an int") + e isa Base.OverflowError && return ParserError(ErrOverflowError) + rethrow() end return v end @@ -1030,8 +1026,9 @@ function try_return_datetime(p, year, month, day, h, m, s, ms) if Dates !== nothing try return Dates.DateTime(year, month, day, h, m, s, ms) - catch - return ParserError(ErrParsingDateTime) + catch ex + ex isa ArgumentError && return ParserError(ErrParsingDateTime) + rethrow() end else return DateTime(year, month, day, h, m, s, ms) @@ -1043,8 +1040,9 @@ function try_return_date(p, year, month, day) if Dates !== nothing try return Dates.Date(year, month, day) - catch - return ParserError(ErrParsingDateTime) + catch ex + ex isa ArgumentError && return ParserError(ErrParsingDateTime) + rethrow() end else return Date(year, month, day) @@ -1065,8 +1063,9 @@ function try_return_time(p, h, m, s, ms) if Dates !== nothing try return Dates.Time(h, m, s, ms) - catch - return ParserError(ErrParsingDateTime) + catch ex + ex isa ArgumentError && return ParserError(ErrParsingDateTime) + rethrow() end else return Time(h, m, s, ms) diff --git a/stdlib/Dates/src/types.jl b/stdlib/Dates/src/types.jl index 7391c277b0718..e1f7f900bff51 100644 --- a/stdlib/Dates/src/types.jl +++ b/stdlib/Dates/src/types.jl @@ -492,3 +492,8 @@ end Base.OrderStyle(::Type{<:AbstractTime}) = Base.Ordered() Base.ArithmeticStyle(::Type{<:AbstractTime}) = Base.ArithmeticWraps() + +# minimal Base.TOML support +Date(d::Base.TOML.Date) = Date(d.year, d.month, d.day) +Time(t::Base.TOML.Time) = Time(t.hour, t.minute, t.second, t.ms) +DateTime(dt::Base.TOML.DateTime) = DateTime(Date(dt.date), Time(dt.time)) diff --git a/stdlib/TOML/Project.toml b/stdlib/TOML/Project.toml index 17fc8be19ec8e..ceb4acf8bbc65 100644 --- a/stdlib/TOML/Project.toml +++ b/stdlib/TOML/Project.toml @@ -6,12 +6,13 @@ version = "1.0.3" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" [compat] +Dates = "1.11.0" julia = "1.6" [extras] Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6" -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Tar = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" p7zip_jll = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" [targets] diff --git a/stdlib/TOML/src/TOML.jl b/stdlib/TOML/src/TOML.jl index 858b75a2e0eff..7414b5dc686f4 100644 --- a/stdlib/TOML/src/TOML.jl +++ b/stdlib/TOML/src/TOML.jl @@ -7,6 +7,8 @@ and to serialize Julia data structures to TOML format. """ module TOML +using Dates + module Internals # The parser is defined in Base using Base.TOML: Parser, parse, tryparse, ParserError, isvalid_barekey_char, reinit! @@ -36,6 +38,17 @@ performance if a larger number of small files are parsed. """ const Parser = Internals.Parser +""" + DTParser() + +Constructor for a TOML `Parser` which returns date and time objects from Dates. +""" +function DTParser(args...; kwargs...) + parser = Parser(args...; kwargs...) + parser.Dates = Dates + return parser +end + """ parsefile(f::AbstractString) parsefile(p::Parser, f::AbstractString) @@ -46,7 +59,7 @@ Parse file `f` and return the resulting table (dictionary). Throw a See also [`TOML.tryparsefile`](@ref). """ parsefile(f::AbstractString) = - Internals.parse(Parser(readstring(f); filepath=abspath(f))) + Internals.parse(DTParser(readstring(f); filepath=abspath(f))) parsefile(p::Parser, f::AbstractString) = Internals.parse(Internals.reinit!(p, readstring(f); filepath=abspath(f))) @@ -60,7 +73,7 @@ Parse file `f` and return the resulting table (dictionary). Return a See also [`TOML.parsefile`](@ref). """ tryparsefile(f::AbstractString) = - Internals.tryparse(Parser(readstring(f); filepath=abspath(f))) + Internals.tryparse(DTParser(readstring(f); filepath=abspath(f))) tryparsefile(p::Parser, f::AbstractString) = Internals.tryparse(Internals.reinit!(p, readstring(f); filepath=abspath(f))) @@ -74,7 +87,7 @@ Throw a [`ParserError`](@ref) upon failure. See also [`TOML.tryparse`](@ref). """ parse(str::AbstractString) = - Internals.parse(Parser(String(str))) + Internals.parse(DTParser(String(str))) parse(p::Parser, str::AbstractString) = Internals.parse(Internals.reinit!(p, String(str))) parse(io::IO) = parse(read(io, String)) @@ -90,7 +103,7 @@ Return a [`ParserError`](@ref) upon failure. See also [`TOML.parse`](@ref). """ tryparse(str::AbstractString) = - Internals.tryparse(Parser(String(str))) + Internals.tryparse(DTParser(String(str))) tryparse(p::Parser, str::AbstractString) = Internals.tryparse(Internals.reinit!(p, String(str))) tryparse(io::IO) = tryparse(read(io, String)) diff --git a/stdlib/TOML/src/print.jl b/stdlib/TOML/src/print.jl index 91ea4fd392e4b..168a7f63c6b5b 100644 --- a/stdlib/TOML/src/print.jl +++ b/stdlib/TOML/src/print.jl @@ -34,7 +34,8 @@ function print_toml_escaped(io::IO, s::AbstractString) end const MbyFunc = Union{Function, Nothing} -const TOMLValue = Union{AbstractVector, AbstractDict, Dates.DateTime, Dates.Time, Dates.Date, Bool, Integer, AbstractFloat, AbstractString} +const TOMLValue = Union{AbstractVector, AbstractDict, Bool, Integer, AbstractFloat, AbstractString, + Dates.DateTime, Dates.Time, Dates.Date, Base.TOML.DateTime, Base.TOML.Time, Base.TOML.Date} ######## @@ -89,6 +90,9 @@ function printvalue(f::MbyFunc, io::IO, value::AbstractVector, sorted::Bool) end function printvalue(f::MbyFunc, io::IO, value::TOMLValue, sorted::Bool) + value isa Base.TOML.DateTime && (value = Dates.DateTime(value)) + value isa Base.TOML.Time && (value = Dates.Time(value)) + value isa Base.TOML.Date && (value = Dates.Date(value)) value isa Dates.DateTime ? Base.print(io, Dates.format(value, Dates.dateformat"YYYY-mm-dd\THH:MM:SS.sss\Z")) : value isa Dates.Time ? Base.print(io, Dates.format(value, Dates.dateformat"HH:MM:SS.sss")) : value isa Dates.Date ? Base.print(io, Dates.format(value, Dates.dateformat"YYYY-mm-dd")) :