Skip to content

Commit a60d238

Browse files
Kenoclaude
andauthored
Convert julia-repl blocks to jldoctest format (#58594)
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 --------- Co-authored-by: Keno Fischer <Keno@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 4962d2d commit a60d238

26 files changed

+183
-108
lines changed

AGENTS.md

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,23 @@ The test system supports doing this automatically (see below).
3838

3939
### Doctests
4040

41-
If you have changed doctests (i.e. any `jldoctest` code block), you should run the doctests before
42-
preparing a PR. See `doc/README.md` for how to do this. For instructions on writing doctests, see
43-
`doc/src/devdocs/contributing/jldoctests.md`. Note that the doctests may take up to 15 minutes.
44-
Do not terminate the doctests before completion. If you are ChatGPT, you may have to increase yield_timeout_ms.
41+
#### Writing doctests
42+
43+
If you are asked to write new doctests, first review `doc/src/devdocs/contributing/jldoctests.md`
44+
for best practices.
45+
46+
#### Verifying doctests
47+
If you have changed any `jldoctest` code blocks you should take
48+
the following steps to verify your work:
49+
- Review `doc/src/devdocs/contributing/jldoctests.md`. In particular, determine
50+
if any of the changed doctests require filters, labels or setup code.
51+
- Run the doctests to verify that your change works:
52+
- To run doctest with the pre-built juliaup: `make -C doc doctest=true revise=true JULIA_EXECUTABLE=$HOME/.juliaup/bin/julia`
53+
- To run doctest with in-trr julia (preferred): `make -C doc doctest=true revise=true`. Do not pass any other options.
54+
- IMPORTANT: The doctests may take up to 15 minutes. Do NOT terminate the doctests before completion. Do NOT use a timeout for doctests.
55+
- If you are ChatGPT, you may have to increase yield_timeout_ms.
56+
57+
Follow these steps for EVERY change you make in a doctest.
4558

4659
### Test changes
4760

base/abstractarray.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -796,7 +796,7 @@ julia> similar(1:10, 1, 4)
796796
Conversely, `similar(trues(10,10), 2)` returns an uninitialized `BitVector` with two
797797
elements since `BitArray`s are both mutable and can support 1-dimensional arrays:
798798
799-
```julia-repl
799+
```jldoctest; filter = r"[01]"
800800
julia> similar(trues(10,10), 2)
801801
2-element BitVector:
802802
0

base/bitarray.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ Construct an undef [`BitArray`](@ref) with the given dimensions.
5353
Behaves identically to the [`Array`](@ref) constructor. See [`undef`](@ref).
5454
5555
# Examples
56-
```julia-repl
56+
```jldoctest; filter = r"[01]"
5757
julia> BitArray(undef, 2, 2)
5858
2×2 BitMatrix:
5959
0 0

base/expr.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,15 +144,15 @@ There are differences between `@macroexpand` and [`macroexpand`](@ref).
144144
expands with respect to the module in which it is called.
145145
146146
This is best seen in the following example:
147-
```julia-repl
147+
```jldoctest
148148
julia> module M
149149
macro m()
150150
1
151151
end
152152
function f()
153153
(@macroexpand(@m),
154154
macroexpand(M, :(@m)),
155-
macroexpand(Main, :(@m))
155+
macroexpand(parentmodule(M), :(@m))
156156
)
157157
end
158158
end

base/file.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ required intermediate directories.
164164
Return `path`.
165165
166166
# Examples
167-
```julia-repl
167+
```jldoctest; setup = :(curdir = pwd(); testdir = mktempdir(); cd(testdir)), teardown = :(cd(curdir); rm(testdir, recursive=true)), filter = r"^\\".*testingdir\\"\$"
168168
julia> mkdir("testingdir")
169169
"testingdir"
170170
@@ -516,7 +516,7 @@ If the file does not exist a new file is created.
516516
Return `path`.
517517
518518
# Examples
519-
```julia-repl
519+
```jldoctest; setup = :(curdir = pwd(); testdir = mktempdir(); cd(testdir)), teardown = :(cd(curdir); rm(testdir, recursive=true)), filter = r"[\\d\\.]+e[\\+\\-]?\\d+"
520520
julia> write("my_little_file", 2);
521521
522522
julia> mtime("my_little_file")
@@ -1134,7 +1134,7 @@ for (path, dirs, files) in walkdir(".")
11341134
end
11351135
```
11361136
1137-
```julia-repl
1137+
```jldoctest; setup = :(prevdir = pwd(); tmpdir = mktempdir(); cd(tmpdir)), teardown = :(cd(prevdir); rm(tmpdir, recursive=true))
11381138
julia> mkpath("my/test/dir");
11391139
11401140
julia> itr = walkdir("my");

base/float.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ julia> NaN == NaN, isequal(NaN, NaN), isnan(NaN)
8383
!!! note
8484
Always use [`isnan`](@ref) or [`isequal`](@ref) for checking for `NaN`.
8585
Using `x === NaN` may give unexpected results:
86-
```julia-repl
86+
```jldoctest
8787
julia> reinterpret(UInt32, NaN32)
8888
0x7fc00000
8989

base/iddict.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ the same, so they get overwritten. The `IdDict` hashes by object-id, and thus
1212
preserves the 3 different keys.
1313
1414
# Examples
15-
```julia-repl
15+
```jldoctest; filter = r" \\S+ +=> \\S+" => " KEY => VALUE"
1616
julia> Dict(true => "yes", 1 => "no", 1.0 => "maybe")
1717
Dict{Real, String} with 1 entry:
1818
1.0 => "maybe"

base/path.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,7 @@ the [Freedesktop File URI spec](https://www.freedesktop.org/wiki/Specifications/
633633
julia> uripath("/home/user/example file.jl") # On a unix machine
634634
"file://<hostname>/home/user/example%20file.jl"
635635
636-
juila> uripath("C:\\Users\\user\\example file.jl") # On a windows machine
636+
julia> uripath("C:\\Users\\user\\example file.jl") # On a windows machine
637637
"file:///C:/Users/user/example%20file.jl"
638638
```
639639
"""

doc/make.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,6 @@ if use_revise
318318
end
319319
function maybe_revise(ex)
320320
use_revise || return ex
321-
STDLIB_DIR = Sys.STDLIB
322321
STDLIBS = filter!(x -> isfile(joinpath(STDLIB_DIR, x, "src", "$(x).jl")), readdir(STDLIB_DIR))
323322
return quote
324323
$ex

doc/src/base/scopedvalues.md

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,38 +27,57 @@ Let's first look at an example of **lexical** scope. A `let` statement begins
2727
a new lexical scope within which the outer definition of `x` is shadowed by
2828
it's inner definition.
2929

30-
```julia
30+
```jldoctest
31+
julia> x = 1
32+
1
33+
34+
julia> let x = 5
35+
@show x
36+
end;
37+
x = 5
38+
39+
julia> @show x;
3140
x = 1
32-
let x = 5
33-
@show x # 5
34-
end
35-
@show x # 1
3641
```
3742

3843
In the following example, since Julia uses lexical scope, the variable `x` in the body
3944
of `f` refers to the `x` defined in the global scope, and entering a `let` scope does
4045
not change the value `f` observes.
4146

42-
```julia
47+
```jldoctest
48+
julia> x = 1
49+
1
50+
51+
julia> f() = @show x
52+
f (generic function with 1 method)
53+
54+
julia> let x = 5
55+
f()
56+
end;
57+
x = 1
58+
59+
julia> f();
4360
x = 1
44-
f() = @show x
45-
let x = 5
46-
f() # 1
47-
end
48-
f() # 1
4961
```
5062

5163
Now using a `ScopedValue` we can use **dynamic** scoping.
5264

53-
```julia
54-
using Base.ScopedValues
65+
```jldoctest
66+
julia> using Base.ScopedValues
5567
56-
x = ScopedValue(1)
57-
f() = @show x[]
58-
with(x=>5) do
59-
f() # 5
60-
end
61-
f() # 1
68+
julia> x = ScopedValue(1)
69+
ScopedValue{Int64}(1)
70+
71+
julia> f() = @show x[]
72+
f (generic function with 1 method)
73+
74+
julia> with(x=>5) do
75+
f()
76+
end;
77+
x[] = 5
78+
79+
julia> f();
80+
x[] = 1
6281
```
6382

6483
Note that the observed value of the `ScopedValue` is dependent on the execution

0 commit comments

Comments
 (0)