Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type annotations in dash (+ add mypy tests in CI) #1748

Closed
wants to merge 1 commit into from
Closed
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
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ include dash/html/*
include dash/dash_table/*
include dash/dash-renderer/build/*.js
include dash/dash-renderer/build/*.map
include dash/py.typed
34 changes: 18 additions & 16 deletions dash/_callback.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import collections
from functools import wraps
from typing import Any, List, Dict, Callable, Union

from .dependencies import (
ClientsideFunction,
handle_callback_args,
handle_grouped_callback_args,
Output,
Expand All @@ -27,9 +29,9 @@ class NoUpdate:
pass


GLOBAL_CALLBACK_LIST = []
GLOBAL_CALLBACK_MAP = {}
GLOBAL_INLINE_SCRIPTS = []
GLOBAL_CALLBACK_LIST: List[dict] = []
GLOBAL_CALLBACK_MAP: Dict[str, dict] = {}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If taken further, I'll try replace dict with Dict with more information about the underlying structure.

GLOBAL_INLINE_SCRIPTS: List[str] = []


def callback(*_args, **_kwargs):
Expand Down Expand Up @@ -106,7 +108,7 @@ def insert_callback(

def register_callback(
callback_list, callback_map, config_prevent_initial_callbacks, *_args, **_kwargs
):
) -> Callable:
(
output,
flat_inputs,
Expand Down Expand Up @@ -137,9 +139,9 @@ def register_callback(
)

# pylint: disable=too-many-locals
def wrap_func(func):
def wrap_func(func: Callable) -> Callable:
@wraps(func)
def add_context(*args, **kwargs):
def add_context(*args, **kwargs): # type: ignore[no-untyped-def]
output_spec = kwargs.pop("outputs_list")
_validate.validate_output_spec(insert_output, output_spec, Output)

Expand Down Expand Up @@ -209,14 +211,14 @@ def add_context(*args, **kwargs):


def register_clientside_callback(
callback_list,
callback_map,
config_prevent_initial_callbacks,
inline_scripts,
clientside_function,
*args,
**kwargs
):
callback_list: List[dict],
callback_map: Dict[str, dict],
config_prevent_initial_callbacks: bool,
inline_scripts: List[str],
clientside_function: Union[ClientsideFunction, str],
*args: Any,
**kwargs: Any,
) -> None:
output, inputs, state, prevent_initial_call = handle_callback_args(args, kwargs)
insert_callback(
callback_list,
Expand All @@ -238,8 +240,8 @@ def register_clientside_callback(
if isinstance(output, (list, tuple)):
out0 = output[0]

namespace = "_dashprivate_{}".format(out0.component_id)
function_name = "{}".format(out0.component_property)
namespace = f"_dashprivate_{out0.component_id}"
function_name = str(out0.component_property)

inline_scripts.append(
_inline_clientside_template.format(
Expand Down
22 changes: 11 additions & 11 deletions dash/_callback_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,17 @@ def __nonzero__(self):

# pylint: disable=no-init
class CallbackContext:
@property
@property # type: ignore[misc]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably due to python/mypy#1362.

@has_context
def inputs(self):
return getattr(flask.g, "input_values", {})

@property
@property # type: ignore[misc]
@has_context
def states(self):
return getattr(flask.g, "state_values", {})

@property
@property # type: ignore[misc]
@has_context
def triggered(self):
# For backward compatibility: previously `triggered` always had a
Expand All @@ -54,17 +54,17 @@ def triggered(self):
# look empty, but you can still do `triggered[0]["prop_id"].split(".")`
return getattr(flask.g, "triggered_inputs", []) or falsy_triggered

@property
@property # type: ignore[misc]
@has_context
def args_grouping(self):
return getattr(flask.g, "args_grouping", [])

@property
@property # type: ignore[misc]
@has_context
def outputs_grouping(self):
return getattr(flask.g, "outputs_grouping", [])

@property
@property # type: ignore[misc]
@has_context
def outputs_list(self):
if self.using_outputs_grouping:
Expand All @@ -75,7 +75,7 @@ def outputs_list(self):

return getattr(flask.g, "outputs_list", [])

@property
@property # type: ignore[misc]
@has_context
def inputs_list(self):
if self.using_args_grouping:
Expand All @@ -86,7 +86,7 @@ def inputs_list(self):

return getattr(flask.g, "inputs_list", [])

@property
@property # type: ignore[misc]
@has_context
def states_list(self):
if self.using_args_grouping:
Expand All @@ -96,7 +96,7 @@ def states_list(self):
)
return getattr(flask.g, "states_list", [])

@property
@property # type: ignore[misc]
@has_context
def response(self):
return getattr(flask.g, "dash_response")
Expand Down Expand Up @@ -125,7 +125,7 @@ def record_timing(name, duration=None, description=None):

setattr(flask.g, "timing_information", timing_information)

@property
@property # type: ignore[misc]
@has_context
def using_args_grouping(self):
"""
Expand All @@ -134,7 +134,7 @@ def using_args_grouping(self):
"""
return getattr(flask.g, "using_args_grouping", [])

@property
@property # type: ignore[misc]
@has_context
def using_outputs_grouping(self):
"""
Expand Down
13 changes: 9 additions & 4 deletions dash/_configs.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import os
from typing import Union, Optional, Tuple

# noinspection PyCompatibility
from . import exceptions
from ._utils import AttributeDict


def load_dash_env_vars():
def load_dash_env_vars() -> AttributeDict:
return AttributeDict(
{
var: os.getenv(var, os.getenv(var.lower()))
Expand Down Expand Up @@ -40,7 +41,9 @@ def load_dash_env_vars():
DASH_ENV_VARS = load_dash_env_vars() # used in tests


def get_combined_config(name, val, default=None):
def get_combined_config(
name: str, val: Optional[Union[bool, str]], default: Union[bool, str] = None
) -> Optional[Union[bool, str]]:
"""Consolidate the config with priority from high to low provided init
value > OS environ > default."""

Expand All @@ -55,8 +58,10 @@ def get_combined_config(name, val, default=None):


def pathname_configs(
url_base_pathname=None, routes_pathname_prefix=None, requests_pathname_prefix=None
):
url_base_pathname: str = None,
routes_pathname_prefix: str = None,
requests_pathname_prefix: str = None,
) -> Tuple[str, str, str]:
_pathname_config_error_message = """
{} This is ambiguous.
To fix this, set `routes_pathname_prefix` instead of `url_base_pathname`.
Expand Down
2 changes: 1 addition & 1 deletion dash/_grouping.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def flatten_grouping(grouping, schema=None):
return [grouping]


def grouping_len(grouping):
def grouping_len(grouping) -> int:
"""
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
Expand Down
12 changes: 10 additions & 2 deletions dash/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import io
import json
from functools import wraps
from typing import Dict

from . import exceptions

logger = logging.getLogger()
Expand All @@ -21,15 +23,21 @@ def to_json(value):
return to_json_plotly(value)


def interpolate_str(template, **data):
def interpolate_str(template, **data) -> str:
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):
def format_tag(
tag_name: str,
attributes: Dict[str, str],
inner: str = "",
closed: bool = False,
opened: bool = False,
) -> str:
tag = "<{tag} {attributes}"
if closed:
tag += "/>"
Expand Down
16 changes: 11 additions & 5 deletions dash/_watch.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,24 @@
import os
import re
import time
from typing import List, Callable, DefaultDict


def watch(folders, on_change, pattern=None, sleep_time=0.1):
pattern = re.compile(pattern) if pattern else None
watched = collections.defaultdict(lambda: -1)
def watch(
folders: List[str],
on_change: Callable,
pattern: str = None,
sleep_time: float = 0.1,
) -> None:
compiled_pattern = re.compile(pattern) if pattern else None
watched: DefaultDict[str, float] = collections.defaultdict(lambda: -1)

def walk():
def walk() -> None:
walked = []
for folder in folders:
for current, _, files in os.walk(folder):
for f in files:
if pattern and not pattern.search(f):
if compiled_pattern and not compiled_pattern.search(f):
continue
path = os.path.join(current, f)

Expand Down
Loading