-
Notifications
You must be signed in to change notification settings - Fork 475
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
add doctest = :fix option to makedocs #656
Conversation
4f36e14
to
3d44803
Compare
Will do something for #448 (comment) too. |
Added a commit that looks for the complete code-snippet before doing the replacement instead of just searching for the result. This should eliminate the risk of double replacement as described in #448 (comment) in 99.9% of cases. |
2b0699d
to
eb801f7
Compare
Actually, this will still turn
into
|
eb801f7
to
75981e0
Compare
Okay, this version should work in all (?) cases: Will add some tests, but feel free to review the implementation. |
75981e0
to
e9b466f
Compare
Turns out the method I described in the last comment still failed in for the following case:
because when we try to fix the second fail in this block we can not find that block in the file, since we replaced it with
I solved this by storing the full I have now also added tests with the corner cases I have found when working on this. |
@@ -343,8 +353,8 @@ function report(result::Result, str, doc::Documents.Document) | |||
println(ioc) | |||
printstyled(ioc, "> File: ", result.file, "\n", color=:cyan) | |||
printstyled(ioc, "\n> Code block:\n", color=:cyan) | |||
println(ioc, "\n```jldoctest") | |||
println(ioc, result.code) | |||
println(ioc, "\n```$(result.block.language)") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Storing the full Markdown.Code
object also allows to print the actual language
field here, including name
, filter
and setup
. Previously it was a bit confusing that those things were left out from the error printing.
e9b466f
to
c8ddea6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is neat, thanks for this!
There's an edge case it can't handle at the moment (it fixes the wrong doctest):
```@meta
DocTestSetup = quote
foo() = 42
end
```
```jldoctest
julia> foo()
42
```
```@meta
DocTestSetup = quote
foo() = 43
end
```
```jldoctest
julia> foo()
42
```
docs/src/man/doctests.md
Outdated
|
||
To fix outdated doctests, the `doctest` flag to [`makedocs`](@ref) can be set to | ||
`doctest = :fixup`. This will run the doctests, and overwrite the old results. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be good to recommend here that the user should git commit
their code changes, just in case, before running the doc build with this option on. That way, should the find-and-replace algorithm go a bit wonky and completely mess up the source file, the user can easily just git reset --hard
to their state.
docs/src/man/doctests.md
Outdated
To fix outdated doctests, the `doctest` flag to [`makedocs`](@ref) can be set to | ||
`doctest = :fixup`. This will run the doctests, and overwrite the old results with | ||
the new output. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be good to recommend here that the user should git commit
their code changes, just in case, before running the doc build with this option on. That way, should the find-and-replace algorithm go a bit wonky and completely mess up the source file, the user can easily just git reset --hard
to their state.
test/doctests.jl
Outdated
str = read(f, String) | ||
@test str == good_str | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should fix doctests in .jl
files too, right? Could perhaps also test that, to make sure that the file paths get resolved correctly etc.
Yea thats a corner case I also thought of. But it should be pretty rare(?) that the same input yields different output. Will add the recommendation to |
Agreed. As you said, a note in the docs saying that there are potential edge cases etc. should be enough. |
43d5e19
to
d76fcd9
Compare
Great stuff to have! |
Hmm, this will not actually work for script-doctests.... |
d76fcd9
to
367ab39
Compare
Ok, we are good I think. |
src/DocChecks.jl
Outdated
# TODO: decide whether to keep `# output` syntax for this. It's a bit ugly. | ||
# Maybe use double blank lines, i.e. | ||
# | ||
# | ||
# to mark `input`/`output` separation. | ||
input, output = split(code, "\n# output\n", limit = 2) | ||
result = Result(code, "", output, meta[:CurrentFile]) | ||
input, output = split(block.code, "\n# output\n\n", limit = 2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any dangers with this? The problem was that output
here became e.g. "\n2"
while the result string became "3"
, thus we replaced
```jldoctest
1 + 2
# output
2
```
with
```jldoctest
1 + 2
# output
3
```
instead of
```jldoctest
1 + 2
# output
3
```
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps it would be safer to do
input, output = split(block.code, "# output\n", limit = 2)
input = rstrip(input, '\n')
output = lstrip(output, '\n')
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From the manual:
Note that the amount of whitespace appearing above and below the # output line is not significant and can be increased or decreased if desired.
So we should strip '\n'
instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also support Windows line endings here I think, which would not get stripped correctly at the moment, right? This might work though?
input = rstrip(input, ['\n', '\r'])
output = lstrip(output, ['\n', '\r'])
367ab39
to
1ca793b
Compare
Perhaps we should change |
Yup, |
1ca793b
to
654b2fa
Compare
|
654b2fa
to
5413f32
Compare
src/DocChecks.jl
Outdated
truncate(f, 0) | ||
# first look for the entire code block | ||
codeidx = findnext(code, content, 1) | ||
@assert codeidx != 0:-1 # this should never happen |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you have CRLF line endings then this throws. It also leaves the source file empty on error right now.. so even if we fix this particular case, I think it would probably be a good idea to try ... catch
all of this and restore the original file when are any errors (and then rethrow).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. We could potentially try to detect the line ending style, unless there could sometimes be a mix of the two?
Probably good to wrap in try catch anyway
881b759
to
ab6d199
Compare
Pushed a new version that should fail more gracefully if we don't find the code block. Also leaves the source file untouched in case of failure. |
8a14599
to
b64a4d7
Compare
return | ||
end | ||
# write everything up until the code block | ||
write(io, content[1:prevind(content, first(codeidx))]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was able to trigger a
ERROR: LoadError: BoundsError: attempt to access "# Test\r\n\r\n```jldoctest\r\njulia> 2 + 2\r\n0000\r\n```\r\n"
at index [0]
Stacktrace:
[1] prevind(::String, ::Int64, ::Int64) at ./strings/basic.jl:426
[2] prevind at ./strings/basic.jl:421 [inlined]
[3] fix_doctest(::Documenter.DocChecks.Result, ::SubString{String}, ::Documenter.Documents.Document) at /home/morten/.julia/v0.7/Documenter/src/DocChecks.jl:395
[4] checkresult(::Module, ::Documenter.DocChecks.Result, ::Dict{Symbol,Any}, ::Documenter.Documents.Document) at /home/morten/.julia/v0.7/Documenter/src/DocChecks.jl:305
with CRLFs (this file) right now. So this manages to avoid your checks. Sorry for keeping on breaking this 😆
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm.. How? I simply get:
!! Could not find code block in source file
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could it be that you don't have JuliaLang/Compat.jl@5ff9892 ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I think you're right, my bad. I happened to be running this with my 0.7 binary and I think it is a bit too old (9 days).
codeidx = Compat.findnext(code, content, 1) | ||
if codeidx === nothing | ||
Utilities.warn("Could not find code block in source file") | ||
return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check (and the one below) should still never happen, right? Perhaps we should keep throw
ing/error
ing/assert
ing here so that in case they are triggered, they don't go unnoticed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My thought here was that since we have replaces \r\n
with \n
in the code block
Documenter.jl/src/DocChecks.jl
Lines 133 to 134 in b64a4d7
# Normalise line endings. | |
block.code = replace(block.code, "\r\n" => "\n") |
nothing
here since we have not performed the replacement in the file where we search.
test/doctests.jl
Outdated
\"\"\" | ||
foo() = 1 | ||
end # module | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having a test with CRLFs wouldn't hurt. We could also keep these in separate files and just cp
them over to tempdir
to avoid problems with differing line endings in a single file (although a simple replace(..., '\n' => "\r\n")
works as well I guess).
So currently this will just do nothing for CRLF files. It is trivial to support this, by simply normalizing the lineendings when reading the source file before we search for the code block. The problem though is that when we print it back to the file we have updated all line endings. We could do this, or somehow try to detect which linendings are used and replace with that before printing back to file. That will however be hard if a file has a combination of lineendings. |
I don't think it should be changing line endings of unrelated lines. But we would only need to figure out the line ending for each doctest separately, right? Could we, say, count the LFs and CRLFs to determine which one it is and then normalize to that? I think we could live with changing a mixed doctest to either pure LF or CRLF. In any case though, I'd be happy to merge this with just LF fixing as well, and it can be improved in the future if there's demand. It will already be massively useful for Base which is LF. Should just probably have a note in the manual about CRLFs. |
b64a4d7
to
6542833
Compare
It is a bit tricky since the byte index in the string will change after we have replaced the line endings. I added a note that this currently only works for LF line endings for now. Also added some better tests, including multiline output which was missing before. |
6542833
to
03e0a58
Compare
I have tried this with Julia base and some other projects and it works well for real projects too, not just the trivial test cases in the tests, so if you are happy with the current state, feel free to merge and we can work out possible problems as we find them. |
I should just mention that it does not quite work together with doctest filters, but perhaps easier to review the changes to make that work in a separate PR. |
Yup, let's iterate on this in future PRs, if the need arises. Thanks a lot for this, this is a really neat feature! |
This is a pretty useful feature when updating packages, doing this Ferrite-FEM/Tensors.jl@213ac2c manually would take some time. |
This makes it easier to fix multiple outdated doctests in one command, which is useful to do e.g. when preparing a new release.
Not really sure how to test this.
fix #448