From 805ee120da3d6754895b528c5e47b2c9e1b83664 Mon Sep 17 00:00:00 2001 From: Philippe Duval Date: Thu, 26 Jul 2018 12:14:59 -0400 Subject: [PATCH 1/5] Add `external_js/css_urls` to dash ctor, support for external only urls --- dash/dash.py | 13 ++++++++++--- tests/test_integration.py | 24 +++++++++++++++++++++++- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/dash/dash.py b/dash/dash.py index 44fad1efee..01ce6051f4 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -61,7 +61,7 @@ # pylint: disable=too-many-instance-attributes -# pylint: disable=too-many-arguments +# pylint: disable=too-many-arguments, too-many-locals class Dash(object): def __init__( self, @@ -75,6 +75,8 @@ def __init__( compress=True, meta_tags=None, index_string=_default_index, + external_script_urls=None, + external_css_urls=None, **kwargs): # pylint-disable: too-many-instance-attributes @@ -128,6 +130,10 @@ def _handle_error(error): # static files from the packages self.css = Css() self.scripts = Scripts() + + self._external_scripts_urls = external_script_urls or [] + self._external_css_urls = external_css_urls or [] + self.registered_paths = {} # urls @@ -302,7 +308,8 @@ def _relative_url_path(relative_package_path='', namespace=''): def _generate_css_dist_html(self): links = self._collect_and_register_resources( self.css.get_all_css() - ) + ) + self._external_css_urls + return '\n'.join([ ''.format(link) for link in links @@ -324,7 +331,7 @@ def _generate_scripts_html(self): self.scripts._resources._filter_resources( dash_renderer._js_dist ) - ) + ) + self._external_scripts_urls return '\n'.join([ ''.format(src) diff --git a/tests/test_integration.py b/tests/test_integration.py index 770b9152cb..aef8f05ffd 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -434,4 +434,26 @@ def will_raise(): self.assertTrue('{%config%}' in exc_msg) self.assertTrue('{%scripts%}' in exc_msg) time.sleep(0.5) - print('invalid index string') + + def test_external_files_init(self): + js_files = [ + 'https://www.google-analytics.com/analytics.js', + 'https://cdn.polyfill.io/v2/polyfill.min.js' + ] + css_files = [ + 'https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css', + 'https://codepen.io/chriddyp/pen/bWLwgP.css' + ] + + app = dash.Dash( + external_script_urls=js_files, external_css_urls=css_files) + + app.layout = html.Div() + + self.startServer(app) + time.sleep(0.5) + + for fmt, url in itertools.chain( + (("//script[@src='{}']", x) for x in js_files), + (("//link[@href='{}']", x) for x in css_files)): + self.driver.find_element_by_xpath(fmt.format(url)) From 2c721ba446b2f61d06dfb53e14dfd740bde446c6 Mon Sep 17 00:00:00 2001 From: Philippe Duval Date: Wed, 1 Aug 2018 10:42:28 -0400 Subject: [PATCH 2/5] Ensure dash_renderer is loaded after externals. --- dash/dash.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dash/dash.py b/dash/dash.py index 01ce6051f4..1205a8b284 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -331,7 +331,8 @@ def _generate_scripts_html(self): self.scripts._resources._filter_resources( dash_renderer._js_dist ) - ) + self._external_scripts_urls + ) + srcs = srcs[:-1] + self._external_scripts_urls + [srcs[-1]] return '\n'.join([ ''.format(src) From 86ea309c3afac7e111d1d6aa4b7285e94074d5e6 Mon Sep 17 00:00:00 2001 From: Philippe Duval Date: Wed, 1 Aug 2018 14:31:34 -0400 Subject: [PATCH 3/5] Add support for arbitrary (link/scripts) tags with attributes. --- dash/_utils.py | 14 ++++++++++++++ dash/dash.py | 30 +++++++++++++++++------------- tests/test_integration.py | 25 +++++++++++++++++++------ 3 files changed, 50 insertions(+), 19 deletions(-) diff --git a/dash/_utils.py b/dash/_utils.py index 1c6e5ca51c..bc71fde955 100644 --- a/dash/_utils.py +++ b/dash/_utils.py @@ -6,6 +6,20 @@ def interpolate_str(template, **data): return s +def format_tag(tag_name, attributes, inner='', closed=False, opened=False): + tag = '<{tag} {attributes}' + if closed: + tag += '/>' + elif opened: + tag += '>' + else: + tag += '>' + inner + '' + return tag.format( + tag=tag_name, + attributes=' '.join([ + '{}="{}"'.format(k, v) for k, v in attributes.items()])) + + class AttributeDict(dict): """ Dictionary subclass enabling attribute lookup/assignment of keys/values. diff --git a/dash/dash.py b/dash/dash.py index 1205a8b284..34ffe27ca5 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -23,6 +23,7 @@ from . import exceptions from ._utils import AttributeDict as _AttributeDict from ._utils import interpolate_str as _interpolate +from ._utils import format_tag as _format_tag _default_index = ''' @@ -75,8 +76,8 @@ def __init__( compress=True, meta_tags=None, index_string=_default_index, - external_script_urls=None, - external_css_urls=None, + external_scripts=None, + external_stylesheets=None, **kwargs): # pylint-disable: too-many-instance-attributes @@ -131,8 +132,8 @@ def _handle_error(error): self.css = Css() self.scripts = Scripts() - self._external_scripts_urls = external_script_urls or [] - self._external_css_urls = external_css_urls or [] + self._external_scripts = external_scripts or [] + self._external_stylesheets = external_stylesheets or [] self.registered_paths = {} @@ -308,10 +309,12 @@ def _relative_url_path(relative_package_path='', namespace=''): def _generate_css_dist_html(self): links = self._collect_and_register_resources( self.css.get_all_css() - ) + self._external_css_urls + ) + self._external_stylesheets return '\n'.join([ - ''.format(link) + _format_tag('link', link, opened=True) + if isinstance(link, dict) + else ''.format(link) for link in links ]) @@ -332,10 +335,12 @@ def _generate_scripts_html(self): dash_renderer._js_dist ) ) - srcs = srcs[:-1] + self._external_scripts_urls + [srcs[-1]] + srcs = srcs[:-1] + self._external_scripts + [srcs[-1]] return '\n'.join([ - ''.format(src) + _format_tag('script', src) + if isinstance(src, dict) + else ''.format(src) for src in srcs ]) @@ -352,11 +357,10 @@ def _generate_meta_html(self): tags = [] if not has_charset: tags.append('') - for meta in self._meta_tags: - attributes = [] - for k, v in meta.items(): - attributes.append('{}="{}"'.format(k, v)) - tags.append(''.format(' '.join(attributes))) + + tags = tags + [ + _format_tag('meta', x, opened=True) for x in self._meta_tags + ] return '\n '.join(tags) diff --git a/tests/test_integration.py b/tests/test_integration.py index aef8f05ffd..3cb378c820 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -438,22 +438,35 @@ def will_raise(): def test_external_files_init(self): js_files = [ 'https://www.google-analytics.com/analytics.js', - 'https://cdn.polyfill.io/v2/polyfill.min.js' + {'src': 'https://cdn.polyfill.io/v2/polyfill.min.js'}, + { + 'src': 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.core.js', + 'integrity': 'sha256-Qqd/EfdABZUcAxjOkMi8eGEivtdTkh3b65xCZL4qAQA=', + 'crossorigin': 'anonymous' + } ] css_files = [ - 'https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css', - 'https://codepen.io/chriddyp/pen/bWLwgP.css' + 'https://codepen.io/chriddyp/pen/bWLwgP.css', + { + 'href': 'https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css', + 'rel': 'stylesheet', + 'integrity': 'sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO', + 'crossorigin': 'anonymous' + } ] app = dash.Dash( - external_script_urls=js_files, external_css_urls=css_files) + external_scripts=js_files, external_stylesheets=css_files) app.layout = html.Div() self.startServer(app) time.sleep(0.5) + js_urls = [x['src'] if isinstance(x, dict) else x for x in js_files] + css_urls = [x['href'] if isinstance(x, dict) else x for x in css_files] + for fmt, url in itertools.chain( - (("//script[@src='{}']", x) for x in js_files), - (("//link[@href='{}']", x) for x in css_files)): + (("//script[@src='{}']", x) for x in js_urls), + (("//link[@href='{}']", x) for x in css_urls)): self.driver.find_element_by_xpath(fmt.format(url)) From bdbdf34463ff819294db268cc568638675ad8b35 Mon Sep 17 00:00:00 2001 From: Philippe Duval Date: Thu, 2 Aug 2018 10:59:02 -0400 Subject: [PATCH 4/5] Remove whitespace from merge. --- dash/dash.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dash/dash.py b/dash/dash.py index 1009d78845..7e64b7a973 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -366,7 +366,7 @@ def _generate_meta_html(self): tags = tags + [ _format_tag('meta', x, opened=True) for x in self._meta_tags ] - + return '\n '.join(tags) # Serve the JS bundles for each package From 4934c7ff97bbd7eb7639fa4d8a03d30ecc891f77 Mon Sep 17 00:00:00 2001 From: Philippe Duval Date: Thu, 2 Aug 2018 11:19:43 -0400 Subject: [PATCH 5/5] Update version and changelog. --- CHANGELOG.md | 5 +++++ dash/version.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 519e2f0fe7..1ed3850535 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.23.1 - 2018-08-02 +## Added +- Add ie-compat meta tag to the index by default. [#316](https://github.com/plotly/dash/pull/316) +- Add `external_script` and `external_css` keywords to dash `__init__`. [#305](https://github.com/plotly/dash/pull/305) + ## 0.23.0 - 2018-08-01 ## Added - Dash components are now generated at build-time and then imported rather than generated when a module is imported. This should reduce the time it takes to import Dash component libraries, and makes Dash compatible with IDEs. diff --git a/dash/version.py b/dash/version.py index 08a9dbff61..db714a8550 100644 --- a/dash/version.py +++ b/dash/version.py @@ -1 +1 @@ -__version__ = '0.23.0' +__version__ = '0.23.1'