1818import base64
1919import traceback
2020from urllib .parse import urlparse
21- from typing import Any , Callable , Dict , Optional , Union , Sequence , Literal
21+ from typing import Any , Callable , Dict , Optional , Union , Sequence , Literal , List
2222
2323import flask
2424
@@ -262,6 +262,12 @@ class Dash(ObsoleteChecker):
262262 to sensitive files.
263263 :type assets_ignore: string
264264
265+ :param assets_path_ignore: A list of regex, each regex as a string to pass to ``re.compile``, for
266+ assets path to omit from immediate loading. The files in these ignored paths will still be
267+ served if specifically requested. You cannot use this to prevent access
268+ to sensitive files.
269+ :type assets_path_ignore: list of strings
270+
265271 :param assets_external_path: an absolute URL from which to load assets.
266272 Use with ``serve_locally=False``. assets_external_path is joined
267273 with assets_url_path to determine the absolute url to the
@@ -408,6 +414,7 @@ def __init__( # pylint: disable=too-many-statements
408414 use_pages : Optional [bool ] = None ,
409415 assets_url_path : str = "assets" ,
410416 assets_ignore : str = "" ,
417+ assets_path_ignore : List [str ] = None ,
411418 assets_external_path : Optional [str ] = None ,
412419 eager_loading : bool = False ,
413420 include_assets_files : bool = True ,
@@ -464,6 +471,7 @@ def __init__( # pylint: disable=too-many-statements
464471 ), # type: ignore
465472 assets_url_path = assets_url_path ,
466473 assets_ignore = assets_ignore ,
474+ assets_path_ignore = assets_path_ignore ,
467475 assets_external_path = get_combined_config (
468476 "assets_external_path" , assets_external_path , ""
469477 ),
@@ -764,7 +772,6 @@ def layout(self, value: Any):
764772 and not self .validation_layout
765773 and not self .config .suppress_callback_exceptions
766774 ):
767-
768775 layout_value = self ._layout_value ()
769776 _validate .validate_layout (value , layout_value )
770777 self .validation_layout = layout_value
@@ -1505,11 +1512,18 @@ def _walk_assets_directory(self):
15051512 walk_dir = self .config .assets_folder
15061513 slash_splitter = re .compile (r"[\\/]+" )
15071514 ignore_str = self .config .assets_ignore
1515+ ignore_path_list = self .config .assets_path_ignore
15081516 ignore_filter = re .compile (ignore_str ) if ignore_str else None
1517+ ignore_path_filters = [
1518+ re .compile (ignore_path )
1519+ for ignore_path in (ignore_path_list or [])
1520+ if ignore_path
1521+ ]
15091522
15101523 for current , _ , files in sorted (os .walk (walk_dir )):
15111524 if current == walk_dir :
15121525 base = ""
1526+ s = ""
15131527 else :
15141528 s = current .replace (walk_dir , "" ).lstrip ("\\ " ).lstrip ("/" )
15151529 splitted = slash_splitter .split (s )
@@ -1518,22 +1532,32 @@ def _walk_assets_directory(self):
15181532 else :
15191533 base = splitted [0 ]
15201534
1521- if ignore_filter :
1522- files_gen = (x for x in files if not ignore_filter .search (x ))
1535+ # Check if any level of current path matches ignore path
1536+ if s and any (
1537+ ignore_path_filter .search (x )
1538+ for ignore_path_filter in ignore_path_filters
1539+ for x in s .split (os .path .sep )
1540+ ):
1541+ pass
15231542 else :
1524- files_gen = files
1543+ if ignore_filter :
1544+ files_gen = (x for x in files if not ignore_filter .search (x ))
1545+ else :
1546+ files_gen = files
15251547
1526- for f in sorted (files_gen ):
1527- path = "/" .join ([base , f ]) if base else f
1548+ for f in sorted (files_gen ):
1549+ path = "/" .join ([base , f ]) if base else f
15281550
1529- full = os .path .join (current , f )
1551+ full = os .path .join (current , f )
15301552
1531- if f .endswith ("js" ):
1532- self .scripts .append_script (self ._add_assets_resource (path , full ))
1533- elif f .endswith ("css" ):
1534- self .css .append_css (self ._add_assets_resource (path , full )) # type: ignore[reportArgumentType]
1535- elif f == "favicon.ico" :
1536- self ._favicon = path
1553+ if f .endswith ("js" ):
1554+ self .scripts .append_script (
1555+ self ._add_assets_resource (path , full )
1556+ )
1557+ elif f .endswith ("css" ):
1558+ self .css .append_css (self ._add_assets_resource (path , full )) # type: ignore[reportArgumentType]
1559+ elif f == "favicon.ico" :
1560+ self ._favicon = path
15371561
15381562 @staticmethod
15391563 def _invalid_resources_handler (err ):
0 commit comments