Repository: plotly/dash

Issue:
Don't strip asset paths
Currently `routes_pathname` is stripped from `requests_pathname` for [asset paths only](https://github.com/plotly/dash/blob/2c182b14b070228efc9a69cfa96c57578e801e93/dash/_utils.py#L30-L43), causing these to be incorrect.

This was noted in https://github.com/plotly/streambed/issues/12111. We're working around this issue in DDS 3.0.0, but it would be good to fix the underlying bug.

@chriddyp @T4rk1n FYI


Discussion:

Resolving patch:
diff --git a/dash/_utils.py b/dash/_utils.py
--- a/dash/_utils.py
+++ b/dash/_utils.py
@@ -29,15 +29,12 @@ def generate_hash():
 
 def get_asset_path(
         requests_pathname,
-        routes_pathname,
         asset_path,
         asset_url_path):
-    i = requests_pathname.rfind(routes_pathname)
-    req = requests_pathname[:i]
 
     return '/'.join([
         # Only take the first part of the pathname
-        req,
+        requests_pathname.rstrip('/'),
         asset_url_path,
         asset_path
     ])
diff --git a/dash/dash.py b/dash/dash.py
--- a/dash/dash.py
+++ b/dash/dash.py
@@ -76,7 +76,7 @@ def __init__(
             name='__main__',
             server=None,
             static_folder='static',
-            assets_folder=None,
+            assets_folder='assets',
             assets_url_path='/assets',
             assets_ignore='',
             include_assets_files=True,
@@ -102,21 +102,15 @@ def __init__(
                 See https://github.com/plotly/dash/issues/141 for details.
                 ''', DeprecationWarning)
 
-        name = name if server is None else server.name
-        self._assets_folder = assets_folder or os.path.join(
-            flask.helpers.get_root_path(name), 'assets'
+        self._assets_folder = os.path.join(
+            flask.helpers.get_root_path(name),
+            assets_folder,
         )
         self._assets_url_path = assets_url_path
 
         # allow users to supply their own flask server
         self.server = server or Flask(name, static_folder=static_folder)
 
-        if 'assets' not in self.server.blueprints:
-            self.server.register_blueprint(
-                flask.Blueprint('assets', 'assets',
-                                static_folder=self._assets_folder,
-                                static_url_path=assets_url_path))
-
         env_configs = _configs.env_configs()
 
         url_base_pathname, routes_pathname_prefix, requests_pathname_prefix = \
@@ -146,6 +140,22 @@ def __init__(
                 env_configs, 2678400))
         })
 
+        assets_blueprint_name = '{}{}'.format(
+            self.config.routes_pathname_prefix.replace('/', '_'),
+            'dash_assets'
+        )
+
+        self.server.register_blueprint(
+            flask.Blueprint(
+                assets_blueprint_name, name,
+                static_folder=self._assets_folder,
+                static_url_path='{}{}'.format(
+                    self.config.routes_pathname_prefix,
+                    assets_url_path.lstrip('/')
+                )
+            )
+        )
+
         # list of dependencies
         self.callback_map = {}
 
@@ -1056,7 +1066,6 @@ def _serve_default_favicon(self):
     def get_asset_url(self, path):
         asset = _get_asset_path(
             self.config.requests_pathname_prefix,
-            self.config.routes_pathname_prefix,
             path,
             self._assets_url_path.lstrip('/')
         )


Test patch:
diff --git a/tests/test_configs.py b/tests/test_configs.py
--- a/tests/test_configs.py
+++ b/tests/test_configs.py
@@ -91,18 +91,16 @@ def test_pathname_prefix_environ_requests(self):
 
     def test_pathname_prefix_assets(self):
         req = '/'
-        routes = '/'
-        path = get_asset_path(req, routes, 'reset.css', 'assets')
+        path = get_asset_path(req, 'reset.css', 'assets')
         self.assertEqual('/assets/reset.css', path)
 
         req = '/requests/'
-        path = get_asset_path(req, routes, 'reset.css', 'assets')
+        path = get_asset_path(req, 'reset.css', 'assets')
         self.assertEqual('/requests/assets/reset.css', path)
 
         req = '/requests/routes/'
-        routes = '/routes/'
-        path = get_asset_path(req, routes, 'reset.css', 'assets')
-        self.assertEqual('/requests/assets/reset.css', path)
+        path = get_asset_path(req, 'reset.css', 'assets')
+        self.assertEqual('/requests/routes/assets/reset.css', path)
 
 
 if __name__ == '__main__':
diff --git a/tests/test_integration.py b/tests/test_integration.py
--- a/tests/test_integration.py
+++ b/tests/test_integration.py
@@ -361,7 +361,6 @@ def test_index_customization(self):
 
     def test_assets(self):
         app = dash.Dash(__name__,
-                        assets_folder='tests/assets',
                         assets_ignore='.*ignored.*')
         app.index_string = '''
         <!DOCTYPE html>