Skip to content

Commit

Permalink
Merge pull request #6 from tkf/withprogress
Browse files Browse the repository at this point in the history
Add withprogress and logprogress macros
  • Loading branch information
pfitzseb authored Oct 18, 2019
2 parents 9ad2529 + 4efea27 commit 6b2d3f4
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 2 deletions.
58 changes: 56 additions & 2 deletions src/ProgressLogging.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
module ProgressLogging

export @progress
export @progress, @withprogress, @logprogress

using Logging: @logmsg, LogLevel
using Logging: Logging, @logmsg, LogLevel

const ProgressLevel = LogLevel(-1)

Expand Down Expand Up @@ -42,6 +42,60 @@ function progress(f; name = "")
end
end

const _id_name = gensym(:progress_id)

"""
@withprogress [name=""] ex
Create a lexical environment in which [`@logprogress`](@ref) can be used to
emit progress log events without manually specifying the log level and `_id`.
```julia
@withprogress begin
for i = 1:10
sleep(0.5)
@logprogress "iterating" progress=i/10
end
end
```
"""
macro withprogress(ex1, ex2 = nothing)
_withprogress(ex1, ex2)
end

_withprogress(ex, ::Nothing) = _withprogress(:(name = ""), ex)
function _withprogress(kwarg, ex)
if !(kwarg.head == :(=) && kwarg.args[1] == :name)
throw(ArgumentError("First expression to @withprogress must be `name=...`. Got: $kwarg"))
end
name = kwarg.args[2]

@gensym name_var
m = @__MODULE__
quote
let $_id_name = gensym(:progress_id),
$name_var = $name
$m.@logprogress $name_var progress = NaN
try
$ex
finally
$m.@logprogress $name_var progress = "done"
end
end
end |> esc
end

"""
@logprogress name progress=value ...
See [`@withprogress`](@ref).
"""
macro logprogress(name, args...)
quote
$Logging.@logmsg($ProgressLevel, $name, _id = $_id_name, $(args...))
end |> esc
end

"""
@progress [name="", threshold=0.005] for i = ..., j = ..., ...
@progress [name="", threshold=0.005] x = [... for i = ..., j = ..., ...]
Expand Down
71 changes: 71 additions & 0 deletions test/test_withprogress_macro.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
module TestWithprogressMacro

using Logging
using ProgressLogging
using ProgressLogging: ProgressLevel
using Test
using Test: collect_test_logs

@testset "simple" begin
logs, = collect_test_logs(min_level = ProgressLevel) do
@withprogress @logprogress "hello" progress = 0.1
end
@test length(logs) == 3
@test logs[1].kwargs[:progress] === NaN
@test logs[2].kwargs[:progress] === 0.1
@test logs[3].kwargs[:progress] === "done"
@test length(unique([l.id for l in logs])) == 1
end

@testset "with name" begin
logs, = collect_test_logs(min_level = ProgressLevel) do
@withprogress name = "name" @logprogress "hello" progress = 0.1
end
@test length(logs) == 3
@test logs[1].kwargs[:progress] === NaN
@test logs[2].kwargs[:progress] === 0.1
@test logs[3].kwargs[:progress] === "done"
@test logs[1].message === "name"
@test logs[2].message === "hello"
@test logs[3].message === "name"
@test length(unique([l.id for l in logs])) == 1
end

@testset "nested" begin
logs, = collect_test_logs(min_level = ProgressLevel) do
@withprogress begin
@logprogress "hello" progress = 0.1
@withprogress begin
@logprogress "world" progress = 0.2
end
end
end

@test length(logs) == 6

ids = unique([l.id for l in logs])
@test length(ids) == 2

@test Tuple((l.id, l.message, l.kwargs[:progress]) for l in logs) === (
(ids[1], "", NaN),
(ids[1], "hello", 0.1),
(ids[2], "", NaN),
(ids[2], "world", 0.2),
(ids[2], "", "done"),
(ids[1], "", "done"),
)
end

@testset "invalid input" begin
local err
@test try
@eval @withprogress invalid_argument = "" nothing
catch err
err
end isa Exception # unfortunately `LoadError`, not an `ArgumentError`
msg = sprint(showerror, err)
@test occursin("First expression to @withprogress must be `name=...`.", msg)
@test occursin("invalid_argument", msg)
end

end # module

0 comments on commit 6b2d3f4

Please sign in to comment.