aboutsummaryrefslogtreecommitdiff
path: root/venv/lib/python3.8/site-packages/dash/resources.py
blob: 8aad614e6d9ad5a9f7d54beb9eca3f13cc941885 (plain)
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)