diff --git a/NEWS.md b/NEWS.md index 8f1022333ca60..b6b127e27dd24 100644 --- a/NEWS.md +++ b/NEWS.md @@ -804,6 +804,9 @@ Deprecated or removed * `sub2ind` and `ind2sub` are deprecated in favor of using `CartesianIndices` and `LinearIndices` ([#24715]). + * `tryparse(T, s[, base])` has been deprecated in favor of `parse(Union{T, Void}, s[, base])` + ([#25130]). + Command-line option changes --------------------------- @@ -1784,3 +1787,4 @@ Command-line option changes [#24869]: https://github.com/JuliaLang/julia/issues/24869 [#25021]: https://github.com/JuliaLang/julia/issues/25021 [#25088]: https://github.com/JuliaLang/julia/issues/25088 +[#25130]: https://github.com/JuliaLang/julia/issues/25130 \ No newline at end of file diff --git a/base/client.jl b/base/client.jl index c4f29777c5149..56cabfdfa7c1a 100644 --- a/base/client.jl +++ b/base/client.jl @@ -75,7 +75,7 @@ color_normal = text_colors[:normal] function repl_color(key, default) env_str = get(ENV, key, "") - c = tryparse(Int, env_str) + c = parse(Union{Int, Void}, env_str) c_conv = coalesce(c, Symbol(env_str)) haskey(text_colors, c_conv) ? c_conv : default end diff --git a/base/deprecated.jl b/base/deprecated.jl index 4a13cbf8b31b9..acf89d3677a5d 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -3411,6 +3411,9 @@ end # PR #25113 @deprecate_binding CartesianRange CartesianIndices +@deprecate tryparse(T::Type, str::AbstractString) parse(Union{T, Void}, str) +@deprecate tryparse(T::Type, str::AbstractString, base::Integer) parse(Union{T, Void}, str, base) + # END 0.7 deprecations # BEGIN 1.0 deprecations diff --git a/base/exports.jl b/base/exports.jl index 40ee38d9b30e5..9f64510fcf3de 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -316,7 +316,6 @@ export fldmod1, flipsign, float, - tryparse, floor, fma, frexp, diff --git a/base/int.jl b/base/int.jl index 4620784e3c3a8..235010c5e91ca 100644 --- a/base/int.jl +++ b/base/int.jl @@ -602,12 +602,12 @@ macro big_str(s) end print(bf, s[end]) seekstart(bf) - n = tryparse(BigInt, String(take!(bf))) + n = parse(Union{BigInt, Void}, String(take!(bf))) n === nothing || return n else - n = tryparse(BigInt, s) + n = parse(Union{BigInt, Void}, s) n === nothing || return n - n = tryparse(BigFloat, s) + n = parse(Union{BigFloat, Void}, s) n === nothing || return n end message = "invalid number format $s for BigInt or BigFloat" diff --git a/base/mpfr.jl b/base/mpfr.jl index c3208e53c5d34..0f61e1518e5a6 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -16,7 +16,7 @@ import eps, signbit, sin, cos, sincos, tan, sec, csc, cot, acos, asin, atan, cosh, sinh, tanh, sech, csch, coth, acosh, asinh, atanh, atan2, cbrt, typemax, typemin, unsafe_trunc, realmin, realmax, rounding, - setrounding, maxintfloat, widen, significand, frexp, tryparse, iszero, + setrounding, maxintfloat, widen, significand, frexp, parse, iszero, isone, big import Base.Rounding: rounding_raw, setrounding_raw @@ -121,8 +121,8 @@ convert(::Type{BigFloat}, x::Union{UInt8,UInt16,UInt32}) = BigFloat(convert(Culo convert(::Type{BigFloat}, x::Union{Float16,Float32}) = BigFloat(Float64(x)) convert(::Type{BigFloat}, x::Rational) = BigFloat(numerator(x)) / BigFloat(denominator(x)) -function tryparse(::Type{BigFloat}, s::AbstractString, base::Int=0) - !isempty(s) && Base.Unicode.isspace(s[end]) && return tryparse(BigFloat, rstrip(s), base) +function parse(::Type{Union{BigFloat, Void}}, s::AbstractString, base::Int=0) + !isempty(s) && Base.Unicode.isspace(s[end]) && return parse(Union{BigFloat, Void}, rstrip(s), base) z = BigFloat() err = ccall((:mpfr_set_str, :libmpfr), Int32, (Ref{BigFloat}, Cstring, Int32, Int32), z, s, base, ROUNDING_MODE[]) err == 0 ? z : nothing diff --git a/base/parse.jl b/base/parse.jl index 9ecf359f7452b..262de0c4af68a 100644 --- a/base/parse.jl +++ b/base/parse.jl @@ -202,14 +202,23 @@ end end """ - tryparse(type, str, [base]) + parse(::Type{Union{T, Void}}, str, [base]) -Like [`parse`](@ref), but returns either a value of the requested type, +Like [`parse`](@ref), but returns either a value of the requested type `T`, or [`nothing`](@ref) if the string does not contain a valid number. + +# Examples +```jldoctest +julia> parse(Union{Int, Void}, "1") +1 + +julia> parse(Union{Int, Void}, "a") === nothing +true +``` """ -tryparse(::Type{T}, s::AbstractString, base::Integer) where {T<:Integer} = +parse(::Type{Union{T, Void}}, s::AbstractString, base::Integer) where {T<:Integer} = tryparse_internal(T, s, start(s), endof(s), check_valid_base(base), false) -tryparse(::Type{T}, s::AbstractString) where {T<:Integer} = +parse(::Type{Union{T, Void}}, s::AbstractString) where {T<:Integer} = tryparse_internal(T, s, start(s), endof(s), 0, false) function parse(::Type{T}, s::AbstractString, base::Integer) where T<:Integer @@ -222,12 +231,12 @@ end ## string to float functions ## -function tryparse(::Type{Float64}, s::String) +function parse(::Type{Union{Float64, Void}}, s::String) hasvalue, val = ccall(:jl_try_substrtod, Tuple{Bool, Float64}, (Ptr{UInt8},Csize_t,Csize_t), s, 0, sizeof(s)) hasvalue ? val : nothing end -function tryparse(::Type{Float64}, s::SubString{String}) +function parse(::Type{Union{Float64, Void}}, s::SubString{String}) hasvalue, val = ccall(:jl_try_substrtod, Tuple{Bool, Float64}, (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset, s.ncodeunits) hasvalue ? val : nothing @@ -242,12 +251,12 @@ function tryparse_internal(::Type{Float64}, s::SubString{String}, startpos::Int, (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset+startpos-1, endpos-startpos+1) hasvalue ? val : nothing end -function tryparse(::Type{Float32}, s::String) +function parse(::Type{Union{Float32, Void}}, s::String) hasvalue, val = ccall(:jl_try_substrtof, Tuple{Bool, Float32}, (Ptr{UInt8},Csize_t,Csize_t), s, 0, sizeof(s)) hasvalue ? val : nothing end -function tryparse(::Type{Float32}, s::SubString{String}) +function parse(::Type{Union{Float32, Void}}, s::SubString{String}) hasvalue, val = ccall(:jl_try_substrtof, Tuple{Bool, Float32}, (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset, s.ncodeunits) hasvalue ? val : nothing @@ -262,9 +271,10 @@ function tryparse_internal(::Type{Float32}, s::SubString{String}, startpos::Int, (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset+startpos-1, endpos-startpos+1) hasvalue ? val : nothing end -tryparse(::Type{T}, s::AbstractString) where {T<:Union{Float32,Float64}} = tryparse(T, String(s)) -tryparse(::Type{Float16}, s::AbstractString) = - convert(Union{Float16, Void}, tryparse(Float32, s)) +parse(::Type{Union{T, Void}}, s::AbstractString) where {T<:Union{Float32,Float64}} = + parse(Union{T, Void}, String(s)) +parse(::Type{Union{Float16, Void}}, s::AbstractString) = + convert(Union{Float16, Void}, parse(Union{Float32, Void}, s)) tryparse_internal(::Type{Float16}, s::AbstractString, startpos::Int, endpos::Int) = convert(Union{Float16, Void}, tryparse_internal(Float32, s, startpos, endpos)) @@ -331,7 +341,9 @@ tryparse_internal(T::Type{<:Complex}, s::AbstractString, i::Int, e::Int, raise:: # fallback methods for tryparse_internal tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos::Int) where T<:Real = - startpos == start(s) && endpos == endof(s) ? tryparse(T, s) : tryparse(T, SubString(s, startpos, endpos)) + startpos == start(s) && endpos == endof(s) ? + parse(Union{T, Void}, s) : + parse(Union{T, Void}, SubString(s, startpos, endpos)) function tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos::Int, raise::Bool) where T<:Real result = tryparse_internal(T, s, startpos, endpos) if raise && result === nothing diff --git a/base/repl/REPL.jl b/base/repl/REPL.jl index a3175eb35d901..d5ff54abd347a 100644 --- a/base/repl/REPL.jl +++ b/base/repl/REPL.jl @@ -979,7 +979,7 @@ function setup_interface( "^Q" => (s, o...) -> begin linfos = Base.LAST_SHOWN_LINE_INFOS str = String(take!(LineEdit.buffer(s))) - n = tryparse(Int, str) + n = parse(Union{Int, Void}, str) n === nothing && @goto writeback if n <= 0 || n > length(linfos) || startswith(linfos[n][1], "./REPL") @goto writeback diff --git a/doc/src/stdlib/numbers.md b/doc/src/stdlib/numbers.md index 9ed0821042869..4f7fbc2c7e13d 100644 --- a/doc/src/stdlib/numbers.md +++ b/doc/src/stdlib/numbers.md @@ -50,7 +50,7 @@ Base.digits Base.digits! Base.bitstring Base.parse(::Type, ::Any, ::Any) -Base.tryparse +Base.parse(::Type{Union{T, Void}}, ::AbstractString, ::Integer) where {T<:Integer} Base.big Base.signed Base.unsigned diff --git a/stdlib/Dates/src/parse.jl b/stdlib/Dates/src/parse.jl index 8c59189b5687c..c89bd493db280 100644 --- a/stdlib/Dates/src/parse.jl +++ b/stdlib/Dates/src/parse.jl @@ -270,7 +270,9 @@ function Base.parse(::Type{T}, str::AbstractString, df::DateFormat=default_forma T(values...) end -function Base.tryparse(::Type{T}, str::AbstractString, df::DateFormat=default_format(T)) where T<:TimeType +function Base.parse(::Type{Union{T, Void}}, + str::AbstractString, + df::DateFormat=default_format(T)) where T<:TimeType pos, len = start(str), endof(str) values, pos = tryparsenext_internal(T, str, pos, len, df, false) if values === nothing diff --git a/stdlib/Dates/src/types.jl b/stdlib/Dates/src/types.jl index 008d6e37afe9e..f6db97cff0d1c 100644 --- a/stdlib/Dates/src/types.jl +++ b/stdlib/Dates/src/types.jl @@ -147,9 +147,10 @@ daysinmonth(y,m) = DAYSINMONTH[m] + (m == 2 && isleapyear(y)) ### UTILITIES ### # These are necessary because the type constructors for TimeType subtypes can -# throw, and we want to be able to use tryparse without requiring a try/catch. +# throw, and we want to be able to use parse(Union{T, Void}, ...) without +# requiring a try/catch. # This is made easier by providing a helper function that checks arguments, so -# we can validate arguments in tryparse. +# we can validate arguments in parse(Union{T, Void}, ...). """ validargs(::Type{<:TimeType}, args...) -> Union{ArgumentError, Void} diff --git a/stdlib/Dates/test/io.jl b/stdlib/Dates/test/io.jl index 8ddf5367229de..f3b30f69f17ec 100644 --- a/stdlib/Dates/test/io.jl +++ b/stdlib/Dates/test/io.jl @@ -439,7 +439,7 @@ end end end # Issue #21504 -@test tryparse(Dates.Date, "0-1000") === nothing +@test parse(Union{Dates.Date, Void}, "0-1000") === nothing @testset "parse milliseconds, Issue #22100" begin @test Dates.DateTime("2017-Mar-17 00:00:00.0000", "y-u-d H:M:S.s") == Dates.DateTime(2017, 3, 17) diff --git a/test/ambiguous.jl b/test/ambiguous.jl index 2a7aa3c8e1efb..94a03901078ee 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -290,6 +290,12 @@ end pop!(need_to_handle_undef_sparam, which(Base.nonmissingtype, Tuple{Type{Union{Missing, T}} where T})) pop!(need_to_handle_undef_sparam, which(Base.convert, (Type{Union{Some{T}, Void}} where T, Some))) pop!(need_to_handle_undef_sparam, which(Base.convert, (Type{Union{T, Void}} where T, Some))) + using Dates + pop!(need_to_handle_undef_sparam, which(Base.parse, (Type{Union{Void, T}} where T<:Union{Float32, Float64}, AbstractString))) + pop!(need_to_handle_undef_sparam, which(Base.parse, (Type{Union{Void, T}} where T<:Dates.TimeType, AbstractString))) + pop!(need_to_handle_undef_sparam, which(Base.parse, (Type{Union{Void, T}} where T<:Integer, AbstractString, Integer))) + pop!(need_to_handle_undef_sparam, which(Base.parse, (Type{Union{Void, T}} where T<:Dates.TimeType, AbstractString, Dates.DateFormat))) + pop!(need_to_handle_undef_sparam, which(Base.parse, (Type{Union{Void, T}} where T<:Integer, AbstractString))) @test need_to_handle_undef_sparam == Set() end end diff --git a/test/parse.jl b/test/parse.jl index 0c17bdc59a474..db2e48282ecf9 100644 --- a/test/parse.jl +++ b/test/parse.jl @@ -212,22 +212,22 @@ end @test parse(Int, "2") === 2 @test parse(Bool, "true") === true @test parse(Bool, "false") === false -@test tryparse(Bool, "true") === true -@test tryparse(Bool, "false") === false +@test parse(Union{Bool, Void}, "true") === true +@test parse(Union{Bool, Void}, "false") === false @test_throws ArgumentError parse(Int, "2", 1) @test_throws ArgumentError parse(Int, "2", 63) -# issue #17333: tryparse should still throw on invalid base +# issue #17333: parse(Union{T, Void}, ...) should still throw on invalid base for T in (Int32, BigInt), base in (0,1,100) - @test_throws ArgumentError tryparse(T, "0", base) + @test_throws ArgumentError parse(Union{T, Void}, "0", base) end # error throwing branch from #10560 @test_throws ArgumentError Base.tryparse_internal(Bool, "foo", 1, 2, 10, true) -@test tryparse(Float64, "1.23") === 1.23 -@test tryparse(Float32, "1.23") === 1.23f0 -@test tryparse(Float16, "1.23") === Float16(1.23) +@test parse(Union{Float64, Void}, "1.23") === 1.23 +@test parse(Union{Float32, Void}, "1.23") === 1.23f0 +@test parse(Union{Float16, Void}, "1.23") === Float16(1.23) # parsing complex numbers (#22250) @testset "complex parsing" begin diff --git a/test/spawn.jl b/test/spawn.jl index 48ee76874296d..f48ad63640a6e 100644 --- a/test/spawn.jl +++ b/test/spawn.jl @@ -451,7 +451,7 @@ end # test for proper handling of FD exhaustion if Sys.isunix() let ps = Pipe[] - ulimit_n = tryparse(Int, readchomp(`sh -c 'ulimit -n'`)) + ulimit_n = parse(Union{Int, Void}, readchomp(`sh -c 'ulimit -n'`)) try for i = 1 : 100 * coalesce(ulimit_n, 1000) p = Pipe() diff --git a/test/strings/basic.jl b/test/strings/basic.jl index dd4623be6c958..6b172e5241361 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -260,14 +260,14 @@ end for T in [Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128] for i in [typemax(T), typemin(T)] s = "$i" - @test tryparse(T, s) == i + @test parse(Union{T, Void}, s) == i end end for T in [Int8, Int16, Int32, Int64, Int128] for i in [typemax(T), typemin(T)] f = "$(i)0" - @test tryparse(T, f) === nothing + @test parse(Union{T, Void}, f) === nothing end end end @@ -284,13 +284,13 @@ end @test unsafe_string(sp,5) == "abcde" @test typeof(unsafe_string(sp)) == String - @test tryparse(BigInt, "1234567890") == BigInt(1234567890) - @test tryparse(BigInt, "1234567890-") === nothing + @test parse(Union{BigInt, Void}, "1234567890") == BigInt(1234567890) + @test parse(Union{BigInt, Void}, "1234567890-") === nothing - @test tryparse(Float64, "64") == 64.0 - @test tryparse(Float64, "64o") === nothing - @test tryparse(Float32, "32") == 32.0f0 - @test tryparse(Float32, "32o") === nothing + @test parse(Union{Float64, Void}, "64") == 64.0 + @test parse(Union{Float64, Void}, "64o") === nothing + @test parse(Union{Float32, Void}, "32") == 32.0f0 + @test parse(Union{Float32, Void}, "32o") === nothing end @testset "issue #10994: handle embedded NUL chars for string parsing" begin @@ -298,7 +298,7 @@ end @test_throws ArgumentError parse(T, "1\0") end for T in [BigInt, Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128, Float64, Float32] - @test tryparse(T, "1\0") === nothing + @test parse(Union{T, Void}, "1\0") === nothing end let s = Base.Unicode.normalize("tést",:NFKC) @test unsafe_string(Base.unsafe_convert(Cstring, Base.cconvert(Cstring, s))) == s