diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index e2534e4b4..01100cdc6 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -13,10 +13,6 @@ jobs: fail-fast: false matrix: version: - - "1.6" - - "1.7" - - "1.8" - - "1.9" - "1.10" - "~1.11.0-0" - nightly diff --git a/Project.toml b/Project.toml index 99b0f5c14..2b8e02cc6 100644 --- a/Project.toml +++ b/Project.toml @@ -1,27 +1,14 @@ name = "Compat" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.16.0" - -[deps] -Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" -LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" -TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76" -UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" +version = "5.0.0" [compat] -julia = "1.6" +julia = "1.10" [extras] -Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" -LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [extensions] -CompatLinearAlgebraExt = "LinearAlgebra" [targets] -test = ["Dates", "LinearAlgebra", "Test"] - -[weakdeps] -Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" -LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +test = ["Test"] diff --git a/README.md b/README.md index 39eb6dd10..fabe10ee2 100644 --- a/README.md +++ b/README.md @@ -25,21 +25,19 @@ pkg> add Compat and add a [version specifier line](https://julialang.github.io/Pkg.jl/v1/compatibility/#Version-specifier-format-1) such as `Compat = "3.22, 4"` in the `[compat]`section of the `Project.toml` file in your package directory. The version in the latter should be the minimum -version that supports all needed features (see list below). Note that Compat v4 -requires Julia v1.6, but some features may have been backported to Compat v3 +version that supports all needed features (see list below). Note that Compat v5 +requires Julia v1.10, but some features may have been backported to Compat v4 (see the -[feature list of the release-3 branch](https://github.com/JuliaLang/Compat.jl/tree/release-3#supported-features)). +[feature list of the release-4 branch](https://github.com/JuliaLang/Compat.jl/tree/release-4#supported-features)). If you require any of those backported features, be sure to specify the correct -compatibility in your `Project.toml`. E.g. if the feature from Compat v4.x has -been backported to v3.y, use `Compat = 3.y, 4.x`. If you use a feature that had -originally been added in Compat v3 (e.g. in 3.x), don't forget to also declare -compatibility with v4 with `Compat = 3.x, 4` (unless you use one the very few -[things that got removed between Compat v3 and v4](https://github.com/JuliaLang/Compat.jl/releases/tag/v4.0.0), -which you most probably don't). +compatibility in your `Project.toml`. E.g. if the feature from Compat v5.x has +been backported to v4.y, use `Compat = 4.y, 5.x`. If you use a feature that had +originally been added in Compat v4 (e.g. in 4.x), don't forget to also declare +compatibility with v5 with `Compat = 4.x, 5`. To minimize dependency conflicts between packages it is recommended that packages -allow for both appropriate v4 and v3 versions of Compat.jl in their Project.toml -(except for rare cases of packages that support only v4 or v3 version of Compat.jl). +allow for both appropriate v5 and v4 versions of Compat.jl in their Project.toml +(except for rare cases of packages that support only v5 or v4 version of Compat.jl). Then, in your package, shortly after the `module` statement include a line like this: @@ -76,66 +74,14 @@ changes in `julia`. * `Compat.Fix{N}` which fixes an argument at the `N`th position ([#54653]) (since Compat 4.16.0) -* `chopprefix(s, prefix)` and `chopsuffix(s, suffix)` ([#40995]) (since Compat 4.15.0) +* `logrange(lo, hi; length)` is like `range` but with a constant ratio, not difference. ([#39071]) (since Compat 4.14.0) -* `logrange(lo, hi; length)` is like `range` but with a constant ratio, not difference. ([#39071]) (since Compat 4.14.0) Note that on Julia 1.8 and earlier, the version from Compat has slightly lower floating-point accuracy than the one in Base (Julia 1.11 and later). +* `Compat.allequal(f, itr)` and `Compat.allunique(f, itr)` methods. ([#47679]) (since Compat 4.13.0) -* `allequal(f, itr)` and `allunique(f, itr)` methods. ([#47679]) (since Compat 4.13.0) - -* `Iterators.cycle(itr, n)` is the lazy version of `repeat(vector, n)`. ([#47354]) (since Compat 4.13.0) +* `Compat.Iterators.cycle(itr, n)` is the lazy version of `repeat(vector, n)`. ([#47354]) (since Compat 4.13.0) * `@compat public foo, bar` marks `foo` and `bar` as public in Julia 1.11+ and is a no-op in Julia 1.10 and earlier. ([#50105]) (since Compat 3.47.0, 4.10.0) -* `redirect_stdio`, for simple stream redirection. ([#37978]) (since Compat 4.8.0) - -* `trunc`, `floor`, `ceil`, and `round` to `Bool`. ([#25085]) (since Compat 4.7.0) - -* `splat(f)` which is equivalent to `args -> f(args...)`. ([#42717], [#48038]) (since Compat 4.6.0) (Note: for historical reasons, `Compat` on Julia before v1.9 also exports `Splat`; its usage is discouraged, however.) - -* `Compat.@assume_effects setting... ex` overrides the compiler's effect modeling for the method definition `ex` on Julia versions that support this feature. Julia version without support just pass back `ex`. ([#43852]) (since Compat 4.4.0) - -* `div`, `lcm`, `gcd`, `/`, `rem`, and `mod` will `promote` heterogenous `Dates.Period`s ([`@bdf9ead9`]). (since Compat 4.3.0) - -* `stack` combines a collection of slices into one array ([#43334]). (since Compat 3.46.0, 4.2.0) - -* `keepat!` removes the items at all the indices which are not given and returns - the modified source ([#36229], [#42351]). (since Compat 3.44.0, 4.1.0) - -* `@compat (; a, b) = (; c=1, b=2, a=3)` supports property descturing assignment syntax ([#39285]). - -* `allequal`, the opposite of `allunique` ([#43354]). (since Compat 3.42.0) - -* `eachsplit` for iteratively performing split(str). ([#39245]). (since Compat 3.41.0) - -* `ismutabletype(t::Type)` check whether a type is mutable (the field `mutable` of `DataType` was removed. [#39037]) (since Compat 3.40) - -* `convert(::Type{<:Period}, ::CompoundPeriod)` can convert `CompoundPeriod`s into the specified `Period` type ([#40803]) (since Compat 3.38.0) - -* `Compat.@inline` and `Compat.@noinline` can be used at function callsites to encourage the compiler to (not) inline the function calls on Julia versions that support these features, and otherwise do not have any effects ([#41312]) (since Compat 3.37) - -* `Compat.@inline` and `Compat.@noinline` can be used within function body to hint to the compiler the inlineability of the defined function ([#41312]) (since Compat 3.37) - -* `Compat.@constprop :aggressive ex` and `Compat.@constprop :none ex` allow control over constant-propagation during inference on Julia versions that support this feature, and otherwise just pass back `ex`. ([#42125]) (since Compat 3.36) - -* `Returns(value)` returns `value` for any arguments ([#39794]) (since Compat 3.35) - -* The function `current_exceptions()` has been added to get the current - exception stack. Julia-1.0 lacks runtime support for full execption stacks, - so we return only the most recent exception in that case. ([#29901]) (since - Compat 3.34) - -* Two argument methods `findmax(f, domain)`, `argmax(f, domain)` and the corresponding `min` versions ([#35316], [#41076]) (since Compat 3.31.1) - -* `isunordered(x)` returns true if `x` is value that is normally unordered, such as `NaN` or `missing` ([#35316]) (since Compat 3.31.1) - -* `get` accepts tuples and numbers ([#41007], [#41032]) (since Compat 3.31) - -* `@something` and `@coalesce` as short-circuiting versions of `something` and `coalesce` ([#40729]) (since Compat 3.29) - -* `pkgversion(m::Module)` returns the version of the package that loaded a given module ([#45607]) (since Compat 4.11) - -* `VersionNumber(::VersionNumber)` defined as a no-op constructor ([#45052]) (since Compat 4.12) - ## Developer tips One of the most important rules for `Compat.jl` is to avoid breaking user code @@ -167,34 +113,9 @@ Note that you should specify the correct minimum version for `Compat` in the `[compat]` section of your `Project.toml`, as given in above list. [`@bdf9ead9`]: https://github.com/JuliaLang/julia/commit/bdf9ead91e5a8dfd91643a17c1626032faada329 -[#25085]: https://github.com/JuliaLang/julia/issues/25085 -[#29901]: https://github.com/JuliaLang/julia/issues/29901 -[#35316]: https://github.com/JuliaLang/julia/issues/35316 -[#36229]: https://github.com/JuliaLang/julia/issues/36229 -[#37978]: https://github.com/JuliaLang/julia/issues/37978 -[#39037]: https://github.com/JuliaLang/julia/issues/39037 -[#39071]: https://github.com/JuliaLang/julia/pull/39071 -[#39245]: https://github.com/JuliaLang/julia/issues/39245 -[#39285]: https://github.com/JuliaLang/julia/issues/39285 -[#39794]: https://github.com/JuliaLang/julia/issues/39794 -[#40729]: https://github.com/JuliaLang/julia/issues/40729 -[#40803]: https://github.com/JuliaLang/julia/issues/40803 -[#40995]: https://github.com/JuliaLang/julia/pull/40995 -[#41007]: https://github.com/JuliaLang/julia/issues/41007 -[#41032]: https://github.com/JuliaLang/julia/issues/41032 -[#41076]: https://github.com/JuliaLang/julia/issues/41076 -[#41312]: https://github.com/JuliaLang/julia/issues/41312 -[#42125]: https://github.com/JuliaLang/julia/issues/42125 -[#42351]: https://github.com/JuliaLang/julia/issues/42351 -[#42717]: https://github.com/JuliaLang/julia/issues/42717 -[#43334]: https://github.com/JuliaLang/julia/issues/43334 -[#43354]: https://github.com/JuliaLang/julia/issues/43354 -[#43852]: https://github.com/JuliaLang/julia/issues/43852 -[#45052]: https://github.com/JuliaLang/julia/issues/45052 -[#45607]: https://github.com/JuliaLang/julia/issues/45607 +[#39071]: https://github.com/JuliaLang/julia/issues/39071 [#45793]: https://github.com/JuliaLang/julia/issues/45793 [#47354]: https://github.com/JuliaLang/julia/issues/47354 -[#47679]: https://github.com/JuliaLang/julia/pull/47679 -[#48038]: https://github.com/JuliaLang/julia/issues/48038 +[#47679]: https://github.com/JuliaLang/julia/issues/47679 [#50105]: https://github.com/JuliaLang/julia/issues/50105 [#54653]: https://github.com/JuliaLang/julia/issues/54653 diff --git a/ext/CompatLinearAlgebraExt.jl b/ext/CompatLinearAlgebraExt.jl deleted file mode 100644 index 8ee446eaf..000000000 --- a/ext/CompatLinearAlgebraExt.jl +++ /dev/null @@ -1,9 +0,0 @@ -module CompatLinearAlgebraExt - -import Compat -import LinearAlgebra - -Base.@deprecate Compat.set_num_threads(nt) LinearAlgebra.BLAS.set_num_threads(nt) false -Base.@deprecate Compat.get_num_threads LinearAlgebra.BLAS.get_num_threads false - -end diff --git a/src/Compat.jl b/src/Compat.jl index 1f5260121..b4c1b808b 100644 --- a/src/Compat.jl +++ b/src/Compat.jl @@ -1,792 +1,18 @@ module Compat -if VERSION < v"1.9.0-" - # `Dates` is a weakdep, so won't be available on Julia versions with weakdep support, - # i.e. Julia 1.9 and later, so this `using` has to be inside the conditional. - # Should a post-1.9 feature of Dates be added to Compat, the way forward will be a - # package extension - using Dates: Period, CompoundPeriod -end - include("compatmacro.jl") -# NOTE these `@inline` and `@noinline` definitions overwrite the definitions implicitly -# imported from Base and so should happen before any usages of them within this module - -# https://github.com/JuliaLang/julia/pull/41312: `@inline`/`@noinline` annotations within a function body -@static if !hasmethod(getfield(Base, Symbol("@inline")), (LineNumberNode,Module)) - macro inline() Expr(:meta, :inline) end - macro noinline() Expr(:meta, :noinline) end -end - -# https://github.com/JuliaLang/julia/pull/41328: callsite annotations of inlining -@static if !isdefined(Base, :annotate_meta_def_or_block) - macro inline(ex) annotate_meta_def_or_nothing(ex, :inline) end - macro noinline(ex) annotate_meta_def_or_nothing(ex, :noinline) end - function annotate_meta_def_or_nothing(@nospecialize(ex), meta::Symbol) - inner = unwrap_macrocalls(ex) - if is_function_def(inner) - # annotation on a definition - return esc(Base.pushmeta!(ex, meta)) - else - # do nothing - return esc(ex) - end - end - unwrap_macrocalls(@nospecialize(x)) = x - function unwrap_macrocalls(ex::Expr) - inner = ex - while inner.head === :macrocall - inner = inner.args[end]::Expr - end - return inner - end - is_function_def(@nospecialize(ex)) = - return Meta.isexpr(ex, :function) || is_short_function_def(ex) || Meta.isexpr(ex, :->) - function is_short_function_def(@nospecialize(ex)) - Meta.isexpr(ex, :(=)) || return false - while length(ex.args) >= 1 && isa(ex.args[1], Expr) - (ex.args[1].head === :call) && return true - (ex.args[1].head === :where || ex.args[1].head === :(::)) || return false - ex = ex.args[1] - end - return false - end -end - -# https://github.com/JuliaLang/julia/pull/43852 -@static if VERSION < v"1.8.0-DEV.1484" - macro assume_effects(args...) - esc(last(args)) - end -else - using Base: @assume_effects -end - -if VERSION < v"1.7.0-DEV.119" - # Part of: - # https://github.com/JuliaLang/julia/pull/35316 - # https://github.com/JuliaLang/julia/pull/41076 - isunordered(x) = false - isunordered(x::AbstractFloat) = isnan(x) - isunordered(x::Missing) = true - - isgreater(x, y) = isunordered(x) || isunordered(y) ? isless(x, y) : isless(y, x) - - Base.findmax(f, domain) = mapfoldl( ((k, v),) -> (f(v), k), _rf_findmax, pairs(domain) ) - _rf_findmax((fm, im), (fx, ix)) = isless(fm, fx) ? (fx, ix) : (fm, im) - - Base.findmin(f, domain) = mapfoldl( ((k, v),) -> (f(v), k), _rf_findmin, pairs(domain) ) - _rf_findmin((fm, im), (fx, ix)) = isgreater(fm, fx) ? (fx, ix) : (fm, im) - - Base.argmax(f, domain) = mapfoldl(x -> (f(x), x), _rf_findmax, domain)[2] - Base.argmin(f, domain) = mapfoldl(x -> (f(x), x), _rf_findmin, domain)[2] -end - -# https://github.com/JuliaLang/julia/pull/40729 -if VERSION < v"1.7.0-DEV.1088" - macro something(args...) - expr = :(nothing) - for arg in reverse(args) - expr = :((val = $arg) !== nothing ? val : $expr) - end - return esc(:(something(let val; $expr; end))) - end - - macro coalesce(args...) - expr = :(missing) - for arg in reverse(args) - expr = :((val = $arg) !== missing ? val : $expr) - end - return esc(:(let val; $expr; end)) - end - - export @something, @coalesce -end - -# https://github.com/JuliaLang/julia/pull/41007 -if VERSION < v"1.7.0-DEV.1220" - Base.get(f::Base.Callable, A::AbstractArray, i::Integer) = checkbounds(Bool, A, i) ? A[i] : f() - Base.get(f::Base.Callable, A::AbstractArray, I::Tuple{}) = checkbounds(Bool, A) ? A[] : f() - Base.get(f::Base.Callable, A::AbstractArray, I::Dims) = checkbounds(Bool, A, I...) ? A[I...] : f() - - Base.get(t::Tuple, i::Integer, default) = i in 1:length(t) ? getindex(t, i) : default - Base.get(f::Base.Callable, t::Tuple, i::Integer) = i in 1:length(t) ? getindex(t, i) : f() -end - -# https://github.com/JuliaLang/julia/pull/41032 -if VERSION < v"1.7.0-DEV.1230" - Base.get(x::Number, i::Integer, default) = isone(i) ? x : default - Base.get(x::Number, ind::Tuple, default) = all(isone, ind) ? x : default - Base.get(f::Base.Callable, x::Number, i::Integer) = isone(i) ? x : f() - Base.get(f::Base.Callable, x::Number, ind::Tuple) = all(isone, ind) ? x : f() -end - -# https://github.com/JuliaLang/julia/pull/29901 -if VERSION < v"1.7.0-DEV.1106" - struct ExceptionStack <: AbstractArray{Any,1} - stack - end - - function current_exceptions(task=current_task(); backtrace=true) - old_stack = Base.catch_stack(task, include_bt=backtrace) - # If include_bt=true, Base.catch_stack yields a Vector of two-tuples, - # where the first element of each tuple is an exception and the second - # element is the corresponding backtrace. If instead include_bt=false, - # Base.catch_stack yields a Vector of exceptions. - # - # Independent of its backtrace keyword argument, Base.current_exceptions - # yields an ExceptionStack that wraps a Vector of two-element - # NamedTuples, where the first element of each named tuple is an exception - # and the second element is either a correpsonding backtrace or `nothing`. - # - # The following constructs the ExceptionStack-wrapped Vector appropriately. - new_stack = backtrace ? - Any[(exception=exc_and_bt[1], backtrace=exc_and_bt[2]) for exc_and_bt in old_stack] : - Any[(exception=exc_only, backtrace=nothing) for exc_only in old_stack] - return ExceptionStack(new_stack) - end - - Base.size(s::ExceptionStack) = size(s.stack) - Base.getindex(s::ExceptionStack, i::Int) = s.stack[i] - - function show_exception_stack(io::IO, stack) - # Display exception stack with the top of the stack first. This ordering - # means that the user doesn't have to scroll up in the REPL to discover the - # root cause. - nexc = length(stack) - for i = nexc:-1:1 - if nexc != i - printstyled(io, "\ncaused by: ", color=Base.error_color()) - end - exc, bt = stack[i] - showerror(io, exc, bt, backtrace = bt!==nothing) - i == 1 || println(io) - end - end - - function Base.display_error(io::IO, stack::ExceptionStack) - printstyled(io, "ERROR: "; bold=true, color=Base.error_color()) - # Julia >=1.2 provides Base.scrub_repl_backtrace; we use it - # where possible and otherwise leave backtraces unscrubbed. - backtrace_scrubber = VERSION >= v"1.2" ? Base.scrub_repl_backtrace : identity - bt = Any[ (x[1], backtrace_scrubber(x[2])) for x in stack ] - show_exception_stack(IOContext(io, :limit => true), bt) - println(io) - end - - function Base.show(io::IO, ::MIME"text/plain", stack::ExceptionStack) - nexc = length(stack) - printstyled(io, nexc, "-element ExceptionStack", nexc == 0 ? "" : ":\n") - show_exception_stack(io, stack) - end - Base.show(io::IO, stack::ExceptionStack) = show(io, MIME("text/plain"), stack) - - export current_exceptions -end - -# https://github.com/JuliaLang/julia/pull/39794 -if VERSION < v"1.7.0-DEV.793" - export Returns - - struct Returns{V} <: Function - value::V - Returns{V}(value) where {V} = new{V}(value) - Returns(value) = new{Core.Typeof(value)}(value) - end - - (obj::Returns)(args...; kw...) = obj.value - function Base.show(io::IO, obj::Returns) - show(io, typeof(obj)) - print(io, "(") - show(io, obj.value) - print(io, ")") - end -end - -# https://github.com/JuliaLang/julia/pull/39037 -if VERSION < v"1.7.0-DEV.204" - # Borrowed from julia base - export ismutabletype - function ismutabletype(@nospecialize(t::Type)) - t = Base.unwrap_unionall(t) - # TODO: what to do for `Union`? - return isa(t, DataType) && t.mutable - end -end - -# https://github.com/JuliaLang/julia/pull/42125 -if !isdefined(Base, Symbol("@constprop")) - if isdefined(Base, Symbol("@aggressive_constprop")) - macro constprop(setting, ex) - if isa(setting, QuoteNode) - setting = setting.value - end - setting === :aggressive && return esc(:(Base.@aggressive_constprop $ex)) - setting === :none && return esc(ex) - throw(ArgumentError("@constprop $setting not supported")) - end - else - macro constprop(setting, ex) - if isa(setting, QuoteNode) - setting = setting.value - end - setting === :aggressive || setting === :none || throw(ArgumentError("@constprop $setting not supported")) - return esc(ex) - end - end -else - using Base: @constprop -end - -# https://github.com/JuliaLang/julia/pull/40803 -if VERSION < v"1.8.0-DEV.300" - function Base.convert(::Type{T}, x::CompoundPeriod) where T<:Period - return isconcretetype(T) ? sum(T, x.periods) : throw(MethodError(convert, (T, x))) - end -end - -# https://github.com/JuliaLang/julia/pull/39245 -if VERSION < v"1.8.0-DEV.487" - export eachsplit - - @doc """ - eachsplit(str::AbstractString, dlm; limit::Integer=0) - eachsplit(str::AbstractString; limit::Integer=0) - - Split `str` on occurrences of the delimiter(s) `dlm` and return an iterator over the - substrings. `dlm` can be any of the formats allowed by [`findnext`](@ref)'s first argument - (i.e. as a string, regular expression or a function), or as a single character or collection - of characters. - - If `dlm` is omitted, it defaults to [`isspace`](@ref). - - The iterator will return a maximum of `limit` results if the keyword argument is supplied. - The default of `limit=0` implies no maximum. - - See also [`split`](@ref). - - # Examples - ```julia - julia> a = "Ma.rch" - "Ma.rch" - julia> collect(eachsplit(a, ".")) - 2-element Vector{SubString}: - "Ma" - "rch" - ``` - """ eachsplit - - function eachsplit end - - struct SplitIterator{S<:AbstractString,F} - str::S - splitter::F - limit::Int - keepempty::Bool - end - - Base.eltype(::Type{<:SplitIterator}) = SubString - Base.IteratorSize(::Type{<:SplitIterator}) = Base.SizeUnknown() - - function Base.iterate(iter::SplitIterator, (i, k, n)=(firstindex(iter.str), firstindex(iter.str), 0)) - i - 1 > ncodeunits(iter.str)::Int && return nothing - r = findnext(iter.splitter, iter.str, k)::Union{Nothing,Int,UnitRange{Int}} - while r !== nothing && n != iter.limit - 1 && first(r) <= ncodeunits(iter.str) - r = r::Union{Int,UnitRange{Int}} #commit dcc2182db228935fe97d03a44ae3b6889e40c542 - #follow #39245, improve inferrability of iterate(::SplitIterator) - #Somehow type constraints from the complex `while` condition don't - #propagate to the `while` body. - j, k = first(r), nextind(iter.str, last(r))::Int - k_ = k <= j ? nextind(iter.str, j) : k - if i < k - substr = @inbounds SubString(iter.str, i, prevind(iter.str, j)::Int) - (iter.keepempty || i < j) && return (substr, (k, k_, n + 1)) - i = k - end - k = k_ - r = findnext(iter.splitter, iter.str, k)::Union{Nothing,Int,UnitRange{Int}} - end - iter.keepempty || i <= ncodeunits(iter.str) || return nothing - @inbounds SubString(iter.str, i), (ncodeunits(iter.str) + 2, k, n + 1) - end - - eachsplit(str::T, splitter; limit::Integer=0, keepempty::Bool=true) where {T<:AbstractString} = - SplitIterator(str, splitter, limit, keepempty) - - eachsplit(str::T, splitter::Union{Tuple{Vararg{AbstractChar}},AbstractVector{<:AbstractChar},Set{<:AbstractChar}}; - limit::Integer=0, keepempty=true) where {T<:AbstractString} = - eachsplit(str, in(splitter); limit=limit, keepempty=keepempty) - - eachsplit(str::T, splitter::AbstractChar; limit::Integer=0, keepempty=true) where {T<:AbstractString} = - eachsplit(str, isequal(splitter); limit=limit, keepempty=keepempty) - - eachsplit(str::AbstractString; limit::Integer=0, keepempty=false) = - eachsplit(str, isspace; limit=limit, keepempty=keepempty) -end - -# https://github.com/JuliaLang/julia/pull/43354 -if VERSION < v"1.8.0-DEV.1494" # 98e60ffb11ee431e462b092b48a31a1204bd263d - export allequal - allequal(itr) = isempty(itr) ? true : all(isequal(first(itr)), itr) - allequal(c::Union{AbstractSet,AbstractDict}) = length(c) <= 1 - allequal(r::AbstractRange) = iszero(step(r)) || length(r) <= 1 -else - import Base: allequal # extended below -end - -# https://github.com/JuliaLang/julia/commit/bdf9ead91e5a8dfd91643a17c1626032faada329 -if VERSION < v"1.8.0-DEV.1109" - # we do not add the methods for == and isless that are included in the above - # commit, since they are already present in earlier versions. - import Base: /, rem, mod, lcm, gcd, div - for op in (:/, :rem, :mod, :lcm, :gcd) - @eval ($op)(x::Period, y::Period) = ($op)(promote(x, y)...) - end - div(x::Period, y::Period, r::RoundingMode) = div(promote(x, y)..., r) -end - -# This function is available as of Julia 1.7. -@static if !isdefined(Base, :keepat!) - export keepat! - - keepat!(B::BitVector, inds) = _keepat!(B, inds) - keepat!(B::BitVector, inds::AbstractVector{Bool}) = _keepat!(B, inds) - keepat!(a::Vector, inds) = _keepat!(a, inds) - keepat!(a::Vector, m::AbstractVector{Bool}) = _keepat!(a, m) - - function _keepat!(a::AbstractVector, inds) - local prev - i = firstindex(a) - for k in inds - if @isdefined(prev) - prev < k || throw(ArgumentError("indices must be unique and sorted")) - end - ak = a[k] # must happen even when i==k for bounds checking - if i != k - @inbounds a[i] = ak # k > i, so a[i] is inbounds - end - prev = k - i = nextind(a, i) - end - deleteat!(a, i:lastindex(a)) - return a - end - - function _keepat!(a::AbstractVector, m::AbstractVector{Bool}) - length(m) == length(a) || throw(BoundsError(a, m)) - j = firstindex(a) - for i in eachindex(a, m) - @inbounds begin - if m[i] - i == j || (a[j] = a[i]) - j = nextind(a, j) - end - end - end - deleteat!(a, j:lastindex(a)) - end -end - -# this function is available as of Julia 1.9 -# https://github.com/JuliaLang/julia/pull/45607 -# https://github.com/JuliaLang/julia/pull/45695 -# https://github.com/JuliaLang/julia/pull/45861 -# https://github.com/JuliaLang/julia/pull/46738 -@static if !isdefined(Base, :pkgversion) - using TOML: parsefile - export pkgversion - - const require_lock = isdefined(Base, :require_lock) ? Base.require_lock : Base.ReentrantLock() - const project_names = ("JuliaProject.toml", "Project.toml") - - function locate_project_file(env::String) - for proj in project_names - project_file = joinpath(env, proj) - if Base.isfile_casesensitive(project_file) - return project_file - end - end - return nothing - end - - function get_pkgversion_from_path(path) - project_file = locate_project_file(path) - if project_file isa String - d = parsefile(project_file) - v = get(d, "version", nothing) - if v !== nothing - return VersionNumber(v::String) - end - end - return nothing - end - - @doc """ - pkgversion(m::Module) - - Return the version of the package that imported module `m`, - or `nothing` if `m` was not imported from a package, or imported - from a package without a version field set. - - The version is read from the package's Project.toml during package - load. - - To get the version of the package that imported the current module - the form `pkgversion(@__MODULE__)` can be used. - """ pkgversion - - function pkgversion(m::Module) - path = pkgdir(m) - path === nothing && return nothing - Base.@lock require_lock begin - v = get_pkgversion_from_path(path) - # https://github.com/JuliaLang/julia/pull/44318 - @static if hasfield(Base.PkgOrigin, :version) - pkgorigin = get(Base.pkgorigins, Base.PkgId(Base.moduleroot(m)), nothing) - # Cache the version - if pkgorigin !== nothing && pkgorigin.version === nothing - pkgorigin.version = v - end - end - return v - end - end -end - -# https://github.com/JuliaLang/julia/pull/43334 -if VERSION < v"1.9.0-DEV.1163" - import Base: IteratorSize, HasLength, HasShape, OneTo - export stack - - @doc """ - stack(iter; [dims]) - - Combine a collection of arrays (or other iterable objects) of equal size - into one larger array, by arranging them along one or more new dimensions. - - By default the axes of the elements are placed first, - giving `size(result) = (size(first(iter))..., size(iter)...)`. - This has the same order of elements as [`Iterators.flatten`](@ref)`(iter)`. - - With keyword `dims::Integer`, instead the `i`th element of `iter` becomes the slice - [`selectdim`](@ref)`(result, dims, i)`, so that `size(result, dims) == length(iter)`. - In this case `stack` reverses the action of [`eachslice`](@ref) with the same `dims`. - - The various [`cat`](@ref) functions also combine arrays. However, these all - extend the arrays' existing (possibly trivial) dimensions, rather than placing - the arrays along new dimensions. - They also accept arrays as separate arguments, rather than a single collection. - - !!! compat "Julia 1.9" - This function is available in Julia 1.9, or in Compat 4.2. - - # Examples - ```jldoctest - julia> vecs = (1:2, [30, 40], Float32[500, 600]); - - julia> mat = stack(vecs) - 2×3 Matrix{Float32}: - 1.0 30.0 500.0 - 2.0 40.0 600.0 - - julia> mat == hcat(vecs...) == reduce(hcat, collect(vecs)) - true - - julia> vec(mat) == vcat(vecs...) == reduce(vcat, collect(vecs)) - true - - julia> stack(zip(1:4, 10:99)) # accepts any iterators of iterators - 2×4 Matrix{Int64}: - 1 2 3 4 - 10 11 12 13 - - julia> vec(ans) == collect(Iterators.flatten(zip(1:4, 10:99))) - true - - julia> stack(vecs; dims=1) # unlike any cat function, 1st axis of vecs[1] is 2nd axis of result - 3×2 Matrix{Float32}: - 1.0 2.0 - 30.0 40.0 - 500.0 600.0 - - julia> x = rand(3,4); - - julia> x == stack(eachcol(x)) == stack(eachrow(x), dims=1) # inverse of eachslice - true - ``` - - Higher-dimensional examples: - - ```jldoctest - julia> A = rand(5, 7, 11); - - julia> E = eachslice(A, dims=2); # a vector of matrices - - julia> (element = size(first(E)), container = size(E)) - (element = (5, 11), container = (7,)) - - julia> stack(E) |> size - (5, 11, 7) - - julia> stack(E) == stack(E; dims=3) == cat(E...; dims=3) - true - - julia> A == stack(E; dims=2) - true - - julia> M = (fill(10i+j, 2, 3) for i in 1:5, j in 1:7); - - julia> (element = size(first(M)), container = size(M)) - (element = (2, 3), container = (5, 7)) - - julia> stack(M) |> size # keeps all dimensions - (2, 3, 5, 7) - - julia> stack(M; dims=1) |> size # vec(container) along dims=1 - (35, 2, 3) - - julia> hvcat(5, M...) |> size # hvcat puts matrices next to each other - (14, 15) - ``` - """ stack - - stack(iter; dims=:) = _stack(dims, iter) - - @doc """ - stack(f, args...; [dims]) - - Apply a function to each element of a collection, and `stack` the result. - Or to several collections, [`zip`](@ref)ped together. - - The function should return arrays (or tuples, or other iterators) all of the same size. - These become slices of the result, each separated along `dims` (if given) or by default - along the last dimensions. - - See also [`mapslices`](@ref), [`eachcol`](@ref). - - # Examples - ```jldoctest - julia> stack(c -> (c, c-32), "julia") - 2×5 Matrix{Char}: - 'j' 'u' 'l' 'i' 'a' - 'J' 'U' 'L' 'I' 'A' - - julia> stack(eachrow([1 2 3; 4 5 6]), (10, 100); dims=1) do row, n - vcat(row, row .* n, row ./ n) - end - 2×9 Matrix{Float64}: - 1.0 2.0 3.0 10.0 20.0 30.0 0.1 0.2 0.3 - 4.0 5.0 6.0 400.0 500.0 600.0 0.04 0.05 0.06 - ``` - """ stack(f, iter) - - stack(f, iter; dims=:) = _stack(dims, f(x) for x in iter) - stack(f, xs, yzs...; dims=:) = _stack(dims, f(xy...) for xy in zip(xs, yzs...)) - - _stack(dims::Union{Integer, Colon}, iter) = _stack(dims, IteratorSize(iter), iter) - - _stack(dims, ::IteratorSize, iter) = _stack(dims, collect(iter)) - - function _stack(dims, ::Union{HasShape, HasLength}, iter) - S = Base.@default_eltype iter - T = S != Union{} ? eltype(S) : Any # Union{} occurs for e.g. stack(1,2), postpone the error - if isconcretetype(T) - _typed_stack(dims, T, S, iter) - else # Need to look inside, but shouldn't run an expensive iterator twice: - array = iter isa Union{Tuple, AbstractArray} ? iter : collect(iter) - isempty(array) && return _empty_stack(dims, T, S, iter) - T2 = mapreduce(eltype, promote_type, array) - _typed_stack(dims, T2, eltype(array), array) - end - end - - function _typed_stack(::Colon, ::Type{T}, ::Type{S}, A, Aax=_iterator_axes(A)) where {T, S} - xit = iterate(A) - nothing === xit && return _empty_stack(:, T, S, A) - x1, _ = xit - ax1 = _iterator_axes(x1) - B = similar(_ensure_array(x1), T, ax1..., Aax...) - off = firstindex(B) - len = length(x1) - while xit !== nothing - x, state = xit - _stack_size_check(x, ax1) - copyto!(B, off, x) - off += len - xit = iterate(A, state) - end - B - end - - _iterator_axes(x) = _iterator_axes(x, IteratorSize(x)) - _iterator_axes(x, ::HasLength) = (OneTo(length(x)),) - _iterator_axes(x, ::IteratorSize) = axes(x) - - # For some dims values, stack(A; dims) == stack(vec(A)), and the : path will be faster - _typed_stack(dims::Integer, ::Type{T}, ::Type{S}, A) where {T,S} = - _typed_stack(dims, T, S, IteratorSize(S), A) - _typed_stack(dims::Integer, ::Type{T}, ::Type{S}, ::HasLength, A) where {T,S} = - _typed_stack(dims, T, S, HasShape{1}(), A) - function _typed_stack(dims::Integer, ::Type{T}, ::Type{S}, ::HasShape{N}, A) where {T,S,N} - if dims == N+1 - _typed_stack(:, T, S, A, (_vec_axis(A),)) - else - _dim_stack(dims, T, S, A) - end - end - _typed_stack(dims::Integer, ::Type{T}, ::Type{S}, ::IteratorSize, A) where {T,S} = - _dim_stack(dims, T, S, A) - - _vec_axis(A, ax=_iterator_axes(A)) = length(ax) == 1 ? only(ax) : OneTo(prod(length, ax; init=1)) - - @constprop :aggressive function _dim_stack(dims::Integer, ::Type{T}, ::Type{S}, A) where {T,S} - xit = Iterators.peel(A) - nothing === xit && return _empty_stack(dims, T, S, A) - x1, xrest = xit - ax1 = _iterator_axes(x1) - N1 = length(ax1)+1 - dims in 1:N1 || throw(ArgumentError(string("cannot stack slices ndims(x) = ", N1-1, " along dims = ", dims))) - - newaxis = _vec_axis(A) - outax = ntuple(d -> d==dims ? newaxis : ax1[d - (d>dims)], N1) - B = similar(_ensure_array(x1), T, outax...) - - if dims == 1 - _dim_stack!(Val(1), B, x1, xrest) - elseif dims == 2 - _dim_stack!(Val(2), B, x1, xrest) - else - _dim_stack!(Val(dims), B, x1, xrest) - end - B - end - - function _dim_stack!(::Val{dims}, B::AbstractArray, x1, xrest) where {dims} - before = ntuple(d -> Colon(), dims - 1) - after = ntuple(d -> Colon(), ndims(B) - dims) - - i = firstindex(B, dims) - copyto!(view(B, before..., i, after...), x1) - - for x in xrest - _stack_size_check(x, _iterator_axes(x1)) - i += 1 - @inbounds copyto!(view(B, before..., i, after...), x) - end - end - - @inline function _stack_size_check(x, ax1::Tuple) - if _iterator_axes(x) != ax1 - uax1 = map(UnitRange, ax1) - uaxN = map(UnitRange, axes(x)) - throw(DimensionMismatch( - string("stack expects uniform slices, got axes(x) == ", uaxN, " while first had ", uax1))) - end - end - - _ensure_array(x::AbstractArray) = x - _ensure_array(x) = 1:0 # passed to similar, makes stack's output an Array - - _empty_stack(_...) = throw(ArgumentError("`stack` on an empty collection is not allowed")) -end - -if v"1.10.0-" <= VERSION < v"1.10.0-DEV.360" || VERSION < v"1.9.0-beta3" - if VERSION < v"1.9.0-DEV.513" - # https://github.com/JuliaLang/julia/pull/42717 - export Splat # Base does not export this, but we have to keep it for compatibility - - struct Splat{F} <: Function - f::F - Splat(f) = new{Core.Typeof(f)}(f) - end - - (s::Splat)(args) = s.f(args...) - Base.print(io::IO, s::Splat) = print(io, "splat(", s.f, ')') - Base.show(io::IO, s::Splat) = print(io, s) - Base.show(io::IO, ::MIME"text/plain", s::Splat) = show(io, s) - end - - # https://github.com/JuliaLang/julia/pull/48038 - export splat - splat(f) = Splat(f) -end - -# https://github.com/JuliaLang/julia/pull/25085 -if VERSION < v"1.8.0-beta2.17" || v"1.9.0-" <= VERSION < v"1.9.0-DEV.94" - Base.trunc(::Type{Bool}, x::AbstractFloat) = (-1 < x < 2) ? 1 <= x : throw(InexactError(:trunc, Bool, x)) - Base.floor(::Type{Bool}, x::AbstractFloat) = (0 <= x < 2) ? 1 <= x : throw(InexactError(:floor, Bool, x)) - Base.ceil(::Type{Bool}, x::AbstractFloat) = (-1 < x <= 1) ? 0 < x : throw(InexactError(:ceil, Bool, x)) - Base.round(::Type{Bool}, x::AbstractFloat) = (-0.5 <= x < 1.5) ? 0.5 < x : throw(InexactError(:round, Bool, x)) -end - -# https://github.com/JuliaLang/julia/pull/37978 -if VERSION < v"1.7.0-DEV.1187" - function redirect_stdio(;stdin=nothing, stderr=nothing, stdout=nothing) - stdin === nothing || redirect_stdin(stdin) - stderr === nothing || redirect_stderr(stderr) - stdout === nothing || redirect_stdout(stdout) - end - function redirect_stdio(f; stdin=nothing, stderr=nothing, stdout=nothing) - - function resolve(new::Nothing, oldstream, mode) - (new=nothing, close=false, old=nothing) - end - function resolve(path::AbstractString, oldstream,mode) - (new=open(path, mode), close=true, old=oldstream) - end - function resolve(new, oldstream, mode) - (new=new, close=false, old=oldstream) - end - - same_path(x, y) = false - function same_path(x::AbstractString, y::AbstractString) - # if x = y = "does_not_yet_exist.txt" then samefile will return false - (abspath(x) == abspath(y)) || Base.Filesystem.samefile(x,y) - end - if same_path(stderr, stdin) - throw(ArgumentError("stdin and stderr cannot be the same path")) - end - if same_path(stdout, stdin) - throw(ArgumentError("stdin and stdout cannot be the same path")) - end - - new_in , close_in , old_in = resolve(stdin , Base.stdin , "r") - new_out, close_out, old_out = resolve(stdout, Base.stdout, "w") - if same_path(stderr, stdout) - # make sure that in case stderr = stdout = "same/path" - # only a single io is used instead of opening the same file twice - new_err, close_err, old_err = new_out, false, Base.stderr - else - new_err, close_err, old_err = resolve(stderr, Base.stderr, "w") - end - - redirect_stdio(; stderr=new_err, stdin=new_in, stdout=new_out) - - try - return f() - finally - redirect_stdio(;stderr=old_err, stdin=old_in, stdout=old_out) - close_err && close(new_err) - close_in && close(new_in ) - close_out && close(new_out) - end - end - - export redirect_stdio -end - # https://github.com/JuliaLang/julia/pull/47679 if VERSION < v"1.11.0-DEV.1562" - Base.allunique(f, xs) = allunique(Base.Generator(f, xs)) - function Base.allunique(f::F, t::Tuple) where {F} + allunique(f, xs) = Base.allunique(Base.Generator(f, xs)) + function allunique(f::F, t::Tuple) where {F} length(t) < 2 && return true length(t) < 32 || return Base._hashed_allunique(Base.Generator(f, t)) - return allunique(map(f, t)) + return Base.allunique(map(f, t)) end + allunique(args...) = Base.allunique(args...) - # allequal is either imported or defined above - allequal(f, xs) = allequal(Base.Generator(f, xs)) + allequal(f, xs) = Base.allequal(Base.Generator(f, xs)) function allequal(f, xs::Tuple) length(xs) <= 1 && return true f1 = f(xs[1]) @@ -795,16 +21,17 @@ if VERSION < v"1.11.0-DEV.1562" end return true end -end - -# https://github.com/JuliaLang/julia/pull/45052 -if VERSION < v"1.9.0-DEV.461" - Base.VersionNumber(v::VersionNumber) = v + allequal(args...) = Base.allequal(args...) +else + const allunique = Base.allunique + const allequal = Base.allequal end # https://github.com/JuliaLang/julia/pull/47354 -if VERSION < v"1.11.0-DEV.1579" - Iterators.cycle(xs, n::Integer) = Iterators.flatten(Iterators.repeated(xs, n)) +@static if VERSION < v"1.11.0-DEV.1579" + include("Iterators.jl") +else + const Iterators = Base.Iterators end # https://github.com/JuliaLang/julia/pull/39071 @@ -1000,128 +227,30 @@ if !isdefined(Base, :logrange) # VERSION < v"1.12.0-DEV.2" or appropriate 1.11. _exp_allowing_twice64(x::Number) = exp(x) - if VERSION >= v"1.9.0-DEV.318" # Julia PR #44717 allows this high-precision path: + _exp_allowing_twice64(x::Base.TwicePrecision{Float64}) = Base.Math.exp_impl(x.hi, x.lo, Val(:ℯ)) - _exp_allowing_twice64(x::Base.TwicePrecision{Float64}) = Base.Math.exp_impl(x.hi, x.lo, Val(:ℯ)) - - function _log_twice64_unchecked(x::Float64) - xu = reinterpret(UInt64, x) - if xu < (UInt64(1)<<52) # x is subnormal - xu = reinterpret(UInt64, x * 0x1p52) # normalize x - xu &= ~Base.sign_mask(Float64) - xu -= UInt64(52) << 52 # mess with the exponent - end - Base.TwicePrecision(Base.Math._log_ext(xu)...) + function _log_twice64_unchecked(x::Float64) + xu = reinterpret(UInt64, x) + if xu < (UInt64(1)<<52) # x is subnormal + xu = reinterpret(UInt64, x * 0x1p52) # normalize x + xu &= ~Base.sign_mask(Float64) + xu -= UInt64(52) << 52 # mess with the exponent end + Base.TwicePrecision(Base.Math._log_ext(xu)...) + end - function _logrange_extra(a::Float64, b::Float64, len::Int) - loga = _log_twice64_unchecked(a) - logb = _log_twice64_unchecked(b) - # The reason not to do linear interpolation on log(a)..log(b) in `getindex` is - # that division of TwicePrecision is quite slow, so do it once on construction: - (loga/(len-1), logb/(len-1)) - end + function _logrange_extra(a::Float64, b::Float64, len::Int) + loga = _log_twice64_unchecked(a) + logb = _log_twice64_unchecked(b) + # The reason not to do linear interpolation on log(a)..log(b) in `getindex` is + # that division of TwicePrecision is quite slow, so do it once on construction: + (loga/(len-1), logb/(len-1)) end else # Ensure that Compat.LogRange is always this struct, not exported from Base using Base: LogRange end -# https://github.com/JuliaLang/julia/pull/40995: add chopprefix, chopsuffix -if VERSION < v"1.8.0-DEV.1016" - function chopprefix(s::AbstractString, prefix::Regex) - m = match(prefix, s, firstindex(s), Base.PCRE.ANCHORED) - m === nothing && return SubString(s) - return SubString(s, ncodeunits(m.match) + 1) - end - - function chopsuffix(s::AbstractString, suffix::Regex) - m = match(suffix, s, firstindex(s), Base.PCRE.ENDANCHORED) - m === nothing && return SubString(s) - isempty(m.match) && return SubString(s) - return SubString(s, firstindex(s), prevind(s, m.offset)) - end - - """ - chopprefix(s::AbstractString, prefix::Union{AbstractString,Regex}) -> SubString - - Remove the prefix `prefix` from `s`. If `s` does not start with `prefix`, a string equal to `s` is returned. - - See also [`chopsuffix`](@ref). - - # Examples - ```jldoctest - julia> chopprefix("Hamburger", "Ham") - "burger" - - julia> chopprefix("Hamburger", "hotdog") - "Hamburger" - ``` - """ - function chopprefix(s::AbstractString, prefix::AbstractString) - k = firstindex(s) - i, j = iterate(s), iterate(prefix) - while true - j === nothing && i === nothing && return SubString(s, 1, 0) # s == prefix: empty result - j === nothing && return @inbounds SubString(s, k) # ran out of prefix: success! - i === nothing && return SubString(s) # ran out of source: failure - i[1] == j[1] || return SubString(s) # mismatch: failure - k = i[2] - i, j = iterate(s, k), iterate(prefix, j[2]) - end - end - - function chopprefix(s::Union{String, SubString{String}}, - prefix::Union{String, SubString{String}}) - if startswith(s, prefix) - SubString(s, 1 + ncodeunits(prefix)) - else - SubString(s) - end - end - - """ - chopsuffix(s::AbstractString, suffix::Union{AbstractString,Regex}) -> SubString - - Remove the suffix `suffix` from `s`. If `s` does not end with `suffix`, a string equal to `s` is returned. - - See also [`chopprefix`](@ref). - - # Examples - ```jldoctest - julia> chopsuffix("Hamburger", "er") - "Hamburg" - julia> chopsuffix("Hamburger", "hotdog") - "Hamburger" - ``` - """ - function chopsuffix(s::AbstractString, suffix::AbstractString) - a, b = Iterators.Reverse(s), Iterators.Reverse(suffix) - k = lastindex(s) - i, j = iterate(a), iterate(b) - while true - j === nothing && i === nothing && return SubString(s, 1, 0) # s == suffix: empty result - j === nothing && return @inbounds SubString(s, firstindex(s), k) # ran out of suffix: success! - i === nothing && return SubString(s) # ran out of source: failure - i[1] == j[1] || return SubString(s) # mismatch: failure - k = i[2] - i, j = iterate(a, k), iterate(b, j[2]) - end - end - - function chopsuffix(s::Union{String, SubString{String}}, - suffix::Union{String, SubString{String}}) - if !isempty(suffix) && endswith(s, suffix) - astart = ncodeunits(s) - ncodeunits(suffix) + 1 - @inbounds SubString(s, firstindex(s), prevind(s, astart)) - else - SubString(s) - end - end - - export chopprefix, chopsuffix -end - if VERSION < v"1.12.0-DEV.974" # contrib/commit-name.sh 2635dea insertdims(A; dims) = _insertdims(A, dims) diff --git a/src/Iterators.jl b/src/Iterators.jl new file mode 100644 index 000000000..a8d019561 --- /dev/null +++ b/src/Iterators.jl @@ -0,0 +1,7 @@ +module Iterators + +using Base.Iterators +cycle(xs, n::Integer) = flatten(repeated(xs, n)) +cycle(args...) = Base.Iterators.cycle(args...) + +end diff --git a/src/compatmacro.jl b/src/compatmacro.jl index aa6ee9e79..b93826902 100644 --- a/src/compatmacro.jl +++ b/src/compatmacro.jl @@ -9,15 +9,6 @@ function _compat(ex::Expr) return ex end - # https://github.com/JuliaLang/julia/pull/39285 - @static if VERSION < v"1.7.0-DEV.364" - if Meta.isexpr(ex, :(=)) && Meta.isexpr(ex.args[1], :tuple) && - Meta.isexpr(ex.args[1].args[1], :parameters) - - ex = _destructure_named_tuple(ex) - end - end - return Expr(ex.head, map(_compat, ex.args)...) end @@ -27,19 +18,6 @@ macro compat(ex) esc(_compat(ex)) end -function _destructure_named_tuple(ex::Expr) - ex.args[1].args[1] isa Expr && ex.args[1].args[1].head === :parameters - values = ex.args[2] - parameters = ex.args[1].args[1].args - ex = Expr(:block) - for p in parameters - asgn = Expr(:(=), p, Expr(:., values, QuoteNode(p))) - push!(ex.args, asgn) - end - push!(ex.args, values) - return ex -end - # https://github.com/JuliaLang/julia/pull/50105 macro compat(public::Symbol, symbols_expr::Union{Expr, Symbol}) public == :public || throw(ArgumentError("Invalid Syntax: `@compat $public $symbols_expr`")) diff --git a/src/deprecated.jl b/src/deprecated.jl index 6e88dd21f..5449c8da9 100644 --- a/src/deprecated.jl +++ b/src/deprecated.jl @@ -1,17 +1,14 @@ -if !isdefined(Base, :get_extension) - # LinearAlgebra is a weakdep, but Julia is old enough to ignore that, so we - # can import and use it here - import LinearAlgebra - Base.@deprecate_binding set_num_threads LinearAlgebra.BLAS.set_num_threads false - Base.@deprecate_binding get_num_threads LinearAlgebra.BLAS.get_num_threads false -else - # deprecation is done in the CompatLinearAlgebraExt package extension, but - # the functions need to be declared here - function set_num_threads end - function get_num_threads end +const var"@assume_effects" = getglobal(Base, Symbol("@assume_effects")) +Base.deprecate(@__MODULE__, Symbol("@assume_effects")) + +const var"@constprop" = getglobal(Base, Symbol("@constprop")) +Base.deprecate(@__MODULE__, Symbol("@constprop")) + +if VERSION < v"1.11.0-DEV.1562" + @deprecate Base.allunique(f, xs) Compat.allunique(f, xs) false + @deprecate Base.allequal(f, xs) Compat.allequal(f, xs) false end -Base.@deprecate_binding parseatom Meta.parseatom false -Base.@deprecate_binding parseall Meta.parseall false -import UUIDs -Base.@deprecate_binding uuid5 UUIDs.uuid5 true +if VERSION < v"1.11.0-DEV.1579" + @deprecate Base.Iterators.cycle(xs, n::Integer) Compat.Iterators.cycle(xs, n::Integer) false +end diff --git a/test/runtests.jl b/test/runtests.jl index 0bc5d9c02..093cb5207 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,706 +1,8 @@ using Compat -using Dates -using TOML using Test @test isempty(detect_ambiguities(Base, Core, Compat)) -begin - # A custom linear slow sparse-like array that relies upon Dict for its storage - struct TSlow{T,N} <: AbstractArray{T,N} - data::Dict{NTuple{N,Int}, T} - dims::NTuple{N,Int} - end - TSlow(::Type{T}, dims::Int...) where {T} = TSlow(T, dims) - TSlow(::Type{T}, dims::NTuple{N,Int}) where {T,N} = TSlow{T,N}(Dict{NTuple{N,Int}, T}(), dims) - - TSlow{T,N}(X::TSlow{T,N}) where {T,N } = X - TSlow( X::AbstractArray{T,N}) where {T,N } = TSlow{T,N}(X) - TSlow{T }(X::AbstractArray{_,N}) where {T,N,_} = TSlow{T,N}(X) - TSlow{T,N}(X::AbstractArray ) where {T,N } = begin - A = TSlow(T, size(X)) - for I in CartesianIndices(X) - A[Tuple(I)...] = X[Tuple(I)...] - end - A - end - Base.size(A::TSlow) = A.dims - Base.similar(A::TSlow, ::Type{T}, dims::Dims) where {T} = TSlow(T, dims) - Base.IndexStyle(::Type{A}) where {A<:TSlow} = IndexCartesian() - Base.getindex(A::TSlow{T,N}, i::Vararg{Int,N}) where {T,N} = get(A.data, i, zero(T)) - Base.setindex!(A::TSlow{T,N}, v, i::Vararg{Int,N}) where {T,N} = (A.data[i] = v) -end - -# https://github.com/JuliaLang/julia/pull/35316 -# https://github.com/JuliaLang/julia/pull/41076 -@testset "2arg" begin - @testset "findmin(f, domain)" begin - @test findmin(-, 1:10) == (-10, 10) - @test findmin(identity, [1, 2, 3, missing]) === (missing, 4) - @test findmin(identity, [1, NaN, 3, missing]) === (missing, 4) - @test findmin(identity, [1, missing, NaN, 3]) === (missing, 2) - @test findmin(identity, [1, NaN, 3]) === (NaN, 2) - @test findmin(identity, [1, 3, NaN]) === (NaN, 3) - @test all(findmin(cos, 0:π/2:2π) .≈ (-1.0, 3)) - end - - @testset "findmax(f, domain)" begin - @test findmax(-, 1:10) == (-1, 1) - @test findmax(identity, [1, 2, 3, missing]) === (missing, 4) - @test findmax(identity, [1, NaN, 3, missing]) === (missing, 4) - @test findmax(identity, [1, missing, NaN, 3]) === (missing, 2) - @test findmax(identity, [1, NaN, 3]) === (NaN, 2) - @test findmax(identity, [1, 3, NaN]) === (NaN, 3) - @test findmax(cos, 0:π/2:2π) == (1.0, 1) - end - - @testset "argmin(f, domain)" begin - @test argmin(-, 1:10) == 10 - @test argmin(sum, Iterators.product(1:5, 1:5)) == (1, 1) - end - - @testset "argmax(f, domain)" begin - @test argmax(-, 1:10) == 1 - @test argmax(sum, Iterators.product(1:5, 1:5)) == (5, 5) - end -end - -# https://github.com/JuliaLang/julia/pull/40729 -@testset "@something" begin - @test_throws ArgumentError @something() - @test_throws ArgumentError @something(nothing) - @test @something(1) === 1 - @test @something(Some(nothing)) === nothing - - @test @something(1, error("failed")) === 1 - @test_throws ErrorException @something(nothing, error("failed")) -end - -@testset "@coalesce" begin - @test @coalesce() === missing - @test @coalesce(1) === 1 - @test @coalesce(nothing) === nothing - @test @coalesce(missing) === missing - - @test @coalesce(1, error("failed")) === 1 - @test_throws ErrorException @coalesce(missing, error("failed")) -end - -@testset "get" begin - A = reshape([1:24...], 4, 3, 2) - B = reshape([1:24...], 4, 3, 2) - - global c = 0 - f() = (global c = c+1; 0) - @test get(f, A, ()) == 0 - @test c == 1 - @test get(f, B, ()) == 0 - @test c == 2 - @test get(f, A, (1,)) == get(f, A, 1) == A[1] == 1 - @test c == 2 - @test get(f, B, (1,)) == get(f, B, 1) == B[1] == 1 - @test c == 2 - @test get(f, A, (25,)) == get(f, A, 25) == 0 - @test c == 4 - @test get(f, B, (25,)) == get(f, B, 25) == 0 - @test c == 6 - @test get(f, A, (1,1,1)) == A[1,1,1] == 1 - @test get(f, B, (1,1,1)) == B[1,1,1] == 1 - @test get(f, A, (1,1,3)) == 0 - @test c == 7 - @test get(f, B, (1,1,3)) == 0 - @test c == 8 - @test get(f, TSlow([]), ()) == 0 - @test c == 9 - - @test get((5, 6, 7), 1, 0) == 5 - @test get((), 5, 0) == 0 - @test get((1,), 3, 0) == 0 - @test get(()->0, (5, 6, 7), 1) == 5 - @test get(()->0, (), 4) == 0 - @test get(()->0, (1,), 3) == 0 - - for x in [1.23, 7, ℯ, 4//5] #[FP, Int, Irrational, Rat] - @test get(x, 1, 99) == x - @test get(x, (), 99) == x - @test get(x, (1,), 99) == x - @test get(x, 2, 99) == 99 - @test get(x, 0, pi) == pi - @test get(x, (1,2), pi) == pi - c = Ref(0) - @test get(() -> c[]+=1, x, 1) == x - @test get(() -> c[]+=1, x, ()) == x - @test get(() -> c[]+=1, x, (1,1,1)) == x - @test get(() -> c[]+=1, x, 2) == 1 - @test get(() -> c[]+=1, x, -1) == 2 - @test get(() -> c[]+=1, x, (3,2,1)) == 3 - end -end - -# https://github.com/JuliaLang/julia/pull/39285 -struct X - x -end -@testset "property destructuring assignment" begin - nt = (; a=1, b=2, c=3) - @compat (; c, b) = nt - @test c == nt.c - @test b == nt.b - - @compat (; x) = X(1) - @test x == 1 -end - -# https://github.com/JuliaLang/julia/pull/29901 -@testset "current_exceptions" begin - # Helper method to retrieve an ExceptionStack that should contain two exceptions, - # each of which accompanied by a backtrace or `nothing` according to `with_backtraces`. - function _retrieve_exception_stack(;with_backtraces::Bool) - exception_stack = try - try - # Generate the first exception: - __not_a_binding__ - catch - # Catch the first exception, and generate a second exception - # during what would be handling of the first exception: - 1 ÷ 0 - end - catch - # Retrieve an ExceptionStack with both exceptions, - # and bind `exception_stack` (at the top of this block) thereto: - current_exceptions(;backtrace=with_backtraces) - end - return exception_stack - end - - excs_with_bts = _retrieve_exception_stack(with_backtraces = true) - excs_sans_bts = _retrieve_exception_stack(with_backtraces = false) - - # Check that the ExceptionStack with backtraces contains backtraces: - BACKTRACE_TYPE = Vector{Union{Ptr{Nothing}, Base.InterpreterIP}} - @test all(exc_with_bt[2] isa BACKTRACE_TYPE for exc_with_bt in excs_with_bts) - - # Check that the ExceptionStack without backtraces contains `nothing`s: - @test all(exc_sans_bt[2] isa Nothing for exc_sans_bt in excs_sans_bts) - - # Check that the ExceptionStacks contain the expected exception types: - @test typeof.(first.(excs_with_bts)) == [UndefVarError, DivideError] - @test typeof.(first.(excs_sans_bts)) == [UndefVarError, DivideError] - - # Check that the ExceptionStack with backtraces `show`s correctly: - @test occursin(r""" - 2-element ExceptionStack: - DivideError: integer division error - Stacktrace:.* - - caused by: UndefVarError: `?__not_a_binding__`? not defined( in `Main`)? - (Suggestion: check for spelling errors or missing imports. - )?Stacktrace:.* - """s, sprint(show, excs_with_bts)) - - # Check that the ExceptionStack without backtraces `show`s correctly: - @test occursin(r""" - 2-element ExceptionStack: - DivideError: integer division error - - caused by: UndefVarError: `?__not_a_binding__`? not defined"""s, - sprint(show, excs_sans_bts)) - - # Check that the ExceptionStack with backtraces `display_error`s correctly: - @test occursin(r""" - ERROR: DivideError: integer division error - Stacktrace:.* - - caused by: UndefVarError: `?__not_a_binding__`? not defined( in `Main`)? - (Suggestion: check for spelling errors or missing imports. - )?Stacktrace:.* - """s, sprint(Base.display_error, excs_with_bts)) - - # Check that the ExceptionStack without backtraces `display_error`s correctly: - @test occursin(r""" - ERROR: DivideError: integer division error - - caused by: UndefVarError: `?__not_a_binding__`? not defined"""s, - sprint(Base.display_error, excs_sans_bts)) -end - -# https://github.com/JuliaLang/julia/pull/39794 -@testset "Returns" begin - @test @inferred(Returns(1)() ) === 1 - @test @inferred(Returns(1)(23) ) === 1 - @test @inferred(Returns("a")(2,3)) == "a" - @test @inferred(Returns(1)(x=1, y=2)) === 1 - @test @inferred(Returns(Int)()) === Int - @test @inferred(Returns(Returns(1))()) === Returns(1) - f = @inferred Returns(Int) - @inferred f(1,2) - val = [1,2,3] - @test Returns(val)(1) === val - @test sprint(show, Returns(1.0)) == "Returns{Float64}(1.0)" -end - -# https://github.com/JuliaLang/julia/pull/43852 -@testset "@assume_effects" begin - # ensure proper macro hygiene across versions - Compat.@assume_effects :total foo() = true - Compat.@assume_effects bar() = true - @test foo() - @test bar() -end - -# https://github.com/JuliaLang/julia/pull/42125 -@testset "@constprop" begin - Compat.@constprop :aggressive aggf(x) = Symbol(x) - Compat.@constprop :none nonef(x) = Symbol(x) - @test_throws Exception Meta.lower(@__MODULE__, - quote - Compat.@constprop :other brokenf(x) = Symbol(x) - end - ) - @test aggf("hi") == nonef("hi") == :hi -end - -# https://github.com/JuliaLang/julia/pull/41312 -@testset "`@inline`/`@noinline` annotations within a function body" begin - callf(f, args...) = f(args...) - function foo1(a) - Compat.@inline - sum(sincos(a)) - end - foo2(a) = (Compat.@inline; sum(sincos(a))) - foo3(a) = callf(a) do a - Compat.@inline - sum(sincos(a)) - end - function foo4(a) - Compat.@noinline - sum(sincos(a)) - end - foo5(a) = (Compat.@noinline; sum(sincos(a))) - foo6(a) = callf(a) do a - Compat.@noinline - sum(sincos(a)) - end - - @test foo1(42) == foo2(42) == foo3(42) == foo4(42) == foo5(42) == foo6(42) -end - -# https://github.com/JuliaLang/julia/pull/41328 -@testset "callsite annotations of inlining" begin - function foo1(a) - Compat.@inline begin - return sum(sincos(a)) - end - end - function foo2(a) - Compat.@noinline begin - return sum(sincos(a)) - end - end - - @test foo1(42) == foo2(42) -end - -# https://github.com/JuliaLang/julia/pull/40803 -@testset "Convert CompoundPeriod to Period" begin - @test convert(Month, Year(1) + Month(1)) === Month(13) - @test convert(Second, Minute(1) + Second(30)) === Second(90) - @test convert(Minute, Minute(1) + Second(60)) === Minute(2) - @test convert(Millisecond, Minute(1) + Second(30)) === Millisecond(90_000) - @test_throws InexactError convert(Minute, Minute(1) + Second(30)) - @test_throws MethodError convert(Month, Minute(1) + Second(30)) - @test_throws MethodError convert(Second, Month(1) + Second(30)) - @test_throws MethodError convert(Period, Minute(1) + Second(30)) - @test_throws MethodError convert(Dates.FixedPeriod, Minute(1) + Second(30)) -end - -@testset "ismutabletype" begin - @test ismutabletype(Array) - @test !ismutabletype(Tuple) -end - -# https://github.com/JuliaLang/julia/pull/39245 - -#= -cmcaine commented on Sep 8, 2021 - -This PR implements split with eachsplit and uses eachsplit in a few other places in Base, -so it's kind of already covered by the existing tests. -Not sure it needs any more? - -so, these are the Base.split tests, but replacing split with eachsplit |> collect -=# -@testset "eachsplit" begin - @test eachsplit("foo,bar,baz", 'x') |> collect == ["foo,bar,baz"] - @test eachsplit("foo,bar,baz", ',') |> collect == ["foo","bar","baz"] - @test eachsplit("foo,bar,baz", ",") |> collect == ["foo","bar","baz"] - @test eachsplit("foo,bar,baz", r",") |> collect == ["foo","bar","baz"] - @test eachsplit("foo,bar,baz", ','; limit=0) |> collect == ["foo","bar","baz"] - @test eachsplit("foo,bar,baz", ','; limit=1) |> collect == ["foo,bar,baz"] - @test eachsplit("foo,bar,baz", ','; limit=2) |> collect == ["foo","bar,baz"] - @test eachsplit("foo,bar,baz", ','; limit=3) |> collect == ["foo","bar","baz"] - @test eachsplit("foo,bar", "o,b") |> collect == ["fo","ar"] - - @test eachsplit("", ',') |> collect == [""] - @test eachsplit(",", ',') |> collect == ["",""] - @test eachsplit(",,", ',') |> collect == ["","",""] - @test eachsplit("", ',' ; keepempty=false) |> collect == SubString[] - @test eachsplit(",", ',' ; keepempty=false) |> collect == SubString[] - @test eachsplit(",,", ','; keepempty=false) |> collect == SubString[] - - @test eachsplit("a b c") |> collect == ["a","b","c"] - @test eachsplit("a b \t c\n") |> collect == ["a","b","c"] - @test eachsplit("α β \u2009 γ\n") |> collect == ["α","β","γ"] - - @test eachsplit("a b c"; limit=2) |> collect == ["a","b c"] - @test eachsplit("a b \t c\n"; limit=3) |> collect == ["a","b","\t c\n"] - @test eachsplit("a b c"; keepempty=true) |> collect == ["a","b","c"] - @test eachsplit("a b \t c\n"; keepempty=true) |> collect == ["a","","b","","","c",""] - - let str = "a.:.ba..:..cba.:.:.dcba.:." - @test eachsplit(str, ".:.") |> collect == ["a","ba.",".cba",":.dcba",""] - @test eachsplit(str, ".:."; keepempty=false) |> collect == ["a","ba.",".cba",":.dcba"] - @test eachsplit(str, ".:.") |> collect == ["a","ba.",".cba",":.dcba",""] - @test eachsplit(str, r"\.(:\.)+") |> collect == ["a","ba.",".cba","dcba",""] - @test eachsplit(str, r"\.(:\.)+"; keepempty=false) |> collect == ["a","ba.",".cba","dcba"] - @test eachsplit(str, r"\.+:\.+") |> collect == ["a","ba","cba",":.dcba",""] - @test eachsplit(str, r"\.+:\.+"; keepempty=false) |> collect == ["a","ba","cba",":.dcba"] - end - - # zero-width splits - @test eachsplit("", "") |> collect == rsplit("", "") == [""] - @test eachsplit("abc", "") |> collect == rsplit("abc", "") == ["a","b","c"] - @test eachsplit("abc", "", limit=2) |> collect == ["a","bc"] - - @test eachsplit("", r"") |> collect == [""] - @test eachsplit("abc", r"") |> collect == ["a","b","c"] - @test eachsplit("abcd", r"b?") |> collect == ["a","c","d"] - @test eachsplit("abcd", r"b*") |> collect == ["a","c","d"] - @test eachsplit("abcd", r"b+") |> collect == ["a","cd"] - @test eachsplit("abcd", r"b?c?") |> collect == ["a","d"] - @test eachsplit("abcd", r"[bc]?") |> collect == ["a","","d"] - @test eachsplit("abcd", r"a*") |> collect == ["","b","c","d"] - @test eachsplit("abcd", r"a+") |> collect == ["","bcd"] - @test eachsplit("abcd", r"d*") |> collect == ["a","b","c",""] - @test eachsplit("abcd", r"d+") |> collect == ["abc",""] - @test eachsplit("abcd", r"[ad]?") |> collect == ["","b","c",""] - - # multi-byte unicode characters (issue #26225) - @test eachsplit("α β γ", " ") |> collect == rsplit("α β γ", " ") == - eachsplit("α β γ", isspace) |> collect == rsplit("α β γ", isspace) == ["α","β","γ"] - @test eachsplit("ö.", ".") |> collect == rsplit("ö.", ".") == ["ö",""] - @test eachsplit("α β γ", "β") |> collect == rsplit("α β γ", "β") == ["α "," γ"] -end - -# https://github.com/JuliaLang/julia/pull/43354 -@testset "allequal" begin - @test allequal(Set()) - @test allequal(Set(1)) - @test !allequal(Set([1, 2])) - @test allequal(Dict()) - @test allequal(Dict(:a => 1)) - @test !allequal(Dict(:a => 1, :b => 2)) - @test allequal([]) - @test allequal([1]) - @test allequal([1, 1]) - @test !allequal([1, 1, 2]) - @test allequal([:a, :a]) - @test !allequal([:a, :b]) - @test !allequal(1:2) - @test allequal(1:1) - @test !allequal(4.0:0.3:7.0) - @test allequal(4:-1:5) # empty range - @test !allequal(7:-1:1) # negative step - @test !allequal(Date(2018, 8, 7):Day(1):Date(2018, 8, 11)) # JuliaCon 2018 - @test !allequal(DateTime(2018, 8, 7):Hour(1):DateTime(2018, 8, 11)) - @test allequal(StepRangeLen(1.0, 0.0, 2)) - @test !allequal(StepRangeLen(1.0, 1.0, 2)) - @test allequal(LinRange(1, 1, 0)) - @test allequal(LinRange(1, 1, 1)) - @test allequal(LinRange(1, 1, 2)) - @test !allequal(LinRange(1, 2, 2)) -end - -@testset "keepat!" begin - a = [1:6;] - @test a === keepat!(a, 1:5) - @test a == 1:5 - @test keepat!(a, [2, 4]) == [2, 4] - @test isempty(keepat!(a, [])) - - a = [1:6;] - @test_throws BoundsError keepat!(a, 1:10) # make sure this is not a no-op - @test_throws BoundsError keepat!(a, 2:10) - @test_throws ArgumentError keepat!(a, [2, 4, 3]) - - b = BitVector([1, 1, 1, 0, 0]) - @test b === keepat!(b, 1:5) - @test b == [1, 1, 1, 0, 0] - @test keepat!(b, 2:4) == [1, 1, 0] - @test_throws BoundsError keepat!(a, -1:10) - @test_throws ArgumentError keepat!(a, [2, 1]) - @test isempty(keepat!(a, [])) - - # Vector - a = Vector(1:10) - keepat!(a, [falses(5); trues(5)]) - @test a == 6:10 - @test_throws BoundsError keepat!(a, trues(1)) - @test_throws BoundsError keepat!(a, trues(11)) - - # BitVector - ba = rand(10) .> 0.5 - @test isa(ba, BitArray) - keepat!(ba, ba) - @test all(ba) - - # empty array - ea = [] - keepat!(ea, Bool[]) - @test isempty(ea) -end - -@testset "pkgversion" begin - toml = joinpath(pkgdir(Compat), "Project.toml") - @test pkgversion(Compat) == VersionNumber(TOML.parsefile(toml)["version"]) - @test pkgversion(Base) === nothing -end - -# https://github.com/JuliaLang/julia/pull/43334 -@testset "stack" begin - # Basics - for args in ([[1, 2]], [1:2, 3:4], [[1 2; 3 4], [5 6; 7 8]], - AbstractVector[1:2, [3.5, 4.5]], Vector[[1,2], [3im, 4im]], - [[1:2, 3:4], [5:6, 7:8]], [fill(1), fill(2)]) - X = stack(args) - Y = cat(args...; dims=ndims(args[1])+1) - @test X == Y - @test typeof(X) === typeof(Y) - - X2 = stack(x for x in args) - @test X2 == Y - @test typeof(X2) === typeof(Y) - - X3 = stack(x for x in args if true) - @test X3 == Y - @test typeof(X3) === typeof(Y) - - if isconcretetype(eltype(args)) - @inferred stack(args) - @inferred stack(x for x in args) - end - end - - # Higher dims - @test size(stack([rand(2,3) for _ in 1:4, _ in 1:5])) == (2,3,4,5) - @test size(stack(rand(2,3) for _ in 1:4, _ in 1:5)) == (2,3,4,5) - @test size(stack(rand(2,3) for _ in 1:4, _ in 1:5 if true)) == (2, 3, 20) - @test size(stack([rand(2,3) for _ in 1:4, _ in 1:5]; dims=1)) == (20, 2, 3) - @test size(stack(rand(2,3) for _ in 1:4, _ in 1:5; dims=2)) == (2, 20, 3) - - # Tuples - @test stack([(1,2), (3,4)]) == [1 3; 2 4] - @test stack(((1,2), (3,4))) == [1 3; 2 4] - @test stack(Any[(1,2), (3,4)]) == [1 3; 2 4] - @test stack([(1,2), (3,4)]; dims=1) == [1 2; 3 4] - @test stack(((1,2), (3,4)); dims=1) == [1 2; 3 4] - @test stack(Any[(1,2), (3,4)]; dims=1) == [1 2; 3 4] - @test size(@inferred stack(Iterators.product(1:3, 1:4))) == (2,3,4) - @test @inferred(stack([('a', 'b'), ('c', 'd')])) == ['a' 'c'; 'b' 'd'] - @test @inferred(stack([(1,2+3im), (4, 5+6im)])) isa Matrix{Number} - - # stack(f, iter) - @test @inferred(stack(x -> [x, 2x], 3:5)) == [3 4 5; 6 8 10] - @test @inferred(stack(x -> x*x'/2, [1:2, 3:4])) == reshape([0.5, 1.0, 1.0, 2.0, 4.5, 6.0, 6.0, 8.0], 2, 2, 2) - @test @inferred(stack(*, [1:2, 3:4], 5:6)) == [5 18; 10 24] - - # Iterators - @test stack([(a=1,b=2), (a=3,b=4)]) == [1 3; 2 4] - @test stack([(a=1,b=2), (c=3,d=4)]) == [1 3; 2 4] - @test stack([(a=1,b=2), (c=3,d=4)]; dims=1) == [1 2; 3 4] - @test stack([(a=1,b=2), (c=3,d=4)]; dims=2) == [1 3; 2 4] - @test stack((x/y for x in 1:3) for y in 4:5) == (1:3) ./ (4:5)' - @test stack((x/y for x in 1:3) for y in 4:5; dims=1) == (1:3)' ./ (4:5) - - # Exotic - ips = ((Iterators.product([i,i^2], [2i,3i,4i], 1:4)) for i in 1:5) - @test size(stack(ips)) == (2, 3, 4, 5) - @test stack(ips) == cat(collect.(ips)...; dims=4) - ips_cat2 = cat(reshape.(collect.(ips), Ref((2,1,3,4)))...; dims=2) - @test stack(ips; dims=2) == ips_cat2 - @test stack(collect.(ips); dims=2) == ips_cat2 - ips_cat3 = cat(reshape.(collect.(ips), Ref((2,3,1,4)))...; dims=3) - @test stack(ips; dims=3) == ips_cat3 # path for non-array accumulation on non-final dims - @test stack(collect, ips; dims=3) == ips_cat3 # ... and for array accumulation - @test stack(collect.(ips); dims=3) == ips_cat3 - - # Trivial, because numbers are iterable: - @test stack(abs2, 1:3) == [1, 4, 9] == collect(Iterators.flatten(abs2(x) for x in 1:3)) - - # Allocation tests - xv = [rand(10) for _ in 1:100] - xt = Tuple.(xv) - for dims in (1, 2, :) - @test stack(xv; dims) == stack(xt; dims) - @test_skip 9000 > @allocated stack(xv; dims) - @test_skip 9000 > @allocated stack(xt; dims) - end - xr = (reshape(1:1000,10,10,10) for _ = 1:1000) - for dims in (1, 2, 3, :) - stack(xr; dims) - @test_skip 8.1e6 > @allocated stack(xr; dims) - end - - # Mismatched sizes - @test_throws DimensionMismatch stack([1:2, 1:3]) - @test_throws DimensionMismatch stack([1:2, 1:3]; dims=1) - @test_throws DimensionMismatch stack([1:2, 1:3]; dims=2) - @test_throws DimensionMismatch stack([(1,2), (3,4,5)]) - @test_throws DimensionMismatch stack([(1,2), (3,4,5)]; dims=1) - @test_throws DimensionMismatch stack(x for x in [1:2, 1:3]) - @test_throws DimensionMismatch stack([[5 6; 7 8], [1, 2, 3, 4]]) - @test_throws DimensionMismatch stack([[5 6; 7 8], [1, 2, 3, 4]]; dims=1) - @test_throws DimensionMismatch stack(x for x in [[5 6; 7 8], [1, 2, 3, 4]]) - # Inner iterator of unknown length - @test_throws MethodError stack((x for x in 1:3 if true) for _ in 1:4) - @test_throws MethodError stack((x for x in 1:3 if true) for _ in 1:4; dims=1) - - @test_throws ArgumentError stack([1:3, 4:6]; dims=0) - @test_throws ArgumentError stack([1:3, 4:6]; dims=3) - @test_throws ArgumentError stack(abs2, 1:3; dims=2) - - # Empty - @test_throws ArgumentError stack(()) - @test_throws ArgumentError stack([]) - @test_throws ArgumentError stack(x for x in 1:3 if false) -end - -@testset "promoting ops for TimePeriod" begin - x = 10 - y = 3 - xs = Second(x) - ys = Second(y) - xms = Millisecond(xs) - yms = Millisecond(ys) - for op in (/, div, rem, mod, lcm, gcd) - @test op(xms, yms) == op(xms, ys) == op(xs, yms) - end -end - -@testset "splat" begin - @test splat(+)((1,2,3)) == 6 - - if v"1.10.0-" <= VERSION < v"1.10.0-DEV.360" || - v"1.9.0-DEV.513" <= VERSION < v"1.9.0-beta3" - # these versions of Base export Splat (which we use) but pretty-print with capital `S` - @test repr(splat(+)) == "Splat(+)" - @test repr(MIME"text/plain"(), splat(+)) == "Splat(+)" - else - @test repr(splat(+)) == "splat(+)" - @test repr(MIME"text/plain"(), splat(+)) == "splat(+)" - end -end - -# https://github.com/JuliaLang/julia/pull/25085 -@testset "Bool rounding (#25074)" begin - @testset "round Bool" begin - @test_throws InexactError round(Bool, -4.1) - @test_throws InexactError round(Bool, 1.5) - @test true == round(Bool, 1.0) - @test false == round(Bool, 0.0) - @test true == round(Bool, 0.6) - @test false == round(Bool, 0.4) - @test false == round(Bool, 0.5) - @test false == round(Bool, -0.5) - end - - @testset "trunc Bool" begin - @test_throws InexactError trunc(Bool, -4.1) - @test_throws InexactError trunc(Bool, 2.5) - @test true == trunc(Bool, 1.0) - @test false == trunc(Bool, 0.0) - @test false == trunc(Bool, 0.6) - @test false == trunc(Bool, 0.4) - @test true == trunc(Bool, 1.8) - @test false == trunc(Bool, -0.5) - end - - @testset "floor Bool" begin - @test_throws InexactError floor(Bool, -0.1) - @test_throws InexactError floor(Bool, 2.5) - @test true == floor(Bool, 1.0) - @test false == floor(Bool, 0.0) - @test false == floor(Bool, 0.6) - @test true == floor(Bool, 1.8) - end - - @testset "ceil Bool" begin - @test_throws InexactError ceil(Bool, -1.4) - @test_throws InexactError ceil(Bool, 1.5) - @test true == ceil(Bool, 1.0) - @test false == ceil(Bool, 0.0) - @test true == ceil(Bool, 0.6) - @test false == ceil(Bool, -0.7) - end -end - -# https://github.com/JuliaLang/julia/pull/37978 -@testset "redirect_stdio" begin - randstring() = join(rand('a':'z', 10)) - - function hello_err_out() - println(stderr, "hello from stderr") - println(stdout, "hello from stdout") - end - @testset "same path for multiple streams" begin - @test_throws ArgumentError redirect_stdio(hello_err_out, - stdin="samepath.txt", stdout="samepath.txt") - @test_throws ArgumentError redirect_stdio(hello_err_out, - stdin="samepath.txt", stderr="samepath.txt") - - @test_throws ArgumentError redirect_stdio(hello_err_out, - stdin=joinpath("tricky", "..", "samepath.txt"), - stderr="samepath.txt") - mktempdir() do dir - path = joinpath(dir, "stdouterr.txt") - redirect_stdio(hello_err_out, stdout=path, stderr=path) - @test read(path, String) == """ - hello from stderr - hello from stdout - """ - end - end - - mktempdir() do dir - path_stdout = joinpath(dir, "stdout.txt") - path_stderr = joinpath(dir, "stderr.txt") - redirect_stdio(hello_err_out, stderr=devnull, stdout=path_stdout) - @test read(path_stdout, String) == "hello from stdout\n" - - open(path_stderr, "w") do ioerr - redirect_stdio(hello_err_out, stderr=ioerr, stdout=devnull) - end - @test read(path_stderr, String) == "hello from stderr\n" - end - - mktempdir() do dir - path_stderr = joinpath(dir, "stderr.txt") - path_stdin = joinpath(dir, "stdin.txt") - path_stdout = joinpath(dir, "stdout.txt") - - content_stderr = randstring() - content_stdout = randstring() - - redirect_stdio(stdout=path_stdout, stderr=path_stderr) do - print(content_stdout) - print(stderr, content_stderr) - end - - @test read(path_stderr, String) == content_stderr - @test read(path_stdout, String) == content_stdout - end -end - module Mod50105 using Compat @compat public foo, var"#", baz @@ -733,41 +35,35 @@ end # https://github.com/JuliaLang/julia/pull/47679 @testset "allunique(f, xs)" begin - @test allunique(sin, 1:3) - @test !allunique(sin, [1,2,3,1]) - @test allunique(sin, (1, 2, pi, im)) # eltype Any - @test allunique(abs2, 1:100) - @test !allunique(abs, -10:10) - @test allunique(abs2, Vector{Any}(1:100)) + @test Compat.allunique(sin, 1:3) + @test !Compat.allunique(sin, [1,2,3,1]) + @test Compat.allunique(sin, (1, 2, pi, im)) # eltype Any + @test Compat.allunique(abs2, 1:100) + @test !Compat.allunique(abs, -10:10) + @test Compat.allunique(abs2, Vector{Any}(1:100)) # These cases don't call the function at all: - @test allunique(error, []) - @test_skip allunique(error, [1]) # depends on updated code in Base to work + @test Compat.allunique(error, []) + @test_skip Compat.allunique(error, [1]) # depends on updated code in Base to work end @testset "allequal(f, xs)" begin - @test allequal(abs2, [3, -3]) - @test allequal(x -> 1, rand(3)) - @test !allequal(x -> rand(), [1,1,1]) + @test Compat.allequal(abs2, [3, -3]) + @test Compat.allequal(x -> 1, rand(3)) + @test !Compat.allequal(x -> rand(), [1,1,1]) # tuples - @test allequal(abs2, (3, -3)) - @test allequal(x -> 1, Tuple(rand(3))) - @test !allequal(x -> rand(), (1,1,1)) + @test Compat.allequal(abs2, (3, -3)) + @test Compat.allequal(x -> 1, Tuple(rand(3))) + @test !Compat.allequal(x -> rand(), (1,1,1)) # These cases don't call the function at all: - @test allequal(error, []) - @test allequal(error, ()) - @test allequal(error, (x for x in 1:3 if false)) - @test_skip allequal(error, [1]) # fixed not by new code but by upgrades to old code - @test allequal(error, (1,)) -end - -# https://github.com/JuliaLang/julia/pull/45052 -@testset "VersionNumber no-op constructor" begin - v = VersionNumber("1.2.3") - @test VersionNumber(v) === v + @test Compat.allequal(error, []) + @test Compat.allequal(error, ()) + @test Compat.allequal(error, (x for x in 1:3 if false)) + @test_skip Compat.allequal(error, [1]) # fixed not by new code but by upgrades to old code + @test Compat.allequal(error, (1,)) end # https://github.com/JuliaLang/julia/pull/47354 @testset "cycle(iter, n)" begin - using Base.Iterators: cycle + using Compat.Iterators: cycle @test collect(cycle(0:3, 2)) == [0, 1, 2, 3, 0, 1, 2, 3] @test collect(cycle(Iterators.filter(iseven, 1:4), 2)) == [2, 4, 2, 4] # @test collect(take(cycle(countfrom(11), 3), 4)) == 11:14 # this iterator is defined in Base's tests @@ -848,68 +144,6 @@ end @test_skip repr("text/plain", Compat.LogRange(1,2,0)) == "LogRange{Float64}(1.0, 2.0, 0)" # empty case end -# https://github.com/JuliaLang/julia/pull/40995 -@testset "chopprefix, chopsuffix" begin - SubStr(s) = SubString("abc$(s)de", firstindex(s) + 3, lastindex(s) + 3) - - for S in (String, SubStr, Test.GenericString) - for T in (String, SubStr, Test.GenericString, Regex) - S === Test.GenericString && T === Regex && continue # not supported - @test chopprefix(S("fo∀\n"), T("bog")) == "fo∀\n" - @test chopprefix(S("fo∀\n"), T("\n∀foΔ")) == "fo∀\n" - @test chopprefix(S("fo∀\n"), T("∀foΔ")) == "fo∀\n" - @test chopprefix(S("fo∀\n"), T("f")) == "o∀\n" - @test chopprefix(S("fo∀\n"), T("fo")) == "∀\n" - @test chopprefix(S("fo∀\n"), T("fo∀")) == "\n" - @test chopprefix(S("fo∀\n"), T("fo∀\n")) == "" - @test chopprefix(S("\nfo∀"), T("bog")) == "\nfo∀" - @test chopprefix(S("\nfo∀"), T("\n∀foΔ")) == "\nfo∀" - @test chopprefix(S("\nfo∀"), T("\nfo∀")) == "" - @test chopprefix(S("\nfo∀"), T("\n")) == "fo∀" - @test chopprefix(S("\nfo∀"), T("\nf")) == "o∀" - @test chopprefix(S("\nfo∀"), T("\nfo")) == "∀" - @test chopprefix(S("\nfo∀"), T("\nfo∀")) == "" - @test chopprefix(S(""), T("")) == "" - @test chopprefix(S(""), T("asdf")) == "" - @test chopprefix(S(""), T("∃∃∃")) == "" - @test chopprefix(S("εfoo"), T("ε")) == "foo" - @test chopprefix(S("ofoε"), T("o")) == "foε" - @test chopprefix(S("∃∃∃∃"), T("∃")) == "∃∃∃" - @test chopprefix(S("∃∃∃∃"), T("")) == "∃∃∃∃" - - @test chopsuffix(S("fo∀\n"), T("bog")) == "fo∀\n" - @test chopsuffix(S("fo∀\n"), T("\n∀foΔ")) == "fo∀\n" - @test chopsuffix(S("fo∀\n"), T("∀foΔ")) == "fo∀\n" - @test chopsuffix(S("fo∀\n"), T("\n")) == "fo∀" - @test chopsuffix(S("fo∀\n"), T("∀\n")) == "fo" - @test chopsuffix(S("fo∀\n"), T("o∀\n")) == "f" - @test chopsuffix(S("fo∀\n"), T("fo∀\n")) == "" - @test chopsuffix(S("\nfo∀"), T("bog")) == "\nfo∀" - @test chopsuffix(S("\nfo∀"), T("\n∀foΔ")) == "\nfo∀" - @test chopsuffix(S("\nfo∀"), T("\nfo∀")) == "" - @test chopsuffix(S("\nfo∀"), T("∀")) == "\nfo" - @test chopsuffix(S("\nfo∀"), T("o∀")) == "\nf" - @test chopsuffix(S("\nfo∀"), T("fo∀")) == "\n" - @test chopsuffix(S("\nfo∀"), T("\nfo∀")) == "" - @test chopsuffix(S(""), T("")) == "" - @test chopsuffix(S(""), T("asdf")) == "" - @test chopsuffix(S(""), T("∃∃∃")) == "" - @test chopsuffix(S("fooε"), T("ε")) == "foo" - @test chopsuffix(S("εofo"), T("o")) == "εof" - @test chopsuffix(S("∃∃∃∃"), T("∃")) == "∃∃∃" - @test chopsuffix(S("∃∃∃∃"), T("")) == "∃∃∃∃" - end - - if S !== Test.GenericString - @test chopprefix(S("∃∃∃b∃"), r"∃+") == "b∃" - @test chopsuffix(S("∃b∃∃∃"), r"∃+") == "∃b" - end - - @test isa(chopprefix(S("foo"), "fo"), SubString) - @test isa(chopsuffix(S("foo"), "oo"), SubString) - end -end - # https://github.com/JuliaLang/julia/pull/45793 @testset "insertdims" begin a = rand(8, 7)