Skip to content

Modules

A Flask adapter for the RouteLit framework, enabling seamless integration of RouteLit's reactive UI components with Flask web applications.

Source code in src/routelit_flask/adapter.py
 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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
class RouteLitFlaskAdapter:
    """
    A Flask adapter for the RouteLit framework, enabling seamless integration of RouteLit's reactive UI components with Flask web applications.
    """

    def __init__(
        self,
        routelit: RouteLit,
        *,
        static_path: Optional[str] = None,
        template_path: str = get_default_template_path(),
        run_mode: RunMode = "prod",
        local_frontend_server: Optional[str] = None,
        local_components_server: Optional[str] = None,
        cookie_config: Optional[dict[str, Any]] = None,
    ):
        """
        Initialize the RouteLitFlaskAdapter.
        - When run_mode="prod", no need to specify local_frontend_server and local_components_server.
        - When run_mode="dev_client", you need to specify local_frontend_server.
        - When run_mode="dev_components", you need to specify local_components_server.

        Args:
            routelit (RouteLit): The RouteLit instance.
            static_path (Optional[str]): The path to the static js/css assets are.
            template_path (str): The path to the index.html template file. Default is in routelit package, so no need to specify.
            run_mode (RunMode): The run mode. Example: "prod", "dev_client", "dev_components".
            local_frontend_server (Optional[str]): The local vite frontend server. Example: "http://localhost:5173".
            local_components_server (Optional[str]): The local vite components server. Example: "http://localhost:5174".
            cookie_config (Optional[dict[str, Any]]): The cookie configuration. Default is production cookie config.
        """
        self.routelit = routelit
        self.static_path = static_path or get_default_static_path()
        self.template_path = template_path
        self.run_mode = run_mode
        self.local_frontend_server = local_frontend_server
        self.local_components_server = local_components_server
        self.cookie_config = {**production_cookie_config, **(cookie_config or {})} if run_mode == "prod" else {}

    @classmethod
    def configure_static_assets(cls, flask_app: Flask, asset_target: AssetTarget) -> None:
        package_name, path = asset_target["package_name"], asset_target["path"]
        assets_path = resources.files(package_name).joinpath(path)
        flask_app.add_url_rule(
            f"/routelit/{package_name}/<path:filename>",
            endpoint=f"assets_static_{package_name}",
            view_func=lambda filename: send_from_directory(str(assets_path), filename),
        )

    def configure(
        self, flask_app: Flask, json_provider_class: Optional[Union[type[JSONProvider], Literal[False]]] = None
    ) -> "RouteLitFlaskAdapter":
        """
        Configure the Flask application to use the RouteLitFlaskAdapter.

        Args:
            flask_app: The Flask application to configure.
            json_provider_class: The JSON provider class to use. If None, use CustomJSONProvider. If False, do not set a custom JSON provider.

        Returns:
            The RouteLitFlaskAdapter instance.
        """
        # Set custom JSON encoder
        if json_provider_class is not False:
            flask_app.json_provider_class = json_provider_class or CustomJSONProvider

        for static_path in self.routelit.get_builder_class().get_client_resource_paths():
            self.configure_static_assets(flask_app, static_path)

        flask_app.add_url_rule(
            "/routelit/<path:filename>",
            endpoint="routelit_static",
            view_func=lambda filename: send_from_directory(self.static_path, filename),
        )

        # configure jinja templates for index.html
        current_loader = flask_app.jinja_loader
        if isinstance(current_loader, ChoiceLoader):
            # Append to the list of loaders
            current_loader.loaders.append(FileSystemLoader(self.template_path))  # type: ignore[attr-defined]
        else:
            # Wrap current loader and new one in a ChoiceLoader
            flask_app.jinja_loader = ChoiceLoader([current_loader, FileSystemLoader(self.template_path)])  # type: ignore[list-item]
        return self

    def _handle_get_request(self, view_fn: ViewFn, request: FlaskRLRequest, **kwargs: Any) -> Response:
        rl_response = self.routelit.handle_get_request(view_fn, request, **kwargs)
        response = make_response(
            render_template(
                "index.html",
                ROUTELIT_DATA=rl_response.get_str_json_elements(),
                PAGE_TITLE=rl_response.head.title,
                PAGE_DESCRIPTION=rl_response.head.description,
                RUN_MODE=self.run_mode,
                LOCAL_FRONTEND_SERVER=self.local_frontend_server,
                LOCAL_COMPONENTS_SERVER=self.local_components_server,
                default_vite_assets=self.routelit.default_client_assets(),
                importmap_json=self.routelit.get_importmap_json(),
                vite_assets=self.routelit.client_assets(),
                extra_head_content=self.routelit.get_extra_head_content(),
                extra_body_content=self.routelit.get_extra_body_content(),
            )
        )
        response.set_cookie(COOKIE_SESSION_KEY, request.get_session_id(), **self.cookie_config)
        return response

    def response(
        self,
        view_fn: ViewFn,
        inject_builder: Optional[bool] = None,
        *args: Any,
        **kwargs: Any,
    ) -> Response:
        """
        Handle a request and return a response.

        Args:
            view_fn (ViewFn): The view function to handle the request.
            inject_builder (Optional[bool]): Whether to inject the builder into the request.
            *args: Additional arguments to pass to the view function.
            **kwargs: Additional keyword arguments to pass to the view function.

        Returns:
            A Flask response.
        """
        req = FlaskRLRequest(request)
        if req.method == "POST":
            actions = self.routelit.handle_post_request(view_fn, req, inject_builder, *args, **kwargs)
            return jsonify(actions)
        else:
            return self._handle_get_request(view_fn, req, **kwargs)

    def stream_response(
        self,
        view_fn: ViewFn,
        inject_builder: Optional[bool] = None,
        *args: Any,
        **kwargs: Any,
    ) -> Response:
        """
        Handle a request and return a response.

        Args:
            view_fn (ViewFn): The view function to handle the request.
            inject_builder (Optional[bool]): Whether to inject the builder into the request.
            *args: Additional arguments to pass to the view function.
            **kwargs: Additional keyword arguments to pass to the view function.

        Returns:
            A Flask response.
        """
        req = FlaskRLRequest(request)
        if req.method == "POST":
            resp = Response(
                stream_with_context(
                    self.routelit.handle_post_request_stream_jsonl(view_fn, req, inject_builder, *args, **kwargs)
                ),
                mimetype="text/event-stream",
            )
            resp.headers["Content-Type"] = "application/jsonlines"
            return resp
        else:
            return self._handle_get_request(view_fn, req, **kwargs)

__init__(routelit, *, static_path=None, template_path=get_default_template_path(), run_mode='prod', local_frontend_server=None, local_components_server=None, cookie_config=None)

Initialize the RouteLitFlaskAdapter. - When run_mode="prod", no need to specify local_frontend_server and local_components_server. - When run_mode="dev_client", you need to specify local_frontend_server. - When run_mode="dev_components", you need to specify local_components_server.

Parameters:

Name Type Description Default
routelit RouteLit

The RouteLit instance.

required
static_path Optional[str]

The path to the static js/css assets are.

None
template_path str

The path to the index.html template file. Default is in routelit package, so no need to specify.

get_default_template_path()
run_mode RunMode

The run mode. Example: "prod", "dev_client", "dev_components".

'prod'
local_frontend_server Optional[str]

The local vite frontend server. Example: "http://localhost:5173".

None
local_components_server Optional[str]

The local vite components server. Example: "http://localhost:5174".

None
cookie_config Optional[dict[str, Any]]

The cookie configuration. Default is production cookie config.

None
Source code in src/routelit_flask/adapter.py
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
def __init__(
    self,
    routelit: RouteLit,
    *,
    static_path: Optional[str] = None,
    template_path: str = get_default_template_path(),
    run_mode: RunMode = "prod",
    local_frontend_server: Optional[str] = None,
    local_components_server: Optional[str] = None,
    cookie_config: Optional[dict[str, Any]] = None,
):
    """
    Initialize the RouteLitFlaskAdapter.
    - When run_mode="prod", no need to specify local_frontend_server and local_components_server.
    - When run_mode="dev_client", you need to specify local_frontend_server.
    - When run_mode="dev_components", you need to specify local_components_server.

    Args:
        routelit (RouteLit): The RouteLit instance.
        static_path (Optional[str]): The path to the static js/css assets are.
        template_path (str): The path to the index.html template file. Default is in routelit package, so no need to specify.
        run_mode (RunMode): The run mode. Example: "prod", "dev_client", "dev_components".
        local_frontend_server (Optional[str]): The local vite frontend server. Example: "http://localhost:5173".
        local_components_server (Optional[str]): The local vite components server. Example: "http://localhost:5174".
        cookie_config (Optional[dict[str, Any]]): The cookie configuration. Default is production cookie config.
    """
    self.routelit = routelit
    self.static_path = static_path or get_default_static_path()
    self.template_path = template_path
    self.run_mode = run_mode
    self.local_frontend_server = local_frontend_server
    self.local_components_server = local_components_server
    self.cookie_config = {**production_cookie_config, **(cookie_config or {})} if run_mode == "prod" else {}

configure(flask_app, json_provider_class=None)

Configure the Flask application to use the RouteLitFlaskAdapter.

Parameters:

Name Type Description Default
flask_app Flask

The Flask application to configure.

required
json_provider_class Optional[Union[type[JSONProvider], Literal[False]]]

The JSON provider class to use. If None, use CustomJSONProvider. If False, do not set a custom JSON provider.

None

Returns:

Type Description
RouteLitFlaskAdapter

The RouteLitFlaskAdapter instance.

Source code in src/routelit_flask/adapter.py
 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
def configure(
    self, flask_app: Flask, json_provider_class: Optional[Union[type[JSONProvider], Literal[False]]] = None
) -> "RouteLitFlaskAdapter":
    """
    Configure the Flask application to use the RouteLitFlaskAdapter.

    Args:
        flask_app: The Flask application to configure.
        json_provider_class: The JSON provider class to use. If None, use CustomJSONProvider. If False, do not set a custom JSON provider.

    Returns:
        The RouteLitFlaskAdapter instance.
    """
    # Set custom JSON encoder
    if json_provider_class is not False:
        flask_app.json_provider_class = json_provider_class or CustomJSONProvider

    for static_path in self.routelit.get_builder_class().get_client_resource_paths():
        self.configure_static_assets(flask_app, static_path)

    flask_app.add_url_rule(
        "/routelit/<path:filename>",
        endpoint="routelit_static",
        view_func=lambda filename: send_from_directory(self.static_path, filename),
    )

    # configure jinja templates for index.html
    current_loader = flask_app.jinja_loader
    if isinstance(current_loader, ChoiceLoader):
        # Append to the list of loaders
        current_loader.loaders.append(FileSystemLoader(self.template_path))  # type: ignore[attr-defined]
    else:
        # Wrap current loader and new one in a ChoiceLoader
        flask_app.jinja_loader = ChoiceLoader([current_loader, FileSystemLoader(self.template_path)])  # type: ignore[list-item]
    return self

response(view_fn, inject_builder=None, *args, **kwargs)

Handle a request and return a response.

Parameters:

Name Type Description Default
view_fn ViewFn

The view function to handle the request.

required
inject_builder Optional[bool]

Whether to inject the builder into the request.

None
*args Any

Additional arguments to pass to the view function.

()
**kwargs Any

Additional keyword arguments to pass to the view function.

{}

Returns:

Type Description
Response

A Flask response.

Source code in src/routelit_flask/adapter.py
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
def response(
    self,
    view_fn: ViewFn,
    inject_builder: Optional[bool] = None,
    *args: Any,
    **kwargs: Any,
) -> Response:
    """
    Handle a request and return a response.

    Args:
        view_fn (ViewFn): The view function to handle the request.
        inject_builder (Optional[bool]): Whether to inject the builder into the request.
        *args: Additional arguments to pass to the view function.
        **kwargs: Additional keyword arguments to pass to the view function.

    Returns:
        A Flask response.
    """
    req = FlaskRLRequest(request)
    if req.method == "POST":
        actions = self.routelit.handle_post_request(view_fn, req, inject_builder, *args, **kwargs)
        return jsonify(actions)
    else:
        return self._handle_get_request(view_fn, req, **kwargs)

stream_response(view_fn, inject_builder=None, *args, **kwargs)

Handle a request and return a response.

Parameters:

Name Type Description Default
view_fn ViewFn

The view function to handle the request.

required
inject_builder Optional[bool]

Whether to inject the builder into the request.

None
*args Any

Additional arguments to pass to the view function.

()
**kwargs Any

Additional keyword arguments to pass to the view function.

{}

Returns:

Type Description
Response

A Flask response.

Source code in src/routelit_flask/adapter.py
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
def stream_response(
    self,
    view_fn: ViewFn,
    inject_builder: Optional[bool] = None,
    *args: Any,
    **kwargs: Any,
) -> Response:
    """
    Handle a request and return a response.

    Args:
        view_fn (ViewFn): The view function to handle the request.
        inject_builder (Optional[bool]): Whether to inject the builder into the request.
        *args: Additional arguments to pass to the view function.
        **kwargs: Additional keyword arguments to pass to the view function.

    Returns:
        A Flask response.
    """
    req = FlaskRLRequest(request)
    if req.method == "POST":
        resp = Response(
            stream_with_context(
                self.routelit.handle_post_request_stream_jsonl(view_fn, req, inject_builder, *args, **kwargs)
            ),
            mimetype="text/event-stream",
        )
        resp.headers["Content-Type"] = "application/jsonlines"
        return resp
    else:
        return self._handle_get_request(view_fn, req, **kwargs)

The run mode for the RouteLitFlaskAdapter.

  • prod: Production mode.
  • dev_client: Development mode for the client.
  • dev_components: Development mode for the components.

Bases: RouteLitRequest

Implements the RouteLitRequest interface for Flask.

Source code in src/routelit_flask/request.py
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
class FlaskRLRequest(RouteLitRequest):
    """
    Implements the RouteLitRequest interface for Flask.
    """

    def __init__(self, request: Request):
        self.request = request
        super().__init__()
        self.__default_session_id = str(uuid.uuid4())

    def get_headers(self) -> dict[str, str]:
        return self.request.headers  # type: ignore[return-value]

    def get_path_params(self) -> Optional[Mapping[str, Any]]:
        return self.request.view_args

    def get_referrer(self) -> Optional[str]:
        return self.request.referrer or self.request.headers.get("Referer")

    @property
    def method(self) -> str:
        return self.request.method

    def get_json(self) -> Optional[Any]:
        if self.is_json():
            return self.request.json
        if self.is_multipart():
            json_str = self.request.form.get("json", "{}")
            return json.loads(json_str)
        return None

    def get_files(self) -> Optional[list[IOBase]]:
        if self.is_multipart():
            return cast(list[IOBase], self.request.files.getlist("files"))
        return None

    def is_json(self) -> bool:
        return self.request.is_json

    def is_multipart(self) -> bool:
        content_type = self.request.content_type or ""
        return content_type.startswith("multipart/form-data")

    def get_query_param(self, key: str) -> Optional[str]:
        return self.request.args.get(key)

    def get_query_param_list(self, key: str) -> list[str]:
        return self.request.args.getlist(key)

    def get_session_id(self) -> str:
        return self.request.cookies.get(COOKIE_SESSION_KEY, self.__default_session_id)

    def get_pathname(self) -> str:
        return self.request.path

    def get_host(self) -> str:
        return self.request.host