1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
|
import json
import warnings
import os
import typing as _t
import typing_extensions as _tx
from .development.base_component import ComponentRegistry
from . import exceptions
# ResourceType has `async` key, use the init form to be able to provide it.
ResourceType = _tx.TypedDict(
"ResourceType",
{
"namespace": str,
"async": _t.Union[bool, _t.Literal["eager", "lazy"]],
"dynamic": bool,
"relative_package_path": str,
"external_url": str,
"dev_package_path": str,
"absolute_path": str,
"asset_path": str,
"external_only": bool,
"filepath": str,
"dev_only": bool,
},
total=False,
)
# pylint: disable=too-few-public-methods
class ResourceConfig:
def __init__(self, serve_locally, eager_loading):
self.eager_loading = eager_loading
self.serve_locally = serve_locally
class Resources:
def __init__(self, resource_name: str, config: ResourceConfig):
self._resources: _t.List[ResourceType] = []
self.resource_name = resource_name
self.config = config
def append_resource(self, resource: ResourceType):
self._resources.append(resource)
# pylint: disable=too-many-branches
def _filter_resources(
self, all_resources: _t.List[ResourceType], dev_bundles=False
):
filtered_resources = []
for s in all_resources:
filtered_resource = {}
valid_resource = True
if "dynamic" in s:
filtered_resource["dynamic"] = s["dynamic"]
if "async" in s:
if "dynamic" in s:
raise exceptions.ResourceException(
f"""
Can't have both 'dynamic' and 'async'.
{json.dumps(filtered_resource)}
"""
)
# Async assigns a value dynamically to 'dynamic'
# based on the value of 'async' and config.eager_loading
#
# True -> dynamic if the server is not eager, False otherwise
# 'lazy' -> always dynamic
# 'eager' -> dynamic if server is not eager
# (to prevent ever loading it)
filtered_resource["dynamic"] = (
not self.config.eager_loading
if s["async"] is True
else (s["async"] == "eager" and not self.config.eager_loading)
or s["async"] == "lazy"
)
if "namespace" in s:
filtered_resource["namespace"] = s["namespace"]
if "external_url" in s and (
s.get("external_only") or not self.config.serve_locally
):
filtered_resource["external_url"] = s["external_url"]
elif "dev_package_path" in s and (dev_bundles or s.get("dev_only")):
if dev_bundles:
filtered_resource["relative_package_path"] = s["dev_package_path"]
else:
valid_resource = False
elif "relative_package_path" in s:
filtered_resource["relative_package_path"] = s["relative_package_path"]
elif "absolute_path" in s:
filtered_resource["absolute_path"] = s["absolute_path"]
elif "asset_path" in s:
info = os.stat(s["filepath"]) # type: ignore
filtered_resource["asset_path"] = s["asset_path"]
filtered_resource["ts"] = info.st_mtime
elif self.config.serve_locally:
warnings.warn(
(
"You have set your config to `serve_locally=True` but "
f"A local version of {s.get('external_url', '')} is not available.\n" # type: ignore
"If you added this file with "
"`app.scripts.append_script` "
"or `app.css.append_css`, use `external_scripts` "
"or `external_stylesheets` instead.\n"
"See https://dash.plotly.com/external-resources"
)
)
continue
else:
raise exceptions.ResourceException(
f"""
{json.dumps(filtered_resource)} does not have a
relative_package_path, absolute_path, or an external_url.
"""
)
if valid_resource:
filtered_resources.append(filtered_resource)
return filtered_resources
def get_all_resources(self, dev_bundles=False):
lib_resources = ComponentRegistry.get_resources(self.resource_name)
all_resources = lib_resources + self._resources
return self._filter_resources(all_resources, dev_bundles)
def get_library_resources(self, libraries, dev_bundles=False):
lib_resources = ComponentRegistry.get_resources(self.resource_name, libraries)
all_resources = lib_resources + self._resources
return self._filter_resources(all_resources, dev_bundles)
class Css:
def __init__(self, serve_locally: bool):
self.config = ResourceConfig(serve_locally, True)
self._resources = Resources("_css_dist", self.config)
def append_css(self, stylesheet: ResourceType):
self._resources.append_resource(stylesheet)
def get_all_css(self):
return self._resources.get_all_resources()
def get_library_css(self, libraries: _t.List[str]):
return self._resources.get_library_resources(libraries)
class Scripts:
def __init__(self, serve_locally, eager):
self.config = ResourceConfig(serve_locally, eager)
self._resources = Resources("_js_dist", self.config)
def append_script(self, script):
self._resources.append_resource(script)
def get_all_scripts(self, dev_bundles=False):
return self._resources.get_all_resources(dev_bundles)
def get_library_scripts(self, libraries, dev_bundles=False):
return self._resources.get_library_resources(libraries, dev_bundles)
|