Skip to content

Commit 08bf742

Browse files
authored
fixes for 404 (#161)
* fixes for #160 * fixing tests * prep a patch release * last fix * mark re-appearing files as changed with -inf mtime
1 parent 36a7b20 commit 08bf742

File tree

6 files changed

+289
-208
lines changed

6 files changed

+289
-208
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ authors = [
44
"Jonas Asprion <jonas.asprion@gmx.ch",
55
"Thibaut Lienart <tlienart@me.com>"
66
]
7-
version = "1.1.1"
7+
version = "1.1.2"
88

99
[deps]
1010
HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3"

src/file_watching.jl

Lines changed: 64 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
"""
22
WatchedFile
33
4-
Struct for a file being watched containing the path to the file as well as the time of last
5-
modification.
4+
Struct for a file being watched containing the path to the file as well as the
5+
time of last modification.
66
"""
77
mutable struct WatchedFile{T<:AbstractString}
88
path::T
@@ -15,15 +15,15 @@ end
1515
Construct a new `WatchedFile` object around a file `f_path`.
1616
"""
1717
WatchedFile(f_path::AbstractString) = WatchedFile(f_path, mtime(f_path))
18-
18+
1919

2020
"""
2121
has_changed(wf::WatchedFile)
2222
23-
Check if a `WatchedFile` has changed. Returns -1 if the file does not exist, 0 if it does exist but
24-
has not changed, and 1 if it has changed.
23+
Check if a `WatchedFile` has changed. Returns -1 if the file does not exist, 0
24+
if it does exist but has not changed, and 1 if it has changed.
2525
"""
26-
function has_changed(wf::WatchedFile)
26+
function has_changed(wf::WatchedFile)::Int
2727
if !isfile(wf.path)
2828
# isfile may return false for a file
2929
# currently being written. Wait for 0.1s
@@ -37,9 +37,17 @@ end
3737
"""
3838
set_unchanged!(wf::WatchedFile)
3939
40-
Set the current state of a `WatchedFile` as unchanged"
40+
Set the current state of a `WatchedFile` as unchanged
41+
"""
42+
set_unchanged!(wf::WatchedFile) = (wf.mtime = mtime(wf.path);)
43+
44+
"""
45+
set_unchanged!(wf::WatchedFile)
46+
47+
Set the current state of a `WatchedFile` as deleted (if it re-appears it will
48+
immediately be marked as changed and trigger the callback).
4149
"""
42-
set_unchanged!(wf::WatchedFile) = (wf.mtime = mtime(wf.path))
50+
set_deleted!(wf::WatchedFile) = (wf.mtime = -Inf;)
4351

4452

4553
"""
@@ -53,69 +61,70 @@ abstract type FileWatcher end
5361
"""
5462
SimpleWatcher([callback]; sleeptime::Float64=0.1) <: FileWatcher
5563
56-
A simple file watcher. You can specify a callback function, receiving the path of each file that
57-
has changed as an `AbstractString`, at construction or later by the API function [`set_callback!`](@ref).
58-
The `sleeptime` is the time waited between two runs of the loop looking for changed files, it is
59-
constrained to be at least 0.05s.
64+
A simple file watcher. You can specify a callback function, receiving the path
65+
of each file that has changed as an `AbstractString`, at construction or later
66+
by the API function [`set_callback!`](@ref).
67+
The `sleeptime` is the time waited between two runs of the loop looking for
68+
changed files, it is constrained to be at least 0.05s.
6069
"""
6170
mutable struct SimpleWatcher <: FileWatcher
62-
callback::Union{Nothing,Function} # callback function triggered upon file change
71+
callback::Union{Nothing,Function} # callback triggered upon file change
6372
task::Union{Nothing,Task} # asynchronous file-watching task
64-
sleeptime::Float64 # sleep-time before checking for file changes
73+
sleeptime::Float64 # sleep before checking for file changes
6574
watchedfiles::Vector{WatchedFile} # list of files being watched
66-
status::Symbol # set to :interrupted as appropriate (caught by server)
75+
status::Symbol # flag caught by server
6776
end
6877

69-
SimpleWatcher(callback::Union{Nothing,Function}=nothing; sleeptime::Float64=0.1) =
70-
SimpleWatcher(callback, nothing, max(0.05, sleeptime), Vector{WatchedFile}(), :runnable)
78+
function SimpleWatcher(
79+
callback::Union{Nothing,Function}=nothing;
80+
sleeptime::Float64=0.1
81+
)
82+
return SimpleWatcher(
83+
callback,
84+
nothing,
85+
max(0.05, sleeptime),
86+
Vector{WatchedFile}(),
87+
:runnable
88+
)
89+
end
7190

7291

7392
"""
7493
file_watcher_task!(w::FileWatcher)
7594
76-
Helper function that's spawned as an asynchronous task and checks for file changes. This task
77-
is normally terminated upon an `InterruptException` and shows a warning in the presence of
78-
any other exception.
95+
Helper function that's spawned as an asynchronous task and checks for file
96+
changes. This task is normally terminated upon an `InterruptException` and
97+
shows a warning in the presence of any other exception.
7998
"""
80-
function file_watcher_task!(fw::FileWatcher)
99+
function file_watcher_task!(fw::FileWatcher)::Nothing
81100
try
82101
while true
83102
sleep(fw.sleeptime)
84103

85104
# only check files if there's a callback to call upon changes
86105
fw.callback === nothing && continue
87106

88-
# keep track of any file that may have been deleted
89-
deleted_files = Vector{Int}()
90-
for (i, wf) enumerate(fw.watchedfiles)
107+
for wf fw.watchedfiles
91108
state = has_changed(wf)
92-
if state == 0
93-
continue
94-
elseif state == 1
95-
# the file has changed, set it unchanged and trigger callback
109+
if state == 1
110+
# file has changed, set it unchanged and trigger callback
96111
set_unchanged!(wf)
97112
fw.callback(wf.path)
98113
elseif state == -1
99-
# the file does not exist, eventually delete it from list of watched files
100-
push!(deleted_files, i)
101-
if VERBOSE[]
102-
@info "[FileWatcher]: file '$(wf.path)' does not " *
103-
"exist (anymore); removing it from list of " *
104-
" watched files."
105-
end
114+
# file has been deleted, set the mtime to -Inf so that
115+
# if it re-appears then it's immediately marked as changed
116+
set_deleted!(wf)
106117
end
107118
end
108-
# remove deleted files from list of watched files
109-
deleteat!(fw.watchedfiles, deleted_files)
110119
end
111120
catch err
112121
fw.status = :interrupted
113122
# an InterruptException is the normal way for this task to end
114123
if !isa(err, InterruptException) && VERBOSE[]
115124
@error "fw error" exception=(err, catch_backtrace())
116125
end
117-
return nothing
118126
end
127+
return nothing
119128
end
120129

121130

@@ -125,7 +134,7 @@ end
125134
Set or change the callback function being executed upon a file change.
126135
Can be "hot-swapped", i.e. while the file watcher is running.
127136
"""
128-
function set_callback!(fw::FileWatcher, callback::Function)
137+
function set_callback!(fw::FileWatcher, callback::Function)::Nothing
129138
prev_running = stop(fw) # returns true if was running
130139
fw.callback = callback
131140
prev_running && start(fw) # restart if it was running before
@@ -149,7 +158,8 @@ Start the file watcher and wait to make sure the task has started.
149158
"""
150159
function start(fw::FileWatcher)
151160
is_running(fw) || (fw.task = @async file_watcher_task!(fw))
152-
# wait until task runs to ensure reliable start (e.g. if `stop` called right afterwards)
161+
# wait until task runs to ensure reliable start (e.g. if `stop` called
162+
# right after start)
153163
while fw.task.state != :runnable
154164
sleep(0.01)
155165
end
@@ -159,15 +169,16 @@ end
159169
"""
160170
stop(fw::FileWatcher)
161171
162-
Stop the file watcher. The list of files being watched is preserved and new files can still be
163-
added to the file watcher using `watch_file!`. It can be restarted with `start`.
164-
Returns a `Bool` indicating whether the watcher was running before `stop` was called.
172+
Stop the file watcher. The list of files being watched is preserved and new
173+
files can still be added to the file watcher using `watch_file!`. It can be
174+
restarted with `start`. Returns a `Bool` indicating whether the watcher was
175+
running before `stop` was called.
165176
"""
166-
function stop(fw::FileWatcher)
177+
function stop(fw::FileWatcher)::Bool
167178
was_running = is_running(fw)
168179
if was_running
169-
# this may fail as the task may get interrupted in between which would lead to
170-
# an error "schedule Task not runnable"
180+
# this may fail as the task may get interrupted in between which would
181+
# lead to an error "schedule Task not runnable"
171182
try
172183
schedule(fw.task, InterruptException(), error=true)
173184
catch
@@ -186,8 +197,9 @@ end
186197
187198
Checks whether the file specified by `f_path` is being watched.
188199
"""
189-
is_watched(fw::FileWatcher, f_path::AbstractString) =
190-
any(wf -> wf.path == f_path, fw.watchedfiles)
200+
function is_watched(fw::FileWatcher, f_path::AbstractString)
201+
return any(wf -> wf.path == f_path, fw.watchedfiles)
202+
end
191203

192204

193205
"""
@@ -198,6 +210,9 @@ Add a file to be watched for changes.
198210
function watch_file!(fw::FileWatcher, f_path::AbstractString)
199211
if isfile(f_path) && !is_watched(fw, f_path)
200212
push!(fw.watchedfiles, WatchedFile(f_path))
201-
VERBOSE[] && @info("[FileWatcher]: now watching '$f_path'")
213+
if VERBOSE[]
214+
@info "[FileWatcher]: now watching '$f_path'"
215+
println()
216+
end
202217
end
203218
end

0 commit comments

Comments
 (0)