From d14d792f342a338bc02232cf70f72d1cb6c189c1 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 31 May 2025 08:28:46 +0000 Subject: [PATCH 1/3] doc: Convert julia-repl blocks to jldoctest format Convert appropriate julia-repl code blocks to jldoctest format to enable automatic testing. In addition, this introduces a new `nodoctest = "reason"` pattern to annotate code blocks that are deliberate not doctested, so future readers will know not to try. Many code blocks are converted, in particular: - Manual pages: arrays.md, asynchronous-programming.md, functions.md, integers-and-floating-point-numbers.md, metaprogramming.md, multi-threading.md, performance-tips.md, variables.md, variables-and-scoping.md - Base documentation: abstractarray.jl, bitarray.jl, expr.jl, file.jl, float.jl, iddict.jl, path.jl, scopedvalues.md, sort.md - Standard library: Dates/conversions.jl, Random/RNGs.jl, Sockets/addrinfo.jl Key changes: - Add filters for non-deterministic output (timing, paths, memory addresses) - Add setup/teardown for filesystem operations - Fix parentmodule(M) usage in expr.jl for doctest compatibility - Document double escaping requirement for regex filters in docstrings - Update AGENTS.md with test running instructions Note: Some julia-repl blocks were intentionally left unchanged when they demonstrate language internals subject to change or contain non-deterministic output that cannot be properly filtered. Refs #56921 --- AGENTS.md | 21 +++++-- base/abstractarray.jl | 2 +- base/bitarray.jl | 2 +- base/expr.jl | 4 +- base/file.jl | 6 +- base/float.jl | 2 +- base/iddict.jl | 2 +- base/path.jl | 2 +- doc/make.jl | 1 - doc/src/base/scopedvalues.md | 57 +++++++++++------ doc/src/base/sort.md | 10 +-- doc/src/devdocs/contributing/jldoctests.md | 28 ++++++++- doc/src/manual/arrays.md | 63 ++++++++++--------- doc/src/manual/asynchronous-programming.md | 4 +- doc/src/manual/functions.md | 9 ++- .../integers-and-floating-point-numbers.md | 6 +- doc/src/manual/metaprogramming.md | 2 +- doc/src/manual/methods.md | 7 ++- doc/src/manual/multi-threading.md | 2 +- doc/src/manual/performance-tips.md | 8 ++- doc/src/manual/strings.md | 9 ++- doc/src/manual/variables-and-scoping.md | 10 +-- doc/src/manual/variables.md | 28 +++++---- stdlib/Dates/src/conversions.jl | 2 +- stdlib/Random/src/RNGs.jl | 2 +- stdlib/Sockets/src/addrinfo.jl | 2 +- 26 files changed, 183 insertions(+), 108 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index e5d9875856df4..b19b8002a2159 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -38,10 +38,23 @@ The test system supports doing this automatically (see below). ### Doctests -If you have changed doctests (i.e. any `jldoctest` code block), you should run the doctests before -preparing a PR. See `doc/README.md` for how to do this. For instructions on writing doctests, see -`doc/src/devdocs/contributing/jldoctests.md`. Note that the doctests may take up to 15 minutes. -Do not terminate the doctests before completion. If you are ChatGPT, you may have to increase yield_timeout_ms. +#### Writing doctests + +If you are asked to write new doctests, first review `doc/src/devdocs/contributing/jldoctests.md` +for best practices. + +#### Verifying doctests +If you have changed any `jldoctest` code blocks you should take +the following steps to verify your work: +- Review `doc/src/devdocs/contributing/jldoctests.md`. In particular, determine + if any of the changed doctests require filters, labels or setup code. +- Run the doctests to verify that your change works: + - To run doctest with the pre-built juliaup: `make -C doc doctest=true revise=true JULIA_EXECUTABLE=$HOME/.juliaup/bin/julia` + - To run doctest with in-trr julia (preferred): `make -C doc doctest=true revise=true`. Do not pass any other options. + - IMPORTANT: The doctests may take up to 15 minutes. Do NOT terminate the doctests before completion. Do NOT use a timeout for doctests. + - If you are ChatGPT, you may have to increase yield_timeout_ms. + +Follow these steps for EVERY change you make in a doctest. ### Test changes diff --git a/base/abstractarray.jl b/base/abstractarray.jl index bf2a6ebabecba..e585ccf7f3f21 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -796,7 +796,7 @@ julia> similar(1:10, 1, 4) Conversely, `similar(trues(10,10), 2)` returns an uninitialized `BitVector` with two elements since `BitArray`s are both mutable and can support 1-dimensional arrays: -```julia-repl +```jldoctest; filter = r"[01]" julia> similar(trues(10,10), 2) 2-element BitVector: 0 diff --git a/base/bitarray.jl b/base/bitarray.jl index e47cf82dd603f..5a3469fa7c7a2 100644 --- a/base/bitarray.jl +++ b/base/bitarray.jl @@ -53,7 +53,7 @@ Construct an undef [`BitArray`](@ref) with the given dimensions. Behaves identically to the [`Array`](@ref) constructor. See [`undef`](@ref). # Examples -```julia-repl +```jldoctest; filter = r"[01]" julia> BitArray(undef, 2, 2) 2×2 BitMatrix: 0 0 diff --git a/base/expr.jl b/base/expr.jl index bf9e2ef2bf92c..b5f7bd8188d50 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -144,7 +144,7 @@ There are differences between `@macroexpand` and [`macroexpand`](@ref). expands with respect to the module in which it is called. This is best seen in the following example: -```julia-repl +```jldoctest julia> module M macro m() 1 @@ -152,7 +152,7 @@ julia> module M function f() (@macroexpand(@m), macroexpand(M, :(@m)), - macroexpand(Main, :(@m)) + macroexpand(parentmodule(M), :(@m)) ) end end diff --git a/base/file.jl b/base/file.jl index 7fbb08c4b6fe6..24a21396721d3 100644 --- a/base/file.jl +++ b/base/file.jl @@ -164,7 +164,7 @@ required intermediate directories. Return `path`. # Examples -```julia-repl +```jldoctest; setup = :(curdir = pwd(); testdir = mktempdir(); cd(testdir)), teardown = :(cd(curdir); rm(testdir, recursive=true)), filter = r"^\\".*testingdir\\"\$" julia> mkdir("testingdir") "testingdir" @@ -516,7 +516,7 @@ If the file does not exist a new file is created. Return `path`. # Examples -```julia-repl +```jldoctest; setup = :(curdir = pwd(); testdir = mktempdir(); cd(testdir)), teardown = :(cd(curdir); rm(testdir, recursive=true)), filter = r"[\\d\\.]+e[\\+\\-]?\\d+" julia> write("my_little_file", 2); julia> mtime("my_little_file") @@ -1134,7 +1134,7 @@ for (path, dirs, files) in walkdir(".") end ``` -```julia-repl +```jldoctest; setup = :(prevdir = pwd(); tmpdir = mktempdir(); cd(tmpdir)), teardown = :(cd(prevdir); rm(tmpdir, recursive=true)) julia> mkpath("my/test/dir"); julia> itr = walkdir("my"); diff --git a/base/float.jl b/base/float.jl index 79d4a26ef930e..ba380102f8751 100644 --- a/base/float.jl +++ b/base/float.jl @@ -83,7 +83,7 @@ julia> NaN == NaN, isequal(NaN, NaN), isnan(NaN) !!! note Always use [`isnan`](@ref) or [`isequal`](@ref) for checking for `NaN`. Using `x === NaN` may give unexpected results: - ```julia-repl + ```jldoctest julia> reinterpret(UInt32, NaN32) 0x7fc00000 diff --git a/base/iddict.jl b/base/iddict.jl index f1632e93427a8..6e3d09454c54e 100644 --- a/base/iddict.jl +++ b/base/iddict.jl @@ -12,7 +12,7 @@ the same, so they get overwritten. The `IdDict` hashes by object-id, and thus preserves the 3 different keys. # Examples -```julia-repl +```jldoctest julia> Dict(true => "yes", 1 => "no", 1.0 => "maybe") Dict{Real, String} with 1 entry: 1.0 => "maybe" diff --git a/base/path.jl b/base/path.jl index 0e67dc4e95db6..a1221e0bdc844 100644 --- a/base/path.jl +++ b/base/path.jl @@ -633,7 +633,7 @@ the [Freedesktop File URI spec](https://www.freedesktop.org/wiki/Specifications/ julia> uripath("/home/user/example file.jl") # On a unix machine "file:///home/user/example%20file.jl" -juila> uripath("C:\\Users\\user\\example file.jl") # On a windows machine +julia> uripath("C:\\Users\\user\\example file.jl") # On a windows machine "file:///C:/Users/user/example%20file.jl" ``` """ diff --git a/doc/make.jl b/doc/make.jl index da78a8420fa2c..bd74316d9fd7e 100644 --- a/doc/make.jl +++ b/doc/make.jl @@ -318,7 +318,6 @@ if use_revise end function maybe_revise(ex) use_revise || return ex - STDLIB_DIR = Sys.STDLIB STDLIBS = filter!(x -> isfile(joinpath(STDLIB_DIR, x, "src", "$(x).jl")), readdir(STDLIB_DIR)) return quote $ex diff --git a/doc/src/base/scopedvalues.md b/doc/src/base/scopedvalues.md index 6ad553429bb1f..d35129642b29b 100644 --- a/doc/src/base/scopedvalues.md +++ b/doc/src/base/scopedvalues.md @@ -27,38 +27,57 @@ Let's first look at an example of **lexical** scope. A `let` statement begins a new lexical scope within which the outer definition of `x` is shadowed by it's inner definition. -```julia +```jldoctest +julia> x = 1 +1 + +julia> let x = 5 + @show x + end; +x = 5 + +julia> @show x; x = 1 -let x = 5 - @show x # 5 -end -@show x # 1 ``` In the following example, since Julia uses lexical scope, the variable `x` in the body of `f` refers to the `x` defined in the global scope, and entering a `let` scope does not change the value `f` observes. -```julia +```jldoctest +julia> x = 1 +1 + +julia> f() = @show x +f (generic function with 1 method) + +julia> let x = 5 + f() + end; +x = 1 + +julia> f(); x = 1 -f() = @show x -let x = 5 - f() # 1 -end -f() # 1 ``` Now using a `ScopedValue` we can use **dynamic** scoping. -```julia -using Base.ScopedValues +```jldoctest +julia> using Base.ScopedValues -x = ScopedValue(1) -f() = @show x[] -with(x=>5) do - f() # 5 -end -f() # 1 +julia> x = ScopedValue(1) +ScopedValue{Int64}(1) + +julia> f() = @show x[] +f (generic function with 1 method) + +julia> with(x=>5) do + f() + end; +x[] = 5 + +julia> f(); +x[] = 1 ``` Note that the observed value of the `ScopedValue` is dependent on the execution diff --git a/doc/src/base/sort.md b/doc/src/base/sort.md index cef080c5f8995..cde0d571424a0 100644 --- a/doc/src/base/sort.md +++ b/doc/src/base/sort.md @@ -39,8 +39,8 @@ julia> a Instead of directly sorting an array, you can compute a permutation of the array's indices that puts the array into sorted order: -```julia-repl -julia> v = randn(5) +```jldoctest sort_example +julia> v = [0.297288, 0.382396, -0.597634, -0.0104452, -0.839027] 5-element Vector{Float64}: 0.297288 0.382396 @@ -67,7 +67,7 @@ julia> v[p] Arrays can be sorted according to an arbitrary transformation of their values: -```julia-repl +```jldoctest sort_example julia> sort(v, by=abs) 5-element Vector{Float64}: -0.0104452 @@ -79,7 +79,7 @@ julia> sort(v, by=abs) Or in reverse order by a transformation: -```julia-repl +```jldoctest sort_example julia> sort(v, by=abs, rev=true) 5-element Vector{Float64}: -0.839027 @@ -91,7 +91,7 @@ julia> sort(v, by=abs, rev=true) If needed, the sorting algorithm can be chosen: -```julia-repl +```jldoctest sort_example julia> sort(v, alg=InsertionSort) 5-element Vector{Float64}: -0.839027 diff --git a/doc/src/devdocs/contributing/jldoctests.md b/doc/src/devdocs/contributing/jldoctests.md index bf989062b1305..fc3ea99b3cc0e 100644 --- a/doc/src/devdocs/contributing/jldoctests.md +++ b/doc/src/devdocs/contributing/jldoctests.md @@ -5,8 +5,17 @@ This page describes how to write and maintain `jldoctest` blocks in the document ## Filters Use `filter =` whenever output contains text that might vary across runs. -The documentation relies on several recurring patterns: +The following are common situations where this may happen: + +- The output contains arrays with undefined memory (e.g. from `undef` or `similar`) +- The output contains random numbers +- The output contains timing information +- The output contains file system pathts + + +### Common filter sequences +The documentation relies on several recurring patterns: - `r"int.jl:\\d+"` — remove line numbers from introspection macros. - `r"Stacktrace:(\\n \\[0-9]+\\].*)*"` — hide stack traces when illustrating errors. @@ -29,6 +38,12 @@ If none of these match your situation, craft a regular expression that removes the varying text. Using filters keeps doctests stable across platforms and Julia versions. +!!! note "Double escaping in docstrings" + When writing regex filters inside docstrings, remember to double escape + backslashes. For example, use `r"[\\d\\.]+"` instead of `r"[\d\.]+"`. + This is necessary because the docstring itself processes escape sequences + before the regex is created. + ## Setup code Small setup expressions may be placed inline using the `setup =` option: @@ -56,6 +71,17 @@ DocTestSetup = nothing ``` ```` +### Teardown code + +If you need teardown code (e.g. to delete created temporary files or to reset +the current directory), you can use the `teardown =` option: + +```` +```jldoctest; setup = :(oldpath = pwd(); cd(mktempdir())), teardown = :(cd(oldpath)) +... +``` +```` + ## Maintaining state between snippets Related doctest blocks can share state by giving them the same label after the diff --git a/doc/src/manual/arrays.md b/doc/src/manual/arrays.md index ee9421ba1730b..6fa27ba75c90f 100644 --- a/doc/src/manual/arrays.md +++ b/doc/src/manual/arrays.md @@ -368,26 +368,26 @@ of the variable ranges `rx`, `ry`, etc. and each `F(x,y,...)` evaluation returns The following example computes a weighted average of the current element and its left and right neighbor along a 1-d grid: -```julia-repl -julia> x = rand(8) -8-element Vector{Float64}: - 0.843025 - 0.869052 - 0.365105 - 0.699456 - 0.977653 - 0.994953 - 0.41084 - 0.809411 +```jldoctest +julia> x = [4, 8, 2, 6, 10, 10, 2, 8] +8-element Vector{Int64}: + 4 + 8 + 2 + 6 + 10 + 10 + 2 + 8 julia> [ 0.25*x[i-1] + 0.5*x[i] + 0.25*x[i+1] for i=2:length(x)-1 ] 6-element Vector{Float64}: - 0.736559 - 0.57468 - 0.685417 - 0.912429 - 0.8446 - 0.656511 + 5.5 + 4.5 + 6.0 + 9.0 + 8.0 + 5.5 ``` The resulting array type depends on the types of the computed elements just like [array literals](@ref man-array-literals) do. In order to control the @@ -413,9 +413,12 @@ julia> sum(1/n^2 for n=1:1000) When writing a generator expression with multiple dimensions inside an argument list, parentheses are needed to separate the generator from subsequent arguments: -```julia-repl +```jldoctest julia> map(tuple, 1/(i+j) for i=1:2, j=1:2, [1:4;]) -ERROR: syntax: invalid iteration specification +ERROR: ParseError: +# Error @ none:1:44 +map(tuple, 1/(i+j) for i=1:2, j=1:2, [1:4;]) +# └ ── invalid iteration spec: expected one of `=` `in` or `∈` ``` All comma-separated expressions after `for` are interpreted as ranges. Adding parentheses lets @@ -1036,33 +1039,33 @@ It is sometimes useful to perform element-by-element binary operations on arrays sizes, such as adding a vector to each column of a matrix. An inefficient way to do this would be to replicate the vector to the size of the matrix: -```julia-repl -julia> a = rand(2, 1); A = rand(2, 3); +```jldoctest broadcast_example +julia> a = [0.2, 0.5]; A = [1.0 1.6 1.05; 1.07 1.36 1.18]; julia> repeat(a, 1, 3) + A 2×3 Matrix{Float64}: - 1.20813 1.82068 1.25387 - 1.56851 1.86401 1.67846 + 1.2 1.8 1.25 + 1.57 1.86 1.68 ``` This is wasteful when dimensions get large, so Julia provides [`broadcast`](@ref), which expands singleton dimensions in array arguments to match the corresponding dimension in the other array without using extra memory, and applies the given function elementwise: -```julia-repl +```jldoctest broadcast_example julia> broadcast(+, a, A) 2×3 Matrix{Float64}: - 1.20813 1.82068 1.25387 - 1.56851 1.86401 1.67846 + 1.2 1.8 1.25 + 1.57 1.86 1.68 -julia> b = rand(1,2) +julia> b = [0.9 0.1] 1×2 Matrix{Float64}: - 0.867535 0.00457906 + 0.9 0.1 julia> broadcast(+, a, b) 2×2 Matrix{Float64}: - 1.71056 0.847604 - 1.73659 0.873631 + 1.1 0.3 + 1.4 0.6 ``` [Dotted operators](@ref man-dot-operators) such as `.+` and `.*` are equivalent diff --git a/doc/src/manual/asynchronous-programming.md b/doc/src/manual/asynchronous-programming.md index d1d095c48b2ff..ccdffda9a1acc 100644 --- a/doc/src/manual/asynchronous-programming.md +++ b/doc/src/manual/asynchronous-programming.md @@ -203,7 +203,7 @@ A channel can be visualized as a pipe, i.e., it has a write end and a read end : freely via [`take!`](@ref) and [`put!`](@ref) calls. [`close`](@ref) closes a [`Channel`](@ref). On a closed [`Channel`](@ref), [`put!`](@ref) will fail. For example: - ```julia-repl + ```jldoctest channel_example julia> c = Channel(2); julia> put!(c, 1) # `put!` on an open channel succeeds @@ -220,7 +220,7 @@ A channel can be visualized as a pipe, i.e., it has a write end and a read end : * [`take!`](@ref) and [`fetch`](@ref) (which retrieves but does not remove the value) on a closed channel successfully return any existing values until it is emptied. Continuing the above example: - ```julia-repl + ```jldoctest channel_example julia> fetch(c) # Any number of `fetch` calls succeed. 1 diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index fd97b44cdc345..ba8c03aa665bf 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -76,7 +76,14 @@ The statement `x[1] = 42` *mutates* the object `x`, and hence this change *will* by the caller for this argument. On the other hand, the assignment `y = 7 + y` changes the *binding* ("name") `y` to refer to a new value `7 + y`, rather than mutating the *original* object referred to by `y`, and hence does *not* change the corresponding argument passed by the caller. This can be seen if we call `f(x, y)`: -```julia-repl +```jldoctest +julia> function f(x, y) + x[1] = 42 # mutates x + y = 7 + y # new binding for y, no mutation + return y + end +f (generic function with 1 method) + julia> a = [4, 5, 6] 3-element Vector{Int64}: 4 diff --git a/doc/src/manual/integers-and-floating-point-numbers.md b/doc/src/manual/integers-and-floating-point-numbers.md index 0139a33f4854b..845d42e33abfb 100644 --- a/doc/src/manual/integers-and-floating-point-numbers.md +++ b/doc/src/manual/integers-and-floating-point-numbers.md @@ -59,7 +59,7 @@ julia> 1234 The default type for an integer literal depends on whether the target system has a 32-bit architecture or a 64-bit architecture: -```julia-repl +```julia-repl ; nodoctest = "Results depend on system word size" # 32-bit system: julia> typeof(1) Int32 @@ -72,7 +72,7 @@ Int64 The Julia internal variable [`Sys.WORD_SIZE`](@ref) indicates whether the target system is 32-bit or 64-bit: -```julia-repl +```julia-repl ; nodoctest = "Results depend on system word size" # 32-bit system: julia> Sys.WORD_SIZE 32 @@ -85,7 +85,7 @@ julia> Sys.WORD_SIZE Julia also defines the types `Int` and `UInt`, which are aliases for the system's signed and unsigned native integer types respectively: -```julia-repl +```julia-repl ; nodoctest = "Results depend on system word size" # 32-bit system: julia> Int Int32 diff --git a/doc/src/manual/metaprogramming.md b/doc/src/manual/metaprogramming.md index 9ed579594571a..ee252d8c9fa2e 100644 --- a/doc/src/manual/metaprogramming.md +++ b/doc/src/manual/metaprogramming.md @@ -711,7 +711,7 @@ user to optionally specify their own error message, instead of just printing the Just like in functions with a variable number of arguments ([Varargs Functions](@ref)), this is specified with an ellipses following the last argument: -```jldoctest assert2 +```julia-repl assert2 julia> macro assert(ex, msgs...) msg_body = isempty(msgs) ? ex : msgs[1] msg = string(msg_body) diff --git a/doc/src/manual/methods.md b/doc/src/manual/methods.md index 7538649f2b8df..7a80bd0617de3 100644 --- a/doc/src/manual/methods.md +++ b/doc/src/manual/methods.md @@ -288,13 +288,14 @@ Such specializations are *not* listed by `methods`, as this doesn't create new ` For example, if you create a method -``` -mysum(x::Real, y::Real) = x + y +```jldoctest mysum_example +julia> mysum(x::Real, y::Real) = x + y +mysum (generic function with 1 method) ``` you've given the function `mysum` one new method (possibly its only method), and that method takes any pair of `Real` number inputs. But if you then execute -```julia-repl +```jldoctest mysum_example julia> mysum(1, 2) 3 diff --git a/doc/src/manual/multi-threading.md b/doc/src/manual/multi-threading.md index ec470f867cc47..5188573310299 100644 --- a/doc/src/manual/multi-threading.md +++ b/doc/src/manual/multi-threading.md @@ -46,7 +46,7 @@ $ julia --threads 4 Let's verify there are 4 threads at our disposal. -```julia-repl +```jldoctest; filter = r"[0-9]+" julia> Threads.nthreads() 4 ``` diff --git a/doc/src/manual/performance-tips.md b/doc/src/manual/performance-tips.md index d506ac9946ba6..b08b71f65db05 100644 --- a/doc/src/manual/performance-tips.md +++ b/doc/src/manual/performance-tips.md @@ -58,14 +58,16 @@ Passing arguments to functions is better style. It leads to more reusable code a In the following REPL session: -```julia-repl +```jldoctest julia> x = 1.0 +1.0 ``` is equivalent to: -```julia-repl +```jldoctest julia> global x = 1.0 +1.0 ``` so all the performance issues discussed previously apply. @@ -1697,7 +1699,7 @@ in generated code by using Julia's [`code_native`](@ref) function. Note that `@fastmath` also assumes that `NaN`s will not occur during the computation, which can lead to surprising behavior: -```julia-repl +```jldoctest julia> f(x) = isnan(x); julia> f(NaN) diff --git a/doc/src/manual/strings.md b/doc/src/manual/strings.md index 57431d07c0aa5..2948c8aad0335 100644 --- a/doc/src/manual/strings.md +++ b/doc/src/manual/strings.md @@ -482,7 +482,7 @@ The resulting string may contain different characters than the input strings, and its number of characters may be lower than sum of numbers of characters of the concatenated strings, e.g.: -```julia-repl +```jldoctest julia> a, b = "\xe2\x88", "\x80" ("\xe2\x88", "\x80") @@ -992,7 +992,7 @@ The `r"..."` literal is constructed without interpolation and unescaping (except quotation mark `"` which still has to be escaped). Here is an example showing the difference from standard string literals: -```julia-repl +```jldoctest julia> x = 10 10 @@ -1006,7 +1006,10 @@ julia> r"\x" r"\x" julia> "\x" -ERROR: syntax: invalid escape sequence +ERROR: ParseError: +# Error @ none:1:2 +"\x" +#└┘ ── invalid hex escape sequence ``` Triple-quoted regex strings, of the form `r"""..."""`, are also supported (and may be convenient diff --git a/doc/src/manual/variables-and-scoping.md b/doc/src/manual/variables-and-scoping.md index ab0dbdb845d8e..899257d6a1b58 100644 --- a/doc/src/manual/variables-and-scoping.md +++ b/doc/src/manual/variables-and-scoping.md @@ -28,7 +28,7 @@ a global variable by the same name is allowed or not. !!! tip "A Common Confusion" If you run into an unexpectedly undefined variable, - ```julia + ```julia ; nodoctest = "Pseudocode" # Print the numbers 1 through 5 i = 0 while i < 5 @@ -40,7 +40,7 @@ a global variable by the same name is allowed or not. a simple fix is to change all global variable definitions into local definitions by wrapping the code in a `let` block or `function`. - ```julia + ```julia ; nodoctest = "Pseudocode" # Print the numbers 1 through 5 let i = 0 while i < 5 @@ -370,7 +370,7 @@ scope rule applies and `x` is created as local to the `for` loop and therefore g undefined after the loop executes. Next, let's consider the body of `sum_to_def` extracted into global scope, fixing its argument to `n = 10` -```julia +```julia ; nodoctest = "Specifically shows scope differences" s = 0 for i = 1:10 t = s + i @@ -472,7 +472,7 @@ years were confused about this behavior and complained that it was complicated a explain and understand. Fair point. Second, and arguably worse, is that it's bad for programming "at scale." When you see a small piece of code in one place like this, it's quite clear what's going on: -```julia +```julia ; nodoctest="Expects global file scope" s = 0 for i = 1:10 s += i @@ -483,7 +483,7 @@ Obviously the intention is to modify the existing global variable `s`. What else However, not all real world code is so short or so clear. We found that code like the following often occurs in the wild: -```julia +```julia ; nodoctest="Expects global file scope" x = 123 # much later diff --git a/doc/src/manual/variables.md b/doc/src/manual/variables.md index 4c3e98ca57281..074a7207698d1 100644 --- a/doc/src/manual/variables.md +++ b/doc/src/manual/variables.md @@ -3,21 +3,17 @@ A variable, in Julia, is a name associated (or bound) to a value. It's useful when you want to store a value (that you obtained after some math, for example) for later use. For example: -```julia-repl -# Assign the value 10 to the variable x -julia> x = 10 +```jldoctest +julia> x = 10 # Assign the value 10 to the variable x 10 -# Doing math with x's value -julia> x + 1 +julia> x + 1 # Doing math with x's value 11 -# Reassign x's value -julia> x = 1 + 1 +julia> x = 1 + 1 # Reassign x's value 2 -# You can assign values of other types, like strings of text -julia> x = "Hello World!" +julia> x = "Hello World!" # You can assign values of other types, like strings of text "Hello World!" ``` @@ -125,7 +121,7 @@ it from `+ ᵃx` where `ᵃx` is the variable name. A particular class of variable names is one that contains only underscores. These identifiers are write-only. I.e. they can only be assigned values, which are immediately discarded, and their values cannot be used in any way. -```julia-repl +```jldoctest julia> x, ___ = size([2 2; 1 1]) (2, 2) @@ -138,12 +134,18 @@ ERROR: syntax: all-underscore identifiers are write-only and their values cannot The only explicitly disallowed names for variables are the names of the built-in [Keywords](@ref Keywords): -```julia-repl +```jldoctest julia> else = false -ERROR: syntax: unexpected "else" +ERROR: ParseError: +# Error @ none:1:1 +else = false +└──┘ ── invalid identifier julia> try = "No" -ERROR: syntax: unexpected "=" +ERROR: ParseError: +# Error @ none:1:1 +try = "No" +└────────┘ ── try without catch or finally ``` Some Unicode characters are considered to be equivalent in identifiers. diff --git a/stdlib/Dates/src/conversions.jl b/stdlib/Dates/src/conversions.jl index edb521d31f831..74f79f10297fb 100644 --- a/stdlib/Dates/src/conversions.jl +++ b/stdlib/Dates/src/conversions.jl @@ -85,7 +85,7 @@ Return a `DateTime` corresponding to the user's system time as UTC/GMT. For other time zones, see the TimeZones.jl package. # Examples -```julia +```julia-repl julia> now(UTC) 2023-01-04T10:52:24.864 ``` diff --git a/stdlib/Random/src/RNGs.jl b/stdlib/Random/src/RNGs.jl index 3e8cb00f7787f..bd3df8e54f194 100644 --- a/stdlib/Random/src/RNGs.jl +++ b/stdlib/Random/src/RNGs.jl @@ -196,7 +196,7 @@ If `rng` is not specified, it defaults to seeding the state of the shared task-local generator. # Examples -```julia-repl +```jldoctest; filter = r"(true|false)" julia> Random.seed!(1234); julia> x1 = rand(2) diff --git a/stdlib/Sockets/src/addrinfo.jl b/stdlib/Sockets/src/addrinfo.jl index db03cdb7c1dc8..8e12a41f08e3b 100644 --- a/stdlib/Sockets/src/addrinfo.jl +++ b/stdlib/Sockets/src/addrinfo.jl @@ -129,7 +129,7 @@ Uses the operating system's underlying getaddrinfo implementation, which may do a DNS lookup. # Examples -```jldoctest +```jldoctest; filter = r"(ip\\"::1\\"|ERROR: DNSError:.*|Stacktrace:(\\n \\[0-9]+\\].*)*)" julia> getaddrinfo("localhost", IPv6) ip"::1" From 5e3c13885a47183eb12aafc71c1bf60f38796c96 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Wed, 18 Jun 2025 21:18:45 +0000 Subject: [PATCH 2/3] doc: Address review comments on PR #58594 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add label to argpassing jldoctest to carry state between code blocks - Fix typo: 'pathts' -> 'paths' in jldoctests.md - Change mysum_example to use output=false style for consistency As noted by reviewers: - The STDLIB_DIR removal in doc/make.jl is valid as it's defined globally - The line numbers in error messages should be stable as they refer to character positions in the input 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- doc/src/devdocs/contributing/jldoctests.md | 2 +- doc/src/manual/functions.md | 15 ++++++--------- doc/src/manual/methods.md | 7 +++++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/doc/src/devdocs/contributing/jldoctests.md b/doc/src/devdocs/contributing/jldoctests.md index fc3ea99b3cc0e..6631390cf9ca3 100644 --- a/doc/src/devdocs/contributing/jldoctests.md +++ b/doc/src/devdocs/contributing/jldoctests.md @@ -10,7 +10,7 @@ The following are common situations where this may happen: - The output contains arrays with undefined memory (e.g. from `undef` or `similar`) - The output contains random numbers - The output contains timing information -- The output contains file system pathts +- The output contains file system paths ### Common filter sequences diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index ba8c03aa665bf..a83d16ca8c9b7 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -65,25 +65,22 @@ a function will be visible to the caller. (This is the same behavior found in Sc Python, Ruby and Perl, among other dynamic languages.) For example, in the function -```julia +```jldoctest argpassing; output = false function f(x, y) x[1] = 42 # mutates x y = 7 + y # new binding for y, no mutation return y end + +# output + +f (generic function with 1 method) ``` The statement `x[1] = 42` *mutates* the object `x`, and hence this change *will* be visible in the array passed by the caller for this argument. On the other hand, the assignment `y = 7 + y` changes the *binding* ("name") `y` to refer to a new value `7 + y`, rather than mutating the *original* object referred to by `y`, and hence does *not* change the corresponding argument passed by the caller. This can be seen if we call `f(x, y)`: -```jldoctest -julia> function f(x, y) - x[1] = 42 # mutates x - y = 7 + y # new binding for y, no mutation - return y - end -f (generic function with 1 method) - +```jldoctest argpassing julia> a = [4, 5, 6] 3-element Vector{Int64}: 4 diff --git a/doc/src/manual/methods.md b/doc/src/manual/methods.md index 7a80bd0617de3..cebd9dbb6061b 100644 --- a/doc/src/manual/methods.md +++ b/doc/src/manual/methods.md @@ -288,8 +288,11 @@ Such specializations are *not* listed by `methods`, as this doesn't create new ` For example, if you create a method -```jldoctest mysum_example -julia> mysum(x::Real, y::Real) = x + y +```jldoctest mysum_example; output = false +mysum(x::Real, y::Real) = x + y + +# output + mysum (generic function with 1 method) ``` From 724a8060d821926b34fcc2ea622653a7d3fc3b19 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 19 Jun 2025 02:37:33 +0000 Subject: [PATCH 3/3] doc: Address additional review comments on PR #58594 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add filter to Dates.now(UTC) doctest to handle changing timestamps - Add complex regex filter to IdDict doctest to handle non-deterministic iteration order while preserving the original example 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- base/iddict.jl | 2 +- stdlib/Dates/src/conversions.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/base/iddict.jl b/base/iddict.jl index 6e3d09454c54e..b12fbfcca107f 100644 --- a/base/iddict.jl +++ b/base/iddict.jl @@ -12,7 +12,7 @@ the same, so they get overwritten. The `IdDict` hashes by object-id, and thus preserves the 3 different keys. # Examples -```jldoctest +```jldoctest; filter = r" \\S+ +=> \\S+" => " KEY => VALUE" julia> Dict(true => "yes", 1 => "no", 1.0 => "maybe") Dict{Real, String} with 1 entry: 1.0 => "maybe" diff --git a/stdlib/Dates/src/conversions.jl b/stdlib/Dates/src/conversions.jl index 74f79f10297fb..a68b2a85e9c87 100644 --- a/stdlib/Dates/src/conversions.jl +++ b/stdlib/Dates/src/conversions.jl @@ -85,7 +85,7 @@ Return a `DateTime` corresponding to the user's system time as UTC/GMT. For other time zones, see the TimeZones.jl package. # Examples -```julia-repl +```jldoctest; filter = r"\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}" => "2023-01-04T10:52:24.864" julia> now(UTC) 2023-01-04T10:52:24.864 ```