algo-backend-framework 0.0.1__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.
Files changed (48) hide show
  1. algo_backend/__init__.py +0 -0
  2. algo_backend/config/__init__.py +8 -0
  3. algo_backend/config/basic_config.py +13 -0
  4. algo_backend/config/loguru_config.py +19 -0
  5. algo_backend/exception/__init__.py +22 -0
  6. algo_backend/exception/error_code_manage.py +126 -0
  7. algo_backend/exception/exception.py +42 -0
  8. algo_backend/exception/status_code.py +103 -0
  9. algo_backend/handler/__init__.py +3 -0
  10. algo_backend/handler/exception_to_vo.py +37 -0
  11. algo_backend/handler/operation_handler.py +71 -0
  12. algo_backend/intercept/__init__.py +9 -0
  13. algo_backend/intercept/common.py +45 -0
  14. algo_backend/intercept/http.py +40 -0
  15. algo_backend/intercept/validate.py +78 -0
  16. algo_backend/log/__init__.py +1 -0
  17. algo_backend/log/common.py +16 -0
  18. algo_backend/log/loguru/__init__.py +5 -0
  19. algo_backend/log/loguru/log_clean.py +140 -0
  20. algo_backend/log/loguru/log_setup.py +89 -0
  21. algo_backend/log/loguru/log_starter.py +65 -0
  22. algo_backend/log/loguru/patch_logging.py +83 -0
  23. algo_backend/log/nblog/__init__.py +0 -0
  24. algo_backend/metrics/__init__.py +22 -0
  25. algo_backend/metrics/collector/__init__.py +12 -0
  26. algo_backend/metrics/collector/common.py +17 -0
  27. algo_backend/metrics/collector/gc_metrics.py +74 -0
  28. algo_backend/metrics/collector/schedule_monitor.py +50 -0
  29. algo_backend/metrics/collector/system_metrics.py +169 -0
  30. algo_backend/metrics/http_metrics.py +56 -0
  31. algo_backend/metrics/prometheus_context.py +55 -0
  32. algo_backend/metrics/time_cost_metrics.py +146 -0
  33. algo_backend/middleware/__init__.py +4 -0
  34. algo_backend/middleware/cors.py +10 -0
  35. algo_backend/middleware/metrics.py +12 -0
  36. algo_backend/schema/__init__.py +3 -0
  37. algo_backend/schema/vo.py +83 -0
  38. algo_backend/starter/__init__.py +4 -0
  39. algo_backend/starter/default_app_generator.py +169 -0
  40. algo_backend/starter/default_service_starter.py +70 -0
  41. algo_backend/starter/event_list.py +32 -0
  42. algo_backend/utils/__init__.py +8 -0
  43. algo_backend/utils/meta_class.py +50 -0
  44. algo_backend/utils/utils.py +22 -0
  45. algo_backend_framework-0.0.1.dist-info/METADATA +60 -0
  46. algo_backend_framework-0.0.1.dist-info/RECORD +48 -0
  47. algo_backend_framework-0.0.1.dist-info/WHEEL +5 -0
  48. algo_backend_framework-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,169 @@
1
+ import logging
2
+ from contextlib import asynccontextmanager
3
+ from typing import Any, Callable, Coroutine, Dict, List, Optional
4
+
5
+ from fastapi import FastAPI
6
+ from fastapi.exceptions import RequestValidationError, StarletteHTTPException
7
+ from starlette.middleware import Middleware
8
+
9
+ from algo_backend.exception import ApiErrorCodeManage
10
+ from algo_backend.intercept import (
11
+ BasicExceptionInterceptor,
12
+ HTTPExceptionInterceptor,
13
+ ValidateExceptionInterceptor,
14
+ )
15
+ from algo_backend.log import BasicLogStarter
16
+ from algo_backend.metrics import (
17
+ PrometheusContext,
18
+ PrometheusTimeCostMetricSetting,
19
+ )
20
+ from algo_backend.metrics.collector import MetricsScheduleMonitor
21
+ from algo_backend.middleware import default_cors_middleware
22
+
23
+ from .event_list import EventList
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ class DefaultAlgoAppGenerator:
29
+ """
30
+ FastApi的App初始化,作用于每个fastapi的worker
31
+ """
32
+
33
+ def __init__(
34
+ self,
35
+ *,
36
+ service_name: str,
37
+ app_error_code_prefix: Optional[int] = None,
38
+ custom_start_event: List[Callable[[], Coroutine[Any, Any, None]]] = None,
39
+ custom_end_event: List[Callable[[], Coroutine[Any, Any, None]]] = None,
40
+ log_starter: Optional[BasicLogStarter] = None,
41
+ intercept_dict: Optional[Dict[Exception, BasicExceptionInterceptor]] = None,
42
+ middlewares: List[Middleware] = (default_cors_middleware,),
43
+ api_time_cost_buckets: Optional[List[int]] = None,
44
+ client_time_cost_buckets: Optional[List[int]] = None,
45
+ ):
46
+ """
47
+ :param service_name: 服务名称,必填
48
+ :param app_error_code_prefix: 错误码前缀,两位数字,如果指定则错误码为8位,如果不填则错误码是6位
49
+ :param custom_start_event: 用户自定义的app时的触发事件列表
50
+ :param custom_end_event: 用户自定义的app结束的触发事件列表
51
+ :param log_starter: 日志启动器
52
+ :param intercept_dict: 拦截器字典,key为异常类型,value为拦截器实例
53
+ :param middlewares: 中间件列表
54
+ :param api_time_cost_buckets: 接口耗时统计的桶,None表示使用默认的,空列表表示不启用这个指标
55
+ :param client_time_cost_buckets: 客户端耗时统计的桶,None表示使用默认的,空列表表示不启用这个指标
56
+ """
57
+ # 注册8位错误码前缀
58
+ ApiErrorCodeManage.set_error_code_prefix_env(app_error_code_prefix)
59
+ self.service_name = service_name
60
+
61
+ self.__app: Optional[FastAPI] = FastAPI()
62
+
63
+ self.start_event_list = EventList()
64
+ self.end_event_list = EventList()
65
+
66
+ self.log_starter = log_starter or BasicLogStarter(service_name=service_name)
67
+
68
+ self.intercept_dict = {
69
+ RequestValidationError: ValidateExceptionInterceptor(),
70
+ StarletteHTTPException: HTTPExceptionInterceptor(),
71
+ }
72
+ self.intercept_dict.update(intercept_dict or {})
73
+
74
+ self.middleware_list = list(middlewares)
75
+
76
+ self.time_cost_metrics_initializer = (
77
+ lambda: PrometheusTimeCostMetricSetting.initialize(
78
+ api_metrics_buckets=api_time_cost_buckets,
79
+ client_metrics_buckets=client_time_cost_buckets,
80
+ )
81
+ )
82
+
83
+ self.init_start_event(custom_start_event)
84
+ self.init_end_event(custom_end_event)
85
+
86
+ def init_start_event(self, custom_event):
87
+ """
88
+ 初始化开始事件
89
+ """
90
+ self.start_event_list.add(self.log_starter.setup_log())
91
+ # 启动系统指标、gc指标的定时采集写入以供多进程时的汇总
92
+ self.start_event_list.add(MetricsScheduleMonitor().run_monitor)
93
+ # 接口计时器和客户端解释器的初始化
94
+ self.start_event_list.add(self.time_cost_metrics_initializer)
95
+ # 插入自定义的开始事件
96
+ for event in custom_event or []:
97
+ self.start_event_list.add(event)
98
+
99
+ def init_end_event(self, custom_event):
100
+ """
101
+ 初始化结束事件
102
+ """
103
+ # 插入自定义事件
104
+ for event in custom_event or []:
105
+ self.end_event_list.add(event)
106
+
107
+ def add_middleware(self, middleware: Middleware) -> "DefaultAlgoAppGenerator":
108
+ """
109
+ 添加中间件
110
+ """
111
+ self.middleware_list.append(middleware)
112
+ return self
113
+
114
+ def set_log_stater(self, log_starter: BasicLogStarter) -> "DefaultAlgoAppGenerator":
115
+ self.log_starter = log_starter
116
+ return self
117
+
118
+ def use_log_loguru(self):
119
+ """
120
+ 使用loguru日志框架
121
+ """
122
+ from algo_backend.log.loguru import LoguruStarter
123
+
124
+ return self.set_log_stater(LoguruStarter(service_name=self.service_name))
125
+
126
+ def use_log_nblog(self):
127
+ """
128
+ 使用nblog日志框架,待实现
129
+ """
130
+ ...
131
+
132
+ def generate(self):
133
+ self.init_app().add_prometheus_endpoint().add_interceptor()
134
+ return self.app
135
+
136
+ @property
137
+ def app(self):
138
+ return self.__app
139
+
140
+ def init_app(self, **kwargs):
141
+ self.__app = FastAPI(
142
+ lifespan=self.gen_life_span(), middleware=self.middleware_list, **kwargs
143
+ )
144
+ return self
145
+
146
+ def add_prometheus_endpoint(self):
147
+ PrometheusContext.mount_prometheus_endpoint(self.__app)
148
+ return self
149
+
150
+ def add_interceptor(self):
151
+ for exc, interceptor in self.intercept_dict.items():
152
+ self.__app.add_exception_handler(exc, interceptor.intercept)
153
+ return self
154
+
155
+ def gen_life_span(self):
156
+ @asynccontextmanager
157
+ async def lifespan(app: FastAPI):
158
+ logger.info("ExecuteStartEvent")
159
+ await self.start_event_list.execute()
160
+ logger.info("Start App")
161
+
162
+ yield
163
+
164
+ logger.info("ExecuteStopEvent")
165
+ await self.end_event_list.execute()
166
+
167
+ logger.info("Goodbye")
168
+
169
+ return lifespan
@@ -0,0 +1,70 @@
1
+ from typing import Any, Callable, List
2
+
3
+ import uvicorn
4
+
5
+ from algo_backend.config import ServiceConfig
6
+ from algo_backend.metrics import PrometheusContext
7
+
8
+
9
+ class DefaultAlgoServiceStarter:
10
+ """
11
+ 主线程的设置
12
+ uvicorn容器启动fastapi
13
+ """
14
+
15
+ def __init__(self, service_name: str):
16
+ self.service_name = service_name
17
+ self.__main_pid_event: List[Callable[[], Any]] = []
18
+ self.init_events()
19
+
20
+ def init_events(self):
21
+ # 初始化prometheus多进程的设置
22
+ self.append_event(PrometheusContext.init)
23
+ return self
24
+
25
+ def append_event(self, func: Callable[[], Any]):
26
+ """尾部插入事件"""
27
+ self.__main_pid_event.append(func)
28
+ return self
29
+
30
+ def insert_event(self, func: Callable[[], Any], index: int = 0):
31
+ """任意位置插入事件"""
32
+ self.__main_pid_event.insert(index, func)
33
+ return self
34
+
35
+ def use_loguru(self):
36
+ """
37
+ 使用loguru日志设置,对主进程的日志生效
38
+ 注册loguru的定时清理器
39
+ """
40
+ from algo_backend.log.loguru import LoguruStarter
41
+
42
+ loguru_stater = LoguruStarter(service_name=self.service_name)
43
+ self.insert_event(loguru_stater.setup_log)
44
+ self.append_event(loguru_stater.run_log_cleaner)
45
+ return self
46
+
47
+ def main_pid_setup(self) -> "DefaultAlgoServiceStarter":
48
+ """
49
+ 主进程中的设置事件
50
+ """
51
+ for func in self.__main_pid_event:
52
+ func()
53
+ return self
54
+
55
+ @classmethod
56
+ def run(
57
+ cls,
58
+ app_str: str = "app.backend.application:app",
59
+ host: str = "0.0.0.0",
60
+ **kwargs,
61
+ ):
62
+ # 启动服务
63
+ uvicorn.run(
64
+ app_str,
65
+ host=host,
66
+ port=ServiceConfig.HTTP_PORT,
67
+ timeout_keep_alive=ServiceConfig.TIMEOUT_KEEP_ALIVE,
68
+ workers=ServiceConfig.PROCESS_NUM,
69
+ **kwargs,
70
+ )
@@ -0,0 +1,32 @@
1
+ import asyncio
2
+ from typing import Any, Callable, Coroutine, List, Optional, Union
3
+
4
+
5
+ class EventList:
6
+ def __init__(self):
7
+ """
8
+ 普通函数和协程函数列表
9
+ """
10
+ self.__events: List[
11
+ Union[Callable, Callable[[], Coroutine[Any, Any, None]]]
12
+ ] = []
13
+
14
+ def add(
15
+ self,
16
+ event: Optional[
17
+ Union[Callable, Callable[[], Coroutine[Any, Any, None]]]
18
+ ] = None,
19
+ ):
20
+ """添加事件到列表"""
21
+ if event:
22
+ self.__events.append(event)
23
+ return self
24
+
25
+ async def execute(self):
26
+ """执行所有事件"""
27
+
28
+ for event in self.__events:
29
+ if asyncio.iscoroutinefunction(event):
30
+ await event()
31
+ else:
32
+ event()
@@ -0,0 +1,8 @@
1
+ from .meta_class import ConstantClass, OsAttrMeta
2
+ from .utils import get_request_id
3
+
4
+ __all__ = [
5
+ "get_request_id",
6
+ "OsAttrMeta",
7
+ "ConstantClass",
8
+ ]
@@ -0,0 +1,50 @@
1
+ import os
2
+
3
+
4
+ class ConstantClassMeta(type):
5
+ """
6
+ 对子类也生效的元类
7
+ """
8
+
9
+ def __setattr__(cls, name, value):
10
+ # 检查属性是否已存在且非特殊属性
11
+ if hasattr(cls, name) and not name.startswith("__"):
12
+ raise AttributeError(
13
+ f"The property '{name}' of the constant class {cls.__name__} is not allowed to be modified."
14
+ )
15
+
16
+ def __call__(cls, *args, **kwargs):
17
+ raise TypeError(f"The constant class {cls.__name__} cannot be instantiated.")
18
+
19
+
20
+ class ConstantClass(metaclass=ConstantClassMeta):
21
+ pass
22
+
23
+
24
+ class OsAttrMeta(type(ConstantClass), type):
25
+ def __new__(cls, name, bases, attrs):
26
+ for attr in attrs["__annotations__"]:
27
+ raw_value = os.getenv(attr.upper(), attrs.get(attr, None))
28
+ expected_type = attrs["__annotations__"][attr]
29
+
30
+ # 根据类型提示转换值
31
+ if expected_type is bool and isinstance(raw_value, str):
32
+ # 将 "false" 转换为 False,"true" 转换为 True
33
+ raw_value = raw_value.lower()
34
+ if raw_value == "true":
35
+ raw_value = True
36
+ elif raw_value == "false":
37
+ raw_value = False
38
+ else:
39
+ raise ValueError(
40
+ f"Invalid value for boolean attribute '{attr}': {raw_value}"
41
+ )
42
+ elif expected_type is int:
43
+ raw_value = int(raw_value)
44
+ elif expected_type is float:
45
+ raw_value = float(raw_value)
46
+
47
+ # 如果环境变量不存在,则保持默认值(如果存在)
48
+ attrs[attr] = raw_value if raw_value is not None else attrs.get(attr)
49
+
50
+ return super().__new__(cls, name, bases, attrs)
@@ -0,0 +1,22 @@
1
+ import uuid
2
+
3
+ from fastapi import Header
4
+
5
+
6
+ def gen_random_request_id():
7
+ return str(uuid.uuid4())
8
+
9
+
10
+ async def get_request_id(
11
+ x_request_id: str = Header(gen_random_request_id(), alias="x-request-id"),
12
+ ):
13
+ """
14
+ 使用示例
15
+
16
+ @app.post("/example/help")
17
+ async def run(body: Body, reqid=Depends(get_request_id)):
18
+ ...
19
+
20
+ 如果请求头中没有x-request-id,则会触发validate_interceptor的检验
21
+ """
22
+ return x_request_id
@@ -0,0 +1,60 @@
1
+ Metadata-Version: 2.4
2
+ Name: algo-backend-framework
3
+ Version: 0.0.1
4
+ Summary: Ctcdn algorithm backend framework
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: fastapi>=0.128.0
8
+ Requires-Dist: loguru>=0.7.3
9
+ Requires-Dist: prometheus-client>=0.24.1
10
+ Requires-Dist: psutil>=7.2.1
11
+ Requires-Dist: pydantic>=2.12.5
12
+ Requires-Dist: python-dotenv>=1.2.1
13
+ Requires-Dist: uvicorn>=0.40.0
14
+
15
+ # algo-backend-framework-python
16
+
17
+ # 功能概述
18
+ - 配置
19
+ - 错误码、异常、参数检验报错转换
20
+ - 标准响应体、x-request-id
21
+ - 日志框架支持:loguru、nb_log
22
+ - 多进程prometheus指标
23
+ - otel
24
+
25
+ ## 分支规范
26
+ - master分支: 归档分支
27
+ - release/release-×.×.×: 发布分支,×.×.×为版本号;修改[pyproject.toml](pyproject.toml)中的version构建并发布包
28
+ - dev分支: 开发分支;合并至release分支时,需要提mr,mr需要填写版本号和功能描述;需要充分自测和review之后才能合并至release
29
+ - feat/feat-×××××: 功能分支;合并至dev时需要mr,mr需要填写功能名称和功能描述;新功能或较大重构需要建feat分支,较小的改动不必开feat分支
30
+
31
+ ## 本地调试
32
+
33
+ 本地开发需要打包验证,则先构建包(但不要发布),然后在本地目录离线安装
34
+ ```shell
35
+ # 构建包
36
+ uv build
37
+ # 拷贝wheel包到本地测试目录下执行以下命令
38
+ uv add algo_backend_framework-*.*.*-py3-none-any.whl
39
+ # 调试时发现包有问题,只需要remove后重新打包,并重新add
40
+ uv remove algo-backend-framework
41
+ ```
42
+
43
+ ## 开发约定
44
+ - 代码提交前进行sonar扫描和语法检查,并进行代码格式化
45
+ ```
46
+ uv run ruff format algo_backend/×××××
47
+ ```
48
+ - 子模块__init__.py文件需要声明包内暴露模块,不建议用户通过包内文件名导入,以减少后期微调重构引起的不兼容
49
+ - 跨模块导入一律使用`from algo_backend.× import 模块名`的全限定包名,同级目录下导入从使用`from . import `
50
+
51
+
52
+ ## 发布
53
+ - 在release分支进行构建和推送
54
+ ```shell
55
+ uv build
56
+ # 推送ctcdn nexus
57
+ uv publish --index ctcdn-pypi-upload
58
+ # 推送pypi
59
+ uv publish
60
+ ```
@@ -0,0 +1,48 @@
1
+ algo_backend/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ algo_backend/config/__init__.py,sha256=MLAvcvodxXiaVsSJaVvQcNjFWIFusBQ0i9OrWJv3zv0,222
3
+ algo_backend/config/basic_config.py,sha256=RSRf8lgc8KfA_FHNjchaghSLNnJy7ejn2ux57SBSakg,292
4
+ algo_backend/config/loguru_config.py,sha256=WovghKcMqeysPuf8vlKFhbNug8Z7zPqxkjlwVYKjZWw,513
5
+ algo_backend/exception/__init__.py,sha256=yYGpAk5oMGDsXDQwjcVMZJT1irwtG0oQacSrv1_VrFo,580
6
+ algo_backend/exception/error_code_manage.py,sha256=d29aMgelf93RUqlvLTjQDxBiSH9ZVFYbW66rzTWDKM4,4391
7
+ algo_backend/exception/exception.py,sha256=Y5YX45nT3tbZ2uBwraTXBqXlpWjpz9fQ0ICfIM3M4kM,1205
8
+ algo_backend/exception/status_code.py,sha256=s5lXXR-oSALk3gtI4KcPKHstvnRFArQL1SEgQFIeSxU,2707
9
+ algo_backend/handler/__init__.py,sha256=hyZWIopKZZX5eoGGmqkepjcF2Vfy6oSFAc2NRlVHOQI,107
10
+ algo_backend/handler/exception_to_vo.py,sha256=e1jM9YvoTEmWL73I5uV_F1xo9UA7LgnWYfFdI7wFidA,1005
11
+ algo_backend/handler/operation_handler.py,sha256=7ffsgjj1EZ_d3x_jx9krju_uwCmwfjXHJ_9KZ8SrpOw,2654
12
+ algo_backend/intercept/__init__.py,sha256=FoNHCzUc3ceLo85ECN3L7HzW6KmLqcG5sgMh_qULLdw,265
13
+ algo_backend/intercept/common.py,sha256=T50_IAeY0HQ8TbupjYvMHMObg3j9I4lQnqZMiWNzuQw,1445
14
+ algo_backend/intercept/http.py,sha256=C_N2nyErFhdOZ1LPQ6iU_JCy4fFYEucDwhWJb-qBnbc,1381
15
+ algo_backend/intercept/validate.py,sha256=FBaLc2CRxtIjfjudOH41nwAX7jKXlWJHgUFooC19deI,2949
16
+ algo_backend/log/__init__.py,sha256=0bRk_Y5olw95oIR7DabfT-1gJv_WdXhe7ToufAr5VKo,36
17
+ algo_backend/log/common.py,sha256=1h9zuCuoas3qUr9yG2fvm_3WNYVQW2eH5DJzS0IMvFQ,355
18
+ algo_backend/log/loguru/__init__.py,sha256=zOfdTmDUf6bFeZeZYbXEg1EMMm3ju90k5dtdpN7Sn0s,177
19
+ algo_backend/log/loguru/log_clean.py,sha256=_BVF9dLooTGIgwctjuqba6hEUUvHww8JuUzFHO88GEM,4590
20
+ algo_backend/log/loguru/log_setup.py,sha256=Xnw6JCB8gBfm6z-KxTSdenoxfsxwiZGWOs75DpE0N3w,3010
21
+ algo_backend/log/loguru/log_starter.py,sha256=2YsLrdNPlF0Mgu4hduEYJGidLtnkFgtoMlzEzOJyuUc,1841
22
+ algo_backend/log/loguru/patch_logging.py,sha256=GS8x0MmIjqaY5nmiPgjU01yNG1AvbOrTU8xKzwb2Vdc,2796
23
+ algo_backend/log/nblog/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
+ algo_backend/metrics/__init__.py,sha256=20CPEFgi0yJ2Fy-LTcFQ3aqlXHhLPtgPFboqOMBhl34,640
25
+ algo_backend/metrics/http_metrics.py,sha256=3GYNhKJoS6oJ8L7FXnVkPZ_wa-SKj_444KLdBeGkbs4,1944
26
+ algo_backend/metrics/prometheus_context.py,sha256=GqemLccNbTx_5DBijOyzySzWydbpWYfnhWh17rujjwE,1910
27
+ algo_backend/metrics/time_cost_metrics.py,sha256=0v8Mu4lXbgKjAFH3aK0GPtVhw_70kIYMd-eH-ceyrkI,4645
28
+ algo_backend/metrics/collector/__init__.py,sha256=t5Csl-Z9qpik-isEUVYDYBWYgf7xdM49548YfJr4X64,413
29
+ algo_backend/metrics/collector/common.py,sha256=yeiZp9ufbYSW7N-R6ytk74UF0dCqJ8r338lL64TmA4g,475
30
+ algo_backend/metrics/collector/gc_metrics.py,sha256=s2ariKimji9LC71eAQFlrFkpcF3hkSy5IBC6D8LFePc,2510
31
+ algo_backend/metrics/collector/schedule_monitor.py,sha256=PTdUIFLrcWFZG5bUlaRj9FJHIy3ulCUk3ExQqVZwRgU,1585
32
+ algo_backend/metrics/collector/system_metrics.py,sha256=SxOswHKGKLDB5wMsKjd7rkQtofhc432OIm4OvlT5ciM,5329
33
+ algo_backend/middleware/__init__.py,sha256=KfSRT3TZ89BzY__Rrbu-9HkjO1LToD5sXyl_J4Po7Q4,173
34
+ algo_backend/middleware/cors.py,sha256=kKdPeZMS38IVvOueJz34lQX1t1hH5OOz068IdIAR-Kc,271
35
+ algo_backend/middleware/metrics.py,sha256=XwZGipQwKp20naZglx41Wc79rXud81Y4AIfswUJj3jM,385
36
+ algo_backend/schema/__init__.py,sha256=STf9SdNuP5dzMDehdGjJGHQ7Pq-qO4jw-5CWsayvx5g,90
37
+ algo_backend/schema/vo.py,sha256=CSYNHpJx2YNCZ5-4GjjSs6BNKiJEyl31gnplnxGvXfY,2211
38
+ algo_backend/starter/__init__.py,sha256=2AfidtR7PmlhVPz65tt_t3QdIYqpp0mVg8M-lbJE2F4,194
39
+ algo_backend/starter/default_app_generator.py,sha256=kUANISNpGHafJPVgTOG-BrcQNRUOn31tSv38tTun9qc,6094
40
+ algo_backend/starter/default_service_starter.py,sha256=QHVDAS4css7tYCH7Fd_is2uWX8fxV1GD7LAugx904B8,2017
41
+ algo_backend/starter/event_list.py,sha256=vQHzQIpW8LZmQ93YyET-1gX6pQGVE5A6I_pLoYTFOH0,824
42
+ algo_backend/utils/__init__.py,sha256=oX6OyL-28jzc94u4fyH1TtntCzQySkfZ8jibMk1KPU8,168
43
+ algo_backend/utils/meta_class.py,sha256=hcZPGF7EIHvJOXXR82_7Gah_AWbqkcSxUc473I_6maY,1850
44
+ algo_backend/utils/utils.py,sha256=q3bxBrivndLRggWsLryloSpu-_Ecbj4mhZL8oYdiDTo,481
45
+ algo_backend_framework-0.0.1.dist-info/METADATA,sha256=D4eriT_lp8IIMOwz5JLKBGca-6k7STDRBNKTLcmMVRE,2196
46
+ algo_backend_framework-0.0.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
47
+ algo_backend_framework-0.0.1.dist-info/top_level.txt,sha256=zLsbLTRV1tO2hQfazqiBLO73VnjSAhJSUpMMBmQaLfw,13
48
+ algo_backend_framework-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ algo_backend