xenoslib 0.2.0__py3-none-any.whl → 0.3.0__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.
- xenoslib/about.py +1 -1
- xenoslib/base.py +62 -0
- xenoslib/tools/config_loader.py +29 -33
- {xenoslib-0.2.0.dist-info → xenoslib-0.3.0.dist-info}/METADATA +1 -1
- {xenoslib-0.2.0.dist-info → xenoslib-0.3.0.dist-info}/RECORD +8 -8
- {xenoslib-0.2.0.dist-info → xenoslib-0.3.0.dist-info}/WHEEL +0 -0
- {xenoslib-0.2.0.dist-info → xenoslib-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {xenoslib-0.2.0.dist-info → xenoslib-0.3.0.dist-info}/top_level.txt +0 -0
xenoslib/about.py
CHANGED
xenoslib/base.py
CHANGED
|
@@ -4,6 +4,68 @@ import argparse
|
|
|
4
4
|
import sys
|
|
5
5
|
import time
|
|
6
6
|
import inspect
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
|
|
10
|
+
from logging.handlers import TimedRotatingFileHandler
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def init_logger(
|
|
14
|
+
use_file: bool = True,
|
|
15
|
+
level: int = logging.INFO,
|
|
16
|
+
backup_count: int = 0, # New parameter: number of log files to retain (0 means keep all)
|
|
17
|
+
) -> logging.Logger:
|
|
18
|
+
"""
|
|
19
|
+
Automatically names logger after caller's filename, rotates by time, and retains specified number of log files
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
use_file: Whether to enable file logging
|
|
23
|
+
level: Logging level (DEBUG/INFO/WARN/ERROR)
|
|
24
|
+
backup_count: Number of historical log files to retain (default 0=keep all)
|
|
25
|
+
"""
|
|
26
|
+
# 1. Dynamically get caller's filename
|
|
27
|
+
caller_frame = inspect.stack()[1]
|
|
28
|
+
caller_path = caller_frame.filename
|
|
29
|
+
caller_name = os.path.splitext(os.path.basename(caller_path))[0]
|
|
30
|
+
|
|
31
|
+
# 2. Configure log directory and filename
|
|
32
|
+
log_dir = "logs"
|
|
33
|
+
os.makedirs(log_dir, exist_ok=True)
|
|
34
|
+
log_filename = f"{caller_name}.log"
|
|
35
|
+
full_path = os.path.join(log_dir, log_filename)
|
|
36
|
+
|
|
37
|
+
# 3. Avoid duplicate initialization
|
|
38
|
+
logger = logging.getLogger(caller_name)
|
|
39
|
+
if logger.hasHandlers():
|
|
40
|
+
return logger
|
|
41
|
+
|
|
42
|
+
logger.setLevel(level)
|
|
43
|
+
formatter = logging.Formatter(
|
|
44
|
+
"%(asctime)s [%(levelname)s] %(filename)s:%(lineno)d - %(message)s",
|
|
45
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# 4. Console handler (required)
|
|
49
|
+
console_handler = logging.StreamHandler()
|
|
50
|
+
console_handler.setFormatter(formatter)
|
|
51
|
+
handlers = [console_handler]
|
|
52
|
+
|
|
53
|
+
# 5. File handler (daily rotation + file retention policy)
|
|
54
|
+
if use_file:
|
|
55
|
+
file_handler = TimedRotatingFileHandler(
|
|
56
|
+
full_path,
|
|
57
|
+
when="midnight", # Rotate daily
|
|
58
|
+
interval=1,
|
|
59
|
+
backupCount=backup_count, # Key parameter: controls number of retained files
|
|
60
|
+
encoding="utf-8",
|
|
61
|
+
)
|
|
62
|
+
file_handler.setFormatter(formatter)
|
|
63
|
+
handlers.append(file_handler)
|
|
64
|
+
|
|
65
|
+
for handler in handlers:
|
|
66
|
+
logger.addHandler(handler)
|
|
67
|
+
|
|
68
|
+
return logger
|
|
7
69
|
|
|
8
70
|
|
|
9
71
|
def sleep(seconds, mute=False):
|
xenoslib/tools/config_loader.py
CHANGED
|
@@ -28,17 +28,17 @@ class ConfigLoader(SingletonWithArgs):
|
|
|
28
28
|
|
|
29
29
|
# With Vault (hvac imported on demand)
|
|
30
30
|
>>> config = ConfigLoader("config.yml", vault_secret_id="my-secret-id")
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
# Write to Vault using attribute style
|
|
33
33
|
>>> config.test_section.test_key = "new_value"
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
# Write to Vault using dictionary style
|
|
36
36
|
>>> config["test_section"]["test_key"] = "new_value"
|
|
37
37
|
"""
|
|
38
38
|
|
|
39
39
|
VAULT_SUFFIX = "@vault"
|
|
40
40
|
KV_MOUNT_POINT = "kv"
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
cache = {}
|
|
43
43
|
vault_client = None
|
|
44
44
|
|
|
@@ -47,7 +47,7 @@ class ConfigLoader(SingletonWithArgs):
|
|
|
47
47
|
with open(config_file_path, "r") as f:
|
|
48
48
|
config_data = yaml.safe_load(f)
|
|
49
49
|
self._raw_config = config_data if isinstance(config_data, dict) else {}
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
if vault_secret_id is not None:
|
|
52
52
|
self.vault_secret_id = vault_secret_id
|
|
53
53
|
self._check_and_renew_vault_client()
|
|
@@ -115,17 +115,15 @@ class ConfigLoader(SingletonWithArgs):
|
|
|
115
115
|
section_config = self._raw_config.get(section)
|
|
116
116
|
if section_config is None:
|
|
117
117
|
raise KeyError(f"Section '{section}' not found")
|
|
118
|
-
|
|
118
|
+
|
|
119
119
|
if not self._is_vault_reference(section_config, key_name):
|
|
120
120
|
raise KeyError(f"Key '{key_name}' is not a Vault reference in section '{section}'")
|
|
121
|
-
|
|
121
|
+
|
|
122
122
|
if self.vault_client is None:
|
|
123
|
-
raise Exception(
|
|
124
|
-
|
|
125
|
-
)
|
|
126
|
-
|
|
123
|
+
raise Exception(f"Vault access required for {key_name} but Vault is not initialized")
|
|
124
|
+
|
|
127
125
|
self._set_value_to_vault(section, key_name, value)
|
|
128
|
-
|
|
126
|
+
|
|
129
127
|
cache_key = f"{section}:{key_name}"
|
|
130
128
|
if use_cache:
|
|
131
129
|
self.cache[cache_key] = value
|
|
@@ -140,10 +138,10 @@ class ConfigLoader(SingletonWithArgs):
|
|
|
140
138
|
|
|
141
139
|
vault_key_ref = f"{key_name}{self.VAULT_SUFFIX}"
|
|
142
140
|
vault_key = section_config[vault_key_ref]
|
|
143
|
-
|
|
141
|
+
|
|
144
142
|
namespace = section_config.get("vault_namespace") or self._raw_config["vault"]["space"]
|
|
145
143
|
self.vault_client.adapter.namespace = namespace
|
|
146
|
-
|
|
144
|
+
|
|
147
145
|
data = self.vault_client.secrets.kv.read_secret_version(
|
|
148
146
|
path=vault_path, mount_point=self.KV_MOUNT_POINT, raise_on_deleted_version=True
|
|
149
147
|
)
|
|
@@ -155,18 +153,18 @@ class ConfigLoader(SingletonWithArgs):
|
|
|
155
153
|
"""Set a secret value to Vault."""
|
|
156
154
|
try:
|
|
157
155
|
self._check_and_renew_vault_client()
|
|
158
|
-
|
|
156
|
+
|
|
159
157
|
section_config = self._raw_config[section]
|
|
160
158
|
vault_path = section_config.get("vault_path")
|
|
161
159
|
if not vault_path:
|
|
162
160
|
raise KeyError(f"Missing vault_path in section '{section}'")
|
|
163
|
-
|
|
161
|
+
|
|
164
162
|
vault_key_ref = f"{key_name}{self.VAULT_SUFFIX}"
|
|
165
163
|
vault_key = section_config[vault_key_ref]
|
|
166
|
-
|
|
164
|
+
|
|
167
165
|
namespace = section_config.get("vault_namespace") or self._raw_config["vault"]["space"]
|
|
168
166
|
self.vault_client.adapter.namespace = namespace
|
|
169
|
-
|
|
167
|
+
|
|
170
168
|
try:
|
|
171
169
|
data = self.vault_client.secrets.kv.read_secret_version(
|
|
172
170
|
path=vault_path, mount_point=self.KV_MOUNT_POINT, raise_on_deleted_version=True
|
|
@@ -175,15 +173,13 @@ class ConfigLoader(SingletonWithArgs):
|
|
|
175
173
|
except Exception:
|
|
176
174
|
logger.warning(f"Secret not found, creating new secret at {vault_path}")
|
|
177
175
|
secret_data = {}
|
|
178
|
-
|
|
176
|
+
|
|
179
177
|
secret_data[vault_key] = value
|
|
180
|
-
|
|
178
|
+
|
|
181
179
|
self.vault_client.secrets.kv.create_or_update_secret(
|
|
182
|
-
path=vault_path,
|
|
183
|
-
secret=secret_data,
|
|
184
|
-
mount_point=self.KV_MOUNT_POINT
|
|
180
|
+
path=vault_path, secret=secret_data, mount_point=self.KV_MOUNT_POINT
|
|
185
181
|
)
|
|
186
|
-
|
|
182
|
+
|
|
187
183
|
logger.info(f"Updated Vault secret: {vault_path}/{vault_key}")
|
|
188
184
|
except Exception as e:
|
|
189
185
|
raise Exception(f"Failed to set {key_name} to Vault: {str(e)}") from e
|
|
@@ -216,7 +212,7 @@ class SectionProxy:
|
|
|
216
212
|
def __getitem__(self, key):
|
|
217
213
|
"""Dictionary-style access to configuration values."""
|
|
218
214
|
return self._loader.get(self._section, key)
|
|
219
|
-
|
|
215
|
+
|
|
220
216
|
def __setitem__(self, key, value):
|
|
221
217
|
"""Dictionary-style setting of configuration values to Vault."""
|
|
222
218
|
self.set(key, value)
|
|
@@ -227,7 +223,7 @@ class SectionProxy:
|
|
|
227
223
|
return self._loader.get(self._section, key)
|
|
228
224
|
except KeyError:
|
|
229
225
|
return default
|
|
230
|
-
|
|
226
|
+
|
|
231
227
|
def set(self, key, value):
|
|
232
228
|
"""Set a configuration value to Vault."""
|
|
233
229
|
self._loader.set(self._section, key, value)
|
|
@@ -238,10 +234,10 @@ class SectionProxy:
|
|
|
238
234
|
return self[key]
|
|
239
235
|
except KeyError as e:
|
|
240
236
|
raise AttributeError(str(e))
|
|
241
|
-
|
|
237
|
+
|
|
242
238
|
def __setattr__(self, name, value):
|
|
243
239
|
"""Attribute-style setting of configuration values to Vault."""
|
|
244
|
-
if name.startswith(
|
|
240
|
+
if name.startswith("_"):
|
|
245
241
|
super().__setattr__(name, value)
|
|
246
242
|
else:
|
|
247
243
|
self.set(name, value)
|
|
@@ -259,13 +255,13 @@ if __name__ == "__main__":
|
|
|
259
255
|
|
|
260
256
|
# 属性方式读取
|
|
261
257
|
print("With Vault (attr):", config_with_vault.test.test)
|
|
262
|
-
|
|
258
|
+
|
|
263
259
|
# 字典方式读取
|
|
264
260
|
print("With Vault (dict):", config_with_vault["cis"]["cis_client_id"])
|
|
265
|
-
|
|
261
|
+
|
|
266
262
|
# 测试不存在的值
|
|
267
263
|
print("Try val not exists: ", config_with_vault.test.get("not_exists"))
|
|
268
|
-
|
|
264
|
+
|
|
269
265
|
# 写入示例 - 属性方式
|
|
270
266
|
try:
|
|
271
267
|
print("Current test value (attr read):", config_with_vault.test.test)
|
|
@@ -273,18 +269,18 @@ if __name__ == "__main__":
|
|
|
273
269
|
print("After write (attr read):", config_with_vault.test.test)
|
|
274
270
|
except Exception as e:
|
|
275
271
|
print("Attribute write failed:", str(e))
|
|
276
|
-
|
|
272
|
+
|
|
277
273
|
# 写入示例 - 字典方式
|
|
278
274
|
try:
|
|
279
275
|
print("Current test value (dict read):", config_with_vault["test"]["test"])
|
|
280
276
|
config_with_vault["test"]["test"] = "new_value_456"
|
|
281
277
|
print("After write (dict read):", config_with_vault["test"]["test"])
|
|
282
|
-
|
|
278
|
+
|
|
283
279
|
# 混合方式验证
|
|
284
280
|
print("After write (attr read):", config_with_vault.test.test)
|
|
285
281
|
except Exception as e:
|
|
286
282
|
print("Dictionary write failed:", str(e))
|
|
287
|
-
|
|
283
|
+
|
|
288
284
|
# 写入新键示例
|
|
289
285
|
try:
|
|
290
286
|
print("Setting new key...")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
xenoslib/__init__.py,sha256=vlMWwdD2gTb9ztpm_orYAm_Y0Be0Es64ifvMQwrE1jU,310
|
|
2
2
|
xenoslib/__main__.py,sha256=UpD3pl4l5ZUFxK6FzNMWRb1AZpEnwerulSFoPb_Pdgw,307
|
|
3
|
-
xenoslib/about.py,sha256=
|
|
4
|
-
xenoslib/base.py,sha256=
|
|
3
|
+
xenoslib/about.py,sha256=JOPWsSbcxv9ls6wxojlF88auWvYf0Ok4vc4yFIolu-Y,228
|
|
4
|
+
xenoslib/base.py,sha256=2qHsptnf_EAaoezr5aQwM6gdPEGnAPn8ommRYtOhD1c,14428
|
|
5
5
|
xenoslib/dev.py,sha256=R6iwKuu-xvaYiBOUyP2gpePyvXss17TOZaCmQEihPCw,1236
|
|
6
6
|
xenoslib/extend.py,sha256=Vl5d8carSGy6ERE2EjTmB9lOo2uIBWs1iz-fKm_TX-M,5947
|
|
7
7
|
xenoslib/linux.py,sha256=EFCQBq_JsmNJc3wGlJlzH9W0tjOA7sSwx0u62R5Ut2k,1318
|
|
@@ -11,9 +11,9 @@ xenoslib/onedrive.py,sha256=-bJJ8Cd_RjJSlDynYqKoZlFKE1HHM34l6NXOQrWOwtg,7783
|
|
|
11
11
|
xenoslib/win_trayicon.py,sha256=7GJwX3c2CS1XWQjsyDK5EfM-MmEHdzPJCTX2sGpWmuU,13115
|
|
12
12
|
xenoslib/windows.py,sha256=lUTD7TowaPqYHgIL6b-GM9PLd-VyJNNGzkzC3K9vOxo,4080
|
|
13
13
|
xenoslib/tools/__init__.py,sha256=D1qP5tD2ZukXHgjKUh0fFl8jUipM3KaPBZBi0vGPypQ,48
|
|
14
|
-
xenoslib/tools/config_loader.py,sha256=
|
|
15
|
-
xenoslib-0.
|
|
16
|
-
xenoslib-0.
|
|
17
|
-
xenoslib-0.
|
|
18
|
-
xenoslib-0.
|
|
19
|
-
xenoslib-0.
|
|
14
|
+
xenoslib/tools/config_loader.py,sha256=dHGqeRX-RvtBCdERsOYTYRIBD8nfYx04XRpps25ob_c,10947
|
|
15
|
+
xenoslib-0.3.0.dist-info/licenses/LICENSE,sha256=lF6hjufhiR-xAje_Wo7ogxV5phz2d5TgkfL5SGshujg,1066
|
|
16
|
+
xenoslib-0.3.0.dist-info/METADATA,sha256=YjQwsxquDj_F_rfl9-GiRVsWRHxmtDE7yh7Q73dKj6g,910
|
|
17
|
+
xenoslib-0.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
18
|
+
xenoslib-0.3.0.dist-info/top_level.txt,sha256=Rfm4GdW0NyA2AHDNUF2wFQOfAo53mAPibddSHGd3X60,9
|
|
19
|
+
xenoslib-0.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|