done
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
# pylint: disable=C0413
|
||||
# __plotly_dash is for the "make sure you don't have a dash.py" check
|
||||
# must come before any other imports.
|
||||
__plotly_dash = True
|
||||
from .dependencies import ( # noqa: F401,E402
|
||||
Input, # noqa: F401,E402
|
||||
Output, # noqa: F401,E402,
|
||||
State, # noqa: F401,E402
|
||||
ClientsideFunction, # noqa: F401,E402
|
||||
MATCH, # noqa: F401,E402
|
||||
ALL, # noqa: F401,E402
|
||||
ALLSMALLER, # noqa: F401,E402
|
||||
) # noqa: F401,E402
|
||||
from . import development # noqa: F401,E402
|
||||
from . import exceptions # noqa: F401,E402
|
||||
from . import resources # noqa: F401,E402
|
||||
from . import dcc # noqa: F401,E402
|
||||
from . import html # noqa: F401,E402
|
||||
from . import dash_table # noqa: F401,E402
|
||||
from .version import __version__ # noqa: F401,E402
|
||||
from ._callback_context import callback_context, set_props # noqa: F401,E402
|
||||
from ._callback import callback, clientside_callback # noqa: F401,E402
|
||||
from ._get_app import get_app # noqa: F401,E402
|
||||
from ._get_paths import ( # noqa: F401,E402
|
||||
get_asset_url,
|
||||
get_relative_path,
|
||||
strip_relative_path,
|
||||
)
|
||||
from ._no_update import NoUpdate # noqa: F401,E402
|
||||
from .background_callback import ( # noqa: F401,E402
|
||||
CeleryManager,
|
||||
DiskcacheManager,
|
||||
)
|
||||
from ._utils import stringify_id # noqa: F401,E402
|
||||
|
||||
|
||||
from ._pages import register_page, PAGE_REGISTRY as page_registry # noqa: F401,E402
|
||||
from .dash import ( # noqa: F401,E402
|
||||
Dash,
|
||||
no_update,
|
||||
page_container,
|
||||
)
|
||||
from ._patch import Patch # noqa: F401,E402
|
||||
from ._jupyter import jupyter_dash # noqa: F401,E402
|
||||
|
||||
from ._hooks import hooks # noqa: F401,E402
|
||||
|
||||
ctx = callback_context
|
||||
|
||||
|
||||
def _jupyter_nbextension_paths():
|
||||
return [
|
||||
{
|
||||
"section": "notebook",
|
||||
"src": "nbextension",
|
||||
"dest": "dash",
|
||||
"require": "dash/main",
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
__all__ = [
|
||||
"Input",
|
||||
"Output",
|
||||
"State",
|
||||
"clientside_callback",
|
||||
"ClientsideFunction",
|
||||
"MATCH",
|
||||
"ALLSMALLER",
|
||||
"ALL",
|
||||
"development",
|
||||
"exceptions",
|
||||
"dcc",
|
||||
"html",
|
||||
"dash_table",
|
||||
"__version__",
|
||||
"callback_context",
|
||||
"set_props",
|
||||
"callback",
|
||||
"get_app",
|
||||
"get_asset_url",
|
||||
"get_relative_path",
|
||||
"strip_relative_path",
|
||||
"CeleryManager",
|
||||
"DiskcacheManager",
|
||||
"register_page",
|
||||
"page_registry",
|
||||
"Dash",
|
||||
"no_update",
|
||||
"NoUpdate",
|
||||
"page_container",
|
||||
"Patch",
|
||||
"jupyter_dash",
|
||||
"ctx",
|
||||
"hooks",
|
||||
"stringify_id",
|
||||
]
|
||||
@@ -0,0 +1,850 @@
|
||||
import collections
|
||||
import hashlib
|
||||
from functools import wraps
|
||||
|
||||
from typing import Callable, Optional, Any, List, Tuple, Union
|
||||
|
||||
|
||||
import asyncio
|
||||
import flask
|
||||
|
||||
from .dependencies import (
|
||||
handle_callback_args,
|
||||
handle_grouped_callback_args,
|
||||
Output,
|
||||
ClientsideFunction,
|
||||
Input,
|
||||
)
|
||||
from .development.base_component import ComponentRegistry
|
||||
from .exceptions import (
|
||||
InvalidCallbackReturnValue,
|
||||
PreventUpdate,
|
||||
WildcardInLongCallback,
|
||||
MissingLongCallbackManagerError,
|
||||
BackgroundCallbackError,
|
||||
ImportedInsideCallbackError,
|
||||
)
|
||||
|
||||
from ._grouping import (
|
||||
flatten_grouping,
|
||||
make_grouping_by_index,
|
||||
grouping_len,
|
||||
)
|
||||
from ._utils import (
|
||||
create_callback_id,
|
||||
stringify_id,
|
||||
to_json,
|
||||
coerce_to_list,
|
||||
AttributeDict,
|
||||
clean_property_name,
|
||||
)
|
||||
|
||||
from . import _validate
|
||||
from .background_callback.managers import BaseBackgroundCallbackManager
|
||||
from ._callback_context import context_value
|
||||
from ._no_update import NoUpdate
|
||||
|
||||
|
||||
async def _async_invoke_callback(
|
||||
func, *args, **kwargs
|
||||
): # used to mark the frame for the debugger
|
||||
# Check if the function is a coroutine function
|
||||
if asyncio.iscoroutinefunction(func):
|
||||
return await func(*args, **kwargs) # %% callback invoked %%
|
||||
# If the function is not a coroutine, call it directly
|
||||
return func(*args, **kwargs) # %% callback invoked %%
|
||||
|
||||
|
||||
def _invoke_callback(func, *args, **kwargs): # used to mark the frame for the debugger
|
||||
return func(*args, **kwargs) # %% callback invoked %%
|
||||
|
||||
|
||||
GLOBAL_CALLBACK_LIST = []
|
||||
GLOBAL_CALLBACK_MAP = {}
|
||||
GLOBAL_INLINE_SCRIPTS = []
|
||||
|
||||
|
||||
# pylint: disable=too-many-locals
|
||||
def callback(
|
||||
*_args,
|
||||
background: bool = False,
|
||||
interval: int = 1000,
|
||||
progress: Optional[Union[List[Output], Output]] = None,
|
||||
progress_default: Any = None,
|
||||
running: Optional[List[Tuple[Output, Any, Any]]] = None,
|
||||
cancel: Optional[Union[List[Input], Input]] = None,
|
||||
manager: Optional[BaseBackgroundCallbackManager] = None,
|
||||
cache_args_to_ignore: Optional[list] = None,
|
||||
cache_ignore_triggered=True,
|
||||
on_error: Optional[Callable[[Exception], Any]] = None,
|
||||
**_kwargs,
|
||||
) -> Callable[..., Any]:
|
||||
"""
|
||||
Normally used as a decorator, `@dash.callback` provides a server-side
|
||||
callback relating the values of one or more `Output` items to one or
|
||||
more `Input` items which will trigger the callback when they change,
|
||||
and optionally `State` items which provide additional information but
|
||||
do not trigger the callback directly.
|
||||
|
||||
`@dash.callback` is an alternative to `@app.callback` (where `app = dash.Dash()`)
|
||||
introduced in Dash 2.0.
|
||||
It allows you to register callbacks without defining or importing the `app`
|
||||
object. The call signature is identical and it can be used instead of `app.callback`
|
||||
in all cases.
|
||||
|
||||
The last, optional argument `prevent_initial_call` causes the callback
|
||||
not to fire when its outputs are first added to the page. Defaults to
|
||||
`False` and unlike `app.callback` is not configurable at the app level.
|
||||
|
||||
:Keyword Arguments:
|
||||
:param background:
|
||||
Mark the callback as a background callback to execute in a manager for
|
||||
callbacks that take a long time without locking up the Dash app
|
||||
or timing out.
|
||||
:param manager:
|
||||
A background callback manager instance. Currently, an instance of one of
|
||||
`DiskcacheManager` or `CeleryManager`.
|
||||
Defaults to the `background_callback_manager` instance provided to the
|
||||
`dash.Dash constructor`.
|
||||
- A diskcache manager (`DiskcacheManager`) that runs callback
|
||||
logic in a separate process and stores the results to disk using the
|
||||
diskcache library. This is the easiest backend to use for local
|
||||
development.
|
||||
- A Celery manager (`CeleryManager`) that runs callback logic
|
||||
in a celery worker and returns results to the Dash app through a Celery
|
||||
broker like RabbitMQ or Redis.
|
||||
:param running:
|
||||
A list of 3-element tuples. The first element of each tuple should be
|
||||
an `Output` dependency object referencing a property of a component in
|
||||
the app layout. The second element is the value that the property
|
||||
should be set to while the callback is running, and the third element
|
||||
is the value the property should be set to when the callback completes.
|
||||
:param cancel:
|
||||
A list of `Input` dependency objects that reference a property of a
|
||||
component in the app's layout. When the value of this property changes
|
||||
while a callback is running, the callback is canceled.
|
||||
Note that the value of the property is not significant, any change in
|
||||
value will result in the cancellation of the running job (if any).
|
||||
This parameter only applies to background callbacks (`background=True`).
|
||||
:param progress:
|
||||
An `Output` dependency grouping that references properties of
|
||||
components in the app's layout. When provided, the decorated function
|
||||
will be called with an extra argument as the first argument to the
|
||||
function. This argument, is a function handle that the decorated
|
||||
function should call in order to provide updates to the app on its
|
||||
current progress. This function accepts a single argument, which
|
||||
correspond to the grouping of properties specified in the provided
|
||||
`Output` dependency grouping. This parameter only applies to background
|
||||
callbacks (`background=True`).
|
||||
:param progress_default:
|
||||
A grouping of values that should be assigned to the components
|
||||
specified by the `progress` argument when the callback is not in
|
||||
progress. If `progress_default` is not provided, all the dependency
|
||||
properties specified in `progress` will be set to `None` when the
|
||||
callback is not running. This parameter only applies to background
|
||||
callbacks (`background=True`).
|
||||
:param cache_args_to_ignore:
|
||||
Arguments to ignore when caching is enabled. If callback is configured
|
||||
with keyword arguments (Input/State provided in a dict),
|
||||
this should be a list of argument names as strings. Otherwise,
|
||||
this should be a list of argument indices as integers.
|
||||
This parameter only applies to background callbacks (`background=True`).
|
||||
:param cache_ignore_triggered:
|
||||
Whether to ignore which inputs triggered the callback when creating
|
||||
the cache. This parameter only applies to background callbacks
|
||||
(`background=True`).
|
||||
:param interval:
|
||||
Time to wait between the background callback update requests.
|
||||
:param on_error:
|
||||
Function to call when the callback raises an exception. Receives the
|
||||
exception object as first argument. The callback_context can be used
|
||||
to access the original callback inputs, states and output.
|
||||
"""
|
||||
|
||||
background_spec = None
|
||||
|
||||
config_prevent_initial_callbacks = _kwargs.pop(
|
||||
"config_prevent_initial_callbacks", False
|
||||
)
|
||||
callback_map = _kwargs.pop("callback_map", GLOBAL_CALLBACK_MAP)
|
||||
callback_list = _kwargs.pop("callback_list", GLOBAL_CALLBACK_LIST)
|
||||
|
||||
if background:
|
||||
background_spec: Any = {
|
||||
"interval": interval,
|
||||
}
|
||||
|
||||
if manager:
|
||||
background_spec["manager"] = manager
|
||||
|
||||
if progress:
|
||||
background_spec["progress"] = coerce_to_list(progress)
|
||||
validate_background_inputs(background_spec["progress"])
|
||||
|
||||
if progress_default:
|
||||
background_spec["progressDefault"] = coerce_to_list(progress_default)
|
||||
|
||||
if not len(background_spec["progress"]) == len(
|
||||
background_spec["progressDefault"]
|
||||
):
|
||||
raise Exception(
|
||||
"Progress and progress default needs to be of same length"
|
||||
)
|
||||
|
||||
if cancel:
|
||||
cancel_inputs = coerce_to_list(cancel)
|
||||
validate_background_inputs(cancel_inputs)
|
||||
|
||||
background_spec["cancel"] = [c.to_dict() for c in cancel_inputs]
|
||||
background_spec["cancel_inputs"] = cancel_inputs
|
||||
|
||||
if cache_args_to_ignore:
|
||||
background_spec["cache_args_to_ignore"] = cache_args_to_ignore
|
||||
|
||||
background_spec["cache_ignore_triggered"] = cache_ignore_triggered
|
||||
|
||||
return register_callback(
|
||||
callback_list,
|
||||
callback_map,
|
||||
config_prevent_initial_callbacks,
|
||||
*_args,
|
||||
**_kwargs,
|
||||
background=background_spec,
|
||||
manager=manager,
|
||||
running=running,
|
||||
on_error=on_error,
|
||||
)
|
||||
|
||||
|
||||
def validate_background_inputs(deps):
|
||||
for dep in deps:
|
||||
if dep.has_wildcard():
|
||||
raise WildcardInLongCallback(
|
||||
f"""
|
||||
background callbacks does not support dependencies with
|
||||
pattern-matching ids
|
||||
Received: {repr(dep)}\n"""
|
||||
)
|
||||
|
||||
|
||||
ClientsideFuncType = Union[str, ClientsideFunction]
|
||||
|
||||
|
||||
def clientside_callback(clientside_function: ClientsideFuncType, *args, **kwargs):
|
||||
return register_clientside_callback(
|
||||
GLOBAL_CALLBACK_LIST,
|
||||
GLOBAL_CALLBACK_MAP,
|
||||
False,
|
||||
GLOBAL_INLINE_SCRIPTS,
|
||||
clientside_function,
|
||||
*args,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
def insert_callback(
|
||||
callback_list,
|
||||
callback_map,
|
||||
config_prevent_initial_callbacks,
|
||||
output,
|
||||
outputs_indices,
|
||||
inputs,
|
||||
state,
|
||||
inputs_state_indices,
|
||||
prevent_initial_call,
|
||||
background=None,
|
||||
manager=None,
|
||||
running=None,
|
||||
dynamic_creator: Optional[bool] = False,
|
||||
no_output=False,
|
||||
):
|
||||
if prevent_initial_call is None:
|
||||
prevent_initial_call = config_prevent_initial_callbacks
|
||||
|
||||
_validate.validate_duplicate_output(
|
||||
output, prevent_initial_call, config_prevent_initial_callbacks
|
||||
)
|
||||
|
||||
callback_id = create_callback_id(output, inputs, no_output)
|
||||
callback_spec = {
|
||||
"output": callback_id,
|
||||
"inputs": [c.to_dict() for c in inputs],
|
||||
"state": [c.to_dict() for c in state],
|
||||
"clientside_function": None,
|
||||
# prevent_initial_call can be a string "initial_duplicates"
|
||||
# which should not prevent the initial call.
|
||||
"prevent_initial_call": prevent_initial_call is True,
|
||||
"background": background
|
||||
and {
|
||||
"interval": background["interval"],
|
||||
},
|
||||
"dynamic_creator": dynamic_creator,
|
||||
"no_output": no_output,
|
||||
}
|
||||
if running:
|
||||
callback_spec["running"] = running
|
||||
|
||||
callback_map[callback_id] = {
|
||||
"inputs": callback_spec["inputs"],
|
||||
"state": callback_spec["state"],
|
||||
"outputs_indices": outputs_indices,
|
||||
"inputs_state_indices": inputs_state_indices,
|
||||
"background": background,
|
||||
"output": output,
|
||||
"raw_inputs": inputs,
|
||||
"manager": manager,
|
||||
"allow_dynamic_callbacks": dynamic_creator,
|
||||
"no_output": no_output,
|
||||
}
|
||||
callback_list.append(callback_spec)
|
||||
|
||||
return callback_id
|
||||
|
||||
|
||||
def _set_side_update(ctx, response) -> bool:
|
||||
side_update = dict(ctx.updated_props)
|
||||
if len(side_update) > 0:
|
||||
response["sideUpdate"] = side_update
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _initialize_context(args, kwargs, inputs_state_indices, has_output, insert_output):
|
||||
"""Initialize context and validate output specifications."""
|
||||
app = kwargs.pop("app", None)
|
||||
output_spec = kwargs.pop("outputs_list")
|
||||
callback_ctx = kwargs.pop("callback_context", AttributeDict({"updated_props": {}}))
|
||||
context_value.set(callback_ctx)
|
||||
original_packages = set(ComponentRegistry.registry)
|
||||
|
||||
if has_output:
|
||||
_validate.validate_output_spec(insert_output, output_spec, Output)
|
||||
|
||||
func_args, func_kwargs = _validate.validate_and_group_input_args(
|
||||
args, inputs_state_indices
|
||||
)
|
||||
return (
|
||||
output_spec,
|
||||
callback_ctx,
|
||||
func_args,
|
||||
func_kwargs,
|
||||
app,
|
||||
original_packages,
|
||||
False,
|
||||
)
|
||||
|
||||
|
||||
def _get_callback_manager(
|
||||
kwargs: dict, background: dict
|
||||
) -> Union[BaseBackgroundCallbackManager, None]:
|
||||
"""Set up the background callback and manage jobs."""
|
||||
callback_manager = background.get(
|
||||
"manager", kwargs.get("background_callback_manager", None)
|
||||
)
|
||||
if background is not None:
|
||||
if not callback_manager:
|
||||
raise MissingLongCallbackManagerError(
|
||||
"Running `background` callbacks requires a manager to be installed.\n"
|
||||
"Available managers:\n"
|
||||
"- Diskcache (`pip install dash[diskcache]`) to run callbacks in a separate Process"
|
||||
" and store results on the local filesystem.\n"
|
||||
"- Celery (`pip install dash[celery]`) to run callbacks in a celery worker"
|
||||
" and store results on redis.\n"
|
||||
)
|
||||
|
||||
old_job = flask.request.args.getlist("oldJob")
|
||||
|
||||
if old_job:
|
||||
for job in old_job:
|
||||
callback_manager.terminate_job(job)
|
||||
|
||||
return callback_manager
|
||||
|
||||
|
||||
def _setup_background_callback(
|
||||
kwargs, background, background_key, func, func_args, func_kwargs, callback_ctx
|
||||
):
|
||||
"""Set up the background callback and manage jobs."""
|
||||
callback_manager = _get_callback_manager(kwargs, background)
|
||||
|
||||
progress_outputs = background.get("progress")
|
||||
|
||||
cache_ignore_triggered = background.get("cache_ignore_triggered", True)
|
||||
|
||||
cache_key = callback_manager.build_cache_key(
|
||||
func,
|
||||
# Inputs provided as dict is kwargs.
|
||||
func_args if func_args else func_kwargs,
|
||||
background.get("cache_args_to_ignore", []),
|
||||
None if cache_ignore_triggered else callback_ctx.get("triggered_inputs", []),
|
||||
)
|
||||
|
||||
job_fn = callback_manager.func_registry.get(background_key)
|
||||
|
||||
ctx_value = AttributeDict(**context_value.get())
|
||||
ctx_value.ignore_register_page = True
|
||||
ctx_value.pop("background_callback_manager")
|
||||
ctx_value.pop("dash_response")
|
||||
|
||||
job = callback_manager.call_job_fn(
|
||||
cache_key,
|
||||
job_fn,
|
||||
func_args if func_args else func_kwargs,
|
||||
ctx_value,
|
||||
)
|
||||
|
||||
data = {
|
||||
"cacheKey": cache_key,
|
||||
"job": job,
|
||||
}
|
||||
|
||||
cancel = background.get("cancel")
|
||||
if cancel:
|
||||
data["cancel"] = cancel
|
||||
|
||||
progress_default = background.get("progressDefault")
|
||||
if progress_default:
|
||||
data["progressDefault"] = {
|
||||
str(o): x for o, x in zip(progress_outputs, progress_default)
|
||||
}
|
||||
return to_json(data)
|
||||
|
||||
|
||||
def _progress_background_callback(response, callback_manager, background):
|
||||
progress_outputs = background.get("progress")
|
||||
cache_key = flask.request.args.get("cacheKey")
|
||||
|
||||
if progress_outputs:
|
||||
# Get the progress before the result as it would be erased after the results.
|
||||
progress = callback_manager.get_progress(cache_key)
|
||||
if progress:
|
||||
response["progress"] = {
|
||||
str(x): progress[i] for i, x in enumerate(progress_outputs)
|
||||
}
|
||||
|
||||
|
||||
def _update_background_callback(
|
||||
error_handler, callback_ctx, response, kwargs, background, multi
|
||||
):
|
||||
"""Set up the background callback and manage jobs."""
|
||||
callback_manager = _get_callback_manager(kwargs, background)
|
||||
|
||||
cache_key = flask.request.args.get("cacheKey")
|
||||
job_id = flask.request.args.get("job")
|
||||
|
||||
_progress_background_callback(response, callback_manager, background)
|
||||
|
||||
output_value = callback_manager.get_result(cache_key, job_id)
|
||||
|
||||
return _handle_rest_background_callback(
|
||||
output_value, callback_manager, response, error_handler, callback_ctx, multi
|
||||
)
|
||||
|
||||
|
||||
def _handle_rest_background_callback(
|
||||
output_value,
|
||||
callback_manager,
|
||||
response,
|
||||
error_handler,
|
||||
callback_ctx,
|
||||
multi,
|
||||
has_update=False,
|
||||
):
|
||||
cache_key = flask.request.args.get("cacheKey")
|
||||
job_id = flask.request.args.get("job")
|
||||
# Must get job_running after get_result since get_results terminates it.
|
||||
job_running = callback_manager.job_running(job_id)
|
||||
if not job_running and output_value is callback_manager.UNDEFINED:
|
||||
# Job canceled -> no output to close the loop.
|
||||
output_value = NoUpdate()
|
||||
|
||||
elif isinstance(output_value, dict) and "background_callback_error" in output_value:
|
||||
error = output_value.get("background_callback_error", {})
|
||||
exc = BackgroundCallbackError(
|
||||
f"An error occurred inside a background callback: {error['msg']}\n{error['tb']}"
|
||||
)
|
||||
if error_handler:
|
||||
output_value = error_handler(exc)
|
||||
|
||||
if output_value is None:
|
||||
output_value = NoUpdate()
|
||||
# set_props from the error handler uses the original ctx
|
||||
# instead of manager.get_updated_props since it runs in the
|
||||
# request process.
|
||||
has_update = (
|
||||
_set_side_update(callback_ctx, response) or output_value is not None
|
||||
)
|
||||
else:
|
||||
raise exc
|
||||
|
||||
if job_running and output_value is not callback_manager.UNDEFINED:
|
||||
# cached results.
|
||||
callback_manager.terminate_job(job_id)
|
||||
|
||||
if multi and isinstance(output_value, (list, tuple)):
|
||||
output_value = [
|
||||
NoUpdate() if NoUpdate.is_no_update(r) else r for r in output_value
|
||||
]
|
||||
updated_props = callback_manager.get_updated_props(cache_key)
|
||||
if len(updated_props) > 0:
|
||||
response["sideUpdate"] = updated_props
|
||||
has_update = True
|
||||
|
||||
if output_value is callback_manager.UNDEFINED:
|
||||
return to_json(response), has_update, True
|
||||
return output_value, has_update, False
|
||||
|
||||
|
||||
# pylint: disable=too-many-branches
|
||||
def _prepare_response(
|
||||
output_value,
|
||||
output_spec,
|
||||
multi,
|
||||
response,
|
||||
callback_ctx,
|
||||
app,
|
||||
original_packages,
|
||||
background,
|
||||
has_update,
|
||||
has_output,
|
||||
output,
|
||||
callback_id,
|
||||
allow_dynamic_callbacks,
|
||||
):
|
||||
"""Prepare the response object based on the callback output."""
|
||||
component_ids = collections.defaultdict(dict)
|
||||
|
||||
if has_output:
|
||||
if not multi:
|
||||
output_value, output_spec = [output_value], [output_spec]
|
||||
flat_output_values = output_value
|
||||
else:
|
||||
if isinstance(output_value, (list, tuple)):
|
||||
# For multi-output, allow top-level collection to be
|
||||
# list or tuple
|
||||
output_value = list(output_value)
|
||||
if NoUpdate.is_no_update(output_value):
|
||||
flat_output_values = [output_value]
|
||||
else:
|
||||
# Flatten grouping and validate grouping structure
|
||||
flat_output_values = flatten_grouping(output_value, output)
|
||||
|
||||
if not NoUpdate.is_no_update(output_value):
|
||||
_validate.validate_multi_return(
|
||||
output_spec, flat_output_values, callback_id
|
||||
)
|
||||
|
||||
for val, spec in zip(flat_output_values, output_spec):
|
||||
if NoUpdate.is_no_update(val):
|
||||
continue
|
||||
for vali, speci in (
|
||||
zip(val, spec) if isinstance(spec, list) else [[val, spec]] # type: ignore[reportArgumentType]
|
||||
):
|
||||
if not NoUpdate.is_no_update(vali):
|
||||
has_update = True
|
||||
id_str = stringify_id(speci["id"])
|
||||
prop = clean_property_name(speci["property"])
|
||||
component_ids[id_str][prop] = vali
|
||||
|
||||
else:
|
||||
if output_value is not None:
|
||||
raise InvalidCallbackReturnValue(
|
||||
f"No-output callback received return value: {output_value}"
|
||||
)
|
||||
|
||||
if not background:
|
||||
has_update = _set_side_update(callback_ctx, response) or has_output
|
||||
|
||||
if not has_update:
|
||||
raise PreventUpdate
|
||||
|
||||
if len(ComponentRegistry.registry) != len(original_packages):
|
||||
diff_packages = list(
|
||||
set(ComponentRegistry.registry).difference(original_packages)
|
||||
)
|
||||
if not allow_dynamic_callbacks:
|
||||
raise ImportedInsideCallbackError(
|
||||
f"Component librar{'y' if len(diff_packages) == 1 else 'ies'} was imported during callback.\n"
|
||||
"You can set `_allow_dynamic_callbacks` to allow for development purpose only."
|
||||
)
|
||||
dist = app.get_dist(diff_packages)
|
||||
response["dist"] = dist
|
||||
return response.update({"response": component_ids})
|
||||
|
||||
|
||||
# pylint: disable=too-many-branches,too-many-statements
|
||||
def register_callback(
|
||||
callback_list, callback_map, config_prevent_initial_callbacks, *_args, **_kwargs
|
||||
):
|
||||
(
|
||||
output,
|
||||
flat_inputs,
|
||||
flat_state,
|
||||
inputs_state_indices,
|
||||
prevent_initial_call,
|
||||
) = handle_grouped_callback_args(_args, _kwargs)
|
||||
if isinstance(output, Output):
|
||||
# Insert callback with scalar (non-multi) Output
|
||||
insert_output = output
|
||||
multi = False
|
||||
has_output = True
|
||||
else:
|
||||
# Insert callback as multi Output
|
||||
insert_output = flatten_grouping(output)
|
||||
multi = True
|
||||
has_output = len(output) > 0
|
||||
|
||||
background = _kwargs.get("background")
|
||||
manager = _kwargs.get("manager")
|
||||
running = _kwargs.get("running")
|
||||
on_error = _kwargs.get("on_error")
|
||||
if running is not None:
|
||||
if not isinstance(running[0], (list, tuple)):
|
||||
running = [running]
|
||||
running = {
|
||||
"running": {str(r[0]): r[1] for r in running},
|
||||
"runningOff": {str(r[0]): r[2] for r in running},
|
||||
}
|
||||
allow_dynamic_callbacks = _kwargs.get("_allow_dynamic_callbacks")
|
||||
|
||||
output_indices = make_grouping_by_index(output, list(range(grouping_len(output))))
|
||||
callback_id = insert_callback(
|
||||
callback_list,
|
||||
callback_map,
|
||||
config_prevent_initial_callbacks,
|
||||
insert_output,
|
||||
output_indices,
|
||||
flat_inputs,
|
||||
flat_state,
|
||||
inputs_state_indices,
|
||||
prevent_initial_call,
|
||||
background=background,
|
||||
manager=manager,
|
||||
dynamic_creator=allow_dynamic_callbacks,
|
||||
running=running,
|
||||
no_output=not has_output,
|
||||
)
|
||||
|
||||
# pylint: disable=too-many-locals
|
||||
def wrap_func(func):
|
||||
if background is None:
|
||||
background_key = None
|
||||
else:
|
||||
background_key = BaseBackgroundCallbackManager.register_func(
|
||||
func,
|
||||
background.get("progress") is not None,
|
||||
callback_id,
|
||||
)
|
||||
|
||||
@wraps(func)
|
||||
def add_context(*args, **kwargs):
|
||||
"""Handles synchronous callbacks with context management."""
|
||||
error_handler = on_error or kwargs.pop("app_on_error", None)
|
||||
|
||||
(
|
||||
output_spec,
|
||||
callback_ctx,
|
||||
func_args,
|
||||
func_kwargs,
|
||||
app,
|
||||
original_packages,
|
||||
has_update,
|
||||
) = _initialize_context(
|
||||
args, kwargs, inputs_state_indices, has_output, insert_output
|
||||
)
|
||||
|
||||
response: dict = {"multi": True}
|
||||
|
||||
jsonResponse = None
|
||||
try:
|
||||
if background is not None:
|
||||
if not flask.request.args.get("cacheKey"):
|
||||
return _setup_background_callback(
|
||||
kwargs,
|
||||
background,
|
||||
background_key,
|
||||
func,
|
||||
func_args,
|
||||
func_kwargs,
|
||||
callback_ctx,
|
||||
)
|
||||
|
||||
output_value, has_update, skip = _update_background_callback(
|
||||
error_handler, callback_ctx, response, kwargs, background, multi
|
||||
)
|
||||
if skip:
|
||||
return output_value
|
||||
else:
|
||||
output_value = _invoke_callback(func, *func_args, **func_kwargs) # type: ignore[reportArgumentType]
|
||||
except PreventUpdate:
|
||||
raise
|
||||
except Exception as err: # pylint: disable=broad-exception-caught
|
||||
if error_handler:
|
||||
output_value = error_handler(err)
|
||||
if output_value is None and output_spec:
|
||||
output_value = NoUpdate()
|
||||
else:
|
||||
raise err
|
||||
|
||||
_prepare_response(
|
||||
output_value,
|
||||
output_spec,
|
||||
multi,
|
||||
response,
|
||||
callback_ctx,
|
||||
app,
|
||||
original_packages,
|
||||
background,
|
||||
has_update,
|
||||
has_output,
|
||||
output,
|
||||
callback_id,
|
||||
allow_dynamic_callbacks,
|
||||
)
|
||||
try:
|
||||
jsonResponse = to_json(response)
|
||||
except TypeError:
|
||||
_validate.fail_callback_output(output_value, output)
|
||||
|
||||
return jsonResponse
|
||||
|
||||
@wraps(func)
|
||||
async def async_add_context(*args, **kwargs):
|
||||
"""Handles async callbacks with context management."""
|
||||
error_handler = on_error or kwargs.pop("app_on_error", None)
|
||||
|
||||
(
|
||||
output_spec,
|
||||
callback_ctx,
|
||||
func_args,
|
||||
func_kwargs,
|
||||
app,
|
||||
original_packages,
|
||||
has_update,
|
||||
) = _initialize_context(
|
||||
args, kwargs, inputs_state_indices, has_output, insert_output
|
||||
)
|
||||
|
||||
response: dict = {"multi": True}
|
||||
|
||||
try:
|
||||
if background is not None:
|
||||
if not flask.request.args.get("cacheKey"):
|
||||
return _setup_background_callback(
|
||||
kwargs,
|
||||
background,
|
||||
background_key,
|
||||
func,
|
||||
func_args,
|
||||
func_kwargs,
|
||||
callback_ctx,
|
||||
)
|
||||
output_value, has_update, skip = _update_background_callback(
|
||||
error_handler, callback_ctx, response, kwargs, background, multi
|
||||
)
|
||||
if skip:
|
||||
return output_value
|
||||
else:
|
||||
output_value = await _async_invoke_callback(
|
||||
func, *func_args, **func_kwargs
|
||||
)
|
||||
except PreventUpdate:
|
||||
raise
|
||||
except Exception as err: # pylint: disable=broad-exception-caught
|
||||
if error_handler:
|
||||
output_value = error_handler(err)
|
||||
if output_value is None and output_spec:
|
||||
output_value = NoUpdate()
|
||||
else:
|
||||
raise err
|
||||
|
||||
_prepare_response(
|
||||
output_value,
|
||||
output_spec,
|
||||
multi,
|
||||
response,
|
||||
callback_ctx,
|
||||
app,
|
||||
original_packages,
|
||||
background,
|
||||
has_update,
|
||||
has_output,
|
||||
output,
|
||||
callback_id,
|
||||
allow_dynamic_callbacks,
|
||||
)
|
||||
try:
|
||||
jsonResponse = to_json(response)
|
||||
except TypeError:
|
||||
_validate.fail_callback_output(output_value, output)
|
||||
|
||||
return jsonResponse
|
||||
|
||||
if asyncio.iscoroutinefunction(func):
|
||||
callback_map[callback_id]["callback"] = async_add_context
|
||||
else:
|
||||
callback_map[callback_id]["callback"] = add_context
|
||||
|
||||
return func
|
||||
|
||||
return wrap_func
|
||||
|
||||
|
||||
_inline_clientside_template = """
|
||||
(function() {{
|
||||
var clientside = window.dash_clientside = window.dash_clientside || {{}};
|
||||
var ns = clientside["{namespace}"] = clientside["{namespace}"] || {{}};
|
||||
ns["{function_name}"] = {clientside_function};
|
||||
}})();
|
||||
"""
|
||||
|
||||
|
||||
def register_clientside_callback(
|
||||
callback_list,
|
||||
callback_map,
|
||||
config_prevent_initial_callbacks,
|
||||
inline_scripts,
|
||||
clientside_function: ClientsideFuncType,
|
||||
*args,
|
||||
**kwargs,
|
||||
):
|
||||
output, inputs, state, prevent_initial_call = handle_callback_args(args, kwargs)
|
||||
no_output = isinstance(output, (list,)) and len(output) == 0
|
||||
insert_callback(
|
||||
callback_list,
|
||||
callback_map,
|
||||
config_prevent_initial_callbacks,
|
||||
output,
|
||||
None,
|
||||
inputs,
|
||||
state,
|
||||
None,
|
||||
prevent_initial_call,
|
||||
no_output=no_output,
|
||||
)
|
||||
|
||||
# If JS source is explicitly given, create a namespace and function
|
||||
# name, then inject the code.
|
||||
if isinstance(clientside_function, str):
|
||||
namespace = "_dashprivate_clientside_funcs"
|
||||
# Create a hash from the function, it will be the same always
|
||||
function_name = hashlib.sha256(clientside_function.encode("utf-8")).hexdigest()
|
||||
|
||||
inline_scripts.append(
|
||||
_inline_clientside_template.format(
|
||||
namespace=namespace,
|
||||
function_name=function_name,
|
||||
clientside_function=clientside_function,
|
||||
)
|
||||
)
|
||||
|
||||
# Callback is stored in an external asset.
|
||||
else:
|
||||
namespace = clientside_function.namespace
|
||||
function_name = clientside_function.function_name
|
||||
|
||||
callback_list[-1]["clientside_function"] = {
|
||||
"namespace": namespace,
|
||||
"function_name": function_name,
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
import functools
|
||||
import warnings
|
||||
import json
|
||||
import contextvars
|
||||
import typing
|
||||
|
||||
import flask
|
||||
|
||||
from . import exceptions
|
||||
from ._utils import AttributeDict, stringify_id
|
||||
|
||||
|
||||
context_value = contextvars.ContextVar("callback_context")
|
||||
context_value.set({})
|
||||
|
||||
|
||||
def has_context(func):
|
||||
@functools.wraps(func)
|
||||
def assert_context(*args, **kwargs):
|
||||
if not context_value.get():
|
||||
raise exceptions.MissingCallbackContextException(
|
||||
f"dash.callback_context.{getattr(func, '__name__')} is only available from a callback!"
|
||||
)
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return assert_context
|
||||
|
||||
|
||||
def _get_context_value():
|
||||
return context_value.get()
|
||||
|
||||
|
||||
def _get_from_context(key, default):
|
||||
return getattr(_get_context_value(), key, default)
|
||||
|
||||
|
||||
class FalsyList(list):
|
||||
def __bool__(self):
|
||||
# for Python 3
|
||||
return False
|
||||
|
||||
def __nonzero__(self):
|
||||
# for Python 2
|
||||
return False
|
||||
|
||||
|
||||
falsy_triggered = FalsyList([{"prop_id": ".", "value": None}])
|
||||
|
||||
|
||||
# pylint: disable=no-init
|
||||
class CallbackContext:
|
||||
@property
|
||||
@has_context
|
||||
def inputs(self):
|
||||
return getattr(_get_context_value(), "input_values", {})
|
||||
|
||||
@property
|
||||
@has_context
|
||||
def states(self):
|
||||
return getattr(_get_context_value(), "state_values", {})
|
||||
|
||||
@property
|
||||
@has_context
|
||||
def triggered(self):
|
||||
"""
|
||||
Returns a list of all the Input props that changed and caused the callback to execute. It is empty when the
|
||||
callback is called on initial load, unless an Input prop got its value from another initial callback.
|
||||
Callbacks triggered by user actions typically have one item in triggered, unless the same action changes
|
||||
two props at once or the callback has several Input props that are all modified by another callback based on
|
||||
a single user action.
|
||||
|
||||
Example: To get the id of the component that triggered the callback:
|
||||
`component_id = ctx.triggered[0]['prop_id'].split('.')[0]`
|
||||
|
||||
Example: To detect initial call, empty triggered is not really empty, it's falsy so that you can use:
|
||||
`if ctx.triggered:`
|
||||
"""
|
||||
# For backward compatibility: previously `triggered` always had a
|
||||
# value - to avoid breaking existing apps, add a dummy item but
|
||||
# make the list still look falsy. So `if ctx.triggered` will make it
|
||||
# look empty, but you can still do `triggered[0]["prop_id"].split(".")`
|
||||
return getattr(_get_context_value(), "triggered_inputs", []) or falsy_triggered
|
||||
|
||||
@property
|
||||
@has_context
|
||||
def triggered_prop_ids(self):
|
||||
"""
|
||||
Returns a dictionary of all the Input props that changed and caused the callback to execute. It is empty when
|
||||
the callback is called on initial load, unless an Input prop got its value from another initial callback.
|
||||
Callbacks triggered by user actions typically have one item in triggered, unless the same action changes
|
||||
two props at once or the callback has several Input props that are all modified by another callback based
|
||||
on a single user action.
|
||||
|
||||
triggered_prop_ids (dict):
|
||||
- keys (str) : the triggered "prop_id" composed of "component_id.component_property"
|
||||
- values (str or dict): the id of the component that triggered the callback. Will be the dict id for pattern matching callbacks
|
||||
|
||||
Example - regular callback
|
||||
{"btn-1.n_clicks": "btn-1"}
|
||||
|
||||
Example - pattern matching callbacks:
|
||||
{'{"index":0,"type":"filter-dropdown"}.value': {"index":0,"type":"filter-dropdown"}}
|
||||
|
||||
Example usage:
|
||||
`if "btn-1.n_clicks" in ctx.triggered_prop_ids:
|
||||
do_something()`
|
||||
"""
|
||||
triggered = getattr(_get_context_value(), "triggered_inputs", [])
|
||||
ids = AttributeDict({})
|
||||
for item in triggered:
|
||||
component_id, _, _ = item["prop_id"].rpartition(".")
|
||||
ids[item["prop_id"]] = component_id
|
||||
if component_id.startswith("{"):
|
||||
ids[item["prop_id"]] = AttributeDict(json.loads(component_id))
|
||||
return ids
|
||||
|
||||
@property
|
||||
@has_context
|
||||
def triggered_id(self):
|
||||
"""
|
||||
Returns the component id (str or dict) of the Input component that triggered the callback.
|
||||
|
||||
Note - use `triggered_prop_ids` if you need both the component id and the prop that triggered the callback or if
|
||||
multiple Inputs triggered the callback.
|
||||
|
||||
Example usage:
|
||||
`if "btn-1" == ctx.triggered_id:
|
||||
do_something()`
|
||||
|
||||
"""
|
||||
component_id = None
|
||||
if self.triggered:
|
||||
prop_id = self.triggered_prop_ids.first()
|
||||
component_id = self.triggered_prop_ids[prop_id]
|
||||
return component_id
|
||||
|
||||
@property
|
||||
@has_context
|
||||
def args_grouping(self):
|
||||
"""
|
||||
args_grouping is a dict of the inputs used with flexible callback signatures. The keys are the variable names
|
||||
and the values are dictionaries containing:
|
||||
- “id”: (string or dict) the component id. If it’s a pattern matching id, it will be a dict.
|
||||
- “id_str”: (str) for pattern matching ids, it’s the stringified dict id with no white spaces.
|
||||
- “property”: (str) The component property used in the callback.
|
||||
- “value”: the value of the component property at the time the callback was fired.
|
||||
- “triggered”: (bool)Whether this input triggered the callback.
|
||||
|
||||
Example usage:
|
||||
@app.callback(
|
||||
Output("container", "children"),
|
||||
inputs=dict(btn1=Input("btn-1", "n_clicks"), btn2=Input("btn-2", "n_clicks")),
|
||||
)
|
||||
def display(btn1, btn2):
|
||||
c = ctx.args_grouping
|
||||
if c.btn1.triggered:
|
||||
return f"Button 1 clicked {btn1} times"
|
||||
elif c.btn2.triggered:
|
||||
return f"Button 2 clicked {btn2} times"
|
||||
else:
|
||||
return "No clicks yet"
|
||||
|
||||
"""
|
||||
return getattr(_get_context_value(), "args_grouping", [])
|
||||
|
||||
@property
|
||||
@has_context
|
||||
def outputs_grouping(self):
|
||||
return getattr(_get_context_value(), "outputs_grouping", [])
|
||||
|
||||
@property
|
||||
@has_context
|
||||
def outputs_list(self):
|
||||
if self.using_outputs_grouping:
|
||||
warnings.warn(
|
||||
"outputs_list is deprecated, use outputs_grouping instead",
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
return getattr(_get_context_value(), "outputs_list", [])
|
||||
|
||||
@property
|
||||
@has_context
|
||||
def inputs_list(self):
|
||||
if self.using_args_grouping:
|
||||
warnings.warn(
|
||||
"inputs_list is deprecated, use args_grouping instead",
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
return getattr(_get_context_value(), "inputs_list", [])
|
||||
|
||||
@property
|
||||
@has_context
|
||||
def states_list(self):
|
||||
if self.using_args_grouping:
|
||||
warnings.warn(
|
||||
"states_list is deprecated, use args_grouping instead",
|
||||
DeprecationWarning,
|
||||
)
|
||||
return getattr(_get_context_value(), "states_list", [])
|
||||
|
||||
@property
|
||||
@has_context
|
||||
def response(self):
|
||||
return getattr(_get_context_value(), "dash_response")
|
||||
|
||||
@staticmethod
|
||||
@has_context
|
||||
def record_timing(name, duration, description=None):
|
||||
"""Records timing information for a server resource.
|
||||
|
||||
:param name: The name of the resource.
|
||||
:type name: string
|
||||
|
||||
:param duration: The time in seconds to report. Internally, this
|
||||
is rounded to the nearest millisecond.
|
||||
:type duration: float
|
||||
|
||||
:param description: A description of the resource.
|
||||
:type description: string or None
|
||||
"""
|
||||
timing_information = getattr(flask.g, "timing_information", {})
|
||||
|
||||
if name in timing_information:
|
||||
raise KeyError(f'Duplicate resource name "{name}" found.')
|
||||
|
||||
timing_information[name] = {"dur": round(duration * 1000), "desc": description}
|
||||
|
||||
setattr(flask.g, "timing_information", timing_information)
|
||||
|
||||
@property
|
||||
@has_context
|
||||
def using_args_grouping(self):
|
||||
"""
|
||||
Return True if this callback is using dictionary or nested groupings for
|
||||
Input/State dependencies, or if Input and State dependencies are interleaved
|
||||
"""
|
||||
return getattr(_get_context_value(), "using_args_grouping", [])
|
||||
|
||||
@property
|
||||
@has_context
|
||||
def using_outputs_grouping(self):
|
||||
"""
|
||||
Return True if this callback is using dictionary or nested groupings for
|
||||
Output dependencies.
|
||||
"""
|
||||
return getattr(_get_context_value(), "using_outputs_grouping", [])
|
||||
|
||||
@property
|
||||
@has_context
|
||||
def timing_information(self):
|
||||
return getattr(flask.g, "timing_information", {})
|
||||
|
||||
@has_context
|
||||
def set_props(self, component_id: typing.Union[str, dict], props: dict):
|
||||
ctx_value = _get_context_value()
|
||||
_id = stringify_id(component_id)
|
||||
existing = ctx_value.updated_props.get(_id)
|
||||
if existing is not None:
|
||||
ctx_value.updated_props[_id] = {**existing, **props}
|
||||
else:
|
||||
ctx_value.updated_props[_id] = props
|
||||
|
||||
@property
|
||||
@has_context
|
||||
def cookies(self):
|
||||
"""
|
||||
Get the cookies for the current callback.
|
||||
Works with background callbacks.
|
||||
"""
|
||||
return _get_from_context("cookies", {})
|
||||
|
||||
@property
|
||||
@has_context
|
||||
def headers(self):
|
||||
"""
|
||||
Get the original headers for the current callback.
|
||||
Works with background callbacks.
|
||||
"""
|
||||
return _get_from_context("headers", {})
|
||||
|
||||
@property
|
||||
@has_context
|
||||
def path(self):
|
||||
"""
|
||||
Path of the callback request with the query parameters.
|
||||
"""
|
||||
return _get_from_context("path", "")
|
||||
|
||||
@property
|
||||
@has_context
|
||||
def remote(self):
|
||||
"""
|
||||
Remote addr of the callback request.
|
||||
"""
|
||||
return _get_from_context("remote", "")
|
||||
|
||||
@property
|
||||
@has_context
|
||||
def origin(self):
|
||||
"""
|
||||
Origin of the callback request.
|
||||
"""
|
||||
return _get_from_context("origin", "")
|
||||
|
||||
@property
|
||||
@has_context
|
||||
def custom_data(self):
|
||||
"""
|
||||
Custom data set by hooks.custom_data.
|
||||
"""
|
||||
return _get_from_context("custom_data", {})
|
||||
|
||||
|
||||
callback_context = CallbackContext()
|
||||
|
||||
|
||||
def set_props(component_id: typing.Union[str, dict], props: dict):
|
||||
"""
|
||||
Set the props for a component not included in the callback outputs.
|
||||
"""
|
||||
callback_context.set_props(component_id, props)
|
||||
@@ -0,0 +1,138 @@
|
||||
import os
|
||||
import flask
|
||||
|
||||
# noinspection PyCompatibility
|
||||
from . import exceptions
|
||||
from ._utils import AttributeDict
|
||||
|
||||
|
||||
def load_dash_env_vars():
|
||||
return AttributeDict(
|
||||
{
|
||||
var: os.getenv(var, os.getenv(var.lower()))
|
||||
for var in (
|
||||
"DASH_APP_NAME",
|
||||
"DASH_URL_BASE_PATHNAME",
|
||||
"DASH_ROUTES_PATHNAME_PREFIX",
|
||||
"DASH_REQUESTS_PATHNAME_PREFIX",
|
||||
"DASH_SUPPRESS_CALLBACK_EXCEPTIONS",
|
||||
"DASH_ASSETS_EXTERNAL_PATH",
|
||||
"DASH_INCLUDE_ASSETS_FILES",
|
||||
"DASH_COMPONENTS_CACHE_MAX_AGE",
|
||||
"DASH_INCLUDE_ASSETS_FILES",
|
||||
"DASH_SERVE_DEV_BUNDLES",
|
||||
"DASH_DEBUG",
|
||||
"DASH_UI",
|
||||
"DASH_PROPS_CHECK",
|
||||
"DASH_HOT_RELOAD",
|
||||
"DASH_HOT_RELOAD_INTERVAL",
|
||||
"DASH_HOT_RELOAD_WATCH_INTERVAL",
|
||||
"DASH_HOT_RELOAD_MAX_RETRY",
|
||||
"DASH_SILENCE_ROUTES_LOGGING",
|
||||
"DASH_DISABLE_VERSION_CHECK",
|
||||
"DASH_PRUNE_ERRORS",
|
||||
"DASH_COMPRESS",
|
||||
"HOST",
|
||||
"PORT",
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
DASH_ENV_VARS = load_dash_env_vars() # used in tests
|
||||
|
||||
|
||||
def get_combined_config(name, val, default=None):
|
||||
"""Consolidate the config with priority from high to low provided init
|
||||
value > OS environ > default."""
|
||||
|
||||
if val is not None:
|
||||
return val
|
||||
|
||||
env = load_dash_env_vars().get(f"DASH_{name.upper()}")
|
||||
if env is None:
|
||||
return default
|
||||
|
||||
return env.lower() == "true" if env.lower() in {"true", "false"} else env
|
||||
|
||||
|
||||
def pathname_configs(
|
||||
url_base_pathname=None, routes_pathname_prefix=None, requests_pathname_prefix=None
|
||||
):
|
||||
_pathname_config_error_message = """
|
||||
{} This is ambiguous.
|
||||
To fix this, set `routes_pathname_prefix` instead of `url_base_pathname`.
|
||||
|
||||
Note that `requests_pathname_prefix` is the prefix for the AJAX calls that
|
||||
originate from the client (the web browser) and `routes_pathname_prefix` is
|
||||
the prefix for the API routes on the backend (this flask server).
|
||||
`url_base_pathname` will set `requests_pathname_prefix` and
|
||||
`routes_pathname_prefix` to the same value.
|
||||
If you need these to be different values then you should set
|
||||
`requests_pathname_prefix` and `routes_pathname_prefix`,
|
||||
not `url_base_pathname`.
|
||||
"""
|
||||
url_base_pathname = get_combined_config("url_base_pathname", url_base_pathname)
|
||||
|
||||
routes_pathname_prefix = get_combined_config(
|
||||
"routes_pathname_prefix", routes_pathname_prefix
|
||||
)
|
||||
|
||||
requests_pathname_prefix = get_combined_config(
|
||||
"requests_pathname_prefix", requests_pathname_prefix
|
||||
)
|
||||
|
||||
if url_base_pathname is not None and requests_pathname_prefix is not None:
|
||||
raise exceptions.InvalidConfig(
|
||||
_pathname_config_error_message.format(
|
||||
"You supplied `url_base_pathname` and `requests_pathname_prefix`."
|
||||
)
|
||||
)
|
||||
|
||||
if url_base_pathname is not None and routes_pathname_prefix is not None:
|
||||
raise exceptions.InvalidConfig(
|
||||
_pathname_config_error_message.format(
|
||||
"You supplied `url_base_pathname` and `routes_pathname_prefix`."
|
||||
)
|
||||
)
|
||||
|
||||
if url_base_pathname is not None and routes_pathname_prefix is None:
|
||||
routes_pathname_prefix = url_base_pathname
|
||||
elif routes_pathname_prefix is None:
|
||||
routes_pathname_prefix = "/"
|
||||
|
||||
if not routes_pathname_prefix.startswith("/"):
|
||||
raise exceptions.InvalidConfig(
|
||||
"`routes_pathname_prefix` needs to start with `/`"
|
||||
)
|
||||
if not routes_pathname_prefix.endswith("/"):
|
||||
raise exceptions.InvalidConfig("`routes_pathname_prefix` needs to end with `/`")
|
||||
|
||||
app_name = load_dash_env_vars().DASH_APP_NAME
|
||||
|
||||
if not requests_pathname_prefix and app_name:
|
||||
requests_pathname_prefix = "/" + app_name + routes_pathname_prefix
|
||||
elif requests_pathname_prefix is None:
|
||||
requests_pathname_prefix = routes_pathname_prefix
|
||||
|
||||
if not requests_pathname_prefix.startswith("/"):
|
||||
raise exceptions.InvalidConfig(
|
||||
"`requests_pathname_prefix` needs to start with `/`"
|
||||
)
|
||||
|
||||
return url_base_pathname, routes_pathname_prefix, requests_pathname_prefix
|
||||
|
||||
|
||||
def pages_folder_config(name, pages_folder, use_pages):
|
||||
if not pages_folder:
|
||||
return None
|
||||
is_custom_folder = str(pages_folder) != "pages"
|
||||
pages_folder_path = os.path.join(flask.helpers.get_root_path(name), pages_folder)
|
||||
if (use_pages or is_custom_folder) and not os.path.isdir(pages_folder_path):
|
||||
error_msg = f"""
|
||||
A folder called `{pages_folder}` does not exist. If a folder for pages is not
|
||||
required in your application, set `pages_folder=""`. For example:
|
||||
`app = Dash(__name__, pages_folder="")`
|
||||
"""
|
||||
raise exceptions.InvalidConfig(error_msg)
|
||||
return pages_folder_path
|
||||
@@ -0,0 +1,77 @@
|
||||
import os
|
||||
|
||||
__version__ = "2.2.0"
|
||||
|
||||
_available_react_versions = {"18.3.1", "18.2.0", "16.14.0"}
|
||||
_available_reactdom_versions = {"18.3.1", "18.2.0", "16.14.0"}
|
||||
_js_dist_dependencies = [] # to be set by _set_react_version
|
||||
|
||||
|
||||
def _set_react_version(v_react, v_reactdom=None):
|
||||
if not v_reactdom:
|
||||
v_reactdom = v_react
|
||||
|
||||
react_err = f"looking for one of {_available_react_versions}, found {v_react}"
|
||||
reactdom_err = (
|
||||
f"looking for one of {_available_reactdom_versions}, found {v_reactdom}"
|
||||
)
|
||||
assert v_react in _available_react_versions, react_err
|
||||
assert v_reactdom in _available_reactdom_versions, reactdom_err
|
||||
|
||||
_js_dist_dependencies[:] = [
|
||||
{
|
||||
"external_url": {
|
||||
"prod": [
|
||||
"https://unpkg.com/@babel/polyfill@7.12.1/dist/polyfill.min.js",
|
||||
f"https://unpkg.com/react@{v_react}/umd/react.production.min.js",
|
||||
f"https://unpkg.com/react-dom@{v_reactdom}/umd/react-dom.production.min.js",
|
||||
"https://unpkg.com/prop-types@15.8.1/prop-types.min.js",
|
||||
],
|
||||
"dev": [
|
||||
"https://unpkg.com/@babel/polyfill@7.12.1/dist/polyfill.min.js",
|
||||
f"https://unpkg.com/react@{v_react}/umd/react.development.js",
|
||||
f"https://unpkg.com/react-dom@{v_reactdom}/umd/react-dom.development.js",
|
||||
"https://unpkg.com/prop-types@15.8.1/prop-types.js",
|
||||
],
|
||||
},
|
||||
"relative_package_path": {
|
||||
"prod": [
|
||||
"deps/polyfill@7.12.1.min.js",
|
||||
f"deps/react@{v_react}.min.js",
|
||||
f"deps/react-dom@{v_reactdom}.min.js",
|
||||
"deps/prop-types@15.8.1.min.js",
|
||||
],
|
||||
"dev": [
|
||||
"deps/polyfill@7.12.1.min.js",
|
||||
f"deps/react@{v_react}.js",
|
||||
f"deps/react-dom@{v_reactdom}.js",
|
||||
"deps/prop-types@15.8.1.js",
|
||||
],
|
||||
},
|
||||
"namespace": "dash",
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
_env_react_version = os.getenv("REACT_VERSION")
|
||||
if _env_react_version:
|
||||
_set_react_version(_env_react_version)
|
||||
print(f"EXPERIMENTAL: Using react version from env: {_env_react_version}")
|
||||
else:
|
||||
_set_react_version("18.3.1", "18.3.1")
|
||||
|
||||
_js_dist = [
|
||||
{
|
||||
"relative_package_path": "dash-renderer/build/dash_renderer.min.js",
|
||||
"dev_package_path": "dash-renderer/build/dash_renderer.dev.js",
|
||||
"external_url": "https://unpkg.com/dash-renderer@2.2.0"
|
||||
"/build/dash_renderer.min.js",
|
||||
"namespace": "dash",
|
||||
},
|
||||
{
|
||||
"relative_package_path": "dash-renderer/build/dash_renderer.min.js.map",
|
||||
"dev_package_path": "dash-renderer/build/dash_renderer.dev.js.map",
|
||||
"namespace": "dash",
|
||||
"dynamic": True,
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,20 @@
|
||||
from textwrap import dedent
|
||||
|
||||
APP = None
|
||||
|
||||
|
||||
def get_app():
|
||||
if APP is None:
|
||||
raise Exception(
|
||||
dedent(
|
||||
"""
|
||||
App object is not yet defined. `app = dash.Dash()` needs to be run
|
||||
before `dash.get_app()` is called and can only be used within apps that use
|
||||
the `pages` multi-page app feature: `dash.Dash(use_pages=True)`.
|
||||
|
||||
`dash.get_app()` is used to get around circular import issues when Python files
|
||||
within the pages/` folder need to reference the `app` object.
|
||||
"""
|
||||
)
|
||||
)
|
||||
return APP
|
||||
@@ -0,0 +1,161 @@
|
||||
from ._utils import AttributeDict
|
||||
from . import exceptions
|
||||
|
||||
CONFIG = AttributeDict()
|
||||
|
||||
|
||||
def get_asset_url(path):
|
||||
"""
|
||||
Return the URL for the provided `path` in the assets directory.
|
||||
|
||||
`dash.get_asset_url` is not compatible with Dash Snapshots.
|
||||
Use `get_asset_url` on the app instance instead: `app.get_asset_url`.
|
||||
See `app.get_asset_url` for more information.
|
||||
"""
|
||||
return app_get_asset_url(CONFIG, path)
|
||||
|
||||
|
||||
def app_get_asset_url(config, path):
|
||||
if config.assets_external_path:
|
||||
prefix = config.assets_external_path
|
||||
else:
|
||||
prefix = config.requests_pathname_prefix
|
||||
return "/".join(
|
||||
[
|
||||
# Only take the first part of the pathname
|
||||
prefix.rstrip("/"),
|
||||
config.assets_url_path.lstrip("/"),
|
||||
path,
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def get_relative_path(path):
|
||||
"""
|
||||
Return a path with `requests_pathname_prefix` prefixed before it.
|
||||
Use this function when specifying local URL paths that will work
|
||||
in environments regardless of what `requests_pathname_prefix` is.
|
||||
In some deployment environments, like Dash Enterprise,
|
||||
`requests_pathname_prefix` is set to the application name,
|
||||
e.g. `my-dash-app`.
|
||||
When working locally, `requests_pathname_prefix` might be unset and
|
||||
so a relative URL like `/page-2` can just be `/page-2`.
|
||||
However, when the app is deployed to a URL like `/my-dash-app`, then
|
||||
`dash.get_relative_path('/page-2')` will return `/my-dash-app/page-2`.
|
||||
This can be used as an alternative to `get_asset_url` as well with
|
||||
`dash.get_relative_path('/assets/logo.png')`
|
||||
|
||||
Use this function with `dash.strip_relative_path` in callbacks that
|
||||
deal with `dcc.Location` `pathname` routing.
|
||||
That is, your usage may look like:
|
||||
```
|
||||
app.layout = html.Div([
|
||||
dcc.Location(id='url'),
|
||||
html.Div(id='content')
|
||||
])
|
||||
@dash.callback(Output('content', 'children'), [Input('url', 'pathname')])
|
||||
def display_content(path):
|
||||
page_name = dash.strip_relative_path(path)
|
||||
if not page_name: # None or ''
|
||||
return html.Div([
|
||||
dcc.Link(href=dash.get_relative_path('/page-1')),
|
||||
dcc.Link(href=dash.get_relative_path('/page-2')),
|
||||
])
|
||||
elif page_name == 'page-1':
|
||||
return chapters.page_1
|
||||
if page_name == "page-2":
|
||||
return chapters.page_2
|
||||
```
|
||||
|
||||
`dash.get_relative_path` is not compatible with Dash Snapshots. Use
|
||||
`get_relative_path` on the app instance instead: `app.get_relative_path`.
|
||||
"""
|
||||
return app_get_relative_path(CONFIG.requests_pathname_prefix, path)
|
||||
|
||||
|
||||
def app_get_relative_path(requests_pathname, path):
|
||||
if requests_pathname == "/" and path == "":
|
||||
return "/"
|
||||
if requests_pathname != "/" and path == "":
|
||||
return requests_pathname
|
||||
if not path.startswith("/"):
|
||||
raise exceptions.UnsupportedRelativePath(
|
||||
f"""
|
||||
Paths that aren't prefixed with a leading / are not supported.
|
||||
You supplied: {path}
|
||||
"""
|
||||
)
|
||||
return "/".join([requests_pathname.rstrip("/"), path.lstrip("/")])
|
||||
|
||||
|
||||
def strip_relative_path(path):
|
||||
"""
|
||||
Return a path with `requests_pathname_prefix` and leading and trailing
|
||||
slashes stripped from it. Also, if None is passed in, None is returned.
|
||||
Use this function with `get_relative_path` in callbacks that deal
|
||||
with `dcc.Location` `pathname` routing.
|
||||
That is, your usage may look like:
|
||||
```
|
||||
app.layout = html.Div([
|
||||
dcc.Location(id='url'),
|
||||
html.Div(id='content')
|
||||
])
|
||||
@dash.callback(Output('content', 'children'), [Input('url', 'pathname')])
|
||||
def display_content(path):
|
||||
page_name = dash.strip_relative_path(path)
|
||||
if not page_name: # None or ''
|
||||
return html.Div([
|
||||
dcc.Link(href=dash.get_relative_path('/page-1')),
|
||||
dcc.Link(href=dash.get_relative_path('/page-2')),
|
||||
])
|
||||
elif page_name == 'page-1':
|
||||
return chapters.page_1
|
||||
if page_name == "page-2":
|
||||
return chapters.page_2
|
||||
```
|
||||
Note that `chapters.page_1` will be served if the user visits `/page-1`
|
||||
_or_ `/page-1/` since `strip_relative_path` removes the trailing slash.
|
||||
|
||||
Also note that `strip_relative_path` is compatible with
|
||||
`get_relative_path` in environments where `requests_pathname_prefix` set.
|
||||
In some deployment environments, like Dash Enterprise,
|
||||
`requests_pathname_prefix` is set to the application name, e.g. `my-dash-app`.
|
||||
When working locally, `requests_pathname_prefix` might be unset and
|
||||
so a relative URL like `/page-2` can just be `/page-2`.
|
||||
However, when the app is deployed to a URL like `/my-dash-app`, then
|
||||
`dash.get_relative_path('/page-2')` will return `/my-dash-app/page-2`
|
||||
|
||||
The `pathname` property of `dcc.Location` will return '`/my-dash-app/page-2`'
|
||||
to the callback.
|
||||
In this case, `dash.strip_relative_path('/my-dash-app/page-2')`
|
||||
will return `'page-2'`
|
||||
|
||||
For nested URLs, slashes are still included:
|
||||
`dash.strip_relative_path('/page-1/sub-page-1/')` will return
|
||||
`page-1/sub-page-1`
|
||||
```
|
||||
"""
|
||||
return app_strip_relative_path(CONFIG.requests_pathname_prefix, path)
|
||||
|
||||
|
||||
def app_strip_relative_path(requests_pathname, path):
|
||||
if path is None:
|
||||
return None
|
||||
if (
|
||||
requests_pathname != "/" and not path.startswith(requests_pathname.rstrip("/"))
|
||||
) or (requests_pathname == "/" and not path.startswith("/")):
|
||||
raise exceptions.UnsupportedRelativePath(
|
||||
f"""
|
||||
Paths that aren't prefixed with requests_pathname_prefix are not supported.
|
||||
You supplied: {path} and requests_pathname_prefix was {requests_pathname}
|
||||
"""
|
||||
)
|
||||
if requests_pathname != "/" and path.startswith(requests_pathname.rstrip("/")):
|
||||
path = path.replace(
|
||||
# handle the case where the path might be `/my-dash-app`
|
||||
# but the requests_pathname_prefix is `/my-dash-app/`
|
||||
requests_pathname.rstrip("/"),
|
||||
"",
|
||||
1,
|
||||
)
|
||||
return path.strip("/")
|
||||
@@ -0,0 +1,250 @@
|
||||
"""
|
||||
This module contains a collection of utility function for dealing with property
|
||||
groupings.
|
||||
|
||||
Terminology:
|
||||
|
||||
For the purpose of grouping and ungrouping, tuples/lists and dictionaries are considered
|
||||
"composite values" and all other values are considered "scalar values".
|
||||
|
||||
A "grouping value" is either composite or scalar.
|
||||
|
||||
A "schema" is a grouping value that can be used to encode an expected grouping
|
||||
structure
|
||||
|
||||
"""
|
||||
from .exceptions import InvalidCallbackReturnValue
|
||||
from ._utils import AttributeDict, stringify_id
|
||||
|
||||
|
||||
def flatten_grouping(grouping, schema=None):
|
||||
"""
|
||||
Convert a grouping value to a list of scalar values
|
||||
|
||||
:param grouping: grouping value to flatten
|
||||
:param schema: If provided, a grouping value representing the expected structure of
|
||||
the input grouping value. If not provided, the grouping value is its own schema.
|
||||
A schema is required in order to be able treat tuples and dicts in the input
|
||||
grouping as scalar values.
|
||||
|
||||
:return: list of the scalar values in the input grouping
|
||||
"""
|
||||
stack = []
|
||||
result = []
|
||||
|
||||
# Avoid repeated recursive Python calls by using an explicit stack
|
||||
push = stack.append
|
||||
pop = stack.pop
|
||||
|
||||
# Only validate once at the top if schema is provided
|
||||
if schema is None:
|
||||
schema = grouping
|
||||
else:
|
||||
validate_grouping(grouping, schema)
|
||||
|
||||
push((grouping, schema))
|
||||
while stack:
|
||||
group, sch = pop()
|
||||
# Inline isinstance checks for perf
|
||||
typ = type(sch)
|
||||
if typ is tuple or typ is list:
|
||||
# Avoid double recursion / excessive list construction
|
||||
for ge, se in zip(group, sch):
|
||||
push((ge, se))
|
||||
elif typ is dict:
|
||||
for k in sch:
|
||||
push((group[k], sch[k]))
|
||||
else:
|
||||
result.append(group)
|
||||
result.reverse() # Since we LIFO, leaf values are in reverse order
|
||||
return result
|
||||
|
||||
|
||||
def grouping_len(grouping):
|
||||
"""
|
||||
Get the length of a grouping. The length equal to the number of scalar values
|
||||
contained in the grouping, which is equivalent to the length of the list that would
|
||||
result from calling flatten_grouping on the grouping value.
|
||||
|
||||
:param grouping: The grouping value to calculate the length of
|
||||
:return: non-negative integer
|
||||
"""
|
||||
if isinstance(grouping, (tuple, list)):
|
||||
return sum([grouping_len(group_el) for group_el in grouping])
|
||||
|
||||
if isinstance(grouping, dict):
|
||||
return sum([grouping_len(group_el) for group_el in grouping.values()])
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
def make_grouping_by_index(schema, flat_values):
|
||||
"""
|
||||
Make a grouping like the provided grouping schema, with scalar values drawn from a
|
||||
flat list by index.
|
||||
|
||||
Note: Scalar values in schema are not used
|
||||
|
||||
:param schema: Grouping value encoding the structure of the grouping to return
|
||||
:param flat_values: List of values with length matching the grouping_len of schema.
|
||||
Elements of flat_values will become the scalar values in the resulting grouping
|
||||
"""
|
||||
|
||||
def _perform_make_grouping_like(value, next_values):
|
||||
if isinstance(value, (tuple, list)):
|
||||
return list(
|
||||
_perform_make_grouping_like(el, next_values)
|
||||
for i, el in enumerate(value)
|
||||
)
|
||||
|
||||
if isinstance(value, dict):
|
||||
return {
|
||||
k: _perform_make_grouping_like(v, next_values)
|
||||
for i, (k, v) in enumerate(value.items())
|
||||
}
|
||||
|
||||
return next_values.pop(0)
|
||||
|
||||
if not isinstance(flat_values, list):
|
||||
raise ValueError(
|
||||
"The flat_values argument must be a list. "
|
||||
f"Received value of type {type(flat_values)}"
|
||||
)
|
||||
|
||||
expected_length = len(flatten_grouping(schema))
|
||||
if len(flat_values) != expected_length:
|
||||
raise ValueError(
|
||||
f"The specified grouping pattern requires {expected_length} "
|
||||
f"elements but received {len(flat_values)}\n"
|
||||
f" Grouping pattern: {repr(schema)}\n"
|
||||
f" Values: {flat_values}"
|
||||
)
|
||||
|
||||
return _perform_make_grouping_like(schema, list(flat_values))
|
||||
|
||||
|
||||
def map_grouping(fn, grouping):
|
||||
"""
|
||||
Map a function over all of the scalar values of a grouping, maintaining the
|
||||
grouping structure
|
||||
|
||||
:param fn: Single-argument function that accepts and returns scalar grouping values
|
||||
:param grouping: The grouping to map the function over
|
||||
:return: A new grouping with the same structure as input grouping with scalar
|
||||
values updated by the input function.
|
||||
"""
|
||||
if isinstance(grouping, (tuple, list)):
|
||||
return [map_grouping(fn, g) for g in grouping]
|
||||
|
||||
if isinstance(grouping, dict):
|
||||
return AttributeDict({k: map_grouping(fn, g) for k, g in grouping.items()})
|
||||
|
||||
return fn(grouping)
|
||||
|
||||
|
||||
def make_grouping_by_key(schema, source, default=None):
|
||||
"""
|
||||
Create a grouping from a schema by using the schema's scalar values to look up
|
||||
items in the provided source object.
|
||||
|
||||
:param schema: A grouping of potential keys in source
|
||||
:param source: Dict-like object to use to look up scalar grouping value using
|
||||
scalar grouping values as keys
|
||||
:param default: Default scalar value to use if grouping scalar key is not present
|
||||
in source
|
||||
:return: grouping
|
||||
"""
|
||||
return map_grouping(lambda s: source.get(s, default), schema)
|
||||
|
||||
|
||||
class SchemaTypeValidationError(InvalidCallbackReturnValue):
|
||||
def __init__(self, value, full_schema, path, expected_type):
|
||||
super().__init__(
|
||||
msg=f"""
|
||||
Schema: {full_schema}
|
||||
Path: {repr(path)}
|
||||
Expected type: {expected_type}
|
||||
Received value of type {type(value)}:
|
||||
{repr(value)}
|
||||
"""
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def check(cls, value, full_schema, path, expected_type):
|
||||
if not isinstance(value, expected_type):
|
||||
raise SchemaTypeValidationError(value, full_schema, path, expected_type)
|
||||
|
||||
|
||||
class SchemaLengthValidationError(InvalidCallbackReturnValue):
|
||||
def __init__(self, value, full_schema, path, expected_len):
|
||||
super().__init__(
|
||||
msg=f"""
|
||||
Schema: {full_schema}
|
||||
Path: {repr(path)}
|
||||
Expected length: {expected_len}
|
||||
Received value of length {len(value)}:
|
||||
{repr(value)}
|
||||
"""
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def check(cls, value, full_schema, path, expected_len):
|
||||
if len(value) != expected_len:
|
||||
raise SchemaLengthValidationError(value, full_schema, path, expected_len)
|
||||
|
||||
|
||||
class SchemaKeysValidationError(InvalidCallbackReturnValue):
|
||||
def __init__(self, value, full_schema, path, expected_keys):
|
||||
super().__init__(
|
||||
msg=f"""
|
||||
Schema: {full_schema}
|
||||
Path: {repr(path)}
|
||||
Expected keys: {expected_keys}
|
||||
Received value with keys {set(value.keys())}:
|
||||
{repr(value)}
|
||||
"""
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def check(cls, value, full_schema, path, expected_keys):
|
||||
if set(value.keys()) != set(expected_keys):
|
||||
raise SchemaKeysValidationError(value, full_schema, path, expected_keys)
|
||||
|
||||
|
||||
def validate_grouping(grouping, schema, full_schema=None, path=()):
|
||||
"""
|
||||
Validate that the provided grouping conforms to the provided schema.
|
||||
If not, raise a SchemaValidationError
|
||||
"""
|
||||
if full_schema is None:
|
||||
full_schema = schema
|
||||
|
||||
if isinstance(schema, (tuple, list)):
|
||||
SchemaTypeValidationError.check(grouping, full_schema, path, (tuple, list))
|
||||
SchemaLengthValidationError.check(grouping, full_schema, path, len(schema))
|
||||
|
||||
for i, (g, s) in enumerate(zip(grouping, schema)):
|
||||
validate_grouping(g, s, full_schema=full_schema, path=path + (i,))
|
||||
elif isinstance(schema, dict):
|
||||
SchemaTypeValidationError.check(grouping, full_schema, path, dict)
|
||||
SchemaKeysValidationError.check(grouping, full_schema, path, set(schema))
|
||||
for k in schema:
|
||||
validate_grouping(
|
||||
grouping[k], schema[k], full_schema=full_schema, path=path + (k,)
|
||||
)
|
||||
else:
|
||||
pass
|
||||
|
||||
|
||||
def update_args_group(g, triggered):
|
||||
if isinstance(g, dict):
|
||||
str_id = stringify_id(g["id"])
|
||||
prop_id = f"{str_id}.{g['property']}"
|
||||
|
||||
new_values = {
|
||||
"value": g.get("value"),
|
||||
"str_id": str_id,
|
||||
"triggered": prop_id in triggered,
|
||||
"id": AttributeDict(g["id"]) if isinstance(g["id"], dict) else g["id"],
|
||||
}
|
||||
g.update(new_values)
|
||||
@@ -0,0 +1,276 @@
|
||||
import typing as _t
|
||||
|
||||
from importlib import metadata as _importlib_metadata
|
||||
|
||||
import typing_extensions as _tx
|
||||
import flask as _f
|
||||
|
||||
from .exceptions import HookError
|
||||
from .resources import ResourceType
|
||||
from ._callback import ClientsideFuncType
|
||||
|
||||
if _t.TYPE_CHECKING:
|
||||
from .dash import Dash
|
||||
from .development.base_component import Component
|
||||
|
||||
ComponentType = _t.TypeVar("ComponentType", bound=Component)
|
||||
LayoutType = _t.Union[ComponentType, _t.List[ComponentType]]
|
||||
else:
|
||||
LayoutType = None
|
||||
ComponentType = None
|
||||
Dash = None
|
||||
|
||||
|
||||
HookDataType = _tx.TypeVar("HookDataType")
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
class _Hook(_tx.Generic[HookDataType]):
|
||||
def __init__(self, func, priority=0, final=False, data: HookDataType = None):
|
||||
self.func = func
|
||||
self.final = final
|
||||
self.data = data
|
||||
self.priority = priority
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
return self.func(*args, **kwargs)
|
||||
|
||||
|
||||
class _Hooks:
|
||||
def __init__(self) -> None:
|
||||
self._ns = {
|
||||
"setup": [],
|
||||
"layout": [],
|
||||
"routes": [],
|
||||
"error": [],
|
||||
"callback": [],
|
||||
"index": [],
|
||||
"custom_data": [],
|
||||
"dev_tools": [],
|
||||
}
|
||||
self._js_dist = []
|
||||
self._css_dist = []
|
||||
self._clientside_callbacks: _t.List[
|
||||
_t.Tuple[ClientsideFuncType, _t.Any, _t.Any]
|
||||
] = []
|
||||
|
||||
# final hooks are a single hook added to the end of regular hooks.
|
||||
self._finals = {}
|
||||
|
||||
def add_hook(
|
||||
self,
|
||||
hook: str,
|
||||
func: _t.Callable,
|
||||
priority: _t.Optional[int] = None,
|
||||
final: bool = False,
|
||||
data: _t.Optional[HookDataType] = None,
|
||||
):
|
||||
if final:
|
||||
existing = self._finals.get(hook)
|
||||
if existing:
|
||||
raise HookError("Final hook already present")
|
||||
self._finals[hook] = _Hook(func, final, data=data)
|
||||
return
|
||||
hks = self._ns.get(hook, [])
|
||||
|
||||
p = priority or 0
|
||||
if not priority and len(hks):
|
||||
# Take the minimum value and remove 1 to keep the order.
|
||||
priority_min = min(h.priority for h in hks)
|
||||
p = priority_min - 1
|
||||
|
||||
hks.append(_Hook(func, priority=p, data=data))
|
||||
self._ns[hook] = sorted(hks, reverse=True, key=lambda h: h.priority)
|
||||
|
||||
def get_hooks(self, hook: str) -> _t.List[_Hook]:
|
||||
final = self._finals.get(hook, None)
|
||||
if final:
|
||||
final = [final]
|
||||
else:
|
||||
final = []
|
||||
return self._ns.get(hook, []) + final
|
||||
|
||||
def layout(self, priority: _t.Optional[int] = None, final: bool = False):
|
||||
"""
|
||||
Run a function when serving the layout, the return value
|
||||
will be used as the layout.
|
||||
"""
|
||||
|
||||
def _wrap(func: _t.Callable[[LayoutType], LayoutType]):
|
||||
self.add_hook("layout", func, priority=priority, final=final)
|
||||
return func
|
||||
|
||||
return _wrap
|
||||
|
||||
def setup(self, priority: _t.Optional[int] = None, final: bool = False):
|
||||
"""
|
||||
Can be used to get a reference to the app after it is instantiated.
|
||||
"""
|
||||
|
||||
def _setup(func: _t.Callable[[Dash], None]):
|
||||
self.add_hook("setup", func, priority=priority, final=final)
|
||||
return func
|
||||
|
||||
return _setup
|
||||
|
||||
def route(
|
||||
self,
|
||||
name: _t.Optional[str] = None,
|
||||
methods: _t.Sequence[str] = ("GET",),
|
||||
priority: _t.Optional[int] = None,
|
||||
final: bool = False,
|
||||
):
|
||||
"""
|
||||
Add a route to the Dash server.
|
||||
"""
|
||||
|
||||
def wrap(func: _t.Callable[[], _f.Response]):
|
||||
_name = name or func.__name__
|
||||
self.add_hook(
|
||||
"routes",
|
||||
func,
|
||||
priority=priority,
|
||||
final=final,
|
||||
data=dict(name=_name, methods=methods),
|
||||
)
|
||||
return func
|
||||
|
||||
return wrap
|
||||
|
||||
def error(self, priority: _t.Optional[int] = None, final: bool = False):
|
||||
"""Automatically add an error handler to the dash app."""
|
||||
|
||||
def _error(func: _t.Callable[[Exception], _t.Any]):
|
||||
self.add_hook("error", func, priority=priority, final=final)
|
||||
return func
|
||||
|
||||
return _error
|
||||
|
||||
def callback(
|
||||
self, *args, priority: _t.Optional[int] = None, final: bool = False, **kwargs
|
||||
):
|
||||
"""
|
||||
Add a callback to all the apps with the hook installed.
|
||||
"""
|
||||
|
||||
def wrap(func):
|
||||
self.add_hook(
|
||||
"callback",
|
||||
func,
|
||||
priority=priority,
|
||||
final=final,
|
||||
data=(list(args), dict(kwargs)),
|
||||
)
|
||||
return func
|
||||
|
||||
return wrap
|
||||
|
||||
def clientside_callback(
|
||||
self, clientside_function: ClientsideFuncType, *args, **kwargs
|
||||
):
|
||||
"""
|
||||
Add a callback to all the apps with the hook installed.
|
||||
"""
|
||||
self._clientside_callbacks.append((clientside_function, args, kwargs))
|
||||
|
||||
def script(self, distribution: _t.List[ResourceType]):
|
||||
"""Add js scripts to the page."""
|
||||
self._js_dist.extend(distribution)
|
||||
|
||||
def stylesheet(self, distribution: _t.List[ResourceType]):
|
||||
"""Add stylesheets to the page."""
|
||||
self._css_dist.extend(distribution)
|
||||
|
||||
def index(self, priority: _t.Optional[int] = None, final=False):
|
||||
"""Modify the index of the apps."""
|
||||
|
||||
def wrap(func):
|
||||
self.add_hook(
|
||||
"index",
|
||||
func,
|
||||
priority=priority,
|
||||
final=final,
|
||||
)
|
||||
return func
|
||||
|
||||
return wrap
|
||||
|
||||
def custom_data(
|
||||
self, namespace: str, priority: _t.Optional[int] = None, final=False
|
||||
):
|
||||
"""
|
||||
Add data to the callback_context.custom_data property under the namespace.
|
||||
|
||||
The hook function takes the current context_value and before the ctx is set
|
||||
and has access to the flask request context.
|
||||
"""
|
||||
|
||||
def wrap(func: _t.Callable[[_t.Dict], _t.Any]):
|
||||
self.add_hook(
|
||||
"custom_data",
|
||||
func,
|
||||
priority=priority,
|
||||
final=final,
|
||||
data={"namespace": namespace},
|
||||
)
|
||||
return func
|
||||
|
||||
return wrap
|
||||
|
||||
def devtool(self, namespace: str, component_type: str, props=None):
|
||||
"""
|
||||
Add a component to be rendered inside the dev tools.
|
||||
|
||||
If it's a dash component, it can be used in callbacks provided
|
||||
that it has an id and the dependency is set with allow_optional=True.
|
||||
|
||||
`props` can be a function, in which case it will be called before
|
||||
sending the component to the frontend.
|
||||
"""
|
||||
self._ns["dev_tools"].append(
|
||||
{
|
||||
"namespace": namespace,
|
||||
"type": component_type,
|
||||
"props": props or {},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
hooks = _Hooks()
|
||||
|
||||
|
||||
class HooksManager:
|
||||
# Flag to only run `register_setuptools` once
|
||||
_registered = False
|
||||
hooks = hooks
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
class HookErrorHandler:
|
||||
def __init__(self, original):
|
||||
self.original = original
|
||||
|
||||
def __call__(self, err: Exception):
|
||||
result = None
|
||||
if self.original:
|
||||
result = self.original(err)
|
||||
hook_result = None
|
||||
for hook in HooksManager.get_hooks("error"):
|
||||
hook_result = hook(err)
|
||||
return result or hook_result
|
||||
|
||||
@classmethod
|
||||
def get_hooks(cls, hook: str):
|
||||
return cls.hooks.get_hooks(hook)
|
||||
|
||||
@classmethod
|
||||
def register_setuptools(cls):
|
||||
if cls._registered:
|
||||
# Only have to register once.
|
||||
return
|
||||
|
||||
for dist in _importlib_metadata.distributions():
|
||||
for entry in dist.entry_points:
|
||||
# Look for setup.py entry points named `dash_hooks`
|
||||
if entry.group != "dash_hooks":
|
||||
continue
|
||||
entry.load()
|
||||
@@ -0,0 +1,487 @@
|
||||
# type: ignore
|
||||
import asyncio
|
||||
import io
|
||||
import inspect
|
||||
import logging
|
||||
import os
|
||||
import queue
|
||||
import uuid
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
from typing import Optional
|
||||
from typing_extensions import Literal
|
||||
|
||||
from werkzeug.serving import make_server
|
||||
|
||||
|
||||
try:
|
||||
from IPython import get_ipython
|
||||
from IPython.display import IFrame, display, Javascript
|
||||
from IPython.core.display import HTML
|
||||
from IPython.core.ultratb import FormattedTB
|
||||
from retrying import retry
|
||||
from ipykernel.comm import Comm
|
||||
import nest_asyncio
|
||||
|
||||
import requests
|
||||
|
||||
_dash_comm = Comm(target_name="dash")
|
||||
_dep_installed = True
|
||||
except ImportError:
|
||||
_dep_installed = False
|
||||
_dash_comm = None
|
||||
get_ipython = lambda: None
|
||||
|
||||
JupyterDisplayMode = Literal["inline", "external", "jupyterlab", "tab", "_none"]
|
||||
|
||||
|
||||
def _get_skip(error: Exception):
|
||||
from dash._callback import ( # pylint: disable=import-outside-toplevel
|
||||
_invoke_callback,
|
||||
)
|
||||
|
||||
tb = error.__traceback__
|
||||
skip = 1
|
||||
while tb.tb_next is not None:
|
||||
skip += 1
|
||||
tb = tb.tb_next
|
||||
if tb.tb_frame.f_code is _invoke_callback.__code__:
|
||||
return skip
|
||||
|
||||
return skip
|
||||
|
||||
|
||||
def _custom_formatargvalues(
|
||||
args,
|
||||
varargs,
|
||||
varkw,
|
||||
locals, # pylint: disable=W0622
|
||||
formatarg=str,
|
||||
formatvarargs=lambda name: "*" + name,
|
||||
formatvarkw=lambda name: "**" + name,
|
||||
formatvalue=lambda value: "=" + repr(value),
|
||||
):
|
||||
|
||||
"""Copied from inspect.formatargvalues, modified to place function
|
||||
arguments on separate lines"""
|
||||
|
||||
# pylint: disable=W0622
|
||||
def convert(name, locals=locals, formatarg=formatarg, formatvalue=formatvalue):
|
||||
return formatarg(name) + formatvalue(locals[name])
|
||||
|
||||
specs = []
|
||||
|
||||
# pylint: disable=C0200
|
||||
for i in range(len(args)):
|
||||
specs.append(convert(args[i]))
|
||||
if varargs:
|
||||
specs.append(formatvarargs(varargs) + formatvalue(locals[varargs]))
|
||||
if varkw:
|
||||
specs.append(formatvarkw(varkw) + formatvalue(locals[varkw]))
|
||||
|
||||
result = "(" + ", ".join(specs) + ")"
|
||||
|
||||
if len(result) < 40:
|
||||
return result
|
||||
# Put each arg on a separate line
|
||||
return "(\n " + ",\n ".join(specs) + "\n)"
|
||||
|
||||
|
||||
_jupyter_config = {}
|
||||
|
||||
_caller = {}
|
||||
|
||||
|
||||
def _send_jupyter_config_comm_request():
|
||||
# If running in an ipython kernel,
|
||||
# request that the front end extension send us the notebook server base URL
|
||||
if get_ipython() is not None:
|
||||
if _dash_comm.kernel is not None:
|
||||
_caller["parent"] = _dash_comm.kernel.get_parent()
|
||||
_dash_comm.send({"type": "base_url_request"})
|
||||
|
||||
|
||||
def _jupyter_comm_response_received():
|
||||
return bool(_jupyter_config)
|
||||
|
||||
|
||||
def _request_jupyter_config(timeout=2):
|
||||
# Heavily inspired by implementation of CaptureExecution in the
|
||||
if _dash_comm.kernel is None:
|
||||
# Not in jupyter setting
|
||||
return
|
||||
|
||||
_send_jupyter_config_comm_request()
|
||||
|
||||
# Get shell and kernel
|
||||
shell = get_ipython()
|
||||
kernel = shell.kernel
|
||||
|
||||
# Start capturing shell events to replay later
|
||||
captured_events = []
|
||||
|
||||
def capture_event(stream, ident, parent):
|
||||
captured_events.append((stream, ident, parent))
|
||||
|
||||
kernel.shell_handlers["execute_request"] = capture_event
|
||||
|
||||
# increment execution count to avoid collision error
|
||||
shell.execution_count += 1
|
||||
|
||||
# Allow kernel to execute comms until we receive the jupyter configuration comm
|
||||
# response
|
||||
t0 = time.time()
|
||||
while True:
|
||||
if (time.time() - t0) > timeout:
|
||||
# give up
|
||||
raise EnvironmentError(
|
||||
"Unable to communicate with the jupyter_dash notebook or JupyterLab \n"
|
||||
"extension required to infer Jupyter configuration."
|
||||
)
|
||||
if _jupyter_comm_response_received():
|
||||
break
|
||||
|
||||
if asyncio.iscoroutinefunction(kernel.do_one_iteration):
|
||||
loop = asyncio.get_event_loop()
|
||||
nest_asyncio.apply(loop)
|
||||
loop.run_until_complete(kernel.do_one_iteration())
|
||||
else:
|
||||
kernel.do_one_iteration()
|
||||
|
||||
# Stop capturing events, revert the kernel shell handler to the default
|
||||
# execute_request behavior
|
||||
kernel.shell_handlers["execute_request"] = kernel.execute_request
|
||||
|
||||
# Replay captured events
|
||||
# need to flush before replaying so messages show up in current cell not
|
||||
# replay cells
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
|
||||
for stream, ident, parent in captured_events:
|
||||
# Using kernel.set_parent is the key to getting the output of the replayed
|
||||
# events to show up in the cells that were captured instead of the current cell
|
||||
kernel.set_parent(ident, parent)
|
||||
kernel.execute_request(stream, ident, parent)
|
||||
|
||||
|
||||
class JupyterDash:
|
||||
"""
|
||||
Interact with dash apps inside jupyter notebooks.
|
||||
"""
|
||||
|
||||
default_mode: JupyterDisplayMode = "inline"
|
||||
alive_token = str(uuid.uuid4())
|
||||
inline_exceptions: bool = True
|
||||
|
||||
_servers = {}
|
||||
|
||||
def infer_jupyter_proxy_config(self):
|
||||
"""
|
||||
Infer the current Jupyter server configuration. This will detect
|
||||
the proper request_pathname_prefix and server_url values to use when
|
||||
displaying Dash apps.Dash requests will be routed through the proxy.
|
||||
|
||||
Requirements:
|
||||
|
||||
In the classic notebook, this method requires the `dash` nbextension
|
||||
which should be installed automatically with the installation of the
|
||||
jupyter-dash Python package. You can see what notebook extensions are installed
|
||||
by running the following command:
|
||||
$ jupyter nbextension list
|
||||
|
||||
In JupyterLab, this method requires the `@plotly/dash-jupyterlab` labextension. This
|
||||
extension should be installed automatically with the installation of the
|
||||
jupyter-dash Python package, but JupyterLab must be allowed to rebuild before
|
||||
the extension is activated (JupyterLab should automatically detect the
|
||||
extension and produce a popup dialog asking for permission to rebuild). You can
|
||||
see what JupyterLab extensions are installed by running the following command:
|
||||
$ jupyter labextension list
|
||||
"""
|
||||
if not self.in_ipython or self.in_colab:
|
||||
# No op when not running in a Jupyter context or when in Colab
|
||||
return
|
||||
# Assume classic notebook or JupyterLab
|
||||
_request_jupyter_config()
|
||||
|
||||
def __init__(self):
|
||||
self.in_ipython = get_ipython() is not None
|
||||
self.in_colab = "google.colab" in sys.modules
|
||||
|
||||
if _dep_installed and self.in_ipython and _dash_comm:
|
||||
|
||||
@_dash_comm.on_msg
|
||||
def _receive_message(msg):
|
||||
prev_parent = _caller.get("parent")
|
||||
if prev_parent and prev_parent != _dash_comm.kernel.get_parent():
|
||||
_dash_comm.kernel.set_parent(
|
||||
[prev_parent["header"]["session"]], prev_parent
|
||||
)
|
||||
del _caller["parent"]
|
||||
|
||||
msg_data = msg.get("content").get("data")
|
||||
msg_type = msg_data.get("type", None)
|
||||
if msg_type == "base_url_response":
|
||||
_jupyter_config.update(msg_data)
|
||||
|
||||
# pylint: disable=too-many-locals, too-many-branches, too-many-statements
|
||||
def run_app(
|
||||
self,
|
||||
app,
|
||||
mode: Optional[JupyterDisplayMode] = None,
|
||||
width="100%",
|
||||
height=650,
|
||||
host="127.0.0.1",
|
||||
port=8050,
|
||||
server_url=None,
|
||||
):
|
||||
"""
|
||||
:type app: dash.Dash
|
||||
:param mode: How to display the app on the notebook. One Of:
|
||||
``"external"``: The URL of the app will be displayed in the notebook
|
||||
output cell. Clicking this URL will open the app in the default
|
||||
web browser.
|
||||
``"inline"``: The app will be displayed inline in the notebook output cell
|
||||
in an iframe.
|
||||
``"jupyterlab"``: The app will be displayed in a dedicate tab in the
|
||||
JupyterLab interface. Requires JupyterLab and the `jupyterlab-dash`
|
||||
extension.
|
||||
:param width: Width of app when displayed using mode="inline"
|
||||
:param height: Height of app when displayed using mode="inline"
|
||||
:param host: Host of the server
|
||||
:param port: Port used by the server
|
||||
:param server_url: Use if a custom url is required to display the app.
|
||||
"""
|
||||
# Validate / infer display mode
|
||||
if self.in_colab:
|
||||
valid_display_values = ["inline", "external"]
|
||||
else:
|
||||
valid_display_values = ["jupyterlab", "inline", "external", "tab", "_none"]
|
||||
|
||||
if mode is None:
|
||||
mode = self.default_mode
|
||||
elif not isinstance(mode, str):
|
||||
raise ValueError(
|
||||
f"The mode argument must be a string\n"
|
||||
f" Received value of type {type(mode)}: {repr(mode)}"
|
||||
)
|
||||
else:
|
||||
mode = mode.lower() # type: ignore
|
||||
if mode not in valid_display_values:
|
||||
raise ValueError(
|
||||
f"Invalid display argument {mode}\n"
|
||||
f" Valid arguments: {valid_display_values}"
|
||||
)
|
||||
|
||||
# Terminate any existing server using this port
|
||||
old_server = self._servers.get((host, port))
|
||||
if old_server:
|
||||
old_server.shutdown()
|
||||
del self._servers[(host, port)]
|
||||
|
||||
# Configure pathname prefix
|
||||
if "base_subpath" in _jupyter_config:
|
||||
requests_pathname_prefix = (
|
||||
_jupyter_config["base_subpath"].rstrip("/") + "/proxy/{port}/"
|
||||
)
|
||||
else:
|
||||
requests_pathname_prefix = app.config.get("requests_pathname_prefix", None)
|
||||
|
||||
if requests_pathname_prefix is not None:
|
||||
requests_pathname_prefix = requests_pathname_prefix.format(port=port)
|
||||
else:
|
||||
requests_pathname_prefix = "/"
|
||||
|
||||
routes_pathname_prefix = app.config.get("routes_pathname_prefix", "/")
|
||||
|
||||
# FIXME Move config initialization to main dash __init__
|
||||
# low-level setter to circumvent Dash's config locking
|
||||
# normally it's unsafe to alter requests_pathname_prefix this late, but
|
||||
# Jupyter needs some unusual behavior.
|
||||
dict.__setitem__(
|
||||
app.config, "requests_pathname_prefix", requests_pathname_prefix
|
||||
)
|
||||
|
||||
# # Compute server_url url
|
||||
if server_url is None:
|
||||
if "server_url" in _jupyter_config:
|
||||
server_url = _jupyter_config["server_url"].rstrip("/")
|
||||
else:
|
||||
domain_base = os.environ.get("DASH_DOMAIN_BASE", None)
|
||||
if domain_base:
|
||||
# Dash Enterprise sets DASH_DOMAIN_BASE environment variable
|
||||
server_url = "https://" + domain_base
|
||||
else:
|
||||
server_url = f"http://{host}:{port}"
|
||||
else:
|
||||
server_url = server_url.rstrip("/")
|
||||
|
||||
# server_url = "http://{host}:{port}".format(host=host, port=port)
|
||||
|
||||
dashboard_url = f"{server_url}{requests_pathname_prefix}"
|
||||
|
||||
# prevent partial import of orjson when it's installed and mode=jupyterlab
|
||||
# TODO: why do we need this? Why only in this mode? Importing here in
|
||||
# all modes anyway, in case there's a way it can pop up in another mode
|
||||
try:
|
||||
# pylint: disable=C0415,W0611
|
||||
import orjson # noqa: F401
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
err_q = queue.Queue()
|
||||
|
||||
server = make_server(host, port, app.server, threaded=True, processes=0)
|
||||
logging.getLogger("werkzeug").setLevel(logging.ERROR)
|
||||
|
||||
@retry(
|
||||
stop_max_attempt_number=15,
|
||||
wait_exponential_multiplier=100,
|
||||
wait_exponential_max=1000,
|
||||
)
|
||||
def run():
|
||||
try:
|
||||
server.serve_forever()
|
||||
except SystemExit:
|
||||
pass
|
||||
except Exception as error:
|
||||
err_q.put(error)
|
||||
raise error
|
||||
|
||||
thread = threading.Thread(target=run)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
self._servers[(host, port)] = server
|
||||
|
||||
# Wait for server to start up
|
||||
alive_url = f"http://{host}:{port}{routes_pathname_prefix}_alive_{JupyterDash.alive_token}"
|
||||
|
||||
def _get_error():
|
||||
try:
|
||||
err = err_q.get_nowait()
|
||||
if err:
|
||||
raise err
|
||||
except queue.Empty:
|
||||
pass
|
||||
|
||||
# Wait for app to respond to _alive endpoint
|
||||
@retry(
|
||||
stop_max_attempt_number=15,
|
||||
wait_exponential_multiplier=10,
|
||||
wait_exponential_max=1000,
|
||||
)
|
||||
def wait_for_app():
|
||||
_get_error()
|
||||
try:
|
||||
req = requests.get(alive_url)
|
||||
res = req.content.decode()
|
||||
if req.status_code != 200:
|
||||
raise Exception(res)
|
||||
|
||||
if res != "Alive":
|
||||
url = f"http://{host}:{port}"
|
||||
raise OSError(
|
||||
f"Address '{url}' already in use.\n"
|
||||
" Try passing a different port to run."
|
||||
)
|
||||
except requests.ConnectionError as err:
|
||||
_get_error()
|
||||
raise err
|
||||
|
||||
try:
|
||||
wait_for_app()
|
||||
|
||||
if self.in_colab:
|
||||
JupyterDash._display_in_colab(dashboard_url, port, mode, width, height)
|
||||
else:
|
||||
JupyterDash._display_in_jupyter(
|
||||
dashboard_url, port, mode, width, height
|
||||
)
|
||||
except Exception as final_error: # pylint: disable=broad-except
|
||||
msg = str(final_error)
|
||||
if msg.startswith("<!"):
|
||||
display(HTML(msg))
|
||||
else:
|
||||
raise final_error
|
||||
|
||||
@staticmethod
|
||||
def _display_in_colab(dashboard_url, port, mode, width, height):
|
||||
# noinspection PyUnresolvedReferences
|
||||
from google.colab import output # pylint: disable=E0401,E0611,C0415
|
||||
|
||||
if mode == "inline":
|
||||
output.serve_kernel_port_as_iframe(port, width=width, height=height)
|
||||
elif mode == "external":
|
||||
# FIXME there is a 403 on this, maybe it's updated?
|
||||
# Display a hyperlink that can be clicked to open Dashboard
|
||||
print("Dash app running on:")
|
||||
output.serve_kernel_port_as_window(port, anchor_text=dashboard_url)
|
||||
|
||||
@staticmethod
|
||||
def _display_in_jupyter(dashboard_url, port, mode, width, height):
|
||||
if mode == "inline":
|
||||
display(IFrame(dashboard_url, width, height))
|
||||
elif mode in ("external", "tab"):
|
||||
# Display a hyperlink that can be clicked to open Dashboard
|
||||
print(f"Dash app running on {dashboard_url}")
|
||||
if mode == "tab":
|
||||
display(Javascript(f"window.open('{dashboard_url}')"))
|
||||
elif mode == "jupyterlab":
|
||||
# Update front-end extension
|
||||
# FIXME valid only in jupyterlab but accepted in regular notebooks show nothing.
|
||||
_dash_comm.send(
|
||||
{
|
||||
"type": "show",
|
||||
"port": port,
|
||||
"url": dashboard_url,
|
||||
}
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def serve_alive():
|
||||
return "Alive"
|
||||
|
||||
def configure_callback_exception_handling(self, app, dev_tools_prune_errors):
|
||||
"""Install traceback handling for callbacks"""
|
||||
|
||||
@app.server.errorhandler(Exception)
|
||||
def _wrap_errors(error):
|
||||
# Compute number of stack frames to skip to get down to callback
|
||||
skip = _get_skip(error) if dev_tools_prune_errors else 0
|
||||
|
||||
# Customized formatargvalues function we can place function parameters
|
||||
# on separate lines
|
||||
original_formatargvalues = inspect.formatargvalues
|
||||
inspect.formatargvalues = _custom_formatargvalues
|
||||
try:
|
||||
# Use IPython traceback formatting to build the traceback string.
|
||||
ostream = io.StringIO()
|
||||
ipytb = FormattedTB(
|
||||
tb_offset=skip,
|
||||
mode="Verbose",
|
||||
color_scheme="NoColor",
|
||||
include_vars=True,
|
||||
ostream=ostream,
|
||||
)
|
||||
ipytb()
|
||||
finally:
|
||||
# Restore formatargvalues
|
||||
inspect.formatargvalues = original_formatargvalues
|
||||
|
||||
stacktrace = ostream.getvalue()
|
||||
|
||||
if self.inline_exceptions:
|
||||
print(stacktrace)
|
||||
|
||||
return stacktrace, 500
|
||||
|
||||
@property
|
||||
def active(self):
|
||||
_inside_dbx = "DATABRICKS_RUNTIME_VERSION" in os.environ
|
||||
return _dep_installed and not _inside_dbx and (self.in_ipython or self.in_colab)
|
||||
|
||||
|
||||
jupyter_dash = JupyterDash()
|
||||
@@ -0,0 +1,11 @@
|
||||
class NoUpdate:
|
||||
def to_plotly_json(self): # pylint: disable=no-self-use
|
||||
return {"_dash_no_update": "_dash_no_update"}
|
||||
|
||||
@staticmethod
|
||||
def is_no_update(obj):
|
||||
return (
|
||||
obj is NoUpdate
|
||||
or isinstance(obj, NoUpdate)
|
||||
or (isinstance(obj, dict) and obj == {"_dash_no_update": "_dash_no_update"})
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
# pylint: disable=too-few-public-methods
|
||||
from .exceptions import ObsoleteAttributeException
|
||||
|
||||
|
||||
class ObsoleteAttribute:
|
||||
def __init__(self, message: str, exc=ObsoleteAttributeException):
|
||||
self.message = message
|
||||
self.exc = exc
|
||||
|
||||
|
||||
class ObsoleteChecker:
|
||||
_obsolete_attributes = {
|
||||
"run_server": ObsoleteAttribute("app.run_server has been replaced by app.run"),
|
||||
"long_callback": ObsoleteAttribute(
|
||||
"app.long_callback has been removed, use app.callback(..., background=True) instead"
|
||||
),
|
||||
}
|
||||
|
||||
def __getattr__(self, name: str):
|
||||
if name in self._obsolete_attributes:
|
||||
err = self._obsolete_attributes[name]
|
||||
raise err.exc(err.message)
|
||||
return getattr(self.__dict__, name)
|
||||
@@ -0,0 +1,450 @@
|
||||
import collections
|
||||
import importlib
|
||||
import importlib.util # to make the type checker happy
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from fnmatch import fnmatch
|
||||
from os.path import isfile, join
|
||||
from pathlib import Path
|
||||
from urllib.parse import parse_qs
|
||||
|
||||
import flask
|
||||
|
||||
from . import _validate
|
||||
from ._callback_context import context_value
|
||||
from ._get_app import get_app
|
||||
from ._get_paths import get_relative_path
|
||||
from ._utils import AttributeDict
|
||||
|
||||
CONFIG = AttributeDict()
|
||||
PAGE_REGISTRY = collections.OrderedDict()
|
||||
|
||||
|
||||
def _infer_image(module):
|
||||
"""
|
||||
Return:
|
||||
- A page specific image: `assets/<module>.<extension>` is used, e.g. `assets/weekly_analytics.png`
|
||||
- A generic app image at `assets/app.<extension>`
|
||||
- A logo at `assets/logo.<extension>`
|
||||
"""
|
||||
assets_folder = CONFIG.assets_folder
|
||||
valid_extensions = ["apng", "avif", "gif", "jpeg", "jpg", "png", "svg", "webp"]
|
||||
page_id = module.split(".")[-1]
|
||||
files_in_assets = []
|
||||
|
||||
if os.path.exists(assets_folder):
|
||||
files_in_assets = [
|
||||
f for f in os.listdir(assets_folder) if isfile(join(assets_folder, f))
|
||||
]
|
||||
app_file = None
|
||||
logo_file = None
|
||||
for fn in files_in_assets:
|
||||
fn_without_extension, _, extension = fn.partition(".")
|
||||
if extension.lower() in valid_extensions:
|
||||
if (
|
||||
fn_without_extension == page_id
|
||||
or fn_without_extension == page_id.replace("_", "-")
|
||||
):
|
||||
return fn
|
||||
|
||||
if fn_without_extension == "app":
|
||||
app_file = fn
|
||||
|
||||
if fn_without_extension == "logo":
|
||||
logo_file = fn
|
||||
|
||||
if app_file:
|
||||
return app_file
|
||||
|
||||
return logo_file
|
||||
|
||||
|
||||
def _module_name_to_page_name(module_name):
|
||||
return module_name.split(".")[-1].replace("_", " ").capitalize()
|
||||
|
||||
|
||||
def _infer_path(module_name, template):
|
||||
if template is None:
|
||||
if CONFIG.pages_folder:
|
||||
pages_module = str(Path(CONFIG.pages_folder).name)
|
||||
path = (
|
||||
module_name.split(pages_module)[-1]
|
||||
.replace("_", "-")
|
||||
.replace(".", "/")
|
||||
.lower()
|
||||
)
|
||||
else:
|
||||
path = module_name.replace("_", "-").replace(".", "/").lower()
|
||||
else:
|
||||
# replace the variables in the template with "none" to create a default path if
|
||||
# no path is supplied
|
||||
path = re.sub("<.*?>", "none", template)
|
||||
path = "/" + path if not path.startswith("/") else path
|
||||
return path
|
||||
|
||||
|
||||
def _module_name_is_package(module_name):
|
||||
if module_name not in sys.modules:
|
||||
return False
|
||||
file = sys.modules[module_name].__file__
|
||||
return file and file.endswith("__init__.py")
|
||||
|
||||
|
||||
def _path_to_module_name(path):
|
||||
return str(path).replace(".py", "").strip(os.sep).replace(os.sep, ".")
|
||||
|
||||
|
||||
def _infer_module_name(page_path):
|
||||
relative_path = page_path.split(CONFIG.pages_folder)[-1]
|
||||
module = _path_to_module_name(relative_path)
|
||||
proj_root = flask.helpers.get_root_path(CONFIG.name)
|
||||
if CONFIG.pages_folder.startswith(proj_root):
|
||||
parent_path = CONFIG.pages_folder[len(proj_root) :]
|
||||
else:
|
||||
parent_path = CONFIG.pages_folder
|
||||
parent_module = _path_to_module_name(parent_path)
|
||||
|
||||
module_name = f"{parent_module}.{module}"
|
||||
if _module_name_is_package(CONFIG.name):
|
||||
# Only prefix with CONFIG.name when it's an imported package name
|
||||
module_name = f"{CONFIG.name}.{module_name}"
|
||||
return module_name
|
||||
|
||||
|
||||
def _parse_query_string(search):
|
||||
if not search or not search.startswith("?"):
|
||||
return {}
|
||||
|
||||
query_string = search[1:]
|
||||
|
||||
parsed_qs = parse_qs(query_string, keep_blank_values=True)
|
||||
|
||||
return {k: v[0] if len(v) == 1 else v for k, v in parsed_qs.items()}
|
||||
|
||||
|
||||
def _parse_path_variables(pathname, path_template):
|
||||
"""
|
||||
creates the dict of path variables passed to the layout
|
||||
e.g. path_template= "/asset/<asset_id>"
|
||||
if pathname provided by the browser is "/assets/a100"
|
||||
returns **{"asset_id": "a100"}
|
||||
"""
|
||||
|
||||
# parse variable definitions e.g. <var_name> from template
|
||||
# and create pattern to match
|
||||
wildcard_pattern = re.sub("<.*?>", "*", path_template)
|
||||
var_pattern = re.sub("<.*?>", "(.*)", path_template)
|
||||
|
||||
# check that static sections of the pathname match the template
|
||||
if not fnmatch(pathname, wildcard_pattern):
|
||||
return None
|
||||
|
||||
# parse variable names e.g. var_name from template
|
||||
var_names = re.findall("<(.*?)>", path_template)
|
||||
|
||||
# parse variables from path
|
||||
variables = re.findall(var_pattern, pathname)
|
||||
variables = variables[0] if isinstance(variables[0], tuple) else variables
|
||||
|
||||
return dict(zip(var_names, variables))
|
||||
|
||||
|
||||
def _create_redirect_function(redirect_to):
|
||||
def redirect():
|
||||
return flask.redirect(redirect_to, code=301)
|
||||
|
||||
return redirect
|
||||
|
||||
|
||||
def _set_redirect(redirect_from, path):
|
||||
app = get_app()
|
||||
if redirect_from and len(redirect_from):
|
||||
for redirect in redirect_from:
|
||||
fullname = app.get_relative_path(redirect)
|
||||
app.server.add_url_rule(
|
||||
fullname,
|
||||
fullname,
|
||||
_create_redirect_function(app.get_relative_path(path)),
|
||||
)
|
||||
|
||||
|
||||
def register_page(
|
||||
module,
|
||||
path=None,
|
||||
path_template=None,
|
||||
name=None,
|
||||
order=None,
|
||||
title=None,
|
||||
description=None,
|
||||
image=None,
|
||||
image_url=None,
|
||||
redirect_from=None,
|
||||
layout=None,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
Assigns the variables to `dash.page_registry` as an `OrderedDict`
|
||||
(ordered by `order`).
|
||||
|
||||
`dash.page_registry` is used by `pages_plugin` to set up the layouts as
|
||||
a multi-page Dash app. This includes the URL routing callbacks
|
||||
(using `dcc.Location`) and the HTML templates to include title,
|
||||
meta description, and the meta description image.
|
||||
|
||||
`dash.page_registry` can also be used by Dash developers to create the
|
||||
page navigation links or by template authors.
|
||||
|
||||
- `module`:
|
||||
The module path where this page's `layout` is defined. Often `__name__`.
|
||||
|
||||
- `path`:
|
||||
URL Path, e.g. `/` or `/home-page`.
|
||||
If not supplied, will be inferred from the `path_template` or `module`,
|
||||
e.g. based on path_template: `/asset/<asset_id` to `/asset/none`
|
||||
e.g. based on module: `pages.weekly_analytics` to `/weekly-analytics`
|
||||
|
||||
- `relative_path`:
|
||||
The path with `requests_pathname_prefix` prefixed before it.
|
||||
Use this path when specifying local URL paths that will work
|
||||
in environments regardless of what `requests_pathname_prefix` is.
|
||||
In some deployment environments, like Dash Enterprise,
|
||||
`requests_pathname_prefix` is set to the application name,
|
||||
e.g. `my-dash-app`.
|
||||
When working locally, `requests_pathname_prefix` might be unset and
|
||||
so a relative URL like `/page-2` can just be `/page-2`.
|
||||
However, when the app is deployed to a URL like `/my-dash-app`, then
|
||||
`relative_path` will be `/my-dash-app/page-2`.
|
||||
|
||||
- `path_template`:
|
||||
Add variables to a URL by marking sections with <variable_name>. The layout function
|
||||
then receives the <variable_name> as a keyword argument.
|
||||
e.g. path_template= "/asset/<asset_id>"
|
||||
then if pathname in browser is "/assets/a100" then layout will receive **{"asset_id":"a100"}
|
||||
|
||||
- `name`:
|
||||
The name of the link.
|
||||
If not supplied, will be inferred from `module`,
|
||||
e.g. `pages.weekly_analytics` to `Weekly analytics`
|
||||
|
||||
- `order`:
|
||||
The order of the pages in `page_registry`.
|
||||
If not supplied, then the filename is used and the page with path `/` has
|
||||
order `0`
|
||||
|
||||
- `title`:
|
||||
(string or function) Specifies the page title displayed in the browser tab.
|
||||
If not supplied, the app's title is used if different from the default "Dash".
|
||||
Otherwise, the title is the given `name` or inferred from the module name.
|
||||
For example, `pages.weekly_analytics` is inferred as "Weekly Analytics".
|
||||
|
||||
- `description`:
|
||||
(string or function) The <meta type="description"></meta>.
|
||||
If not defined, the application description will be used if available.
|
||||
|
||||
- `image`:
|
||||
The meta description image used by social media platforms.
|
||||
If not supplied, then it looks for the following images in `assets/`:
|
||||
- A page specific image: `assets/<module>.<extension>` is used, e.g. `assets/weekly_analytics.png`
|
||||
- A generic app image at `assets/app.<extension>`
|
||||
- A logo at `assets/logo.<extension>`
|
||||
When inferring the image file, it will look for the following extensions:
|
||||
APNG, AVIF, GIF, JPEG, JPG, PNG, SVG, WebP.
|
||||
|
||||
- `image_url`:
|
||||
Overrides the image property and sets the `<image>` meta tag to the provided image URL.
|
||||
|
||||
- `redirect_from`:
|
||||
A list of paths that should redirect to this page.
|
||||
For example: `redirect_from=['/v2', '/v3']`
|
||||
|
||||
- `layout`:
|
||||
The layout function or component for this page.
|
||||
If not supplied, then looks for `layout` from within the supplied `module`.
|
||||
|
||||
- `**kwargs`:
|
||||
Arbitrary keyword arguments that can be stored
|
||||
|
||||
***
|
||||
|
||||
`page_registry` stores the original property that was passed in under
|
||||
`supplied_<property>` and the coerced property under `<property>`.
|
||||
For example, if this was called:
|
||||
```
|
||||
register_page(
|
||||
'pages.historical_outlook',
|
||||
name='Our historical view',
|
||||
custom_key='custom value'
|
||||
)
|
||||
```
|
||||
Then this will appear in `page_registry`:
|
||||
```
|
||||
OrderedDict([
|
||||
(
|
||||
'pages.historical_outlook',
|
||||
dict(
|
||||
module='pages.historical_outlook',
|
||||
|
||||
supplied_path=None,
|
||||
path='/historical-outlook',
|
||||
|
||||
supplied_name='Our historical view',
|
||||
name='Our historical view',
|
||||
|
||||
supplied_title=None,
|
||||
title='Our historical view'
|
||||
|
||||
supplied_layout=None,
|
||||
layout=<function pages.historical_outlook.layout>,
|
||||
|
||||
custom_key='custom value'
|
||||
)
|
||||
),
|
||||
])
|
||||
```
|
||||
"""
|
||||
if context_value.get().get("ignore_register_page"):
|
||||
return
|
||||
|
||||
_validate.validate_use_pages(CONFIG)
|
||||
|
||||
page = dict(
|
||||
module=_validate.validate_module_name(module),
|
||||
supplied_path=path,
|
||||
path_template=path_template,
|
||||
path=path if path is not None else _infer_path(module, path_template),
|
||||
supplied_name=name,
|
||||
name=name if name is not None else _module_name_to_page_name(module),
|
||||
)
|
||||
page.update(
|
||||
supplied_title=title,
|
||||
title=title
|
||||
if title is not None
|
||||
else CONFIG.title
|
||||
if CONFIG.title != "Dash"
|
||||
else page["name"],
|
||||
)
|
||||
page.update(
|
||||
description=description
|
||||
if description
|
||||
else CONFIG.description
|
||||
if CONFIG.description
|
||||
else "",
|
||||
order=order,
|
||||
supplied_order=order,
|
||||
supplied_layout=layout,
|
||||
**kwargs,
|
||||
)
|
||||
page.update(
|
||||
supplied_image=image,
|
||||
image=(image if image is not None else _infer_image(module)),
|
||||
image_url=image_url,
|
||||
)
|
||||
page.update(redirect_from=_set_redirect(redirect_from, page["path"]))
|
||||
|
||||
PAGE_REGISTRY[module] = page
|
||||
|
||||
if page["path_template"]:
|
||||
_validate.validate_template(page["path_template"])
|
||||
|
||||
if layout is not None:
|
||||
# Override the layout found in the file set during `plug`
|
||||
PAGE_REGISTRY[module]["layout"] = layout
|
||||
|
||||
# set home page order
|
||||
order_supplied = any(
|
||||
p["supplied_order"] is not None for p in PAGE_REGISTRY.values()
|
||||
)
|
||||
|
||||
for p in PAGE_REGISTRY.values():
|
||||
p["order"] = (
|
||||
0 if p["path"] == "/" and not order_supplied else p["supplied_order"]
|
||||
)
|
||||
p["relative_path"] = get_relative_path(p["path"])
|
||||
|
||||
# Sort numeric orders first, then string orders, then no order,
|
||||
# finally by module name for matching orders
|
||||
for page in sorted(
|
||||
PAGE_REGISTRY.values(),
|
||||
key=lambda i: (
|
||||
i["order"] is None, # False (order given) sorts before True
|
||||
i["order"] if isinstance(i["order"], (int, float)) else float("inf"),
|
||||
str(i["order"]),
|
||||
i["module"],
|
||||
),
|
||||
):
|
||||
PAGE_REGISTRY.move_to_end(page["module"])
|
||||
|
||||
|
||||
def _path_to_page(path_id):
|
||||
path_variables = None
|
||||
for page in PAGE_REGISTRY.values():
|
||||
if page["path_template"]:
|
||||
template_id = page["path_template"].strip("/")
|
||||
path_variables = _parse_path_variables(path_id, template_id)
|
||||
if path_variables:
|
||||
return page, path_variables
|
||||
if path_id == page["path"].strip("/"):
|
||||
return page, path_variables
|
||||
return {}, None
|
||||
|
||||
|
||||
def _page_meta_tags(app):
|
||||
start_page, path_variables = _path_to_page(flask.request.path.strip("/"))
|
||||
|
||||
# use the supplied image_url or create url based on image in the assets folder
|
||||
image = start_page.get("image", "")
|
||||
if image:
|
||||
image = app.get_asset_url(image)
|
||||
assets_image_url = (
|
||||
"".join([flask.request.url_root, image.lstrip("/")]) if image else None
|
||||
)
|
||||
supplied_image_url = start_page.get("image_url")
|
||||
image_url = supplied_image_url if supplied_image_url else assets_image_url
|
||||
|
||||
title = start_page.get("title", app.title)
|
||||
if callable(title):
|
||||
title = title(**path_variables) if path_variables else title()
|
||||
|
||||
description = start_page.get("description", "")
|
||||
if callable(description):
|
||||
description = description(**path_variables) if path_variables else description()
|
||||
|
||||
return [
|
||||
{"name": "description", "content": description},
|
||||
{"property": "twitter:card", "content": "summary_large_image"},
|
||||
{"property": "twitter:url", "content": flask.request.url},
|
||||
{"property": "twitter:title", "content": title},
|
||||
{"property": "twitter:description", "content": description},
|
||||
{"property": "twitter:image", "content": image_url or ""},
|
||||
{"property": "og:title", "content": title},
|
||||
{"property": "og:type", "content": "website"},
|
||||
{"property": "og:description", "content": description},
|
||||
{"property": "og:image", "content": image_url or ""},
|
||||
]
|
||||
|
||||
|
||||
def _import_layouts_from_pages(pages_folder):
|
||||
for root, dirs, files in os.walk(pages_folder):
|
||||
dirs[:] = [d for d in dirs if not d.startswith(".") and not d.startswith("_")]
|
||||
for file in files:
|
||||
if file.startswith("_") or file.startswith(".") or not file.endswith(".py"):
|
||||
continue
|
||||
page_path = os.path.join(root, file)
|
||||
with open(page_path, encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
if "register_page" not in content:
|
||||
continue
|
||||
|
||||
module_name = _infer_module_name(page_path)
|
||||
spec = importlib.util.spec_from_file_location(module_name, page_path)
|
||||
page_module = importlib.util.module_from_spec(spec) # type: ignore[reportArgumentType]
|
||||
spec.loader.exec_module(page_module) # type: ignore[reportOptionalMemberAccess]
|
||||
sys.modules[module_name] = page_module
|
||||
|
||||
if (
|
||||
module_name in PAGE_REGISTRY
|
||||
and not PAGE_REGISTRY[module_name]["supplied_layout"]
|
||||
):
|
||||
_validate.validate_pages_layout(module_name, page_module)
|
||||
PAGE_REGISTRY[module_name]["layout"] = getattr(page_module, "layout")
|
||||
@@ -0,0 +1,175 @@
|
||||
from typing import List, Union, Optional, Any
|
||||
|
||||
|
||||
def _operation(name, location, **kwargs):
|
||||
return {"operation": name, "location": location, "params": kwargs}
|
||||
|
||||
|
||||
_noop = object()
|
||||
|
||||
_KeyType = Union[str, int]
|
||||
|
||||
|
||||
def validate_slice(obj: Any):
|
||||
if isinstance(obj, slice):
|
||||
raise TypeError("a slice is not a valid index for patch")
|
||||
|
||||
|
||||
class Patch:
|
||||
"""
|
||||
Patch a callback output value
|
||||
|
||||
Act like a proxy of the output prop value on the frontend.
|
||||
|
||||
Supported prop types: Dictionaries and lists.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
location: Optional[List[_KeyType]] = None,
|
||||
parent: Optional["Patch"] = None,
|
||||
):
|
||||
if location is not None:
|
||||
self._location = location
|
||||
else:
|
||||
# pylint: disable=consider-using-ternary
|
||||
self._location = (parent and parent._location) or []
|
||||
if parent is not None:
|
||||
self._operations = parent._operations
|
||||
else:
|
||||
self._operations = []
|
||||
|
||||
def __getstate__(self):
|
||||
return vars(self)
|
||||
|
||||
def __setstate__(self, state):
|
||||
vars(self).update(state)
|
||||
|
||||
def __getitem__(self, item: _KeyType) -> "Patch":
|
||||
validate_slice(item)
|
||||
return Patch(location=self._location + [item], parent=self)
|
||||
|
||||
def __getattr__(self, item: _KeyType) -> "Patch":
|
||||
if item == "tolist":
|
||||
# to_json fix
|
||||
raise AttributeError
|
||||
if item == "_location":
|
||||
return self._location # type: ignore
|
||||
if item == "_operations":
|
||||
return self._operations # type: ignore
|
||||
return self.__getitem__(item)
|
||||
|
||||
def __setattr__(self, key: _KeyType, value: Any):
|
||||
if key in ("_location", "_operations"):
|
||||
self.__dict__[key] = value
|
||||
else:
|
||||
self.__setitem__(key, value)
|
||||
|
||||
def __delattr__(self, item: _KeyType):
|
||||
self.__delitem__(item)
|
||||
|
||||
def __setitem__(self, key: _KeyType, value: Any):
|
||||
validate_slice(key)
|
||||
if value is _noop:
|
||||
# The += set themselves.
|
||||
return
|
||||
self._operations.append(
|
||||
_operation(
|
||||
"Assign",
|
||||
self._location + [key],
|
||||
value=value,
|
||||
)
|
||||
)
|
||||
|
||||
def __delitem__(self, key: _KeyType):
|
||||
validate_slice(key)
|
||||
self._operations.append(_operation("Delete", self._location + [key]))
|
||||
|
||||
def __iadd__(self, other: Any):
|
||||
if isinstance(other, (list, tuple)):
|
||||
self.extend(other)
|
||||
else:
|
||||
self._operations.append(_operation("Add", self._location, value=other))
|
||||
if not self._location:
|
||||
return self
|
||||
return _noop
|
||||
|
||||
def __isub__(self, other: Any):
|
||||
self._operations.append(_operation("Sub", self._location, value=other))
|
||||
if not self._location:
|
||||
return self
|
||||
return _noop
|
||||
|
||||
def __imul__(self, other: Any) -> "Patch":
|
||||
self._operations.append(_operation("Mul", self._location, value=other))
|
||||
if not self._location:
|
||||
return self
|
||||
return _noop
|
||||
|
||||
def __itruediv__(self, other: Any):
|
||||
self._operations.append(_operation("Div", self._location, value=other))
|
||||
if not self._location:
|
||||
return self
|
||||
return _noop
|
||||
|
||||
def __ior__(self, other: Any):
|
||||
self.update(E=other)
|
||||
if not self._location:
|
||||
return self
|
||||
return _noop
|
||||
|
||||
def __iter__(self):
|
||||
raise TypeError("Patch objects are write-only, you cannot iterate them.")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<write-only dash.Patch object at {self._location}>"
|
||||
|
||||
def append(self, item: Any) -> None:
|
||||
"""Add the item to the end of a list"""
|
||||
self._operations.append(_operation("Append", self._location, value=item))
|
||||
|
||||
def prepend(self, item: Any) -> None:
|
||||
"""Add the item to the start of a list"""
|
||||
self._operations.append(_operation("Prepend", self._location, value=item))
|
||||
|
||||
def insert(self, index: int, item: Any) -> None:
|
||||
"""Add the item at the index of a list"""
|
||||
self._operations.append(
|
||||
_operation("Insert", self._location, value=item, index=index)
|
||||
)
|
||||
|
||||
def clear(self) -> None:
|
||||
"""Remove all items in a list"""
|
||||
self._operations.append(_operation("Clear", self._location))
|
||||
|
||||
def reverse(self) -> None:
|
||||
"""Reversal of the order of items in a list"""
|
||||
self._operations.append(_operation("Reverse", self._location))
|
||||
|
||||
def extend(self, item: Union[list, tuple]) -> None:
|
||||
"""Add all the items to the end of a list"""
|
||||
if not isinstance(item, (list, tuple)):
|
||||
raise TypeError(f"{item} should be a list or tuple")
|
||||
self._operations.append(_operation("Extend", self._location, value=item))
|
||||
|
||||
def remove(self, item: Any) -> None:
|
||||
"""filter the item out of a list on the frontend"""
|
||||
self._operations.append(_operation("Remove", self._location, value=item))
|
||||
|
||||
def update(self, E: Any = None, **F) -> None:
|
||||
"""Merge a dict or keyword arguments with another dictionary"""
|
||||
value = E or {}
|
||||
value.update(F)
|
||||
self._operations.append(_operation("Merge", self._location, value=value))
|
||||
|
||||
# pylint: disable=no-self-use
|
||||
def sort(self):
|
||||
raise KeyError(
|
||||
"sort is reserved for future use, use brackets to access this key on your object"
|
||||
)
|
||||
|
||||
def to_plotly_json(self) -> Any:
|
||||
return {
|
||||
"__dash_patch_update": "__dash_patch_update",
|
||||
"operations": self._operations,
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import shlex
|
||||
import sys
|
||||
import uuid
|
||||
import hashlib
|
||||
from collections import abc
|
||||
import subprocess
|
||||
import logging
|
||||
import io
|
||||
import json
|
||||
import secrets
|
||||
import string
|
||||
import inspect
|
||||
import re
|
||||
|
||||
from html import escape
|
||||
from functools import wraps
|
||||
from typing import Union
|
||||
from .types import RendererHooks
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
|
||||
def to_json(value):
|
||||
# pylint: disable=import-outside-toplevel
|
||||
from plotly.io.json import to_json_plotly
|
||||
|
||||
return to_json_plotly(value)
|
||||
|
||||
|
||||
def interpolate_str(template, **data):
|
||||
s = template
|
||||
for k, v in data.items():
|
||||
key = "{%" + k + "%}"
|
||||
s = s.replace(key, v)
|
||||
return s
|
||||
|
||||
|
||||
def format_tag(
|
||||
tag_name, attributes, inner="", closed=False, opened=False, sanitize=False
|
||||
):
|
||||
attributes = " ".join(
|
||||
[f'{k}="{escape(v) if sanitize else v}"' for k, v in attributes.items()]
|
||||
)
|
||||
tag = f"<{tag_name} {attributes}"
|
||||
if closed:
|
||||
tag += "/>"
|
||||
elif opened:
|
||||
tag += ">"
|
||||
else:
|
||||
tag += ">" + inner + f"</{tag_name}>"
|
||||
return tag
|
||||
|
||||
|
||||
def generate_hash():
|
||||
return str(uuid.uuid4().hex).strip("-")
|
||||
|
||||
|
||||
# pylint: disable=no-member
|
||||
def patch_collections_abc(member):
|
||||
return getattr(abc, member)
|
||||
|
||||
|
||||
class AttributeDict(dict):
|
||||
"""Dictionary subclass enabling attribute lookup/assignment of keys/values.
|
||||
|
||||
For example::
|
||||
>>> m = AttributeDict({'foo': 'bar'})
|
||||
>>> m.foo
|
||||
'bar'
|
||||
>>> m.foo = 'not bar'
|
||||
>>> m['foo']
|
||||
'not bar'
|
||||
``AttributeDict`` objects also provide ``.first()`` which acts like
|
||||
``.get()`` but accepts multiple keys as arguments, and returns the value of
|
||||
the first hit, e.g.::
|
||||
>>> m = AttributeDict({'foo': 'bar', 'biz': 'baz'})
|
||||
>>> m.first('wrong', 'incorrect', 'foo', 'biz')
|
||||
'bar'
|
||||
"""
|
||||
|
||||
def __setattr__(self, key, value):
|
||||
self[key] = value
|
||||
|
||||
def __getattr__(self, key):
|
||||
try:
|
||||
return self[key]
|
||||
except KeyError:
|
||||
pass
|
||||
# to conform with __getattr__ spec
|
||||
# but get out of the except block so it doesn't look like a nested err
|
||||
raise AttributeError(key)
|
||||
|
||||
def set_read_only(self, names, msg="Attribute is read-only"):
|
||||
"""
|
||||
Designate named attributes as read-only with the corresponding msg
|
||||
|
||||
Method is additive. Making additional calls to this method will update
|
||||
existing messages and add to the current set of _read_only names.
|
||||
"""
|
||||
new_read_only = {name: msg for name in names}
|
||||
if getattr(self, "_read_only", False):
|
||||
self._read_only.update(new_read_only)
|
||||
else:
|
||||
object.__setattr__(self, "_read_only", new_read_only)
|
||||
|
||||
def finalize(self, msg="Object is final: No new keys may be added."):
|
||||
"""Prevent any new keys being set."""
|
||||
object.__setattr__(self, "_final", msg)
|
||||
|
||||
def __setitem__(self, key, val):
|
||||
if key in self.__dict__.get("_read_only", {}):
|
||||
raise AttributeError(self._read_only[key], key)
|
||||
|
||||
final_msg = self.__dict__.get("_final")
|
||||
if final_msg and key not in self:
|
||||
raise AttributeError(final_msg, key)
|
||||
|
||||
return super().__setitem__(key, val)
|
||||
|
||||
def update(self, other=None, **kwargs):
|
||||
# Overrides dict.update() to use __setitem__ above
|
||||
# Needs default `None` and `kwargs` to satisfy type checking
|
||||
source = other if other is not None else kwargs
|
||||
for k, v in source.items():
|
||||
self[k] = v
|
||||
|
||||
# pylint: disable=inconsistent-return-statements
|
||||
def first(self, *names):
|
||||
for name in names:
|
||||
value = self.get(name)
|
||||
if value:
|
||||
return value
|
||||
if not names:
|
||||
return next(iter(self), {})
|
||||
|
||||
|
||||
def create_callback_id(output, inputs, no_output=False):
|
||||
# A single dot within a dict id key or value is OK
|
||||
# but in case of multiple dots together escape each dot
|
||||
# with `\` so we don't mistake it for multi-outputs
|
||||
hashed_inputs = None
|
||||
|
||||
def _hash_inputs():
|
||||
return hashlib.sha256(
|
||||
".".join(str(x) for x in inputs).encode("utf-8")
|
||||
).hexdigest()
|
||||
|
||||
def _concat(x):
|
||||
nonlocal hashed_inputs
|
||||
_id = x.component_id_str().replace(".", "\\.") + "." + x.component_property
|
||||
if x.allow_duplicate:
|
||||
if not hashed_inputs:
|
||||
hashed_inputs = _hash_inputs()
|
||||
# Actually adds on the property part.
|
||||
_id += f"@{hashed_inputs}"
|
||||
return _id
|
||||
|
||||
if no_output:
|
||||
# No output will hash the inputs.
|
||||
return _hash_inputs()
|
||||
|
||||
if isinstance(output, (list, tuple)):
|
||||
return ".." + "...".join(_concat(x) for x in output) + ".."
|
||||
|
||||
return _concat(output)
|
||||
|
||||
|
||||
# inverse of create_callback_id - should only be relevant if an old renderer is
|
||||
# hooked up to a new back end, which will only happen in special cases like
|
||||
# embedded
|
||||
def split_callback_id(callback_id):
|
||||
if callback_id.startswith(".."):
|
||||
return [split_callback_id(oi) for oi in callback_id[2:-2].split("...")]
|
||||
|
||||
id_, prop = callback_id.rsplit(".", 1)
|
||||
return {"id": id_, "property": prop}
|
||||
|
||||
|
||||
def stringify_id(id_) -> str:
|
||||
def _json(k, v):
|
||||
vstr = v.to_json() if hasattr(v, "to_json") else json.dumps(v)
|
||||
return f"{json.dumps(k)}:{vstr}"
|
||||
|
||||
if isinstance(id_, dict):
|
||||
return "{" + ",".join(_json(k, id_[k]) for k in sorted(id_)) + "}"
|
||||
return id_
|
||||
|
||||
|
||||
def inputs_to_dict(inputs_list):
|
||||
inputs = AttributeDict()
|
||||
for i in inputs_list:
|
||||
inputsi = i if isinstance(i, list) else [i]
|
||||
for ii in inputsi:
|
||||
id_str = stringify_id(ii["id"])
|
||||
inputs[f'{id_str}.{ii["property"]}'] = ii.get("value")
|
||||
return inputs
|
||||
|
||||
|
||||
def convert_to_AttributeDict(nested_list):
|
||||
new_dict = []
|
||||
for i in nested_list:
|
||||
if isinstance(i, dict):
|
||||
new_dict.append(AttributeDict(i))
|
||||
else:
|
||||
new_dict.append([AttributeDict(ii) for ii in i])
|
||||
return new_dict
|
||||
|
||||
|
||||
def inputs_to_vals(inputs):
|
||||
return [
|
||||
[ii.get("value") for ii in i] if isinstance(i, list) else i.get("value")
|
||||
for i in inputs
|
||||
]
|
||||
|
||||
|
||||
def run_command_with_process(cmd):
|
||||
is_win = sys.platform == "win32"
|
||||
with subprocess.Popen(shlex.split(cmd, posix=is_win), shell=is_win) as proc:
|
||||
proc.wait()
|
||||
if proc.poll() is None:
|
||||
logger.warning("🚨 trying to terminate subprocess in safe way")
|
||||
try:
|
||||
proc.communicate()
|
||||
except Exception: # pylint: disable=broad-except
|
||||
logger.exception("🚨 first try communicate failed")
|
||||
proc.kill()
|
||||
proc.communicate()
|
||||
|
||||
|
||||
def compute_hash(path):
|
||||
with io.open(path, encoding="utf-8") as fp:
|
||||
return hashlib.sha256(fp.read().encode("utf-8")).hexdigest()
|
||||
|
||||
|
||||
def job(msg=""):
|
||||
def wrapper(func):
|
||||
@wraps(func)
|
||||
def _wrapper(*args, **kwargs):
|
||||
logger.info("🏗️ [%s] 🏗️️ - %s", func.__name__, msg)
|
||||
res = func(*args, **kwargs)
|
||||
logger.info("::: 🍻🍻🍻 [%s] job done 🍻🍻🍻 :::", func.__name__)
|
||||
return res
|
||||
|
||||
return _wrapper
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def gen_salt(chars):
|
||||
return "".join(
|
||||
secrets.choice(string.ascii_letters + string.digits) for _ in range(chars)
|
||||
)
|
||||
|
||||
|
||||
class OrderedSet(abc.MutableSet):
|
||||
def __init__(self, *args):
|
||||
self._data = []
|
||||
for i in args:
|
||||
self.add(i)
|
||||
|
||||
def add(self, value):
|
||||
if value not in self._data:
|
||||
self._data.append(value)
|
||||
|
||||
def discard(self, value):
|
||||
self._data.remove(value)
|
||||
|
||||
def __contains__(self, x):
|
||||
return x in self._data
|
||||
|
||||
def __len__(self):
|
||||
return len(self._data)
|
||||
|
||||
def __iter__(self):
|
||||
for i in self._data:
|
||||
yield i
|
||||
|
||||
|
||||
def coerce_to_list(obj):
|
||||
if not isinstance(obj, (list, tuple)):
|
||||
return [obj]
|
||||
return obj
|
||||
|
||||
|
||||
def clean_property_name(name: str):
|
||||
return name.split("@")[0]
|
||||
|
||||
|
||||
def hooks_to_js_object(hooks: Union[RendererHooks, None]) -> str:
|
||||
if hooks is None:
|
||||
return ""
|
||||
hook_str = ",".join(f"{key}: {val}" for key, val in hooks.items())
|
||||
|
||||
return f"{{{hook_str}}}"
|
||||
|
||||
|
||||
def parse_version(version):
|
||||
return tuple(int(s) for s in version.split("."))
|
||||
|
||||
|
||||
def get_caller_name():
|
||||
stack = inspect.stack()
|
||||
for s in stack:
|
||||
if s.function == "<module>":
|
||||
return s.frame.f_locals.get("__name__", "__main__")
|
||||
|
||||
return "__main__"
|
||||
|
||||
|
||||
def pascal_case(name: Union[str, None]):
|
||||
s = re.sub(r"\s", "_", str(name))
|
||||
# Replace leading `_`
|
||||
s = re.sub("^[_]+", "", s)
|
||||
if not s:
|
||||
return s
|
||||
return s[0].upper() + re.sub(
|
||||
r"[\-_\.]+([a-z])", lambda match: match.group(1).upper(), s[1:]
|
||||
)
|
||||
@@ -0,0 +1,587 @@
|
||||
import sys
|
||||
from collections.abc import MutableSequence
|
||||
import re
|
||||
from textwrap import dedent
|
||||
from keyword import iskeyword
|
||||
import flask
|
||||
|
||||
from ._grouping import grouping_len, map_grouping
|
||||
from ._no_update import NoUpdate
|
||||
from .development.base_component import Component
|
||||
from . import exceptions
|
||||
from ._utils import (
|
||||
patch_collections_abc,
|
||||
stringify_id,
|
||||
to_json,
|
||||
coerce_to_list,
|
||||
clean_property_name,
|
||||
)
|
||||
|
||||
|
||||
def validate_callback(outputs, inputs, state, extra_args, types):
|
||||
Input, Output, State = types
|
||||
if extra_args:
|
||||
if not isinstance(extra_args[0], (Output, Input, State)):
|
||||
raise exceptions.IncorrectTypeException(
|
||||
dedent(
|
||||
f"""
|
||||
Callback arguments must be `Output`, `Input`, or `State` objects,
|
||||
optionally wrapped in a list or tuple. We found (possibly after
|
||||
unwrapping a list or tuple):
|
||||
{repr(extra_args[0])}
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
raise exceptions.IncorrectTypeException(
|
||||
dedent(
|
||||
f"""
|
||||
In a callback definition, you must provide all Outputs first,
|
||||
then all Inputs, then all States. After this item:
|
||||
{(outputs + inputs + state)[-1]!r}
|
||||
we found this item next:
|
||||
{extra_args[0]!r}
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
for args in [outputs, inputs, state]:
|
||||
for arg in args:
|
||||
validate_callback_arg(arg)
|
||||
|
||||
|
||||
def validate_callback_arg(arg):
|
||||
if not isinstance(getattr(arg, "component_property", None), str):
|
||||
raise exceptions.IncorrectTypeException(
|
||||
dedent(
|
||||
f"""
|
||||
component_property must be a string, found {arg.component_property!r}
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
if hasattr(arg, "component_event"):
|
||||
raise exceptions.NonExistentEventException(
|
||||
"""
|
||||
Events have been removed.
|
||||
Use the associated property instead.
|
||||
"""
|
||||
)
|
||||
|
||||
if isinstance(arg.component_id, dict):
|
||||
validate_id_dict(arg)
|
||||
|
||||
elif isinstance(arg.component_id, str):
|
||||
validate_id_string(arg)
|
||||
|
||||
else:
|
||||
raise exceptions.IncorrectTypeException(
|
||||
dedent(
|
||||
f"""
|
||||
component_id must be a string or dict, found {arg.component_id!r}
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def validate_id_dict(arg):
|
||||
arg_id = arg.component_id
|
||||
|
||||
for k in arg_id:
|
||||
# Need to keep key type validation on the Python side, since
|
||||
# non-string keys will be converted to strings in json.dumps and may
|
||||
# cause unwanted collisions
|
||||
if not isinstance(k, str):
|
||||
raise exceptions.IncorrectTypeException(
|
||||
dedent(
|
||||
f"""
|
||||
Wildcard ID keys must be non-empty strings,
|
||||
found {k!r} in id {arg_id!r}
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def validate_id_string(arg):
|
||||
arg_id = arg.component_id
|
||||
|
||||
invalid_chars = ".{"
|
||||
invalid_found = [x for x in invalid_chars if x in arg_id]
|
||||
if invalid_found:
|
||||
raise exceptions.InvalidComponentIdError(
|
||||
f"""
|
||||
The element `{arg_id}` contains `{"`, `".join(invalid_found)}` in its ID.
|
||||
Characters `{"`, `".join(invalid_chars)}` are not allowed in IDs.
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def validate_output_spec(output, output_spec, Output):
|
||||
"""
|
||||
This validation is for security and internal debugging, not for users,
|
||||
so the messages are not intended to be clear.
|
||||
`output` comes from the callback definition, `output_spec` from the request.
|
||||
"""
|
||||
if not isinstance(output, (list, tuple)):
|
||||
output, output_spec = [output], [output_spec]
|
||||
elif len(output) != len(output_spec):
|
||||
raise exceptions.CallbackException("Wrong length output_spec")
|
||||
|
||||
for outi, speci in zip(output, output_spec):
|
||||
speci_list = speci if isinstance(speci, (list, tuple)) else [speci]
|
||||
for specij in speci_list:
|
||||
if (
|
||||
not Output(specij["id"], clean_property_name(specij["property"]))
|
||||
== outi
|
||||
):
|
||||
raise exceptions.CallbackException(
|
||||
"Output does not match callback definition"
|
||||
)
|
||||
|
||||
|
||||
def validate_and_group_input_args(flat_args, arg_index_grouping):
|
||||
if grouping_len(arg_index_grouping) != len(flat_args):
|
||||
raise exceptions.CallbackException("Inputs do not match callback definition")
|
||||
|
||||
args_grouping = map_grouping(lambda ind: flat_args[ind], arg_index_grouping)
|
||||
if isinstance(arg_index_grouping, dict):
|
||||
func_args = []
|
||||
func_kwargs = args_grouping
|
||||
for key in func_kwargs:
|
||||
if not key.isidentifier():
|
||||
raise exceptions.CallbackException(
|
||||
f"{key} is not a valid Python variable name"
|
||||
)
|
||||
elif isinstance(arg_index_grouping, (tuple, list)):
|
||||
func_args = list(args_grouping)
|
||||
func_kwargs = {}
|
||||
else:
|
||||
# Scalar input
|
||||
func_args = [args_grouping]
|
||||
func_kwargs = {}
|
||||
|
||||
return func_args, func_kwargs
|
||||
|
||||
|
||||
def validate_multi_return(output_lists, output_values, callback_id):
|
||||
if not isinstance(output_values, (list, tuple)):
|
||||
raise exceptions.InvalidCallbackReturnValue(
|
||||
dedent(
|
||||
f"""
|
||||
The callback {callback_id} is a multi-output.
|
||||
Expected the output type to be a list or tuple but got:
|
||||
{output_values!r}.
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
if len(output_values) != len(output_lists):
|
||||
raise exceptions.InvalidCallbackReturnValue(
|
||||
f"""
|
||||
Invalid number of output values for {callback_id}.
|
||||
Expected {len(output_lists)}, got {len(output_values)}
|
||||
"""
|
||||
)
|
||||
|
||||
for i, output_spec in enumerate(output_lists):
|
||||
if isinstance(output_spec, list):
|
||||
output_value = output_values[i]
|
||||
if not isinstance(output_value, (list, tuple)):
|
||||
raise exceptions.InvalidCallbackReturnValue(
|
||||
dedent(
|
||||
f"""
|
||||
The callback {callback_id} output {i} is a wildcard multi-output.
|
||||
Expected the output type to be a list or tuple but got:
|
||||
{output_value!r}.
|
||||
output spec: {output_spec!r}
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
if len(output_value) != len(output_spec):
|
||||
raise exceptions.InvalidCallbackReturnValue(
|
||||
dedent(
|
||||
f"""
|
||||
Invalid number of output values for {callback_id} item {i}.
|
||||
Expected {len(output_spec)}, got {len(output_value)}
|
||||
output spec: {output_spec!r}
|
||||
output value: {output_value!r}
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def fail_callback_output(output_value, output):
|
||||
valid_children = (str, int, float, type(None), Component, NoUpdate)
|
||||
valid_props = (str, int, float, type(None), tuple, MutableSequence, NoUpdate)
|
||||
|
||||
def _raise_invalid(bad_val, outer_val, path, index=None, toplevel=False):
|
||||
bad_type = type(bad_val).__name__
|
||||
outer_id = f"(id={outer_val.id:s})" if getattr(outer_val, "id", False) else ""
|
||||
outer_type = type(outer_val).__name__
|
||||
if toplevel:
|
||||
location = dedent(
|
||||
"""
|
||||
The value in question is either the only value returned,
|
||||
or is in the top level of the returned list,
|
||||
"""
|
||||
)
|
||||
else:
|
||||
index_string = "[*]" if index is None else f"[{index:d}]"
|
||||
location = dedent(
|
||||
f"""
|
||||
The value in question is located at
|
||||
{index_string} {outer_type} {outer_id}
|
||||
{path},
|
||||
"""
|
||||
)
|
||||
|
||||
obj = "tree with one value" if not toplevel else "value"
|
||||
raise exceptions.InvalidCallbackReturnValue(
|
||||
dedent(
|
||||
f"""
|
||||
The callback for `{output!r}`
|
||||
returned a {obj:s} having type `{bad_type}`
|
||||
which is not JSON serializable.
|
||||
|
||||
{location}
|
||||
and has string representation
|
||||
`{bad_val}`
|
||||
|
||||
In general, Dash properties can only be
|
||||
dash components, strings, dictionaries, numbers, None,
|
||||
or lists of those.
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
def _valid_child(val):
|
||||
return isinstance(val, valid_children)
|
||||
|
||||
def _valid_prop(val):
|
||||
return isinstance(val, valid_props)
|
||||
|
||||
def _can_serialize(val):
|
||||
if not (_valid_child(val) or _valid_prop(val)):
|
||||
return False
|
||||
try:
|
||||
to_json(val)
|
||||
except TypeError:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _validate_value(val, index=None):
|
||||
# val is a Component
|
||||
if isinstance(val, Component):
|
||||
unserializable_items = []
|
||||
# pylint: disable=protected-access
|
||||
for p, j in val._traverse_with_paths():
|
||||
# check each component value in the tree
|
||||
if not _valid_child(j):
|
||||
_raise_invalid(bad_val=j, outer_val=val, path=p, index=index)
|
||||
|
||||
if not _can_serialize(j):
|
||||
# collect unserializable items separately, so we can report
|
||||
# only the deepest level, not all the parent components that
|
||||
# are just unserializable because of their children.
|
||||
unserializable_items = [
|
||||
i for i in unserializable_items if not p.startswith(i[0])
|
||||
]
|
||||
if unserializable_items:
|
||||
# we already have something unserializable in a different
|
||||
# branch - time to stop and fail
|
||||
break
|
||||
if all(not i[0].startswith(p) for i in unserializable_items):
|
||||
unserializable_items.append((p, j))
|
||||
|
||||
# Children that are not of type Component or
|
||||
# list/tuple not returned by traverse
|
||||
child = getattr(j, "children", None)
|
||||
if not isinstance(child, (tuple, MutableSequence)):
|
||||
if child and not _can_serialize(child):
|
||||
_raise_invalid(
|
||||
bad_val=child,
|
||||
outer_val=val,
|
||||
path=p + "\n" + "[*] " + type(child).__name__,
|
||||
index=index,
|
||||
)
|
||||
if unserializable_items:
|
||||
p, j = unserializable_items[0]
|
||||
# just report the first one, even if there are multiple,
|
||||
# as that's how all the other errors work
|
||||
_raise_invalid(bad_val=j, outer_val=val, path=p, index=index)
|
||||
|
||||
# Also check the child of val, as it will not be returned
|
||||
child = getattr(val, "children", None)
|
||||
if not isinstance(child, (tuple, MutableSequence)):
|
||||
if child and not _can_serialize(val):
|
||||
_raise_invalid(
|
||||
bad_val=child,
|
||||
outer_val=val,
|
||||
path=type(child).__name__,
|
||||
index=index,
|
||||
)
|
||||
|
||||
if not _can_serialize(val):
|
||||
_raise_invalid(
|
||||
bad_val=val,
|
||||
outer_val=type(val).__name__,
|
||||
path="",
|
||||
index=index,
|
||||
toplevel=True,
|
||||
)
|
||||
|
||||
if isinstance(output_value, list):
|
||||
for i, val in enumerate(output_value):
|
||||
_validate_value(val, index=i)
|
||||
else:
|
||||
_validate_value(output_value)
|
||||
|
||||
# if we got this far, raise a generic JSON error
|
||||
raise exceptions.InvalidCallbackReturnValue(
|
||||
f"""
|
||||
The callback for output `{output!r}`
|
||||
returned a value which is not JSON serializable.
|
||||
|
||||
In general, Dash properties can only be dash components, strings,
|
||||
dictionaries, numbers, None, or lists of those.
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def check_obsolete(kwargs):
|
||||
for key in kwargs:
|
||||
if key in ["components_cache_max_age", "static_folder"]:
|
||||
raise exceptions.ObsoleteKwargException(
|
||||
f"""
|
||||
{key} is no longer a valid keyword argument in Dash since v1.0.
|
||||
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
|
||||
if key in ["long_callback_manager"]:
|
||||
raise exceptions.ObsoleteKwargException(
|
||||
"long_callback_manager is obsolete, use background_callback_manager instead"
|
||||
)
|
||||
# any other kwarg mimic the built-in exception
|
||||
raise TypeError(f"Dash() got an unexpected keyword argument '{key}'")
|
||||
|
||||
|
||||
def validate_js_path(registered_paths, package_name, path_in_package_dist):
|
||||
if package_name not in registered_paths:
|
||||
raise exceptions.DependencyException(
|
||||
f"""
|
||||
Error loading dependency. "{package_name}" is not a registered library.
|
||||
Registered libraries are:
|
||||
{list(registered_paths.keys())}
|
||||
"""
|
||||
)
|
||||
|
||||
if path_in_package_dist not in registered_paths[package_name]:
|
||||
raise exceptions.DependencyException(
|
||||
f"""
|
||||
"{package_name}" is registered but the path requested is not valid.
|
||||
The path requested: "{path_in_package_dist}"
|
||||
List of registered paths: {registered_paths}
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def validate_index(name, checks, index):
|
||||
missing = [i for check, i in checks if not re.compile(check).search(index)]
|
||||
if missing:
|
||||
plural = "s" if len(missing) > 1 else ""
|
||||
raise exceptions.InvalidIndexException(
|
||||
f"Missing item{plural} {', '.join(missing)} in {name}."
|
||||
)
|
||||
|
||||
|
||||
def validate_layout_type(value):
|
||||
if not isinstance(
|
||||
value, (Component, patch_collections_abc("Callable"), list, tuple)
|
||||
):
|
||||
raise exceptions.NoLayoutException(
|
||||
"""
|
||||
Layout must be a single dash component, a list of dash components,
|
||||
or a function that returns a dash component.
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def validate_layout(layout, layout_value):
|
||||
if layout is None:
|
||||
raise exceptions.NoLayoutException(
|
||||
"""
|
||||
The layout was `None` at the time that `run` was called.
|
||||
Make sure to set the `layout` attribute of your application
|
||||
before running the server.
|
||||
"""
|
||||
)
|
||||
|
||||
component_ids = set()
|
||||
|
||||
def _validate(value):
|
||||
def _validate_id(comp):
|
||||
component_id = stringify_id(getattr(comp, "id", None))
|
||||
if component_id and component_id in component_ids:
|
||||
raise exceptions.DuplicateIdError(
|
||||
f"""
|
||||
Duplicate component id found in the initial layout: `{component_id}`
|
||||
"""
|
||||
)
|
||||
component_ids.add(component_id)
|
||||
|
||||
_validate_id(value)
|
||||
|
||||
for component in value._traverse(): # pylint: disable=protected-access
|
||||
_validate_id(component)
|
||||
|
||||
if isinstance(layout_value, (list, tuple)):
|
||||
for component in layout_value:
|
||||
if isinstance(component, (str,)):
|
||||
continue
|
||||
if isinstance(component, (Component,)):
|
||||
_validate(component)
|
||||
else:
|
||||
raise exceptions.NoLayoutException(
|
||||
"Only strings and components are allowed in a list layout."
|
||||
)
|
||||
else:
|
||||
_validate(layout_value)
|
||||
|
||||
|
||||
def validate_template(template):
|
||||
variable_names = re.findall("<(.*?)>", template)
|
||||
|
||||
for name in variable_names:
|
||||
if not name.isidentifier() or iskeyword(name):
|
||||
raise Exception(
|
||||
f'`{name}` is not a valid Python variable name in `path_template`: "{template}".'
|
||||
)
|
||||
|
||||
|
||||
def check_for_duplicate_pathnames(registry):
|
||||
path_to_module = {}
|
||||
for page in registry.values():
|
||||
if page["path"] not in path_to_module:
|
||||
path_to_module[page["path"]] = [page["module"]]
|
||||
else:
|
||||
path_to_module[page["path"]].append(page["module"])
|
||||
|
||||
for modules in path_to_module.values():
|
||||
if len(modules) > 1:
|
||||
raise Exception(f"modules {modules} have duplicate paths")
|
||||
|
||||
|
||||
def validate_registry(registry):
|
||||
for page in registry.values():
|
||||
if "layout" not in page:
|
||||
raise exceptions.NoLayoutException(
|
||||
f"No layout in module `{page['module']}` in dash.page_registry"
|
||||
)
|
||||
if page["module"] == "__main__":
|
||||
raise Exception(
|
||||
"""
|
||||
When registering pages from app.py, `__name__` is not a valid module name. Use a string instead.
|
||||
For example, `dash.register_page("my_module_name")`, rather than `dash.register_page(__name__)`
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def validate_pages_layout(module, page):
|
||||
if not hasattr(page, "layout"):
|
||||
raise exceptions.NoLayoutException(
|
||||
f"""
|
||||
No layout found in module {module}
|
||||
A variable or a function named "layout" is required.
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def validate_use_pages(config):
|
||||
if not config.get("assets_folder", None):
|
||||
raise exceptions.PageError(
|
||||
"`dash.register_page()` must be called after app instantiation"
|
||||
)
|
||||
|
||||
if flask.has_request_context():
|
||||
raise exceptions.PageError(
|
||||
"""
|
||||
dash.register_page() can’t be called within a callback as it updates dash.page_registry, which is a global variable.
|
||||
For more details, see https://dash.plotly.com/sharing-data-between-callbacks#why-global-variables-will-break-your-app
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def validate_module_name(module):
|
||||
if not isinstance(module, str):
|
||||
raise exceptions.PageError(
|
||||
"The first attribute of dash.register_page() must be a string or '__name__'"
|
||||
)
|
||||
return module
|
||||
|
||||
|
||||
def validate_background_callbacks(callback_map):
|
||||
# Validate that background callback side output & inputs are not circular
|
||||
# If circular, triggering a background callback would result in a fatal server/computer crash.
|
||||
all_outputs = set()
|
||||
input_indexed = {}
|
||||
for callback in callback_map.values():
|
||||
out = coerce_to_list(callback["output"])
|
||||
all_outputs.update(out)
|
||||
for o in out:
|
||||
input_indexed.setdefault(o, set())
|
||||
input_indexed[o].update(coerce_to_list(callback["raw_inputs"]))
|
||||
|
||||
for callback in (x for x in callback_map.values() if x.get("background")):
|
||||
bg_info = callback["background"]
|
||||
progress = bg_info.get("progress", [])
|
||||
running = bg_info.get("running", [])
|
||||
|
||||
bg_inputs = coerce_to_list(callback["raw_inputs"])
|
||||
outputs = set([x[0] for x in running] + progress)
|
||||
circular = [
|
||||
x
|
||||
for x in set(k for k, v in input_indexed.items() if v.intersection(outputs))
|
||||
if x in bg_inputs
|
||||
]
|
||||
|
||||
if circular:
|
||||
raise exceptions.BackgroundCallbackError(
|
||||
f"Background callback circular error!\n{circular} is used as input for a background callback"
|
||||
f" but also used as output from an input that is updated with progress or running argument."
|
||||
)
|
||||
|
||||
|
||||
def validate_duplicate_output(
|
||||
output, prevent_initial_call, config_prevent_initial_call
|
||||
):
|
||||
if "initial_duplicate" in (prevent_initial_call, config_prevent_initial_call):
|
||||
return
|
||||
|
||||
def _valid(out):
|
||||
if (
|
||||
out.allow_duplicate
|
||||
and not prevent_initial_call
|
||||
and not config_prevent_initial_call
|
||||
):
|
||||
raise exceptions.DuplicateCallback(
|
||||
"allow_duplicate requires prevent_initial_call to be True. The order of the call is not"
|
||||
" guaranteed to be the same on every page load. "
|
||||
"To enable duplicate callback with initial call, set prevent_initial_call='initial_duplicate' "
|
||||
" or globally in the config prevent_initial_callbacks='initial_duplicate'"
|
||||
)
|
||||
|
||||
if isinstance(output, (list, tuple)):
|
||||
for o in output:
|
||||
_valid(o)
|
||||
|
||||
return
|
||||
|
||||
_valid(output)
|
||||
@@ -0,0 +1,36 @@
|
||||
import collections
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
|
||||
|
||||
def watch(folders, on_change, pattern=None, sleep_time=0.1):
|
||||
pattern = re.compile(pattern) if pattern else None
|
||||
watched = collections.defaultdict(lambda: -1.0)
|
||||
|
||||
def walk():
|
||||
walked = []
|
||||
for folder in folders:
|
||||
for current, _, files in os.walk(folder):
|
||||
for f in files:
|
||||
if pattern and not pattern.search(f):
|
||||
continue
|
||||
path = os.path.join(current, f)
|
||||
|
||||
info = os.stat(path)
|
||||
new_time = info.st_mtime
|
||||
|
||||
if new_time > watched[path] > 0:
|
||||
on_change(path, new_time, False)
|
||||
|
||||
watched[path] = new_time
|
||||
walked.append(path)
|
||||
|
||||
# Look for deleted files
|
||||
for w in [x for x in watched.keys() if x not in walked]:
|
||||
del watched[w]
|
||||
on_change(w, -1, True)
|
||||
|
||||
while True:
|
||||
walk()
|
||||
time.sleep(sleep_time)
|
||||
@@ -0,0 +1,6 @@
|
||||
from .managers.celery_manager import ( # noqa: F401,E402
|
||||
CeleryManager,
|
||||
)
|
||||
from .managers.diskcache_manager import ( # noqa: F401,E402
|
||||
DiskcacheManager,
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
class ProxySetProps(dict):
|
||||
"""
|
||||
Defer dictionary item setter to run a custom function on change.
|
||||
Used by background callback manager to save the `set_props` data.
|
||||
"""
|
||||
|
||||
def __init__(self, on_change):
|
||||
super().__init__()
|
||||
self.on_change = on_change
|
||||
self._data = {}
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.on_change(key, value)
|
||||
self._data.setdefault(key, {})
|
||||
self._data[key] = {**self._data[key], **value}
|
||||
|
||||
def get(self, key, default=None):
|
||||
return self._data.get(key, default)
|
||||
@@ -0,0 +1,117 @@
|
||||
from abc import ABC
|
||||
import inspect
|
||||
import hashlib
|
||||
|
||||
|
||||
class BaseBackgroundCallbackManager(ABC):
|
||||
UNDEFINED = object()
|
||||
|
||||
# Keep a ref to all the ref to register every callback to every manager.
|
||||
managers = []
|
||||
|
||||
# Keep every function for late registering.
|
||||
functions = []
|
||||
|
||||
def __init__(self, cache_by):
|
||||
if cache_by is not None and not isinstance(cache_by, list):
|
||||
cache_by = [cache_by]
|
||||
|
||||
self.cache_by = cache_by
|
||||
|
||||
BaseBackgroundCallbackManager.managers.append(self)
|
||||
|
||||
self.func_registry = {}
|
||||
|
||||
# Register all funcs that were added before instantiation.
|
||||
# Ensure all celery task are registered.
|
||||
for fdetails in self.functions:
|
||||
self.register(*fdetails)
|
||||
|
||||
def terminate_job(self, job):
|
||||
raise NotImplementedError
|
||||
|
||||
def terminate_unhealthy_job(self, job):
|
||||
raise NotImplementedError
|
||||
|
||||
def job_running(self, job):
|
||||
raise NotImplementedError
|
||||
|
||||
def make_job_fn(self, fn, progress, key=None):
|
||||
raise NotImplementedError
|
||||
|
||||
def call_job_fn(self, key, job_fn, args, context):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_progress(self, key):
|
||||
raise NotImplementedError
|
||||
|
||||
def result_ready(self, key):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_result(self, key, job):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_updated_props(self, key):
|
||||
raise NotImplementedError
|
||||
|
||||
def build_cache_key(self, fn, args, cache_args_to_ignore, triggered):
|
||||
fn_source = inspect.getsource(fn)
|
||||
|
||||
if not isinstance(cache_args_to_ignore, (list, tuple)):
|
||||
cache_args_to_ignore = [cache_args_to_ignore]
|
||||
|
||||
if cache_args_to_ignore:
|
||||
if isinstance(args, dict):
|
||||
args = {k: v for k, v in args.items() if k not in cache_args_to_ignore}
|
||||
else:
|
||||
args = [
|
||||
arg for i, arg in enumerate(args) if i not in cache_args_to_ignore
|
||||
]
|
||||
|
||||
hash_dict = dict(args=args, fn_source=fn_source, triggered=triggered)
|
||||
|
||||
if self.cache_by is not None:
|
||||
# Caching enabled
|
||||
for i, cache_item in enumerate(self.cache_by):
|
||||
# Call cache function
|
||||
hash_dict[f"cache_key_{i}"] = cache_item()
|
||||
|
||||
return hashlib.sha256(str(hash_dict).encode("utf-8")).hexdigest()
|
||||
|
||||
def register(self, key, fn, progress):
|
||||
self.func_registry[key] = self.make_job_fn(fn, progress, key)
|
||||
|
||||
@staticmethod
|
||||
def register_func(fn, progress, callback_id):
|
||||
key = BaseBackgroundCallbackManager.hash_function(fn, callback_id)
|
||||
BaseBackgroundCallbackManager.functions.append(
|
||||
(
|
||||
key,
|
||||
fn,
|
||||
progress,
|
||||
)
|
||||
)
|
||||
|
||||
for manager in BaseBackgroundCallbackManager.managers:
|
||||
manager.register(key, fn, progress)
|
||||
|
||||
return key
|
||||
|
||||
@staticmethod
|
||||
def _make_progress_key(key):
|
||||
return key + "-progress"
|
||||
|
||||
@staticmethod
|
||||
def _make_set_props_key(key):
|
||||
return f"{key}-set_props"
|
||||
|
||||
@staticmethod
|
||||
def hash_function(fn, callback_id=""):
|
||||
try:
|
||||
fn_source = inspect.getsource(fn)
|
||||
fn_str = fn_source
|
||||
except OSError: # pylint: disable=too-broad-exception
|
||||
fn_str = getattr(fn, "__name__", "")
|
||||
return hashlib.sha256(
|
||||
callback_id.encode("utf-8") + fn_str.encode("utf-8")
|
||||
).hexdigest()
|
||||
@@ -0,0 +1,263 @@
|
||||
import json
|
||||
import traceback
|
||||
from contextvars import copy_context
|
||||
import asyncio
|
||||
from functools import partial
|
||||
|
||||
from _plotly_utils.utils import PlotlyJSONEncoder
|
||||
|
||||
from dash._callback_context import context_value
|
||||
from dash._utils import AttributeDict
|
||||
from dash.exceptions import PreventUpdate
|
||||
from dash.background_callback._proxy_set_props import ProxySetProps
|
||||
from dash.background_callback.managers import BaseBackgroundCallbackManager
|
||||
|
||||
|
||||
class CeleryManager(BaseBackgroundCallbackManager):
|
||||
"""Manage background execution of callbacks with a celery queue."""
|
||||
|
||||
def __init__(self, celery_app, cache_by=None, expire=None):
|
||||
"""
|
||||
Background callback manager that runs callback logic on a celery task queue,
|
||||
and stores results using a celery result backend.
|
||||
|
||||
:param celery_app:
|
||||
A celery.Celery application instance that must be configured with a
|
||||
result backend. See the celery documentation for information on
|
||||
configuration options.
|
||||
:param cache_by:
|
||||
A list of zero-argument functions. When provided, caching is enabled and
|
||||
the return values of these functions are combined with the callback
|
||||
function's input arguments, triggered inputs and source code to generate cache keys.
|
||||
:param expire:
|
||||
If provided, a cache entry will be removed when it has not been accessed
|
||||
for ``expire`` seconds. If not provided, the lifetime of cache entries
|
||||
is determined by the default behavior of the celery result backend.
|
||||
"""
|
||||
try:
|
||||
import celery # type: ignore[reportMissingImports]; pylint: disable=import-outside-toplevel,import-error
|
||||
from celery.backends.base import ( # type: ignore[reportMissingImports]; pylint: disable=import-outside-toplevel,import-error
|
||||
DisabledBackend,
|
||||
)
|
||||
except ImportError as missing_imports:
|
||||
raise ImportError(
|
||||
"""\
|
||||
CeleryManager requires extra dependencies which can be installed doing
|
||||
|
||||
$ pip install "dash[celery]"\n"""
|
||||
) from missing_imports
|
||||
|
||||
if not isinstance(celery_app, celery.Celery):
|
||||
raise ValueError("First argument must be a celery.Celery object")
|
||||
|
||||
if isinstance(celery_app.backend, DisabledBackend):
|
||||
raise ValueError("Celery instance must be configured with a result backend")
|
||||
|
||||
self.handle = celery_app
|
||||
self.expire = expire
|
||||
super().__init__(cache_by)
|
||||
|
||||
def terminate_job(self, job):
|
||||
if job is None:
|
||||
return
|
||||
|
||||
self.handle.control.terminate(job)
|
||||
|
||||
def terminate_unhealthy_job(self, job):
|
||||
task = self.get_task(job)
|
||||
if task and task.status in ("FAILURE", "REVOKED"):
|
||||
return self.terminate_job(job)
|
||||
return False
|
||||
|
||||
def job_running(self, job):
|
||||
future = self.get_task(job)
|
||||
return future and future.status in (
|
||||
"PENDING",
|
||||
"RECEIVED",
|
||||
"STARTED",
|
||||
"RETRY",
|
||||
"PROGRESS",
|
||||
)
|
||||
|
||||
def make_job_fn(self, fn, progress, key=None):
|
||||
return _make_job_fn(fn, self.handle, progress, key)
|
||||
|
||||
def get_task(self, job):
|
||||
if job:
|
||||
return self.handle.AsyncResult(job)
|
||||
|
||||
return None
|
||||
|
||||
def clear_cache_entry(self, key):
|
||||
self.handle.backend.delete(key)
|
||||
|
||||
def call_job_fn(self, key, job_fn, args, context):
|
||||
task = job_fn.delay(key, self._make_progress_key(key), args, context)
|
||||
return task.task_id
|
||||
|
||||
def get_progress(self, key):
|
||||
progress_key = self._make_progress_key(key)
|
||||
progress_data = self.handle.backend.get(progress_key)
|
||||
if progress_data:
|
||||
self.handle.backend.delete(progress_key)
|
||||
return json.loads(progress_data)
|
||||
|
||||
return None
|
||||
|
||||
def result_ready(self, key):
|
||||
return self.handle.backend.get(key) is not None
|
||||
|
||||
def get_result(self, key, job):
|
||||
# Get result value
|
||||
result = self.handle.backend.get(key)
|
||||
if result is None:
|
||||
return self.UNDEFINED
|
||||
|
||||
result = json.loads(result)
|
||||
|
||||
# Clear result if not caching
|
||||
if self.cache_by is None:
|
||||
self.clear_cache_entry(key)
|
||||
else:
|
||||
if self.expire:
|
||||
# Set/update expiration time
|
||||
self.handle.backend.expire(key, self.expire)
|
||||
self.clear_cache_entry(self._make_progress_key(key))
|
||||
|
||||
self.terminate_job(job)
|
||||
return result
|
||||
|
||||
def get_updated_props(self, key):
|
||||
updated_props = self.handle.backend.get(self._make_set_props_key(key))
|
||||
if updated_props is None:
|
||||
return {}
|
||||
|
||||
self.clear_cache_entry(key)
|
||||
|
||||
return json.loads(updated_props)
|
||||
|
||||
|
||||
def _make_job_fn(fn, celery_app, progress, key): # pylint: disable=too-many-statements
|
||||
cache = celery_app.backend
|
||||
|
||||
@celery_app.task(name=f"background_callback_{key}")
|
||||
def job_fn(
|
||||
result_key, progress_key, user_callback_args, context=None
|
||||
): # pylint: disable=too-many-statements
|
||||
def _set_progress(progress_value):
|
||||
if not isinstance(progress_value, (list, tuple)):
|
||||
progress_value = [progress_value]
|
||||
|
||||
cache.set(progress_key, json.dumps(progress_value, cls=PlotlyJSONEncoder))
|
||||
|
||||
maybe_progress = [_set_progress] if progress else []
|
||||
|
||||
def _set_props(_id, props):
|
||||
cache.set(
|
||||
f"{result_key}-set_props",
|
||||
json.dumps({_id: props}, cls=PlotlyJSONEncoder),
|
||||
)
|
||||
|
||||
ctx = copy_context()
|
||||
|
||||
def run():
|
||||
c = AttributeDict(**context) # type: ignore[reportCallIssue]
|
||||
c.ignore_register_page = False
|
||||
c.updated_props = ProxySetProps(_set_props)
|
||||
context_value.set(c)
|
||||
errored = False
|
||||
user_callback_output = None # to help type checking
|
||||
try:
|
||||
if isinstance(user_callback_args, dict):
|
||||
user_callback_output = fn(*maybe_progress, **user_callback_args)
|
||||
elif isinstance(user_callback_args, (list, tuple)):
|
||||
user_callback_output = fn(*maybe_progress, *user_callback_args)
|
||||
else:
|
||||
user_callback_output = fn(*maybe_progress, user_callback_args)
|
||||
except PreventUpdate:
|
||||
# Put NoUpdate dict directly to avoid circular imports.
|
||||
errored = True
|
||||
cache.set(
|
||||
result_key,
|
||||
json.dumps(
|
||||
{"_dash_no_update": "_dash_no_update"}, cls=PlotlyJSONEncoder
|
||||
),
|
||||
)
|
||||
except Exception as err: # pylint: disable=broad-except
|
||||
errored = True
|
||||
cache.set(
|
||||
result_key,
|
||||
json.dumps(
|
||||
{
|
||||
"background_callback_error": {
|
||||
"msg": str(err),
|
||||
"tb": traceback.format_exc(),
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
if not errored:
|
||||
cache.set(
|
||||
result_key, json.dumps(user_callback_output, cls=PlotlyJSONEncoder)
|
||||
)
|
||||
|
||||
async def async_run():
|
||||
c = AttributeDict(**context)
|
||||
c.ignore_register_page = False
|
||||
c.updated_props = ProxySetProps(_set_props)
|
||||
context_value.set(c)
|
||||
errored = False
|
||||
try:
|
||||
if isinstance(user_callback_args, dict):
|
||||
user_callback_output = await fn(
|
||||
*maybe_progress, **user_callback_args
|
||||
)
|
||||
elif isinstance(user_callback_args, (list, tuple)):
|
||||
user_callback_output = await fn(
|
||||
*maybe_progress, *user_callback_args
|
||||
)
|
||||
else:
|
||||
user_callback_output = await fn(*maybe_progress, user_callback_args)
|
||||
except PreventUpdate:
|
||||
# Put NoUpdate dict directly to avoid circular imports.
|
||||
errored = True
|
||||
cache.set(
|
||||
result_key,
|
||||
json.dumps(
|
||||
{"_dash_no_update": "_dash_no_update"}, cls=PlotlyJSONEncoder
|
||||
),
|
||||
)
|
||||
except Exception as err: # pylint: disable=broad-except
|
||||
errored = True
|
||||
cache.set(
|
||||
result_key,
|
||||
json.dumps(
|
||||
{
|
||||
"background_callback_error": {
|
||||
"msg": str(err),
|
||||
"tb": traceback.format_exc(),
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
if asyncio.iscoroutine(user_callback_output):
|
||||
user_callback_output = await user_callback_output
|
||||
|
||||
if not errored:
|
||||
cache.set(
|
||||
result_key, json.dumps(user_callback_output, cls=PlotlyJSONEncoder)
|
||||
)
|
||||
|
||||
if asyncio.iscoroutinefunction(fn):
|
||||
func = partial(ctx.run, async_run)
|
||||
asyncio.run(func())
|
||||
else:
|
||||
ctx.run(run)
|
||||
|
||||
return job_fn
|
||||
|
||||
|
||||
class CeleryLongCallbackManager(CeleryManager):
|
||||
"""Deprecated: use `from dash import CeleryManager` instead."""
|
||||
@@ -0,0 +1,305 @@
|
||||
import traceback
|
||||
from contextvars import copy_context
|
||||
import asyncio
|
||||
from functools import partial
|
||||
|
||||
|
||||
from . import BaseBackgroundCallbackManager
|
||||
from .._proxy_set_props import ProxySetProps
|
||||
from ..._callback_context import context_value
|
||||
from ..._utils import AttributeDict
|
||||
from ...exceptions import PreventUpdate
|
||||
|
||||
_pending_value = "__$pending__"
|
||||
|
||||
|
||||
class DiskcacheManager(BaseBackgroundCallbackManager):
|
||||
"""Manage the background execution of callbacks with subprocesses and a diskcache result backend."""
|
||||
|
||||
def __init__(self, cache=None, cache_by=None, expire=None):
|
||||
"""
|
||||
Background callback manager that runs callback logic in a subprocess and stores
|
||||
results on disk using diskcache
|
||||
|
||||
:param cache:
|
||||
A diskcache.Cache or diskcache.FanoutCache instance. See the diskcache
|
||||
documentation for information on configuration options. If not provided,
|
||||
a diskcache.Cache instance will be created with default values.
|
||||
:param cache_by:
|
||||
A list of zero-argument functions. When provided, caching is enabled and
|
||||
the return values of these functions are combined with the callback
|
||||
function's input arguments, triggered inputs and source code to generate cache keys.
|
||||
:param expire:
|
||||
If provided, a cache entry will be removed when it has not been accessed
|
||||
for ``expire`` seconds. If not provided, the lifetime of cache entries
|
||||
is determined by the default behavior of the ``cache`` instance.
|
||||
"""
|
||||
try:
|
||||
import diskcache # type: ignore[reportMissingImports]; pylint: disable=import-outside-toplevel
|
||||
import psutil # noqa: F401,E402 pylint: disable=import-outside-toplevel,unused-import,unused-variable,import-error
|
||||
import multiprocess # noqa: F401,E402 pylint: disable=import-outside-toplevel,unused-import,unused-variable
|
||||
except ImportError as missing_imports:
|
||||
raise ImportError(
|
||||
"""\
|
||||
DiskcacheManager requires extra dependencies which can be installed doing
|
||||
|
||||
$ pip install "dash[diskcache]"\n"""
|
||||
) from missing_imports
|
||||
|
||||
if cache is None:
|
||||
self.handle = diskcache.Cache()
|
||||
else:
|
||||
if not isinstance(cache, (diskcache.Cache, diskcache.FanoutCache)):
|
||||
raise ValueError(
|
||||
"First argument must be a diskcache.Cache "
|
||||
"or diskcache.FanoutCache object"
|
||||
)
|
||||
self.handle = cache
|
||||
|
||||
self.expire = expire
|
||||
super().__init__(cache_by)
|
||||
|
||||
def terminate_job(self, job):
|
||||
import psutil # pylint: disable=import-outside-toplevel,import-error
|
||||
|
||||
if job is None:
|
||||
return
|
||||
|
||||
job = int(job)
|
||||
|
||||
# Use diskcache transaction so multiple process don't try to kill the
|
||||
# process at the same time
|
||||
with self.handle.transact():
|
||||
if psutil.pid_exists(job):
|
||||
process = psutil.Process(job)
|
||||
|
||||
for proc in process.children(recursive=True):
|
||||
try:
|
||||
proc.kill()
|
||||
except psutil.NoSuchProcess:
|
||||
pass
|
||||
|
||||
try:
|
||||
process.kill()
|
||||
except psutil.NoSuchProcess:
|
||||
pass
|
||||
|
||||
try:
|
||||
process.wait(1)
|
||||
except (psutil.TimeoutExpired, psutil.NoSuchProcess):
|
||||
pass
|
||||
|
||||
def terminate_unhealthy_job(self, job):
|
||||
import psutil # pylint: disable=import-outside-toplevel,import-error
|
||||
|
||||
job = int(job)
|
||||
|
||||
if job and psutil.pid_exists(job):
|
||||
if not self.job_running(job):
|
||||
self.terminate_job(job)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def job_running(self, job):
|
||||
import psutil # pylint: disable=import-outside-toplevel,import-error
|
||||
|
||||
job = int(job)
|
||||
|
||||
if job and psutil.pid_exists(job):
|
||||
proc = psutil.Process(job)
|
||||
return proc.status() != psutil.STATUS_ZOMBIE
|
||||
return False
|
||||
|
||||
def make_job_fn(self, fn, progress, key=None):
|
||||
return _make_job_fn(fn, self.handle, progress)
|
||||
|
||||
def clear_cache_entry(self, key):
|
||||
self.handle.delete(key)
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
def call_job_fn(self, key, job_fn, args, context):
|
||||
"""
|
||||
Call the job function, supporting both sync and async jobs.
|
||||
Args:
|
||||
key: Cache key for the job.
|
||||
job_fn: The job function to execute.
|
||||
args: Arguments for the job function.
|
||||
context: Context for the job.
|
||||
Returns:
|
||||
The PID of the spawned process or None for async execution.
|
||||
"""
|
||||
# pylint: disable-next=import-outside-toplevel,no-name-in-module,import-error
|
||||
from multiprocess import Process # type: ignore
|
||||
|
||||
# pylint: disable-next=not-callable
|
||||
process = Process(
|
||||
target=job_fn,
|
||||
args=(key, self._make_progress_key(key), args, context),
|
||||
)
|
||||
process.start()
|
||||
return process.pid
|
||||
|
||||
@staticmethod
|
||||
def _run_async_in_process(job_fn, key, args, context):
|
||||
"""
|
||||
Helper function to run an async job in a new process.
|
||||
Args:
|
||||
job_fn: The async job function.
|
||||
key: Cache key for the job.
|
||||
args: Arguments for the job function.
|
||||
context: Context for the job.
|
||||
"""
|
||||
# Create a new event loop for the process
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
|
||||
# Wrap the job function to include key and progress
|
||||
async_job = partial(job_fn, key, args, context)
|
||||
|
||||
try:
|
||||
# Run the async job and wait for completion
|
||||
loop.run_until_complete(async_job())
|
||||
except Exception as e:
|
||||
# Handle errors, log them, and cache if necessary
|
||||
raise Exception(str(e)) from e
|
||||
finally:
|
||||
loop.close()
|
||||
|
||||
def get_progress(self, key):
|
||||
progress_key = self._make_progress_key(key)
|
||||
progress_data = self.handle.get(progress_key)
|
||||
if progress_data:
|
||||
self.handle.delete(progress_key)
|
||||
|
||||
return progress_data
|
||||
|
||||
def result_ready(self, key):
|
||||
return self.handle.get(key) is not None
|
||||
|
||||
def get_result(self, key, job):
|
||||
# Get result value
|
||||
result = self.handle.get(key, self.UNDEFINED)
|
||||
if result is self.UNDEFINED:
|
||||
return self.UNDEFINED
|
||||
|
||||
# Clear result if not caching
|
||||
if self.cache_by is None:
|
||||
self.clear_cache_entry(key)
|
||||
else:
|
||||
if self.expire:
|
||||
self.handle.touch(key, expire=self.expire)
|
||||
|
||||
self.clear_cache_entry(self._make_progress_key(key))
|
||||
|
||||
if job:
|
||||
self.terminate_job(job)
|
||||
return result
|
||||
|
||||
def get_updated_props(self, key):
|
||||
set_props_key = self._make_set_props_key(key)
|
||||
result = self.handle.get(set_props_key, self.UNDEFINED)
|
||||
if result is self.UNDEFINED:
|
||||
return {}
|
||||
|
||||
self.clear_cache_entry(set_props_key)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
# pylint: disable-next=too-many-statements
|
||||
def _make_job_fn(fn, cache, progress):
|
||||
# pylint: disable-next=too-many-statements
|
||||
def job_fn(result_key, progress_key, user_callback_args, context):
|
||||
def _set_progress(progress_value):
|
||||
if not isinstance(progress_value, (list, tuple)):
|
||||
progress_value = [progress_value]
|
||||
|
||||
cache.set(progress_key, progress_value)
|
||||
|
||||
maybe_progress = [_set_progress] if progress else []
|
||||
|
||||
def _set_props(_id, props):
|
||||
cache.set(f"{result_key}-set_props", {_id: props})
|
||||
|
||||
ctx = copy_context()
|
||||
|
||||
def run():
|
||||
c = AttributeDict(**context)
|
||||
c.ignore_register_page = False
|
||||
c.updated_props = ProxySetProps(_set_props)
|
||||
context_value.set(c)
|
||||
errored = False
|
||||
user_callback_output = None # initialized to prevent type checker warnings
|
||||
try:
|
||||
if isinstance(user_callback_args, dict):
|
||||
user_callback_output = fn(*maybe_progress, **user_callback_args)
|
||||
elif isinstance(user_callback_args, (list, tuple)):
|
||||
user_callback_output = fn(*maybe_progress, *user_callback_args)
|
||||
else:
|
||||
user_callback_output = fn(*maybe_progress, user_callback_args)
|
||||
except PreventUpdate:
|
||||
errored = True
|
||||
cache.set(result_key, {"_dash_no_update": "_dash_no_update"})
|
||||
except Exception as err: # pylint: disable=broad-except
|
||||
errored = True
|
||||
cache.set(
|
||||
result_key,
|
||||
{
|
||||
"background_callback_error": {
|
||||
"msg": str(err),
|
||||
"tb": traceback.format_exc(),
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
if not errored:
|
||||
cache.set(result_key, user_callback_output)
|
||||
|
||||
async def async_run():
|
||||
c = AttributeDict(**context)
|
||||
c.ignore_register_page = False
|
||||
c.updated_props = ProxySetProps(_set_props)
|
||||
context_value.set(c)
|
||||
errored = False
|
||||
try:
|
||||
if isinstance(user_callback_args, dict):
|
||||
user_callback_output = await fn(
|
||||
*maybe_progress, **user_callback_args
|
||||
)
|
||||
elif isinstance(user_callback_args, (list, tuple)):
|
||||
user_callback_output = await fn(
|
||||
*maybe_progress, *user_callback_args
|
||||
)
|
||||
else:
|
||||
user_callback_output = await fn(*maybe_progress, user_callback_args)
|
||||
except PreventUpdate:
|
||||
errored = True
|
||||
cache.set(result_key, {"_dash_no_update": "_dash_no_update"})
|
||||
except Exception as err: # pylint: disable=broad-except
|
||||
errored = True
|
||||
cache.set(
|
||||
result_key,
|
||||
{
|
||||
"background_callback_error": {
|
||||
"msg": str(err),
|
||||
"tb": traceback.format_exc(),
|
||||
}
|
||||
},
|
||||
)
|
||||
if asyncio.iscoroutine(user_callback_output):
|
||||
user_callback_output = await user_callback_output
|
||||
if not errored:
|
||||
cache.set(result_key, user_callback_output)
|
||||
|
||||
if asyncio.iscoroutinefunction(fn):
|
||||
func = partial(ctx.run, async_run)
|
||||
asyncio.run(func())
|
||||
else:
|
||||
ctx.run(run)
|
||||
|
||||
return job_fn
|
||||
|
||||
|
||||
class DiskcacheLongCallbackManager(DiskcacheManager):
|
||||
"""Deprecated: use `from dash import DiskcacheManager` instead."""
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,287 @@
|
||||
import collections
|
||||
|
||||
|
||||
def get_named_tuple(name, dict):
|
||||
return collections.namedtuple(name, dict.keys())(*dict.values())
|
||||
|
||||
|
||||
Align = get_named_tuple(
|
||||
"align",
|
||||
{"default": "", "left": "<", "right": ">", "center": "^", "right_sign": "="},
|
||||
)
|
||||
|
||||
Group = get_named_tuple("group", {"no": "", "yes": ","})
|
||||
|
||||
Padding = get_named_tuple("padding", {"no": "", "yes": "0"})
|
||||
|
||||
Prefix = get_named_tuple(
|
||||
"prefix",
|
||||
{
|
||||
"yocto": 10**-24,
|
||||
"zepto": 10**-21,
|
||||
"atto": 10**-18,
|
||||
"femto": 10**-15,
|
||||
"pico": 10**-12,
|
||||
"nano": 10**-9,
|
||||
"micro": 10**-6,
|
||||
"milli": 10**-3,
|
||||
"none": None,
|
||||
"kilo": 10**3,
|
||||
"mega": 10**6,
|
||||
"giga": 10**9,
|
||||
"tera": 10**12,
|
||||
"peta": 10**15,
|
||||
"exa": 10**18,
|
||||
"zetta": 10**21,
|
||||
"yotta": 10**24,
|
||||
},
|
||||
)
|
||||
|
||||
Scheme = get_named_tuple(
|
||||
"scheme",
|
||||
{
|
||||
"default": "",
|
||||
"decimal": "r",
|
||||
"decimal_integer": "d",
|
||||
"decimal_or_exponent": "g",
|
||||
"decimal_si_prefix": "s",
|
||||
"exponent": "e",
|
||||
"fixed": "f",
|
||||
"percentage": "%",
|
||||
"percentage_rounded": "p",
|
||||
"binary": "b",
|
||||
"octal": "o",
|
||||
"lower_case_hex": "x",
|
||||
"upper_case_hex": "X",
|
||||
"unicode": "c",
|
||||
},
|
||||
)
|
||||
|
||||
Sign = get_named_tuple(
|
||||
"sign",
|
||||
{"default": "", "negative": "-", "positive": "+", "parantheses": "(", "space": " "},
|
||||
)
|
||||
|
||||
Symbol = get_named_tuple(
|
||||
"symbol", {"no": "", "yes": "$", "binary": "#b", "octal": "#o", "hex": "#x"}
|
||||
)
|
||||
|
||||
Trim = get_named_tuple("trim", {"no": "", "yes": "~"})
|
||||
|
||||
|
||||
class Format:
|
||||
def __init__(self, **kwargs):
|
||||
self._locale = {}
|
||||
self._nully = ""
|
||||
self._prefix = Prefix.none
|
||||
self._specifier = {
|
||||
"align": Align.default,
|
||||
"fill": "",
|
||||
"group": Group.no,
|
||||
"width": "",
|
||||
"padding": Padding.no,
|
||||
"precision": "",
|
||||
"sign": Sign.default,
|
||||
"symbol": Symbol.no,
|
||||
"trim": Trim.no,
|
||||
"type": Scheme.default,
|
||||
}
|
||||
|
||||
valid_methods = [
|
||||
m for m in dir(self.__class__) if m[0] != "_" and m != "to_plotly_json"
|
||||
]
|
||||
|
||||
for kw, val in kwargs.items():
|
||||
if kw not in valid_methods:
|
||||
raise TypeError(
|
||||
"{0} is not a format method. Expected one of".format(kw),
|
||||
str(list(valid_methods)),
|
||||
)
|
||||
|
||||
getattr(self, kw)(val)
|
||||
|
||||
def _validate_char(self, value):
|
||||
self._validate_string(value)
|
||||
|
||||
if len(value) != 1:
|
||||
raise ValueError("expected value to a string of length one")
|
||||
|
||||
def _validate_non_negative_integer_or_none(self, value):
|
||||
if value is None:
|
||||
return
|
||||
|
||||
if not isinstance(value, int):
|
||||
raise TypeError("expected value to be an integer")
|
||||
|
||||
if value < 0:
|
||||
raise ValueError("expected value to be non-negative", str(value))
|
||||
|
||||
def _validate_named(self, value, named_values):
|
||||
if value not in named_values:
|
||||
raise TypeError("expected value to be one of", str(list(named_values)))
|
||||
|
||||
def _validate_string(self, value):
|
||||
if not isinstance(value, (str, "".__class__)):
|
||||
raise TypeError("expected value to be a string")
|
||||
|
||||
# Specifier
|
||||
def align(self, value):
|
||||
self._validate_named(value, Align)
|
||||
|
||||
self._specifier["align"] = value
|
||||
return self
|
||||
|
||||
def fill(self, value):
|
||||
self._validate_char(value)
|
||||
|
||||
self._specifier["fill"] = value
|
||||
return self
|
||||
|
||||
def group(self, value):
|
||||
if isinstance(value, bool):
|
||||
value = Group.yes if value else Group.no
|
||||
|
||||
self._validate_named(value, Group)
|
||||
|
||||
self._specifier["group"] = value
|
||||
return self
|
||||
|
||||
def padding(self, value):
|
||||
if isinstance(value, bool):
|
||||
value = Padding.yes if value else Padding.no
|
||||
|
||||
self._validate_named(value, Padding)
|
||||
|
||||
self._specifier["padding"] = value
|
||||
return self
|
||||
|
||||
def padding_width(self, value):
|
||||
self._validate_non_negative_integer_or_none(value)
|
||||
|
||||
self._specifier["width"] = value if value is not None else ""
|
||||
return self
|
||||
|
||||
def precision(self, value):
|
||||
self._validate_non_negative_integer_or_none(value)
|
||||
|
||||
self._specifier["precision"] = ".{0}".format(value) if value is not None else ""
|
||||
return self
|
||||
|
||||
def scheme(self, value):
|
||||
self._validate_named(value, Scheme)
|
||||
|
||||
self._specifier["type"] = value
|
||||
return self
|
||||
|
||||
def sign(self, value):
|
||||
self._validate_named(value, Sign)
|
||||
|
||||
self._specifier["sign"] = value
|
||||
return self
|
||||
|
||||
def symbol(self, value):
|
||||
self._validate_named(value, Symbol)
|
||||
|
||||
self._specifier["symbol"] = value
|
||||
return self
|
||||
|
||||
def trim(self, value):
|
||||
if isinstance(value, bool):
|
||||
value = Trim.yes if value else Trim.no
|
||||
|
||||
self._validate_named(value, Trim)
|
||||
|
||||
self._specifier["trim"] = value
|
||||
return self
|
||||
|
||||
# Locale
|
||||
def symbol_prefix(self, value):
|
||||
self._validate_string(value)
|
||||
|
||||
if "symbol" not in self._locale:
|
||||
self._locale["symbol"] = [value, ""]
|
||||
else:
|
||||
self._locale["symbol"][0] = value
|
||||
|
||||
return self
|
||||
|
||||
def symbol_suffix(self, value):
|
||||
self._validate_string(value)
|
||||
|
||||
if "symbol" not in self._locale:
|
||||
self._locale["symbol"] = ["", value]
|
||||
else:
|
||||
self._locale["symbol"][1] = value
|
||||
|
||||
return self
|
||||
|
||||
def decimal_delimiter(self, value):
|
||||
self._validate_char(value)
|
||||
|
||||
self._locale["decimal"] = value
|
||||
return self
|
||||
|
||||
def group_delimiter(self, value):
|
||||
self._validate_char(value)
|
||||
|
||||
self._locale["group"] = value
|
||||
return self
|
||||
|
||||
def groups(self, groups):
|
||||
groups = (
|
||||
groups
|
||||
if isinstance(groups, list)
|
||||
else [groups]
|
||||
if isinstance(groups, int)
|
||||
else None
|
||||
)
|
||||
|
||||
if not isinstance(groups, list):
|
||||
raise TypeError("expected groups to be an integer or a list of integers")
|
||||
if len(groups) == 0:
|
||||
raise ValueError(
|
||||
"expected groups to be an integer or a list of " "one or more integers"
|
||||
)
|
||||
|
||||
for group in groups:
|
||||
if not isinstance(group, int):
|
||||
raise TypeError("expected entry to be an integer")
|
||||
|
||||
if group <= 0:
|
||||
raise ValueError("expected entry to be a non-negative integer")
|
||||
|
||||
self._locale["grouping"] = groups
|
||||
return self
|
||||
|
||||
# Nully
|
||||
def nully(self, value):
|
||||
self._nully = value
|
||||
return self
|
||||
|
||||
# Prefix
|
||||
def si_prefix(self, value):
|
||||
self._validate_named(value, Prefix)
|
||||
|
||||
self._prefix = value
|
||||
return self
|
||||
|
||||
def to_plotly_json(self):
|
||||
f = {}
|
||||
f["locale"] = self._locale.copy()
|
||||
f["nully"] = self._nully
|
||||
f["prefix"] = self._prefix
|
||||
aligned = self._specifier["align"] != Align.default
|
||||
f["specifier"] = "{}{}{}{}{}{}{}{}{}{}".format(
|
||||
self._specifier["fill"] if aligned else "",
|
||||
self._specifier["align"],
|
||||
self._specifier["sign"],
|
||||
self._specifier["symbol"],
|
||||
self._specifier["padding"],
|
||||
self._specifier["width"],
|
||||
self._specifier["group"],
|
||||
self._specifier["precision"],
|
||||
self._specifier["trim"],
|
||||
self._specifier["type"],
|
||||
)
|
||||
|
||||
return f
|
||||
@@ -0,0 +1,19 @@
|
||||
from .Format import Format, Group, Scheme, Sign, Symbol
|
||||
|
||||
|
||||
def money(decimals, sign=Sign.default):
|
||||
return Format(
|
||||
group=Group.yes,
|
||||
precision=decimals,
|
||||
scheme=Scheme.fixed,
|
||||
sign=sign,
|
||||
symbol=Symbol.yes,
|
||||
)
|
||||
|
||||
|
||||
def percentage(decimals, rounded=False):
|
||||
if not isinstance(rounded, bool):
|
||||
raise TypeError("expected rounded to be a boolean")
|
||||
|
||||
rounded = Scheme.percentage_rounded if rounded else Scheme.percentage
|
||||
return Format(scheme=rounded, precision=decimals)
|
||||
@@ -0,0 +1,97 @@
|
||||
# type: ignore
|
||||
|
||||
import os as _os
|
||||
import sys as _sys
|
||||
import json
|
||||
|
||||
import dash as _dash
|
||||
|
||||
if not hasattr(_dash, "__plotly_dash") and not hasattr(_dash, "development"):
|
||||
print(
|
||||
"Dash was not successfully imported. "
|
||||
"Make sure you don't have a file "
|
||||
'named \n"dash.py" in your current directory.',
|
||||
file=_sys.stderr,
|
||||
)
|
||||
_sys.exit(1)
|
||||
|
||||
from ._imports_ import * # noqa: E402, F401, F403
|
||||
from ._imports_ import __all__ as _components
|
||||
from . import Format # noqa: F401, E402
|
||||
from . import FormatTemplate # noqa: F401, E402
|
||||
|
||||
__all__ = _components + ["Format", "FormatTemplate"]
|
||||
|
||||
_basepath = _os.path.dirname(__file__)
|
||||
_filepath = _os.path.abspath(_os.path.join(_basepath, "package-info.json"))
|
||||
with open(_filepath) as f:
|
||||
package = json.load(f)
|
||||
|
||||
package_name = package["name"].replace(" ", "_").replace("-", "_")
|
||||
__version__ = package["version"]
|
||||
|
||||
_current_path = _os.path.dirname(_os.path.abspath(__file__))
|
||||
|
||||
_this_module = _sys.modules[__name__]
|
||||
|
||||
|
||||
async_resources = ["export", "table", "highlight"]
|
||||
|
||||
_js_dist = []
|
||||
|
||||
_js_dist.extend(
|
||||
[
|
||||
{
|
||||
"relative_package_path": "dash_table/async-{}.js".format(async_resource),
|
||||
"external_url": (
|
||||
"https://unpkg.com/dash-table@{}" "/dash_table/async-{}.js"
|
||||
).format(__version__, async_resource),
|
||||
"namespace": "dash",
|
||||
"async": True,
|
||||
}
|
||||
for async_resource in async_resources
|
||||
]
|
||||
)
|
||||
|
||||
_js_dist.extend(
|
||||
[
|
||||
{
|
||||
"relative_package_path": "dash_table/async-{}.js.map".format(
|
||||
async_resource
|
||||
),
|
||||
"external_url": (
|
||||
"https://unpkg.com/dash-table@{}" "/dash_table/async-{}.js.map"
|
||||
).format(__version__, async_resource),
|
||||
"namespace": "dash",
|
||||
"dynamic": True,
|
||||
}
|
||||
for async_resource in async_resources
|
||||
]
|
||||
)
|
||||
|
||||
_js_dist.extend(
|
||||
[
|
||||
{
|
||||
"relative_package_path": "dash_table/bundle.js",
|
||||
"external_url": (
|
||||
"https://unpkg.com/dash-table@{}/dash_table/bundle.js"
|
||||
).format(__version__),
|
||||
"namespace": "dash",
|
||||
},
|
||||
{
|
||||
"relative_package_path": "dash_table/bundle.js.map",
|
||||
"external_url": (
|
||||
"https://unpkg.com/dash-table@{}/dash_table/bundle.js.map"
|
||||
).format(__version__),
|
||||
"namespace": "dash",
|
||||
"dynamic": True,
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
_css_dist = []
|
||||
|
||||
|
||||
for _component in __all__:
|
||||
setattr(locals()[_component], "_js_dist", _js_dist)
|
||||
setattr(locals()[_component], "_css_dist", _css_dist)
|
||||
@@ -0,0 +1,3 @@
|
||||
from .DataTable import DataTable
|
||||
|
||||
__all__ = ["DataTable"]
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
/*! cpexcel.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
|
||||
/*! cputils.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,18 @@
|
||||
/*!
|
||||
Copyright (c) 2018 Jed Watson.
|
||||
Licensed under the MIT License (MIT), see
|
||||
http://jedwatson.github.io/classnames
|
||||
*/
|
||||
|
||||
/*!
|
||||
Copyright (c) 2018 Jed Watson.
|
||||
Licensed under the MIT License (MIT), see
|
||||
http://jedwatson.github.io/react-select
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
*/
|
||||
|
||||
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,13 @@
|
||||
<!doctype>
|
||||
<html>
|
||||
<head>
|
||||
<title>dash-table</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id='root'></div>
|
||||
<script src='https://unpkg.com/react@16.14.0/umd/react.development.js'></script>
|
||||
<script src='https://unpkg.com/react-dom@16.14.0/umd/react-dom.development.js'></script>
|
||||
|
||||
<script src="./demo.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,124 @@
|
||||
{
|
||||
"name": "dash-table",
|
||||
"version": "6.0.4",
|
||||
"description": "Dash table",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:plotly/dash.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/plotly/dash/issues"
|
||||
},
|
||||
"homepage": "https://github.com/plotly/dash",
|
||||
"main": "dash_table/bundle.js",
|
||||
"scripts": {
|
||||
"preprivate::test.server": "run-s private::wait_dash*",
|
||||
"private::build": "node --max_old_space_size=4096 node_modules/webpack/bin/webpack --bail",
|
||||
"private::build:js": "run-s \"private::build -- --mode production\"",
|
||||
"private::build:js-test": "run-s \"private::build -- --mode development --config webpack.test.config.js\"",
|
||||
"private::build:js-test-watch": "run-s \"private::build -- --mode development --config webpack.test.config.js --watch\"",
|
||||
"private::build:backends": "dash-generate-components src/dash-table/dash/DataTable.js dash_table -p package-info.json && cp dash_table_base/** dash_table/ && dash-generate-components src/dash-table/dash/DataTable.js dash_table -p package-info.json -k DataTable --r-prefix 'dash' --r-suggests 'dash' --jl-prefix 'dash' && black dash_table",
|
||||
"private::format.ts": "npm run private::lint.ts -- --fix",
|
||||
"private::format.prettier": "prettier --config .prettierrc --write \"{src,tests,demo}/**/*.{js,ts,tsx}\"",
|
||||
"private::format.black": "black dash_table_base tests",
|
||||
"private::lint.ts": "eslint ./src ./tests",
|
||||
"private::lint.flake": "flake8 dash_table_base tests",
|
||||
"private::lint.black": "black --check dash_table_base tests",
|
||||
"private::lint.prettier": "prettier --config .prettierrc \"{src,tests,demo}/**/*.{js,ts,tsx}\" --list-different",
|
||||
"private::test.python": "python -m unittest tests/unit/format_test.py",
|
||||
"private::test.unit": "karma start karma.conf.js --single-run",
|
||||
"build.watch": "webpack serve --disable-host-check --content-base dash_table --mode development --config webpack.dev.config.js",
|
||||
"build": "run-s private::build:js private::build:backends",
|
||||
"postbuild": "es-check es2015 dash_table/bundle.js dash_table/async-*.js",
|
||||
"format": "run-s private::format.*",
|
||||
"lint": "run-s private::lint.*",
|
||||
"test.server": "pytest --nopercyfinalize tests/selenium",
|
||||
"test.unit": "run-s private::test.python private::test.unit",
|
||||
"test.visual": "npm install --package-lock-only=false --no-save @percy/storybook@^3.3.1 @storybook/builder-webpack5@^6.5.13 @storybook/cli@^6.5.13 @storybook/manager-webpack5@^6.5.16 @storybook/react@^6.5.13 @storybook/semver@^7.3.2 && build-storybook && percy-storybook --widths=1280",
|
||||
"test.visual-local": "npm install --package-lock-only=false --no-save @percy/storybook@^3.3.1 @storybook/builder-webpack5@^6.5.13 @storybook/cli@^6.5.13 @storybook/manager-webpack5@^6.5.16 @storybook/react@^6.5.13 @storybook/semver@^7.3.2 && build-storybook"
|
||||
},
|
||||
"author": "Chris Parmer <chris@plotly.com>",
|
||||
"maintainer": "Alex Johnson <alex@plotly.com>",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.28.0",
|
||||
"@babel/core": "^7.28.0",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@babel/plugin-transform-regenerator": "^7.28.1",
|
||||
"@babel/polyfill": "^7.12.1",
|
||||
"@babel/preset-env": "^7.28.0",
|
||||
"@babel/preset-react": "^7.27.1",
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.36",
|
||||
"@fortawesome/free-regular-svg-icons": "^5.15.4",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.4",
|
||||
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||
"@plotly/dash-component-plugins": "^1.2.3",
|
||||
"@plotly/webpack-dash-dynamic-import": "^1.3.0",
|
||||
"@types/chai": "^4.3.5",
|
||||
"@types/d3-format": "^3.0.1",
|
||||
"@types/mocha": "^10.0.1",
|
||||
"@types/papaparse": "^5.3.7",
|
||||
"@types/ramda": "0.30.2",
|
||||
"@types/react": "^16.14.8",
|
||||
"@types/react-dom": "^16.9.13",
|
||||
"@types/react-select": "^4.0.16",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.7",
|
||||
"@typescript-eslint/parser": "^5.59.7",
|
||||
"babel-loader": "^9.2.1",
|
||||
"chai": "^4.3.7",
|
||||
"css-loader": "^6.8.1",
|
||||
"css.escape": "^1.5.1",
|
||||
"d3-format": "^3.1.0",
|
||||
"es-check": "^7.1.1",
|
||||
"eslint": "^8.41.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"fast-isnumeric": "^1.1.4",
|
||||
"file-loader": "^6.2.0",
|
||||
"highlight.js": "^11.8.0",
|
||||
"karma": "^6.4.2",
|
||||
"karma-chrome-launcher": "^3.2.0",
|
||||
"karma-mocha": "^2.0.1",
|
||||
"karma-typescript": "^5.5.4",
|
||||
"karma-webpack": "^5.0.0",
|
||||
"less": "^4.1.3",
|
||||
"less-loader": "^11.1.0",
|
||||
"mocha": "^10.2.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"papaparse": "^5.4.1",
|
||||
"prettier": "^2.8.8",
|
||||
"ramda": "^0.30.1",
|
||||
"raw-loader": "^4.0.2",
|
||||
"react": "^16.14.0",
|
||||
"react-docgen": "^5.4.3",
|
||||
"react-dom": "^16.14.0",
|
||||
"react-select": "^1.3.0",
|
||||
"regenerator-runtime": "^0.13.11",
|
||||
"remarkable": "^2.0.1",
|
||||
"sheetclip": "^0.3.0",
|
||||
"style-loader": "^3.3.3",
|
||||
"ts-loader": "^9.5.2",
|
||||
"typescript": "^5.8.3",
|
||||
"webpack": "^5.101.0",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "5.2.2",
|
||||
"webpack-preprocessor": "^0.1.12",
|
||||
"xlsx": "^0.17.5",
|
||||
"rimraf": "^5.0.5"
|
||||
},
|
||||
"files": [
|
||||
"/dash_table/async-*{.js,.map}",
|
||||
"/dash_table/bundle*{.js,.map}"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"prop-types": "^15.7.2",
|
||||
"react": ">=16",
|
||||
"react-dom": ">=16"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0",
|
||||
"npm": ">=6.1.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"last 9 years and not dead"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class Checklist(Component):
|
||||
"""A Checklist component.
|
||||
Checklist is a component that encapsulates several checkboxes.
|
||||
The values and labels of the checklist are specified in the `options`
|
||||
property and the checked items are specified with the `value` property.
|
||||
Each checkbox is rendered as an input with a surrounding label.
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- options (list of dicts; optional):
|
||||
An array of options.
|
||||
|
||||
`options` is a list of string | number | booleans | dict | list of
|
||||
dicts with keys:
|
||||
|
||||
- label (a list of or a singular dash component, string or number; required):
|
||||
The option's label.
|
||||
|
||||
- value (string | number | boolean; required):
|
||||
The value of the option. This value corresponds to the items
|
||||
specified in the `value` property.
|
||||
|
||||
- disabled (boolean; optional):
|
||||
If True, this option is disabled and cannot be selected.
|
||||
|
||||
- title (string; optional):
|
||||
The HTML 'title' attribute for the option. Allows for
|
||||
information on hover. For more information on this attribute,
|
||||
see
|
||||
https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title.
|
||||
|
||||
- value (list of string | number | booleans; optional):
|
||||
The currently selected value.
|
||||
|
||||
- inline (boolean; default False):
|
||||
Indicates whether the options labels should be displayed inline
|
||||
(True=horizontal) or in a block (False=vertical).
|
||||
|
||||
- className (string; optional):
|
||||
The class of the container (div).
|
||||
|
||||
- inputStyle (dict; optional):
|
||||
The style of the <input> checkbox element.
|
||||
|
||||
- inputClassName (string; default ''):
|
||||
The class of the <input> checkbox element.
|
||||
|
||||
- labelStyle (dict; optional):
|
||||
The style of the <label> that wraps the checkbox input and the
|
||||
option's label.
|
||||
|
||||
- labelClassName (string; default ''):
|
||||
The class of the <label> that wraps the checkbox input and the
|
||||
option's label.
|
||||
|
||||
- id (string; optional):
|
||||
The ID of this component, used to identify dash components in
|
||||
callbacks. The ID needs to be unique across all of the components
|
||||
in an app.
|
||||
|
||||
- persistence (boolean | string | number; optional):
|
||||
Used to allow user interactions in this component to be persisted
|
||||
when the component - or the page - is refreshed. If `persisted` is
|
||||
truthy and hasn't changed from its previous value, a `value` that
|
||||
the user has changed while using the app will keep that change, as
|
||||
long as the new `value` also matches what was given originally.
|
||||
Used in conjunction with `persistence_type`.
|
||||
|
||||
- persisted_props (list of a value equal to: 'value's; default ['value']):
|
||||
Properties whose user interactions will persist after refreshing
|
||||
the component or the page. Since only `value` is allowed this prop
|
||||
can normally be ignored.
|
||||
|
||||
- persistence_type (a value equal to: 'local', 'session', 'memory'; default 'local'):
|
||||
Where persisted user changes will be stored: memory: only kept in
|
||||
memory, reset on page refresh. local: window.localStorage, data is
|
||||
kept after the browser quit. session: window.sessionStorage, data
|
||||
is cleared once the browser quit."""
|
||||
|
||||
_children_props = ["options[].label"]
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "Checklist"
|
||||
Options = TypedDict(
|
||||
"Options",
|
||||
{
|
||||
"label": ComponentType,
|
||||
"value": typing.Union[str, NumberType, bool],
|
||||
"disabled": NotRequired[bool],
|
||||
"title": NotRequired[str],
|
||||
},
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
options: typing.Optional[
|
||||
typing.Union[
|
||||
typing.Sequence[typing.Union[str, NumberType, bool]],
|
||||
dict,
|
||||
typing.Sequence["Options"],
|
||||
]
|
||||
] = None,
|
||||
value: typing.Optional[
|
||||
typing.Sequence[typing.Union[str, NumberType, bool]]
|
||||
] = None,
|
||||
inline: typing.Optional[bool] = None,
|
||||
className: typing.Optional[str] = None,
|
||||
style: typing.Optional[typing.Any] = None,
|
||||
inputStyle: typing.Optional[dict] = None,
|
||||
inputClassName: typing.Optional[str] = None,
|
||||
labelStyle: typing.Optional[dict] = None,
|
||||
labelClassName: typing.Optional[str] = None,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
persistence: typing.Optional[typing.Union[bool, str, NumberType]] = None,
|
||||
persisted_props: typing.Optional[typing.Sequence[Literal["value"]]] = None,
|
||||
persistence_type: typing.Optional[Literal["local", "session", "memory"]] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = [
|
||||
"options",
|
||||
"value",
|
||||
"inline",
|
||||
"className",
|
||||
"style",
|
||||
"inputStyle",
|
||||
"inputClassName",
|
||||
"labelStyle",
|
||||
"labelClassName",
|
||||
"id",
|
||||
"persistence",
|
||||
"persisted_props",
|
||||
"persistence_type",
|
||||
]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"options",
|
||||
"value",
|
||||
"inline",
|
||||
"className",
|
||||
"style",
|
||||
"inputStyle",
|
||||
"inputClassName",
|
||||
"labelStyle",
|
||||
"labelClassName",
|
||||
"id",
|
||||
"persistence",
|
||||
"persisted_props",
|
||||
"persistence_type",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args}
|
||||
|
||||
super(Checklist, self).__init__(**args)
|
||||
|
||||
|
||||
setattr(Checklist, "__init__", _explicitize_args(Checklist.__init__))
|
||||
@@ -0,0 +1,99 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class Clipboard(Component):
|
||||
"""A Clipboard component.
|
||||
The Clipboard component copies text to the clipboard
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- id (string; optional):
|
||||
The ID used to identify this component.
|
||||
|
||||
- className (string; optional):
|
||||
The class name of the icon element.
|
||||
|
||||
- content (string; optional):
|
||||
The text to be copied to the clipboard if the `target_id` is None.
|
||||
|
||||
- html_content (string; optional):
|
||||
The clipboard html text be copied to the clipboard if the
|
||||
`target_id` is None.
|
||||
|
||||
- n_clicks (number; default 0):
|
||||
The number of times copy button was clicked.
|
||||
|
||||
- target_id (string | dict; optional):
|
||||
The id of target component containing text to copy to the
|
||||
clipboard. The inner text of the `children` prop will be copied to
|
||||
the clipboard. If none, then the text from the `value` prop will
|
||||
be copied.
|
||||
|
||||
- title (string; optional):
|
||||
The text shown as a tooltip when hovering over the copy icon."""
|
||||
|
||||
_children_props = []
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "Clipboard"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
target_id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
content: typing.Optional[str] = None,
|
||||
n_clicks: typing.Optional[NumberType] = None,
|
||||
html_content: typing.Optional[str] = None,
|
||||
title: typing.Optional[str] = None,
|
||||
style: typing.Optional[typing.Any] = None,
|
||||
className: typing.Optional[str] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = [
|
||||
"id",
|
||||
"className",
|
||||
"content",
|
||||
"html_content",
|
||||
"n_clicks",
|
||||
"style",
|
||||
"target_id",
|
||||
"title",
|
||||
]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"id",
|
||||
"className",
|
||||
"content",
|
||||
"html_content",
|
||||
"n_clicks",
|
||||
"style",
|
||||
"target_id",
|
||||
"title",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args}
|
||||
|
||||
super(Clipboard, self).__init__(**args)
|
||||
|
||||
|
||||
setattr(Clipboard, "__init__", _explicitize_args(Clipboard.__init__))
|
||||
@@ -0,0 +1,97 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class ConfirmDialog(Component):
|
||||
"""A ConfirmDialog component.
|
||||
ConfirmDialog is used to display the browser's native "confirm" modal,
|
||||
with an optional message and two buttons ("OK" and "Cancel").
|
||||
This ConfirmDialog can be used in conjunction with buttons when the user
|
||||
is performing an action that should require an extra step of verification.
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- id (string; optional):
|
||||
The ID of this component, used to identify dash components in
|
||||
callbacks. The ID needs to be unique across all of the components
|
||||
in an app.
|
||||
|
||||
- cancel_n_clicks (number; default 0):
|
||||
Number of times the popup was canceled.
|
||||
|
||||
- cancel_n_clicks_timestamp (number; default -1):
|
||||
Last time the cancel button was clicked.
|
||||
|
||||
- displayed (boolean; optional):
|
||||
Set to True to send the ConfirmDialog.
|
||||
|
||||
- message (string; optional):
|
||||
Message to show in the popup.
|
||||
|
||||
- submit_n_clicks (number; default 0):
|
||||
Number of times the submit button was clicked.
|
||||
|
||||
- submit_n_clicks_timestamp (number; default -1):
|
||||
Last time the submit button was clicked."""
|
||||
|
||||
_children_props = []
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "ConfirmDialog"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
message: typing.Optional[str] = None,
|
||||
submit_n_clicks: typing.Optional[NumberType] = None,
|
||||
submit_n_clicks_timestamp: typing.Optional[NumberType] = None,
|
||||
cancel_n_clicks: typing.Optional[NumberType] = None,
|
||||
cancel_n_clicks_timestamp: typing.Optional[NumberType] = None,
|
||||
displayed: typing.Optional[bool] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = [
|
||||
"id",
|
||||
"cancel_n_clicks",
|
||||
"cancel_n_clicks_timestamp",
|
||||
"displayed",
|
||||
"message",
|
||||
"submit_n_clicks",
|
||||
"submit_n_clicks_timestamp",
|
||||
]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"id",
|
||||
"cancel_n_clicks",
|
||||
"cancel_n_clicks_timestamp",
|
||||
"displayed",
|
||||
"message",
|
||||
"submit_n_clicks",
|
||||
"submit_n_clicks_timestamp",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args}
|
||||
|
||||
super(ConfirmDialog, self).__init__(**args)
|
||||
|
||||
|
||||
setattr(ConfirmDialog, "__init__", _explicitize_args(ConfirmDialog.__init__))
|
||||
@@ -0,0 +1,111 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class ConfirmDialogProvider(Component):
|
||||
"""A ConfirmDialogProvider component.
|
||||
A wrapper component that will display a confirmation dialog
|
||||
when its child component has been clicked on.
|
||||
|
||||
For example:
|
||||
```
|
||||
dcc.ConfirmDialogProvider(
|
||||
html.Button('click me', id='btn'),
|
||||
message='Danger - Are you sure you want to continue.'
|
||||
id='confirm')
|
||||
```
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- children (boolean | number | string | dict | list; optional):
|
||||
The children to hijack clicks from and display the popup.
|
||||
|
||||
- id (string; optional):
|
||||
The ID of this component, used to identify dash components in
|
||||
callbacks. The ID needs to be unique across all of the components
|
||||
in an app.
|
||||
|
||||
- cancel_n_clicks (number; default 0):
|
||||
Number of times the popup was canceled.
|
||||
|
||||
- cancel_n_clicks_timestamp (number; default -1):
|
||||
Last time the cancel button was clicked.
|
||||
|
||||
- displayed (boolean; optional):
|
||||
Is the modal currently displayed.
|
||||
|
||||
- message (string; optional):
|
||||
Message to show in the popup.
|
||||
|
||||
- submit_n_clicks (number; default 0):
|
||||
Number of times the submit was clicked.
|
||||
|
||||
- submit_n_clicks_timestamp (number; default -1):
|
||||
Last time the submit button was clicked."""
|
||||
|
||||
_children_props = []
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "ConfirmDialogProvider"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
children: typing.Optional[ComponentType] = None,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
message: typing.Optional[str] = None,
|
||||
submit_n_clicks: typing.Optional[NumberType] = None,
|
||||
submit_n_clicks_timestamp: typing.Optional[NumberType] = None,
|
||||
cancel_n_clicks: typing.Optional[NumberType] = None,
|
||||
cancel_n_clicks_timestamp: typing.Optional[NumberType] = None,
|
||||
displayed: typing.Optional[bool] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = [
|
||||
"children",
|
||||
"id",
|
||||
"cancel_n_clicks",
|
||||
"cancel_n_clicks_timestamp",
|
||||
"displayed",
|
||||
"message",
|
||||
"submit_n_clicks",
|
||||
"submit_n_clicks_timestamp",
|
||||
]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"children",
|
||||
"id",
|
||||
"cancel_n_clicks",
|
||||
"cancel_n_clicks_timestamp",
|
||||
"displayed",
|
||||
"message",
|
||||
"submit_n_clicks",
|
||||
"submit_n_clicks_timestamp",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args if k != "children"}
|
||||
|
||||
super(ConfirmDialogProvider, self).__init__(children=children, **args)
|
||||
|
||||
|
||||
setattr(
|
||||
ConfirmDialogProvider, "__init__", _explicitize_args(ConfirmDialogProvider.__init__)
|
||||
)
|
||||
@@ -0,0 +1,302 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
import datetime
|
||||
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class DatePickerRange(Component):
|
||||
"""A DatePickerRange component.
|
||||
DatePickerRange is a tailor made component designed for selecting
|
||||
timespan across multiple days off of a calendar.
|
||||
|
||||
The DatePicker integrates well with the Python datetime module with the
|
||||
startDate and endDate being returned in a string format suitable for
|
||||
creating datetime objects.
|
||||
|
||||
This component is based off of Airbnb's react-dates react component
|
||||
which can be found here: https://github.com/airbnb/react-dates
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- start_date (string; optional):
|
||||
Specifies the starting date for the component. Accepts
|
||||
datetime.datetime objects or strings in the format 'YYYY-MM-DD'.
|
||||
|
||||
- end_date (string; optional):
|
||||
Specifies the ending date for the component. Accepts
|
||||
datetime.datetime objects or strings in the format 'YYYY-MM-DD'.
|
||||
|
||||
- min_date_allowed (string; optional):
|
||||
Specifies the lowest selectable date for the component. Accepts
|
||||
datetime.datetime objects or strings in the format 'YYYY-MM-DD'.
|
||||
|
||||
- max_date_allowed (string; optional):
|
||||
Specifies the highest selectable date for the component. Accepts
|
||||
datetime.datetime objects or strings in the format 'YYYY-MM-DD'.
|
||||
|
||||
- disabled_days (list of strings; optional):
|
||||
Specifies additional days between min_date_allowed and
|
||||
max_date_allowed that should be disabled. Accepted
|
||||
datetime.datetime objects or strings in the format 'YYYY-MM-DD'.
|
||||
|
||||
- minimum_nights (number; optional):
|
||||
Specifies a minimum number of nights that must be selected between
|
||||
the startDate and the endDate.
|
||||
|
||||
- updatemode (a value equal to: 'singledate', 'bothdates'; default 'singledate'):
|
||||
Determines when the component should update its value. If
|
||||
`bothdates`, then the DatePicker will only trigger its value when
|
||||
the user has finished picking both dates. If `singledate`, then
|
||||
the DatePicker will update its value as one date is picked.
|
||||
|
||||
- start_date_placeholder_text (string; optional):
|
||||
Text that will be displayed in the first input box of the date
|
||||
picker when no date is selected. Default value is 'Start Date'.
|
||||
|
||||
- end_date_placeholder_text (string; optional):
|
||||
Text that will be displayed in the second input box of the date
|
||||
picker when no date is selected. Default value is 'End Date'.
|
||||
|
||||
- initial_visible_month (string; optional):
|
||||
Specifies the month that is initially presented when the user
|
||||
opens the calendar. Accepts datetime.datetime objects or strings
|
||||
in the format 'YYYY-MM-DD'.
|
||||
|
||||
- clearable (boolean; default False):
|
||||
Whether or not the dropdown is \"clearable\", that is, whether or
|
||||
not a small \"x\" appears on the right of the dropdown that
|
||||
removes the selected value.
|
||||
|
||||
- reopen_calendar_on_clear (boolean; default False):
|
||||
If True, the calendar will automatically open when cleared.
|
||||
|
||||
- display_format (string; optional):
|
||||
Specifies the format that the selected dates will be displayed
|
||||
valid formats are variations of \"MM YY DD\". For example: \"MM YY
|
||||
DD\" renders as '05 10 97' for May 10th 1997 \"MMMM, YY\" renders
|
||||
as 'May, 1997' for May 10th 1997 \"M, D, YYYY\" renders as '07,
|
||||
10, 1997' for September 10th 1997 \"MMMM\" renders as 'May' for
|
||||
May 10 1997.
|
||||
|
||||
- month_format (string; optional):
|
||||
Specifies the format that the month will be displayed in the
|
||||
calendar, valid formats are variations of \"MM YY\". For example:
|
||||
\"MM YY\" renders as '05 97' for May 1997 \"MMMM, YYYY\" renders
|
||||
as 'May, 1997' for May 1997 \"MMM, YY\" renders as 'Sep, 97' for
|
||||
September 1997.
|
||||
|
||||
- first_day_of_week (a value equal to: 0, 1, 2, 3, 4, 5, 6; default 0):
|
||||
Specifies what day is the first day of the week, values must be
|
||||
from [0, ..., 6] with 0 denoting Sunday and 6 denoting Saturday.
|
||||
|
||||
- show_outside_days (boolean; optional):
|
||||
If True the calendar will display days that rollover into the next
|
||||
month.
|
||||
|
||||
- stay_open_on_select (boolean; default False):
|
||||
If True the calendar will not close when the user has selected a
|
||||
value and will wait until the user clicks off the calendar.
|
||||
|
||||
- calendar_orientation (a value equal to: 'vertical', 'horizontal'; default 'horizontal'):
|
||||
Orientation of calendar, either vertical or horizontal. Valid
|
||||
options are 'vertical' or 'horizontal'.
|
||||
|
||||
- number_of_months_shown (number; default 1):
|
||||
Number of calendar months that are shown when calendar is opened.
|
||||
|
||||
- with_portal (boolean; default False):
|
||||
If True, calendar will open in a screen overlay portal, not
|
||||
supported on vertical calendar.
|
||||
|
||||
- with_full_screen_portal (boolean; default False):
|
||||
If True, calendar will open in a full screen overlay portal, will
|
||||
take precedent over 'withPortal' if both are set to True, not
|
||||
supported on vertical calendar.
|
||||
|
||||
- day_size (number; default 39):
|
||||
Size of rendered calendar days, higher number means bigger day
|
||||
size and larger calendar overall.
|
||||
|
||||
- is_RTL (boolean; default False):
|
||||
Determines whether the calendar and days operate from left to
|
||||
right or from right to left.
|
||||
|
||||
- disabled (boolean; default False):
|
||||
If True, no dates can be selected.
|
||||
|
||||
- start_date_id (string; optional):
|
||||
The HTML element ID of the start date input field. Not used by
|
||||
Dash, only by CSS.
|
||||
|
||||
- end_date_id (string; optional):
|
||||
The HTML element ID of the end date input field. Not used by Dash,
|
||||
only by CSS.
|
||||
|
||||
- className (string; optional):
|
||||
Appends a CSS class to the wrapper div component.
|
||||
|
||||
- id (string; optional):
|
||||
The ID of this component, used to identify dash components in
|
||||
callbacks. The ID needs to be unique across all of the components
|
||||
in an app.
|
||||
|
||||
- persistence (boolean | string | number; optional):
|
||||
Used to allow user interactions in this component to be persisted
|
||||
when the component - or the page - is refreshed. If `persisted` is
|
||||
truthy and hasn't changed from its previous value, any
|
||||
`persisted_props` that the user has changed while using the app
|
||||
will keep those changes, as long as the new prop value also
|
||||
matches what was given originally. Used in conjunction with
|
||||
`persistence_type` and `persisted_props`.
|
||||
|
||||
- persisted_props (list of a value equal to: 'start_date', 'end_date's; default ['start_date', 'end_date']):
|
||||
Properties whose user interactions will persist after refreshing
|
||||
the component or the page.
|
||||
|
||||
- persistence_type (a value equal to: 'local', 'session', 'memory'; default 'local'):
|
||||
Where persisted user changes will be stored: memory: only kept in
|
||||
memory, reset on page refresh. local: window.localStorage, data is
|
||||
kept after the browser quit. session: window.sessionStorage, data
|
||||
is cleared once the browser quit."""
|
||||
|
||||
_children_props = []
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "DatePickerRange"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
start_date: typing.Optional[typing.Union[str, datetime.datetime]] = None,
|
||||
end_date: typing.Optional[typing.Union[str, datetime.datetime]] = None,
|
||||
min_date_allowed: typing.Optional[typing.Union[str, datetime.datetime]] = None,
|
||||
max_date_allowed: typing.Optional[typing.Union[str, datetime.datetime]] = None,
|
||||
disabled_days: typing.Optional[
|
||||
typing.Sequence[typing.Union[str, datetime.datetime]]
|
||||
] = None,
|
||||
minimum_nights: typing.Optional[NumberType] = None,
|
||||
updatemode: typing.Optional[Literal["singledate", "bothdates"]] = None,
|
||||
start_date_placeholder_text: typing.Optional[str] = None,
|
||||
end_date_placeholder_text: typing.Optional[str] = None,
|
||||
initial_visible_month: typing.Optional[str] = None,
|
||||
clearable: typing.Optional[bool] = None,
|
||||
reopen_calendar_on_clear: typing.Optional[bool] = None,
|
||||
display_format: typing.Optional[str] = None,
|
||||
month_format: typing.Optional[str] = None,
|
||||
first_day_of_week: typing.Optional[Literal[0, 1, 2, 3, 4, 5, 6]] = None,
|
||||
show_outside_days: typing.Optional[bool] = None,
|
||||
stay_open_on_select: typing.Optional[bool] = None,
|
||||
calendar_orientation: typing.Optional[Literal["vertical", "horizontal"]] = None,
|
||||
number_of_months_shown: typing.Optional[NumberType] = None,
|
||||
with_portal: typing.Optional[bool] = None,
|
||||
with_full_screen_portal: typing.Optional[bool] = None,
|
||||
day_size: typing.Optional[NumberType] = None,
|
||||
is_RTL: typing.Optional[bool] = None,
|
||||
disabled: typing.Optional[bool] = None,
|
||||
start_date_id: typing.Optional[str] = None,
|
||||
end_date_id: typing.Optional[str] = None,
|
||||
style: typing.Optional[typing.Any] = None,
|
||||
className: typing.Optional[str] = None,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
persistence: typing.Optional[typing.Union[bool, str, NumberType]] = None,
|
||||
persisted_props: typing.Optional[
|
||||
typing.Sequence[Literal["start_date", "end_date"]]
|
||||
] = None,
|
||||
persistence_type: typing.Optional[Literal["local", "session", "memory"]] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = [
|
||||
"start_date",
|
||||
"end_date",
|
||||
"min_date_allowed",
|
||||
"max_date_allowed",
|
||||
"disabled_days",
|
||||
"minimum_nights",
|
||||
"updatemode",
|
||||
"start_date_placeholder_text",
|
||||
"end_date_placeholder_text",
|
||||
"initial_visible_month",
|
||||
"clearable",
|
||||
"reopen_calendar_on_clear",
|
||||
"display_format",
|
||||
"month_format",
|
||||
"first_day_of_week",
|
||||
"show_outside_days",
|
||||
"stay_open_on_select",
|
||||
"calendar_orientation",
|
||||
"number_of_months_shown",
|
||||
"with_portal",
|
||||
"with_full_screen_portal",
|
||||
"day_size",
|
||||
"is_RTL",
|
||||
"disabled",
|
||||
"start_date_id",
|
||||
"end_date_id",
|
||||
"style",
|
||||
"className",
|
||||
"id",
|
||||
"persistence",
|
||||
"persisted_props",
|
||||
"persistence_type",
|
||||
]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"start_date",
|
||||
"end_date",
|
||||
"min_date_allowed",
|
||||
"max_date_allowed",
|
||||
"disabled_days",
|
||||
"minimum_nights",
|
||||
"updatemode",
|
||||
"start_date_placeholder_text",
|
||||
"end_date_placeholder_text",
|
||||
"initial_visible_month",
|
||||
"clearable",
|
||||
"reopen_calendar_on_clear",
|
||||
"display_format",
|
||||
"month_format",
|
||||
"first_day_of_week",
|
||||
"show_outside_days",
|
||||
"stay_open_on_select",
|
||||
"calendar_orientation",
|
||||
"number_of_months_shown",
|
||||
"with_portal",
|
||||
"with_full_screen_portal",
|
||||
"day_size",
|
||||
"is_RTL",
|
||||
"disabled",
|
||||
"start_date_id",
|
||||
"end_date_id",
|
||||
"style",
|
||||
"className",
|
||||
"id",
|
||||
"persistence",
|
||||
"persisted_props",
|
||||
"persistence_type",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args}
|
||||
|
||||
super(DatePickerRange, self).__init__(**args)
|
||||
|
||||
|
||||
setattr(DatePickerRange, "__init__", _explicitize_args(DatePickerRange.__init__))
|
||||
@@ -0,0 +1,258 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
import datetime
|
||||
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class DatePickerSingle(Component):
|
||||
"""A DatePickerSingle component.
|
||||
DatePickerSingle is a tailor made component designed for selecting
|
||||
a single day off of a calendar.
|
||||
|
||||
The DatePicker integrates well with the Python datetime module with the
|
||||
startDate and endDate being returned in a string format suitable for
|
||||
creating datetime objects.
|
||||
|
||||
This component is based off of Airbnb's react-dates react component
|
||||
which can be found here: https://github.com/airbnb/react-dates
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- date (string; optional):
|
||||
Specifies the starting date for the component, best practice is to
|
||||
pass value via datetime object.
|
||||
|
||||
- min_date_allowed (string; optional):
|
||||
Specifies the lowest selectable date for the component. Accepts
|
||||
datetime.datetime objects or strings in the format 'YYYY-MM-DD'.
|
||||
|
||||
- max_date_allowed (string; optional):
|
||||
Specifies the highest selectable date for the component. Accepts
|
||||
datetime.datetime objects or strings in the format 'YYYY-MM-DD'.
|
||||
|
||||
- disabled_days (list of strings; optional):
|
||||
Specifies additional days between min_date_allowed and
|
||||
max_date_allowed that should be disabled. Accepted
|
||||
datetime.datetime objects or strings in the format 'YYYY-MM-DD'.
|
||||
|
||||
- placeholder (string; optional):
|
||||
Text that will be displayed in the input box of the date picker
|
||||
when no date is selected. Default value is 'Start Date'.
|
||||
|
||||
- initial_visible_month (string; optional):
|
||||
Specifies the month that is initially presented when the user
|
||||
opens the calendar. Accepts datetime.datetime objects or strings
|
||||
in the format 'YYYY-MM-DD'.
|
||||
|
||||
- clearable (boolean; default False):
|
||||
Whether or not the dropdown is \"clearable\", that is, whether or
|
||||
not a small \"x\" appears on the right of the dropdown that
|
||||
removes the selected value.
|
||||
|
||||
- reopen_calendar_on_clear (boolean; default False):
|
||||
If True, the calendar will automatically open when cleared.
|
||||
|
||||
- display_format (string; optional):
|
||||
Specifies the format that the selected dates will be displayed
|
||||
valid formats are variations of \"MM YY DD\". For example: \"MM YY
|
||||
DD\" renders as '05 10 97' for May 10th 1997 \"MMMM, YY\" renders
|
||||
as 'May, 1997' for May 10th 1997 \"M, D, YYYY\" renders as '07,
|
||||
10, 1997' for September 10th 1997 \"MMMM\" renders as 'May' for
|
||||
May 10 1997.
|
||||
|
||||
- month_format (string; optional):
|
||||
Specifies the format that the month will be displayed in the
|
||||
calendar, valid formats are variations of \"MM YY\". For example:
|
||||
\"MM YY\" renders as '05 97' for May 1997 \"MMMM, YYYY\" renders
|
||||
as 'May, 1997' for May 1997 \"MMM, YY\" renders as 'Sep, 97' for
|
||||
September 1997.
|
||||
|
||||
- first_day_of_week (a value equal to: 0, 1, 2, 3, 4, 5, 6; default 0):
|
||||
Specifies what day is the first day of the week, values must be
|
||||
from [0, ..., 6] with 0 denoting Sunday and 6 denoting Saturday.
|
||||
|
||||
- show_outside_days (boolean; default True):
|
||||
If True the calendar will display days that rollover into the next
|
||||
month.
|
||||
|
||||
- stay_open_on_select (boolean; default False):
|
||||
If True the calendar will not close when the user has selected a
|
||||
value and will wait until the user clicks off the calendar.
|
||||
|
||||
- calendar_orientation (a value equal to: 'vertical', 'horizontal'; default 'horizontal'):
|
||||
Orientation of calendar, either vertical or horizontal. Valid
|
||||
options are 'vertical' or 'horizontal'.
|
||||
|
||||
- number_of_months_shown (number; default 1):
|
||||
Number of calendar months that are shown when calendar is opened.
|
||||
|
||||
- with_portal (boolean; default False):
|
||||
If True, calendar will open in a screen overlay portal, not
|
||||
supported on vertical calendar.
|
||||
|
||||
- with_full_screen_portal (boolean; default False):
|
||||
If True, calendar will open in a full screen overlay portal, will
|
||||
take precedent over 'withPortal' if both are set to True, not
|
||||
supported on vertical calendar.
|
||||
|
||||
- day_size (number; default 39):
|
||||
Size of rendered calendar days, higher number means bigger day
|
||||
size and larger calendar overall.
|
||||
|
||||
- is_RTL (boolean; default False):
|
||||
Determines whether the calendar and days operate from left to
|
||||
right or from right to left.
|
||||
|
||||
- disabled (boolean; default False):
|
||||
If True, no dates can be selected.
|
||||
|
||||
- className (string; optional):
|
||||
Appends a CSS class to the wrapper div component.
|
||||
|
||||
- id (string; optional):
|
||||
The ID of this component, used to identify dash components in
|
||||
callbacks. The ID needs to be unique across all of the components
|
||||
in an app.
|
||||
|
||||
- persistence (boolean | string | number; optional):
|
||||
Used to allow user interactions in this component to be persisted
|
||||
when the component - or the page - is refreshed. If `persisted` is
|
||||
truthy and hasn't changed from its previous value, a `date` that
|
||||
the user has changed while using the app will keep that change, as
|
||||
long as the new `date` also matches what was given originally.
|
||||
Used in conjunction with `persistence_type`.
|
||||
|
||||
- persisted_props (list of a value equal to: 'date's; default ['date']):
|
||||
Properties whose user interactions will persist after refreshing
|
||||
the component or the page. Since only `date` is allowed this prop
|
||||
can normally be ignored.
|
||||
|
||||
- persistence_type (a value equal to: 'local', 'session', 'memory'; default 'local'):
|
||||
Where persisted user changes will be stored: memory: only kept in
|
||||
memory, reset on page refresh. local: window.localStorage, data is
|
||||
kept after the browser quit. session: window.sessionStorage, data
|
||||
is cleared once the browser quit."""
|
||||
|
||||
_children_props = []
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "DatePickerSingle"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
date: typing.Optional[typing.Union[str, datetime.datetime]] = None,
|
||||
min_date_allowed: typing.Optional[typing.Union[str, datetime.datetime]] = None,
|
||||
max_date_allowed: typing.Optional[typing.Union[str, datetime.datetime]] = None,
|
||||
disabled_days: typing.Optional[
|
||||
typing.Sequence[typing.Union[str, datetime.datetime]]
|
||||
] = None,
|
||||
placeholder: typing.Optional[str] = None,
|
||||
initial_visible_month: typing.Optional[
|
||||
typing.Union[str, datetime.datetime]
|
||||
] = None,
|
||||
clearable: typing.Optional[bool] = None,
|
||||
reopen_calendar_on_clear: typing.Optional[bool] = None,
|
||||
display_format: typing.Optional[str] = None,
|
||||
month_format: typing.Optional[str] = None,
|
||||
first_day_of_week: typing.Optional[Literal[0, 1, 2, 3, 4, 5, 6]] = None,
|
||||
show_outside_days: typing.Optional[bool] = None,
|
||||
stay_open_on_select: typing.Optional[bool] = None,
|
||||
calendar_orientation: typing.Optional[Literal["vertical", "horizontal"]] = None,
|
||||
number_of_months_shown: typing.Optional[NumberType] = None,
|
||||
with_portal: typing.Optional[bool] = None,
|
||||
with_full_screen_portal: typing.Optional[bool] = None,
|
||||
day_size: typing.Optional[NumberType] = None,
|
||||
is_RTL: typing.Optional[bool] = None,
|
||||
disabled: typing.Optional[bool] = None,
|
||||
style: typing.Optional[typing.Any] = None,
|
||||
className: typing.Optional[str] = None,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
persistence: typing.Optional[typing.Union[bool, str, NumberType]] = None,
|
||||
persisted_props: typing.Optional[typing.Sequence[Literal["date"]]] = None,
|
||||
persistence_type: typing.Optional[Literal["local", "session", "memory"]] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = [
|
||||
"date",
|
||||
"min_date_allowed",
|
||||
"max_date_allowed",
|
||||
"disabled_days",
|
||||
"placeholder",
|
||||
"initial_visible_month",
|
||||
"clearable",
|
||||
"reopen_calendar_on_clear",
|
||||
"display_format",
|
||||
"month_format",
|
||||
"first_day_of_week",
|
||||
"show_outside_days",
|
||||
"stay_open_on_select",
|
||||
"calendar_orientation",
|
||||
"number_of_months_shown",
|
||||
"with_portal",
|
||||
"with_full_screen_portal",
|
||||
"day_size",
|
||||
"is_RTL",
|
||||
"disabled",
|
||||
"style",
|
||||
"className",
|
||||
"id",
|
||||
"persistence",
|
||||
"persisted_props",
|
||||
"persistence_type",
|
||||
]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"date",
|
||||
"min_date_allowed",
|
||||
"max_date_allowed",
|
||||
"disabled_days",
|
||||
"placeholder",
|
||||
"initial_visible_month",
|
||||
"clearable",
|
||||
"reopen_calendar_on_clear",
|
||||
"display_format",
|
||||
"month_format",
|
||||
"first_day_of_week",
|
||||
"show_outside_days",
|
||||
"stay_open_on_select",
|
||||
"calendar_orientation",
|
||||
"number_of_months_shown",
|
||||
"with_portal",
|
||||
"with_full_screen_portal",
|
||||
"day_size",
|
||||
"is_RTL",
|
||||
"disabled",
|
||||
"style",
|
||||
"className",
|
||||
"id",
|
||||
"persistence",
|
||||
"persisted_props",
|
||||
"persistence_type",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args}
|
||||
|
||||
super(DatePickerSingle, self).__init__(**args)
|
||||
|
||||
|
||||
setattr(DatePickerSingle, "__init__", _explicitize_args(DatePickerSingle.__init__))
|
||||
@@ -0,0 +1,90 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class Download(Component):
|
||||
"""A Download component.
|
||||
The Download component opens a download dialog when the data property changes.
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- id (string; optional):
|
||||
The ID of this component, used to identify dash components in
|
||||
callbacks.
|
||||
|
||||
- base64 (boolean; default False):
|
||||
Default value for base64, used when not set as part of the data
|
||||
property.
|
||||
|
||||
- data (dict; optional):
|
||||
On change, a download is invoked.
|
||||
|
||||
`data` is a dict with keys:
|
||||
|
||||
- filename (string; required):
|
||||
Suggested filename in the download dialogue.
|
||||
|
||||
- content (string; required):
|
||||
File content.
|
||||
|
||||
- base64 (boolean; optional):
|
||||
Set to True, when data is base64 encoded.
|
||||
|
||||
- type (string; optional):
|
||||
Blob type, usually a MIME-type.
|
||||
|
||||
- type (string; default 'text/plain'):
|
||||
Default value for type, used when not set as part of the data
|
||||
property."""
|
||||
|
||||
_children_props = []
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "Download"
|
||||
Data = TypedDict(
|
||||
"Data",
|
||||
{
|
||||
"filename": str,
|
||||
"content": str,
|
||||
"base64": NotRequired[bool],
|
||||
"type": NotRequired[str],
|
||||
},
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
data: typing.Optional["Data"] = None,
|
||||
base64: typing.Optional[bool] = None,
|
||||
type: typing.Optional[str] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = ["id", "base64", "data", "type"]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = ["id", "base64", "data", "type"]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args}
|
||||
|
||||
super(Download, self).__init__(**args)
|
||||
|
||||
|
||||
setattr(Download, "__init__", _explicitize_args(Download.__init__))
|
||||
@@ -0,0 +1,227 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class Dropdown(Component):
|
||||
"""A Dropdown component.
|
||||
Dropdown is an interactive dropdown element for selecting one or more
|
||||
items.
|
||||
The values and labels of the dropdown items are specified in the `options`
|
||||
property and the selected item(s) are specified with the `value` property.
|
||||
|
||||
Use a dropdown when you have many options (more than 5) or when you are
|
||||
constrained for space. Otherwise, you can use RadioItems or a Checklist,
|
||||
which have the benefit of showing the users all of the items at once.
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- options (list of dicts; optional):
|
||||
An array of options {label: [string|number], value:
|
||||
[string|number]}, an optional disabled field can be used for each
|
||||
option.
|
||||
|
||||
`options` is a list of string | number | booleans | dict | list of
|
||||
dicts with keys:
|
||||
|
||||
- label (a list of or a singular dash component, string or number; required):
|
||||
The option's label.
|
||||
|
||||
- value (string | number | boolean; required):
|
||||
The value of the option. This value corresponds to the items
|
||||
specified in the `value` property.
|
||||
|
||||
- disabled (boolean; optional):
|
||||
If True, this option is disabled and cannot be selected.
|
||||
|
||||
- title (string; optional):
|
||||
The HTML 'title' attribute for the option. Allows for
|
||||
information on hover. For more information on this attribute,
|
||||
see
|
||||
https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title.
|
||||
|
||||
- search (string; optional):
|
||||
Optional search value for the option, to use if the label is a
|
||||
component or provide a custom search value different from the
|
||||
label. If no search value and the label is a component, the
|
||||
`value` will be used for search.
|
||||
|
||||
- value (string | number | boolean | list of string | number | booleans; optional):
|
||||
The value of the input. If `multi` is False (the default) then
|
||||
value is just a string that corresponds to the values provided in
|
||||
the `options` property. If `multi` is True, then multiple values
|
||||
can be selected at once, and `value` is an array of items with
|
||||
values corresponding to those in the `options` prop.
|
||||
|
||||
- multi (boolean; default False):
|
||||
If True, the user can select multiple values.
|
||||
|
||||
- clearable (boolean; default True):
|
||||
Whether or not the dropdown is \"clearable\", that is, whether or
|
||||
not a small \"x\" appears on the right of the dropdown that
|
||||
removes the selected value.
|
||||
|
||||
- searchable (boolean; default True):
|
||||
Whether to enable the searching feature or not.
|
||||
|
||||
- search_value (string; optional):
|
||||
The value typed in the DropDown for searching.
|
||||
|
||||
- placeholder (string; optional):
|
||||
The grey, default text shown when no option is selected.
|
||||
|
||||
- disabled (boolean; default False):
|
||||
If True, this dropdown is disabled and the selection cannot be
|
||||
changed.
|
||||
|
||||
- closeOnSelect (boolean; default True):
|
||||
If False, the menu of the dropdown will not close once a value is
|
||||
selected.
|
||||
|
||||
- optionHeight (number; default 35):
|
||||
height of each option. Can be increased when label lengths would
|
||||
wrap around.
|
||||
|
||||
- maxHeight (number; default 200):
|
||||
height of the options dropdown.
|
||||
|
||||
- className (string; optional):
|
||||
className of the dropdown element.
|
||||
|
||||
- id (string; optional):
|
||||
The ID of this component, used to identify dash components in
|
||||
callbacks. The ID needs to be unique across all of the components
|
||||
in an app.
|
||||
|
||||
- persistence (boolean | string | number; optional):
|
||||
Used to allow user interactions in this component to be persisted
|
||||
when the component - or the page - is refreshed. If `persisted` is
|
||||
truthy and hasn't changed from its previous value, a `value` that
|
||||
the user has changed while using the app will keep that change, as
|
||||
long as the new `value` also matches what was given originally.
|
||||
Used in conjunction with `persistence_type`.
|
||||
|
||||
- persisted_props (list of a value equal to: 'value's; default ['value']):
|
||||
Properties whose user interactions will persist after refreshing
|
||||
the component or the page. Since only `value` is allowed this prop
|
||||
can normally be ignored.
|
||||
|
||||
- persistence_type (a value equal to: 'local', 'session', 'memory'; default 'local'):
|
||||
Where persisted user changes will be stored: memory: only kept in
|
||||
memory, reset on page refresh. local: window.localStorage, data is
|
||||
kept after the browser quit. session: window.sessionStorage, data
|
||||
is cleared once the browser quit."""
|
||||
|
||||
_children_props = ["options[].label"]
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "Dropdown"
|
||||
Options = TypedDict(
|
||||
"Options",
|
||||
{
|
||||
"label": ComponentType,
|
||||
"value": typing.Union[str, NumberType, bool],
|
||||
"disabled": NotRequired[bool],
|
||||
"title": NotRequired[str],
|
||||
"search": NotRequired[str],
|
||||
},
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
options: typing.Optional[
|
||||
typing.Union[
|
||||
typing.Sequence[typing.Union[str, NumberType, bool]],
|
||||
dict,
|
||||
typing.Sequence["Options"],
|
||||
]
|
||||
] = None,
|
||||
value: typing.Optional[
|
||||
typing.Union[
|
||||
str,
|
||||
NumberType,
|
||||
bool,
|
||||
typing.Sequence[typing.Union[str, NumberType, bool]],
|
||||
]
|
||||
] = None,
|
||||
multi: typing.Optional[bool] = None,
|
||||
clearable: typing.Optional[bool] = None,
|
||||
searchable: typing.Optional[bool] = None,
|
||||
search_value: typing.Optional[str] = None,
|
||||
placeholder: typing.Optional[str] = None,
|
||||
disabled: typing.Optional[bool] = None,
|
||||
closeOnSelect: typing.Optional[bool] = None,
|
||||
optionHeight: typing.Optional[NumberType] = None,
|
||||
maxHeight: typing.Optional[NumberType] = None,
|
||||
style: typing.Optional[typing.Any] = None,
|
||||
className: typing.Optional[str] = None,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
persistence: typing.Optional[typing.Union[bool, str, NumberType]] = None,
|
||||
persisted_props: typing.Optional[typing.Sequence[Literal["value"]]] = None,
|
||||
persistence_type: typing.Optional[Literal["local", "session", "memory"]] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = [
|
||||
"options",
|
||||
"value",
|
||||
"multi",
|
||||
"clearable",
|
||||
"searchable",
|
||||
"search_value",
|
||||
"placeholder",
|
||||
"disabled",
|
||||
"closeOnSelect",
|
||||
"optionHeight",
|
||||
"maxHeight",
|
||||
"style",
|
||||
"className",
|
||||
"id",
|
||||
"persistence",
|
||||
"persisted_props",
|
||||
"persistence_type",
|
||||
]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"options",
|
||||
"value",
|
||||
"multi",
|
||||
"clearable",
|
||||
"searchable",
|
||||
"search_value",
|
||||
"placeholder",
|
||||
"disabled",
|
||||
"closeOnSelect",
|
||||
"optionHeight",
|
||||
"maxHeight",
|
||||
"style",
|
||||
"className",
|
||||
"id",
|
||||
"persistence",
|
||||
"persisted_props",
|
||||
"persistence_type",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args}
|
||||
|
||||
super(Dropdown, self).__init__(**args)
|
||||
|
||||
|
||||
setattr(Dropdown, "__init__", _explicitize_args(Dropdown.__init__))
|
||||
@@ -0,0 +1,173 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class Geolocation(Component):
|
||||
"""A Geolocation component.
|
||||
The CurrentLocation component gets geolocation of the device from the web browser. See more info here:
|
||||
https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- id (string; optional):
|
||||
The ID used to identify this component in Dash callbacks.
|
||||
|
||||
- high_accuracy (boolean; default False):
|
||||
If True and if the device is able to provide a more accurate
|
||||
position, it will do so. Note that this can result in slower
|
||||
response times or increased power consumption (with a GPS chip on
|
||||
a mobile device for example). If False (the default value), the
|
||||
device can save resources by responding more quickly and/or using
|
||||
less power.
|
||||
|
||||
- local_date (string; optional):
|
||||
The local date and time when the device position was updated.
|
||||
Format: MM/DD/YYYY, hh:mm:ss p where p is AM or PM.
|
||||
|
||||
- maximum_age (number; default 0):
|
||||
The maximum age in milliseconds of a possible cached position that
|
||||
is acceptable to return. If set to 0, it means that the device
|
||||
cannot use a cached position and must attempt to retrieve the real
|
||||
current position. If set to Infinity the device must return a
|
||||
cached position regardless of its age. Default: 0.
|
||||
|
||||
- position (dict; optional):
|
||||
The position of the device. `lat`, `lon`, and `accuracy` will
|
||||
always be returned. The other data will be included when
|
||||
available, otherwise it will be NaN. `lat` is latitude in
|
||||
degrees. `lon` is longitude in degrees. `accuracy` is
|
||||
the accuracy of the lat/lon in meters. * `alt` is
|
||||
altitude above mean sea level in meters. `alt_accuracy` is
|
||||
the accuracy of the altitude in meters. `heading` is the
|
||||
compass heading in degrees. `speed` is the speed in meters
|
||||
per second.
|
||||
|
||||
`position` is a dict with keys:
|
||||
|
||||
- lat (number; optional)
|
||||
|
||||
- lon (number; optional)
|
||||
|
||||
- accuracy (number; optional)
|
||||
|
||||
- alt (number; optional)
|
||||
|
||||
- alt_accuracy (number; optional)
|
||||
|
||||
- heading (number; optional)
|
||||
|
||||
- speed (number; optional)
|
||||
|
||||
- position_error (dict; optional):
|
||||
Position error.
|
||||
|
||||
`position_error` is a dict with keys:
|
||||
|
||||
- code (number; optional)
|
||||
|
||||
- message (string; optional)
|
||||
|
||||
- show_alert (boolean; default False):
|
||||
If True, error messages will be displayed as an alert.
|
||||
|
||||
- timeout (number; default Infinity):
|
||||
The maximum length of time (in milliseconds) the device is allowed
|
||||
to take in order to return a position. The default value is
|
||||
Infinity, meaning that data will not be return until the position
|
||||
is available.
|
||||
|
||||
- timestamp (number; optional):
|
||||
The Unix timestamp from when the position was updated.
|
||||
|
||||
- update_now (boolean; default False):
|
||||
Forces a one-time update of the position data. If set to True in
|
||||
a callback, the browser will update the position data and reset
|
||||
update_now back to False. This can, for example, be used to
|
||||
update the position with a button or an interval timer."""
|
||||
|
||||
_children_props = []
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "Geolocation"
|
||||
Position = TypedDict(
|
||||
"Position",
|
||||
{
|
||||
"lat": NotRequired[NumberType],
|
||||
"lon": NotRequired[NumberType],
|
||||
"accuracy": NotRequired[NumberType],
|
||||
"alt": NotRequired[NumberType],
|
||||
"alt_accuracy": NotRequired[NumberType],
|
||||
"heading": NotRequired[NumberType],
|
||||
"speed": NotRequired[NumberType],
|
||||
},
|
||||
)
|
||||
|
||||
PositionError = TypedDict(
|
||||
"PositionError", {"code": NotRequired[NumberType], "message": NotRequired[str]}
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
local_date: typing.Optional[str] = None,
|
||||
timestamp: typing.Optional[NumberType] = None,
|
||||
position: typing.Optional["Position"] = None,
|
||||
position_error: typing.Optional["PositionError"] = None,
|
||||
show_alert: typing.Optional[bool] = None,
|
||||
update_now: typing.Optional[bool] = None,
|
||||
high_accuracy: typing.Optional[bool] = None,
|
||||
maximum_age: typing.Optional[NumberType] = None,
|
||||
timeout: typing.Optional[NumberType] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = [
|
||||
"id",
|
||||
"high_accuracy",
|
||||
"local_date",
|
||||
"maximum_age",
|
||||
"position",
|
||||
"position_error",
|
||||
"show_alert",
|
||||
"timeout",
|
||||
"timestamp",
|
||||
"update_now",
|
||||
]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"id",
|
||||
"high_accuracy",
|
||||
"local_date",
|
||||
"maximum_age",
|
||||
"position",
|
||||
"position_error",
|
||||
"show_alert",
|
||||
"timeout",
|
||||
"timestamp",
|
||||
"update_now",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args}
|
||||
|
||||
super(Geolocation, self).__init__(**args)
|
||||
|
||||
|
||||
setattr(Geolocation, "__init__", _explicitize_args(Geolocation.__init__))
|
||||
@@ -0,0 +1,463 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
from plotly.graph_objects import Figure
|
||||
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class Graph(Component):
|
||||
"""A Graph component.
|
||||
Graph can be used to render any plotly.js-powered data visualization.
|
||||
|
||||
You can define callbacks based on user interaction with Graphs such as
|
||||
hovering, clicking or selecting
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- id (string; optional):
|
||||
The ID of this component, used to identify dash components in
|
||||
callbacks. The ID needs to be unique across all of the components
|
||||
in an app.
|
||||
|
||||
- animate (boolean; default False):
|
||||
Beta: If True, animate between updates using plotly.js's `animate`
|
||||
function.
|
||||
|
||||
- animation_options (dict; default { frame: { redraw: False, }, transition: { duration: 750, ease: 'cubic-in-out', },}):
|
||||
Beta: Object containing animation settings. Only applies if
|
||||
`animate` is `True`.
|
||||
|
||||
- className (string; optional):
|
||||
className of the parent div.
|
||||
|
||||
- clear_on_unhover (boolean; default False):
|
||||
If True, `clear_on_unhover` will clear the `hoverData` property
|
||||
when the user \"unhovers\" from a point. If False, then the
|
||||
`hoverData` property will be equal to the data from the last point
|
||||
that was hovered over.
|
||||
|
||||
- clickAnnotationData (dict; optional):
|
||||
Data from latest click annotation event. Read-only.
|
||||
|
||||
- clickData (dict; optional):
|
||||
Data from latest click event. Read-only.
|
||||
|
||||
- config (dict; optional):
|
||||
Plotly.js config options. See
|
||||
https://plotly.com/javascript/configuration-options/ for more
|
||||
info.
|
||||
|
||||
`config` is a dict with keys:
|
||||
|
||||
- staticPlot (boolean; optional):
|
||||
No interactivity, for export or image generation.
|
||||
|
||||
- plotlyServerURL (string; optional):
|
||||
Base URL for a Plotly cloud instance, if `showSendToCloud` is
|
||||
enabled.
|
||||
|
||||
- editable (boolean; optional):
|
||||
We can edit titles, move annotations, etc - sets all pieces of
|
||||
`edits` unless a separate `edits` config item overrides
|
||||
individual parts.
|
||||
|
||||
- editSelection (boolean; optional):
|
||||
Enables moving selections.
|
||||
|
||||
- edits (dict; optional):
|
||||
A set of editable properties.
|
||||
|
||||
`edits` is a dict with keys:
|
||||
|
||||
- annotationPosition (boolean; optional):
|
||||
The main anchor of the annotation, which is the text (if
|
||||
no arrow) or the arrow (which drags the whole thing
|
||||
leaving the arrow length & direction unchanged).
|
||||
|
||||
- annotationTail (boolean; optional):
|
||||
Just for annotations with arrows, change the length and
|
||||
direction of the arrow.
|
||||
|
||||
- annotationText (boolean; optional)
|
||||
|
||||
- axisTitleText (boolean; optional)
|
||||
|
||||
- colorbarPosition (boolean; optional)
|
||||
|
||||
- colorbarTitleText (boolean; optional)
|
||||
|
||||
- legendPosition (boolean; optional)
|
||||
|
||||
- legendText (boolean; optional):
|
||||
Edit the trace name fields from the legend.
|
||||
|
||||
- shapePosition (boolean; optional)
|
||||
|
||||
- titleText (boolean; optional):
|
||||
The global `layout.title`.
|
||||
|
||||
- autosizable (boolean; optional):
|
||||
DO autosize once regardless of layout.autosize (use default
|
||||
width or height values otherwise).
|
||||
|
||||
- responsive (boolean; optional):
|
||||
Whether to change layout size when the window size changes.
|
||||
|
||||
- queueLength (number; optional):
|
||||
Set the length of the undo/redo queue.
|
||||
|
||||
- fillFrame (boolean; optional):
|
||||
If we DO autosize, do we fill the container or the screen?.
|
||||
|
||||
- frameMargins (number; optional):
|
||||
If we DO autosize, set the frame margins in percents of plot
|
||||
size.
|
||||
|
||||
- scrollZoom (boolean; optional):
|
||||
Mousewheel or two-finger scroll zooms the plot.
|
||||
|
||||
- doubleClick (a value equal to: false, 'reset', 'autosize', 'reset+autosize'; optional):
|
||||
Double click interaction (False, 'reset', 'autosize' or
|
||||
'reset+autosize').
|
||||
|
||||
- doubleClickDelay (number; optional):
|
||||
Delay for registering a double-click event in ms. The minimum
|
||||
value is 100 and the maximum value is 1000. By default this is
|
||||
300.
|
||||
|
||||
- showTips (boolean; optional):
|
||||
New users see some hints about interactivity.
|
||||
|
||||
- showAxisDragHandles (boolean; optional):
|
||||
Enable axis pan/zoom drag handles.
|
||||
|
||||
- showAxisRangeEntryBoxes (boolean; optional):
|
||||
Enable direct range entry at the pan/zoom drag points (drag
|
||||
handles must be enabled above).
|
||||
|
||||
- showLink (boolean; optional):
|
||||
Link to open this plot in plotly.
|
||||
|
||||
- sendData (boolean; optional):
|
||||
If we show a link, does it contain data or just link to a
|
||||
plotly file?.
|
||||
|
||||
- linkText (string; optional):
|
||||
Text appearing in the sendData link.
|
||||
|
||||
- displayModeBar (a value equal to: true, false, 'hover'; optional):
|
||||
Display the mode bar (True, False, or 'hover').
|
||||
|
||||
- showSendToCloud (boolean; optional):
|
||||
Should we include a modebar button to send this data to a
|
||||
Plotly Cloud instance, linked by `plotlyServerURL`. By default
|
||||
this is False.
|
||||
|
||||
- showEditInChartStudio (boolean; optional):
|
||||
Should we show a modebar button to send this data to a Plotly
|
||||
Chart Studio plot. If both this and showSendToCloud are
|
||||
selected, only showEditInChartStudio will be honored. By
|
||||
default this is False.
|
||||
|
||||
- modeBarButtonsToRemove (list; optional):
|
||||
Remove mode bar button by name. All modebar button names at
|
||||
https://github.com/plotly/plotly.js/blob/master/src/components/modebar/buttons.js
|
||||
Common names include: sendDataToCloud; (2D) zoom2d, pan2d,
|
||||
select2d, lasso2d, zoomIn2d, zoomOut2d, autoScale2d,
|
||||
resetScale2d; (Cartesian) hoverClosestCartesian,
|
||||
hoverCompareCartesian; (3D) zoom3d, pan3d, orbitRotation,
|
||||
tableRotation, handleDrag3d, resetCameraDefault3d,
|
||||
resetCameraLastSave3d, hoverClosest3d; (Geo) zoomInGeo,
|
||||
zoomOutGeo, resetGeo, hoverClosestGeo; hoverClosestGl2d,
|
||||
hoverClosestPie, toggleHover, resetViews.
|
||||
|
||||
- modeBarButtonsToAdd (list; optional):
|
||||
Add mode bar button using config objects.
|
||||
|
||||
- modeBarButtons (boolean | number | string | dict | list; optional):
|
||||
Fully custom mode bar buttons as nested array, where the outer
|
||||
arrays represents button groups, and the inner arrays have
|
||||
buttons config objects or names of default buttons.
|
||||
|
||||
- toImageButtonOptions (dict; optional):
|
||||
Modifications to how the toImage modebar button works.
|
||||
|
||||
`toImageButtonOptions` is a dict with keys:
|
||||
|
||||
- format (a value equal to: 'jpeg', 'png', 'webp', 'svg'; optional):
|
||||
The file format to create.
|
||||
|
||||
- filename (string; optional):
|
||||
The name given to the downloaded file.
|
||||
|
||||
- width (number; optional):
|
||||
Width of the downloaded file, in px.
|
||||
|
||||
- height (number; optional):
|
||||
Height of the downloaded file, in px.
|
||||
|
||||
- scale (number; optional):
|
||||
Extra resolution to give the file after rendering it with
|
||||
the given width and height.
|
||||
|
||||
- displaylogo (boolean; optional):
|
||||
Add the plotly logo on the end of the mode bar.
|
||||
|
||||
- watermark (boolean; optional):
|
||||
Add the plotly logo even with no modebar.
|
||||
|
||||
- plotGlPixelRatio (number; optional):
|
||||
Increase the pixel ratio for Gl plot images.
|
||||
|
||||
- topojsonURL (string; optional):
|
||||
URL to topojson files used in geo charts.
|
||||
|
||||
- mapboxAccessToken (boolean | number | string | dict | list; optional):
|
||||
Mapbox access token (required to plot mapbox trace types) If
|
||||
using an Mapbox Atlas server, set this option to '', so that
|
||||
plotly.js won't attempt to authenticate to the public Mapbox
|
||||
server.
|
||||
|
||||
- locale (string; optional):
|
||||
The locale to use. Locales may be provided with the plot
|
||||
(`locales` below) or by loading them on the page, see:
|
||||
https://github.com/plotly/plotly.js/blob/master/dist/README.md#to-include-localization.
|
||||
|
||||
- locales (dict; optional):
|
||||
Localization definitions, if you choose to provide them with
|
||||
the plot rather than registering them globally.
|
||||
|
||||
- extendData (list | dict; optional):
|
||||
Data that should be appended to existing traces. Has the form
|
||||
`[updateData, traceIndices, maxPoints]`, where `updateData` is an
|
||||
object containing the data to extend, `traceIndices` (optional) is
|
||||
an array of trace indices that should be extended, and `maxPoints`
|
||||
(optional) is either an integer defining the maximum number of
|
||||
points allowed or an object with key:value pairs matching
|
||||
`updateData` Reference the Plotly.extendTraces API for full usage:
|
||||
https://plotly.com/javascript/plotlyjs-function-reference/#plotlyextendtraces.
|
||||
|
||||
- figure (dict; default { data: [], layout: {}, frames: [],}):
|
||||
Plotly `figure` object. See schema:
|
||||
https://plotly.com/javascript/reference `config` is set
|
||||
separately by the `config` property.
|
||||
|
||||
`figure` is a dict with keys:
|
||||
|
||||
- data (list of dicts; optional)
|
||||
|
||||
- layout (dict; optional)
|
||||
|
||||
- frames (list of dicts; optional)
|
||||
|
||||
- hoverData (dict; optional):
|
||||
Data from latest hover event. Read-only.
|
||||
|
||||
- mathjax (boolean; default False):
|
||||
If True, loads mathjax v3 (tex-svg) into the page and use it in
|
||||
the graph.
|
||||
|
||||
- prependData (list | dict; optional):
|
||||
Data that should be prepended to existing traces. Has the form
|
||||
`[updateData, traceIndices, maxPoints]`, where `updateData` is an
|
||||
object containing the data to prepend, `traceIndices` (optional)
|
||||
is an array of trace indices that should be prepended, and
|
||||
`maxPoints` (optional) is either an integer defining the maximum
|
||||
number of points allowed or an object with key:value pairs
|
||||
matching `updateData` Reference the Plotly.prependTraces API for
|
||||
full usage:
|
||||
https://plotly.com/javascript/plotlyjs-function-reference/#plotlyprependtraces.
|
||||
|
||||
- relayoutData (dict; optional):
|
||||
Data from latest relayout event which occurs when the user zooms
|
||||
or pans on the plot or other layout-level edits. Has the form
|
||||
`{<attr string>: <value>}` describing the changes made. Read-only.
|
||||
|
||||
- responsive (a value equal to: true, false, 'auto'; default 'auto'):
|
||||
If True, the Plotly.js plot will be fully responsive to window
|
||||
resize and parent element resize event. This is achieved by
|
||||
overriding `config.responsive` to True, `figure.layout.autosize`
|
||||
to True and unsetting `figure.layout.height` and
|
||||
`figure.layout.width`. If False, the Plotly.js plot not be
|
||||
responsive to window resize and parent element resize event. This
|
||||
is achieved by overriding `config.responsive` to False and
|
||||
`figure.layout.autosize` to False. If 'auto' (default), the Graph
|
||||
will determine if the Plotly.js plot can be made fully responsive
|
||||
(True) or not (False) based on the values in `config.responsive`,
|
||||
`figure.layout.autosize`, `figure.layout.height`,
|
||||
`figure.layout.width`. This is the legacy behavior of the Graph
|
||||
component. Needs to be combined with appropriate dimension /
|
||||
styling through the `style` prop to fully take effect.
|
||||
|
||||
- restyleData (list; optional):
|
||||
Data from latest restyle event which occurs when the user toggles
|
||||
a legend item, changes parcoords selections, or other trace-level
|
||||
edits. Has the form `[edits, indices]`, where `edits` is an object
|
||||
`{<attr string>: <value>}` describing the changes made, and
|
||||
`indices` is an array of trace indices that were edited.
|
||||
Read-only.
|
||||
|
||||
- selectedData (dict; optional):
|
||||
Data from latest select event. Read-only."""
|
||||
|
||||
_children_props = []
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "Graph"
|
||||
ConfigEdits = TypedDict(
|
||||
"ConfigEdits",
|
||||
{
|
||||
"annotationPosition": NotRequired[bool],
|
||||
"annotationTail": NotRequired[bool],
|
||||
"annotationText": NotRequired[bool],
|
||||
"axisTitleText": NotRequired[bool],
|
||||
"colorbarPosition": NotRequired[bool],
|
||||
"colorbarTitleText": NotRequired[bool],
|
||||
"legendPosition": NotRequired[bool],
|
||||
"legendText": NotRequired[bool],
|
||||
"shapePosition": NotRequired[bool],
|
||||
"titleText": NotRequired[bool],
|
||||
},
|
||||
)
|
||||
|
||||
ConfigToImageButtonOptions = TypedDict(
|
||||
"ConfigToImageButtonOptions",
|
||||
{
|
||||
"format": NotRequired[Literal["jpeg", "png", "webp", "svg"]],
|
||||
"filename": NotRequired[str],
|
||||
"width": NotRequired[NumberType],
|
||||
"height": NotRequired[NumberType],
|
||||
"scale": NotRequired[NumberType],
|
||||
},
|
||||
)
|
||||
|
||||
Config = TypedDict(
|
||||
"Config",
|
||||
{
|
||||
"staticPlot": NotRequired[bool],
|
||||
"plotlyServerURL": NotRequired[str],
|
||||
"editable": NotRequired[bool],
|
||||
"editSelection": NotRequired[bool],
|
||||
"edits": NotRequired["ConfigEdits"],
|
||||
"autosizable": NotRequired[bool],
|
||||
"responsive": NotRequired[bool],
|
||||
"queueLength": NotRequired[NumberType],
|
||||
"fillFrame": NotRequired[bool],
|
||||
"frameMargins": NotRequired[NumberType],
|
||||
"scrollZoom": NotRequired[bool],
|
||||
"doubleClick": NotRequired[
|
||||
Literal[False, "reset", "autosize", "reset+autosize"]
|
||||
],
|
||||
"doubleClickDelay": NotRequired[NumberType],
|
||||
"showTips": NotRequired[bool],
|
||||
"showAxisDragHandles": NotRequired[bool],
|
||||
"showAxisRangeEntryBoxes": NotRequired[bool],
|
||||
"showLink": NotRequired[bool],
|
||||
"sendData": NotRequired[bool],
|
||||
"linkText": NotRequired[str],
|
||||
"displayModeBar": NotRequired[Literal[True, False, "hover"]],
|
||||
"showSendToCloud": NotRequired[bool],
|
||||
"showEditInChartStudio": NotRequired[bool],
|
||||
"modeBarButtonsToRemove": NotRequired[typing.Sequence],
|
||||
"modeBarButtonsToAdd": NotRequired[typing.Sequence],
|
||||
"modeBarButtons": NotRequired[typing.Any],
|
||||
"toImageButtonOptions": NotRequired["ConfigToImageButtonOptions"],
|
||||
"displaylogo": NotRequired[bool],
|
||||
"watermark": NotRequired[bool],
|
||||
"plotGlPixelRatio": NotRequired[NumberType],
|
||||
"topojsonURL": NotRequired[str],
|
||||
"mapboxAccessToken": NotRequired[typing.Any],
|
||||
"locale": NotRequired[str],
|
||||
"locales": NotRequired[dict],
|
||||
},
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
responsive: typing.Optional[Literal[True, False, "auto"]] = None,
|
||||
clickData: typing.Optional[dict] = None,
|
||||
clickAnnotationData: typing.Optional[dict] = None,
|
||||
hoverData: typing.Optional[dict] = None,
|
||||
clear_on_unhover: typing.Optional[bool] = None,
|
||||
selectedData: typing.Optional[dict] = None,
|
||||
relayoutData: typing.Optional[dict] = None,
|
||||
extendData: typing.Optional[typing.Union[typing.Sequence, dict]] = None,
|
||||
prependData: typing.Optional[typing.Union[typing.Sequence, dict]] = None,
|
||||
restyleData: typing.Optional[typing.Sequence] = None,
|
||||
figure: typing.Optional[typing.Union[Figure, dict]] = None,
|
||||
style: typing.Optional[typing.Any] = None,
|
||||
className: typing.Optional[str] = None,
|
||||
mathjax: typing.Optional[bool] = None,
|
||||
animate: typing.Optional[bool] = None,
|
||||
animation_options: typing.Optional[dict] = None,
|
||||
config: typing.Optional["Config"] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = [
|
||||
"id",
|
||||
"animate",
|
||||
"animation_options",
|
||||
"className",
|
||||
"clear_on_unhover",
|
||||
"clickAnnotationData",
|
||||
"clickData",
|
||||
"config",
|
||||
"extendData",
|
||||
"figure",
|
||||
"hoverData",
|
||||
"mathjax",
|
||||
"prependData",
|
||||
"relayoutData",
|
||||
"responsive",
|
||||
"restyleData",
|
||||
"selectedData",
|
||||
"style",
|
||||
]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"id",
|
||||
"animate",
|
||||
"animation_options",
|
||||
"className",
|
||||
"clear_on_unhover",
|
||||
"clickAnnotationData",
|
||||
"clickData",
|
||||
"config",
|
||||
"extendData",
|
||||
"figure",
|
||||
"hoverData",
|
||||
"mathjax",
|
||||
"prependData",
|
||||
"relayoutData",
|
||||
"responsive",
|
||||
"restyleData",
|
||||
"selectedData",
|
||||
"style",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args}
|
||||
|
||||
super(Graph, self).__init__(**args)
|
||||
|
||||
|
||||
setattr(Graph, "__init__", _explicitize_args(Graph.__init__))
|
||||
@@ -0,0 +1,399 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class Input(Component):
|
||||
"""An Input component.
|
||||
A basic HTML input control for entering text, numbers, or passwords.
|
||||
|
||||
Note that checkbox and radio types are supported through
|
||||
the Checklist and RadioItems component. Dates, times, and file uploads
|
||||
are also supported through separate components.
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- value (string | number; optional):
|
||||
The value of the input.
|
||||
|
||||
- type (a value equal to: 'text', 'number', 'password', 'email', 'range', 'search', 'tel', 'url', 'hidden'; default 'text'):
|
||||
The type of control to render.
|
||||
|
||||
- debounce (boolean | number; default False):
|
||||
If True, changes to input will be sent back to the Dash server
|
||||
only on enter or when losing focus. If it's False, it will send
|
||||
the value back on every change. If a number, it will not send
|
||||
anything back to the Dash server until the user has stopped typing
|
||||
for that number of seconds.
|
||||
|
||||
- placeholder (string | number; optional):
|
||||
A hint to the user of what can be entered in the control . The
|
||||
placeholder text must not contain carriage returns or line-feeds.
|
||||
Note: Do not use the placeholder attribute instead of a <label>
|
||||
element, their purposes are different. The <label> attribute
|
||||
describes the role of the form element (i.e. it indicates what
|
||||
kind of information is expected), and the placeholder attribute is
|
||||
a hint about the format that the content should take. There are
|
||||
cases in which the placeholder attribute is never displayed to the
|
||||
user, so the form must be understandable without it.
|
||||
|
||||
- n_submit (number; default 0):
|
||||
Number of times the `Enter` key was pressed while the input had
|
||||
focus.
|
||||
|
||||
- n_submit_timestamp (number; default -1):
|
||||
Last time that `Enter` was pressed.
|
||||
|
||||
- inputMode (a value equal to: 'verbatim', 'latin', 'latin-name', 'latin-prose', 'full-width-latin', 'kana', 'katakana', 'numeric', 'tel', 'email', 'url'; optional):
|
||||
Provides a hint to the browser as to the type of data that might
|
||||
be entered by the user while editing the element or its contents.
|
||||
|
||||
- autoComplete (string; optional):
|
||||
This attribute indicates whether the value of the control can be
|
||||
automatically completed by the browser.
|
||||
|
||||
- readOnly (boolean | a value equal to: 'readOnly', 'readonly', 'READONLY'; optional):
|
||||
This attribute indicates that the user cannot modify the value of
|
||||
the control. The value of the attribute is irrelevant. If you need
|
||||
read-write access to the input value, do not add the \"readonly\"
|
||||
attribute. It is ignored if the value of the type attribute is
|
||||
hidden, range, color, checkbox, radio, file, or a button type
|
||||
(such as button or submit). readOnly is an HTML boolean attribute
|
||||
- it is enabled by a boolean or 'readOnly'. Alternative
|
||||
capitalizations `readonly` & `READONLY` are also acccepted.
|
||||
|
||||
- required (a value equal to: 'required', 'REQUIRED' | boolean; optional):
|
||||
This attribute specifies that the user must fill in a value before
|
||||
submitting a form. It cannot be used when the type attribute is
|
||||
hidden, image, or a button type (submit, reset, or button). The
|
||||
:optional and :required CSS pseudo-classes will be applied to the
|
||||
field as appropriate. required is an HTML boolean attribute - it
|
||||
is enabled by a boolean or 'required'. Alternative capitalizations
|
||||
`REQUIRED` are also acccepted.
|
||||
|
||||
- autoFocus (a value equal to: 'autoFocus', 'autofocus', 'AUTOFOCUS' | boolean; optional):
|
||||
The element should be automatically focused after the page loaded.
|
||||
autoFocus is an HTML boolean attribute - it is enabled by a
|
||||
boolean or 'autoFocus'. Alternative capitalizations `autofocus` &
|
||||
`AUTOFOCUS` are also acccepted.
|
||||
|
||||
- disabled (a value equal to: 'disabled', 'DISABLED' | boolean; optional):
|
||||
If True, the input is disabled and can't be clicked on. disabled
|
||||
is an HTML boolean attribute - it is enabled by a boolean or
|
||||
'disabled'. Alternative capitalizations `DISABLED`.
|
||||
|
||||
- list (string; optional):
|
||||
Identifies a list of pre-defined options to suggest to the user.
|
||||
The value must be the id of a <datalist> element in the same
|
||||
document. The browser displays only options that are valid values
|
||||
for this input element. This attribute is ignored when the type
|
||||
attribute's value is hidden, checkbox, radio, file, or a button
|
||||
type.
|
||||
|
||||
- multiple (boolean; optional):
|
||||
This Boolean attribute indicates whether the user can enter more
|
||||
than one value. This attribute applies when the type attribute is
|
||||
set to email or file, otherwise it is ignored.
|
||||
|
||||
- spellCheck (a value equal to: 'true', 'false' | boolean; optional):
|
||||
Setting the value of this attribute to True indicates that the
|
||||
element needs to have its spelling and grammar checked. The value
|
||||
default indicates that the element is to act according to a
|
||||
default behavior, possibly based on the parent element's own
|
||||
spellcheck value. The value False indicates that the element
|
||||
should not be checked.
|
||||
|
||||
- name (string; optional):
|
||||
The name of the control, which is submitted with the form data.
|
||||
|
||||
- min (string | number; optional):
|
||||
The minimum (numeric or date-time) value for this item, which must
|
||||
not be greater than its maximum (max attribute) value.
|
||||
|
||||
- max (string | number; optional):
|
||||
The maximum (numeric or date-time) value for this item, which must
|
||||
not be less than its minimum (min attribute) value.
|
||||
|
||||
- step (string | number; default 'any'):
|
||||
Works with the min and max attributes to limit the increments at
|
||||
which a numeric or date-time value can be set. It can be the
|
||||
string any or a positive floating point number. If this attribute
|
||||
is not set to any, the control accepts only values at multiples of
|
||||
the step value greater than the minimum.
|
||||
|
||||
- minLength (string | number; optional):
|
||||
If the value of the type attribute is text, email, search,
|
||||
password, tel, or url, this attribute specifies the minimum number
|
||||
of characters (in Unicode code points) that the user can enter.
|
||||
For other control types, it is ignored.
|
||||
|
||||
- maxLength (string | number; optional):
|
||||
If the value of the type attribute is text, email, search,
|
||||
password, tel, or url, this attribute specifies the maximum number
|
||||
of characters (in UTF-16 code units) that the user can enter. For
|
||||
other control types, it is ignored. It can exceed the value of the
|
||||
size attribute. If it is not specified, the user can enter an
|
||||
unlimited number of characters. Specifying a negative number
|
||||
results in the default behavior (i.e. the user can enter an
|
||||
unlimited number of characters). The constraint is evaluated only
|
||||
when the value of the attribute has been changed.
|
||||
|
||||
- pattern (string; optional):
|
||||
A regular expression that the control's value is checked against.
|
||||
The pattern must match the entire value, not just some subset. Use
|
||||
the title attribute to describe the pattern to help the user. This
|
||||
attribute applies when the value of the type attribute is text,
|
||||
search, tel, url, email, or password, otherwise it is ignored. The
|
||||
regular expression language is the same as JavaScript RegExp
|
||||
algorithm, with the 'u' parameter that makes it treat the pattern
|
||||
as a sequence of unicode code points. The pattern is not
|
||||
surrounded by forward slashes.
|
||||
|
||||
- selectionStart (string; optional):
|
||||
The offset into the element's text content of the first selected
|
||||
character. If there's no selection, this value indicates the
|
||||
offset to the character following the current text input cursor
|
||||
position (that is, the position the next character typed would
|
||||
occupy).
|
||||
|
||||
- selectionEnd (string; optional):
|
||||
The offset into the element's text content of the last selected
|
||||
character. If there's no selection, this value indicates the
|
||||
offset to the character following the current text input cursor
|
||||
position (that is, the position the next character typed would
|
||||
occupy).
|
||||
|
||||
- selectionDirection (string; optional):
|
||||
The direction in which selection occurred. This is \"forward\" if
|
||||
the selection was made from left-to-right in an LTR locale or
|
||||
right-to-left in an RTL locale, or \"backward\" if the selection
|
||||
was made in the opposite direction. On platforms on which it's
|
||||
possible this value isn't known, the value can be \"none\"; for
|
||||
example, on macOS, the default direction is \"none\", then as the
|
||||
user begins to modify the selection using the keyboard, this will
|
||||
change to reflect the direction in which the selection is
|
||||
expanding.
|
||||
|
||||
- n_blur (number; default 0):
|
||||
Number of times the input lost focus.
|
||||
|
||||
- n_blur_timestamp (number; default -1):
|
||||
Last time the input lost focus.
|
||||
|
||||
- size (string; optional):
|
||||
The initial size of the control. This value is in pixels unless
|
||||
the value of the type attribute is text or password, in which case
|
||||
it is an integer number of characters. Starting in, this attribute
|
||||
applies only when the type attribute is set to text, search, tel,
|
||||
url, email, or password, otherwise it is ignored. In addition, the
|
||||
size must be greater than zero. If you do not specify a size, a
|
||||
default value of 20 is used.' simply states \"the user agent
|
||||
should ensure that at least that many characters are visible\",
|
||||
but different characters can have different widths in certain
|
||||
fonts. In some browsers, a certain string with x characters will
|
||||
not be entirely visible even if size is defined to at least x.
|
||||
|
||||
- className (string; optional):
|
||||
The class of the input element.
|
||||
|
||||
- id (string; optional):
|
||||
The ID of this component, used to identify dash components in
|
||||
callbacks. The ID needs to be unique across all of the components
|
||||
in an app.
|
||||
|
||||
- persistence (boolean | string | number; optional):
|
||||
Used to allow user interactions in this component to be persisted
|
||||
when the component - or the page - is refreshed. If `persisted` is
|
||||
truthy and hasn't changed from its previous value, a `value` that
|
||||
the user has changed while using the app will keep that change, as
|
||||
long as the new `value` also matches what was given originally.
|
||||
Used in conjunction with `persistence_type`.
|
||||
|
||||
- persisted_props (list of a value equal to: 'value's; default ['value']):
|
||||
Properties whose user interactions will persist after refreshing
|
||||
the component or the page. Since only `value` is allowed this prop
|
||||
can normally be ignored.
|
||||
|
||||
- persistence_type (a value equal to: 'local', 'session', 'memory'; default 'local'):
|
||||
Where persisted user changes will be stored: memory: only kept in
|
||||
memory, reset on page refresh. local: window.localStorage, data is
|
||||
kept after the browser quit. session: window.sessionStorage, data
|
||||
is cleared once the browser quit."""
|
||||
|
||||
_children_props = []
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "Input"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
value: typing.Optional[typing.Union[str, NumberType]] = None,
|
||||
type: typing.Optional[
|
||||
Literal[
|
||||
"text",
|
||||
"number",
|
||||
"password",
|
||||
"email",
|
||||
"range",
|
||||
"search",
|
||||
"tel",
|
||||
"url",
|
||||
"hidden",
|
||||
]
|
||||
] = None,
|
||||
debounce: typing.Optional[typing.Union[bool, NumberType]] = None,
|
||||
placeholder: typing.Optional[typing.Union[str, NumberType]] = None,
|
||||
n_submit: typing.Optional[NumberType] = None,
|
||||
n_submit_timestamp: typing.Optional[NumberType] = None,
|
||||
inputMode: typing.Optional[
|
||||
Literal[
|
||||
"verbatim",
|
||||
"latin",
|
||||
"latin-name",
|
||||
"latin-prose",
|
||||
"full-width-latin",
|
||||
"kana",
|
||||
"katakana",
|
||||
"numeric",
|
||||
"tel",
|
||||
"email",
|
||||
"url",
|
||||
]
|
||||
] = None,
|
||||
autoComplete: typing.Optional[str] = None,
|
||||
readOnly: typing.Optional[
|
||||
typing.Union[bool, Literal["readOnly", "readonly", "READONLY"]]
|
||||
] = None,
|
||||
required: typing.Optional[
|
||||
typing.Union[Literal["required", "REQUIRED"], bool]
|
||||
] = None,
|
||||
autoFocus: typing.Optional[
|
||||
typing.Union[Literal["autoFocus", "autofocus", "AUTOFOCUS"], bool]
|
||||
] = None,
|
||||
disabled: typing.Optional[
|
||||
typing.Union[Literal["disabled", "DISABLED"], bool]
|
||||
] = None,
|
||||
list: typing.Optional[str] = None,
|
||||
multiple: typing.Optional[bool] = None,
|
||||
spellCheck: typing.Optional[
|
||||
typing.Union[Literal["true", "false"], bool]
|
||||
] = None,
|
||||
name: typing.Optional[str] = None,
|
||||
min: typing.Optional[typing.Union[str, NumberType]] = None,
|
||||
max: typing.Optional[typing.Union[str, NumberType]] = None,
|
||||
step: typing.Optional[typing.Union[str, NumberType]] = None,
|
||||
minLength: typing.Optional[typing.Union[str, NumberType]] = None,
|
||||
maxLength: typing.Optional[typing.Union[str, NumberType]] = None,
|
||||
pattern: typing.Optional[str] = None,
|
||||
selectionStart: typing.Optional[str] = None,
|
||||
selectionEnd: typing.Optional[str] = None,
|
||||
selectionDirection: typing.Optional[str] = None,
|
||||
n_blur: typing.Optional[NumberType] = None,
|
||||
n_blur_timestamp: typing.Optional[NumberType] = None,
|
||||
size: typing.Optional[str] = None,
|
||||
style: typing.Optional[typing.Any] = None,
|
||||
className: typing.Optional[str] = None,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
persistence: typing.Optional[typing.Union[bool, str, NumberType]] = None,
|
||||
persisted_props: typing.Optional[typing.Sequence[Literal["value"]]] = None,
|
||||
persistence_type: typing.Optional[Literal["local", "session", "memory"]] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = [
|
||||
"value",
|
||||
"type",
|
||||
"debounce",
|
||||
"placeholder",
|
||||
"n_submit",
|
||||
"n_submit_timestamp",
|
||||
"inputMode",
|
||||
"autoComplete",
|
||||
"readOnly",
|
||||
"required",
|
||||
"autoFocus",
|
||||
"disabled",
|
||||
"list",
|
||||
"multiple",
|
||||
"spellCheck",
|
||||
"name",
|
||||
"min",
|
||||
"max",
|
||||
"step",
|
||||
"minLength",
|
||||
"maxLength",
|
||||
"pattern",
|
||||
"selectionStart",
|
||||
"selectionEnd",
|
||||
"selectionDirection",
|
||||
"n_blur",
|
||||
"n_blur_timestamp",
|
||||
"size",
|
||||
"style",
|
||||
"className",
|
||||
"id",
|
||||
"persistence",
|
||||
"persisted_props",
|
||||
"persistence_type",
|
||||
]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"value",
|
||||
"type",
|
||||
"debounce",
|
||||
"placeholder",
|
||||
"n_submit",
|
||||
"n_submit_timestamp",
|
||||
"inputMode",
|
||||
"autoComplete",
|
||||
"readOnly",
|
||||
"required",
|
||||
"autoFocus",
|
||||
"disabled",
|
||||
"list",
|
||||
"multiple",
|
||||
"spellCheck",
|
||||
"name",
|
||||
"min",
|
||||
"max",
|
||||
"step",
|
||||
"minLength",
|
||||
"maxLength",
|
||||
"pattern",
|
||||
"selectionStart",
|
||||
"selectionEnd",
|
||||
"selectionDirection",
|
||||
"n_blur",
|
||||
"n_blur_timestamp",
|
||||
"size",
|
||||
"style",
|
||||
"className",
|
||||
"id",
|
||||
"persistence",
|
||||
"persisted_props",
|
||||
"persistence_type",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args}
|
||||
|
||||
super(Input, self).__init__(**args)
|
||||
|
||||
|
||||
setattr(Input, "__init__", _explicitize_args(Input.__init__))
|
||||
@@ -0,0 +1,88 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class Interval(Component):
|
||||
"""An Interval component.
|
||||
A component that repeatedly increments a counter `n_intervals`
|
||||
with a fixed time delay between each increment.
|
||||
Interval is good for triggering a component on a recurring basis.
|
||||
The time delay is set with the property "interval" in milliseconds.
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- id (string; optional):
|
||||
The ID of this component, used to identify dash components in
|
||||
callbacks. The ID needs to be unique across all of the components
|
||||
in an app.
|
||||
|
||||
- disabled (boolean; optional):
|
||||
If True, the counter will no longer update.
|
||||
|
||||
- interval (number; default 1000):
|
||||
This component will increment the counter `n_intervals` every
|
||||
`interval` milliseconds.
|
||||
|
||||
- max_intervals (number; default -1):
|
||||
Number of times the interval will be fired. If -1, then the
|
||||
interval has no limit (the default) and if 0 then the interval
|
||||
stops running.
|
||||
|
||||
- n_intervals (number; default 0):
|
||||
Number of times the interval has passed."""
|
||||
|
||||
_children_props = []
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "Interval"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
interval: typing.Optional[NumberType] = None,
|
||||
disabled: typing.Optional[bool] = None,
|
||||
n_intervals: typing.Optional[NumberType] = None,
|
||||
max_intervals: typing.Optional[NumberType] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = [
|
||||
"id",
|
||||
"disabled",
|
||||
"interval",
|
||||
"max_intervals",
|
||||
"n_intervals",
|
||||
]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"id",
|
||||
"disabled",
|
||||
"interval",
|
||||
"max_intervals",
|
||||
"n_intervals",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args}
|
||||
|
||||
super(Interval, self).__init__(**args)
|
||||
|
||||
|
||||
setattr(Interval, "__init__", _explicitize_args(Interval.__init__))
|
||||
@@ -0,0 +1,132 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class Link(Component):
|
||||
"""A Link component.
|
||||
Link allows you to create a clickable link within a multi-page app.
|
||||
|
||||
For links with destinations outside the current app, `html.A` is a better
|
||||
component to use.
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- children (a list of or a singular dash component, string or number; optional):
|
||||
The children of this component.
|
||||
|
||||
- href (string; required):
|
||||
The URL of a linked resource.
|
||||
|
||||
- target (string; optional):
|
||||
Specifies where to open the link reference.
|
||||
|
||||
- refresh (boolean; default False):
|
||||
Controls whether or not the page will refresh when the link is
|
||||
clicked.
|
||||
|
||||
- title (string; optional):
|
||||
Adds the title attribute to your link, which can contain
|
||||
supplementary information.
|
||||
|
||||
- className (string; optional):
|
||||
Often used with CSS to style elements with common properties.
|
||||
|
||||
- id (string; optional):
|
||||
The ID of this component, used to identify dash components in
|
||||
callbacks. The ID needs to be unique across all of the components
|
||||
in an app.
|
||||
|
||||
- loading_state (dict; optional):
|
||||
Object that holds the loading state object coming from
|
||||
dash-renderer.
|
||||
|
||||
`loading_state` is a dict with keys:
|
||||
|
||||
- is_loading (boolean; optional):
|
||||
Determines if the component is loading or not.
|
||||
|
||||
- prop_name (string; optional):
|
||||
Holds which property is loading.
|
||||
|
||||
- component_name (string; optional):
|
||||
Holds the name of the component that is loading."""
|
||||
|
||||
_children_props = []
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "Link"
|
||||
LoadingState = TypedDict(
|
||||
"LoadingState",
|
||||
{
|
||||
"is_loading": NotRequired[bool],
|
||||
"prop_name": NotRequired[str],
|
||||
"component_name": NotRequired[str],
|
||||
},
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
children: typing.Optional[ComponentType] = None,
|
||||
href: typing.Optional[str] = None,
|
||||
target: typing.Optional[str] = None,
|
||||
refresh: typing.Optional[bool] = None,
|
||||
title: typing.Optional[str] = None,
|
||||
className: typing.Optional[str] = None,
|
||||
style: typing.Optional[typing.Any] = None,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
loading_state: typing.Optional["LoadingState"] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = [
|
||||
"children",
|
||||
"href",
|
||||
"target",
|
||||
"refresh",
|
||||
"title",
|
||||
"className",
|
||||
"style",
|
||||
"id",
|
||||
"loading_state",
|
||||
]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"children",
|
||||
"href",
|
||||
"target",
|
||||
"refresh",
|
||||
"title",
|
||||
"className",
|
||||
"style",
|
||||
"id",
|
||||
"loading_state",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args if k != "children"}
|
||||
|
||||
for k in ["href"]:
|
||||
if k not in args:
|
||||
raise TypeError("Required argument `" + k + "` was not specified.")
|
||||
|
||||
super(Link, self).__init__(children=children, **args)
|
||||
|
||||
|
||||
setattr(Link, "__init__", _explicitize_args(Link.__init__))
|
||||
@@ -0,0 +1,171 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class Loading(Component):
|
||||
"""A Loading component.
|
||||
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- children (list of a list of or a singular dash component, string or numbers | a list of or a singular dash component, string or number; optional):
|
||||
Array that holds components to render.
|
||||
|
||||
- id (string; optional):
|
||||
The ID of this component, used to identify dash components in
|
||||
callbacks. The ID needs to be unique across all of the components
|
||||
in an app.
|
||||
|
||||
- className (string; optional):
|
||||
Additional CSS class for the built-in spinner root DOM node.
|
||||
|
||||
- color (string; default '#119DFF'):
|
||||
Primary color used for the built-in loading spinners.
|
||||
|
||||
- custom_spinner (a list of or a singular dash component, string or number; optional):
|
||||
Component to use rather than the built-in spinner specified in the
|
||||
`type` prop.
|
||||
|
||||
- debug (boolean; optional):
|
||||
If True, the built-in spinner will display the component_name and
|
||||
prop_name while loading.
|
||||
|
||||
- delay_hide (number; default 0):
|
||||
Add a time delay (in ms) to the spinner being removed to prevent
|
||||
flickering.
|
||||
|
||||
- delay_show (number; default 0):
|
||||
Add a time delay (in ms) to the spinner being shown after the
|
||||
loading_state is set to True.
|
||||
|
||||
- display (a value equal to: 'auto', 'show', 'hide'; default 'auto'):
|
||||
Setting display to \"show\" or \"hide\" will override the
|
||||
loading state coming from dash-renderer.
|
||||
|
||||
- fullscreen (boolean; optional):
|
||||
Boolean that makes the built-in spinner display full-screen.
|
||||
|
||||
- overlay_style (dict; optional):
|
||||
Additional CSS styling for the spinner overlay. This is applied to
|
||||
the dcc.Loading children while the spinner is active. The default
|
||||
is `{'visibility': 'hidden'}`.
|
||||
|
||||
- parent_className (string; optional):
|
||||
Additional CSS class for the outermost dcc.Loading parent div DOM
|
||||
node.
|
||||
|
||||
- parent_style (dict; optional):
|
||||
Additional CSS styling for the outermost dcc.Loading parent div
|
||||
DOM node.
|
||||
|
||||
- show_initially (boolean; default True):
|
||||
Whether the Spinner should show on app start-up before the loading
|
||||
state has been determined. Default True. Use when also setting
|
||||
`delay_show`.
|
||||
|
||||
- target_components (dict with strings as keys and values of type string | list of strings; optional):
|
||||
Specify component and prop to trigger showing the loading spinner
|
||||
example: `{\"output-container\": \"children\", \"grid\":
|
||||
[\"rowData\", \"columnDefs]}`.
|
||||
|
||||
- type (a value equal to: 'graph', 'cube', 'circle', 'dot', 'default'; optional):
|
||||
Property that determines which built-in spinner to show one of
|
||||
'graph', 'cube', 'circle', 'dot', or 'default'."""
|
||||
|
||||
_children_props = ["custom_spinner"]
|
||||
_base_nodes = ["custom_spinner", "children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "Loading"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
children: typing.Optional[ComponentType] = None,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
type: typing.Optional[
|
||||
Literal["graph", "cube", "circle", "dot", "default"]
|
||||
] = None,
|
||||
fullscreen: typing.Optional[bool] = None,
|
||||
debug: typing.Optional[bool] = None,
|
||||
className: typing.Optional[str] = None,
|
||||
parent_className: typing.Optional[str] = None,
|
||||
style: typing.Optional[typing.Any] = None,
|
||||
parent_style: typing.Optional[dict] = None,
|
||||
overlay_style: typing.Optional[dict] = None,
|
||||
color: typing.Optional[str] = None,
|
||||
display: typing.Optional[Literal["auto", "show", "hide"]] = None,
|
||||
delay_hide: typing.Optional[NumberType] = None,
|
||||
delay_show: typing.Optional[NumberType] = None,
|
||||
show_initially: typing.Optional[bool] = None,
|
||||
target_components: typing.Optional[
|
||||
typing.Dict[
|
||||
typing.Union[str, float, int], typing.Union[str, typing.Sequence[str]]
|
||||
]
|
||||
] = None,
|
||||
custom_spinner: typing.Optional[ComponentType] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = [
|
||||
"children",
|
||||
"id",
|
||||
"className",
|
||||
"color",
|
||||
"custom_spinner",
|
||||
"debug",
|
||||
"delay_hide",
|
||||
"delay_show",
|
||||
"display",
|
||||
"fullscreen",
|
||||
"overlay_style",
|
||||
"parent_className",
|
||||
"parent_style",
|
||||
"show_initially",
|
||||
"style",
|
||||
"target_components",
|
||||
"type",
|
||||
]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"children",
|
||||
"id",
|
||||
"className",
|
||||
"color",
|
||||
"custom_spinner",
|
||||
"debug",
|
||||
"delay_hide",
|
||||
"delay_show",
|
||||
"display",
|
||||
"fullscreen",
|
||||
"overlay_style",
|
||||
"parent_className",
|
||||
"parent_style",
|
||||
"show_initially",
|
||||
"style",
|
||||
"target_components",
|
||||
"type",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args if k != "children"}
|
||||
|
||||
super(Loading, self).__init__(children=children, **args)
|
||||
|
||||
|
||||
setattr(Loading, "__init__", _explicitize_args(Loading.__init__))
|
||||
@@ -0,0 +1,94 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class Location(Component):
|
||||
"""A Location component.
|
||||
Update and track the current window.location object through the window.history state.
|
||||
Use in conjunction with the `dash_core_components.Link` component to make apps with multiple pages.
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- id (string; required):
|
||||
The ID of this component, used to identify dash components in
|
||||
callbacks. The ID needs to be unique across all of the components
|
||||
in an app.
|
||||
|
||||
- hash (string; optional):
|
||||
hash in window.location - e.g., \"#myhash\".
|
||||
|
||||
- href (string; optional):
|
||||
href in window.location - e.g.,
|
||||
\"/my/full/pathname?myargument=1#myhash\".
|
||||
|
||||
- pathname (string; optional):
|
||||
pathname in window.location - e.g., \"/my/full/pathname\".
|
||||
|
||||
- refresh (a value equal to: 'callback-nav' | boolean; default True):
|
||||
Use `True` to navigate outside the Dash app or to manually refresh
|
||||
a page. Use `False` if the same callback that updates the Location
|
||||
component is also updating the page content - typically used in
|
||||
multi-page apps that do not use Pages. Use 'callback-nav' if you
|
||||
are updating the URL in a callback, or a different callback will
|
||||
respond to the new Location with updated content. This is typical
|
||||
with multi-page apps that use Pages. This will allow for
|
||||
navigating to a new page without refreshing the page.
|
||||
|
||||
- search (string; optional):
|
||||
search in window.location - e.g., \"?myargument=1\"."""
|
||||
|
||||
_children_props = []
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "Location"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
pathname: typing.Optional[str] = None,
|
||||
search: typing.Optional[str] = None,
|
||||
hash: typing.Optional[str] = None,
|
||||
href: typing.Optional[str] = None,
|
||||
refresh: typing.Optional[typing.Union[Literal["callback-nav"], bool]] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = ["id", "hash", "href", "pathname", "refresh", "search"]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"id",
|
||||
"hash",
|
||||
"href",
|
||||
"pathname",
|
||||
"refresh",
|
||||
"search",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args}
|
||||
|
||||
for k in ["id"]:
|
||||
if k not in args:
|
||||
raise TypeError("Required argument `" + k + "` was not specified.")
|
||||
|
||||
super(Location, self).__init__(**args)
|
||||
|
||||
|
||||
setattr(Location, "__init__", _explicitize_args(Location.__init__))
|
||||
@@ -0,0 +1,122 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class Markdown(Component):
|
||||
"""A Markdown component.
|
||||
A component that renders Markdown text as specified by the
|
||||
GitHub Markdown spec. These component uses
|
||||
[react-markdown](https://rexxars.github.io/react-markdown/) under the hood.
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- children (string | list of strings; optional):
|
||||
A markdown string (or array of strings) that adheres to the
|
||||
CommonMark spec.
|
||||
|
||||
- id (string; optional):
|
||||
The ID of this component, used to identify dash components in
|
||||
callbacks. The ID needs to be unique across all of the components
|
||||
in an app.
|
||||
|
||||
- className (string; optional):
|
||||
Class name of the container element.
|
||||
|
||||
- dangerously_allow_html (boolean; default False):
|
||||
A boolean to control raw HTML escaping. Setting HTML from code is
|
||||
risky because it's easy to inadvertently expose your users to a
|
||||
cross-site scripting (XSS)
|
||||
(https://en.wikipedia.org/wiki/Cross-site_scripting) attack.
|
||||
|
||||
- dedent (boolean; default True):
|
||||
Remove matching leading whitespace from all lines. Lines that are
|
||||
empty, or contain *only* whitespace, are ignored. Both spaces and
|
||||
tab characters are removed, but only if they match; we will not
|
||||
convert tabs to spaces or vice versa.
|
||||
|
||||
- highlight_config (dict; optional):
|
||||
Config options for syntax highlighting.
|
||||
|
||||
`highlight_config` is a dict with keys:
|
||||
|
||||
- theme (a value equal to: 'dark', 'light'; optional):
|
||||
Color scheme; default 'light'.
|
||||
|
||||
- link_target (string; optional):
|
||||
A string for the target attribute to use on links (such as
|
||||
\"_blank\").
|
||||
|
||||
- mathjax (boolean; default False):
|
||||
If True, loads mathjax v3 (tex-svg) into the page and use it in
|
||||
the markdown."""
|
||||
|
||||
_children_props = []
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "Markdown"
|
||||
HighlightConfig = TypedDict(
|
||||
"HighlightConfig", {"theme": NotRequired[Literal["dark", "light"]]}
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
children: typing.Optional[ComponentType] = None,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
className: typing.Optional[str] = None,
|
||||
mathjax: typing.Optional[bool] = None,
|
||||
dangerously_allow_html: typing.Optional[bool] = None,
|
||||
link_target: typing.Optional[str] = None,
|
||||
dedent: typing.Optional[bool] = None,
|
||||
highlight_config: typing.Optional["HighlightConfig"] = None,
|
||||
style: typing.Optional[typing.Any] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = [
|
||||
"children",
|
||||
"id",
|
||||
"className",
|
||||
"dangerously_allow_html",
|
||||
"dedent",
|
||||
"highlight_config",
|
||||
"link_target",
|
||||
"mathjax",
|
||||
"style",
|
||||
]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"children",
|
||||
"id",
|
||||
"className",
|
||||
"dangerously_allow_html",
|
||||
"dedent",
|
||||
"highlight_config",
|
||||
"link_target",
|
||||
"mathjax",
|
||||
"style",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args if k != "children"}
|
||||
|
||||
super(Markdown, self).__init__(children=children, **args)
|
||||
|
||||
|
||||
setattr(Markdown, "__init__", _explicitize_args(Markdown.__init__))
|
||||
@@ -0,0 +1,177 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class RadioItems(Component):
|
||||
"""A RadioItems component.
|
||||
RadioItems is a component that encapsulates several radio item inputs.
|
||||
The values and labels of the RadioItems is specified in the `options`
|
||||
property and the seleced item is specified with the `value` property.
|
||||
Each radio item is rendered as an input with a surrounding label.
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- options (list of dicts; optional):
|
||||
An array of options, or inline dictionary of options.
|
||||
|
||||
`options` is a list of string | number | booleans | dict | list of
|
||||
dicts with keys:
|
||||
|
||||
- label (a list of or a singular dash component, string or number; required):
|
||||
The option's label.
|
||||
|
||||
- value (string | number | boolean; required):
|
||||
The value of the option. This value corresponds to the items
|
||||
specified in the `value` property.
|
||||
|
||||
- disabled (boolean; optional):
|
||||
If True, this option is disabled and cannot be selected.
|
||||
|
||||
- title (string; optional):
|
||||
The HTML 'title' attribute for the option. Allows for
|
||||
information on hover. For more information on this attribute,
|
||||
see
|
||||
https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title.
|
||||
|
||||
- value (string | number | boolean; optional):
|
||||
The currently selected value.
|
||||
|
||||
- inline (boolean; default False):
|
||||
Indicates whether the options labels should be displayed inline
|
||||
(True=horizontal) or in a block (False=vertical).
|
||||
|
||||
- className (string; optional):
|
||||
The class of the container (div).
|
||||
|
||||
- inputStyle (dict; optional):
|
||||
The style of the <input> radio element.
|
||||
|
||||
- inputClassName (string; default ''):
|
||||
The class of the <input> radio element.
|
||||
|
||||
- labelStyle (dict; optional):
|
||||
The style of the <label> that wraps the radio input and the
|
||||
option's label.
|
||||
|
||||
- labelClassName (string; default ''):
|
||||
The class of the <label> that wraps the radio input and the
|
||||
option's label.
|
||||
|
||||
- id (string; optional):
|
||||
The ID of this component, used to identify dash components in
|
||||
callbacks. The ID needs to be unique across all of the components
|
||||
in an app.
|
||||
|
||||
- persistence (boolean | string | number; optional):
|
||||
Used to allow user interactions in this component to be persisted
|
||||
when the component - or the page - is refreshed. If `persisted` is
|
||||
truthy and hasn't changed from its previous value, a `value` that
|
||||
the user has changed while using the app will keep that change, as
|
||||
long as the new `value` also matches what was given originally.
|
||||
Used in conjunction with `persistence_type`.
|
||||
|
||||
- persisted_props (list of a value equal to: 'value's; default ['value']):
|
||||
Properties whose user interactions will persist after refreshing
|
||||
the component or the page. Since only `value` is allowed this prop
|
||||
can normally be ignored.
|
||||
|
||||
- persistence_type (a value equal to: 'local', 'session', 'memory'; default 'local'):
|
||||
Where persisted user changes will be stored: memory: only kept in
|
||||
memory, reset on page refresh. local: window.localStorage, data is
|
||||
kept after the browser quit. session: window.sessionStorage, data
|
||||
is cleared once the browser quit."""
|
||||
|
||||
_children_props = ["options[].label"]
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "RadioItems"
|
||||
Options = TypedDict(
|
||||
"Options",
|
||||
{
|
||||
"label": ComponentType,
|
||||
"value": typing.Union[str, NumberType, bool],
|
||||
"disabled": NotRequired[bool],
|
||||
"title": NotRequired[str],
|
||||
},
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
options: typing.Optional[
|
||||
typing.Union[
|
||||
typing.Sequence[typing.Union[str, NumberType, bool]],
|
||||
dict,
|
||||
typing.Sequence["Options"],
|
||||
]
|
||||
] = None,
|
||||
value: typing.Optional[typing.Union[str, NumberType, bool]] = None,
|
||||
inline: typing.Optional[bool] = None,
|
||||
style: typing.Optional[typing.Any] = None,
|
||||
className: typing.Optional[str] = None,
|
||||
inputStyle: typing.Optional[dict] = None,
|
||||
inputClassName: typing.Optional[str] = None,
|
||||
labelStyle: typing.Optional[dict] = None,
|
||||
labelClassName: typing.Optional[str] = None,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
persistence: typing.Optional[typing.Union[bool, str, NumberType]] = None,
|
||||
persisted_props: typing.Optional[typing.Sequence[Literal["value"]]] = None,
|
||||
persistence_type: typing.Optional[Literal["local", "session", "memory"]] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = [
|
||||
"options",
|
||||
"value",
|
||||
"inline",
|
||||
"style",
|
||||
"className",
|
||||
"inputStyle",
|
||||
"inputClassName",
|
||||
"labelStyle",
|
||||
"labelClassName",
|
||||
"id",
|
||||
"persistence",
|
||||
"persisted_props",
|
||||
"persistence_type",
|
||||
]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"options",
|
||||
"value",
|
||||
"inline",
|
||||
"style",
|
||||
"className",
|
||||
"inputStyle",
|
||||
"inputClassName",
|
||||
"labelStyle",
|
||||
"labelClassName",
|
||||
"id",
|
||||
"persistence",
|
||||
"persisted_props",
|
||||
"persistence_type",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args}
|
||||
|
||||
super(RadioItems, self).__init__(**args)
|
||||
|
||||
|
||||
setattr(RadioItems, "__init__", _explicitize_args(RadioItems.__init__))
|
||||
@@ -0,0 +1,263 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class RangeSlider(Component):
|
||||
"""A RangeSlider component.
|
||||
A double slider with two handles.
|
||||
Used for specifying a range of numerical values.
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- min (number; optional):
|
||||
Minimum allowed value of the slider.
|
||||
|
||||
- max (number; optional):
|
||||
Maximum allowed value of the slider.
|
||||
|
||||
- step (number; optional):
|
||||
Value by which increments or decrements are made.
|
||||
|
||||
- marks (dict; optional):
|
||||
Marks on the slider. The key determines the position (a number),
|
||||
and the value determines what will show. If you want to set the
|
||||
style of a specific mark point, the value should be an object
|
||||
which contains style and label properties.
|
||||
|
||||
`marks` is a dict with strings as keys and values of type string |
|
||||
dict with keys:
|
||||
|
||||
- label (string; optional)
|
||||
|
||||
- style (dict; optional)
|
||||
|
||||
- value (list of numbers; optional):
|
||||
The value of the input.
|
||||
|
||||
- drag_value (list of numbers; optional):
|
||||
The value of the input during a drag.
|
||||
|
||||
- allowCross (boolean; optional):
|
||||
allowCross could be set as True to allow those handles to cross.
|
||||
|
||||
- pushable (boolean | number; optional):
|
||||
pushable could be set as True to allow pushing of surrounding
|
||||
handles when moving an handle. When set to a number, the number
|
||||
will be the minimum ensured distance between handles.
|
||||
|
||||
- disabled (boolean; optional):
|
||||
If True, the handles can't be moved.
|
||||
|
||||
- count (number; optional):
|
||||
Determine how many ranges to render, and multiple handles will be
|
||||
rendered (number + 1).
|
||||
|
||||
- dots (boolean; optional):
|
||||
When the step value is greater than 1, you can set the dots to
|
||||
True if you want to render the slider with dots.
|
||||
|
||||
- included (boolean; optional):
|
||||
If the value is True, it means a continuous value is included.
|
||||
Otherwise, it is an independent value.
|
||||
|
||||
- tooltip (dict; optional):
|
||||
Configuration for tooltips describing the current slider values.
|
||||
|
||||
`tooltip` is a dict with keys:
|
||||
|
||||
- always_visible (boolean; optional):
|
||||
Determines whether tooltips should always be visible (as
|
||||
opposed to the default, visible on hover).
|
||||
|
||||
- placement (a value equal to: 'left', 'right', 'top', 'bottom', 'topLeft', 'topRight', 'bottomLeft', 'bottomRight'; optional):
|
||||
Determines the placement of tooltips See
|
||||
https://github.com/react-component/tooltip#api top/bottom{*}
|
||||
sets the _origin_ of the tooltip, so e.g. `topLeft` will in
|
||||
reality appear to be on the top right of the handle.
|
||||
|
||||
- template (string; optional):
|
||||
Template string to display the tooltip in. Must contain
|
||||
`{value}`, which will be replaced with either the default
|
||||
string representation of the value or the result of the
|
||||
transform function if there is one.
|
||||
|
||||
- style (dict; optional):
|
||||
Custom style for the tooltip.
|
||||
|
||||
- transform (string; optional):
|
||||
Reference to a function in the `window.dccFunctions`
|
||||
namespace. This can be added in a script in the asset folder.
|
||||
For example, in `assets/tooltip.js`: ``` window.dccFunctions =
|
||||
window.dccFunctions || {}; window.dccFunctions.multByTen =
|
||||
function(value) { return value * 10; } ``` Then in the
|
||||
component `tooltip={'transform': 'multByTen'}`.
|
||||
|
||||
- updatemode (a value equal to: 'mouseup', 'drag'; default 'mouseup'):
|
||||
Determines when the component should update its `value` property.
|
||||
If `mouseup` (the default) then the slider will only trigger its
|
||||
value when the user has finished dragging the slider. If `drag`,
|
||||
then the slider will update its value continuously as it is being
|
||||
dragged. Note that for the latter case, the `drag_value` property
|
||||
could be used instead.
|
||||
|
||||
- vertical (boolean; optional):
|
||||
If True, the slider will be vertical.
|
||||
|
||||
- verticalHeight (number; default 400):
|
||||
The height, in px, of the slider if it is vertical.
|
||||
|
||||
- className (string; optional):
|
||||
Additional CSS class for the root DOM node.
|
||||
|
||||
- id (string; optional):
|
||||
The ID of this component, used to identify dash components in
|
||||
callbacks. The ID needs to be unique across all of the components
|
||||
in an app.
|
||||
|
||||
- persistence (boolean | string | number; optional):
|
||||
Used to allow user interactions in this component to be persisted
|
||||
when the component - or the page - is refreshed. If `persisted` is
|
||||
truthy and hasn't changed from its previous value, a `value` that
|
||||
the user has changed while using the app will keep that change, as
|
||||
long as the new `value` also matches what was given originally.
|
||||
Used in conjunction with `persistence_type`.
|
||||
|
||||
- persisted_props (list of a value equal to: 'value's; default ['value']):
|
||||
Properties whose user interactions will persist after refreshing
|
||||
the component or the page. Since only `value` is allowed this prop
|
||||
can normally be ignored.
|
||||
|
||||
- persistence_type (a value equal to: 'local', 'session', 'memory'; default 'local'):
|
||||
Where persisted user changes will be stored: memory: only kept in
|
||||
memory, reset on page refresh. local: window.localStorage, data is
|
||||
kept after the browser quit. session: window.sessionStorage, data
|
||||
is cleared once the browser quit."""
|
||||
|
||||
_children_props = []
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "RangeSlider"
|
||||
Marks = TypedDict("Marks", {"label": NotRequired[str], "style": NotRequired[dict]})
|
||||
|
||||
Tooltip = TypedDict(
|
||||
"Tooltip",
|
||||
{
|
||||
"always_visible": NotRequired[bool],
|
||||
"placement": NotRequired[
|
||||
Literal[
|
||||
"left",
|
||||
"right",
|
||||
"top",
|
||||
"bottom",
|
||||
"topLeft",
|
||||
"topRight",
|
||||
"bottomLeft",
|
||||
"bottomRight",
|
||||
]
|
||||
],
|
||||
"template": NotRequired[str],
|
||||
"style": NotRequired[dict],
|
||||
"transform": NotRequired[str],
|
||||
},
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
min: typing.Optional[NumberType] = None,
|
||||
max: typing.Optional[NumberType] = None,
|
||||
step: typing.Optional[NumberType] = None,
|
||||
marks: typing.Optional[
|
||||
typing.Dict[typing.Union[str, float, int], typing.Union[str, "Marks"]]
|
||||
] = None,
|
||||
value: typing.Optional[typing.Sequence[NumberType]] = None,
|
||||
drag_value: typing.Optional[typing.Sequence[NumberType]] = None,
|
||||
allowCross: typing.Optional[bool] = None,
|
||||
pushable: typing.Optional[typing.Union[bool, NumberType]] = None,
|
||||
disabled: typing.Optional[bool] = None,
|
||||
count: typing.Optional[NumberType] = None,
|
||||
dots: typing.Optional[bool] = None,
|
||||
included: typing.Optional[bool] = None,
|
||||
tooltip: typing.Optional["Tooltip"] = None,
|
||||
updatemode: typing.Optional[Literal["mouseup", "drag"]] = None,
|
||||
vertical: typing.Optional[bool] = None,
|
||||
verticalHeight: typing.Optional[NumberType] = None,
|
||||
className: typing.Optional[str] = None,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
persistence: typing.Optional[typing.Union[bool, str, NumberType]] = None,
|
||||
persisted_props: typing.Optional[typing.Sequence[Literal["value"]]] = None,
|
||||
persistence_type: typing.Optional[Literal["local", "session", "memory"]] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = [
|
||||
"min",
|
||||
"max",
|
||||
"step",
|
||||
"marks",
|
||||
"value",
|
||||
"drag_value",
|
||||
"allowCross",
|
||||
"pushable",
|
||||
"disabled",
|
||||
"count",
|
||||
"dots",
|
||||
"included",
|
||||
"tooltip",
|
||||
"updatemode",
|
||||
"vertical",
|
||||
"verticalHeight",
|
||||
"className",
|
||||
"id",
|
||||
"persistence",
|
||||
"persisted_props",
|
||||
"persistence_type",
|
||||
]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"min",
|
||||
"max",
|
||||
"step",
|
||||
"marks",
|
||||
"value",
|
||||
"drag_value",
|
||||
"allowCross",
|
||||
"pushable",
|
||||
"disabled",
|
||||
"count",
|
||||
"dots",
|
||||
"included",
|
||||
"tooltip",
|
||||
"updatemode",
|
||||
"vertical",
|
||||
"verticalHeight",
|
||||
"className",
|
||||
"id",
|
||||
"persistence",
|
||||
"persisted_props",
|
||||
"persistence_type",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args}
|
||||
|
||||
super(RangeSlider, self).__init__(**args)
|
||||
|
||||
|
||||
setattr(RangeSlider, "__init__", _explicitize_args(RangeSlider.__init__))
|
||||
@@ -0,0 +1,242 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class Slider(Component):
|
||||
"""A Slider component.
|
||||
A slider component with a single handle.
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- min (number; optional):
|
||||
Minimum allowed value of the slider.
|
||||
|
||||
- max (number; optional):
|
||||
Maximum allowed value of the slider.
|
||||
|
||||
- step (number; optional):
|
||||
Value by which increments or decrements are made.
|
||||
|
||||
- marks (dict; optional):
|
||||
Marks on the slider. The key determines the position (a number),
|
||||
and the value determines what will show. If you want to set the
|
||||
style of a specific mark point, the value should be an object
|
||||
which contains style and label properties.
|
||||
|
||||
`marks` is a dict with strings as keys and values of type string |
|
||||
dict with keys:
|
||||
|
||||
- label (string; optional)
|
||||
|
||||
- style (dict; optional)
|
||||
|
||||
- value (number; optional):
|
||||
The value of the input.
|
||||
|
||||
- drag_value (number; optional):
|
||||
The value of the input during a drag.
|
||||
|
||||
- disabled (boolean; optional):
|
||||
If True, the handles can't be moved.
|
||||
|
||||
- dots (boolean; optional):
|
||||
When the step value is greater than 1, you can set the dots to
|
||||
True if you want to render the slider with dots.
|
||||
|
||||
- included (boolean; optional):
|
||||
If the value is True, it means a continuous value is included.
|
||||
Otherwise, it is an independent value.
|
||||
|
||||
- tooltip (dict; optional):
|
||||
Configuration for tooltips describing the current slider value.
|
||||
|
||||
`tooltip` is a dict with keys:
|
||||
|
||||
- always_visible (boolean; optional):
|
||||
Determines whether tooltips should always be visible (as
|
||||
opposed to the default, visible on hover).
|
||||
|
||||
- placement (a value equal to: 'left', 'right', 'top', 'bottom', 'topLeft', 'topRight', 'bottomLeft', 'bottomRight'; optional):
|
||||
Determines the placement of tooltips See
|
||||
https://github.com/react-component/tooltip#api top/bottom{*}
|
||||
sets the _origin_ of the tooltip, so e.g. `topLeft` will in
|
||||
reality appear to be on the top right of the handle.
|
||||
|
||||
- template (string; optional):
|
||||
Template string to display the tooltip in. Must contain
|
||||
`{value}`, which will be replaced with either the default
|
||||
string representation of the value or the result of the
|
||||
transform function if there is one.
|
||||
|
||||
- style (dict; optional):
|
||||
Custom style for the tooltip.
|
||||
|
||||
- transform (string; optional):
|
||||
Reference to a function in the `window.dccFunctions`
|
||||
namespace. This can be added in a script in the asset folder.
|
||||
For example, in `assets/tooltip.js`: ``` window.dccFunctions =
|
||||
window.dccFunctions || {}; window.dccFunctions.multByTen =
|
||||
function(value) { return value * 10; } ``` Then in the
|
||||
component `tooltip={'transform': 'multByTen'}`.
|
||||
|
||||
- updatemode (a value equal to: 'mouseup', 'drag'; default 'mouseup'):
|
||||
Determines when the component should update its `value` property.
|
||||
If `mouseup` (the default) then the slider will only trigger its
|
||||
value when the user has finished dragging the slider. If `drag`,
|
||||
then the slider will update its value continuously as it is being
|
||||
dragged. If you want different actions during and after drag,
|
||||
leave `updatemode` as `mouseup` and use `drag_value` for the
|
||||
continuously updating value.
|
||||
|
||||
- vertical (boolean; optional):
|
||||
If True, the slider will be vertical.
|
||||
|
||||
- verticalHeight (number; default 400):
|
||||
The height, in px, of the slider if it is vertical.
|
||||
|
||||
- className (string; optional):
|
||||
Additional CSS class for the root DOM node.
|
||||
|
||||
- id (string; optional):
|
||||
The ID of this component, used to identify dash components in
|
||||
callbacks. The ID needs to be unique across all of the components
|
||||
in an app.
|
||||
|
||||
- persistence (boolean | string | number; optional):
|
||||
Used to allow user interactions in this component to be persisted
|
||||
when the component - or the page - is refreshed. If `persisted` is
|
||||
truthy and hasn't changed from its previous value, a `value` that
|
||||
the user has changed while using the app will keep that change, as
|
||||
long as the new `value` also matches what was given originally.
|
||||
Used in conjunction with `persistence_type`.
|
||||
|
||||
- persisted_props (list of a value equal to: 'value's; default ['value']):
|
||||
Properties whose user interactions will persist after refreshing
|
||||
the component or the page. Since only `value` is allowed this prop
|
||||
can normally be ignored.
|
||||
|
||||
- persistence_type (a value equal to: 'local', 'session', 'memory'; default 'local'):
|
||||
Where persisted user changes will be stored: memory: only kept in
|
||||
memory, reset on page refresh. local: window.localStorage, data is
|
||||
kept after the browser quit. session: window.sessionStorage, data
|
||||
is cleared once the browser quit."""
|
||||
|
||||
_children_props = []
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "Slider"
|
||||
Marks = TypedDict("Marks", {"label": NotRequired[str], "style": NotRequired[dict]})
|
||||
|
||||
Tooltip = TypedDict(
|
||||
"Tooltip",
|
||||
{
|
||||
"always_visible": NotRequired[bool],
|
||||
"placement": NotRequired[
|
||||
Literal[
|
||||
"left",
|
||||
"right",
|
||||
"top",
|
||||
"bottom",
|
||||
"topLeft",
|
||||
"topRight",
|
||||
"bottomLeft",
|
||||
"bottomRight",
|
||||
]
|
||||
],
|
||||
"template": NotRequired[str],
|
||||
"style": NotRequired[dict],
|
||||
"transform": NotRequired[str],
|
||||
},
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
min: typing.Optional[NumberType] = None,
|
||||
max: typing.Optional[NumberType] = None,
|
||||
step: typing.Optional[NumberType] = None,
|
||||
marks: typing.Optional[
|
||||
typing.Dict[typing.Union[str, float, int], typing.Union[str, "Marks"]]
|
||||
] = None,
|
||||
value: typing.Optional[NumberType] = None,
|
||||
drag_value: typing.Optional[NumberType] = None,
|
||||
disabled: typing.Optional[bool] = None,
|
||||
dots: typing.Optional[bool] = None,
|
||||
included: typing.Optional[bool] = None,
|
||||
tooltip: typing.Optional["Tooltip"] = None,
|
||||
updatemode: typing.Optional[Literal["mouseup", "drag"]] = None,
|
||||
vertical: typing.Optional[bool] = None,
|
||||
verticalHeight: typing.Optional[NumberType] = None,
|
||||
className: typing.Optional[str] = None,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
persistence: typing.Optional[typing.Union[bool, str, NumberType]] = None,
|
||||
persisted_props: typing.Optional[typing.Sequence[Literal["value"]]] = None,
|
||||
persistence_type: typing.Optional[Literal["local", "session", "memory"]] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = [
|
||||
"min",
|
||||
"max",
|
||||
"step",
|
||||
"marks",
|
||||
"value",
|
||||
"drag_value",
|
||||
"disabled",
|
||||
"dots",
|
||||
"included",
|
||||
"tooltip",
|
||||
"updatemode",
|
||||
"vertical",
|
||||
"verticalHeight",
|
||||
"className",
|
||||
"id",
|
||||
"persistence",
|
||||
"persisted_props",
|
||||
"persistence_type",
|
||||
]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"min",
|
||||
"max",
|
||||
"step",
|
||||
"marks",
|
||||
"value",
|
||||
"drag_value",
|
||||
"disabled",
|
||||
"dots",
|
||||
"included",
|
||||
"tooltip",
|
||||
"updatemode",
|
||||
"vertical",
|
||||
"verticalHeight",
|
||||
"className",
|
||||
"id",
|
||||
"persistence",
|
||||
"persisted_props",
|
||||
"persistence_type",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args}
|
||||
|
||||
super(Slider, self).__init__(**args)
|
||||
|
||||
|
||||
setattr(Slider, "__init__", _explicitize_args(Slider.__init__))
|
||||
@@ -0,0 +1,94 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class Store(Component):
|
||||
"""A Store component.
|
||||
Easily keep data on the client side with this component.
|
||||
The data is not inserted in the DOM.
|
||||
Data can be in memory, localStorage or sessionStorage.
|
||||
The data will be kept with the id as key.
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- id (string; required):
|
||||
The ID of this component, used to identify dash components in
|
||||
callbacks. The ID needs to be unique across all of the components
|
||||
in an app.
|
||||
|
||||
- clear_data (boolean; default False):
|
||||
Set to True to remove the data contained in `data_key`.
|
||||
|
||||
- data (dict | list | number | string | boolean; optional):
|
||||
The stored data for the id.
|
||||
|
||||
- modified_timestamp (number; default -1):
|
||||
The last time the storage was modified.
|
||||
|
||||
- storage_type (a value equal to: 'local', 'session', 'memory'; default 'memory'):
|
||||
The type of the web storage. memory: only kept in memory, reset
|
||||
on page refresh. local: window.localStorage, data is kept after
|
||||
the browser quit. session: window.sessionStorage, data is cleared
|
||||
once the browser quit."""
|
||||
|
||||
_children_props = []
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "Store"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
storage_type: typing.Optional[Literal["local", "session", "memory"]] = None,
|
||||
data: typing.Optional[
|
||||
typing.Union[dict, typing.Sequence, NumberType, str, bool]
|
||||
] = None,
|
||||
clear_data: typing.Optional[bool] = None,
|
||||
modified_timestamp: typing.Optional[NumberType] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = [
|
||||
"id",
|
||||
"clear_data",
|
||||
"data",
|
||||
"modified_timestamp",
|
||||
"storage_type",
|
||||
]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"id",
|
||||
"clear_data",
|
||||
"data",
|
||||
"modified_timestamp",
|
||||
"storage_type",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args}
|
||||
|
||||
for k in ["id"]:
|
||||
if k not in args:
|
||||
raise TypeError("Required argument `" + k + "` was not specified.")
|
||||
|
||||
super(Store, self).__init__(**args)
|
||||
|
||||
|
||||
setattr(Store, "__init__", _explicitize_args(Store.__init__))
|
||||
@@ -0,0 +1,118 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class Tab(Component):
|
||||
"""A Tab component.
|
||||
Part of dcc.Tabs - this is the child Tab component used to render a tabbed page.
|
||||
Its children will be set as the content of that tab, which if clicked will become visible.
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- children (a list of or a singular dash component, string or number; optional):
|
||||
The content of the tab - will only be displayed if this tab is
|
||||
selected.
|
||||
|
||||
- id (string; optional):
|
||||
The ID of this component, used to identify dash components in
|
||||
callbacks. The ID needs to be unique across all of the components
|
||||
in an app.
|
||||
|
||||
- className (string; optional):
|
||||
Appends a class to the Tab component.
|
||||
|
||||
- disabled (boolean; default False):
|
||||
Determines if tab is disabled or not - defaults to False.
|
||||
|
||||
- disabled_className (string; optional):
|
||||
Appends a class to the Tab component when it is disabled.
|
||||
|
||||
- disabled_style (dict; default {color: '#d6d6d6'}):
|
||||
Overrides the default (inline) styles when disabled.
|
||||
|
||||
- label (string; optional):
|
||||
The tab's label.
|
||||
|
||||
- selected_className (string; optional):
|
||||
Appends a class to the Tab component when it is selected.
|
||||
|
||||
- selected_style (dict; optional):
|
||||
Overrides the default (inline) styles for the Tab component when
|
||||
it is selected.
|
||||
|
||||
- value (string; optional):
|
||||
Value for determining which Tab is currently selected."""
|
||||
|
||||
_children_props = []
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "Tab"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
children: typing.Optional[ComponentType] = None,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
label: typing.Optional[str] = None,
|
||||
value: typing.Optional[str] = None,
|
||||
disabled: typing.Optional[bool] = None,
|
||||
disabled_style: typing.Optional[dict] = None,
|
||||
disabled_className: typing.Optional[str] = None,
|
||||
className: typing.Optional[str] = None,
|
||||
selected_className: typing.Optional[str] = None,
|
||||
style: typing.Optional[typing.Any] = None,
|
||||
selected_style: typing.Optional[dict] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = [
|
||||
"children",
|
||||
"id",
|
||||
"className",
|
||||
"disabled",
|
||||
"disabled_className",
|
||||
"disabled_style",
|
||||
"label",
|
||||
"selected_className",
|
||||
"selected_style",
|
||||
"style",
|
||||
"value",
|
||||
]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"children",
|
||||
"id",
|
||||
"className",
|
||||
"disabled",
|
||||
"disabled_className",
|
||||
"disabled_style",
|
||||
"label",
|
||||
"selected_className",
|
||||
"selected_style",
|
||||
"style",
|
||||
"value",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args if k != "children"}
|
||||
|
||||
super(Tab, self).__init__(children=children, **args)
|
||||
|
||||
|
||||
setattr(Tab, "__init__", _explicitize_args(Tab.__init__))
|
||||
@@ -0,0 +1,176 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class Tabs(Component):
|
||||
"""A Tabs component.
|
||||
A Dash component that lets you render pages with tabs - the Tabs component's children
|
||||
can be dcc.Tab components, which can hold a label that will be displayed as a tab, and can in turn hold
|
||||
children components that will be that tab's content.
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- children (list of a list of or a singular dash component, string or numbers | a list of or a singular dash component, string or number; optional):
|
||||
Array that holds Tab components.
|
||||
|
||||
- id (string; optional):
|
||||
The ID of this component, used to identify dash components in
|
||||
callbacks. The ID needs to be unique across all of the components
|
||||
in an app.
|
||||
|
||||
- className (string; optional):
|
||||
Appends a class to the Tabs container holding the individual Tab
|
||||
components.
|
||||
|
||||
- colors (dict; default { border: '#d6d6d6', primary: '#1975FA', background: '#f9f9f9',}):
|
||||
Holds the colors used by the Tabs and Tab components. If you set
|
||||
these, you should specify colors for all properties, so: colors: {
|
||||
border: '#d6d6d6', primary: '#1975FA', background: '#f9f9f9'
|
||||
}.
|
||||
|
||||
`colors` is a dict with keys:
|
||||
|
||||
- border (string; optional)
|
||||
|
||||
- primary (string; optional)
|
||||
|
||||
- background (string; optional)
|
||||
|
||||
- content_className (string; optional):
|
||||
Appends a class to the Tab content container holding the children
|
||||
of the Tab that is selected.
|
||||
|
||||
- content_style (dict; optional):
|
||||
Appends (inline) styles to the tab content container holding the
|
||||
children of the Tab that is selected.
|
||||
|
||||
- mobile_breakpoint (number; default 800):
|
||||
Breakpoint at which tabs are rendered full width (can be 0 if you
|
||||
don't want full width tabs on mobile).
|
||||
|
||||
- parent_className (string; optional):
|
||||
Appends a class to the top-level parent container holding both the
|
||||
Tabs container and the content container.
|
||||
|
||||
- parent_style (dict; optional):
|
||||
Appends (inline) styles to the top-level parent container holding
|
||||
both the Tabs container and the content container.
|
||||
|
||||
- persisted_props (list of a value equal to: 'value's; default ['value']):
|
||||
Properties whose user interactions will persist after refreshing
|
||||
the component or the page. Since only `value` is allowed this prop
|
||||
can normally be ignored.
|
||||
|
||||
- persistence (boolean | string | number; optional):
|
||||
Used to allow user interactions in this component to be persisted
|
||||
when the component - or the page - is refreshed. If `persisted` is
|
||||
truthy and hasn't changed from its previous value, a `value` that
|
||||
the user has changed while using the app will keep that change, as
|
||||
long as the new `value` also matches what was given originally.
|
||||
Used in conjunction with `persistence_type`.
|
||||
|
||||
- persistence_type (a value equal to: 'local', 'session', 'memory'; default 'local'):
|
||||
Where persisted user changes will be stored: memory: only kept in
|
||||
memory, reset on page refresh. local: window.localStorage, data is
|
||||
kept after the browser quit. session: window.sessionStorage, data
|
||||
is cleared once the browser quit.
|
||||
|
||||
- value (string; optional):
|
||||
The value of the currently selected Tab.
|
||||
|
||||
- vertical (boolean; default False):
|
||||
Renders the tabs vertically (on the side)."""
|
||||
|
||||
_children_props = []
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "Tabs"
|
||||
Colors = TypedDict(
|
||||
"Colors",
|
||||
{
|
||||
"border": NotRequired[str],
|
||||
"primary": NotRequired[str],
|
||||
"background": NotRequired[str],
|
||||
},
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
children: typing.Optional[ComponentType] = None,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
value: typing.Optional[str] = None,
|
||||
className: typing.Optional[str] = None,
|
||||
content_className: typing.Optional[str] = None,
|
||||
parent_className: typing.Optional[str] = None,
|
||||
style: typing.Optional[typing.Any] = None,
|
||||
parent_style: typing.Optional[dict] = None,
|
||||
content_style: typing.Optional[dict] = None,
|
||||
vertical: typing.Optional[bool] = None,
|
||||
mobile_breakpoint: typing.Optional[NumberType] = None,
|
||||
colors: typing.Optional["Colors"] = None,
|
||||
persistence: typing.Optional[typing.Union[bool, str, NumberType]] = None,
|
||||
persisted_props: typing.Optional[typing.Sequence[Literal["value"]]] = None,
|
||||
persistence_type: typing.Optional[Literal["local", "session", "memory"]] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = [
|
||||
"children",
|
||||
"id",
|
||||
"className",
|
||||
"colors",
|
||||
"content_className",
|
||||
"content_style",
|
||||
"mobile_breakpoint",
|
||||
"parent_className",
|
||||
"parent_style",
|
||||
"persisted_props",
|
||||
"persistence",
|
||||
"persistence_type",
|
||||
"style",
|
||||
"value",
|
||||
"vertical",
|
||||
]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"children",
|
||||
"id",
|
||||
"className",
|
||||
"colors",
|
||||
"content_className",
|
||||
"content_style",
|
||||
"mobile_breakpoint",
|
||||
"parent_className",
|
||||
"parent_style",
|
||||
"persisted_props",
|
||||
"persistence",
|
||||
"persistence_type",
|
||||
"style",
|
||||
"value",
|
||||
"vertical",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args if k != "children"}
|
||||
|
||||
super(Tabs, self).__init__(children=children, **args)
|
||||
|
||||
|
||||
setattr(Tabs, "__init__", _explicitize_args(Tabs.__init__))
|
||||
@@ -0,0 +1,275 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class Textarea(Component):
|
||||
"""A Textarea component.
|
||||
A basic HTML textarea for entering multiline text.
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- id (string; optional):
|
||||
The ID of this component, used to identify dash components in
|
||||
callbacks. The ID needs to be unique across all of the components
|
||||
in an app.
|
||||
|
||||
- accessKey (string; optional):
|
||||
Defines a keyboard shortcut to activate or add focus to the
|
||||
element.
|
||||
|
||||
- autoFocus (string; optional):
|
||||
The element should be automatically focused after the page loaded.
|
||||
|
||||
- className (string; optional):
|
||||
Often used with CSS to style elements with common properties.
|
||||
|
||||
- cols (string | number; optional):
|
||||
Defines the number of columns in a textarea.
|
||||
|
||||
- contentEditable (string | boolean; optional):
|
||||
Indicates whether the element's content is editable.
|
||||
|
||||
- contextMenu (string; optional):
|
||||
Defines the ID of a <menu> element which will serve as the
|
||||
element's context menu.
|
||||
|
||||
- dir (string; optional):
|
||||
Defines the text direction. Allowed values are ltr (Left-To-Right)
|
||||
or rtl (Right-To-Left).
|
||||
|
||||
- disabled (string | boolean; optional):
|
||||
Indicates whether the user can interact with the element.
|
||||
|
||||
- draggable (a value equal to: 'true', 'false' | boolean; optional):
|
||||
Defines whether the element can be dragged.
|
||||
|
||||
- form (string; optional):
|
||||
Indicates the form that is the owner of the element.
|
||||
|
||||
- hidden (string; optional):
|
||||
Prevents rendering of given element, while keeping child elements,
|
||||
e.g. script elements, active.
|
||||
|
||||
- lang (string; optional):
|
||||
Defines the language used in the element.
|
||||
|
||||
- maxLength (string | number; optional):
|
||||
Defines the maximum number of characters allowed in the element.
|
||||
|
||||
- minLength (string | number; optional):
|
||||
Defines the minimum number of characters allowed in the element.
|
||||
|
||||
- n_blur (number; default 0):
|
||||
Number of times the textarea lost focus.
|
||||
|
||||
- n_blur_timestamp (number; default -1):
|
||||
Last time the textarea lost focus.
|
||||
|
||||
- n_clicks (number; optional):
|
||||
Number of times the textarea has been clicked.
|
||||
|
||||
- n_clicks_timestamp (number; default -1):
|
||||
Last time the textarea was clicked.
|
||||
|
||||
- name (string; optional):
|
||||
Name of the element. For example used by the server to identify
|
||||
the fields in form submits.
|
||||
|
||||
- persisted_props (list of a value equal to: 'value's; optional):
|
||||
Properties whose user interactions will persist after refreshing
|
||||
the component or the page. Since only `value` is allowed this prop
|
||||
can normally be ignored.
|
||||
|
||||
- persistence (boolean | string | number; optional):
|
||||
Used to allow user interactions in this component to be persisted
|
||||
when the component - or the page - is refreshed. If `persisted` is
|
||||
truthy and hasn't changed from its previous value, a `value` that
|
||||
the user has changed while using the app will keep that change, as
|
||||
long as the new `value` also matches what was given originally.
|
||||
Used in conjunction with `persistence_type`.
|
||||
|
||||
- persistence_type (a value equal to: 'local', 'session', 'memory'; optional):
|
||||
Where persisted user changes will be stored: memory: only kept in
|
||||
memory, reset on page refresh. local: window.localStorage, data is
|
||||
kept after the browser quit. session: window.sessionStorage, data
|
||||
is cleared once the browser quit.
|
||||
|
||||
- placeholder (string; optional):
|
||||
Provides a hint to the user of what can be entered in the field.
|
||||
|
||||
- readOnly (boolean | a value equal to: 'readOnly', 'readonly', 'READONLY'; optional):
|
||||
Indicates whether the element can be edited. readOnly is an HTML
|
||||
boolean attribute - it is enabled by a boolean or 'readOnly'.
|
||||
Alternative capitalizations `readonly` & `READONLY` are also
|
||||
acccepted.
|
||||
|
||||
- required (a value equal to: 'required', 'REQUIRED' | boolean; optional):
|
||||
Indicates whether this element is required to fill out or not.
|
||||
required is an HTML boolean attribute - it is enabled by a boolean
|
||||
or 'required'. Alternative capitalizations `REQUIRED` are also
|
||||
acccepted.
|
||||
|
||||
- rows (string | number; optional):
|
||||
Defines the number of rows in a text area.
|
||||
|
||||
- spellCheck (a value equal to: 'true', 'false' | boolean; optional):
|
||||
Indicates whether spell checking is allowed for the element.
|
||||
|
||||
- tabIndex (string | number; optional):
|
||||
Overrides the browser's default tab order and follows the one
|
||||
specified instead.
|
||||
|
||||
- title (string; optional):
|
||||
Text to be displayed in a tooltip when hovering over the element.
|
||||
|
||||
- value (string; optional):
|
||||
The value of the textarea.
|
||||
|
||||
- wrap (string; optional):
|
||||
Indicates whether the text should be wrapped."""
|
||||
|
||||
_children_props = []
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "Textarea"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
value: typing.Optional[str] = None,
|
||||
autoFocus: typing.Optional[str] = None,
|
||||
cols: typing.Optional[typing.Union[str, NumberType]] = None,
|
||||
disabled: typing.Optional[typing.Union[str, bool]] = None,
|
||||
form: typing.Optional[str] = None,
|
||||
maxLength: typing.Optional[typing.Union[str, NumberType]] = None,
|
||||
minLength: typing.Optional[typing.Union[str, NumberType]] = None,
|
||||
name: typing.Optional[str] = None,
|
||||
placeholder: typing.Optional[str] = None,
|
||||
readOnly: typing.Optional[
|
||||
typing.Union[bool, Literal["readOnly", "readonly", "READONLY"]]
|
||||
] = None,
|
||||
required: typing.Optional[
|
||||
typing.Union[Literal["required", "REQUIRED"], bool]
|
||||
] = None,
|
||||
rows: typing.Optional[typing.Union[str, NumberType]] = None,
|
||||
wrap: typing.Optional[str] = None,
|
||||
accessKey: typing.Optional[str] = None,
|
||||
className: typing.Optional[str] = None,
|
||||
contentEditable: typing.Optional[typing.Union[str, bool]] = None,
|
||||
contextMenu: typing.Optional[str] = None,
|
||||
dir: typing.Optional[str] = None,
|
||||
draggable: typing.Optional[typing.Union[Literal["true", "false"], bool]] = None,
|
||||
hidden: typing.Optional[str] = None,
|
||||
lang: typing.Optional[str] = None,
|
||||
spellCheck: typing.Optional[
|
||||
typing.Union[Literal["true", "false"], bool]
|
||||
] = None,
|
||||
style: typing.Optional[typing.Any] = None,
|
||||
tabIndex: typing.Optional[typing.Union[str, NumberType]] = None,
|
||||
title: typing.Optional[str] = None,
|
||||
n_blur: typing.Optional[NumberType] = None,
|
||||
n_blur_timestamp: typing.Optional[NumberType] = None,
|
||||
n_clicks: typing.Optional[NumberType] = None,
|
||||
n_clicks_timestamp: typing.Optional[NumberType] = None,
|
||||
persistence: typing.Optional[typing.Union[bool, str, NumberType]] = None,
|
||||
persisted_props: typing.Optional[typing.Sequence[Literal["value"]]] = None,
|
||||
persistence_type: typing.Optional[Literal["local", "session", "memory"]] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = [
|
||||
"id",
|
||||
"accessKey",
|
||||
"autoFocus",
|
||||
"className",
|
||||
"cols",
|
||||
"contentEditable",
|
||||
"contextMenu",
|
||||
"dir",
|
||||
"disabled",
|
||||
"draggable",
|
||||
"form",
|
||||
"hidden",
|
||||
"lang",
|
||||
"maxLength",
|
||||
"minLength",
|
||||
"n_blur",
|
||||
"n_blur_timestamp",
|
||||
"n_clicks",
|
||||
"n_clicks_timestamp",
|
||||
"name",
|
||||
"persisted_props",
|
||||
"persistence",
|
||||
"persistence_type",
|
||||
"placeholder",
|
||||
"readOnly",
|
||||
"required",
|
||||
"rows",
|
||||
"spellCheck",
|
||||
"style",
|
||||
"tabIndex",
|
||||
"title",
|
||||
"value",
|
||||
"wrap",
|
||||
]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"id",
|
||||
"accessKey",
|
||||
"autoFocus",
|
||||
"className",
|
||||
"cols",
|
||||
"contentEditable",
|
||||
"contextMenu",
|
||||
"dir",
|
||||
"disabled",
|
||||
"draggable",
|
||||
"form",
|
||||
"hidden",
|
||||
"lang",
|
||||
"maxLength",
|
||||
"minLength",
|
||||
"n_blur",
|
||||
"n_blur_timestamp",
|
||||
"n_clicks",
|
||||
"n_clicks_timestamp",
|
||||
"name",
|
||||
"persisted_props",
|
||||
"persistence",
|
||||
"persistence_type",
|
||||
"placeholder",
|
||||
"readOnly",
|
||||
"required",
|
||||
"rows",
|
||||
"spellCheck",
|
||||
"style",
|
||||
"tabIndex",
|
||||
"title",
|
||||
"value",
|
||||
"wrap",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args}
|
||||
|
||||
super(Textarea, self).__init__(**args)
|
||||
|
||||
|
||||
setattr(Textarea, "__init__", _explicitize_args(Textarea.__init__))
|
||||
@@ -0,0 +1,145 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class Tooltip(Component):
|
||||
"""A Tooltip component.
|
||||
A tooltip with an absolute position.
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- children (a list of or a singular dash component, string or number; optional):
|
||||
The contents of the tooltip.
|
||||
|
||||
- id (string; optional):
|
||||
The ID of this component, used to identify dash components in
|
||||
callbacks. The ID needs to be unique across all of the components
|
||||
in an app.
|
||||
|
||||
- background_color (string; default 'white'):
|
||||
Color of the tooltip background, as a CSS color string.
|
||||
|
||||
- bbox (dict; optional):
|
||||
The bounding box coordinates of the item to label, in px relative
|
||||
to the positioning parent of the Tooltip component.
|
||||
|
||||
`bbox` is a dict with keys:
|
||||
|
||||
- x0 (number; optional)
|
||||
|
||||
- y0 (number; optional)
|
||||
|
||||
- x1 (number; optional)
|
||||
|
||||
- y1 (number; optional)
|
||||
|
||||
- border_color (string; default '#d6d6d6'):
|
||||
Color of the tooltip border, as a CSS color string.
|
||||
|
||||
- className (string; default ''):
|
||||
The class of the tooltip.
|
||||
|
||||
- direction (a value equal to: 'top', 'right', 'bottom', 'left'; default 'right'):
|
||||
The side of the `bbox` on which the tooltip should open.
|
||||
|
||||
- loading_text (string; default 'Loading...'):
|
||||
The text displayed in the tooltip while loading.
|
||||
|
||||
- show (boolean; default True):
|
||||
Whether to show the tooltip.
|
||||
|
||||
- targetable (boolean; default False):
|
||||
Whether the tooltip itself can be targeted by pointer events. For
|
||||
tooltips triggered by hover events, typically this should be left
|
||||
`False` to avoid the tooltip interfering with those same events.
|
||||
|
||||
- zindex (number; default 1):
|
||||
The `z-index` CSS property to assign to the tooltip. Components
|
||||
with higher values will be displayed on top of components with
|
||||
lower values."""
|
||||
|
||||
_children_props = []
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "Tooltip"
|
||||
Bbox = TypedDict(
|
||||
"Bbox",
|
||||
{
|
||||
"x0": NotRequired[NumberType],
|
||||
"y0": NotRequired[NumberType],
|
||||
"x1": NotRequired[NumberType],
|
||||
"y1": NotRequired[NumberType],
|
||||
},
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
children: typing.Optional[ComponentType] = None,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
className: typing.Optional[str] = None,
|
||||
style: typing.Optional[typing.Any] = None,
|
||||
bbox: typing.Optional["Bbox"] = None,
|
||||
show: typing.Optional[bool] = None,
|
||||
direction: typing.Optional[Literal["top", "right", "bottom", "left"]] = None,
|
||||
border_color: typing.Optional[str] = None,
|
||||
background_color: typing.Optional[str] = None,
|
||||
loading_text: typing.Optional[str] = None,
|
||||
zindex: typing.Optional[NumberType] = None,
|
||||
targetable: typing.Optional[bool] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = [
|
||||
"children",
|
||||
"id",
|
||||
"background_color",
|
||||
"bbox",
|
||||
"border_color",
|
||||
"className",
|
||||
"direction",
|
||||
"loading_text",
|
||||
"show",
|
||||
"style",
|
||||
"targetable",
|
||||
"zindex",
|
||||
]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"children",
|
||||
"id",
|
||||
"background_color",
|
||||
"bbox",
|
||||
"border_color",
|
||||
"className",
|
||||
"direction",
|
||||
"loading_text",
|
||||
"show",
|
||||
"style",
|
||||
"targetable",
|
||||
"zindex",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args if k != "children"}
|
||||
|
||||
super(Tooltip, self).__init__(children=children, **args)
|
||||
|
||||
|
||||
setattr(Tooltip, "__init__", _explicitize_args(Tooltip.__init__))
|
||||
@@ -0,0 +1,173 @@
|
||||
# AUTO GENERATED FILE - DO NOT EDIT
|
||||
|
||||
import typing # noqa: F401
|
||||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
|
||||
from dash.development.base_component import Component, _explicitize_args
|
||||
|
||||
ComponentType = typing.Union[
|
||||
str,
|
||||
int,
|
||||
float,
|
||||
Component,
|
||||
None,
|
||||
typing.Sequence[typing.Union[str, int, float, Component, None]],
|
||||
]
|
||||
|
||||
NumberType = typing.Union[
|
||||
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
|
||||
]
|
||||
|
||||
|
||||
class Upload(Component):
|
||||
"""An Upload component.
|
||||
Upload components allow your app to accept user-uploaded files via drag'n'drop
|
||||
|
||||
Keyword arguments:
|
||||
|
||||
- children (a list of or a singular dash component, string or number | string; optional):
|
||||
Contents of the upload component.
|
||||
|
||||
- id (string; optional):
|
||||
The ID of this component, used to identify dash components in
|
||||
callbacks. The ID needs to be unique across all of the components
|
||||
in an app.
|
||||
|
||||
- accept (string; optional):
|
||||
Allow specific types of files. See
|
||||
https://github.com/okonet/attr-accept for more information. Keep
|
||||
in mind that mime type determination is not reliable across
|
||||
platforms. CSV files, for example, are reported as text/plain
|
||||
under macOS but as application/vnd.ms-excel under Windows. In some
|
||||
cases there might not be a mime type set at all. See:
|
||||
https://github.com/react-dropzone/react-dropzone/issues/276.
|
||||
|
||||
- className (string; optional):
|
||||
HTML class name of the component.
|
||||
|
||||
- className_active (string; optional):
|
||||
HTML class name of the component while active.
|
||||
|
||||
- className_disabled (string; optional):
|
||||
HTML class name of the component if disabled.
|
||||
|
||||
- className_reject (string; optional):
|
||||
HTML class name of the component if rejected.
|
||||
|
||||
- contents (string | list of strings; optional):
|
||||
The contents of the uploaded file as a binary string.
|
||||
|
||||
- disable_click (boolean; default False):
|
||||
Disallow clicking on the component to open the file dialog.
|
||||
|
||||
- disabled (boolean; default False):
|
||||
Enable/disable the upload component entirely.
|
||||
|
||||
- filename (string | list of strings; optional):
|
||||
The name of the file(s) that was(were) uploaded. Note that this
|
||||
does not include the path of the file (for security reasons).
|
||||
|
||||
- last_modified (number | list of numbers; optional):
|
||||
The last modified date of the file that was uploaded in unix time
|
||||
(seconds since 1970).
|
||||
|
||||
- max_size (number; default -1):
|
||||
Maximum file size in bytes. If `-1`, then infinite.
|
||||
|
||||
- min_size (number; default 0):
|
||||
Minimum file size in bytes.
|
||||
|
||||
- multiple (boolean; default False):
|
||||
Allow dropping multiple files.
|
||||
|
||||
- style_active (dict; default { borderStyle: 'solid', borderColor: '#6c6', backgroundColor: '#eee',}):
|
||||
CSS styles to apply while active.
|
||||
|
||||
- style_disabled (dict; default { opacity: 0.5,}):
|
||||
CSS styles if disabled.
|
||||
|
||||
- style_reject (dict; default { borderStyle: 'solid', borderColor: '#c66', backgroundColor: '#eee',}):
|
||||
CSS styles if rejected."""
|
||||
|
||||
_children_props = []
|
||||
_base_nodes = ["children"]
|
||||
_namespace = "dash_core_components"
|
||||
_type = "Upload"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
children: typing.Optional[ComponentType] = None,
|
||||
id: typing.Optional[typing.Union[str, dict]] = None,
|
||||
contents: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,
|
||||
filename: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,
|
||||
last_modified: typing.Optional[
|
||||
typing.Union[NumberType, typing.Sequence[NumberType]]
|
||||
] = None,
|
||||
accept: typing.Optional[str] = None,
|
||||
disabled: typing.Optional[bool] = None,
|
||||
disable_click: typing.Optional[bool] = None,
|
||||
max_size: typing.Optional[NumberType] = None,
|
||||
min_size: typing.Optional[NumberType] = None,
|
||||
multiple: typing.Optional[bool] = None,
|
||||
className: typing.Optional[str] = None,
|
||||
className_active: typing.Optional[str] = None,
|
||||
className_reject: typing.Optional[str] = None,
|
||||
className_disabled: typing.Optional[str] = None,
|
||||
style: typing.Optional[typing.Any] = None,
|
||||
style_active: typing.Optional[dict] = None,
|
||||
style_reject: typing.Optional[dict] = None,
|
||||
style_disabled: typing.Optional[dict] = None,
|
||||
**kwargs
|
||||
):
|
||||
self._prop_names = [
|
||||
"children",
|
||||
"id",
|
||||
"accept",
|
||||
"className",
|
||||
"className_active",
|
||||
"className_disabled",
|
||||
"className_reject",
|
||||
"contents",
|
||||
"disable_click",
|
||||
"disabled",
|
||||
"filename",
|
||||
"last_modified",
|
||||
"max_size",
|
||||
"min_size",
|
||||
"multiple",
|
||||
"style",
|
||||
"style_active",
|
||||
"style_disabled",
|
||||
"style_reject",
|
||||
]
|
||||
self._valid_wildcard_attributes = []
|
||||
self.available_properties = [
|
||||
"children",
|
||||
"id",
|
||||
"accept",
|
||||
"className",
|
||||
"className_active",
|
||||
"className_disabled",
|
||||
"className_reject",
|
||||
"contents",
|
||||
"disable_click",
|
||||
"disabled",
|
||||
"filename",
|
||||
"last_modified",
|
||||
"max_size",
|
||||
"min_size",
|
||||
"multiple",
|
||||
"style",
|
||||
"style_active",
|
||||
"style_disabled",
|
||||
"style_reject",
|
||||
]
|
||||
self.available_wildcard_properties = []
|
||||
_explicit_args = kwargs.pop("_explicit_args")
|
||||
_locals = locals()
|
||||
_locals.update(kwargs) # For wildcard attrs and excess named props
|
||||
args = {k: _locals[k] for k in _explicit_args if k != "children"}
|
||||
|
||||
super(Upload, self).__init__(children=children, **args)
|
||||
|
||||
|
||||
setattr(Upload, "__init__", _explicitize_args(Upload.__init__))
|
||||
@@ -0,0 +1,129 @@
|
||||
import json
|
||||
import os as _os
|
||||
import sys as _sys
|
||||
|
||||
import dash as _dash
|
||||
|
||||
from ._imports_ import * # noqa: F401, F403, E402
|
||||
from ._imports_ import __all__ as _components
|
||||
from .express import ( # noqa: F401, E402
|
||||
send_bytes,
|
||||
send_data_frame,
|
||||
send_file,
|
||||
send_string,
|
||||
)
|
||||
|
||||
__all__ = _components + [ # type: ignore[reportUnsupportedDunderAll]
|
||||
"send_bytes",
|
||||
"send_data_frame",
|
||||
"send_file",
|
||||
"send_string",
|
||||
]
|
||||
|
||||
_basepath = _os.path.dirname(__file__)
|
||||
_filepath = _os.path.abspath(_os.path.join(_basepath, "package-info.json"))
|
||||
with open(_filepath) as f:
|
||||
package = json.load(f)
|
||||
|
||||
package_name = package["name"].replace(" ", "_").replace("-", "_")
|
||||
__version__ = package["version"]
|
||||
|
||||
# Module imports trigger a dash.development import, need to check this first
|
||||
if not hasattr(_dash, "__plotly_dash") and not hasattr(_dash, "development"):
|
||||
print(
|
||||
"Dash was not successfully imported. Make sure you don't have a file "
|
||||
"named \n'dash.py' in your current directory.",
|
||||
file=_sys.stderr,
|
||||
)
|
||||
_sys.exit(1)
|
||||
|
||||
|
||||
_current_path = _os.path.dirname(_os.path.abspath(__file__))
|
||||
|
||||
|
||||
_this_module = "dash_core_components"
|
||||
|
||||
async_resources = [
|
||||
"datepicker",
|
||||
"dropdown",
|
||||
"graph",
|
||||
"highlight",
|
||||
"markdown",
|
||||
"mathjax",
|
||||
"slider",
|
||||
"upload",
|
||||
]
|
||||
|
||||
_js_dist = []
|
||||
|
||||
_js_dist.extend(
|
||||
[
|
||||
{
|
||||
"relative_package_path": "dcc/async-{}.js".format(async_resource),
|
||||
"external_url": (
|
||||
"https://unpkg.com/dash-core-components@{}"
|
||||
"/dash_core_components/async-{}.js"
|
||||
).format(__version__, async_resource),
|
||||
"namespace": "dash",
|
||||
"async": True,
|
||||
}
|
||||
for async_resource in async_resources
|
||||
]
|
||||
)
|
||||
|
||||
_js_dist.extend(
|
||||
[
|
||||
{
|
||||
"relative_package_path": "dcc/async-{}.js.map".format(async_resource),
|
||||
"external_url": (
|
||||
"https://unpkg.com/dash-core-components@{}"
|
||||
"/dash_core_components/async-{}.js.map"
|
||||
).format(__version__, async_resource),
|
||||
"namespace": "dash",
|
||||
"dynamic": True,
|
||||
}
|
||||
for async_resource in async_resources
|
||||
]
|
||||
)
|
||||
|
||||
_js_dist.extend(
|
||||
[
|
||||
{
|
||||
"relative_package_path": "dcc/{}.js".format(_this_module),
|
||||
"external_url": (
|
||||
"https://unpkg.com/dash-core-components@{}"
|
||||
"/dash_core_components/dash_core_components.js"
|
||||
).format(__version__),
|
||||
"namespace": "dash",
|
||||
},
|
||||
{
|
||||
"relative_package_path": "dcc/{}.js.map".format(_this_module),
|
||||
"external_url": (
|
||||
"https://unpkg.com/dash-core-components@{}"
|
||||
"/dash_core_components/dash_core_components.js.map"
|
||||
).format(__version__),
|
||||
"namespace": "dash",
|
||||
"dynamic": True,
|
||||
},
|
||||
{
|
||||
"relative_package_path": "dcc/{}-shared.js".format(_this_module),
|
||||
"external_url": (
|
||||
"https://unpkg.com/dash-core-components@{}"
|
||||
"/dash_core_components/dash_core_components-shared.js"
|
||||
).format(__version__),
|
||||
"namespace": "dash",
|
||||
},
|
||||
{
|
||||
"relative_package_path": "dcc/{}-shared.js.map".format(_this_module),
|
||||
"external_url": (
|
||||
"https://unpkg.com/dash-core-components@{}"
|
||||
"/dash_core_components/dash_core_components-shared.js.map"
|
||||
).format(__version__),
|
||||
"namespace": "dash",
|
||||
"dynamic": True,
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
for _component in __all__:
|
||||
setattr(locals()[_component], "_js_dist", _js_dist)
|
||||
@@ -0,0 +1,53 @@
|
||||
from .Checklist import Checklist
|
||||
from .Clipboard import Clipboard
|
||||
from .ConfirmDialog import ConfirmDialog
|
||||
from .ConfirmDialogProvider import ConfirmDialogProvider
|
||||
from .DatePickerRange import DatePickerRange
|
||||
from .DatePickerSingle import DatePickerSingle
|
||||
from .Download import Download
|
||||
from .Dropdown import Dropdown
|
||||
from .Geolocation import Geolocation
|
||||
from .Graph import Graph
|
||||
from .Input import Input
|
||||
from .Interval import Interval
|
||||
from .Link import Link
|
||||
from .Loading import Loading
|
||||
from .Location import Location
|
||||
from .Markdown import Markdown
|
||||
from .RadioItems import RadioItems
|
||||
from .RangeSlider import RangeSlider
|
||||
from .Slider import Slider
|
||||
from .Store import Store
|
||||
from .Tab import Tab
|
||||
from .Tabs import Tabs
|
||||
from .Textarea import Textarea
|
||||
from .Tooltip import Tooltip
|
||||
from .Upload import Upload
|
||||
|
||||
__all__ = [
|
||||
"Checklist",
|
||||
"Clipboard",
|
||||
"ConfirmDialog",
|
||||
"ConfirmDialogProvider",
|
||||
"DatePickerRange",
|
||||
"DatePickerSingle",
|
||||
"Download",
|
||||
"Dropdown",
|
||||
"Geolocation",
|
||||
"Graph",
|
||||
"Input",
|
||||
"Interval",
|
||||
"Link",
|
||||
"Loading",
|
||||
"Location",
|
||||
"Markdown",
|
||||
"RadioItems",
|
||||
"RangeSlider",
|
||||
"Slider",
|
||||
"Store",
|
||||
"Tab",
|
||||
"Tabs",
|
||||
"Textarea",
|
||||
"Tooltip",
|
||||
"Upload",
|
||||
]
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
|
||||
/** @license React v16.13.1
|
||||
* react-is.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,5 @@
|
||||
/*!
|
||||
Copyright (c) 2018 Jed Watson.
|
||||
Licensed under the MIT License (MIT), see
|
||||
http://jedwatson.github.io/react-select
|
||||
*/
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,13 @@
|
||||
/*!
|
||||
* Determine if an object is a Buffer
|
||||
*
|
||||
* @author Feross Aboukhadijeh <https://feross.org>
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/*!
|
||||
* repeat-string <https://github.com/jonschlinkert/repeat-string>
|
||||
*
|
||||
* Copyright (c) 2014-2015, Jon Schlinkert.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,11 @@
|
||||
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-is.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,14 @@
|
||||
/*!
|
||||
Copyright (c) 2018 Jed Watson.
|
||||
Licensed under the MIT License (MIT), see
|
||||
http://jedwatson.github.io/classnames
|
||||
*/
|
||||
|
||||
/** @license React v16.13.1
|
||||
* react-is.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,25 @@
|
||||
/*!
|
||||
* Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
*/
|
||||
|
||||
/*!
|
||||
* The buffer module from node.js, for the browser.
|
||||
*
|
||||
* @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */
|
||||
|
||||
//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
|
||||
|
||||
//! license : MIT
|
||||
|
||||
//! moment.js
|
||||
|
||||
//! moment.js locale configuration
|
||||
|
||||
//! momentjs.com
|
||||
|
||||
//! version : 2.30.1
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,106 @@
|
||||
import io
|
||||
import ntpath
|
||||
import base64
|
||||
|
||||
# region Utils for Download component
|
||||
|
||||
|
||||
def send_file(path, filename=None, type=None):
|
||||
"""
|
||||
Convert a file into the format expected by the Download component.
|
||||
:param path: path to the file to be sent
|
||||
:param filename: name of the file, if not provided the original filename is used
|
||||
:param type: type of the file (optional, passed to Blob in the javascript layer)
|
||||
:return: dict of file content (base64 encoded) and meta data used by the Download component
|
||||
"""
|
||||
# If filename is not set, read it from the path.
|
||||
if filename is None:
|
||||
filename = ntpath.basename(path)
|
||||
# Read the file contents and send it.
|
||||
with open(path, "rb") as f:
|
||||
return send_bytes(f.read(), filename, type)
|
||||
|
||||
|
||||
def send_bytes(src, filename, type=None, **kwargs):
|
||||
"""
|
||||
Convert data written to BytesIO into the format expected by the Download component.
|
||||
:param src: array of bytes or a writer that can write to BytesIO
|
||||
:param filename: the name of the file
|
||||
:param type: type of the file (optional, passed to Blob in the javascript layer)
|
||||
:return: dict of data frame content (base64 encoded) and meta data used by the Download component
|
||||
"""
|
||||
content = src if isinstance(src, bytes) else _io_to_str(io.BytesIO(), src, **kwargs)
|
||||
return dict(
|
||||
content=base64.b64encode(content).decode(),
|
||||
filename=filename,
|
||||
type=type,
|
||||
base64=True,
|
||||
)
|
||||
|
||||
|
||||
def send_string(src, filename, type=None, **kwargs):
|
||||
"""
|
||||
Convert data written to StringIO into the format expected by the Download component.
|
||||
:param src: a string or a writer that can write to StringIO
|
||||
:param filename: the name of the file
|
||||
:param type: type of the file (optional, passed to Blob in the javascript layer)
|
||||
:return: dict of data frame content (NOT base64 encoded) and meta data used by the Download component
|
||||
"""
|
||||
content = src if isinstance(src, str) else _io_to_str(io.StringIO(), src, **kwargs)
|
||||
return dict(content=content, filename=filename, type=type, base64=False)
|
||||
|
||||
|
||||
def _io_to_str(data_io, writer, **kwargs):
|
||||
# Some pandas writers try to close the IO, we do not want that.
|
||||
data_io_close = data_io.close
|
||||
data_io.close = lambda: None
|
||||
# Write data content.
|
||||
writer(data_io, **kwargs)
|
||||
data_value = data_io.getvalue()
|
||||
data_io_close()
|
||||
return data_value
|
||||
|
||||
|
||||
def send_data_frame(writer, filename, type=None, **kwargs):
|
||||
"""
|
||||
Convert data frame into the format expected by the Download component.
|
||||
:param writer: a data frame writer
|
||||
:param filename: the name of the file
|
||||
:param type: type of the file (optional, passed to Blob in the javascript layer)
|
||||
:return: dict of data frame content (base64 encoded) and meta data used by the Download component
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
>>> df = pd.DataFrame({'a': [1, 2, 3, 4], 'b': [2, 1, 5, 6], 'c': ['x', 'x', 'y', 'y']})
|
||||
...
|
||||
>>> send_data_frame(df.to_csv, "mydf.csv") # download as csv
|
||||
>>> send_data_frame(df.to_json, "mydf.json") # download as json
|
||||
>>> send_data_frame(df.to_excel, "mydf.xls", index=False) # download as excel
|
||||
>>> send_data_frame(df.to_pickle, "mydf.pkl") # download as pickle
|
||||
|
||||
"""
|
||||
name = writer.__name__
|
||||
# Check if the provided writer is known.
|
||||
if name not in _data_frame_senders.keys():
|
||||
raise ValueError(
|
||||
"The provided writer ({}) is not supported, "
|
||||
"try calling send_string or send_bytes directly.".format(name)
|
||||
)
|
||||
# Send data frame using the appropriate send function.
|
||||
return _data_frame_senders[name](writer, filename, type, **kwargs)
|
||||
|
||||
|
||||
_data_frame_senders = {
|
||||
"to_csv": send_string,
|
||||
"to_json": send_string,
|
||||
"to_html": send_string,
|
||||
"to_excel": send_bytes,
|
||||
"to_feather": send_bytes,
|
||||
"to_parquet": send_bytes,
|
||||
"to_msgpack": send_bytes,
|
||||
"to_stata": send_bytes,
|
||||
"to_pickle": send_bytes,
|
||||
}
|
||||
|
||||
# endregion
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user