Skip to content

Commit 3645518

Browse files
committed
Removed unnecessary checks for the validity of callbacks
1 parent 30f22d0 commit 3645518

File tree

5 files changed

+62
-51
lines changed

5 files changed

+62
-51
lines changed

src/app/callbacks.jl

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -165,21 +165,11 @@ function check_callback(func, app::DashApp, deps::CallbackDeps)
165165
isempty(deps.output) && error("The callback method requires that one or more properly formatted outputs are passed.")
166166
isempty(deps.input) && error("The callback method requires that one or more properly formatted inputs are passed.")
167167

168-
!check_unique(deps.output) && error("One or more callback outputs have been duplicated; please confirm that all outputs are unique.")
169-
170-
for out in deps.output
171-
if any(x->out in x.dependencies.output, values(app.callbacks))
172-
error("output \"$(out)\" already registered")
173-
end
174-
end
175168

176169
args_count = length(deps.state) + length(deps.input)
177170

178171
check_callback_func(func, args_count)
179172

180-
for id_prop in deps.input
181-
id_prop in deps.output && error("Circular input and output arguments were found. Please verify that callback outputs are not also input arguments.")
182-
end
183173
end
184174

185175
function check_callback_func(func::Function, args_count)

src/handler/callback_context.jl

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,50 @@
11
using .Contexts
22
const CallbackContextItems = Union{Nothing, Vector{NamedTuple}}
33
const TriggeredParam = NamedTuple{(:prop_id, :value)}
4+
5+
struct FakeTriggeredParams <: AbstractVector{TriggeredParam}
6+
end
7+
Base.size(v::FakeTriggeredParams) = (1,)
8+
Base.isempty(v::FakeTriggeredParams) = true
9+
Base.IndexStyle(::Type{FakeTriggeredParams}) = IndexLinear()
10+
Base.length(v::FakeTriggeredParams) = 1
11+
function Base.getindex(v::FakeTriggeredParams, i::Int)
12+
i != 1 && throw(BoundsError(v, i))
13+
return (prop_id = ".", value = nothing)
14+
end
15+
416
mutable struct CallbackContext
517
response::HTTP.Response
618
inputs::Dict{String, Any}
719
states::Dict{String, Any}
820
outputs_list::Vector{Any}
921
inputs_list::Vector{Any}
1022
states_list::Vector{Any}
11-
triggered::Vector{TriggeredParam}
23+
triggered::Union{Vector{TriggeredParam}, FakeTriggeredParams}
1224
function CallbackContext(response, outputs_list, inputs_list, states_list, changed_props)
1325
input_values = inputs_list_to_dict(inputs_list)
1426
state_values = inputs_list_to_dict(states_list)
15-
triggered = TriggeredParam[(prop_id = id, value = input_values[id]) for id in changed_props]
27+
if !isempty(changed_props)
28+
triggered = TriggeredParam[(prop_id = id, value = input_values[id]) for id in changed_props]
29+
else
30+
triggered = FakeTriggeredParams()
31+
end
1632
return new(response, input_values, state_values, outputs_list, inputs_list, states_list, triggered)
1733
end
1834
end
1935

2036
const _callback_context_storage = TaskContextStorage()
2137

22-
function with_callback_context(f, context::CallbackContext)
38+
function with_callback_context(f, context::CallbackContext)
2339
return with_context(f, _callback_context_storage, context)
24-
end
40+
end
2541

2642
"""
2743
callback_context()::CallbackContext
2844
2945
Get context of current callback, available only inside callback processing function
3046
"""
31-
function callback_context()
47+
function callback_context()
3248
!has_context(_callback_context_storage) && error("callback_context() is only available from a callback processing function")
3349
return get_context(_callback_context_storage)
3450
end

test/callbacks.jl

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -260,32 +260,6 @@ end
260260
@test app.callbacks[Symbol("my-div.children")].func("value") == "value"
261261
@test app.callbacks[Symbol("my-div2.children")].func("value") == "v_value"
262262

263-
#output already exists
264-
@test_throws ErrorException callback!(app, Output("my-div2","children"), Input("my-id","value")) do value
265-
return "v_$(value)"
266-
end
267-
268-
#output same as input
269-
@test_throws ErrorException callback!(app,
270-
Output("my-id","value"),
271-
Input("my-id","value"),
272-
State("my-id","value")) do value
273-
return "v_$(value)"
274-
end
275-
276-
#output same as input
277-
@test_throws ErrorException callback!(app,
278-
Output("my-id","value"),
279-
[Input("my-id","value"), Input("my-div","children")]) do value
280-
return "v_$(value)"
281-
end
282-
283-
#output same as input
284-
@test_throws ErrorException callback!(app,
285-
[Output("my-id","value"), Output("my-div","children")],
286-
Input("my-id","value")) do value
287-
return "v_$(value)"
288-
end
289263

290264
#empty input
291265
@test_throws ErrorException callback!(app,
@@ -295,13 +269,6 @@ end
295269
end
296270

297271

298-
#duplicated output
299-
@test_throws ErrorException callback!(app,
300-
[Output("my-div","value"), Output("my-div","value")],
301-
Input("my-id","value")) do value
302-
return "v_$(value)"
303-
end
304-
305272
app = dash()
306273

307274
app.layout = html_div() do
@@ -339,6 +306,38 @@ end
339306

340307
end
341308

309+
@testset "fake triggered params" begin
310+
app = dash()
311+
app.layout = html_div() do
312+
dcc_input(id = "test-in", value="initial value", type = "text"),
313+
html_div(id = "test-out")
314+
end
315+
316+
callback!(app, Output("test-out", "children"), Input("test-out", "value")) do value
317+
context = callback_context()
318+
@test length(context.triggered) == 1
319+
@test context.triggered[1].prop_id == "."
320+
@test isnothing(context.triggered[1].value)
321+
@test isempty(context.triggered)
322+
return string(value)
323+
end
324+
@test length(app.callbacks) == 1
325+
326+
handler = Dash.make_handler(app)
327+
request = (
328+
output = "test-out.children",
329+
changedPropIds = [],
330+
inputs = [
331+
(id = "test-in", property = "value", value = "test")
332+
]
333+
)
334+
test_json = JSON2.write(request)
335+
request = HTTP.Request("POST", "/_dash-update-component", [], Vector{UInt8}(test_json))
336+
response = HTTP.handle(handler, request)
337+
338+
@test response.status == 200
339+
340+
end
342341
@testset "pattern-match" begin
343342
app = dash()
344343
app.layout = html_div() do

test/integration/callbacks/jl_test_wildcards/jlcbwc004_layout_chunk_changed_props.jl

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,14 @@ end
2424

2525
function trigger_info()
2626
triggered = callback_context().triggered
27-
trig_string = !isempty(triggered) ? "Truthy" : "Falsy"
28-
prop_ids = join(getproperty.(triggered, :prop_id), ", ")
27+
trig_string = ""
28+
prop_ids = ""
29+
if isempty(triggered)
30+
trig_string = "Falsy"
31+
else
32+
trig_string = "Truthy"
33+
prop_ids = join(getproperty.(triggered, :prop_id), ", ")
34+
end
2935
return "triggered is $trig_string with prop_ids $prop_ids"
3036
end
3137

test/integration/callbacks/test_wildcards.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ def test_jlcbwc004_layout_chunk_changed_props(dashjl):
174174

175175
dashjl.wait_for_text_to_equal("#container", "No content initially", timeout = 10)
176176
dashjl.wait_for_text_to_equal(
177-
"#output-outer", "triggered is Falsy with prop_ids", timeout = 10
177+
"#output-outer", "triggered is Falsy with prop_ids", timeout = 30
178178
)
179179

180180
dashjl.find_element("#btn").click()

0 commit comments

Comments
 (0)