Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
Marc-André Rivet committed Oct 30, 2019
2 parents 5fd4e71 + 8e0c80d commit 6150ec5
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 56 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
All notable changes to `dash` will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [1.5.1] - 2019-10-29
### Fixed
- [#987](https://github.com/plotly/dash/pull/987) Fix cache string handling for component suites with nested folders in their packages.
- [#986](https://github.com/plotly/dash/pull/986) Fix a bug with evaluation of `_force_eager_loading` when application is loaded with gunicorn

## [1.5.0] - 2019-10-29
### Added
- [#964](https://github.com/plotly/dash/pull/964) Adds support for preventing updates in clientside functions.
Expand Down
17 changes: 11 additions & 6 deletions dash/dash.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,12 +229,6 @@ def __init__(
plugins=None,
**obsolete
):
# Apply _force_eager_loading overrides from modules
for module_name in ComponentRegistry.registry:
module = sys.modules[module_name]
eager = getattr(module, '_force_eager_loading', False)
eager_loading = eager_loading or eager

for key in obsolete:
if key in ["components_cache_max_age", "static_folder"]:
raise exceptions.ObsoleteKwargException(
Expand Down Expand Up @@ -272,6 +266,7 @@ def __init__(
assets_external_path=get_combined_config(
"assets_external_path", assets_external_path, ""
),
eager_loading=eager_loading,
include_assets_files=get_combined_config(
"include_assets_files", include_assets_files, True
),
Expand Down Expand Up @@ -1434,6 +1429,16 @@ def _validate_layout(self):
component_ids.add(component_id)

def _setup_server(self):
# Apply _force_eager_loading overrides from modules
eager_loading = self.config.eager_loading
for module_name in ComponentRegistry.registry:
module = sys.modules[module_name]
eager = getattr(module, '_force_eager_loading', False)
eager_loading = eager_loading or eager

# Update eager_loading settings
self.scripts.config.eager_loading = eager_loading

if self.config.include_assets_files:
self._walk_assets_directory()

Expand Down
34 changes: 15 additions & 19 deletions dash/fingerprint.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,27 @@
import re

build_regex = re.compile(r'^(?P<filename>[\w@~-]+)(?P<extension>.*)$')

check_regex = re.compile(
r'^(?P<filename>.*)[.]v[\w-]+m[0-9a-fA-F]+(?P<extension>(?:(?:(?<![.])[.])?[\w])+)$'
)
cache_regex = re.compile(r"^v[\w-]+m[0-9a-fA-F]+$")


def build_fingerprint(path, version, hash_value):
res = build_regex.match(path)
path_parts = path.split("/")
filename, extension = path_parts[-1].split(".", 1)

return '{}.v{}m{}{}'.format(
res.group('filename'),
str(version).replace('.', '_'),
return "{}.v{}m{}.{}".format(
"/".join(path_parts[:-1] + [filename]),
str(version).replace(".", "_"),
hash_value,
res.group('extension'),
extension,
)


def check_fingerprint(path):
path_parts = path.split("/")
name_parts = path_parts[-1].split(".")

# Check if the resource has a fingerprint
res = check_regex.match(path)

# Resolve real resource name from fingerprinted resource path
return (
res.group('filename') + res.group('extension')
if res is not None
else path,
res is not None,
)
if len(name_parts) > 2 and cache_regex.match(name_parts[1]):
original_name = ".".join([name_parts[0]] + name_parts[2:])
return "/".join(path_parts[:-1] + [original_name]), True

return path, False
2 changes: 1 addition & 1 deletion dash/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.5.0'
__version__ = '1.5.1'
126 changes: 96 additions & 30 deletions tests/unit/test_fingerprint.py
Original file line number Diff line number Diff line change
@@ -1,56 +1,122 @@

from dash.fingerprint import build_fingerprint, check_fingerprint

version = 1
hash_value = 1

valid_resources = [
{'path': '[email protected]', 'fingerprint': '[email protected]'},
{'path': '[email protected]', 'fingerprint': '[email protected]_1_1m1234567890abcdef.8.6.min.js', 'version': '1.1.1', 'hash': '1234567890abcdef' },
{'path': '[email protected]', 'fingerprint': '[email protected]_1_1-alpha_1m1234567890abcdef.8.6.min.js', 'version': '1.1.1-alpha.1', 'hash': '1234567890abcdef' },
{'path': 'dash.plotly.js', 'fingerprint': 'dash.v1m1.plotly.js'},
{'path': 'dash.plotly.j_s', 'fingerprint': 'dash.v1m1.plotly.j_s'},
{'path': 'dash.plotly.css', 'fingerprint': 'dash.v1m1.plotly.css'},
{'path': 'dash.plotly.xxx.yyy.zzz', 'fingerprint': 'dash.v1m1.plotly.xxx.yyy.zzz'},
{'path': 'dash~plotly.js', 'fingerprint': 'dash~plotly.v1m1.js'}
{"path": "[email protected]", "fingerprint": "[email protected]"},
{
"path": "[email protected]",
"fingerprint": "[email protected]_1_1m1234567890abcdef.8.6.min.js",
"version": "1.1.1",
"hash": "1234567890abcdef",
},
{
"path": "[email protected]",
"fingerprint": "[email protected]_1_1-alpha_1m1234567890abcdef.8.6.min.js",
"version": "1.1.1-alpha.1",
"hash": "1234567890abcdef",
},
{"path": "dash.plotly.js", "fingerprint": "dash.v1m1.plotly.js"},
{"path": "dash.plotly.j_s", "fingerprint": "dash.v1m1.plotly.j_s"},
{"path": "dash.plotly.css", "fingerprint": "dash.v1m1.plotly.css"},
{"path": "dash.plotly.xxx.yyy.zzz", "fingerprint": "dash.v1m1.plotly.xxx.yyy.zzz"},
{"path": "dash~plotly.js", "fingerprint": "dash~plotly.v1m1.js"},
{"path": "nested/folder/file.js", "fingerprint": "nested/folder/file.v1m1.js"},
{
# kind of pathological, but we have what looks like a version string
# in a different place - still works
"path": "nested.v2m2/folder/file.js",
"fingerprint": "nested.v2m2/folder/file.v1m1.js"
},
{
# even works if it gets doubled up in the right place
"path": "nested/folder/file.v2m2.js",
"fingerprint": "nested/folder/file.v1m1.v2m2.js"
},
{
"path": "nested.dotted/folder.structure/file.name.css",
"fingerprint": "nested.dotted/folder.structure/file.v1m1.name.css",
},
{
"path": "dash..plotly.js",
"fingerprint": "dash.v1_1_1m1234567890..plotly.js",
"version": "1.1.1",
"hash": "1234567890",
},
{
"path": "dash.",
"fingerprint": "dash.v1_1_1m1234567890.",
"version": "1.1.1",
"hash": "1234567890",
},
{
"path": "dash..",
"fingerprint": "dash.v1_1_1m1234567890..",
"version": "1.1.1",
"hash": "1234567890",
},
{
"path": "dash.js.",
"fingerprint": "dash.v1_1_1m1234567890.js.",
"version": "1.1.1",
"hash": "1234567890",
},
{
"path": "dash.j-s",
"fingerprint": "dash.v1_1_1m1234567890.j-s",
"version": "1.1.1",
"hash": "1234567890",
},
]

valid_fingerprints = [
'[email protected]_1_2m1571771240.8.6.min.js',
'dash.plotly.v1_1_1m1234567890.js',
'dash.plotly.v1_1_1m1234567890.j_s',
'dash.plotly.v1_1_1m1234567890.css',
'dash.plotly.v1_1_1m1234567890.xxx.yyy.zzz',
'dash.plotly.v1_1_1-alpha1m1234567890.js',
'dash.plotly.v1_1_1-alpha_3m1234567890.js',
'dash.plotly.v1_1_1m1234567890123.js',
'dash.plotly.v1_1_1m4bc3.js',
'dash~plotly.v1m1.js'
"[email protected]_1_2m1571771240.8.6.min.js",
"dash.v1_1_1m1234567890.plotly.js",
"dash.v1_1_1m1234567890.plotly.j_s",
"dash.v1_1_1m1234567890.plotly.css",
"dash.v1_1_1m1234567890.plotly.xxx.yyy.zzz",
"dash.v1_1_1-alpha1m1234567890.plotly.js",
"dash.v1_1_1-alpha_3m1234567890.plotly.js",
"dash.v1_1_1m1234567890123.plotly.js",
"dash.v1_1_1m4bc3.plotly.js",
"dash~plotly.v1m1.js",
"nested/folder/file.v1m1.js",
"nested.dotted/folder.structure/file.v1m1.name.css",
# this one has a pattern that looks like the version string in the wrong place
# AND one in the right place.
"nested.v2m2/folder/file.v1m1.js",
"nested.v2m2.dotted/folder.structure/file.v1m1.name.css",
]

invalid_fingerprints = [
'dash.plotly.v1_1_1m1234567890..js',
'dash.plotly.v1_1_1m1234567890.',
'dash.plotly.v1_1_1m1234567890..',
'dash.plotly.v1_1_1m1234567890.js.',
'dash.plotly.v1_1_1m1234567890.j-s'
"dash.plotly.v1_1_1m1234567890.js",
"folder/dash.plotly.v1_1_1m1234567890.js",
"nested.v1m1/folder/file.js",
"nested.v1m1.dotted/folder.structure/file.name.css",
]


def test_fingerprint():
for resource in valid_resources:
# The fingerprint matches expectations
fingerprint = build_fingerprint(resource.get('path'), resource.get('version', version), resource.get('hash', hash_value))
assert fingerprint == resource.get('fingerprint')
fingerprint = build_fingerprint(
resource.get("path"),
resource.get("version", version),
resource.get("hash", hash_value),
)
assert fingerprint == resource.get("fingerprint")

(original_path, has_fingerprint) = check_fingerprint(fingerprint)
# The inverse operation returns that the fingerprint was valid and the original path
# The inverse operation returns that the fingerprint was valid
# and the original path
assert has_fingerprint
assert original_path == resource.get('path')
assert original_path == resource.get("path")

for resource in valid_fingerprints:
(_, has_fingerprint) = check_fingerprint(resource)
assert has_fingerprint
assert has_fingerprint, resource

for resource in invalid_fingerprints:
(_, has_fingerprint) = check_fingerprint(resource)
assert not has_fingerprint
assert not has_fingerprint, resource

0 comments on commit 6150ec5

Please sign in to comment.