-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
[4.2] Persistent callbacks #3771
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
base: v4.2
Are you sure you want to change the base?
Changes from all commits
7ba747a
2bdb5da
9c76a17
b1a6f0f
512e119
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -165,6 +165,20 @@ def _concat(x): | |||||
|
|
||||||
| if no_output: | ||||||
| # No output will hash the inputs. | ||||||
| # For no-input callbacks, also include the call site to make each unique | ||||||
| if not inputs: | ||||||
| # Get the call site of the @callback decorator | ||||||
| stack = inspect.stack() | ||||||
| # Walk up the stack to find the actual callback call site | ||||||
| # (skip internal dash package frames) | ||||||
| dash_package_path = os.path.dirname(__file__) | ||||||
| for frame_info in stack: | ||||||
| # Skip frames from within the dash package itself | ||||||
| if not frame_info.filename.startswith(dash_package_path): | ||||||
| call_site = f"{frame_info.filename}:{frame_info.lineno}" | ||||||
| return hashlib.sha256(call_site.encode("utf-8")).hexdigest() | ||||||
| # Fallback to empty hash if no external frame found | ||||||
| return _hash_inputs() | ||||||
|
Comment on lines
+180
to
+181
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| return _hash_inputs() | ||||||
|
|
||||||
| if isinstance(output, (list, tuple)): | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,7 +5,12 @@ import {getAppState} from '../reducers/constants'; | |
| import {getAction} from './constants'; | ||
| import * as cookie from 'cookie'; | ||
| import {validateCallbacksToLayout} from './dependencies'; | ||
| import {includeObservers, getLayoutCallbacks} from './dependencies_ts'; | ||
| import { | ||
| includeObservers, | ||
| getLayoutCallbacks, | ||
| makeResolvedCallback, | ||
| resolveDeps | ||
| } from './dependencies_ts'; | ||
| import {computePaths, getPath} from './paths'; | ||
| import {recordUiEdit} from '../persistence'; | ||
|
|
||
|
|
@@ -95,12 +100,62 @@ function triggerDefaultState(dispatch, getState) { | |
| ); | ||
| } | ||
|
|
||
| dispatch( | ||
| addRequestedCallbacks( | ||
| getLayoutCallbacks(graphs, paths, layout.components, { | ||
| outputsOnly: true | ||
| }) | ||
| const layoutCallbacks = getLayoutCallbacks( | ||
| graphs, | ||
| paths, | ||
| layout.components, | ||
| { | ||
| outputsOnly: true | ||
| } | ||
| ); | ||
|
|
||
| // Also include no-output callbacks whose inputs are in the layout (or have no inputs) | ||
| const noOutputCallbacks = (graphs.callbacks || []) | ||
| .filter(cb => cb.noOutput && !cb.prevent_initial_call) | ||
| .map(cb => { | ||
| const resolved = makeResolvedCallback(cb, resolveDeps(), ''); | ||
| resolved.initialCall = true; | ||
| return resolved; | ||
| }) | ||
| .filter(cb => { | ||
| // If no inputs, always include (fires once on initial load) | ||
| if (cb.callback.inputs.length === 0) { | ||
| return true; | ||
| } | ||
| // Check if any input is in the layout | ||
| const inputs = cb.getInputs(paths); | ||
| return inputs.some(inp => | ||
| Array.isArray(inp) ? inp.length > 0 : inp | ||
| ); | ||
| }); | ||
|
Comment on lines
+113
to
+130
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You iterate through the callbacks three times. What do you think of switching to a |
||
|
|
||
| // Also include no-input callbacks (with outputs) that should fire on initial load | ||
| const noInputCallbacks = (graphs.callbacks || []) | ||
| .filter( | ||
| cb => | ||
| !cb.noOutput && | ||
| cb.inputs.length === 0 && | ||
| !cb.prevent_initial_call | ||
| ) | ||
| .map(cb => { | ||
| const resolved = makeResolvedCallback(cb, resolveDeps(), ''); | ||
| resolved.initialCall = true; | ||
| return resolved; | ||
| }) | ||
| .filter(cb => { | ||
| // Check if any output is in the layout | ||
| const outputs = cb.getOutputs(paths); | ||
| return outputs.some(out => | ||
| Array.isArray(out) ? out.length > 0 : out | ||
| ); | ||
| }); | ||
|
|
||
| dispatch( | ||
| addRequestedCallbacks([ | ||
| ...layoutCallbacks, | ||
| ...noOutputCallbacks, | ||
| ...noInputCallbacks | ||
| ]) | ||
| ); | ||
| } | ||
|
|
||
|
|
||
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.