bisslog-flask 0.0.1__py3-none-any.whl → 0.0.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
File without changes
@@ -0,0 +1,371 @@
1
+ """
2
+ Module for generating a Flask application boilerplate from Bisslog metadata and use case code.
3
+
4
+ This builder analyzes declared metadata (e.g., triggers) and discovered use case implementations,
5
+ and generates the corresponding Flask code—including HTTP routes, WebSocket endpoints, security
6
+ configuration, and runtime setup.
7
+
8
+ The generated code is returned as a full Python script and can be written to a file (e.g.,
9
+ `flask_app.py`).
10
+ """
11
+
12
+ from typing import Optional, Callable
13
+ import json
14
+
15
+ from bisslog_schema import read_full_service_metadata
16
+ from bisslog_schema.eager_import_module_or_package import EagerImportModulePackage
17
+ from bisslog_schema.schema import UseCaseInfo, TriggerHttp, TriggerWebsocket
18
+ from bisslog_schema.setup import get_setup_metadata
19
+ from bisslog_schema.use_case_code_inspector.use_case_code_metadata import UseCaseCodeInfo, \
20
+ UseCaseCodeInfoClass, UseCaseCodeInfoObject
21
+
22
+ from .static_python_construct_data import StaticPythonConstructData
23
+
24
+
25
+ class BuilderFlaskAppManager:
26
+ """
27
+ Flask application builder for Bisslog-based services.
28
+
29
+ This class dynamically generates Flask code based on user-declared metadata and
30
+ the implementation of use cases discovered in the source tree. It supports HTTP
31
+ and WebSocket triggers, integrates runtime setup from decorators, and configures
32
+ environment-based security.
33
+
34
+ The result is a complete Flask application scaffold that can be directly executed
35
+ or used as a starting point for further customization.
36
+ """
37
+
38
+ def __init__(self, eager_importer: Callable[[str], None]):
39
+ self._eager_importer = eager_importer
40
+
41
+
42
+ def _get_bisslog_setup(self, infra_path: Optional[str]) -> Optional[StaticPythonConstructData]:
43
+ """
44
+ Retrieves the Bisslog setup call for the 'flask' runtime, if defined.
45
+
46
+ This inspects the global Bisslog configuration and returns the corresponding
47
+ setup function call code for Flask.
48
+
49
+ Returns
50
+ -------
51
+ Optional[StaticPythonConstructData]
52
+ The setup code and imports, or None if no setup was declared.
53
+ """
54
+ self._eager_importer(infra_path)
55
+ setup_metadata = get_setup_metadata()
56
+ if setup_metadata is None:
57
+ return None
58
+
59
+ if setup_metadata.setup_function is not None:
60
+ n_params = setup_metadata.setup_function.n_params
61
+ if n_params == 0:
62
+ build = f"{setup_metadata.setup_function.function_name}()"
63
+ elif n_params == 1:
64
+ build = f"{setup_metadata.setup_function.function_name}(\"flask\")"
65
+ else:
66
+ build = (f"{setup_metadata.setup_function.function_name}(\"flask\")"
67
+ " # TODO: change this")
68
+ return StaticPythonConstructData(
69
+ importing={setup_metadata.setup_function.module:
70
+ {setup_metadata.setup_function.function_name}},
71
+ build=build,
72
+ )
73
+ custom_runtime_setup = setup_metadata.runtime.get("flask", None)
74
+ if custom_runtime_setup is not None:
75
+ return StaticPythonConstructData(
76
+ importing={custom_runtime_setup.module:
77
+ {custom_runtime_setup.function_name}},
78
+ build=f"{custom_runtime_setup.function_name}()"
79
+ )
80
+ return None
81
+
82
+ @staticmethod
83
+ def _generate_security_code() -> StaticPythonConstructData:
84
+ """
85
+ Generates Flask configuration code for secret keys using environment variables.
86
+
87
+ Returns
88
+ -------
89
+ StaticPythonConstructData
90
+ Code that assigns `SECRET_KEY` and `JWT_SECRET_KEY` to the Flask app.
91
+ """
92
+ build = """
93
+ if "SECRET_KEY" in os.environ:
94
+ app.config["SECRET_KEY"] = os.environ["SECRET_KEY"]
95
+ if "JWT_SECRET_KEY" in os.environ:
96
+ app.config["JWT_SECRET_KEY"] = os.environ["JWT_SECRET_KEY"]
97
+ """
98
+ return StaticPythonConstructData(build=build)
99
+
100
+ @staticmethod
101
+ def _generate_use_case_code_build(use_case_code_info: UseCaseCodeInfo):
102
+ """
103
+ Prepares the use case callable to be used in HTTP or WebSocket routes.
104
+
105
+ If the use case is a class, an instance is created. If it's an object, it's referenced.
106
+
107
+ Parameters
108
+ ----------
109
+ use_case_code_info : UseCaseCodeInfo
110
+ Static metadata about the use case implementation.
111
+
112
+ Returns
113
+ -------
114
+ Tuple[str, StaticPythonConstructData]
115
+ - Name of the callable reference (e.g., variable or instance).
116
+ - Generated setup code and required imports.
117
+ """
118
+ importing = {"flask": {"request"}, "bisslog.utils.mapping": {"Mapper"}}
119
+ starting_build = ""
120
+ if isinstance(use_case_code_info, UseCaseCodeInfoClass):
121
+ importing[use_case_code_info.module] = {use_case_code_info.class_name}
122
+ uc_callable = f"{use_case_code_info.name}_uc"
123
+ starting_build += f"{uc_callable} = {use_case_code_info.class_name}()"
124
+ elif isinstance(use_case_code_info, UseCaseCodeInfoObject):
125
+ importing[use_case_code_info.module] = {use_case_code_info.var_name}
126
+ uc_callable = use_case_code_info.var_name
127
+ else:
128
+ raise ValueError("Unsupported UseCaseCodeInfo type")
129
+ return uc_callable, StaticPythonConstructData(build=starting_build, importing=importing)
130
+
131
+ @staticmethod
132
+ def _generate_use_case_code_http_trigger(
133
+ use_case_key: str, uc_callable: str, use_case_code_info: UseCaseCodeInfo,
134
+ trigger_info: TriggerHttp, identifier: int) -> StaticPythonConstructData:
135
+ """
136
+ Generates the code for a use case with an HTTP trigger.
137
+
138
+ Parameters
139
+ ----------
140
+ use_case_key : str
141
+ Name used to identify the use case route.
142
+ use_case_code_info : UseCaseCodeInfo
143
+ Static code metadata for the specific use case.
144
+ trigger_info : TriggerHttp
145
+ Metadata of the HTTP trigger.
146
+
147
+ Returns
148
+ -------
149
+ StaticPythonConstructData
150
+ The generated code for the HTTP trigger.
151
+ """
152
+ imports = {
153
+ "flask": {"jsonify"}
154
+ }
155
+ starting_build = ""
156
+ mapper_code_lines = []
157
+ if trigger_info.mapper is not None:
158
+ mapper_name = f"{use_case_code_info.name}_mapper_{identifier}"
159
+ starting_build += (f"\n{mapper_name} = Mapper(name=\"{use_case_key}_mapper\", "
160
+ f"base={json.dumps(trigger_info.mapper)})")
161
+ mapper_code_lines.append(f"""
162
+ res_map = {mapper_name}.map({{
163
+ "path_query": route_vars,
164
+ "body": request.get_json(silent=True) or {{}},
165
+ "params": request.args.to_dict(),
166
+ "headers": request.headers,
167
+ }})""")
168
+ method = trigger_info.method.upper()
169
+ flask_path = (trigger_info.path or f"/{use_case_key}").replace("{", "<").replace("}", ">")
170
+ handler_name = f"{use_case_key}_handler_{identifier}"
171
+
172
+ lines = [
173
+ f'@app.route("{flask_path}", methods=["{method}"])',
174
+ ]
175
+
176
+ if use_case_code_info.is_coroutine:
177
+ lines.append(f"async def {handler_name}(**route_vars):")
178
+ else:
179
+ lines.append(f"def {handler_name}(**route_vars):")
180
+
181
+ if not mapper_code_lines:
182
+ lines.append(" kwargs = {}")
183
+ lines.append(" kwargs.update(route_vars)")
184
+ lines.append(" kwargs.update(request.get_json(silent=True) or {})")
185
+ lines.append(" kwargs.update(request.args.to_dict())")
186
+ lines.append(" kwargs.update(dict(request.headers))")
187
+ var_to_unpack = "kwargs"
188
+ else:
189
+ lines.extend(mapper_code_lines)
190
+ var_to_unpack = "res_map"
191
+
192
+ if use_case_code_info.is_coroutine:
193
+ lines.append(f' result = await {uc_callable}(**{var_to_unpack})')
194
+ else:
195
+ lines.append(f' result = {uc_callable}(**{var_to_unpack})\n')
196
+
197
+ lines.append(' return jsonify(result)\n')
198
+
199
+ return StaticPythonConstructData(build=starting_build,
200
+ body="\n".join(lines), importing=imports)
201
+
202
+ @staticmethod
203
+ def _generate_use_case_code_websocket_trigger(
204
+ use_case_key: str,
205
+ uc_callable: str,
206
+ use_case_code_info: UseCaseCodeInfo,
207
+ trigger_info: TriggerWebsocket,
208
+ identifier: int
209
+ ) -> StaticPythonConstructData:
210
+ """
211
+ Generates the code for a use case with a WebSocket trigger using flask-sock.
212
+
213
+ Parameters
214
+ ----------
215
+ use_case_key : str
216
+ The identifier of the use case.
217
+ uc_callable : str
218
+ The callable name to invoke.
219
+ use_case_code_info : UseCaseCodeInfo
220
+ Info about where the use case is defined.
221
+ trigger_info : TriggerWebsocket
222
+ Metadata describing the trigger.
223
+ identifier : int
224
+ An integer used to ensure uniqueness of function names.
225
+
226
+ Returns
227
+ -------
228
+ StaticPythonConstructData
229
+ Code and imports needed for WebSocket registration.
230
+ """
231
+ route_key = trigger_info.route_key or f"{use_case_key}.default"
232
+ handler_name = f"{use_case_key}_ws_handler_{identifier}"
233
+ mapper_decl = ""
234
+
235
+ imports = {
236
+ use_case_code_info.module: {use_case_code_info.name},
237
+ "flask_sock": {"Sock"},
238
+ "flask": {"request", "jsonify"},
239
+ "bisslog.utils.mapping": {"Mapper"},
240
+ "json": None
241
+ }
242
+
243
+ if trigger_info.mapper:
244
+ mapper_var = f"{use_case_key}_ws_mapper_{identifier}"
245
+ mapper_json = json.dumps(trigger_info.mapper)
246
+ mapper_decl = (f'\n{mapper_var} = Mapper(name="{use_case_key}_ws_mapper",'
247
+ f' base={mapper_json})')
248
+
249
+ mapper_code = f"""
250
+ try:
251
+ body = json.loads(data)
252
+ except Exception:
253
+ body = {{}}
254
+ res_map = {mapper_var}.map({{
255
+ "route_key": "{route_key}",
256
+ "connection_id": request.headers.get("Sec-WebSocket-Key"),
257
+ "headers": request.headers,
258
+ "body": body
259
+ }})
260
+ response = {uc_callable}(**res_map)
261
+ """
262
+
263
+ else:
264
+ # fallback: pass entire raw message
265
+ mapper_code = f"""
266
+ try:
267
+ payload = json.loads(data)
268
+ except Exception:
269
+ payload = {{}}
270
+ response = {uc_callable}(**payload)
271
+ """
272
+
273
+ build = f"""
274
+ @sock.route("/ws/{route_key}")
275
+ def {handler_name}(ws):
276
+ while True:
277
+ data = ws.receive()
278
+ if data is None:
279
+ break; # Client disconnected
280
+ {mapper_code}
281
+ if response is not None:
282
+ ws.send(response)
283
+ """
284
+
285
+ return StaticPythonConstructData(
286
+ importing=imports,
287
+ build=(mapper_decl + build)
288
+ )
289
+
290
+ def __call__(self,
291
+ metadata_file: Optional[str] = None,
292
+ use_cases_folder_path: Optional[str] = None,
293
+ infra_path: Optional[str] = None,
294
+ *,
295
+ encoding: str = "utf-8",
296
+ secret_key: Optional[str] = None,
297
+ jwt_secret_key: Optional[str] = None,
298
+ **kwargs) -> str:
299
+ """
300
+ Main entry point for generating the full Flask application code.
301
+
302
+ This method orchestrates metadata loading, trigger processing, and Flask code generation
303
+ (HTTP routes, WebSocket handlers, runtime setup, security config). The resulting app code
304
+ is returned as a ready-to-write Python string.
305
+
306
+ Parameters
307
+ ----------
308
+ metadata_file : str, optional
309
+ Path to the YAML or JSON metadata file.
310
+ use_cases_folder_path : str, optional
311
+ Path to the folder where use case implementations are located.
312
+ infra_path : str, optional
313
+ Path to additional infrastructure or adapter code.
314
+ encoding : str, default="utf-8"
315
+ Encoding used to read the metadata file.
316
+ secret_key : str, optional
317
+ secret key for Flask configuration.
318
+ jwt_secret_key : str, optional
319
+ JWT secret key for Flask configuration.
320
+ **kwargs
321
+ Additional keyword arguments (currently unused).
322
+
323
+ Returns
324
+ -------
325
+ str
326
+ The complete Flask application source code as a string.
327
+ """
328
+ full_service_metadata = read_full_service_metadata(
329
+ metadata_file=metadata_file,
330
+ use_cases_folder_path=use_cases_folder_path,
331
+ encoding=encoding
332
+ )
333
+ service_info = full_service_metadata.declared_metadata
334
+ use_cases = full_service_metadata.discovered_use_cases
335
+
336
+ res = StaticPythonConstructData(
337
+ importing={"flask": {"Flask"}, "os": None},
338
+ build="app = Flask(__name__)"
339
+ )
340
+ res += self._get_bisslog_setup(infra_path)
341
+
342
+ res += self._generate_security_code()
343
+
344
+ # Use cases
345
+ for use_case_key in service_info.use_cases:
346
+ use_case_info: UseCaseInfo = service_info.use_cases[use_case_key]
347
+ use_case_code_info: UseCaseCodeInfo = use_cases[use_case_key]
348
+ triggers_http = [t for t in use_case_info.triggers
349
+ if isinstance(t.options, TriggerHttp)]
350
+ triggers_ws = [t for t in use_case_info.triggers
351
+ if isinstance(t.options, TriggerWebsocket)]
352
+ triggers_flask = triggers_http + triggers_ws
353
+ if len(triggers_flask) == 0:
354
+ continue
355
+ uc_callable, res_uc = self._generate_use_case_code_build(use_case_code_info)
356
+ res += res_uc
357
+ for i, trigger in enumerate(triggers_flask):
358
+ if isinstance(trigger.options, TriggerHttp):
359
+ res += self._generate_use_case_code_http_trigger(
360
+ use_case_key, uc_callable, use_case_code_info, trigger.options, i
361
+ )
362
+ elif isinstance(trigger.options, TriggerWebsocket):
363
+ res += self._generate_use_case_code_websocket_trigger(
364
+ use_case_key, uc_callable, use_case_code_info, trigger.options, i
365
+ )
366
+ res += StaticPythonConstructData(body='\nif __name__ == "__main__":\n'
367
+ ' app.run(debug=True, host="0.0.0.0")')
368
+ return res.generate_boiler_plate_flask()
369
+
370
+
371
+ bisslog_flask_builder = BuilderFlaskAppManager(EagerImportModulePackage(("src.infra", "infra")))
@@ -0,0 +1,172 @@
1
+ """
2
+ Module defining the response structure for AWS Lambda handler code generation.
3
+
4
+ This module provides a dataclass that encapsulates the components produced during
5
+ AWS Lambda handler code generation, including the function body, optional setup code,
6
+ and required import statements.
7
+ """
8
+
9
+ from dataclasses import dataclass, field
10
+ from typing import Optional, Dict, Any, Set
11
+
12
+
13
+
14
+ @dataclass
15
+ class StaticPythonConstructData:
16
+ """
17
+ Response structure for generated AWS Lambda handler components.
18
+
19
+ This dataclass represents the output of a code generator that produces AWS Lambda
20
+ handler functions. It contains the function body as a string, any optional setup/build
21
+ logic, and the necessary import statements grouped by module.
22
+
23
+ Attributes
24
+ ----------
25
+ body : Optional[str]
26
+ The body of the Lambda handler function as a Python code string.
27
+ build : Optional[str]
28
+ Optional setup or preconstruction code to include before the function body.
29
+ importing : Dict[str, Set[str]]
30
+ A mapping of module names to sets of symbols to import from them.
31
+
32
+ This structure assumes simple `from module import symbol` semantics.
33
+
34
+ Examples
35
+ --------
36
+ {
37
+ "typing": {"List", "Optional"},
38
+ "os": {"path", "environ"}
39
+ }
40
+
41
+ extra : Dict[str, Any]
42
+ Optional extra data provided by the generator.
43
+ """
44
+ body: Optional[str] = None
45
+ build: Optional[str] = None
46
+ importing: Dict[str, Set[str]] = field(default_factory=dict)
47
+ extra: Dict[str, Any] = field(default_factory=dict)
48
+
49
+ def add_imports(self, new_imports: Dict[str, Set[str]]) -> None:
50
+ """
51
+ Adds new import statements to the existing import dictionary.
52
+
53
+ Parameters
54
+ ----------
55
+ new_imports : Dict[str, Set[str]]
56
+ A dictionary of module names and symbols to import.
57
+ """
58
+ for module, symbols in new_imports.items():
59
+ if module not in self.importing:
60
+ self.importing[module] = set()
61
+ self.importing[module].update(symbols)
62
+
63
+ @staticmethod
64
+ def _generate_imports_string(imports: Dict[str, Set[str]]) -> str:
65
+ """
66
+ Generates a string representing import statements.
67
+
68
+ Parameters
69
+ ----------
70
+ imports : Dict[str, Set[str]]
71
+ A dictionary where keys are module names and values are sets of symbols.
72
+
73
+ Returns
74
+ -------
75
+ str
76
+ Formatted import statements, one per line.
77
+ """
78
+ return "\n".join(
79
+ f"from {source} import {', '.join(sorted(var))}" if var else f"import {source}"
80
+ for source, var in imports.items()
81
+ )
82
+
83
+ def generate_boiler_plate_flask(self) -> str:
84
+ """
85
+ Builds the final AWS Lambda handler code as a complete Python string.
86
+
87
+ This includes import statements, any pre-construction logic, and
88
+ the `lambda_handler` function definition.
89
+
90
+ Returns
91
+ -------
92
+ str
93
+ The full source code of the handler as a string.
94
+ """
95
+ imports_chunk = self._generate_imports_string(self.importing)
96
+ sep = "\n" * 3
97
+ return f"{imports_chunk}{sep}{self.build or ''}{sep}{self.body or ''}\n"
98
+
99
+ def __add__(self, other: "StaticPythonConstructData") -> "StaticPythonConstructData":
100
+ """
101
+ Combine two AWSHandlerGenResponse objects by merging their fields.
102
+
103
+ Parameters
104
+ ----------
105
+ other : StaticPythonConstructData
106
+ Another response to merge.
107
+
108
+ Returns
109
+ -------
110
+ StaticPythonConstructData
111
+ A new instance with merged content.
112
+
113
+ Raises
114
+ ------
115
+ NotImplementedError
116
+ If `other` is not an instance of AWSHandlerGenResponse.
117
+ """
118
+ if not isinstance(other, StaticPythonConstructData):
119
+ raise NotImplementedError
120
+
121
+ merged_body = "\n".join(filter(None, [self.body, other.body])) or None
122
+ merged_build = "\n".join(filter(None, [self.build, other.build])) or None
123
+
124
+ merged_importing: Dict[str, Set[str]] = {}
125
+ for module in set(self.importing) | set(other.importing):
126
+ symbols_self = self.importing.get(module, set())
127
+ symbols_other = other.importing.get(module, set())
128
+ merged_importing[module] = symbols_self.union(symbols_other)
129
+
130
+ return StaticPythonConstructData(
131
+ body=merged_body,
132
+ build=merged_build,
133
+ importing=merged_importing,
134
+ extra={}
135
+ )
136
+
137
+ def __iadd__(self, other: "StaticPythonConstructData") -> "StaticPythonConstructData":
138
+ """
139
+ In-place addition of another AWSHandlerGenResponse instance.
140
+
141
+ Modifies the current instance by merging the fields from `other`.
142
+
143
+ Parameters
144
+ ----------
145
+ other : StaticPythonConstructData
146
+ The response to merge into this one.
147
+
148
+ Returns
149
+ -------
150
+ StaticPythonConstructData
151
+ The modified instance (`self`).
152
+
153
+ Raises
154
+ ------
155
+ NotImplementedError
156
+ If `other` is not an instance of AWSHandlerGenResponse.
157
+ """
158
+ if other is None:
159
+ return self
160
+ if not isinstance(other, StaticPythonConstructData):
161
+ raise NotImplementedError
162
+
163
+ self.body = "\n".join(filter(None, [self.body, other.body])) or None
164
+ self.build = "\n".join(filter(None, [self.build, other.build])) or None
165
+
166
+ for module, symbols in other.importing.items():
167
+ if module not in self.importing:
168
+ self.importing[module] = set()
169
+ self.importing[module].update(symbols)
170
+
171
+ self.extra = {}
172
+ return self
@@ -0,0 +1,90 @@
1
+ """Command-line interface for the `bisslog_flask` package."""
2
+ import argparse
3
+ import os
4
+ import sys
5
+ import traceback
6
+
7
+ from .commands.build import build_boiler_plate_flask
8
+ from .commands.run import run
9
+
10
+
11
+ def main():
12
+ """
13
+ Entry point for the `bisslog_flask` command-line interface.
14
+
15
+ This function parses command-line arguments and executes the corresponding
16
+ subcommand. Currently, it supports the `run` and `build` commands.
17
+
18
+ Commands
19
+ --------
20
+ run
21
+ Starts a Flask application based on provided metadata and use case folder.
22
+
23
+ build
24
+ Generates a Flask boilerplate app file from metadata and use cases.
25
+
26
+ Raises
27
+ ------
28
+ Exception
29
+ Any exception raised during command execution is caught and printed to stderr.
30
+ """
31
+ project_root = os.getcwd()
32
+
33
+ if project_root not in sys.path:
34
+ sys.path.insert(0, project_root)
35
+
36
+ parser = argparse.ArgumentParser(prog="bisslog_flask")
37
+ subparsers = parser.add_subparsers(dest="command", required=True)
38
+
39
+ # Define `run` command
40
+ run_parser = subparsers.add_parser("run", help="Run a Flask app using metadata.")
41
+ run_parser.add_argument("--metadata-file", type=str, default=None,
42
+ help="Path to metadata file (YAML or JSON).")
43
+ run_parser.add_argument("--use-cases-folder-path", type=str, default=None,
44
+ help="Path to use case source folder.")
45
+ run_parser.add_argument("--infra-path", type=str, default=None,
46
+ help="Path to infrastructure code folder (optional).")
47
+ run_parser.add_argument("--encoding", type=str, default="utf-8",
48
+ help="File encoding (default: utf-8).")
49
+ run_parser.add_argument("--secret-key", type=str,
50
+ help="Flask SECRET_KEY config value.")
51
+ run_parser.add_argument("--jwt-secret-key", type=str,
52
+ help="Flask JWT_SECRET_KEY config value.")
53
+
54
+ # Define `build` command
55
+ build_parser = subparsers.add_parser("build",
56
+ help="Generate a Flask app boilerplate file.")
57
+ build_parser.add_argument("--metadata-file", type=str, default=None,
58
+ help="Path to metadata file (YAML or JSON).")
59
+ build_parser.add_argument("--use-cases-folder-path", type=str, default=None,
60
+ help="Path to use case source folder.")
61
+ build_parser.add_argument("--infra-path", type=str, default=None,
62
+ help="Path to infrastructure code folder (optional).")
63
+ build_parser.add_argument("--encoding", type=str, default="utf-8",
64
+ help="File encoding (default: utf-8).")
65
+ build_parser.add_argument("--target-filename", type=str, default="flask_app.py",
66
+ help="Filename to write the generated "
67
+ "boilerplate (default: flask_app.py)")
68
+
69
+ args = parser.parse_args()
70
+
71
+ try:
72
+ if args.command == "run":
73
+ run(metadata_file=args.metadata_file,
74
+ use_cases_folder_path=args.use_cases_folder_path,
75
+ infra_path=args.infra_path,
76
+ encoding=args.encoding,
77
+ secret_key=args.secret_key,
78
+ jwt_secret_key=args.jwt_secret_key)
79
+ elif args.command == "build":
80
+ build_boiler_plate_flask(
81
+ metadata_file=args.metadata_file,
82
+ use_cases_folder_path=args.use_cases_folder_path,
83
+ infra_path=args.infra_path,
84
+ encoding=args.encoding,
85
+ target_filename=args.target_filename
86
+ )
87
+ except Exception as e: # pylint: disable=broad-except
88
+ traceback.print_exc()
89
+ print(e)
90
+ sys.exit(1)
File without changes
@@ -0,0 +1,57 @@
1
+ """
2
+ CLI-compatible utility for generating a Flask application boilerplate from Bisslog metadata.
3
+
4
+ This module defines a function that reads service metadata and discovered use cases,
5
+ generates the corresponding Flask application source code, and writes it to a file.
6
+ It is intended to be used as part of a command-line interface or automation script
7
+ to scaffold ready-to-run Flask services.
8
+
9
+ The generated code supports HTTP and WebSocket routes, environment-based security
10
+ configuration, and respects the Bisslog runtime setup defined via decorators.
11
+ """
12
+ from typing import Optional
13
+
14
+ from ...builder.builder_flask_app_manager import bisslog_flask_builder
15
+
16
+
17
+ def build_boiler_plate_flask(
18
+ metadata_file: Optional[str] = None,
19
+ use_cases_folder_path: Optional[str] = None,
20
+ infra_path: Optional[str] = None,
21
+ encoding: str = "utf-8",
22
+ target_filename: str = "flask_app.py"
23
+ ):
24
+ """
25
+ Generates a Flask application boilerplate file from Bisslog metadata and use case code.
26
+
27
+ This function loads the service metadata and associated use cases, builds the Flask
28
+ application source code dynamically, and writes it to a Python file (e.g., `flask_app.py`).
29
+ The generated app includes route registration, security setup, and optional WebSocket support.
30
+
31
+ Parameters
32
+ ----------
33
+ metadata_file : str, optional
34
+ Path to the YAML or JSON metadata file describing the service.
35
+ use_cases_folder_path : str, optional
36
+ Path to the folder where the use case implementations are located.
37
+ infra_path : str, optional
38
+ Path to the folder where infrastructure components (e.g., adapters) are defined.
39
+ encoding : str, default="utf-8"
40
+ The file encoding to use when reading and writing files.
41
+ target_filename : str, default="flask_app.py"
42
+ The output filename where the Flask boilerplate code will be written.
43
+
44
+ Returns
45
+ -------
46
+ None
47
+ This function writes the generated Flask app code directly to the specified file.
48
+ """
49
+ flask_boiler_plate_string = bisslog_flask_builder(
50
+ metadata_file=metadata_file,
51
+ use_cases_folder_path=use_cases_folder_path,
52
+ infra_path=infra_path,
53
+ encoding=encoding
54
+ )
55
+
56
+ with open(target_filename, "w", encoding=encoding) as f:
57
+ f.write(flask_boiler_plate_string)
@@ -0,0 +1,52 @@
1
+ """
2
+ Command module to run a Flask application using Bisslog metadata.
3
+
4
+ This module provides a simple `run` function that initializes a Flask app
5
+ with use case metadata and launches the server. It is intended to be used
6
+ by the `bisslog_flask run` CLI command or directly from Python code.
7
+ """
8
+
9
+ from typing import Optional
10
+
11
+ from bisslog_flask import BisslogFlask
12
+
13
+
14
+ def run(metadata_file: Optional[str] = None,
15
+ use_cases_folder_path: Optional[str] = None,
16
+ infra_path: Optional[str] = None,
17
+ encoding: str = "utf-8",
18
+ secret_key: Optional[str] = None,
19
+ jwt_secret_key: Optional[str] = None):
20
+ """
21
+ Run a Flask application using metadata and use-case source.
22
+
23
+ This function creates and runs a Flask app configured through the
24
+ BisslogFlask integration layer. It loads metadata definitions,
25
+ applies HTTP and WebSocket use case resolvers, and starts the server.
26
+
27
+ Parameters
28
+ ----------
29
+ metadata_file : str, optional
30
+ Path to the metadata file (YAML or JSON) containing service and trigger definitions.
31
+ use_cases_folder_path : str, optional
32
+ Path to the folder where the use case implementation code is located.
33
+ infra_path : str, optional
34
+ Path to the folder containing infrastructure code (e.g., database, cache).
35
+ This is not used in the current implementation but can be extended.
36
+ encoding : str, optional
37
+ Encoding used to read the metadata file (default is "utf-8").
38
+ secret_key : str, optional
39
+ Value to set as Flask's SECRET_KEY for session signing.
40
+ jwt_secret_key : str, optional
41
+ Value to set as Flask's JWT_SECRET_KEY for JWT-based authentication.
42
+ """
43
+ app = BisslogFlask(
44
+ metadata_file=metadata_file,
45
+ use_cases_folder_path=use_cases_folder_path,
46
+ infra_path=infra_path,
47
+ encoding=encoding,
48
+ secret_key=secret_key,
49
+ jwt_secret_key=jwt_secret_key
50
+ )
51
+
52
+ app.run()
@@ -16,10 +16,12 @@ Dependencies
16
16
  - bisslog_schema
17
17
  - bisslog.utils.mapping
18
18
  """
19
+ import inspect
19
20
  from copy import deepcopy
20
- from typing import Callable, Optional, Dict
21
+ from typing import Callable, Optional, Dict, Union, Awaitable, Any
21
22
 
22
23
  from flask import Flask, request, jsonify
24
+
23
25
  try:
24
26
  from flask_cors import cross_origin
25
27
  except ImportError:
@@ -82,10 +84,10 @@ class BisslogFlaskHttpResolver(BisslogFlaskResolver):
82
84
 
83
85
  @staticmethod
84
86
  def _use_case_factory(
85
- use_case_name: str,
86
- fn: Callable,
87
- mapper: Optional[Dict[str, str]] = None,
88
- trigger: Optional[TriggerHttp] = None
87
+ use_case_name: str,
88
+ fn: Callable,
89
+ mapper: Optional[Dict[str, str]] = None,
90
+ trigger: Optional[TriggerHttp] = None
89
91
  ):
90
92
  """
91
93
  Factory to produce a Flask view function with optional mapping and CORS.
@@ -109,10 +111,49 @@ class BisslogFlaskHttpResolver(BisslogFlaskResolver):
109
111
  use_case_fn_copy = deepcopy(fn)
110
112
  __mapper__ = Mapper(name=f"Mapper {use_case_name}", base=mapper) if mapper else None
111
113
 
112
- def uc(*args, **kwargs):
113
- return BisslogFlaskHttpResolver._lambda_fn(
114
- *args, fn=use_case_fn_copy, __mapper__=__mapper__, **kwargs
115
- )
114
+ is_async = (
115
+ inspect.iscoroutinefunction(fn)
116
+ or inspect.iscoroutinefunction(getattr(fn, "__call__", None))
117
+ )
118
+
119
+ if is_async:
120
+ async def uc(*args, **kwargs):
121
+ if __mapper__ is None:
122
+ more_kwargs = {}
123
+ if request.method.lower() != "get":
124
+ more_kwargs.update(request.get_json(silent=True) or {})
125
+ res = await use_case_fn_copy(*args, **kwargs, **more_kwargs)
126
+ return jsonify(res)
127
+
128
+ res_map = __mapper__.map({
129
+ "path_query": request.view_args or {},
130
+ "body": request.get_json(silent=True) or {},
131
+ "params": request.args.to_dict(),
132
+ "headers": request.headers,
133
+ })
134
+ res = await use_case_fn_copy(**res_map)
135
+ return jsonify(res)
136
+
137
+ view = uc
138
+ else:
139
+ def uc(*args, **kwargs):
140
+ if __mapper__ is None:
141
+ more_kwargs = {}
142
+ if request.method.lower() != "get":
143
+ more_kwargs.update(request.get_json(silent=True) or {})
144
+ res = use_case_fn_copy(*args, **kwargs, **more_kwargs)
145
+ return jsonify(res)
146
+
147
+ res_map = __mapper__.map({
148
+ "path_query": request.view_args or {},
149
+ "body": request.get_json(silent=True) or {},
150
+ "params": request.args.to_dict(),
151
+ "headers": request.headers,
152
+ })
153
+ res = use_case_fn_copy(**res_map)
154
+ return jsonify(res)
155
+
156
+ view = uc
116
157
 
117
158
  # Apply CORS dynamically if allowed
118
159
  if trigger and trigger.allow_cors:
@@ -124,13 +165,13 @@ class BisslogFlaskHttpResolver(BisslogFlaskResolver):
124
165
  "allow_headers": ["Content-Type", "Authorization"],
125
166
  "supports_credentials": True
126
167
  }
127
- return cross_origin(**cors_kwargs)(uc)
168
+ return cross_origin(**cors_kwargs)(view)
128
169
 
129
- return uc
170
+ return view
130
171
 
131
172
  @classmethod
132
173
  def _add_use_case(cls, app: Flask, use_case_info: UseCaseInfo, trigger: TriggerInfo,
133
- use_case_function):
174
+ use_case_function: Union[Callable[..., Any], Callable[..., Awaitable[Any]]]):
134
175
  """
135
176
  Adds an HTTP endpoint to the Flask app for a given use case.
136
177
 
@@ -9,7 +9,6 @@ from abc import ABC, abstractmethod
9
9
  from typing import Callable
10
10
 
11
11
  from bisslog_schema.schema import UseCaseInfo, TriggerInfo
12
-
13
12
  from flask import Flask
14
13
 
15
14
 
@@ -5,7 +5,9 @@ This module defines the `BisslogFlaskWebSocketResolver` class, which dynamically
5
5
  WebSocket-based use case triggers in a Flask app. The class uses metadata definitions to
6
6
  configure event routes (via `route_key`) and binds them to corresponding use case functions.
7
7
  """
8
+ import inspect
8
9
  from typing import Callable
10
+
9
11
  from flask import Flask, request
10
12
 
11
13
  try:
@@ -64,13 +66,27 @@ class BisslogFlaskWebSocketResolver(BisslogFlaskResolver):
64
66
  base=trigger_info.options.mapper
65
67
  ) if trigger_info.options.mapper else None
66
68
 
67
- @socket_io_obj.on(route_key)
68
- def on_event(data):
69
- mapped_data = mapper.map({
70
- "route_key": route_key,
71
- "connection_id": request.sid,
72
- "body": data,
73
- "headers": dict(request.headers)
74
- }) if mapper else data
69
+ is_async = inspect.iscoroutinefunction(use_case_callable)
70
+
71
+ if is_async:
72
+ @socket_io_obj.on(route_key)
73
+ async def on_event(data):
74
+ mapped_data = mapper.map({
75
+ "route_key": route_key,
76
+ "connection_id": request.sid,
77
+ "body": data,
78
+ "headers": dict(request.headers)
79
+ }) if mapper else data
80
+
81
+ return await use_case_callable(**mapped_data) if mapper else use_case_callable(data)
82
+ else:
83
+ @socket_io_obj.on(route_key)
84
+ def on_event(data):
85
+ mapped_data = mapper.map({
86
+ "route_key": route_key,
87
+ "connection_id": request.sid,
88
+ "body": data,
89
+ "headers": dict(request.headers)
90
+ }) if mapper else data
75
91
 
76
- return use_case_callable(**mapped_data) if mapper else use_case_callable(data)
92
+ return use_case_callable(**mapped_data) if mapper else use_case_callable(data)
@@ -18,7 +18,9 @@ Dependencies
18
18
  from typing import Optional, Callable
19
19
 
20
20
  from bisslog_schema import read_service_info_with_code
21
+ from bisslog_schema.eager_import_module_or_package import EagerImportModulePackage
21
22
  from bisslog_schema.schema import UseCaseInfo, TriggerHttp, TriggerWebsocket
23
+ from bisslog_schema.setup import run_setup
22
24
  from flask import Flask
23
25
 
24
26
  from .bisslog_flask_http_resolver import BisslogFlaskHttpResolver
@@ -42,14 +44,17 @@ class InitFlaskAppManager:
42
44
  """
43
45
 
44
46
  def __init__(self, http_processor: BisslogFlaskResolver,
45
- websocket_processor: BisslogFlaskResolver) -> None:
47
+ websocket_processor: BisslogFlaskResolver,
48
+ force_import: Callable[[str], None]) -> None:
46
49
  self._http_processor = http_processor
47
50
  self._websocket_processor = websocket_processor
51
+ self._force_import = force_import
48
52
 
49
53
  def __call__(
50
54
  self,
51
55
  metadata_file: Optional[str] = None,
52
56
  use_cases_folder_path: Optional[str] = None,
57
+ infra_path: Optional[str] = None,
53
58
  app: Optional[Flask] = None,
54
59
  *,
55
60
  encoding: str = "utf-8",
@@ -69,6 +74,9 @@ class InitFlaskAppManager:
69
74
  Path to the metadata file (YAML/JSON).
70
75
  use_cases_folder_path : str, optional
71
76
  Directory where use case code is located.
77
+ infra_path : str, optional
78
+ Path to the folder where infrastructure components (e.g., adapters) are defined.
79
+ This is used to ensure that necessary modules are imported before route registration.
72
80
  app : Flask, optional
73
81
  An existing Flask app instance to which routes will be added.
74
82
  If not provided, a new app is created using the service name.
@@ -94,6 +102,11 @@ class InitFlaskAppManager:
94
102
  service_info = full_service_data.declared_metadata
95
103
  use_cases = full_service_data.discovered_use_cases
96
104
 
105
+ # Force import
106
+ self._force_import(infra_path)
107
+ # Run global setup if defined
108
+ run_setup("flask")
109
+
97
110
  # Initialize Flask app
98
111
  if app is None:
99
112
  app = Flask(service_info.name)
@@ -120,4 +133,5 @@ class InitFlaskAppManager:
120
133
  return app
121
134
 
122
135
 
123
- BisslogFlask = InitFlaskAppManager(BisslogFlaskHttpResolver(), BisslogFlaskWebSocketResolver())
136
+ BisslogFlask = InitFlaskAppManager(BisslogFlaskHttpResolver(), BisslogFlaskWebSocketResolver(),
137
+ EagerImportModulePackage(("src.infra", "infra")))
@@ -20,9 +20,6 @@ class BisslogFlaskSocketHelper(WebSocketManager):
20
20
  a consistent interface.
21
21
  """
22
22
 
23
- def __init__(self, conn) -> None:
24
- super().__init__(conn)
25
-
26
23
  def emit(self, event: str, connection_id: str, payload: Any,
27
24
  broadcast: bool = False, to: str = None):
28
25
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bisslog_flask
3
- Version: 0.0.1
3
+ Version: 0.0.3
4
4
  Summary: It is an extension of the bisslog library to support processes with flask
5
5
  Author-email: Darwin Stiven Herrera Cartagena <darwinsherrerac@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/darwinhc/bisslog-flask
@@ -10,54 +10,54 @@ Classifier: Operating System :: OS Independent
10
10
  Requires-Python: >=3.7
11
11
  Description-Content-Type: text/markdown
12
12
  License-File: LICENSE
13
- Requires-Dist: bisslog>=0.0.7
14
- Requires-Dist: bisslog-schema>=0.0.3
13
+ Requires-Dist: bisslog>=0.0.9
14
+ Requires-Dist: bisslog-schema>=0.0.10
15
15
  Requires-Dist: flask
16
16
  Provides-Extra: websocket
17
17
  Requires-Dist: flask-socketio; extra == "websocket"
18
18
  Provides-Extra: cors
19
19
  Requires-Dist: flask-cors>=6.0.0; extra == "cors"
20
+ Provides-Extra: async
21
+ Requires-Dist: Flask[async]>=2.2; extra == "async"
20
22
  Dynamic: license-file
21
23
 
22
-
23
24
  # bisslog-flask
24
25
 
25
26
  [![PyPI](https://img.shields.io/pypi/v/bisslog-flask)](https://pypi.org/project/bisslog-flask/)
26
27
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
27
28
 
28
- **bisslog-flask** is an extension of the bisslog library to support processes with Flask. It enables dynamic HTTP and WebSocket route registration from use case metadata, allowing developers to build clean, modular, and metadata-driven APIs with minimal boilerplate.
29
+ **bisslog-flask** is an extension of the Bisslog library to support processes with Flask.
30
+ It enables dynamic HTTP and WebSocket route registration from use case metadata, allowing developers to build clean, modular, and metadata-driven APIs with minimal boilerplate.
29
31
 
30
- Part of the bisslog ecosystem, it is designed to work seamlessly with domain-centric architectures like Hexagonal or Clean Architecture.
32
+ Part of the Bisslog ecosystem, it is designed to work seamlessly with domain-centric architectures like Hexagonal or Clean Architecture.
31
33
 
32
- ## Features
34
+ ---
33
35
 
34
- - 🔁 Dynamic route registration for HTTP and WebSocket triggers
36
+ ## Features
35
37
 
38
+ - 🔁 Dynamic route registration for HTTP and WebSocket triggers
36
39
  - 🧠 Metadata-driven setup – use YAML or JSON to declare your use cases
37
-
38
- - 🔒 Automatic CORS per endpoint using flask-cors
39
-
40
+ - 🔒 Automatic CORS per endpoint using `flask-cors`
40
41
  - 🔌 Extensible resolver pattern – plug in your own processor
41
-
42
42
  - ⚙️ Mapper integration – maps HTTP request parts to domain function arguments
43
43
 
44
-
44
+ ---
45
45
 
46
46
  ## 📦 Installation
47
47
 
48
- ~~~shell
48
+ ```bash
49
49
  pip install bisslog-flask
50
- ~~~
51
-
52
-
50
+ ```
53
51
 
52
+ ---
54
53
 
55
54
  ## 🚀 Quickstart
56
55
 
57
56
  ### Programmatically
58
57
 
59
- if you want to configure the app before bisslog touches it
60
- ~~~python
58
+ Use this approach if you want to configure the app before Bisslog touches it:
59
+
60
+ ```python
61
61
  from flask import Flask
62
62
  from bisslog_flask import BisslogFlask
63
63
 
@@ -70,11 +70,11 @@ BisslogFlask(
70
70
 
71
71
  if __name__ == "__main__":
72
72
  app.run(debug=True)
73
- ~~~
73
+ ```
74
74
 
75
- or
75
+ Or use the factory version:
76
76
 
77
- ~~~python
77
+ ```python
78
78
  from bisslog_flask import BisslogFlask
79
79
 
80
80
  app = BisslogFlask(
@@ -84,58 +84,73 @@ app = BisslogFlask(
84
84
 
85
85
  if __name__ == "__main__":
86
86
  app.run(debug=True)
87
- ~~~
88
-
87
+ ```
89
88
 
89
+ ---
90
90
 
91
- ## 🔧 How It Works
91
+ ## 🖥️ CLI Usage
92
92
 
93
- 1. Loads metadata and discovers use case functions (or callables), then uses resolvers to register routes dynamically into a Flask app.
93
+ You can also use the `bisslog_flask` CLI to run or generate a Flask app.
94
94
 
95
+ ```bash
96
+ bisslog_flask run [--metadata-file FILE] [--use-cases-folder-path DIR]
97
+ [--infra-folder-path DIR] [--encoding ENC]
98
+ [--secret-key KEY] [--jwt-secret-key KEY]
95
99
 
96
- ## 🔐 CORS Handling
100
+ bisslog_flask build [--metadata-file FILE] [--use-cases-folder-path DIR]
101
+ [--infra-folder-path DIR] [--encoding ENC]
102
+ [--target-filename FILE]
103
+ ```
97
104
 
98
- CORS is applied only when allow_cors: true is specified in the trigger
105
+ - `run`: Launches the Flask application from metadata.
106
+ - `build`: Generates a boilerplate Flask file (`flask_app.py` by default).
99
107
 
100
- Fully dynamic: works even with dynamic Flask routes like /users/<id>
108
+ All options are optional. You can override defaults via CLI flags.
101
109
 
102
- Powered by `@cross_origin` from `flask-cors`
110
+ ---
103
111
 
112
+ ## 🔐 CORS Handling
104
113
 
105
- ## Requirements
114
+ CORS is applied only when `allow_cors: true` is specified in the trigger.
106
115
 
107
- Python 3.7
116
+ Fully dynamic: works even with Flask dynamic routes like `/users/<id>`.
108
117
 
109
- Flask 2.0
118
+ Powered by `@cross_origin` from `flask-cors`.
110
119
 
111
- bisslog-schema ≥ 0.0.3
120
+ ---
112
121
 
113
- flask-cors
122
+ ## ✅ Requirements
114
123
 
115
- (Optional) flask-socketio if using WebSocket triggers
124
+ - Python 3.7
125
+ - Flask ≥ 2.0
126
+ - bisslog-schema ≥ 0.0.3
127
+ - flask-cors
128
+ - (Optional) flask-sock if using WebSocket triggers
116
129
 
130
+ ---
117
131
 
118
132
  ## 🧪 Testing Tip
119
133
 
120
- You can test the generated Flask app directly with `app.test_client()` if you take the programmatic way:
134
+ You can test the generated Flask app directly with `app.test_client()` if using the programmatic interface:
121
135
 
122
136
  ```python
123
137
  from bisslog_flask import BisslogFlask
124
138
 
125
139
  def test_user_create():
126
- app = BisslogFlask(metadata_file="metadata.yml", use_cases_folder_path="src/use_cases")
140
+ app = BisslogFlask(
141
+ metadata_file="metadata.yml",
142
+ use_cases_folder_path="src/use_cases"
143
+ )
127
144
  client = app.test_client()
128
145
  response = client.post("/user", json={"name": "Ana", "email": "ana@example.com"})
129
146
  assert response.status_code == 200
130
147
  ```
131
148
 
132
- Not generating code or using the programmatic way you just need to test your use cases.
133
-
149
+ If you're generating the code (boilerplate), you just need to test your use cases.
134
150
 
151
+ ---
135
152
 
136
153
  ## 📜 License
137
154
 
138
- This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
139
-
140
-
141
-
155
+ This project is licensed under the MIT License.
156
+ See the [LICENSE](LICENSE) file for details.
@@ -0,0 +1,21 @@
1
+ bisslog_flask/__init__.py,sha256=BEf_UxFtcMfaM-Smh_bwc6Xn8pR8LcEjby3nCs0hdXE,758
2
+ bisslog_flask/builder/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ bisslog_flask/builder/builder_flask_app_manager.py,sha256=HffxWfMRRlrfmYSojCdH563bJgQGa3vF2GB9rOYnINw,14777
4
+ bisslog_flask/builder/static_python_construct_data.py,sha256=p8QcXUAXZRXtNlghWv-husw9hByGTp53_g0RxhmzoNc,5735
5
+ bisslog_flask/cli/__init__.py,sha256=DlqqUfYI3e3Q6wY7xg7X-Ux9_6gTNQAAClAe2aI2-tg,3792
6
+ bisslog_flask/cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ bisslog_flask/cli/commands/build.py,sha256=uI-aH8HvW7UPdqGJvtQ2p5tw5nEFcUAzvicLCGuKccY,2341
8
+ bisslog_flask/cli/commands/run.py,sha256=gUfgpLzPxQ-hB2DApA-BcxpkPaI_QCNH7OUUpkl8D34,1953
9
+ bisslog_flask/initializer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ bisslog_flask/initializer/bisslog_flask_http_resolver.py,sha256=nxAZFyl_7akpMzgDQsURVAbJmTxuFKlpCu3SfYioh44,7890
11
+ bisslog_flask/initializer/bisslog_flask_resolver.py,sha256=pt7EC-WqfsBp7w3Z0SE378hfwWTLfRDM6fGMfF3_sb8,1707
12
+ bisslog_flask/initializer/bisslog_flask_ws_resolver.py,sha256=wcG8iPky2PYyRUovHJg5woX1vLJ-fmLRmgUW-PLU_Jc,3572
13
+ bisslog_flask/initializer/init_flask_app_manager.py,sha256=gwu6cg6RLh5UMT4GGiIFJx8cD385j7EvUnGfk9S6aC0,5389
14
+ bisslog_flask/socket_helper/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ bisslog_flask/socket_helper/socket_helper.py,sha256=HHENwYT-qdrJlwmGszYqFM1MA1g6naRurUp6sYCHRRQ,2967
16
+ bisslog_flask-0.0.3.dist-info/licenses/LICENSE,sha256=TSlM1hRIXc6yR3xpGzy2DMSSbds0svqHSetfNfQqCEk,1074
17
+ bisslog_flask-0.0.3.dist-info/METADATA,sha256=8X8f3Lsko5s7NX8iZwkP1gFpVNoOdRVUSjKinqaaVyk,4251
18
+ bisslog_flask-0.0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
+ bisslog_flask-0.0.3.dist-info/entry_points.txt,sha256=A2_A5lZt973oUBpuOCC78aTm7dGyHk6cemnn5_jOXHw,57
20
+ bisslog_flask-0.0.3.dist-info/top_level.txt,sha256=Xk85d0SIhkUP1HjsXOtq2vlU7yQT3mFApyb5IVgtG6w,14
21
+ bisslog_flask-0.0.3.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ bisslog_flask = bisslog_flask.cli:main
@@ -1,13 +0,0 @@
1
- bisslog_flask/__init__.py,sha256=BEf_UxFtcMfaM-Smh_bwc6Xn8pR8LcEjby3nCs0hdXE,758
2
- bisslog_flask/initializer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- bisslog_flask/initializer/bisslog_flask_http_resolver.py,sha256=d-KFQwMXeRyq3_Cu5bxCF7CpnetfnKainChEbdxDk2c,6257
4
- bisslog_flask/initializer/bisslog_flask_resolver.py,sha256=QWjft6HZbBplbP1vVMNoPhGoSZBJubAotwLR76HVttw,1708
5
- bisslog_flask/initializer/bisslog_flask_ws_resolver.py,sha256=s-eDdk5HA6E_D8iZC6IN7RSW-96mzu8m8VGL3Xqm9PQ,2974
6
- bisslog_flask/initializer/init_flask_app_manager.py,sha256=QzO1dae3YFap2VEXK3gSexLkdfBGaLWrHK732z5s0K0,4682
7
- bisslog_flask/socket_helper/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- bisslog_flask/socket_helper/socket_helper.py,sha256=sR9xQSPWzijhPNRh8WRxDbzE7rqFqm4w0Vql4gBGiGU,3037
9
- bisslog_flask-0.0.1.dist-info/licenses/LICENSE,sha256=TSlM1hRIXc6yR3xpGzy2DMSSbds0svqHSetfNfQqCEk,1074
10
- bisslog_flask-0.0.1.dist-info/METADATA,sha256=w82no5oSrRtS7XEDT38gs6UYIrzyuBHK4eEJkpQptH0,3551
11
- bisslog_flask-0.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
12
- bisslog_flask-0.0.1.dist-info/top_level.txt,sha256=Xk85d0SIhkUP1HjsXOtq2vlU7yQT3mFApyb5IVgtG6w,14
13
- bisslog_flask-0.0.1.dist-info/RECORD,,