pyutilities_simple 0.1.0__tar.gz
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.
- pyutilities_simple-0.1.0/.gitignore +207 -0
- pyutilities_simple-0.1.0/.python-version +1 -0
- pyutilities_simple-0.1.0/LICENSE +21 -0
- pyutilities_simple-0.1.0/PKG-INFO +30 -0
- pyutilities_simple-0.1.0/README.md +16 -0
- pyutilities_simple-0.1.0/pyproject.toml +32 -0
- pyutilities_simple-0.1.0/pytest.ini +3 -0
- pyutilities_simple-0.1.0/src/pyutilities_simple/__init__.py +2 -0
- pyutilities_simple-0.1.0/src/pyutilities_simple/const.py +106 -0
- pyutilities_simple-0.1.0/src/pyutilities_simple/gvar.py +85 -0
- pyutilities_simple-0.1.0/src/pyutilities_simple/logit.py +584 -0
- pyutilities_simple-0.1.0/src/pyutilities_simple/singleton.py +73 -0
- pyutilities_simple-0.1.0/src/pyutilities_simple/sqlite.py +420 -0
- pyutilities_simple-0.1.0/src/pyutilities_simple/utilities.py +134 -0
- pyutilities_simple-0.1.0/tests/run_tests.py +177 -0
- pyutilities_simple-0.1.0/tests/test_const.py +190 -0
- pyutilities_simple-0.1.0/tests/test_gvar.py +160 -0
- pyutilities_simple-0.1.0/tests/test_logit.py +966 -0
- pyutilities_simple-0.1.0/tests/test_singleton.py +263 -0
- pyutilities_simple-0.1.0/tests/test_sqlite.py +359 -0
- pyutilities_simple-0.1.0/tests/test_utilities.py +133 -0
- pyutilities_simple-0.1.0/uv.lock +172 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
#Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
#uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
#poetry.lock
|
|
109
|
+
#poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
#pdm.lock
|
|
116
|
+
#pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
#pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# SageMath parsed files
|
|
135
|
+
*.sage.py
|
|
136
|
+
|
|
137
|
+
# Environments
|
|
138
|
+
.env
|
|
139
|
+
.envrc
|
|
140
|
+
.venv
|
|
141
|
+
env/
|
|
142
|
+
venv/
|
|
143
|
+
ENV/
|
|
144
|
+
env.bak/
|
|
145
|
+
venv.bak/
|
|
146
|
+
|
|
147
|
+
# Spyder project settings
|
|
148
|
+
.spyderproject
|
|
149
|
+
.spyproject
|
|
150
|
+
|
|
151
|
+
# Rope project settings
|
|
152
|
+
.ropeproject
|
|
153
|
+
|
|
154
|
+
# mkdocs documentation
|
|
155
|
+
/site
|
|
156
|
+
|
|
157
|
+
# mypy
|
|
158
|
+
.mypy_cache/
|
|
159
|
+
.dmypy.json
|
|
160
|
+
dmypy.json
|
|
161
|
+
|
|
162
|
+
# Pyre type checker
|
|
163
|
+
.pyre/
|
|
164
|
+
|
|
165
|
+
# pytype static type analyzer
|
|
166
|
+
.pytype/
|
|
167
|
+
|
|
168
|
+
# Cython debug symbols
|
|
169
|
+
cython_debug/
|
|
170
|
+
|
|
171
|
+
# PyCharm
|
|
172
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
173
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
174
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
175
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
176
|
+
#.idea/
|
|
177
|
+
|
|
178
|
+
# Abstra
|
|
179
|
+
# Abstra is an AI-powered process automation framework.
|
|
180
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
181
|
+
# Learn more at https://abstra.io/docs
|
|
182
|
+
.abstra/
|
|
183
|
+
|
|
184
|
+
# Visual Studio Code
|
|
185
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
186
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
188
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
189
|
+
# .vscode/
|
|
190
|
+
|
|
191
|
+
# Ruff stuff:
|
|
192
|
+
.ruff_cache/
|
|
193
|
+
|
|
194
|
+
# PyPI configuration file
|
|
195
|
+
.pypirc
|
|
196
|
+
|
|
197
|
+
# Cursor
|
|
198
|
+
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
|
|
199
|
+
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
|
|
200
|
+
# refer to https://docs.cursor.com/context/ignore-files
|
|
201
|
+
.cursorignore
|
|
202
|
+
.cursorindexingignore
|
|
203
|
+
|
|
204
|
+
# Marimo
|
|
205
|
+
marimo/_static/
|
|
206
|
+
marimo/_lsp/
|
|
207
|
+
__marimo__/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 YoucheeXu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyutilities_simple
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
Project-URL: Homepage, https://github.com/YoucheeXu/pyutilities
|
|
6
|
+
Project-URL: Issues, https://github.com/YoucheeXu/pyutilities/issues
|
|
7
|
+
Author-email: Youchee <youchi.xu@live.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Requires-Python: >=3.13
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# Readme
|
|
16
|
+
|
|
17
|
+
[TOC]
|
|
18
|
+
|
|
19
|
+
## test
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
uv pip install -e .
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## build & publish
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
uv build
|
|
29
|
+
uv publish
|
|
30
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pyutilities_simple"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Add your description here"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "Youchee", email = "youchi.xu@live.com" }
|
|
8
|
+
]
|
|
9
|
+
requires-python = ">=3.13"
|
|
10
|
+
dependencies = []
|
|
11
|
+
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"Operating System :: OS Independent",
|
|
15
|
+
]
|
|
16
|
+
license = "MIT"
|
|
17
|
+
license-files = ["LICEN[CS]E*"]
|
|
18
|
+
|
|
19
|
+
[project.urls]
|
|
20
|
+
Homepage = "https://github.com/YoucheeXu/pyutilities"
|
|
21
|
+
Issues = "https://github.com/YoucheeXu/pyutilities/issues"
|
|
22
|
+
|
|
23
|
+
[build-system]
|
|
24
|
+
requires = ["hatchling"]
|
|
25
|
+
build-backend = "hatchling.build"
|
|
26
|
+
|
|
27
|
+
[dependency-groups]
|
|
28
|
+
dev = [
|
|
29
|
+
"pytest>=9.0.2",
|
|
30
|
+
"pytest-cov>=7.0.0",
|
|
31
|
+
"pytest-timeout>=2.4.0",
|
|
32
|
+
]
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
# -*- coding: UTF-8 -*-
|
|
3
|
+
import sys
|
|
4
|
+
from types import ModuleType
|
|
5
|
+
from typing import Final, Callable
|
|
6
|
+
|
|
7
|
+
from pyutilities_simple.singleton import singleton
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@singleton
|
|
11
|
+
class _const(ModuleType):
|
|
12
|
+
""" 单例模式的常量管理模块
|
|
13
|
+
|
|
14
|
+
特性:
|
|
15
|
+
- 常量名称必须全大写(强制命名规范)
|
|
16
|
+
- 常量一旦定义不可修改或删除(只读特性)
|
|
17
|
+
- 全局唯一实例,确保常量在整个程序中保持一致
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
class ConstError(PermissionError):
|
|
21
|
+
"""尝试修改或删除已定义常量时触发的异常"""
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
class ConstCaseError(TypeError):
|
|
25
|
+
"""常量名称不是全大写时触发的异常"""
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
def __init__(self, module_name: str | None = None) -> None:
|
|
29
|
+
"""初始化常量模块,继承ModuleType的特性"""
|
|
30
|
+
# 确定模块名称,默认使用当前模块名
|
|
31
|
+
name = module_name if module_name is not None else __name__
|
|
32
|
+
super().__init__(name)
|
|
33
|
+
|
|
34
|
+
# 初始化内置属性(标记为"内置",清空时保留)
|
|
35
|
+
self.__dict__["__version__"] = "1.0.0"
|
|
36
|
+
self.__dict__["__description__"] = "A singleton-based constant management module"
|
|
37
|
+
# 手动绑定模块原生属性(解决coverage访问__file__的问题)
|
|
38
|
+
self.__dict__["__file__"] = sys.modules[name].__file__ if name in sys.modules else __file__
|
|
39
|
+
self.__dict__["__name__"] = name
|
|
40
|
+
|
|
41
|
+
def __setattr__(self, name: str, value: object) -> None:
|
|
42
|
+
""" 重写属性设置方法,实现常量约束:
|
|
43
|
+
1. 已存在的常量不允许重新赋值
|
|
44
|
+
2. 常量名称必须使用全大写字母
|
|
45
|
+
"""
|
|
46
|
+
# 跳过内置特殊属性的设置(避免覆盖__file__等)
|
|
47
|
+
if name.startswith("__") and name.endswith("__"):
|
|
48
|
+
self.__dict__[name] = value
|
|
49
|
+
return
|
|
50
|
+
# 校验1:不允许重复赋值(已存在则报错)
|
|
51
|
+
if name in self.__dict__:
|
|
52
|
+
raise self.ConstError(f"Cannot modify constant '{name}': constants are immutable")
|
|
53
|
+
# 校验2:名称必须全大写
|
|
54
|
+
if not name.isupper():
|
|
55
|
+
raise self.ConstCaseError(f"Constant name '{name}' must be in uppercase")
|
|
56
|
+
# 合法常量:添加到字典
|
|
57
|
+
self.__dict__[name] = value
|
|
58
|
+
|
|
59
|
+
def __delattr__(self, name: str) -> None:
|
|
60
|
+
"""重写属性删除方法,禁止删除已定义的常量"""
|
|
61
|
+
# 跳过内置特殊属性的设置(避免覆盖__file__等)
|
|
62
|
+
if name.startswith("__") and name.endswith("__"):
|
|
63
|
+
raise self.ConstError(f"Cannot delete built-in attribute '{name}'")
|
|
64
|
+
if name in self.__dict__:
|
|
65
|
+
raise self.ConstError(f"Cannot delete constant '{name}': constants cannot be deleted")
|
|
66
|
+
raise NameError(f"Constant '{name}' is not defined")
|
|
67
|
+
|
|
68
|
+
def __getattr__(self, name: str) -> object:
|
|
69
|
+
"""获取属性:内置属性正常返回,未定义常量抛异常"""
|
|
70
|
+
if name.startswith("__") and name.endswith("__"):
|
|
71
|
+
return super().__getattribute__(name)
|
|
72
|
+
if name not in self.__dict__:
|
|
73
|
+
raise NameError(f"Constant '{name}' is not defined")
|
|
74
|
+
return self.__dict__[name]
|
|
75
|
+
|
|
76
|
+
def list_constants(self) -> list[str]:
|
|
77
|
+
"""返回所有用户定义的常量名称(排除模块内置属性)"""
|
|
78
|
+
return [
|
|
79
|
+
name for name in sorted(self.__dict__.keys())
|
|
80
|
+
if name.isupper() and not name.startswith("__")
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
def get_constant(self, name: str) -> object | None:
|
|
84
|
+
"""安全获取常量值,不存在时返回None而非抛出异常"""
|
|
85
|
+
return self.__dict__.get(name)
|
|
86
|
+
|
|
87
|
+
def clear_user_constants(self) -> None:
|
|
88
|
+
"""彻底清空所有用户常量(保留内置属性)"""
|
|
89
|
+
# 遍历并删除所有用户定义的常量(全大写、非双下划线)
|
|
90
|
+
for name in list(self.__dict__.keys()): # 转列表避免遍历中修改字典
|
|
91
|
+
if name.isupper() and not name.startswith("__"):
|
|
92
|
+
del self.__dict__[name]
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# 替换模块为单例实例(全局唯一)
|
|
96
|
+
sys.modules[__name__] = _const(__name__)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# 导出公共接口
|
|
100
|
+
__all__ = ["ConstError", "ConstCaseError", "list_constants", "get_constant", "clear_user_constants"]
|
|
101
|
+
|
|
102
|
+
# 为公共接口添加Final类型提示,明确其不可修改性,绑定模块级接口
|
|
103
|
+
list_constants: Final[Callable[[], list[str]]] = sys.modules[__name__].list_constants
|
|
104
|
+
get_constant: Final[Callable[[str], object | None]] = sys.modules[__name__].get_constant
|
|
105
|
+
ConstError: Final[type[PermissionError]] = sys.modules[__name__].ConstError
|
|
106
|
+
ConstCaseError: Final[type[TypeError]] = sys.modules[__name__].ConstCaseError
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
# -*- coding: UTF-8 -*-
|
|
3
|
+
import sys
|
|
4
|
+
from types import ModuleType
|
|
5
|
+
from typing import Final, Callable, override, cast
|
|
6
|
+
|
|
7
|
+
from pyutilities_simple.singleton import singleton
|
|
8
|
+
|
|
9
|
+
@singleton
|
|
10
|
+
class GlobalVarModule(ModuleType):
|
|
11
|
+
"""
|
|
12
|
+
单例模式的全局变量管理模块
|
|
13
|
+
特性:
|
|
14
|
+
1. 全局唯一实例,跨模块共享变量
|
|
15
|
+
2. 支持安全获取变量(不存在返回None)
|
|
16
|
+
3. 支持列出所有全局变量
|
|
17
|
+
4. 支持清空全局变量(主要用于测试)
|
|
18
|
+
5. 可选只读变量保护
|
|
19
|
+
"""
|
|
20
|
+
# 内置只读变量(不可修改/删除)
|
|
21
|
+
_READ_ONLY_KEYS: Final[list[str]] = ["ver"]
|
|
22
|
+
|
|
23
|
+
def __init__(self, module_name: str = __name__) -> None:
|
|
24
|
+
"""初始化全局变量模块,继承ModuleType以兼容模块特性"""
|
|
25
|
+
super().__init__(module_name)
|
|
26
|
+
# 初始化内置版本号(只读)
|
|
27
|
+
self.__dict__["ver"] = "0.1.0"
|
|
28
|
+
# 模块元信息
|
|
29
|
+
self.__dict__["__description__"] = "Singleton-based global variable manager"
|
|
30
|
+
|
|
31
|
+
@override
|
|
32
|
+
def __setattr__(self, name: str, value: object) -> None:
|
|
33
|
+
"""重写属性设置:保护只读变量"""
|
|
34
|
+
# 保护内置只读变量
|
|
35
|
+
if name in self._READ_ONLY_KEYS and name in self.__dict__:
|
|
36
|
+
raise PermissionError(f"Read-only variable '{name}' cannot be modified")
|
|
37
|
+
self.__dict__[name] = value
|
|
38
|
+
|
|
39
|
+
@override
|
|
40
|
+
def __delattr__(self, name: str) -> None:
|
|
41
|
+
"""重写属性删除:保护只读变量"""
|
|
42
|
+
if name in self._READ_ONLY_KEYS:
|
|
43
|
+
raise PermissionError(f"Read-only variable '{name}' cannot be deleted")
|
|
44
|
+
if name not in self.__dict__:
|
|
45
|
+
raise NameError(f"Global variable '{name}' does not exist")
|
|
46
|
+
del self.__dict__[name]
|
|
47
|
+
|
|
48
|
+
def get_var(self, name: str, default: object = None):
|
|
49
|
+
"""
|
|
50
|
+
安全获取全局变量
|
|
51
|
+
Args:
|
|
52
|
+
name: 变量名
|
|
53
|
+
default: 不存在时的默认值(默认None)
|
|
54
|
+
Returns:
|
|
55
|
+
变量值或默认值
|
|
56
|
+
"""
|
|
57
|
+
return cast(object, self.__dict__.get(name, default))
|
|
58
|
+
|
|
59
|
+
def list_vars(self):
|
|
60
|
+
"""返回所有用户定义的全局变量(排除内置属性)"""
|
|
61
|
+
return [
|
|
62
|
+
name for name in sorted(self.__dict__.keys())
|
|
63
|
+
if not name.startswith("__") and name not in self._READ_ONLY_KEYS
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
def list_all_vars(self):
|
|
67
|
+
"""返回所有变量(包含内置/只读变量)"""
|
|
68
|
+
return sorted(self.__dict__.keys())
|
|
69
|
+
|
|
70
|
+
def clear_vars(self):
|
|
71
|
+
"""清空所有非只读的全局变量(仅用于测试/重置)"""
|
|
72
|
+
for name in self.list_vars():
|
|
73
|
+
del self.__dict__[name]
|
|
74
|
+
|
|
75
|
+
# 将模块替换为单例实例,实现全局唯一
|
|
76
|
+
sys.modules[__name__] = GlobalVarModule(__name__)
|
|
77
|
+
|
|
78
|
+
# 导出公共接口(增强类型提示)
|
|
79
|
+
__all__ = ["get_var", "list_vars", "list_all_vars", "clear_vars"]
|
|
80
|
+
|
|
81
|
+
# 绑定模块级接口(适配直接调用)
|
|
82
|
+
get_var: Final[Callable[[str, object | None], object | None]] = sys.modules[__name__].get_var
|
|
83
|
+
list_vars: Final[Callable[[], list[str]]] = sys.modules[__name__].list_vars
|
|
84
|
+
list_all_vars: Final[Callable[[], list[str]]] = sys.modules[__name__].list_all_vars
|
|
85
|
+
clear_vars: Final[Callable[[], None]] = sys.modules[__name__].clear_vars
|