Skip to content

Commit 3033733

Browse files
committed
加强了部分数据管理模块的类型安全
1 parent 528fa1d commit 3033733

File tree

11 files changed

+232
-171
lines changed

11 files changed

+232
-171
lines changed

linpg/abstracts/__init__.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"""
2+
结构:
3+
getter -> setter
4+
"""
5+
6+
from .setter import *

linpg/abstracts/getter.py

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import copy
2+
from functools import reduce
3+
from operator import getitem
4+
from typing import Any, Final, Optional, Sequence
5+
6+
from ..exception import EXCEPTION
7+
8+
9+
class TypeSafeGetter:
10+
11+
__RETURN_NONE_FOR_KEY_ERROR: Final[str] = "<!RETURN_NONE_FOR_KEY_ERROR>"
12+
13+
# 根据keys查找值,最后返回一个复制的对象
14+
@classmethod
15+
def get_by_keys(cls, _dict: dict, _keys: Sequence, _default: Optional[Any] = None, _deepcopy: bool = True) -> Any:
16+
try:
17+
return copy.deepcopy(reduce(getitem, _keys, _dict)) if _deepcopy is True else reduce(getitem, _keys, _dict)
18+
except KeyError:
19+
if _default is None:
20+
EXCEPTION.fatal('Getting "KeyError" while trying to get keys {} from dict!'.format(_keys))
21+
return _default if _default is not cls.__RETURN_NONE_FOR_KEY_ERROR else None
22+
23+
# 获取static数据字典 (子类需实现)
24+
@classmethod
25+
def _get_data(cls) -> dict:
26+
raise EXCEPTION.fatal("_get_data()", 1)
27+
28+
# 获取特定的数据
29+
@classmethod
30+
def get(cls, *_key: Any, _deepcopy: bool = True) -> Any:
31+
return cls.get_by_keys(cls._get_data(), _key, _deepcopy)
32+
33+
# 尝试获取特定的数据
34+
@classmethod
35+
def try_get(cls, *_key: Any, _default: Optional[Any] = None) -> Optional[Any]:
36+
return cls.get_by_keys(cls._get_data(), _key, _default)
37+
38+
# 是否值存在且不为None
39+
@classmethod
40+
def exists_not_none(cls, *_key: Any) -> bool:
41+
return cls.try_get(*_key, _default=cls.__RETURN_NONE_FOR_KEY_ERROR) is not None
42+
43+
# 以str的形式获取特定的数据
44+
@classmethod
45+
def get_str(cls, *_key: Any) -> str:
46+
return str(cls.get(*_key))
47+
48+
# 尝试以str的形式获取特定的数据
49+
@classmethod
50+
def try_get_str(cls, *_key: Any) -> Optional[str]:
51+
_temp: Optional[Any] = cls.try_get(*_key, _default=cls.__RETURN_NONE_FOR_KEY_ERROR)
52+
return str(_temp) if _temp is not None else _temp
53+
54+
# 以int的形式获取特定的数据
55+
@classmethod
56+
def get_int(cls, *_key: Any) -> int:
57+
return int(cls.get(*_key))
58+
59+
# 尝试以int的形式获取特定的数据
60+
@classmethod
61+
def try_get_int(cls, *_key: Any) -> Optional[int]:
62+
_temp: Optional[Any] = cls.try_get(*_key, _default=cls.__RETURN_NONE_FOR_KEY_ERROR)
63+
return int(_temp) if _temp is not None else _temp
64+
65+
# 以bool的形式获取特定的数据
66+
@classmethod
67+
def get_bool(cls, *_key: Any) -> bool:
68+
return bool(cls.get(*_key))
69+
70+
# 尝试以bool的形式获取特定的数据
71+
@classmethod
72+
def try_get_bool(cls, *_key: Any) -> Optional[bool]:
73+
_temp: Optional[Any] = cls.try_get(*_key, _default=cls.__RETURN_NONE_FOR_KEY_ERROR)
74+
return bool(_temp) if _temp is not None else _temp
75+
76+
# 以dict的形式获取特定的数据
77+
@classmethod
78+
def get_dict(cls, *_key: Any) -> dict:
79+
return dict(cls.get(*_key))
80+
81+
# 以dict的形式获取特定的数据(不复制)
82+
@classmethod
83+
def get_dict_ref(cls, *_key: Any) -> dict:
84+
return dict(cls.get(*_key, _deepcopy=False))
85+
86+
# 尝试以dict的形式获取特定的数据
87+
@classmethod
88+
def try_get_dict(cls, *_key: Any) -> Optional[dict]:
89+
_temp: Optional[Any] = cls.try_get(*_key, _default=cls.__RETURN_NONE_FOR_KEY_ERROR)
90+
return dict(_temp) if _temp is not None else _temp
91+
92+
# 以list的形式获取特定的数据
93+
@classmethod
94+
def get_list(cls, *_key: Any) -> list:
95+
return list(cls.get(*_key))
96+
97+
# 尝试以list的形式获取特定的数据
98+
@classmethod
99+
def try_get_list(cls, *_key: Any) -> Optional[list]:
100+
_temp: Optional[Any] = cls.try_get(*_key, _default=cls.__RETURN_NONE_FOR_KEY_ERROR)
101+
return list(_temp) if _temp is not None else _temp
102+
103+
# 以tuple的形式获取特定的数据
104+
@classmethod
105+
def get_tuple(cls, *_key: Any) -> tuple:
106+
return tuple(cls.get(*_key))
107+
108+
# 尝试以tuple的形式获取特定的数据
109+
@classmethod
110+
def try_get_tuple(cls, *_key: Any) -> Optional[tuple]:
111+
_temp: Optional[Any] = cls.try_get(*_key, _default=cls.__RETURN_NONE_FOR_KEY_ERROR)
112+
return tuple(_temp) if _temp is not None else _temp

linpg/abstracts/setter.py

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from .getter import *
2+
3+
4+
class TypeSafeSetter:
5+
6+
# 根据keys查找被设置对应对应对象为指定值
7+
@staticmethod
8+
def set_by_keys(_dict: dict, _keys: Sequence, value: object, assumeKeyExists: bool = True) -> None:
9+
if len(_keys) < 1:
10+
EXCEPTION.fatal("Keys' length has to be greater than 0.")
11+
pointer: dict = _dict
12+
last_key_index: int = len(_keys) - 1
13+
for index in range(last_key_index):
14+
_item: Optional[object] = pointer.get(_keys[index])
15+
if isinstance(_item, dict):
16+
pointer = _item
17+
elif _item is None:
18+
if assumeKeyExists is True:
19+
EXCEPTION.fatal('Getting "KeyError" while trying to set keys {} to dict!'.format(_keys))
20+
pointer[_keys[index]] = {}
21+
pointer = pointer[_keys[index]]
22+
else:
23+
EXCEPTION.fatal("Getting not dict object {0} while trying to set keys {1} to dict!".format(_item, _keys))
24+
pointer[_keys[last_key_index]] = value
25+
26+
# 获取static数据字典 (子类需实现)
27+
@classmethod
28+
def _get_data(cls) -> dict:
29+
raise EXCEPTION.fatal("_get_data()", 1)
30+
31+
# 设置特定的数据
32+
@classmethod
33+
def set(cls, *_key: str, value: Any, assumeKeyExists: bool = True) -> None:
34+
cls.set_by_keys(cls._get_data(), _key, value, assumeKeyExists)
35+
36+
# 清空所有数据(谨慎使用)
37+
@classmethod
38+
def clear(cls) -> None:
39+
cls._get_data().clear()

linpg/basic/saves.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,18 @@
44
from .font import *
55

66
# 持久数据管理IO
7-
class PersistentData:
7+
class PersistentData(TypeSafeGetter, TypeSafeSetter):
88

99
__DATA: Final[dict[str, Any]] = {}
1010
__PATH: Final[str] = Specification.get_directory("save", "persistent." + Config.get_file_type())
1111

1212
@classmethod
13-
def get(cls, *_keys: str, _default: Optional[Any] = None) -> Any:
14-
return get_value_by_keys(cls.__DATA, _keys, _default)
13+
def _get_data(cls) -> dict:
14+
return cls.__DATA
1515

1616
@classmethod
17-
def set(cls, *_keys: str, value: object) -> None:
18-
set_value_by_keys(cls.__DATA, _keys, value, False)
17+
def set(cls, *_key: str, value: Any, assumeKeyExists: bool = False) -> None:
18+
super().set(*_key, value=value, assumeKeyExists=assumeKeyExists)
1919
cls.save()
2020

2121
@classmethod

linpg/config/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
22
结构:
3-
base -> setting -> modules -> data
3+
base -> setting -> modules
44
"""
5-
from .data import *
5+
from .modules import *

linpg/config/base.py

+49-39
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
import copy
21
import json
3-
from functools import reduce
42
from glob import glob
5-
from operator import getitem
6-
from typing import Any, Final, Optional, Sequence
73

4+
from ..abstracts import *
85
from ..exception import EXCEPTION, os
96

107
# 尝试导入yaml库
@@ -16,37 +13,6 @@
1613
except Exception:
1714
pass
1815

19-
20-
# 根据keys查找值,最后返回一个复制的对象
21-
def get_value_by_keys(_dict: dict, _keys: Sequence, _default: Optional[Any] = None) -> Any:
22-
try:
23-
return copy.deepcopy(reduce(getitem, _keys, _dict))
24-
except KeyError:
25-
if _default is None:
26-
EXCEPTION.fatal('Getting "KeyError" while trying to get keys {} from dict!'.format(_keys))
27-
return _default
28-
29-
30-
# 根据keys查找被设置对应对应对象为指定值
31-
def set_value_by_keys(_dict: dict, _keys: Sequence, value: object, assumeKeyExists: bool = True) -> None:
32-
if len(_keys) < 1:
33-
EXCEPTION.fatal("Keys' length has to be greater than 0.")
34-
pointer: dict = _dict
35-
last_key_index: int = len(_keys) - 1
36-
for index in range(last_key_index):
37-
_item: Optional[object] = pointer.get(_keys[index])
38-
if isinstance(_item, dict):
39-
pointer = _item
40-
elif _item is None:
41-
if assumeKeyExists is True:
42-
EXCEPTION.fatal('Getting "KeyError" while trying to set keys {} to dict!'.format(_keys))
43-
pointer[_keys[index]] = {}
44-
pointer = pointer[_keys[index]]
45-
else:
46-
EXCEPTION.fatal("Getting not dict object {0} while trying to set keys {1} to dict!".format(_item, _keys))
47-
pointer[_keys[last_key_index]] = value
48-
49-
5016
# 配置文件管理模块
5117
class Config:
5218

@@ -93,7 +59,7 @@ def try_load_file_if_exists(cls, _path: str, _default: dict = {}) -> dict:
9359
# 加载配置文件,并根据key(s)返回对应的数据
9460
@classmethod
9561
def load(cls, path: str, *key: str) -> Any:
96-
return get_value_by_keys(cls.__load_file(path), key)
62+
return TypeSafeGetter.get_by_keys(cls.__load_file(path), key)
9763

9864
# 加载内部配置文件
9965
@classmethod
@@ -147,16 +113,60 @@ def resolve_path_and_load_file(cls, file_location: str) -> dict:
147113

148114

149115
# 使用引擎的开发者可以自定义的参数
150-
class Specification:
116+
class Specification(TypeSafeGetter):
151117

152118
__SPECIFICATIONS: Final[dict] = Config.load_internal_file("specifications.json")
153119
# 尝试加载项目自定义的参数
154120
__SPECIFICATIONS.update(Config.resolve_path_and_load_file(os.path.join("Data", "specifications")))
155121

156122
@classmethod
157-
def get(cls, *key: str) -> Any:
158-
return get_value_by_keys(cls.__SPECIFICATIONS, key)
123+
def _get_data(cls) -> dict:
124+
return cls.__SPECIFICATIONS
159125

160126
@classmethod
161127
def get_directory(cls, category: str, *_sub: str) -> str:
162128
return str(os.path.join(*cls.__SPECIFICATIONS["Directory"][category], *_sub))
129+
130+
131+
# 数据库
132+
class DataBase(TypeSafeGetter):
133+
134+
# 用于存放数据库数据的字典
135+
__DATA_BASE_DICT: Final[dict] = {"Tiles": {}, "Decorations": {}, "Npc": {}, "Filters": {}}
136+
137+
@classmethod
138+
def _get_data(cls) -> dict:
139+
return cls.__DATA_BASE_DICT
140+
141+
@classmethod
142+
def update(cls, _value: dict) -> None:
143+
for key, value in _value.items():
144+
if key not in cls.__DATA_BASE_DICT:
145+
cls.__DATA_BASE_DICT[key] = value
146+
else:
147+
cls.__DATA_BASE_DICT[key].update(value)
148+
149+
150+
# 全局数据
151+
class GlobalVariables(TypeSafeGetter, TypeSafeSetter):
152+
153+
# 用于存放全局数据的字典
154+
__GLOBAL_VARIABLES_DICT: Final[dict] = {}
155+
156+
@classmethod
157+
def _get_data(cls) -> dict:
158+
return cls.__GLOBAL_VARIABLES_DICT
159+
160+
# 删除特定的全局数据
161+
@classmethod
162+
def remove(cls, _key: str) -> None:
163+
cls.__GLOBAL_VARIABLES_DICT.pop(_key, None)
164+
165+
# 如果不是对应的值,则设置为对应的值,返回是否对应
166+
@classmethod
167+
def if_get_set(cls, _key: str, valueToGet: object, valueToSet: object) -> bool:
168+
if cls.__GLOBAL_VARIABLES_DICT[_key] == valueToGet:
169+
cls.__GLOBAL_VARIABLES_DICT[_key] = valueToSet
170+
return True
171+
else:
172+
return False

0 commit comments

Comments
 (0)