diff --git a/CHANGELOG.md b/CHANGELOG.md index 42d9ad2c30..49bf3ed5a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ All notable changes to `dash` will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added +- [#1134](https://github.com/plotly/dash/pull/1134) Allow `dash.run_server()` host and port parameters to be set with environment variables HOST & PORT, respectively + ### Changed - [#1145](https://github.com/plotly/dash/pull/1145) Update from React 16.8.6 to 16.13.0 diff --git a/dash/_configs.py b/dash/_configs.py index f7e8c59bae..802705ed07 100644 --- a/dash/_configs.py +++ b/dash/_configs.py @@ -30,6 +30,8 @@ def load_dash_env_vars(): "DASH_SILENCE_ROUTES_LOGGING", "DASH_PRUNE_ERRORS", "DASH_COMPRESS", + "HOST", + "PORT", ) } ) diff --git a/dash/dash.py b/dash/dash.py index b1ae3a1a01..09c2ef2fb6 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -1841,7 +1841,8 @@ def delete_resource(resources): def run_server( self, - port=8050, + host=os.getenv("HOST", "127.0.0.1"), + port=os.getenv("PORT", "8050"), debug=False, dev_tools_ui=None, dev_tools_props_check=None, @@ -1860,7 +1861,12 @@ def run_server( If a parameter can be set by an environment variable, that is listed too. Values provided here take precedence over environment variables. + :param host: Host IP used to serve the application + env: ``HOST`` + :type host: string + :param port: Port used to serve the application + env: ``PORT`` :type port: int :param debug: Set Flask debug mode and enable dev tools. @@ -1933,9 +1939,18 @@ def run_server( dev_tools_prune_errors, ) + # Verify port value + try: + port = int(port) + assert port in range(1, 65536) + except Exception as e: + e.args = [ + "Expecting an integer from 1 to 65535, found port={}".format(repr(port)) + ] + raise + if self._dev_tools.silence_routes_logging: # Since it's silenced, the address doesn't show anymore. - host = flask_run_options.get("host", "127.0.0.1") ssl_context = flask_run_options.get("ssl_context") self.logger.info( "Running on %s://%s:%s%s", @@ -1955,4 +1970,4 @@ def run_server( self.logger.info("Debugger PIN: %s", debugger_pin) - self.server.run(port=port, debug=debug, **flask_run_options) + self.server.run(host=host, port=port, debug=debug, **flask_run_options) diff --git a/tests/integration/test_integration.py b/tests/integration/test_integration.py index a81c9bbb56..f1f5c670b2 100644 --- a/tests/integration/test_integration.py +++ b/tests/integration/test_integration.py @@ -974,3 +974,11 @@ def g2(a): @app.callback(Output("inner-div", "children"), [Input("inner-input", "value")]) def h(a): return a + + +def test_inin_024_port_env_success(dash_duo): + app = Dash(__name__) + app.layout = html.Div("hi", "out") + dash_duo.start_server(app, port="12345") + assert dash_duo.server_url == "http://localhost:12345" + dash_duo.wait_for_text_to_equal("#out", "hi") diff --git a/tests/unit/test_configs.py b/tests/unit/test_configs.py index 54ca653a83..2eff1a5660 100644 --- a/tests/unit/test_configs.py +++ b/tests/unit/test_configs.py @@ -231,3 +231,30 @@ def test_strip_relative_path(prefix, partial_path, expected): def test_invalid_strip_relative_path(prefix, partial_path): with pytest.raises(_exc.UnsupportedRelativePath): strip_relative_path(prefix, partial_path) + + +def test_port_env_fail_str(empty_environ): + app = Dash() + with pytest.raises(Exception) as excinfo: + app.run_server(port="garbage") + assert ( + excinfo.exconly() + == "ValueError: Expecting an integer from 1 to 65535, found port='garbage'" + ) + + +def test_port_env_fail_range(empty_environ): + app = Dash() + with pytest.raises(Exception) as excinfo: + app.run_server(port="0") + assert ( + excinfo.exconly() + == "AssertionError: Expecting an integer from 1 to 65535, found port=0" + ) + + with pytest.raises(Exception) as excinfo: + app.run_server(port="65536") + assert ( + excinfo.exconly() + == "AssertionError: Expecting an integer from 1 to 65535, found port=65536" + )