xenoslib 0.2.0__py3-none-any.whl → 0.3.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.
xenoslib/about.py CHANGED
@@ -4,4 +4,4 @@ __url__ = "https://github.com/XenosLu/xenoslib.git"
4
4
  __author__ = "Xenocider"
5
5
  __author_email__ = "xenos.lu@gmail.com"
6
6
  __license__ = "MIT License"
7
- __version__ = "0.2.0"
7
+ __version__ = "0.3.1"
xenoslib/base.py CHANGED
@@ -4,6 +4,51 @@ 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
+ backup_count: int = 0, # New parameter: number of log files to retain (0 means keep all)
16
+ ) -> logging.Logger:
17
+ """
18
+ Automatically names logger after caller's filename, rotates by time, and retains specified number of log files
19
+
20
+ Args:
21
+ use_file: Whether to enable file logging
22
+ backup_count: Number of historical log files to retain (default 0=keep all)
23
+ """
24
+ formatter = logging.Formatter(
25
+ "%(asctime)s %(filename)s %(levelname)s [line:%(lineno)d] %(message)s",
26
+ datefmt="%Y-%m-%d %H:%M:%S",
27
+ )
28
+ console_handler = logging.StreamHandler()
29
+ console_handler.setFormatter(formatter)
30
+ handlers = [console_handler]
31
+
32
+ # File handler (daily rotation + file retention policy)
33
+ if use_file:
34
+ # Dynamically get caller's filename
35
+ caller_frame = inspect.stack()[1]
36
+ caller_path = caller_frame.filename
37
+ caller_name = os.path.splitext(caller_path)[0]
38
+ # Configure log directory and filename
39
+ log_filename = f"{caller_name}.log"
40
+
41
+ file_handler = TimedRotatingFileHandler(
42
+ log_filename,
43
+ when="midnight", # Rotate daily
44
+ interval=1,
45
+ backupCount=backup_count, # Key parameter: controls number of retained files
46
+ encoding="utf-8",
47
+ )
48
+ file_handler.setFormatter(formatter)
49
+ handlers.append(file_handler)
50
+
51
+ logging.basicConfig(level=logging.INFO, handlers=handlers)
7
52
 
8
53
 
9
54
  def sleep(seconds, mute=False):
@@ -352,12 +397,6 @@ def get_attr_val(obj, *args):
352
397
 
353
398
 
354
399
  if __name__ == "__main__":
355
- data = {"a": 1, "b": {"c": 2, "d": [3, 4, {"e": 5}]}, "f": (6, 7, {"g": 8})}
356
- n = NestedData(data)
357
-
358
- from pprint import pprint
359
400
 
360
- pprint(list(n.search("b")))
361
- # pprint(list(n.search(1)))
362
- # n.show_result()
363
- # pprint(list(n.find(lambda k, v: "i" in v, ignore_exc=True)))
401
+ init_logger()
402
+ logging.info("test")
@@ -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
- f"Vault access required for {key_name} but Vault is not initialized"
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xenoslib
3
- Version: 0.2.0
3
+ Version: 0.3.1
4
4
  Summary: Xenos' common lib
5
5
  Home-page: https://github.com/XenosLu/xenoslib.git
6
6
  Author: Xenocider
@@ -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=uFeXGwBMEZ9Wsa7XjcqkicPpYbNWez7gMZ6AunNMQe4,228
4
- xenoslib/base.py,sha256=rAXk71AsnWvQ7MaSad5Xxd9Lwucp-cZc15yyqoV5Dk4,12440
3
+ xenoslib/about.py,sha256=XwzXx2OFQipiXMHLgQtLWtmVwxrewQAmeBxPwTZJwUg,228
4
+ xenoslib/base.py,sha256=Z0u5J0oZsjShj6dlSfNoYszFz7WbUSeTKqsxznDvB4Y,13766
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=Iuw7DiAb4z2mR3hFsZBQw9YGgGHUJvcNeT_Dj53NahM,11257
15
- xenoslib-0.2.0.dist-info/licenses/LICENSE,sha256=lF6hjufhiR-xAje_Wo7ogxV5phz2d5TgkfL5SGshujg,1066
16
- xenoslib-0.2.0.dist-info/METADATA,sha256=QtFP5XxADSObEVeou3ket3GfwWj6374GJqqBHJLPiMc,910
17
- xenoslib-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
18
- xenoslib-0.2.0.dist-info/top_level.txt,sha256=Rfm4GdW0NyA2AHDNUF2wFQOfAo53mAPibddSHGd3X60,9
19
- xenoslib-0.2.0.dist-info/RECORD,,
14
+ xenoslib/tools/config_loader.py,sha256=dHGqeRX-RvtBCdERsOYTYRIBD8nfYx04XRpps25ob_c,10947
15
+ xenoslib-0.3.1.dist-info/licenses/LICENSE,sha256=lF6hjufhiR-xAje_Wo7ogxV5phz2d5TgkfL5SGshujg,1066
16
+ xenoslib-0.3.1.dist-info/METADATA,sha256=eY0WJBZphYV9r7aDZzqn1osiELxwLBTJrIGfvEc74E4,910
17
+ xenoslib-0.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
18
+ xenoslib-0.3.1.dist-info/top_level.txt,sha256=Rfm4GdW0NyA2AHDNUF2wFQOfAo53mAPibddSHGd3X60,9
19
+ xenoslib-0.3.1.dist-info/RECORD,,