xenfra 0.3.5__py3-none-any.whl → 0.3.7__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.
xenfra/utils/config.py
CHANGED
|
@@ -29,18 +29,8 @@ def read_xenfra_yaml(filename: str = "xenfra.yaml") -> dict:
|
|
|
29
29
|
Raises:
|
|
30
30
|
FileNotFoundError: If the config file doesn't exist
|
|
31
31
|
yaml.YAMLError: If the YAML is malformed
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
Read and parse xenfra.yaml configuration file.
|
|
35
|
-
|
|
36
|
-
Args:
|
|
37
|
-
filename: Path to the config file (default: xenfra.yaml)
|
|
38
|
-
|
|
39
|
-
Returns:
|
|
40
|
-
Dictionary containing the configuration
|
|
41
|
-
|
|
42
|
-
Raises:
|
|
43
|
-
FileNotFoundError: If the config file doesn't exist
|
|
32
|
+
ValueError: If the YAML is invalid
|
|
33
|
+
IOError: If reading fails
|
|
44
34
|
"""
|
|
45
35
|
if not Path(filename).exists():
|
|
46
36
|
raise FileNotFoundError(
|
|
@@ -162,17 +152,7 @@ def apply_patch(patch: dict, target_file: str = None, create_backup_file: bool =
|
|
|
162
152
|
raise ValueError(
|
|
163
153
|
f"Invalid patch operation: {operation}. Must be 'add', 'replace', or 'remove'"
|
|
164
154
|
)
|
|
165
|
-
"""
|
|
166
|
-
Apply a JSON patch to a configuration file with automatic backup.
|
|
167
155
|
|
|
168
|
-
Args:
|
|
169
|
-
patch: Patch object with file, operation, path, value
|
|
170
|
-
target_file: Optional override for the file to patch
|
|
171
|
-
create_backup_file: Whether to create a backup before patching (default: True)
|
|
172
|
-
|
|
173
|
-
Returns:
|
|
174
|
-
Path to the backup file if created, None otherwise
|
|
175
|
-
"""
|
|
176
156
|
file_to_patch = target_file or patch.get("file")
|
|
177
157
|
|
|
178
158
|
if not file_to_patch:
|
|
@@ -193,7 +173,7 @@ def apply_patch(patch: dict, target_file: str = None, create_backup_file: bool =
|
|
|
193
173
|
|
|
194
174
|
# Apply patch based on operation
|
|
195
175
|
operation = patch.get("operation")
|
|
196
|
-
path = patch.get("path"
|
|
176
|
+
path = (patch.get("path") or "").strip("/")
|
|
197
177
|
value = patch.get("value")
|
|
198
178
|
|
|
199
179
|
if operation == "add":
|
|
@@ -235,7 +215,7 @@ def apply_patch(patch: dict, target_file: str = None, create_backup_file: bool =
|
|
|
235
215
|
config_data = json.load(f)
|
|
236
216
|
|
|
237
217
|
operation = patch.get("operation")
|
|
238
|
-
path = patch.get("path"
|
|
218
|
+
path = (patch.get("path") or "").strip("/")
|
|
239
219
|
value = patch.get("value")
|
|
240
220
|
|
|
241
221
|
if operation == "add":
|
|
@@ -280,6 +260,68 @@ def apply_patch(patch: dict, target_file: str = None, create_backup_file: bool =
|
|
|
280
260
|
# Replace entire file
|
|
281
261
|
with open(file_to_patch, "w") as f:
|
|
282
262
|
f.write(str(value))
|
|
263
|
+
|
|
264
|
+
# For TOML files (pyproject.toml)
|
|
265
|
+
elif file_to_patch.endswith(".toml"):
|
|
266
|
+
import toml
|
|
267
|
+
|
|
268
|
+
with open(file_to_patch, "r") as f:
|
|
269
|
+
config_data = toml.load(f)
|
|
270
|
+
|
|
271
|
+
operation = patch.get("operation")
|
|
272
|
+
path = (patch.get("path") or "").strip("/")
|
|
273
|
+
value = patch.get("value")
|
|
274
|
+
|
|
275
|
+
if operation == "add":
|
|
276
|
+
# Special case for pyproject.toml dependencies
|
|
277
|
+
is_pyproject = os.path.basename(file_to_patch) == "pyproject.toml"
|
|
278
|
+
if is_pyproject and (not path or path == "project/dependencies"):
|
|
279
|
+
# Ensure project and dependencies exist
|
|
280
|
+
if "project" not in config_data:
|
|
281
|
+
config_data["project"] = {}
|
|
282
|
+
if "dependencies" not in config_data["project"]:
|
|
283
|
+
config_data["project"]["dependencies"] = []
|
|
284
|
+
|
|
285
|
+
# Add value if not already present
|
|
286
|
+
if value not in config_data["project"]["dependencies"]:
|
|
287
|
+
config_data["project"]["dependencies"].append(value)
|
|
288
|
+
elif path:
|
|
289
|
+
path_parts = path.split("/")
|
|
290
|
+
current = config_data
|
|
291
|
+
for part in path_parts[:-1]:
|
|
292
|
+
if part not in current:
|
|
293
|
+
current[part] = {}
|
|
294
|
+
current = current[part]
|
|
295
|
+
|
|
296
|
+
# If target is a list (like dependencies), append
|
|
297
|
+
target_key = path_parts[-1]
|
|
298
|
+
if target_key in current and isinstance(current[target_key], list):
|
|
299
|
+
if value not in current[target_key]:
|
|
300
|
+
current[target_key].append(value)
|
|
301
|
+
else:
|
|
302
|
+
current[target_key] = value
|
|
303
|
+
else:
|
|
304
|
+
# Root level add
|
|
305
|
+
if isinstance(value, dict):
|
|
306
|
+
config_data.update(value)
|
|
307
|
+
else:
|
|
308
|
+
# Ignore root-level non-dict adds for structured files
|
|
309
|
+
# to prevent overwriting the entire config with a string
|
|
310
|
+
pass
|
|
311
|
+
|
|
312
|
+
elif operation == "replace":
|
|
313
|
+
if path:
|
|
314
|
+
path_parts = path.split("/")
|
|
315
|
+
current = config_data
|
|
316
|
+
for part in path_parts[:-1]:
|
|
317
|
+
current = current[part]
|
|
318
|
+
current[path_parts[-1]] = value
|
|
319
|
+
else:
|
|
320
|
+
config_data = value
|
|
321
|
+
|
|
322
|
+
# Write back
|
|
323
|
+
with open(file_to_patch, "w") as f:
|
|
324
|
+
toml.dump(config_data, f)
|
|
283
325
|
else:
|
|
284
326
|
# Design decision: Only support auto-patching for common dependency files
|
|
285
327
|
# Other file types should be manually edited to avoid data loss
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: xenfra
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.7
|
|
4
4
|
Summary: A 'Zen Mode' infrastructure engine for Python developers.
|
|
5
5
|
Author: xenfra-cloud
|
|
6
6
|
Author-email: xenfra-cloud <xenfracloud@gmail.com>
|
|
@@ -24,6 +24,7 @@ Requires-Dist: keyring>=25.7.0
|
|
|
24
24
|
Requires-Dist: keyrings-alt>=5.0.2
|
|
25
25
|
Requires-Dist: tenacity>=8.2.3
|
|
26
26
|
Requires-Dist: cryptography>=43.0.0
|
|
27
|
+
Requires-Dist: toml>=0.10.2
|
|
27
28
|
Requires-Dist: pytest>=8.0.0 ; extra == 'test'
|
|
28
29
|
Requires-Dist: pytest-mock>=3.12.0 ; extra == 'test'
|
|
29
30
|
Requires-Python: >=3.13
|
|
@@ -10,11 +10,11 @@ xenfra/main.py,sha256=2EPPuIdxjhW-I-e-Mc0i2ayeLaSJdmzddNThkXq7B7c,2033
|
|
|
10
10
|
xenfra/utils/__init__.py,sha256=4ZRYkrb--vzoXjBHG8zRxz2jCXNGtAoKNtkyu2WRI2A,45
|
|
11
11
|
xenfra/utils/auth.py,sha256=9JbFnv0-rdlJF-4hKD2uWd9h9ehqC1iIHee1O5e-3RM,13769
|
|
12
12
|
xenfra/utils/codebase.py,sha256=GMrqhOJWX8q5ZXSLI9P3hJZBpufXMQA3Z4fKh2XSTNo,5949
|
|
13
|
-
xenfra/utils/config.py,sha256=
|
|
13
|
+
xenfra/utils/config.py,sha256=BylyzHLkL6rmvNNW9zxCaSvk1dV0yzJQPaW-dP7E5j0,13931
|
|
14
14
|
xenfra/utils/errors.py,sha256=6G91YzzDDNkKHANTgfAMiOiMElEyi57wo6-FzRa4VuQ,4211
|
|
15
15
|
xenfra/utils/security.py,sha256=EA8CIPLt8Y-QP5uZ7c5NuC6ZLRV1aZS8NapS9ix_vok,11479
|
|
16
16
|
xenfra/utils/validation.py,sha256=cvuL_AEFJ2oCoP0abCqoOIABOwz79Gkf-jh_dcFIQlM,6912
|
|
17
|
-
xenfra-0.3.
|
|
18
|
-
xenfra-0.3.
|
|
19
|
-
xenfra-0.3.
|
|
20
|
-
xenfra-0.3.
|
|
17
|
+
xenfra-0.3.7.dist-info/WHEEL,sha256=KSLUh82mDPEPk0Bx0ScXlWL64bc8KmzIPNcpQZFV-6E,79
|
|
18
|
+
xenfra-0.3.7.dist-info/entry_points.txt,sha256=a_2cGhYK__X6eW05Ba8uB6RIM_61c2sHtXsPY8N0mic,45
|
|
19
|
+
xenfra-0.3.7.dist-info/METADATA,sha256=P2qLV0wwwrioPQtkXda94RA7FUOBSmKmzOooQCNiD8Q,3898
|
|
20
|
+
xenfra-0.3.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|