Browse Source

Add soft scoping rule capabilities

This patch enables "soft" scoping rules (see e.g.
https://github.com/JuliaLang/SoftGlobalScope.jl) for code execution
(markdown and notebook output). This is enabled by default for Jupyter
notebook output (to mimic how the IJulia kernel works), and disabled
otherwise. Soft scope rules can be enabled/disabled with the `softscope
:: Bool` configuration variable. Fixes #227.
pull/230/head
Fredrik Ekre 2 years ago
parent
commit
c157721604
  1. 7
      CHANGELOG.md
  2. 24
      src/Literate.jl
  3. 50
      test/runtests.jl

7
CHANGELOG.md

@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- "Soft" scoping rules (see e.g. https://github.com/JuliaLang/SoftGlobalScope.jl) are now
available for code execution (markdown and notebook output). This is enabled by default
for Jupyter notebook output (to mimic how the IJulia kernel works), and disabled
otherwise. Soft scope rules can be enabled/disabled with the `softscope :: Bool`
configuration variable. ([#227][github-227], [#230][github-230])
### Changed
- The minimum Julia version requirement for Literate >= 2.16.0 is now 1.6.0 (from 1.0.0).
([#230][github-230])
@ -269,6 +275,7 @@ https://discourse.julialang.org/t/ann-literate-jl/10651 for release announcement @@ -269,6 +275,7 @@ https://discourse.julialang.org/t/ann-literate-jl/10651 for release announcement
[github-221]: https://github.com/fredrikekre/Literate.jl/pull/221
[github-222]: https://github.com/fredrikekre/Literate.jl/issues/222
[github-223]: https://github.com/fredrikekre/Literate.jl/pull/223
[github-227]: https://github.com/fredrikekre/Literate.jl/issues/227
[github-228]: https://github.com/fredrikekre/Literate.jl/issues/228
[github-229]: https://github.com/fredrikekre/Literate.jl/pull/229
[github-230]: https://github.com/fredrikekre/Literate.jl/pull/230

24
src/Literate.jl

@ -312,6 +312,7 @@ function create_configuration(inputfile; user_config, user_kwargs, type=nothing) @@ -312,6 +312,7 @@ function create_configuration(inputfile; user_config, user_kwargs, type=nothing)
cfg["flavor"] = type === (:md) ? DocumenterFlavor() : DefaultFlavor()
cfg["credit"] = true
cfg["mdstrings"] = false
cfg["softscope"] = type === (:nb) ? true : false # on for Jupyter notebooks
cfg["keep_comments"] = false
cfg["execute"] = type === :md ? false : true
cfg["codefence"] = get(user_config, "flavor", cfg["flavor"]) isa DocumenterFlavor &&
@ -408,6 +409,8 @@ Available options: @@ -408,6 +409,8 @@ Available options:
- `devurl` (default: `"dev"`): URL for "in-development" docs, see [Documenter docs]
(https://juliadocs.github.io/Documenter.jl/). Unused if `repo_root_url`/
`nbviewer_root_url`/`binder_root_url` are set.
- `softscope` (default: `true` for Jupyter notebooks, `false` otherwise): enable/disable
"soft" scoping rules when executing, see e.g. https://github.com/JuliaLang/SoftGlobalScope.jl.
- `repo_root_url`: URL to the root of the repository. Determined automatically on Travis CI,
GitHub Actions and GitLab CI. Used for `@__REPO_ROOT_URL__`.
- `nbviewer_root_url`: URL to the root of the repository as seen on nbviewer. Determined
@ -580,6 +583,7 @@ function markdown(inputfile, outputdir=pwd(); config::AbstractDict=Dict(), kwarg @@ -580,6 +583,7 @@ function markdown(inputfile, outputdir=pwd(); config::AbstractDict=Dict(), kwarg
flavor=config["flavor"],
image_formats=config["image_formats"],
file_prefix="$(config["name"])-$(chunknum)",
softscope=config["softscope"],
)
end
end
@ -597,9 +601,10 @@ end @@ -597,9 +601,10 @@ end
function execute_markdown!(io::IO, sb::Module, block::String, outputdir;
inputfile::String, fake_source::String,
flavor::AbstractFlavor, image_formats::Vector, file_prefix::String)
flavor::AbstractFlavor, image_formats::Vector, file_prefix::String,
softscope::Bool)
# TODO: Deal with explicit display(...) calls
r, str, _ = execute_block(sb, block; inputfile=inputfile, fake_source=fake_source)
r, str, _ = execute_block(sb, block; inputfile=inputfile, fake_source=fake_source, softscope=softscope)
# issue #101: consecutive codefenced blocks need newline
# issue #144: quadruple backticks allow for triple backticks in the output
plain_fence = "\n````\n" => "\n````"
@ -734,7 +739,8 @@ function jupyter_notebook(chunks, config) @@ -734,7 +739,8 @@ function jupyter_notebook(chunks, config)
try
cd(config["literate_outputdir"]) do
nb = execute_notebook(nb; inputfile=config["literate_inputfile"],
fake_source=config["literate_outputfile"])
fake_source=config["literate_outputfile"],
softscope=config["softscope"])
end
catch err
@error "error when executing notebook based on input file: " *
@ -745,7 +751,7 @@ function jupyter_notebook(chunks, config) @@ -745,7 +751,7 @@ function jupyter_notebook(chunks, config)
return nb
end
function execute_notebook(nb; inputfile::String, fake_source::String)
function execute_notebook(nb; inputfile::String, fake_source::String, softscope::Bool)
sb = sandbox()
execution_count = 0
for cell in nb["cells"]
@ -753,7 +759,7 @@ function execute_notebook(nb; inputfile::String, fake_source::String) @@ -753,7 +759,7 @@ function execute_notebook(nb; inputfile::String, fake_source::String)
execution_count += 1
cell["execution_count"] = execution_count
block = join(cell["source"])
r, str, display_dicts = execute_block(sb, block; inputfile=inputfile, fake_source=fake_source)
r, str, display_dicts = execute_block(sb, block; inputfile=inputfile, fake_source=fake_source, softscope=softscope)
# str should go into stream
if !isempty(str)
@ -835,7 +841,7 @@ function Base.display(ld::LiterateDisplay, mime::MIME, x) @@ -835,7 +841,7 @@ function Base.display(ld::LiterateDisplay, mime::MIME, x)
end
# Execute a code-block in a module and capture stdout/stderr and the result
function execute_block(sb::Module, block::String; inputfile::String, fake_source::String)
function execute_block(sb::Module, block::String; inputfile::String, fake_source::String, softscope::Bool)
@debug """execute_block($sb, block)
```
$(block)
@ -851,7 +857,11 @@ function execute_block(sb::Module, block::String; inputfile::String, fake_source @@ -851,7 +857,11 @@ function execute_block(sb::Module, block::String; inputfile::String, fake_source
# `rethrow = Union{}` means that we try-catch all the exceptions thrown in the do-block
# and return them via the return value (they get handled below).
c = IOCapture.capture(rethrow = Union{}) do
include_string(sb, block, fake_source)
if softscope
include_string(REPL.softscope, sb, block, fake_source)
else
include_string(sb, block, fake_source)
end
end
popdisplay(disp) # IOCapture.capture has a try-catch so should always end up here
if c.error

50
test/runtests.jl

@ -905,6 +905,27 @@ end end @@ -905,6 +905,27 @@ end end
Literate.markdown(inputfile, relpath(outdir); execute=true,
flavor=Literate.CommonMarkFlavor())
@test read(joinpath(outdir, "inputfile-1.svg"), String) == "issue228"
# Softscope
write(
inputfile,
"""
ret = 0
for k = 1:10
ret += k
end
println("ret = ", ret)
"""
)
Literate.markdown(inputfile, outdir; execute=true, softscope=true)
@test occursin("ret = 55", read(joinpath(outdir, "inputfile.md"), String))
## Disabled softscope
try
Literate.markdown(inputfile, outdir; execute=true, softscope=false)
error("unreachable")
catch err
@test occursin(r"`?ret`? not defined", sprint(Base.showerror, err))
end
end # cd(sandbox)
end # mktemp
end end
@ -1319,8 +1340,33 @@ end end @@ -1319,8 +1340,33 @@ end end
@test keys(cellout[1]["data"]) == Set(("text/latex",))
@test cellout[1]["data"]["text/latex"] == "DF(4) as text/latex"
@test !haskey(cellout[1], "execution_count")
end
end
# Softscope
# TODO: Windows CI says here, but no longer: The input file that have been used
# above multiple times without problem now gives
# "SystemError: opening file: Invalid argument" for some reason...
new_inputfile = "inputfile2.jl"
write(
new_inputfile,
"""
ret = 0
for k = 1:10
ret += k
end
println("ret = ", ret)
"""
)
Literate.notebook(new_inputfile, outdir)
@test occursin("ret = 55", read(joinpath(outdir, "inputfile2.ipynb"), String))
## Disabled softscope
try
Literate.notebook(new_inputfile, outdir; softscope=false)
error("unreachable")
catch err
@test occursin(r"`?ret`? not defined", sprint(Base.showerror, err))
end
end # cd(sandbox)
end # mktempdir
end end
@testset "Configuration" begin; Base.CoreLogging.with_logger(Base.CoreLogging.NullLogger()) do

Loading…
Cancel
Save