Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-dash for openSUSE:Factory checked in at 2021-04-12 17:10:39 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-dash (Old) and /work/SRC/openSUSE:Factory/.python-dash.new.2401 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-dash" Mon Apr 12 17:10:39 2021 rev:11 rq:884578 version:1.20.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-dash/python-dash.changes 2021-01-05 18:12:57.251305577 +0100 +++ /work/SRC/openSUSE:Factory/.python-dash.new.2401/python-dash.changes 2021-04-12 17:10:48.158594073 +0200 @@ -1,0 +2,41 @@ +Fri Apr 9 21:34:20 UTC 2021 - Ben Greiner <c...@bnavigator.de> + +- Update to 1.20.0 + Changed + * #1531 Update the format of the docstrings to make them easier + to read in the reference pages of Dash Docs and in the + console. This also addresses #1205 + * #1553 Increase the z-index of the Dash error menu from 1001 to + 1100 in order to make sure it appears above Bootstrap + components. + Fixed + * #1546 Validate callback request outputs vs output to avoid a + perceived security issue. +- Release 1.19.0 + Added + * #1508 Fix #1403: Adds an x button to close the error messages + box. + * #1525 Adds support for callbacks which have overlapping inputs + and outputs. Combined with dash.callback_context this + addresses many use cases which require circular callbacks. + Changed + * #1503 Fix #1466: loosen dash[testing] requirements for easier + integration in external projects. This PR also bumps many dash + [dev] requirements. + Fixed + * #1530 Dedent error messages more carefully. + * #1527??? get_asset_url now pulls from an external source if + assets_external_path is set. + - updated _add_assets_resource to build asset urls the same + way as get_asset_url. + - updated doc string for assets_external_path Dash argument to + be more clear that it will allways be joined with the + assets_url_path argument when determining the url to an + external asset. + * #1493 Fix #1143, a bug where having a file with one of several + common names (test.py, code.py, org.py, etc) that imports a + dash component package would make import dash fail with a + cryptic error message asking whether you have a file named + "dash.py" + +------------------------------------------------------------------- Old: ---- dash-1.18.1.tar.gz New: ---- dash-1.20.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-dash.spec ++++++ --- /var/tmp/diff_new_pack.pf887R/_old 2021-04-12 17:10:49.050595061 +0200 +++ /var/tmp/diff_new_pack.pf887R/_new 2021-04-12 17:10:49.050595061 +0200 @@ -22,7 +22,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-dash -Version: 1.18.1 +Version: 1.20.0 Release: 0 Summary: Python framework for building reactive web-apps License: MIT @@ -32,10 +32,10 @@ BuildRequires: %{python_module Flask >= 1.0.4} BuildRequires: %{python_module Flask-Compress} BuildRequires: %{python_module beautifulsoup4} -BuildRequires: %{python_module dash-core-components >= 1.14.1} -BuildRequires: %{python_module dash-html-components >= 1.1.1} -BuildRequires: %{python_module dash-renderer >= 1.8.3} -BuildRequires: %{python_module dash-table >= 4.11.1} +BuildRequires: %{python_module dash-core-components >= 1.16.0} +BuildRequires: %{python_module dash-html-components >= 1.1.3} +BuildRequires: %{python_module dash-renderer >= 1.9.1} +BuildRequires: %{python_module dash-table >= 4.11.3} BuildRequires: %{python_module future} BuildRequires: %{python_module percy} BuildRequires: %{python_module plotly} @@ -46,16 +46,16 @@ Requires: python-Flask-Compress # dash/testing/dash_page.py Requires: python-beautifulsoup4 -Requires: python-dash-core-components >= 1.14.1 -Requires: python-dash-html-components >= 1.1.1 -Requires: python-dash-renderer >= 1.8.3 -Requires: python-dash-table >= 4.11.1 +Requires: python-dash-core-components >= 1.16.0 +Requires: python-dash-html-components >= 1.1.3 +Requires: python-dash-renderer >= 1.9.1 +Requires: python-dash-table >= 4.11.3 Requires: python-future # needed for dash/testing/browser.py Requires: python-percy Requires: python-plotly Requires(post): update-alternatives -Requires(postun): update-alternatives +Requires(postun):update-alternatives BuildArch: noarch %if %{with test} BuildRequires: %{python_module lxml} ++++++ dash-1.18.1.tar.gz -> dash-1.20.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dash-1.18.1/LICENSE new/dash-1.20.0/LICENSE --- old/dash-1.18.1/LICENSE 2020-12-08 14:42:55.000000000 +0100 +++ new/dash-1.20.0/LICENSE 2021-04-09 00:03:55.000000000 +0200 @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2020 Plotly, Inc +Copyright (c) 2021 Plotly, Inc Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dash-1.18.1/PKG-INFO new/dash-1.20.0/PKG-INFO --- old/dash-1.18.1/PKG-INFO 2020-12-09 22:33:39.000000000 +0100 +++ new/dash-1.20.0/PKG-INFO 2021-04-09 00:19:30.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: dash -Version: 1.18.1 +Version: 1.20.0 Summary: A Python framework for building reactive web-apps. Developed by Plotly. Home-page: https://plotly.com/dash Author: Chris Parmer @@ -43,9 +43,9 @@ Scale up with Dash Enterprise when your Dash app is ready for department or company-wide consumption. Or, launch your initiative with Dash Enterprise from the start to unlock developer productivity gains and hands-on acceleration from Plotly's team. - ML Ops Features: A one-stop shop for ML Ops: Horizontally scalable hosting, deployment, and authentication for your Dash apps. No IT or DevOps required. + ML Ops Features: A one-stop shop for ML Ops: Horizontally scalable hosting, deployment, and authentication for your Dash apps. No IT or DevOps required. - [**App manager**](https://plotly.com/dash/app-manager/) Deploy & manage Dash apps without needing IT or a DevOps team. App Manager gives you point & click control over all aspects of your Dash deployments. - - [**Kubernetes scaling**](https://plotly.com/dash/kubernetes/) Ensure high availability of Dash apps and scale horizontally with Dash Enterprise???s Kubernetes architecture. No IT or Helm required. + - [**Kubernetes scaling**](https://plotly.com/dash/kubernetes/) Ensure high availability of Dash apps and scale horizontally with Dash Enterprise???s Kubernetes architecture. No IT or Helm required. - [**No code auth**](https://plotly.com/dash/authentication/) Control Dash app access in a few clicks. Dash Enterprise supports LDAP, AD, PKI, Okta, SAML, OpenID Connect, OAuth, SSO, and simple email authentication. - [**Job Queue**](https://plotly.com/dash/job-queue/) The Job Queue is the key to building scalable Dash apps. Move heavy computation from synchronous Dash callbacks to the Job Queue for asynchronous background processing. @@ -61,7 +61,6 @@ - [**GPU & Dask Acceleration**](https://plotly.com/dash/gpu-dask-acceleration/) Dash Enterprise puts Python???s most popular HPC stack for GPU and parallel CPU computing in the hands of business users. - [**Data Science Workspaces**](https://plotly.com/dash/workspaces/) Be productive from Day 1. Write and execute Python, R, & Julia code from Dash Enterprise's onboard code editor. - See [https://plotly.com/contact-us/](https://plotly.com/contact-us/) to get in touch.  diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dash-1.18.1/README.md new/dash-1.20.0/README.md --- old/dash-1.18.1/README.md 2020-12-08 14:42:55.000000000 +0100 +++ new/dash-1.20.0/README.md 2021-04-09 00:03:55.000000000 +0200 @@ -35,9 +35,9 @@ Scale up with Dash Enterprise when your Dash app is ready for department or company-wide consumption. Or, launch your initiative with Dash Enterprise from the start to unlock developer productivity gains and hands-on acceleration from Plotly's team. -ML Ops Features: A one-stop shop for ML Ops: Horizontally scalable hosting, deployment, and authentication for your Dash apps. No IT or DevOps required. +ML Ops Features: A one-stop shop for ML Ops: Horizontally scalable hosting, deployment, and authentication for your Dash apps. No IT or DevOps required. - [**App manager**](https://plotly.com/dash/app-manager/) Deploy & manage Dash apps without needing IT or a DevOps team. App Manager gives you point & click control over all aspects of your Dash deployments. -- [**Kubernetes scaling**](https://plotly.com/dash/kubernetes/) Ensure high availability of Dash apps and scale horizontally with Dash Enterprise???s Kubernetes architecture. No IT or Helm required. +- [**Kubernetes scaling**](https://plotly.com/dash/kubernetes/) Ensure high availability of Dash apps and scale horizontally with Dash Enterprise???s Kubernetes architecture. No IT or Helm required. - [**No code auth**](https://plotly.com/dash/authentication/) Control Dash app access in a few clicks. Dash Enterprise supports LDAP, AD, PKI, Okta, SAML, OpenID Connect, OAuth, SSO, and simple email authentication. - [**Job Queue**](https://plotly.com/dash/job-queue/) The Job Queue is the key to building scalable Dash apps. Move heavy computation from synchronous Dash callbacks to the Job Queue for asynchronous background processing. @@ -53,7 +53,6 @@ - [**GPU & Dask Acceleration**](https://plotly.com/dash/gpu-dask-acceleration/) Dash Enterprise puts Python???s most popular HPC stack for GPU and parallel CPU computing in the hands of business users. - [**Data Science Workspaces**](https://plotly.com/dash/workspaces/) Be productive from Day 1. Write and execute Python, R, & Julia code from Dash Enterprise's onboard code editor. - See [https://plotly.com/contact-us/](https://plotly.com/contact-us/) to get in touch.  diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dash-1.18.1/dash/__init__.py new/dash-1.20.0/dash/__init__.py --- old/dash-1.18.1/dash/__init__.py 2020-12-08 14:42:55.000000000 +0100 +++ new/dash-1.20.0/dash/__init__.py 2021-01-20 00:35:20.000000000 +0100 @@ -1,7 +1,11 @@ -from .dash import Dash, no_update # noqa: F401 -from . import dependencies # noqa: F401 -from . import development # noqa: F401 -from . import exceptions # noqa: F401 -from . import resources # noqa: F401 -from .version import __version__ # noqa: F401 -from ._callback_context import callback_context # noqa: F401 +# 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 .dash import Dash, no_update # noqa: F401,E402 +from . import dependencies # noqa: F401,E402 +from . import development # noqa: F401,E402 +from . import exceptions # noqa: F401,E402 +from . import resources # noqa: F401,E402 +from .version import __version__ # noqa: F401,E402 +from ._callback_context import callback_context # noqa: F401,E402 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dash-1.18.1/dash/_utils.py new/dash-1.20.0/dash/_utils.py --- old/dash-1.18.1/dash/_utils.py 2020-12-08 14:42:55.000000000 +0100 +++ new/dash-1.20.0/dash/_utils.py 2021-01-20 00:35:20.000000000 +0100 @@ -65,8 +65,12 @@ return requests_pathname elif not path.startswith("/"): raise exceptions.UnsupportedRelativePath( - "Paths that aren't prefixed with a leading / are not supported.\n" - + "You supplied: {}".format(path) + """ + Paths that aren't prefixed with a leading / are not supported. + You supplied: {} + """.format( + path + ) ) return "/".join([requests_pathname.rstrip("/"), path.lstrip("/")]) @@ -78,9 +82,10 @@ requests_pathname != "/" and not path.startswith(requests_pathname.rstrip("/")) ) or (requests_pathname == "/" and not path.startswith("/")): raise exceptions.UnsupportedRelativePath( - "Paths that aren't prefixed with a leading " - + "requests_pathname_prefix are not supported.\n" - + "You supplied: {} and requests_pathname_prefix was {}".format( + """ + Paths that aren't prefixed with requests_pathname_prefix are not supported. + You supplied: {} and requests_pathname_prefix was {} + """.format( path, requests_pathname ) ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dash-1.18.1/dash/_validate.py new/dash-1.20.0/dash/_validate.py --- old/dash-1.18.1/dash/_validate.py 2020-12-08 14:42:55.000000000 +0100 +++ new/dash-1.20.0/dash/_validate.py 2021-04-09 00:03:55.000000000 +0200 @@ -1,5 +1,6 @@ import collections import re +from textwrap import dedent from .development.base_component import Component from . import exceptions @@ -15,26 +16,26 @@ if extra_args: if not isinstance(extra_args[0], (Output, Input, State)): raise exceptions.IncorrectTypeException( - """ - 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): - {} - """.format( - repr(extra_args[0]) - ) + dedent( + """ + 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): + {} + """ + ).format(repr(extra_args[0])) ) raise exceptions.IncorrectTypeException( - """ - In a callback definition, you must provide all Outputs first, - then all Inputs, then all States. After this item: - {} - we found this item next: - {} - """.format( - repr((outputs + inputs + state)[-1]), repr(extra_args[0]) - ) + dedent( + """ + In a callback definition, you must provide all Outputs first, + then all Inputs, then all States. After this item: + {} + we found this item next: + {} + """ + ).format(repr((outputs + inputs + state)[-1]), repr(extra_args[0])) ) for args in [outputs, inputs, state]: @@ -45,11 +46,11 @@ def validate_callback_arg(arg): if not isinstance(getattr(arg, "component_property", None), _strings): raise exceptions.IncorrectTypeException( - """ - component_property must be a string, found {!r} - """.format( - arg.component_property - ) + dedent( + """ + component_property must be a string, found {!r} + """ + ).format(arg.component_property) ) if hasattr(arg, "component_event"): @@ -68,11 +69,11 @@ else: raise exceptions.IncorrectTypeException( - """ - component_id must be a string or dict, found {!r} - """.format( - arg.component_id - ) + dedent( + """ + component_id must be a string or dict, found {!r} + """ + ).format(arg.component_id) ) @@ -85,12 +86,12 @@ # cause unwanted collisions if not isinstance(k, _strings): raise exceptions.IncorrectTypeException( - """ - Wildcard ID keys must be non-empty strings, - found {!r} in id {!r} - """.format( - k, arg_id - ) + dedent( + """ + Wildcard ID keys must be non-empty strings, + found {!r} in id {!r} + """ + ).format(k, arg_id) ) @@ -110,16 +111,36 @@ ) +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"], specij["property"]) == outi: + raise exceptions.CallbackException( + "Output does not match callback definition" + ) + + def validate_multi_return(outputs_list, output_value, callback_id): if not isinstance(output_value, (list, tuple)): raise exceptions.InvalidCallbackReturnValue( - """ - The callback {} is a multi-output. - Expected the output type to be a list or tuple but got: - {}. - """.format( - callback_id, repr(output_value) - ) + dedent( + """ + The callback {} is a multi-output. + Expected the output type to be a list or tuple but got: + {}. + """ + ).format(callback_id, repr(output_value)) ) if len(output_value) != len(outputs_list): @@ -137,26 +158,26 @@ vi = output_value[i] if not isinstance(vi, (list, tuple)): raise exceptions.InvalidCallbackReturnValue( - """ - The callback {} output {} is a wildcard multi-output. - Expected the output type to be a list or tuple but got: - {}. - output spec: {} - """.format( - callback_id, i, repr(vi), repr(outi) - ) + dedent( + """ + The callback {} output {} is a wildcard multi-output. + Expected the output type to be a list or tuple but got: + {}. + output spec: {} + """ + ).format(callback_id, i, repr(vi), repr(outi)) ) if len(vi) != len(outi): raise exceptions.InvalidCallbackReturnValue( - """ - Invalid number of output values for {} item {}. - Expected {}, got {} - output spec: {} - output value: {} - """.format( - callback_id, i, len(vi), len(outi), repr(outi), repr(vi) - ) + dedent( + """ + Invalid number of output values for {} item {}. + Expected {}, got {} + output spec: {} + output value: {} + """ + ).format(callback_id, i, len(vi), len(outi), repr(outi), repr(vi)) ) @@ -170,34 +191,38 @@ ) outer_type = type(outer_val).__name__ if toplevel: - location = """ - The value in question is either the only value returned, - or is in the top level of the returned list, - """ + 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 "[{:d}]".format(index) - location = """ - The value in question is located at - {} {} {} - {}, - """.format( - index_string, outer_type, outer_id, path - ) + location = dedent( + """ + The value in question is located at + {} {} {} + {}, + """ + ).format(index_string, outer_type, outer_id, path) raise exceptions.InvalidCallbackReturnValue( - """ - The callback for `{output}` - returned a {object:s} having type `{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. - """.format( + dedent( + """ + The callback for `{output}` + returned a {object:s} having type `{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. + """ + ).format( output=repr(output), object="tree with one value" if not toplevel else "value", type=bad_type, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dash-1.18.1/dash/dash.py new/dash-1.20.0/dash/dash.py --- old/dash-1.18.1/dash/dash.py 2020-12-08 14:42:55.000000000 +0100 +++ new/dash-1.20.0/dash/dash.py 2021-04-09 00:03:55.000000000 +0200 @@ -27,7 +27,7 @@ from .fingerprint import build_fingerprint, check_fingerprint from .resources import Scripts, Css -from .dependencies import handle_callback_args +from .dependencies import handle_callback_args, Output from .development.base_component import ComponentRegistry from .exceptions import PreventUpdate, InvalidResourceError, ProxyError from .version import __version__ @@ -150,10 +150,12 @@ :type assets_ignore: string :param assets_external_path: an absolute URL from which to load assets. - Use with ``serve_locally=False``. Dash can still find js and css to - automatically load if you also keep local copies in your assets - folder that Dash can index, but external serving can improve - performance and reduce load on the Dash server. + Use with ``serve_locally=False``. assets_external_path is joined + with assets_url_path to determine the absolute url to the + asset folder. Dash can still find js and css to automatically load + if you also keep local copies in your assets folder that Dash can index, + but external serving can improve performance and reduce load on + the Dash server. env: ``DASH_ASSETS_EXTERNAL_PATH`` :type assets_external_path: string @@ -1002,6 +1004,7 @@ @wraps(func) def add_context(*args, **kwargs): output_spec = kwargs.pop("outputs_list") + _validate.validate_output_spec(output, output_spec, Output) # don't touch the comment on the next line - used by debugger output_value = func(*args, **kwargs) # %% callback invoked %% @@ -1098,9 +1101,7 @@ def _add_assets_resource(self, url_path, file_path): res = {"asset_path": url_path, "filepath": file_path} if self.config.assets_external_path: - res["external_url"] = "{}{}".format( - self.config.assets_external_path, url_path - ) + res["external_url"] = self.get_asset_url(url_path.lstrip("/")) self._assets_files.append(file_path) return res @@ -1185,11 +1186,12 @@ ] def get_asset_url(self, path): - asset = get_asset_path( - self.config.requests_pathname_prefix, - path, - self.config.assets_url_path.lstrip("/"), - ) + if self.config.assets_external_path: + prefix = self.config.assets_external_path + else: + prefix = self.config.requests_pathname_prefix + + asset = get_asset_path(prefix, path, self.config.assets_url_path.lstrip("/")) return asset diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dash-1.18.1/dash/development/_jl_components_generation.py new/dash-1.20.0/dash/development/_jl_components_generation.py --- old/dash-1.18.1/dash/development/_jl_components_generation.py 2020-12-08 14:42:55.000000000 +0100 +++ new/dash-1.20.0/dash/development/_jl_components_generation.py 2021-04-09 00:03:55.000000000 +0200 @@ -4,7 +4,6 @@ import copy import os import shutil -import glob import warnings import sys import importlib @@ -173,7 +172,7 @@ ) ), # React's PropTypes.objectOf - objectOf=lambda: ("Dict with Strings as keys and values of type {}").format( + objectOf=lambda: "Dict with Strings as keys and values of type {}".format( get_jl_type(type_object["value"]) ), # React's PropTypes.shape @@ -266,7 +265,7 @@ # Ensure props are ordered with children first props = reorder_props(props=props) - return ("A{n} {name} component.\n{description}\nKeyword arguments:\n{args}").format( + return "A{n} {name} component.\n{description}\nKeyword arguments:\n{args}".format( n="n" if component_name[0].lower() in "aeiou" else "", name=component_name, description=description, @@ -526,14 +525,21 @@ os.makedirs("deps") - for javascript in glob.glob("{}/*.js".format(project_shortname)): - shutil.copy(javascript, "deps/") + for rel_dirname, _, filenames in os.walk(project_shortname): + for filename in filenames: + extension = os.path.splitext(filename)[1] - for css in glob.glob("{}/*.css".format(project_shortname)): - shutil.copy(css, "deps/") + if extension in [".py", ".pyc", ".json"]: + continue - for sourcemap in glob.glob("{}/*.map".format(project_shortname)): - shutil.copy(sourcemap, "deps/") + target_dirname = os.path.join( + "deps/", os.path.relpath(rel_dirname, project_shortname) + ) + + if not os.path.exists(target_dirname): + os.makedirs(target_dirname) + + shutil.copy(os.path.join(rel_dirname, filename), target_dirname) generate_package_file(project_shortname, components, pkg_data, prefix) generate_toml_file(project_shortname, pkg_data) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dash-1.18.1/dash/development/_py_components_generation.py new/dash-1.20.0/dash/development/_py_components_generation.py --- old/dash-1.18.1/dash/development/_py_components_generation.py 2020-12-08 14:42:55.000000000 +0100 +++ new/dash-1.20.0/dash/development/_py_components_generation.py 2021-04-09 00:03:55.000000000 +0200 @@ -1,6 +1,7 @@ from collections import OrderedDict import copy import os +from textwrap import fill from dash.development.base_component import _explicitize_args from dash.exceptions import NonExistentEventException @@ -12,16 +13,13 @@ def generate_class_string(typename, props, description, namespace): """Dynamically generate class strings to have nicely formatted docstrings, keyword arguments, and repr. - Inspired by http://jameso.be/2013/08/06/namedtuple.html - Parameters ---------- typename props description namespace - Returns ------- string @@ -53,12 +51,10 @@ self.available_properties = {list_of_valid_keys} self.available_wildcard_properties =\ {list_of_valid_wildcard_attr_prefixes} - _explicit_args = kwargs.pop('_explicit_args') _locals = locals() _locals.update(kwargs) # For wildcard attrs args = {{k: _locals[k] for k in _explicit_args if k != 'children'}} - for k in {required_props}: if k not in args: raise TypeError( @@ -112,14 +108,12 @@ def generate_class_file(typename, props, description, namespace): """Generate a Python class file (.py) given a class string. - Parameters ---------- typename props description namespace - Returns ------- """ @@ -170,14 +164,12 @@ def generate_class(typename, props, description, namespace): """Generate a Python class object given a class string. - Parameters ---------- typename props description namespace - Returns ------- """ @@ -191,11 +183,9 @@ def required_props(props): """Pull names of required props from the props object. - Parameters ---------- props: dict - Returns ------- list @@ -206,7 +196,6 @@ def create_docstring(component_name, props, description): """Create the Dash component docstring. - Parameters ---------- component_name: str @@ -215,7 +204,6 @@ Dictionary with {propName: propMetadata} structure description: str Component description - Returns ------- str @@ -248,12 +236,10 @@ def prohibit_events(props): """Events have been removed. Raise an error if we see dashEvents or fireEvents. - Parameters ---------- props: dict Dictionary with {propName: propMetadata} structure - Raises ------- ? @@ -267,12 +253,10 @@ def parse_wildcards(props): """Pull out the wildcard attributes from the Component props. - Parameters ---------- props: dict Dictionary with {propName: propMetadata} structure - Returns ------- list @@ -287,43 +271,37 @@ def reorder_props(props): """If "children" is in props, then move it to the front to respect dash - convention. - + convention, then 'id', then the remaining props sorted by prop name Parameters ---------- props: dict Dictionary with {propName: propMetadata} structure - Returns ------- dict Dictionary with {propName: propMetadata} structure """ - if "children" in props: - # Constructing an OrderedDict with duplicate keys, you get the order - # from the first one but the value from the last. - # Doing this to avoid mutating props, which can cause confusion. - props = OrderedDict([("children", "")] + list(props.items())) - return props + # Constructing an OrderedDict with duplicate keys, you get the order + # from the first one but the value from the last. + # Doing this to avoid mutating props, which can cause confusion. + props1 = [("children", "")] if "children" in props else [] + props2 = [("id", "")] if "id" in props else [] + return OrderedDict(props1 + props2 + sorted(list(props.items()))) def filter_props(props): """Filter props from the Component arguments to exclude: - - Those without a "type" or a "flowType" field - Those with arg.type.name in {'func', 'symbol', 'instanceOf'} - Parameters ---------- props: dict Dictionary with {propName: propMetadata} structure - Returns ------- dict Filtered dictionary with {propName: propMetadata} structure - Examples -------- ```python @@ -380,7 +358,18 @@ return filtered_props +def fix_keywords(txt): + """ + replaces javascript keywords true, false, null with Python keywords + """ + fix_word = {"true": "True", "false": "False", "null": "None"} + for js_keyword, python_keyword in fix_word.items(): + txt = txt.replace(js_keyword, python_keyword) + return txt + + # pylint: disable=too-many-arguments +# pylint: disable=too-many-locals def create_prop_docstring( prop_name, type_object, @@ -391,7 +380,6 @@ is_flow_type=False, ): """Create the Dash component prop docstring. - Parameters ---------- prop_name: str @@ -411,71 +399,110 @@ (creates 2 spaces for every indent) is_flow_type: bool Does the prop use Flow types? Otherwise, uses PropTypes - Returns ------- str Dash component prop docstring """ py_type_name = js_to_py_type( - type_object=type_object, is_flow_type=is_flow_type, indent_num=indent_num + 1 + type_object=type_object, is_flow_type=is_flow_type, indent_num=indent_num ) indent_spacing = " " * indent_num - if default is None: - default = "" - else: - default = default["value"] - - if default in ["true", "false"]: - default = default.title() + default = default["value"] if default else "" + default = fix_keywords(default) is_required = "optional" if required: is_required = "required" - elif default and default not in ["null", "{}", "[]"]: - is_required = "default {}".format(default.replace("\n", "\n" + indent_spacing)) + elif default and default not in ["None", "{}", "[]"]: + is_required = "default {}".format(default.replace("\n", "")) + + # formats description + period = "." if description else "" + description = description.strip().strip(".").replace('"', r"\"") + period + desc_indent = indent_spacing + " " + description = fill( + description, + initial_indent=desc_indent, + subsequent_indent=desc_indent, + break_long_words=False, + break_on_hyphens=False, + ) + description = "\n{}".format(description) if description else "" + colon = ":" if description else "" + description = fix_keywords(description) if "\n" in py_type_name: + # corrects the type + dict_or_list = "list of dicts" if py_type_name.startswith("list") else "dict" + + # format and rewrite the intro to the nested dicts + intro1, intro2, dict_descr = py_type_name.partition("with keys:") + intro = "".join(["`{}`".format(prop_name), " is a ", intro1, intro2]) + intro = fill( + intro, + initial_indent=desc_indent, + subsequent_indent=desc_indent, + break_long_words=False, + break_on_hyphens=False, + ) + + # captures optional nested dict description and puts the "or" condition on a new line + if "| dict with keys:" in dict_descr: + dict_part1, dict_part2 = dict_descr.split(" |", 1) + dict_part2 = "".join([desc_indent, "Or", dict_part2]) + dict_descr = "{}\n\n {}".format(dict_part1, dict_part2) + + # ensures indent is correct if there is a second nested list of dicts + current_indent = dict_descr.lstrip("\n").find("-") + if current_indent == len(indent_spacing): + dict_descr = "".join( + "\n\n " + line for line in dict_descr.splitlines() if line != "" + ) + return ( - "{indent_spacing}- {name} (dict; {is_required}): " - "{description}{period}" - "{name} has the following type: {type}".format( + "\n{indent_spacing}- {name} ({dict_or_list}; {is_required}){colon}" + "{description}" + "\n\n{intro}{dict_descr}".format( indent_spacing=indent_spacing, name=prop_name, - type=py_type_name, - description=description.strip().strip("."), - period=". " if description else "", + colon=colon, + description=description, + intro=intro, + dict_descr=dict_descr, + dict_or_list=dict_or_list, is_required=is_required, ) ) - return "{indent_spacing}- {name} ({type}{is_required}){description}".format( - indent_spacing=indent_spacing, - name=prop_name, - type="{}; ".format(py_type_name) if py_type_name else "", - description=(": {}".format(description) if description != "" else ""), - is_required=is_required, + return ( + "\n{indent_spacing}- {name} ({type}{is_required}){colon}" + "{description}".format( + indent_spacing=indent_spacing, + name=prop_name, + type="{}; ".format(py_type_name) if py_type_name else "", + colon=colon, + description=description, + is_required=is_required, + ) ) -def map_js_to_py_types_prop_types(type_object): +def map_js_to_py_types_prop_types(type_object, indent_num): """Mapping from the PropTypes js type object to the Python type.""" def shape_or_exact(): - return "dict containing keys {}.\n{}".format( - ", ".join("'{}'".format(t) for t in list(type_object["value"].keys())), - "Those keys have the following types:\n{}".format( - "\n".join( - create_prop_docstring( - prop_name=prop_name, - type_object=prop, - required=prop["required"], - description=prop.get("description", ""), - default=prop.get("defaultValue"), - indent_num=1, - ) - for prop_name, prop in list(type_object["value"].items()) + return "dict with keys:\n{}".format( + "\n".join( + create_prop_docstring( + prop_name=prop_name, + type_object=prop, + required=prop["required"], + description=prop.get("description", ""), + default=prop.get("defaultValue"), + indent_num=indent_num + 2, ) + for prop_name, prop in sorted(list(type_object["value"].items())) ), ) @@ -526,7 +553,6 @@ def map_js_to_py_types_flow_types(type_object): """Mapping from the Flow js types to the Python type.""" - return dict( array=lambda: "list", boolean=lambda: "boolean", @@ -551,24 +577,18 @@ else "" ), # React's PropTypes.shape - signature=lambda indent_num: "dict containing keys {}.\n{}".format( - ", ".join( - "'{}'".format(d["key"]) for d in type_object["signature"]["properties"] - ), - "{}Those keys have the following types:\n{}".format( - " " * indent_num, - "\n".join( - create_prop_docstring( - prop_name=prop["key"], - type_object=prop["value"], - required=prop["value"]["required"], - description=prop["value"].get("description", ""), - default=prop.get("defaultValue"), - indent_num=indent_num, - is_flow_type=True, - ) - for prop in type_object["signature"]["properties"] - ), + signature=lambda indent_num: "dict with keys:\n{}".format( + "\n".join( + create_prop_docstring( + prop_name=prop["key"], + type_object=prop["value"], + required=prop["value"]["required"], + description=prop["value"].get("description", ""), + default=prop.get("defaultValue"), + indent_num=indent_num + 2, + is_flow_type=True, + ) + for prop in type_object["signature"]["properties"] ), ), ) @@ -576,7 +596,6 @@ def js_to_py_type(type_object, is_flow_type=False, indent_num=0): """Convert JS types to Python types for the component definition. - Parameters ---------- type_object: dict @@ -585,17 +604,19 @@ Does the prop use Flow types? Otherwise, uses PropTypes indent_num: int Number of indents to use for the docstring for the prop - Returns ------- str Python type string """ + js_type_name = type_object["name"] js_to_py_types = ( map_js_to_py_types_flow_types(type_object=type_object) if is_flow_type - else map_js_to_py_types_prop_types(type_object=type_object) + else map_js_to_py_types_prop_types( + type_object=type_object, indent_num=indent_num + ) ) if ( Binary files old/dash-1.18.1/dash/favicon.ico and new/dash-1.20.0/dash/favicon.ico differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dash-1.18.1/dash/resources.py new/dash-1.20.0/dash/resources.py --- old/dash-1.18.1/dash/resources.py 2020-12-08 14:42:55.000000000 +0100 +++ new/dash-1.20.0/dash/resources.py 2021-01-20 00:35:20.000000000 +0100 @@ -24,8 +24,12 @@ if "async" in s: if "dynamic" in s: raise exceptions.ResourceException( - "Can't have both 'dynamic' and 'async'. " - "{}".format(json.dumps(filtered_resource)) + """ + Can't have both 'dynamic' and 'async'. + {} + """.format( + json.dumps(filtered_resource) + ) ) # Async assigns a value dynamically to 'dynamic' @@ -70,9 +74,12 @@ continue else: raise exceptions.ResourceException( - "{} does not have a " - "relative_package_path, absolute_path, or an " - "external_url.".format(json.dumps(filtered_resource)) + """ + {} does not have a relative_package_path, absolute_path, + or an external_url. + """.format( + json.dumps(filtered_resource) + ) ) filtered_resources.append(filtered_resource) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dash-1.18.1/dash/testing/application_runners.py new/dash-1.20.0/dash/testing/application_runners.py --- old/dash-1.18.1/dash/testing/application_runners.py 2020-12-08 14:42:55.000000000 +0100 +++ new/dash-1.20.0/dash/testing/application_runners.py 2021-01-20 00:35:20.000000000 +0100 @@ -125,7 +125,7 @@ stopper() return "Flask server is shutting down" - # pylint: disable=arguments-differ,C0330 + # pylint: disable=arguments-differ def start(self, app, **kwargs): """Start the app server in threading flavor.""" app.server.add_url_rule(self.stop_route, self.stop_route, self._stop_server) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dash-1.18.1/dash/testing/browser.py new/dash-1.20.0/dash/testing/browser.py --- old/dash-1.18.1/dash/testing/browser.py 2020-12-08 14:42:55.000000000 +0100 +++ new/dash-1.20.0/dash/testing/browser.py 2021-04-09 00:03:55.000000000 +0200 @@ -77,10 +77,10 @@ ) self.percy_runner.initialize_build() - logger.info("initialize browser with arguments") - logger.info(" headless => %s", self._headless) - logger.info(" download_path => %s", self._download_path) - logger.info(" percy asset root => %s", os.path.abspath(percy_assets_root)) + logger.debug("initialize browser with arguments") + logger.debug(" headless => %s", self._headless) + logger.debug(" download_path => %s", self._download_path) + logger.debug(" percy asset root => %s", os.path.abspath(percy_assets_root)) def __enter__(self): return self @@ -358,9 +358,9 @@ if self._pause: try: - import pdb as pdb_ + import pdb as pdb_ # pylint: disable=import-outside-toplevel except ImportError: - import ipdb as pdb_ + import ipdb as pdb_ # pylint: disable=import-outside-toplevel pdb_.set_trace() @@ -431,6 +431,7 @@ capabilities = DesiredCapabilities.CHROME capabilities["loggingPrefs"] = {"browser": "SEVERE"} + capabilities["goog:loggingPrefs"] = {"browser": "SEVERE"} if "DASH_TEST_CHROMEPATH" in os.environ: options.binary_location = os.environ["DASH_TEST_CHROMEPATH"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dash-1.18.1/dash/testing/plugin.py new/dash-1.20.0/dash/testing/plugin.py --- old/dash-1.18.1/dash/testing/plugin.py 2020-12-08 14:42:55.000000000 +0100 +++ new/dash-1.20.0/dash/testing/plugin.py 2021-01-20 00:35:20.000000000 +0100 @@ -65,7 +65,7 @@ def pytest_addhooks(pluginmanager): # https://github.com/pytest-dev/pytest-xdist/blob/974bd566c599dc6a9ea291838c6f226197208b46/xdist/plugin.py#L67 # avoid warnings with pytest-2.8 - from dash.testing import newhooks + from dash.testing import newhooks # pylint: disable=import-outside-toplevel method = getattr(pluginmanager, "add_hookspecs", None) if method is None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dash-1.18.1/dash/version.py new/dash-1.20.0/dash/version.py --- old/dash-1.18.1/dash/version.py 2020-12-09 22:30:29.000000000 +0100 +++ new/dash-1.20.0/dash/version.py 2021-04-09 00:03:55.000000000 +0200 @@ -1 +1 @@ -__version__ = "1.18.1" +__version__ = "1.20.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dash-1.18.1/dash.egg-info/PKG-INFO new/dash-1.20.0/dash.egg-info/PKG-INFO --- old/dash-1.18.1/dash.egg-info/PKG-INFO 2020-12-09 22:33:39.000000000 +0100 +++ new/dash-1.20.0/dash.egg-info/PKG-INFO 2021-04-09 00:19:30.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: dash -Version: 1.18.1 +Version: 1.20.0 Summary: A Python framework for building reactive web-apps. Developed by Plotly. Home-page: https://plotly.com/dash Author: Chris Parmer @@ -43,9 +43,9 @@ Scale up with Dash Enterprise when your Dash app is ready for department or company-wide consumption. Or, launch your initiative with Dash Enterprise from the start to unlock developer productivity gains and hands-on acceleration from Plotly's team. - ML Ops Features: A one-stop shop for ML Ops: Horizontally scalable hosting, deployment, and authentication for your Dash apps. No IT or DevOps required. + ML Ops Features: A one-stop shop for ML Ops: Horizontally scalable hosting, deployment, and authentication for your Dash apps. No IT or DevOps required. - [**App manager**](https://plotly.com/dash/app-manager/) Deploy & manage Dash apps without needing IT or a DevOps team. App Manager gives you point & click control over all aspects of your Dash deployments. - - [**Kubernetes scaling**](https://plotly.com/dash/kubernetes/) Ensure high availability of Dash apps and scale horizontally with Dash Enterprise???s Kubernetes architecture. No IT or Helm required. + - [**Kubernetes scaling**](https://plotly.com/dash/kubernetes/) Ensure high availability of Dash apps and scale horizontally with Dash Enterprise???s Kubernetes architecture. No IT or Helm required. - [**No code auth**](https://plotly.com/dash/authentication/) Control Dash app access in a few clicks. Dash Enterprise supports LDAP, AD, PKI, Okta, SAML, OpenID Connect, OAuth, SSO, and simple email authentication. - [**Job Queue**](https://plotly.com/dash/job-queue/) The Job Queue is the key to building scalable Dash apps. Move heavy computation from synchronous Dash callbacks to the Job Queue for asynchronous background processing. @@ -61,7 +61,6 @@ - [**GPU & Dask Acceleration**](https://plotly.com/dash/gpu-dask-acceleration/) Dash Enterprise puts Python???s most popular HPC stack for GPU and parallel CPU computing in the hands of business users. - [**Data Science Workspaces**](https://plotly.com/dash/workspaces/) Be productive from Day 1. Write and execute Python, R, & Julia code from Dash Enterprise's onboard code editor. - See [https://plotly.com/contact-us/](https://plotly.com/contact-us/) to get in touch.  diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dash-1.18.1/dash.egg-info/requires.txt new/dash-1.20.0/dash.egg-info/requires.txt --- old/dash-1.18.1/dash.egg-info/requires.txt 2020-12-09 22:33:39.000000000 +0100 +++ new/dash-1.20.0/dash.egg-info/requires.txt 2021-04-09 00:19:30.000000000 +0200 @@ -1,51 +1,54 @@ Flask>=1.0.4 flask-compress plotly -dash_renderer==1.8.3 -dash-core-components==1.14.1 -dash-html-components==1.1.1 -dash-table==4.11.1 +dash_renderer==1.9.1 +dash-core-components==1.16.0 +dash-html-components==1.1.3 +dash-table==4.11.3 future [dev] dash_flow_example==0.0.5 dash-dangerously-set-inner-html -isort==4.3.21 -flake8==3.7.9 -PyYAML==5.3 -fire==0.2.1 -coloredlogs==14.0 +flake8==3.8.4 +PyYAML==5.3.1 +fire==0.3.1 +coloredlogs==15.0 +flask-talisman==0.7.0 [dev:python_version < "3.7"] -pylint==1.9.4 +isort==4.3.21 +pylint==1.9.5 [dev:python_version == "2.7"] mock==3.0.5 -virtualenv==20.0.10 - -[dev:python_version == "3.7"] -pylint==2.3.1 -astroid==2.2.5 +virtualenv==20.2.2 [dev:python_version >= "3.0"] -mock==4.0.1 +mock==4.0.3 black==19.10b0 +[dev:python_version >= "3.7"] +pylint==2.6.0 +astroid==2.4.2 + [testing] -pytest-sugar==0.9.4 -lxml==4.6.1 -selenium==3.141.0 -percy==2.0.2 -cryptography==3.0 -requests[security]==2.21.0 -beautifulsoup4==4.8.2 -waitress==1.4.3 -flask-talisman==0.7.0 +pytest-sugar>=0.9.4 +lxml>=4.6.2 +selenium>=3.141.0 +percy>=2.0.2 +requests[security]>=2.21.0 +waitress>=1.4.4 + +[testing:python_version < "3.7"] +cryptography<3.4 [testing:python_version == "2.7"] -pytest==4.6.9 -pytest-mock==2.0.0 +pytest<5,>=4.6 +pytest-mock<3,>=2.0.0 +beautifulsoup4<=4.9.3,>=4.8.2 [testing:python_version >= "3.0"] -pytest==6.0.1 -pytest-mock==3.2.0 +pytest>=6.0.2 +pytest-mock>=3.2.0 +beautifulsoup4>=4.8.2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dash-1.18.1/requires-dev.txt new/dash-1.20.0/requires-dev.txt --- old/dash-1.18.1/requires-dev.txt 2020-12-08 14:42:55.000000000 +0100 +++ new/dash-1.20.0/requires-dev.txt 2021-01-20 00:35:20.000000000 +0100 @@ -1,14 +1,15 @@ dash_flow_example==0.0.5 dash-dangerously-set-inner-html -isort==4.3.21 -mock==4.0.1;python_version>="3.0" +isort==4.3.21;python_version<"3.7" +mock==4.0.3;python_version>="3.0" mock==3.0.5;python_version=="2.7" -flake8==3.7.9 -PyYAML==5.3 -pylint==1.9.4;python_version<"3.7" -pylint==2.3.1;python_version=="3.7" -astroid==2.2.5;python_version=="3.7" +flake8==3.8.4 +PyYAML==5.3.1 +pylint==1.9.5;python_version<"3.7" +pylint==2.6.0;python_version>="3.7" +astroid==2.4.2;python_version>="3.7" black==19.10b0;python_version>="3.0" -virtualenv==20.0.10;python_version=="2.7" -fire==0.2.1 -coloredlogs==14.0 +virtualenv==20.2.2;python_version=="2.7" +fire==0.3.1 +coloredlogs==15.0 +flask-talisman==0.7.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dash-1.18.1/requires-install.txt new/dash-1.20.0/requires-install.txt --- old/dash-1.18.1/requires-install.txt 2020-12-09 22:30:29.000000000 +0100 +++ new/dash-1.20.0/requires-install.txt 2021-04-09 00:03:55.000000000 +0200 @@ -1,8 +1,8 @@ Flask>=1.0.4 flask-compress plotly -dash_renderer==1.8.3 -dash-core-components==1.14.1 -dash-html-components==1.1.1 -dash-table==4.11.1 +dash_renderer==1.9.1 +dash-core-components==1.16.0 +dash-html-components==1.1.3 +dash-table==4.11.3 future diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/dash-1.18.1/requires-testing.txt new/dash-1.20.0/requires-testing.txt --- old/dash-1.18.1/requires-testing.txt 2020-12-08 14:42:55.000000000 +0100 +++ new/dash-1.20.0/requires-testing.txt 2021-04-09 00:03:55.000000000 +0200 @@ -1,13 +1,13 @@ -pytest==6.0.1;python_version>="3.0" -pytest==4.6.9;python_version=="2.7" -pytest-sugar==0.9.4 -pytest-mock==3.2.0;python_version>="3.0" -pytest-mock==2.0.0;python_version=="2.7" -lxml==4.6.1 -selenium==3.141.0 -percy==2.0.2 -cryptography==3.0 -requests[security]==2.21.0 -beautifulsoup4==4.8.2 -waitress==1.4.3 -flask-talisman==0.7.0 +pytest>=6.0.2;python_version>="3.0" +pytest>=4.6,<5;python_version=="2.7" +pytest-sugar>=0.9.4 +pytest-mock>=3.2.0;python_version>="3.0" +pytest-mock>=2.0.0,<3;python_version=="2.7" +lxml>=4.6.2 +selenium>=3.141.0 +percy>=2.0.2 +cryptography<3.4;python_version<"3.7" +requests[security]>=2.21.0 +beautifulsoup4>=4.8.2,<=4.9.3;python_version=="2.7" +beautifulsoup4>=4.8.2;python_version>="3.0" +waitress>=1.4.4