From bc0735cc6864affbc2d382a17e040bf46e375b83 Mon Sep 17 00:00:00 2001 From: philippe Date: Tue, 5 Mar 2024 13:12:46 -0500 Subject: [PATCH 1/4] Remove dynamic loading. --- dash/_validate.py | 8 ++ dash/dash-renderer/src/APIController.react.js | 44 ++---- .../src/CheckedComponent.react.js | 30 ---- dash/dash-renderer/src/TreeContainer.js | 68 +++++++-- dash/dash-renderer/src/actions/callbacks.ts | 43 +----- dash/dash-renderer/src/actions/libraries.ts | 10 -- .../src/libraries/LibraryComponent.tsx | 47 ------- .../src/libraries/LibraryManager.tsx | 67 --------- .../src/libraries/createLibraryElement.js | 10 -- dash/dash-renderer/src/libraries/fetchDist.ts | 12 -- .../src/libraries/librariesContext.ts | 132 ------------------ .../src/libraries/libraryTypes.ts | 17 --- .../src/libraries/loadLibrary.ts | 31 ---- dash/dash-renderer/src/reducers/libraries.ts | 60 -------- dash/dash-renderer/src/reducers/reducer.js | 2 - dash/dash-renderer/src/types/callbacks.ts | 3 - dash/dash.py | 51 ++----- tests/integration/renderer/test_libraries.py | 52 ------- tests/integration/test_generation.py | 8 +- 19 files changed, 90 insertions(+), 605 deletions(-) delete mode 100644 dash/dash-renderer/src/CheckedComponent.react.js delete mode 100644 dash/dash-renderer/src/actions/libraries.ts delete mode 100644 dash/dash-renderer/src/libraries/LibraryComponent.tsx delete mode 100644 dash/dash-renderer/src/libraries/LibraryManager.tsx delete mode 100644 dash/dash-renderer/src/libraries/createLibraryElement.js delete mode 100644 dash/dash-renderer/src/libraries/fetchDist.ts delete mode 100644 dash/dash-renderer/src/libraries/librariesContext.ts delete mode 100644 dash/dash-renderer/src/libraries/libraryTypes.ts delete mode 100644 dash/dash-renderer/src/libraries/loadLibrary.ts delete mode 100644 dash/dash-renderer/src/reducers/libraries.ts delete mode 100644 tests/integration/renderer/test_libraries.py diff --git a/dash/_validate.py b/dash/_validate.py index 29caf0ace2..dcd075b8cd 100644 --- a/dash/_validate.py +++ b/dash/_validate.py @@ -1,3 +1,4 @@ +import sys from collections.abc import MutableSequence import re from textwrap import dedent @@ -356,6 +357,13 @@ def check_obsolete(kwargs): See https://dash.plotly.com for details. """ ) + if key in ["dynamic_loading", "preloaded_libraries"]: + # Only warns as this was only available for a short time. + print( + f"{key} has been removed and no longer a valid keyword argument in Dash.", + file=sys.stderr, + ) + continue # any other kwarg mimic the built-in exception raise TypeError(f"Dash() got an unexpected keyword argument '{key}'") diff --git a/dash/dash-renderer/src/APIController.react.js b/dash/dash-renderer/src/APIController.react.js index 9e90dc6268..ed443bad5c 100644 --- a/dash/dash-renderer/src/APIController.react.js +++ b/dash/dash-renderer/src/APIController.react.js @@ -1,12 +1,6 @@ import {batch, connect} from 'react-redux'; import {includes, isEmpty} from 'ramda'; -import React, { - useEffect, - useRef, - useState, - createContext, - useCallback -} from 'react'; +import React, {useEffect, useRef, useState, createContext} from 'react'; import PropTypes from 'prop-types'; import TreeContainer from './TreeContainer'; import GlobalErrorContainer from './components/error/GlobalErrorContainer.react'; @@ -27,7 +21,6 @@ import {getAppState} from './reducers/constants'; import {STATUS} from './constants/constants'; import {getLoadingState, getLoadingHash} from './utils/TreeContainer'; import wait from './utils/wait'; -import LibraryManager from './libraries/LibraryManager'; export const DashContext = createContext({}); @@ -53,10 +46,6 @@ const UnconnectedContainer = props => { if (!events.current) { events.current = new EventEmitter(); } - - const [libraryReady, setLibraryReady] = useState(false); - const onLibraryReady = useCallback(() => setLibraryReady(true), []); - const renderedTree = useRef(false); const propsRef = useRef({}); @@ -71,9 +60,7 @@ const UnconnectedContainer = props => { }) }); - useEffect( - storeEffect.bind(null, props, events, setErrorLoading, libraryReady) - ); + useEffect(storeEffect.bind(null, props, events, setErrorLoading)); useEffect(() => { if (renderedTree.current) { @@ -130,23 +117,14 @@ const UnconnectedContainer = props => { content =
Loading...
; } - return ( - - {config && config.ui === true ? ( - {content} - ) : ( - content - )} - + return config && config.ui === true ? ( + {content} + ) : ( + content ); }; -function storeEffect(props, events, setErrorLoading, libraryReady) { +function storeEffect(props, events, setErrorLoading) { const { appLifecycle, dependenciesRequest, @@ -165,7 +143,7 @@ function storeEffect(props, events, setErrorLoading, libraryReady) { } dispatch(apiThunk('_dash-layout', 'GET', 'layoutRequest')); } else if (layoutRequest.status === STATUS.OK) { - if (isEmpty(layout) && libraryReady) { + if (isEmpty(layout)) { if (typeof hooks.layout_post === 'function') { hooks.layout_post(layoutRequest.content); } @@ -208,8 +186,7 @@ function storeEffect(props, events, setErrorLoading, libraryReady) { layoutRequest.status === STATUS.OK && !isEmpty(layout) && // Hasn't already hydrated - appLifecycle === getAppState('STARTED') && - libraryReady + appLifecycle === getAppState('STARTED') ) { let hasError = false; try { @@ -258,8 +235,7 @@ const Container = connect( graphs: state.graphs, history: state.history, error: state.error, - config: state.config, - paths: state.paths + config: state.config }), dispatch => ({dispatch}) )(UnconnectedContainer); diff --git a/dash/dash-renderer/src/CheckedComponent.react.js b/dash/dash-renderer/src/CheckedComponent.react.js deleted file mode 100644 index 4eeed6d524..0000000000 --- a/dash/dash-renderer/src/CheckedComponent.react.js +++ /dev/null @@ -1,30 +0,0 @@ -import checkPropTypes from './checkPropTypes'; -import {propTypeErrorHandler} from './exceptions'; -import {createLibraryElement} from './libraries/createLibraryElement'; -import PropTypes from 'prop-types'; - -export function CheckedComponent(p) { - const {element, extraProps, props, children, type} = p; - - const errorMessage = checkPropTypes( - element.propTypes, - props, - 'component prop', - element - ); - if (errorMessage) { - propTypeErrorHandler(errorMessage, props, type); - } - - return createLibraryElement(element, props, extraProps, children); -} - -CheckedComponent.propTypes = { - children: PropTypes.any, - element: PropTypes.any, - layout: PropTypes.any, - props: PropTypes.any, - extraProps: PropTypes.any, - id: PropTypes.string, - type: PropTypes.string -}; diff --git a/dash/dash-renderer/src/TreeContainer.js b/dash/dash-renderer/src/TreeContainer.js index b79980e9a8..304cd0be74 100644 --- a/dash/dash-renderer/src/TreeContainer.js +++ b/dash/dash-renderer/src/TreeContainer.js @@ -1,5 +1,7 @@ import React, {Component, memo, useContext} from 'react'; import PropTypes from 'prop-types'; +import Registry from './registry'; +import {propTypeErrorHandler} from './exceptions'; import { addIndex, assoc, @@ -7,25 +9,25 @@ import { concat, dissoc, equals, - has, isEmpty, isNil, + has, keys, map, mapObjIndexed, - path as rpath, - pathOr, + mergeRight, pick, pickBy, propOr, + path as rpath, + pathOr, type } from 'ramda'; -import {batch} from 'react-redux'; - import {notifyObservers, updateProps, onError} from './actions'; import isSimpleComponent from './isSimpleComponent'; import {recordUiEdit} from './persistence'; import ComponentErrorBoundary from './components/error/ComponentErrorBoundary.react'; +import checkPropTypes from './checkPropTypes'; import {getWatchedKeys, stringifyId} from './actions/dependencies'; import { getLoadingHash, @@ -33,12 +35,45 @@ import { validateComponent } from './utils/TreeContainer'; import {DashContext} from './APIController.react'; -import LibraryComponent from './libraries/LibraryComponent'; +import {batch} from 'react-redux'; const NOT_LOADING = { is_loading: false }; +function CheckedComponent(p) { + const {element, extraProps, props, children, type} = p; + + const errorMessage = checkPropTypes( + element.propTypes, + props, + 'component prop', + element + ); + if (errorMessage) { + propTypeErrorHandler(errorMessage, props, type); + } + + return createElement(element, props, extraProps, children); +} + +CheckedComponent.propTypes = { + children: PropTypes.any, + element: PropTypes.any, + layout: PropTypes.any, + props: PropTypes.any, + extraProps: PropTypes.any, + id: PropTypes.string +}; + +function createElement(element, props, extraProps, children) { + const allProps = mergeRight(props, extraProps); + if (Array.isArray(children)) { + return React.createElement(element, allProps, ...children); + } + return React.createElement(element, allProps, children); +} + function isDryComponent(obj) { return ( type(obj) === 'Object' && @@ -215,6 +250,8 @@ class BaseTreeContainer extends Component { } validateComponent(_dashprivate_layout); + const element = Registry.resolve(_dashprivate_layout); + // Hydrate components props const childrenProps = pathOr( [], @@ -418,14 +455,17 @@ class BaseTreeContainer extends Component { dispatch={_dashprivate_dispatch} error={_dashprivate_error} > - + {_dashprivate_config.props_check ? ( + + ) : ( + createElement(element, props, extraProps, children) + )} ); } diff --git a/dash/dash-renderer/src/actions/callbacks.ts b/dash/dash-renderer/src/actions/callbacks.ts index e1e4b4601b..68320568e5 100644 --- a/dash/dash-renderer/src/actions/callbacks.ts +++ b/dash/dash-renderer/src/actions/callbacks.ts @@ -34,7 +34,7 @@ import { CallbackResponseData } from '../types/callbacks'; import {isMultiValued, stringifyId, isMultiOutputProp} from './dependencies'; -import {crawlLayout, urlBase} from './utils'; +import {urlBase} from './utils'; import {getCSRFHeader} from '.'; import {createAction, Action} from 'redux-actions'; import {addHttpHeaders} from '../actions'; @@ -44,9 +44,6 @@ import {handlePatch, isPatch} from './patch'; import {getPath} from './paths'; import {requestDependencies} from './requestDependencies'; -import loadLibrary from '../libraries/loadLibrary'; -import fetchDist from '../libraries/fetchDist'; -import {setLibraryLoaded} from './libraries'; export const addBlockedCallbacks = createAction( CallbackActionType.AddBlocked @@ -366,7 +363,6 @@ function handleServerside( let runningOff: any; let progressDefault: any; let moreArgs = additionalArgs; - const libraries = Object.keys(getState().libraries); if (running) { sideUpdate(running.running, dispatch, paths); @@ -512,41 +508,8 @@ function handleServerside( } if (!long || data.response !== undefined) { - const newLibs: string[] = []; - Object.values(data.response as any).forEach( - (newData: any) => { - Object.values(newData).forEach(newProp => { - crawlLayout(newProp, (c: any) => { - if ( - c.namespace && - !libraries.includes(c.namespace) && - !newLibs.includes(c.namespace) - ) { - newLibs.push(c.namespace); - } - }); - }); - } - ); - if (newLibs.length) { - fetchDist( - getState().config.requests_pathname_prefix, - newLibs - ) - .then(data => { - return Promise.all(data.map(loadLibrary)); - }) - .then(() => { - completeJob(); - finishLine(data); - dispatch( - setLibraryLoaded({libraries: newLibs}) - ); - }); - } else { - completeJob(); - finishLine(data); - } + completeJob(); + finishLine(data); } else { // Poll chain. setTimeout( diff --git a/dash/dash-renderer/src/actions/libraries.ts b/dash/dash-renderer/src/actions/libraries.ts deleted file mode 100644 index 5cac6b3597..0000000000 --- a/dash/dash-renderer/src/actions/libraries.ts +++ /dev/null @@ -1,10 +0,0 @@ -import {LibrariesActions} from '../libraries/libraryTypes'; - -const createAction = (type: LibrariesActions) => (payload: any) => ({ - type, - payload -}); - -export const setLibraryLoading = createAction(LibrariesActions.LOAD); -export const setLibraryLoaded = createAction(LibrariesActions.LOADED); -export const setLibraryToLoad = createAction(LibrariesActions.TO_LOAD); diff --git a/dash/dash-renderer/src/libraries/LibraryComponent.tsx b/dash/dash-renderer/src/libraries/LibraryComponent.tsx deleted file mode 100644 index 0b8b18104c..0000000000 --- a/dash/dash-renderer/src/libraries/LibraryComponent.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import React, {useContext, useEffect} from 'react'; -import {LibrariesContext} from './librariesContext'; -import Registry from '../registry'; -import {CheckedComponent} from '../CheckedComponent.react'; -import {createLibraryElement} from './createLibraryElement'; - -type LibraryComponentProps = { - type: string; - namespace: string; - props: any; - extraProps: any; - children: any; - props_check: boolean; -}; - -const LibraryComponent = (props: LibraryComponentProps) => { - const {props_check, namespace, type, ...rest} = props; - - const context = useContext(LibrariesContext); - - useEffect(() => { - context.addToLoad(namespace); - }, []); - - if (!context.isLoaded(namespace)) { - return <>; - } - const element = Registry.resolve({namespace, type}); - if (props_check) { - return ( - - ); - } - return createLibraryElement( - element, - rest.props, - rest.extraProps, - rest.children - ); -}; -export default LibraryComponent; diff --git a/dash/dash-renderer/src/libraries/LibraryManager.tsx b/dash/dash-renderer/src/libraries/LibraryManager.tsx deleted file mode 100644 index ad248be751..0000000000 --- a/dash/dash-renderer/src/libraries/LibraryManager.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import React, {JSX, useEffect, useState} from 'react'; - -import {createLibrariesContext, LibrariesContext} from './librariesContext'; -import {crawlLayout} from '../actions/utils'; -import {isEmpty} from 'ramda'; - -type LibrariesManagerProps = { - children: JSX.Element; - requests_pathname_prefix: string; - onReady: () => void; - ready: boolean; - layout?: any; - initialLibraries?: string[]; -}; - -const LibraryProvider = (props: LibrariesManagerProps) => { - const { - children, - requests_pathname_prefix, - onReady, - ready, - initialLibraries - } = props; - const contextValue = createLibrariesContext( - requests_pathname_prefix, - initialLibraries as string[], - onReady, - ready - ); - return ( - - {children} - - ); -}; - -const LibraryManager = (props: LibrariesManagerProps) => { - const {children, ready, layout} = props; - - const [initialLibraries, setInitialLibraries] = useState( - null - ); - - useEffect(() => { - if (layout && !isEmpty(layout) && !ready && !initialLibraries) { - const libraries: string[] = []; - crawlLayout(layout, (child: any) => { - if (child.namespace && !libraries.includes(child.namespace)) { - libraries.push(child.namespace); - } - }); - setInitialLibraries(libraries); - } - }, [layout, ready, initialLibraries]); - - if (!initialLibraries) { - return children; - } - - return ( - - {children} - - ); -}; - -export default LibraryManager; diff --git a/dash/dash-renderer/src/libraries/createLibraryElement.js b/dash/dash-renderer/src/libraries/createLibraryElement.js deleted file mode 100644 index e5ebe6fa68..0000000000 --- a/dash/dash-renderer/src/libraries/createLibraryElement.js +++ /dev/null @@ -1,10 +0,0 @@ -import {mergeRight} from 'ramda'; -import React from 'react'; - -export function createLibraryElement(element, props, extraProps, children) { - const allProps = mergeRight(props, extraProps); - if (Array.isArray(children)) { - return React.createElement(element, allProps, ...children); - } - return React.createElement(element, allProps, children); -} diff --git a/dash/dash-renderer/src/libraries/fetchDist.ts b/dash/dash-renderer/src/libraries/fetchDist.ts deleted file mode 100644 index d82841e6d2..0000000000 --- a/dash/dash-renderer/src/libraries/fetchDist.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {LibraryResource} from './libraryTypes'; - -export default function fetchDist( - pathnamePrefix: string, - libraries: string[] -): Promise { - return fetch(`${pathnamePrefix}_dash-dist`, { - body: JSON.stringify(libraries), - headers: {'Content-Type': 'application/json'}, - method: 'POST' - }).then(response => response.json()); -} diff --git a/dash/dash-renderer/src/libraries/librariesContext.ts b/dash/dash-renderer/src/libraries/librariesContext.ts deleted file mode 100644 index ed871850c8..0000000000 --- a/dash/dash-renderer/src/libraries/librariesContext.ts +++ /dev/null @@ -1,132 +0,0 @@ -import {createContext, useEffect, useState} from 'react'; -import {pathOr, toPairs} from 'ramda'; -import loadLibrary from './loadLibrary'; -import {batch, useDispatch, useSelector} from 'react-redux'; -import {LibrariesState} from './libraryTypes'; -import { - setLibraryLoaded, - setLibraryLoading, - setLibraryToLoad -} from '../actions/libraries'; -import fetchDist from './fetchDist'; - -export type LibrariesContextType = { - state: LibrariesState; - isLoading: (libraryName: string) => boolean; - isLoaded: (libraryName: string) => boolean; - fetchLibraries: () => void; - getLibrariesToLoad: () => string[]; - addToLoad: (libName: string) => void; -}; - -function librarySelector(s: any) { - return s.libraries as LibrariesState; -} - -export function createLibrariesContext( - pathnamePrefix: string, - initialLibraries: string[], - onReady: () => void, - ready: boolean -): LibrariesContextType { - const dispatch = useDispatch(); - const state = useSelector(librarySelector); - const [callback, setCallback] = useState(-1); - - const isLoaded = (libraryName: string) => - pathOr(false, [libraryName, 'loaded'], state); - const isLoading = (libraryName: string) => - pathOr(false, [libraryName, 'loading'], state); - - const addToLoad = (libraryName: string) => { - const lib = state[libraryName]; - if (!lib) { - // Check if already loaded on the window - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - if (window[libraryName]) { - dispatch(setLibraryLoaded({libraries: [libraryName]})); - } else { - dispatch(setLibraryToLoad({library: libraryName})); - } - } - // if lib is already in don't do anything. - }; - - const getLibrariesToLoad = () => - toPairs(state).reduce((acc: string[], [key, value]) => { - if (value.toLoad) { - acc.push(key); - } - return acc; - }, []); - - const fetchLibraries = () => { - const libraries = getLibrariesToLoad(); - if (!libraries.length) { - return; - } - - dispatch(setLibraryLoading({libraries})); - - fetchDist(pathnamePrefix, libraries) - .then(data => { - return Promise.all(data.map(loadLibrary)); - }) - .then(() => { - dispatch(setLibraryLoaded({libraries})); - setCallback(-1); - onReady(); - }); - }; - - useEffect(() => { - batch(() => { - const loaded: string[] = []; - initialLibraries.forEach(lib => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - if (window[lib]) { - loaded.push(lib); - } else { - dispatch(setLibraryToLoad({library: lib})); - } - }); - if (loaded.length) { - dispatch(setLibraryLoaded({libraries: loaded})); - } - if (loaded.length === initialLibraries.length) { - onReady(); - } - }); - }, [initialLibraries]); - - // Load libraries on a throttle to have time to gather all the components in one go. - useEffect(() => { - if (ready) { - return; - } - const libraries = getLibrariesToLoad(); - if (!libraries.length) { - return; - } - if (callback > 0) { - window.clearTimeout(callback); - } - const timeout = window.setTimeout(fetchLibraries, 0); - setCallback(timeout); - }, [state, ready, initialLibraries]); - - return { - state, - isLoaded, - isLoading, - fetchLibraries, - getLibrariesToLoad, - addToLoad - }; -} - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -export const LibrariesContext = createContext(null); diff --git a/dash/dash-renderer/src/libraries/libraryTypes.ts b/dash/dash-renderer/src/libraries/libraryTypes.ts deleted file mode 100644 index b1c5f4887c..0000000000 --- a/dash/dash-renderer/src/libraries/libraryTypes.ts +++ /dev/null @@ -1,17 +0,0 @@ -export enum LibrariesActions { - LOAD = 'LOAD_LIBRARY', - LOADED = 'LOADED_LIBRARY', - TO_LOAD = 'TO_LOAD' -} - -export type LibrariesState = { - [libname: string]: { - toLoad: boolean; - loading: boolean; - loaded: boolean; - }; -}; -export type LibraryResource = { - type: '_js_dist' | '_css_dist'; - url: string; -}; diff --git a/dash/dash-renderer/src/libraries/loadLibrary.ts b/dash/dash-renderer/src/libraries/loadLibrary.ts deleted file mode 100644 index 0f83670c1d..0000000000 --- a/dash/dash-renderer/src/libraries/loadLibrary.ts +++ /dev/null @@ -1,31 +0,0 @@ -import {LibraryResource} from './libraryTypes'; - -export default function (resource: LibraryResource) { - let prom; - const head = document.querySelector('head'); - if (resource.type === '_js_dist') { - const element = document.createElement('script'); - element.src = resource.url; - element.async = true; - prom = new Promise((resolve, reject) => { - element.onload = () => { - resolve(); - }; - element.onerror = error => reject(error); - }); - - head?.appendChild(element); - } else if (resource.type === '_css_dist') { - const element = document.createElement('link'); - element.href = resource.url; - element.rel = 'stylesheet'; - prom = new Promise((resolve, reject) => { - element.onload = () => { - resolve(); - }; - element.onerror = error => reject(error); - }); - head?.appendChild(element); - } - return prom; -} diff --git a/dash/dash-renderer/src/reducers/libraries.ts b/dash/dash-renderer/src/reducers/libraries.ts deleted file mode 100644 index 421108b486..0000000000 --- a/dash/dash-renderer/src/reducers/libraries.ts +++ /dev/null @@ -1,60 +0,0 @@ -import {assocPath, pipe} from 'ramda'; -import {LibrariesActions, LibrariesState} from '../libraries/libraryTypes'; - -type LoadingPayload = { - libraries: string[]; -}; - -type LoadedPayload = { - libraries: string[]; -}; - -type ToLoadPayload = { - library: string; -}; - -type LibrariesAction = { - type: LibrariesActions; - payload: LoadingPayload | LoadedPayload | ToLoadPayload; -}; - -function handleLoad(library: string, state: LibrariesState) { - return pipe( - assocPath([library, 'loading'], true), - assocPath([library, 'toLoad'], false) - )(state) as LibrariesState; -} - -function handleLoaded(library: string, state: LibrariesState) { - return pipe( - assocPath([library, 'loaded'], true), - assocPath([library, 'loading'], false) - )(state) as LibrariesState; -} - -export default function librariesReducer( - state: LibrariesState = {}, - action: LibrariesAction -): LibrariesState { - switch (action.type) { - case LibrariesActions.LOAD: - return (action.payload as LoadingPayload).libraries.reduce( - (acc, lib) => handleLoad(lib, acc), - state - ); - case LibrariesActions.LOADED: - return (action.payload as LoadedPayload).libraries.reduce( - (acc, lib) => handleLoaded(lib, acc), - state - ); - case LibrariesActions.TO_LOAD: - return pipe( - assocPath( - [(action.payload as ToLoadPayload).library, 'toLoad'], - true - ) - )(state) as LibrariesState; - default: - return state; - } -} diff --git a/dash/dash-renderer/src/reducers/reducer.js b/dash/dash-renderer/src/reducers/reducer.js index f025d27354..97b71b6bce 100644 --- a/dash/dash-renderer/src/reducers/reducer.js +++ b/dash/dash-renderer/src/reducers/reducer.js @@ -18,7 +18,6 @@ import layout from './layout'; import loadingMap from './loadingMap'; import paths from './paths'; import callbackJobs from './callbackJobs'; -import libraries from './libraries'; export const apiRequests = [ 'dependenciesRequest', @@ -48,7 +47,6 @@ function mainReducer() { }, apiRequests); parts.callbackJobs = callbackJobs; - parts.libraries = libraries; return combineReducers(parts); } diff --git a/dash/dash-renderer/src/types/callbacks.ts b/dash/dash-renderer/src/types/callbacks.ts index 6a499cf1d1..62fcb19d20 100644 --- a/dash/dash-renderer/src/types/callbacks.ts +++ b/dash/dash-renderer/src/types/callbacks.ts @@ -1,5 +1,3 @@ -import {LibraryResource} from '../libraries/libraryTypes'; - type CallbackId = string | {[key: string]: any}; export interface ICallbackDefinition { @@ -105,5 +103,4 @@ export type CallbackResponseData = { running?: CallbackResponse; runningOff?: CallbackResponse; cancel?: ICallbackProperty[]; - resources: LibraryResource[]; }; diff --git a/dash/dash.py b/dash/dash.py index 318015abe5..1876eecd15 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -404,8 +404,6 @@ def __init__( # pylint: disable=too-many-statements add_log_handler=True, hooks: Union[RendererHooks, None] = None, routing_callback_inputs: Optional[Dict[str, Union[Input, State]]] = None, - dynamic_loading=True, - preloaded_libraries=None, **obsolete, ): _validate.check_obsolete(obsolete) @@ -460,8 +458,6 @@ def __init__( # pylint: disable=too-many-statements title=title, update_title=update_title, include_pages_meta=include_pages_meta, - dynamic_loading=dynamic_loading, - preloaded_libraries=preloaded_libraries or [], ) self.config.set_read_only( [ @@ -472,8 +468,6 @@ def __init__( # pylint: disable=too-many-statements "serve_locally", "compress", "pages_folder", - "dynamic_loading", - "preloaded_libraries", ], "Read-only: can only be set in the Dash constructor", ) @@ -651,7 +645,6 @@ def _setup_routes(self): self._add_url("_dash-update-component", self.dispatch, ["POST"]) self._add_url("_reload-hash", self.serve_reload_hash) self._add_url("_favicon.ico", self._serve_default_favicon) - self._add_url("_dash-dist", self.serve_dist, methods=["POST"]) self._add_url("", self.index) if jupyter_dash.active: @@ -806,10 +799,7 @@ def serve_reload_hash(self): ) def serve_dist(self): - libraries = [ - ComponentRegistry.namespace_to_package.get(lib, lib) - for lib in flask.request.get_json() - ] + libraries = flask.request.get_json() dists = [] for dist_type in ("_js_dist", "_css_dist"): resources = ComponentRegistry.get_resources(dist_type, libraries) @@ -895,13 +885,7 @@ def _relative_url_path(relative_package_path="", namespace=""): def _generate_css_dist_html(self): external_links = self.config.external_stylesheets - - if self.config.dynamic_loading: - links = self._collect_and_register_resources( - self.css.get_library_css(self.config.preloaded_libraries) - ) - else: - links = self._collect_and_register_resources(self.css.get_all_css()) + links = self._collect_and_register_resources(self.css.get_all_css()) return "\n".join( [ @@ -936,27 +920,20 @@ def _generate_scripts_html(self): self.scripts._resources._filter_resources(deps, dev_bundles=dev) ) + self.config.external_scripts - ) - - if not self.config.dynamic_loading: - srcs += self._collect_and_register_resources( + + self._collect_and_register_resources( self.scripts.get_all_scripts(dev_bundles=dev) - ) - else: - srcs += self._collect_and_register_resources( - self.scripts.get_library_scripts( - self.config.preloaded_libraries, dev_bundles=dev + + self.scripts._resources._filter_resources( + _dash_renderer._js_dist, dev_bundles=dev + ) + + self.scripts._resources._filter_resources( + dcc._js_dist, dev_bundles=dev + ) + + self.scripts._resources._filter_resources( + html._js_dist, dev_bundles=dev + ) + + self.scripts._resources._filter_resources( + dash_table._js_dist, dev_bundles=dev ) - ) - - srcs += self._collect_and_register_resources( - self.scripts._resources._filter_resources( - _dash_renderer._js_dist, dev_bundles=dev - ) - + self.scripts._resources._filter_resources(dcc._js_dist, dev_bundles=dev) - + self.scripts._resources._filter_resources(html._js_dist, dev_bundles=dev) - + self.scripts._resources._filter_resources( - dash_table._js_dist, dev_bundles=dev ) ) diff --git a/tests/integration/renderer/test_libraries.py b/tests/integration/renderer/test_libraries.py deleted file mode 100644 index e7332c99d3..0000000000 --- a/tests/integration/renderer/test_libraries.py +++ /dev/null @@ -1,52 +0,0 @@ -from dash import Dash, html, Input, Output -import dash_test_components as dt -import dash_generator_test_component_standard as dgs - - -def test_rblib001_dynamic_loading(dash_duo): - app = Dash(__name__) - - app.layout = html.Div( - [ - html.Button("Insert", id="insert-btn"), - html.Div(id="output"), - dgs.MyStandardComponent(id="dgs"), - ] - ) - - @app.callback( - Output("output", "children"), - [Input("insert-btn", "n_clicks")], - prevent_initial_call=True, - ) - def update_output(_): - import dash_generator_test_component_nested as dn - - return [ - dt.StyledComponent(value="Styled", id="styled"), - dn.MyNestedComponent(value="nested", id="nested"), - ] - - dash_duo.start_server(app) - - def assert_unloaded(namespace): - assert dash_duo.driver.execute_script( - f"return window['{namespace}'] === undefined" - ) - - def assert_loaded(namespace): - assert dash_duo.driver.execute_script( - f"return window['{namespace}'] !== undefined" - ) - - assert_unloaded(dt.package_name) - assert_unloaded("dash_generator_test_component_nested") - dash_duo.wait_for_element("#dgs") - assert_unloaded(dt.package_name) - assert_loaded(dgs.package_name) - - dash_duo.wait_for_element("#insert-btn").click() - - dash_duo.wait_for_element("#styled") - assert_loaded(dt.package_name) - assert_loaded("dash_generator_test_component_nested") diff --git a/tests/integration/test_generation.py b/tests/integration/test_generation.py index b888c5b2cc..1ed4f7ac78 100644 --- a/tests/integration/test_generation.py +++ b/tests/integration/test_generation.py @@ -56,13 +56,7 @@ def test_gene001_simple_callback(dash_duo): def test_gene002_arbitrary_resources(dash_duo): app = Dash(__name__) - app.layout = Div( - [ - Button("Click", id="btn"), - Div(id="container"), - MyStandardComponent(), - ] - ) + app.layout = Div([Button("Click", id="btn"), Div(id="container")]) @app.callback(Output("container", "children"), [Input("btn", "n_clicks")]) def update_container(n_clicks): From 39aa3b73e3ba51982bb791f296a257dd187ed09c Mon Sep 17 00:00:00 2001 From: philippe Date: Wed, 6 Mar 2024 11:33:38 -0500 Subject: [PATCH 2/4] Update changelog. --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8d9cf35d2..d93d730917 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to `dash` will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/). +## [UNRELEASED] + +## Fixed + +- [#2783](https://github.com/plotly/dash/pull/2783) Remove dynamic loading. + ## [2.16.0] - 2024-03-01 ## Fixed From fb96986a558fb32ac335957cbd3b3d1e126a50e6 Mon Sep 17 00:00:00 2001 From: philippe Date: Wed, 6 Mar 2024 12:02:28 -0500 Subject: [PATCH 3/4] Set lcbc flaky. --- tests/integration/long_callback/test_basic_long_callback016.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/integration/long_callback/test_basic_long_callback016.py b/tests/integration/long_callback/test_basic_long_callback016.py index e822a408e4..ac65d16cf6 100644 --- a/tests/integration/long_callback/test_basic_long_callback016.py +++ b/tests/integration/long_callback/test_basic_long_callback016.py @@ -1,6 +1,7 @@ import sys import pytest +from flaky import flaky from tests.integration.long_callback.utils import setup_long_callback_app @@ -8,6 +9,7 @@ @pytest.mark.skipif( sys.version_info < (3, 9), reason="Python 3.8 long callbacks tests hangs up" ) +@flaky(max_runs=3) def test_lcbc016_multi_page_cancel(dash_duo, manager): with setup_long_callback_app(manager, "app_page_cancel") as app: dash_duo.start_server(app) From 37da46c3f162f29ad3468adfff901d4bb49b271c Mon Sep 17 00:00:00 2001 From: philippe Date: Wed, 6 Mar 2024 12:21:03 -0500 Subject: [PATCH 4/4] Set sp001 flaky. --- tests/integration/clientside/test_clientside_functions.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/integration/clientside/test_clientside_functions.py b/tests/integration/clientside/test_clientside_functions.py index c00062835e..5a955a1564 100644 --- a/tests/integration/clientside/test_clientside_functions.py +++ b/tests/integration/clientside/test_clientside_functions.py @@ -2,7 +2,10 @@ import json from multiprocessing import Value +from flaky import flaky + +@flaky(max_runs=3) def test_sp001_clientside_setprops(dash_duo): call_count = Value("i", 0)