diff --git a/CHANGELOG.md b/CHANGELOG.md index d7bf82c57a..2cd893bd6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,11 @@ This project adheres to [Semantic Versioning](https://semver.org/). ## [UNRELEASED] ## Dash and Dash Renderer +### Added +- [#1675](https://github.com/plotly/dash/pull/1675) Add new `Dash` constructor argument `extra_hot_reload_paths`. This allows you to re-initialize the Python code of the app when non-Python files change, if you know that these files impact the app. + ### Changed +- [#1675](https://github.com/plotly/dash/pull/1675) Remove the constraint that `requests_pathname_prefix` ends with `routes_pathname_prefix`. When you are serving your app behind a reverse proxy that rewrites URLs that constraint needs to be violated. - [#1611](https://github.com/plotly/dash/pull/1611) Package dash-renderer artifacts and dependencies with Dash, and source renderer resources from within Dash. - [#1567](https://github.com/plotly/dash/pull/1567) Julia component generator puts components into `src/jl` - fixes an issue on case-insensitive filesystems when the component name and module name match (modulo case) and no prefix is used. Also reduces JS/Julia clutter in the overloaded `src` directory. diff --git a/dash/_configs.py b/dash/_configs.py index 802705ed07..35a10b5b32 100644 --- a/dash/_configs.py +++ b/dash/_configs.py @@ -117,9 +117,5 @@ def pathname_configs( raise exceptions.InvalidConfig( "`requests_pathname_prefix` needs to start with `/`" ) - if not requests_pathname_prefix.endswith(routes_pathname_prefix): - raise exceptions.InvalidConfig( - "`requests_pathname_prefix` needs to ends with `routes_pathname_prefix`." - ) return url_base_pathname, routes_pathname_prefix, requests_pathname_prefix diff --git a/dash/dash.py b/dash/dash.py index fb7f10f951..b4a3adf00f 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -232,6 +232,11 @@ class Dash(object): and redo buttons for stepping through the history of the app state. :type show_undo_redo: boolean + :param extra_hot_reload_paths: A list of paths to watch for changes, in + addition to assets and known Python and JS code, if hot reloading is + enabled. + :type extra_hot_reload_paths: list of strings + :param plugins: Extend Dash functionality by passing a list of objects with a ``plug`` method, taking a single argument: this app, which will be called after the Flask server is attached. @@ -269,6 +274,7 @@ def __init__( suppress_callback_exceptions=None, prevent_initial_callbacks=False, show_undo_redo=False, + extra_hot_reload_paths=None, plugins=None, title="Dash", update_title="Updating...", @@ -329,6 +335,7 @@ def __init__( ), prevent_initial_callbacks=prevent_initial_callbacks, show_undo_redo=show_undo_redo, + extra_hot_reload_paths=extra_hot_reload_paths or [], title=title, update_title=update_title, ) @@ -1730,4 +1737,14 @@ def verify_url_part(served_part, url_part, part_name): self.logger.info("Dash is running on %s://%s%s%s\n", *display_url) + if self.config.extra_hot_reload_paths: + extra_files = flask_run_options["extra_files"] = [] + for path in self.config.extra_hot_reload_paths: + if os.path.isdir(path): + for dirpath, _, filenames in os.walk(path): + for fn in filenames: + extra_files.append(os.path.join(dirpath, fn)) + elif os.path.isfile(path): + extra_files.append(path) + self.server.run(host=host, port=port, debug=debug, **flask_run_options) diff --git a/dash/development/component_generator.py b/dash/development/component_generator.py index 97da66cf9d..2db2551d66 100644 --- a/dash/development/component_generator.py +++ b/dash/development/component_generator.py @@ -49,6 +49,7 @@ def generate_components( rimports="", rsuggests="", jlprefix=None, + metadata=None, ): project_shortname = project_shortname.replace("-", "_").rstrip("/\\") @@ -61,36 +62,37 @@ def generate_components( os.environ["NODE_PATH"] = "node_modules" - cmd = shlex.split( - 'node {} "{}" "{}" {}'.format( - extract_path, ignore, reserved_patterns, components_source - ), - posix=not is_windows, - ) - shutil.copyfile( "package.json", os.path.join(project_shortname, package_info_filename) ) - proc = subprocess.Popen( - cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=is_windows - ) - out, err = proc.communicate() - status = proc.poll() - - if err: - print(err.decode(), file=sys.stderr) - - if not out: - print( - "Error generating metadata in {} (status={})".format( - project_shortname, status + if not metadata: + cmd = shlex.split( + 'node {} "{}" "{}" {}'.format( + extract_path, ignore, reserved_patterns, components_source ), - file=sys.stderr, + posix=not is_windows, + ) + + proc = subprocess.Popen( + cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=is_windows ) - sys.exit(1) + out, err = proc.communicate() + status = proc.poll() + + if err: + print(err.decode(), file=sys.stderr) + + if not out: + print( + "Error generating metadata in {} (status={})".format( + project_shortname, status + ), + file=sys.stderr, + ) + sys.exit(1) - metadata = safe_json_loads(out.decode("utf-8")) + metadata = safe_json_loads(out.decode("utf-8")) generator_methods = [generate_class_file] @@ -148,7 +150,7 @@ def safe_json_loads(s): return byteify(jsondata_unicode) -def cli(): +def component_build_arg_parser(): parser = argparse.ArgumentParser( prog="dash-generate-components", formatter_class=_CombinedFormatter, @@ -199,8 +201,11 @@ def cli(): help="Specify a prefix for Dash for R component names, write " "components to R dir, create R package.", ) + return parser + - args = parser.parse_args() +def cli(): + args = component_build_arg_parser().parse_args() generate_components( args.components_source, args.project_shortname,