Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,7 @@ jobs:

- name: Run typing tests
run: |
cd tests
pytest compliance/test_typing.py
pytest tests/compliance/test_typing.py

background-callbacks:
name: Run Background & Async Callback Tests (Python ${{ matrix.python-version }})
Expand Down
16 changes: 8 additions & 8 deletions dash/_callback.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ def _invoke_callback(func, *args, **kwargs): # used to mark the frame for the d
return func(*args, **kwargs) # %% callback invoked %%


GLOBAL_CALLBACK_LIST = []
GLOBAL_CALLBACK_MAP = {}
GLOBAL_INLINE_SCRIPTS = []
GLOBAL_API_PATHS = {}
GLOBAL_CALLBACK_LIST: List[Any] = []
GLOBAL_CALLBACK_MAP: dict[str, Any] = {}
GLOBAL_INLINE_SCRIPTS: List[Any] = []
GLOBAL_API_PATHS: dict[str, Any] = {}


# pylint: disable=too-many-locals,too-many-arguments
Expand Down Expand Up @@ -177,7 +177,7 @@ def callback(
callbacks in the Dash devtools.
"""

background_spec = None
background_spec: Any = None

config_prevent_initial_callbacks = _kwargs.pop(
"config_prevent_initial_callbacks", False
Expand All @@ -186,7 +186,7 @@ def callback(
callback_list = _kwargs.pop("callback_list", GLOBAL_CALLBACK_LIST)

if background:
background_spec: Any = {
background_spec = {
"interval": interval,
}

Expand Down Expand Up @@ -687,7 +687,7 @@ def add_context(*args, **kwargs):
args, kwargs, inputs_state_indices, has_output, insert_output
)

response: dict = {"multi": True}
response: dict = {"multi": True} # type: ignore

jsonResponse = None
try:
Expand Down Expand Up @@ -759,7 +759,7 @@ async def async_add_context(*args, **kwargs):
args, kwargs, inputs_state_indices, has_output, insert_output
)

response: dict = {"multi": True}
response = {"multi": True}

try:
if background is not None:
Expand Down
4 changes: 3 additions & 1 deletion dash/_callback_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
from ._utils import AttributeDict, stringify_id


context_value = contextvars.ContextVar("callback_context")
context_value: contextvars.ContextVar[
typing.Dict[str, typing.Any]
] = contextvars.ContextVar("callback_context")
context_value.set({})


Expand Down
3 changes: 2 additions & 1 deletion dash/_dash_renderer.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import os
from typing import Any, List, Dict

__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
_js_dist_dependencies: List[Dict[str, Any]] = [] # to be set by _set_react_version


def _set_react_version(v_react, v_reactdom=None):
Expand Down
5 changes: 3 additions & 2 deletions dash/_get_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

from contextvars import ContextVar, copy_context
from textwrap import dedent
from typing import Any, Optional

APP = None
APP: Optional[Any] = None

app_context = ContextVar("dash_app_context")
app_context: ContextVar[Any] = ContextVar("dash_app_context")


def with_app_context(func):
Expand Down
12 changes: 7 additions & 5 deletions dash/_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@

# pylint: disable=too-few-public-methods
class _Hook(_tx.Generic[HookDataType]):
def __init__(self, func, priority=0, final=False, data: HookDataType = None):
def __init__(
self, func, priority=0, final=False, data: _t.Optional[HookDataType] = None
):
self.func = func
self.final = final
self.data = data
Expand All @@ -39,7 +41,7 @@ def __call__(self, *args, **kwargs):

class _Hooks:
def __init__(self) -> None:
self._ns = {
self._ns: _t.Dict[str, _t.List[_t.Any]] = {
"setup": [],
"layout": [],
"routes": [],
Expand All @@ -49,14 +51,14 @@ def __init__(self) -> None:
"custom_data": [],
"dev_tools": [],
}
self._js_dist = []
self._css_dist = []
self._js_dist: _t.List[_t.Any] = []
self._css_dist: _t.List[_t.Any] = []
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 = {}
self._finals: _t.Dict[str, _t.Any] = {}

def add_hook(
self,
Expand Down
119 changes: 87 additions & 32 deletions dash/_jupyter.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# type: ignore
import asyncio
import io
import inspect
Expand All @@ -10,29 +9,83 @@
import threading
import time

from typing import Optional
from typing import Optional, Any
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 comm import create_comm
import nest_asyncio
from IPython import get_ipython # type: ignore[attr-defined]
from IPython.display import IFrame, display, Javascript # type: ignore[import-not-found]
from IPython.core.display import HTML # type: ignore[import-not-found]
from IPython.core.ultratb import FormattedTB # type: ignore[import-not-found]
from retrying import retry # type: ignore[import-untyped]
from comm import create_comm # type: ignore[import-not-found]
import nest_asyncio # type: ignore[import-untyped]

import requests
import requests # type: ignore[import-untyped]

_dash_comm = create_comm(target_name="dash")
_dash_comm = create_comm(target_name="dash") # type: ignore[misc]
_dep_installed = True
except ImportError:
_dep_installed = False
_dash_comm = None
get_ipython = lambda: None
_dash_comm = None # type: ignore[assignment]

# Stub implementations for when dependencies are not installed
def get_ipython(): # type: ignore[misc]
return None

# pylint: disable=unused-argument
def retry(*args: Any, **kwargs: Any): # type: ignore[misc]
def decorator(func: Any) -> Any:
return func

return decorator

# pylint: disable=unused-argument,too-few-public-methods
class IFrame: # type: ignore[no-redef]
def __init__(self, *args: Any, **kwargs: Any) -> None:
pass

# pylint: disable=unused-argument,too-few-public-methods
def display(*args: Any, **kwargs: Any) -> None: # type: ignore[misc]
pass

# pylint: disable=unused-argument,too-few-public-methods
class Javascript: # type: ignore[no-redef]
def __init__(self, *args: Any, **kwargs: Any) -> None:
pass

# pylint: disable=unused-argument,too-few-public-methods
class HTML: # type: ignore[no-redef]
def __init__(self, *args: Any, **kwargs: Any) -> None:
pass

# pylint: disable=unused-argument,too-few-public-methods
class FormattedTB: # type: ignore[no-redef]
def __init__(self, *args: Any, **kwargs: Any) -> None:
pass

def __call__(self, *args: Any, **kwargs: Any) -> None:
pass

# pylint: disable=unused-argument,too-few-public-methods
class _RequestsModule: # type: ignore[misc]
class ConnectionError(Exception):
pass

def get(self, *args: Any, **kwargs: Any) -> Any:
return None

requests = _RequestsModule() # type: ignore[assignment]

# pylint: disable=unused-argument,too-few-public-methods
class _NestAsyncioModule: # type: ignore[misc]
@staticmethod
def apply(*args: Any, **kwargs: Any) -> None:
pass

nest_asyncio = _NestAsyncioModule() # type: ignore[assignment]

JupyterDisplayMode = Literal["inline", "external", "jupyterlab", "tab", "_none"]

Expand All @@ -44,7 +97,7 @@ def _get_skip(error: Exception):

tb = error.__traceback__
skip = 1
while tb.tb_next is not None:
while tb is not None and tb.tb_next is not None:
skip += 1
tb = tb.tb_next
if tb.tb_frame.f_code is _invoke_callback.__code__:
Expand Down Expand Up @@ -89,9 +142,9 @@ def convert(name, locals=locals, formatarg=formatarg, formatvalue=formatvalue):
return "(\n " + ",\n ".join(specs) + "\n)"


_jupyter_config = {}
_jupyter_config: Any = {}

_caller = {}
_caller: Any = {}


def _send_jupyter_config_comm_request():
Expand All @@ -102,9 +155,10 @@ def _send_jupyter_config_comm_request():
ipython is not None
and hasattr(ipython, "kernel")
and ipython.kernel is not None
and _dash_comm is not None
):
_caller["parent"] = ipython.kernel.get_parent()
_dash_comm.send({"type": "base_url_request"})
_dash_comm.send({"type": "base_url_request"}) # type: ignore[attr-defined]


def _jupyter_comm_response_received():
Expand All @@ -121,19 +175,19 @@ def _request_jupyter_config(timeout=2):
_send_jupyter_config_comm_request()

# Get shell and kernel
shell = get_ipython()
kernel = shell.kernel
shell = ipython
kernel = shell.kernel # type: ignore[attr-defined]

# 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
kernel.shell_handlers["execute_request"] = capture_event # type: ignore[attr-defined]

# increment execution count to avoid collision error
shell.execution_count += 1
shell.execution_count += 1 # type: ignore[attr-defined]

# Allow kernel to execute comms until we receive the jupyter configuration comm
# response
Expand Down Expand Up @@ -181,7 +235,7 @@ class JupyterDash:
alive_token = str(uuid.uuid4())
inline_exceptions: bool = True

_servers = {}
_servers: Any = {}

def infer_jupyter_proxy_config(self):
"""
Expand Down Expand Up @@ -343,7 +397,7 @@ def run_app(
except ImportError:
pass

err_q = queue.Queue()
err_q: Any = queue.Queue()

server = make_server(host, port, app.server, threaded=True, processes=0)
logging.getLogger("werkzeug").setLevel(logging.ERROR)
Expand Down Expand Up @@ -422,7 +476,7 @@ def wait_for_app():
@staticmethod
def _display_in_colab(dashboard_url, port, mode, width, height):
# noinspection PyUnresolvedReferences
from google.colab import output # pylint: disable=E0401,E0611,C0415
from google.colab import output # type: ignore[import-not-found] # pylint: disable=E0401,E0611,C0415

if mode == "inline":
output.serve_kernel_port_as_iframe(port, width=width, height=height)
Expand All @@ -444,13 +498,14 @@ def _display_in_jupyter(dashboard_url, port, mode, width, height):
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,
}
)
if _dash_comm is not None:
_dash_comm.send( # type: ignore[attr-defined]
{
"type": "show",
"port": port,
"url": dashboard_url,
}
)

@staticmethod
def serve_alive():
Expand Down
12 changes: 6 additions & 6 deletions dash/_patch.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from typing import List, Union, Optional, Any
from typing import List, Union, Optional, Any, Dict


def _operation(name, location, **kwargs):
def _operation(name: str, location: List["_KeyType"], **kwargs: Any) -> Dict[str, Any]:
return {"operation": name, "location": location, "params": kwargs}


_noop = object()
_noop: Any = object()

_KeyType = Union[str, int]

Expand All @@ -30,12 +30,12 @@ def __init__(
parent: Optional["Patch"] = None,
):
if location is not None:
self._location = location
self._location: List[_KeyType] = location
else:
# pylint: disable=consider-using-ternary
self._location = (parent and parent._location) or []
if parent is not None:
self._operations = parent._operations
self._operations: List[Dict[str, Any]] = parent._operations
else:
self._operations = []

Expand All @@ -61,7 +61,7 @@ def __getattr__(self, item: _KeyType) -> "Patch":

def __setattr__(self, key: _KeyType, value: Any):
if key in ("_location", "_operations"):
self.__dict__[key] = value
self.__dict__[str(key)] = value
else:
self.__setitem__(key, value)

Expand Down
2 changes: 1 addition & 1 deletion dash/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

def to_json(value):
# pylint: disable=import-outside-toplevel
from plotly.io.json import to_json_plotly
from plotly.io.json import to_json_plotly # type: ignore[import-untyped]

return to_json_plotly(value)

Expand Down
2 changes: 1 addition & 1 deletion dash/_validate.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import sys
from collections.abc import MutableSequence
from collections.abc import MutableSequence # pylint: disable=import-error
import re
from textwrap import dedent
from keyword import iskeyword
Expand Down
Loading
Loading