Skip to content

Commit

Permalink
TOML: Avoid type-pirating Base.TOML.Parser (#55892)
Browse files Browse the repository at this point in the history
Since stdlibs can be duplicated but Base never is, `Base.require_stdlib`
makes type piracy even more complicated than it normally would be.

To adapt, this changes `TOML.Parser` to be a type defined by the TOML
stdlib, so that we can define methods on it without committing
type-piracy and avoid problems like Pkg.jl#4017

Resolves
JuliaLang/Pkg.jl#4017 (comment)
  • Loading branch information
topolarity authored Sep 30, 2024
1 parent 4da0671 commit 2a2878c
Showing 1 changed file with 30 additions and 13 deletions.
43 changes: 30 additions & 13 deletions stdlib/TOML/src/TOML.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ module Internals
end

# https://github.com/JuliaLang/julia/issues/36605
readstring(f::AbstractString) = isfile(f) ? read(f, String) : error(repr(f), ": No such file")
_readstring(f::AbstractString) = isfile(f) ? read(f, String) : error(repr(f), ": No such file")

"""
Parser()
Expand All @@ -36,12 +36,14 @@ explicitly create a `Parser` but instead one directly use use
will however reuse some internal data structures which can be beneficial for
performance if a larger number of small files are parsed.
"""
const Parser = Internals.Parser
struct Parser
_p::Internals.Parser{Dates}
end

# Dates-enabled constructors
Parser() = Parser{Dates}()
Parser(io::IO) = Parser{Dates}(io)
Parser(str::String; filepath=nothing) = Parser{Dates}(str; filepath)
Parser() = Parser(Internals.Parser{Dates}())
Parser(io::IO) = Parser(Internals.Parser{Dates}(io))
Parser(str::String; filepath=nothing) = Parser(Internals.Parser{Dates}(str; filepath))

"""
parsefile(f::AbstractString)
Expand All @@ -53,9 +55,9 @@ 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(Internals.Parser{Dates}(_readstring(f); filepath=abspath(f)))
parsefile(p::Parser, f::AbstractString) =
Internals.parse(Internals.reinit!(p, readstring(f); filepath=abspath(f)))
Internals.parse(Internals.reinit!(p._p, _readstring(f); filepath=abspath(f)))

"""
tryparsefile(f::AbstractString)
Expand All @@ -67,9 +69,9 @@ 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(Internals.Parser{Dates}(_readstring(f); filepath=abspath(f)))
tryparsefile(p::Parser, f::AbstractString) =
Internals.tryparse(Internals.reinit!(p, readstring(f); filepath=abspath(f)))
Internals.tryparse(Internals.reinit!(p._p, _readstring(f); filepath=abspath(f)))

"""
parse(x::Union{AbstractString, IO})
Expand All @@ -80,10 +82,11 @@ Throw a [`ParserError`](@ref) upon failure.
See also [`TOML.tryparse`](@ref).
"""
parse(p::Parser) = Internals.parse(p._p)
parse(str::AbstractString) =
Internals.parse(Parser(String(str)))
Internals.parse(Internals.Parser{Dates}(String(str)))
parse(p::Parser, str::AbstractString) =
Internals.parse(Internals.reinit!(p, String(str)))
Internals.parse(Internals.reinit!(p._p, String(str)))
parse(io::IO) = parse(read(io, String))
parse(p::Parser, io::IO) = parse(p, read(io, String))

Expand All @@ -96,10 +99,11 @@ Return a [`ParserError`](@ref) upon failure.
See also [`TOML.parse`](@ref).
"""
tryparse(p::Parser) = Internals.tryparse(p._p)
tryparse(str::AbstractString) =
Internals.tryparse(Parser(String(str)))
Internals.tryparse(Internals.Parser{Dates}(String(str)))
tryparse(p::Parser, str::AbstractString) =
Internals.tryparse(Internals.reinit!(p, String(str)))
Internals.tryparse(Internals.reinit!(p._p, String(str)))
tryparse(io::IO) = tryparse(read(io, String))
tryparse(p::Parser, io::IO) = tryparse(p, read(io, String))

Expand Down Expand Up @@ -131,4 +135,17 @@ supported type.
"""
const print = Internals.Printer.print

public Parser, parsefile, tryparsefile, parse, tryparse, ParserError, print

# These methods are private Base interfaces, but we do our best to support them over
# the TOML stdlib types anyway to minimize downstream breakage.
Base.TOMLCache(p::Parser) = Base.TOMLCache(p._p, Dict{String, Base.CachedTOMLDict}())
Base.TOMLCache(p::Parser, d::Base.CachedTOMLDict) = Base.TOMLCache(p._p, d)
Base.TOMLCache(p::Parser, d::Dict{String, Dict{String, Any}}) = Base.TOMLCache(p._p, d)

Internals.reinit!(p::Parser, str::String; filepath::Union{Nothing, String}=nothing) =
Internals.reinit!(p._p, str; filepath)
Internals.parse(p::Parser) = Internals.parse(p._p)
Internals.tryparse(p::Parser) = Internals.tryparse(p._p)

end

0 comments on commit 2a2878c

Please sign in to comment.