Skip to content

Convert julia-repl blocks to jldoctest format #58594

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion base/bitarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions base/expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -144,15 +144,15 @@ 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
end
function f()
(@macroexpand(@m),
macroexpand(M, :(@m)),
macroexpand(Main, :(@m))
macroexpand(parentmodule(M), :(@m))
)
end
end
Expand Down
6 changes: 3 additions & 3 deletions base/file.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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");
Expand Down
2 changes: 1 addition & 1 deletion base/float.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion base/iddict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Iteration order depends on hashes for IdDict. Needs a filter that accept any RHS/LHS of the =>

julia> Dict(true => "yes", 1 => "no", 1.0 => "maybe")
Dict{Real, String} with 1 entry:
1.0 => "maybe"
Expand Down
2 changes: 1 addition & 1 deletion base/path.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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://<hostname>/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"
```
"""
Expand Down
1 change: 0 additions & 1 deletion doc/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
57 changes: 38 additions & 19 deletions doc/src/base/scopedvalues.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 5 additions & 5 deletions doc/src/base/sort.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
28 changes: 27 additions & 1 deletion doc/src/devdocs/contributing/jldoctests.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 paths


### 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.
Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand Down
63 changes: 33 additions & 30 deletions doc/src/manual/arrays.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading