From 259f8d44abaad7336b2001699bfd26fe87ae1a60 Mon Sep 17 00:00:00 2001 From: initial01 <1484866681@qq.com> Date: Mon, 22 Jun 2026 13:41:59 +0800 Subject: [PATCH 1/6] refactor(core): restructure initialization logic in `__init__.py` for global instances --- astrbot/core/__init__.py | 89 ++++++++++++++++++++++++++++++++-------- 1 file changed, 71 insertions(+), 18 deletions(-) diff --git a/astrbot/core/__init__.py b/astrbot/core/__init__.py index 51690ede27..161cde31e3 100644 --- a/astrbot/core/__init__.py +++ b/astrbot/core/__init__.py @@ -25,23 +25,76 @@ from .log import LogBroker, LogManager # noqa from .utils.astrbot_path import get_astrbot_data_path -# 初始化数据存储文件夹 -os.makedirs(get_astrbot_data_path(), exist_ok=True) - DEMO_MODE = os.getenv("DEMO_MODE", "False").strip().lower() in ("true", "1", "t") -astrbot_config = AstrBotConfig() -t2i_base_url = astrbot_config.get("t2i_endpoint", "https://t2i.soulter.top/text2img") -html_renderer = HtmlRenderer(t2i_base_url) -logger = LogManager.GetLogger(log_name="astrbot") -LogManager.configure_logger(logger, astrbot_config) -LogManager.configure_trace_logger(astrbot_config) -db_helper = SQLiteDatabase(DB_PATH) -# 简单的偏好设置存储, 这里后续应该存储到数据库中, 一些部分可以存储到配置中 -sp = SharedPreferences(db_helper=db_helper) -# 文件令牌服务 -file_token_service = FileTokenService() -pip_installer = PipInstaller( - astrbot_config.get("pip_install_arg", ""), - astrbot_config.get("pypi_index_url", None), -) +_initialized = False + + +def _bootstrap(): + """ + internal initialization function, triggered only on first access to global instances. + Do not call this function directly from outside. + """ + global _initialized + if _initialized: + return + _initialized = True + + global \ + astrbot_config, \ + t2i_base_url, \ + html_renderer, \ + logger, \ + db_helper, \ + sp, \ + file_token_service, \ + pip_installer + + # 初始化数据存储文件夹 + os.makedirs(get_astrbot_data_path(), exist_ok=True) + + astrbot_config = AstrBotConfig() + t2i_base_url = astrbot_config.get( + "t2i_endpoint", "https://t2i.soulter.top/text2img" + ) + html_renderer = HtmlRenderer(t2i_base_url) + logger = LogManager.GetLogger(log_name="astrbot") + LogManager.configure_logger(logger, astrbot_config) + LogManager.configure_trace_logger(astrbot_config) + db_helper = SQLiteDatabase(DB_PATH) + # 简单的偏好设置存储, 这里后续应该存储到数据库中, 一些部分可以存储到配置中 + sp = SharedPreferences(db_helper=db_helper) + # 文件令牌服务 + file_token_service = FileTokenService() + pip_installer = PipInstaller( + astrbot_config.get("pip_install_arg", ""), + astrbot_config.get("pypi_index_url", None), + ) + + +# PEP 562 module-level __getattr__ and __dir__ for lazy loading of global instances +_lazy_attrs = { + "astrbot_config", + "t2i_base_url", + "html_renderer", + "logger", + "db_helper", + "sp", + "file_token_service", + "pip_installer", +} + + +def __getattr__(name: str): + """lazy load global instances on first access""" + if name in _lazy_attrs: + _bootstrap() + return globals()[name] + raise AttributeError(f"module 'astrbot.core' has no attribute {name!r}") + + +def __dir__(): + """make sure dir() and IDE completion can discover lazy-loaded attributes""" + # auto-collect all public symbols in the module __dict__ that do not start with an underscore + public_api = [k for k in globals() if not k.startswith("_")] + return public_api + list(_lazy_attrs) From 29fde075404832f37f868fdab67bedac2a1cb4cf Mon Sep 17 00:00:00 2001 From: initial01 <1484866681@qq.com> Date: Mon, 22 Jun 2026 14:50:55 +0800 Subject: [PATCH 2/6] refactor(core): setting _initialized = True to end of function; use a sorted set of unique names in __dir__; --- astrbot/core/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/astrbot/core/__init__.py b/astrbot/core/__init__.py index 161cde31e3..998582c8ae 100644 --- a/astrbot/core/__init__.py +++ b/astrbot/core/__init__.py @@ -38,7 +38,6 @@ def _bootstrap(): global _initialized if _initialized: return - _initialized = True global \ astrbot_config, \ @@ -71,6 +70,8 @@ def _bootstrap(): astrbot_config.get("pypi_index_url", None), ) + _initialized = True + # PEP 562 module-level __getattr__ and __dir__ for lazy loading of global instances _lazy_attrs = { @@ -96,5 +97,5 @@ def __getattr__(name: str): def __dir__(): """make sure dir() and IDE completion can discover lazy-loaded attributes""" # auto-collect all public symbols in the module __dict__ that do not start with an underscore - public_api = [k for k in globals() if not k.startswith("_")] - return public_api + list(_lazy_attrs) + public_api = {k for k in globals() if not k.startswith("_")} + return sorted(public_api | _lazy_attrs) From e1b175a8371f62311ae53e99d97200b5001ef694 Mon Sep 17 00:00:00 2001 From: initial01 <1484866681@qq.com> Date: Mon, 22 Jun 2026 15:09:26 +0800 Subject: [PATCH 3/6] refactor(core): enhance _bootstrap function with double-checked locking for initialization --- astrbot/core/__init__.py | 72 ++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/astrbot/core/__init__.py b/astrbot/core/__init__.py index 998582c8ae..090acde40e 100644 --- a/astrbot/core/__init__.py +++ b/astrbot/core/__init__.py @@ -1,4 +1,5 @@ import os +import threading from astrbot.core.config import AstrBotConfig from astrbot.core.config.default import DB_PATH @@ -27,6 +28,7 @@ DEMO_MODE = os.getenv("DEMO_MODE", "False").strip().lower() in ("true", "1", "t") +_bootstrap_lock = threading.Lock() _initialized = False @@ -36,41 +38,47 @@ def _bootstrap(): Do not call this function directly from outside. """ global _initialized + + # double-checked locking to avoid unnecessary locking after initialization is done if _initialized: return - global \ - astrbot_config, \ - t2i_base_url, \ - html_renderer, \ - logger, \ - db_helper, \ - sp, \ - file_token_service, \ - pip_installer - - # 初始化数据存储文件夹 - os.makedirs(get_astrbot_data_path(), exist_ok=True) - - astrbot_config = AstrBotConfig() - t2i_base_url = astrbot_config.get( - "t2i_endpoint", "https://t2i.soulter.top/text2img" - ) - html_renderer = HtmlRenderer(t2i_base_url) - logger = LogManager.GetLogger(log_name="astrbot") - LogManager.configure_logger(logger, astrbot_config) - LogManager.configure_trace_logger(astrbot_config) - db_helper = SQLiteDatabase(DB_PATH) - # 简单的偏好设置存储, 这里后续应该存储到数据库中, 一些部分可以存储到配置中 - sp = SharedPreferences(db_helper=db_helper) - # 文件令牌服务 - file_token_service = FileTokenService() - pip_installer = PipInstaller( - astrbot_config.get("pip_install_arg", ""), - astrbot_config.get("pypi_index_url", None), - ) - - _initialized = True + with _bootstrap_lock: + if _initialized: + return + + global \ + astrbot_config, \ + t2i_base_url, \ + html_renderer, \ + logger, \ + db_helper, \ + sp, \ + file_token_service, \ + pip_installer + + # 初始化数据存储文件夹 + os.makedirs(get_astrbot_data_path(), exist_ok=True) + + astrbot_config = AstrBotConfig() + t2i_base_url = astrbot_config.get( + "t2i_endpoint", "https://t2i.soulter.top/text2img" + ) + html_renderer = HtmlRenderer(t2i_base_url) + logger = LogManager.GetLogger(log_name="astrbot") + LogManager.configure_logger(logger, astrbot_config) + LogManager.configure_trace_logger(astrbot_config) + db_helper = SQLiteDatabase(DB_PATH) + # 简单的偏好设置存储, 这里后续应该存储到数据库中, 一些部分可以存储到配置中 + sp = SharedPreferences(db_helper=db_helper) + # 文件令牌服务 + file_token_service = FileTokenService() + pip_installer = PipInstaller( + astrbot_config.get("pip_install_arg", ""), + astrbot_config.get("pypi_index_url", None), + ) + + _initialized = True # PEP 562 module-level __getattr__ and __dir__ for lazy loading of global instances From 8713549cdbcf8a416bc48b215575401f1b51d909 Mon Sep 17 00:00:00 2001 From: initial01 <1484866681@qq.com> Date: Mon, 22 Jun 2026 15:17:26 +0800 Subject: [PATCH 4/6] refactor(core): add locking to __dir__ for thread-safe access to public API --- astrbot/core/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/astrbot/core/__init__.py b/astrbot/core/__init__.py index 090acde40e..3e9c1f60c4 100644 --- a/astrbot/core/__init__.py +++ b/astrbot/core/__init__.py @@ -105,5 +105,6 @@ def __getattr__(name: str): def __dir__(): """make sure dir() and IDE completion can discover lazy-loaded attributes""" # auto-collect all public symbols in the module __dict__ that do not start with an underscore - public_api = {k for k in globals() if not k.startswith("_")} + with _bootstrap_lock: + public_api = {k for k in globals() if not k.startswith("_")} return sorted(public_api | _lazy_attrs) From 74788de2ea57b4c4fd3a98bace71a9b315c9a6c8 Mon Sep 17 00:00:00 2001 From: initial01 <1484866681@qq.com> Date: Mon, 22 Jun 2026 15:30:03 +0800 Subject: [PATCH 5/6] refactor(core): move TYPE_CHECKING imports and _lazy_attrs definition for better organization --- astrbot/core/__init__.py | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/astrbot/core/__init__.py b/astrbot/core/__init__.py index 3e9c1f60c4..b6160cac63 100644 --- a/astrbot/core/__init__.py +++ b/astrbot/core/__init__.py @@ -1,5 +1,6 @@ import os import threading +from typing import TYPE_CHECKING from astrbot.core.config import AstrBotConfig from astrbot.core.config.default import DB_PATH @@ -31,6 +32,30 @@ _bootstrap_lock = threading.Lock() _initialized = False +if TYPE_CHECKING: + import logging + + astrbot_config: AstrBotConfig + t2i_base_url: str + html_renderer: HtmlRenderer + logger: logging.Logger + db_helper: SQLiteDatabase + sp: SharedPreferences + file_token_service: FileTokenService + pip_installer: PipInstaller + +# PEP 562 module-level __getattr__ and __dir__ for lazy loading of global instances +_lazy_attrs = { + "astrbot_config", + "t2i_base_url", + "html_renderer", + "logger", + "db_helper", + "sp", + "file_token_service", + "pip_installer", +} + def _bootstrap(): """ @@ -81,19 +106,6 @@ def _bootstrap(): _initialized = True -# PEP 562 module-level __getattr__ and __dir__ for lazy loading of global instances -_lazy_attrs = { - "astrbot_config", - "t2i_base_url", - "html_renderer", - "logger", - "db_helper", - "sp", - "file_token_service", - "pip_installer", -} - - def __getattr__(name: str): """lazy load global instances on first access""" if name in _lazy_attrs: From 9497f33f5b87d9479ae74d41be736cf20354e6d5 Mon Sep 17 00:00:00 2001 From: initial01 <1484866681@qq.com> Date: Mon, 22 Jun 2026 16:32:00 +0800 Subject: [PATCH 6/6] refactor(core): remove threading lock and simplify _bootstrap function --- astrbot/core/__init__.py | 74 ++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 41 deletions(-) diff --git a/astrbot/core/__init__.py b/astrbot/core/__init__.py index b6160cac63..d54eca5e47 100644 --- a/astrbot/core/__init__.py +++ b/astrbot/core/__init__.py @@ -1,5 +1,4 @@ import os -import threading from typing import TYPE_CHECKING from astrbot.core.config import AstrBotConfig @@ -29,7 +28,6 @@ DEMO_MODE = os.getenv("DEMO_MODE", "False").strip().lower() in ("true", "1", "t") -_bootstrap_lock = threading.Lock() _initialized = False if TYPE_CHECKING: @@ -64,46 +62,41 @@ def _bootstrap(): """ global _initialized - # double-checked locking to avoid unnecessary locking after initialization is done if _initialized: return - with _bootstrap_lock: - if _initialized: - return - - global \ - astrbot_config, \ - t2i_base_url, \ - html_renderer, \ - logger, \ - db_helper, \ - sp, \ - file_token_service, \ - pip_installer - - # 初始化数据存储文件夹 - os.makedirs(get_astrbot_data_path(), exist_ok=True) - - astrbot_config = AstrBotConfig() - t2i_base_url = astrbot_config.get( - "t2i_endpoint", "https://t2i.soulter.top/text2img" - ) - html_renderer = HtmlRenderer(t2i_base_url) - logger = LogManager.GetLogger(log_name="astrbot") - LogManager.configure_logger(logger, astrbot_config) - LogManager.configure_trace_logger(astrbot_config) - db_helper = SQLiteDatabase(DB_PATH) - # 简单的偏好设置存储, 这里后续应该存储到数据库中, 一些部分可以存储到配置中 - sp = SharedPreferences(db_helper=db_helper) - # 文件令牌服务 - file_token_service = FileTokenService() - pip_installer = PipInstaller( - astrbot_config.get("pip_install_arg", ""), - astrbot_config.get("pypi_index_url", None), - ) - - _initialized = True + global \ + astrbot_config, \ + t2i_base_url, \ + html_renderer, \ + logger, \ + db_helper, \ + sp, \ + file_token_service, \ + pip_installer + + # 初始化数据存储文件夹 + os.makedirs(get_astrbot_data_path(), exist_ok=True) + + astrbot_config = AstrBotConfig() + t2i_base_url = astrbot_config.get( + "t2i_endpoint", "https://t2i.soulter.top/text2img" + ) + html_renderer = HtmlRenderer(t2i_base_url) + logger = LogManager.GetLogger(log_name="astrbot") + LogManager.configure_logger(logger, astrbot_config) + LogManager.configure_trace_logger(astrbot_config) + db_helper = SQLiteDatabase(DB_PATH) + # 简单的偏好设置存储, 这里后续应该存储到数据库中, 一些部分可以存储到配置中 + sp = SharedPreferences(db_helper=db_helper) + # 文件令牌服务 + file_token_service = FileTokenService() + pip_installer = PipInstaller( + astrbot_config.get("pip_install_arg", ""), + astrbot_config.get("pypi_index_url", None), + ) + + _initialized = True def __getattr__(name: str): @@ -117,6 +110,5 @@ def __getattr__(name: str): def __dir__(): """make sure dir() and IDE completion can discover lazy-loaded attributes""" # auto-collect all public symbols in the module __dict__ that do not start with an underscore - with _bootstrap_lock: - public_api = {k for k in globals() if not k.startswith("_")} + public_api = {k for k in globals() if not k.startswith("_")} return sorted(public_api | _lazy_attrs)