ckparser 0.3.1__tar.gz → 0.3.3__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.
- {ckparser-0.3.1/ckparser.egg-info → ckparser-0.3.3}/PKG-INFO +28 -4
- {ckparser-0.3.1 → ckparser-0.3.3}/README.md +27 -3
- {ckparser-0.3.1 → ckparser-0.3.3/ckparser.egg-info}/PKG-INFO +28 -4
- {ckparser-0.3.1 → ckparser-0.3.3}/ckparser.py +116 -33
- {ckparser-0.3.1 → ckparser-0.3.3}/pyproject.toml +1 -1
- {ckparser-0.3.1 → ckparser-0.3.3}/LICENSE +0 -0
- {ckparser-0.3.1 → ckparser-0.3.3}/MANIFEST.in +0 -0
- {ckparser-0.3.1 → ckparser-0.3.3}/ckparser.egg-info/SOURCES.txt +0 -0
- {ckparser-0.3.1 → ckparser-0.3.3}/ckparser.egg-info/dependency_links.txt +0 -0
- {ckparser-0.3.1 → ckparser-0.3.3}/ckparser.egg-info/entry_points.txt +0 -0
- {ckparser-0.3.1 → ckparser-0.3.3}/ckparser.egg-info/requires.txt +0 -0
- {ckparser-0.3.1 → ckparser-0.3.3}/ckparser.egg-info/top_level.txt +0 -0
- {ckparser-0.3.1 → ckparser-0.3.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ckparser
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: Parse Paradox Jomini data files to Python/JSON and revert them back experimentally.
|
|
5
5
|
Author: Marc Debureaux (debnet)
|
|
6
6
|
License-Expression: MIT
|
|
@@ -284,6 +284,29 @@ from ckparser import walk
|
|
|
284
284
|
for value, path in walk(data):
|
|
285
285
|
print(path, value)
|
|
286
286
|
```
|
|
287
|
+
|
|
288
|
+
### `objectify`
|
|
289
|
+
|
|
290
|
+
Transform a dictionary datastructure into an object-like dictionary which allows a better inspection.
|
|
291
|
+
|
|
292
|
+
```python
|
|
293
|
+
from ckparser import objectify
|
|
294
|
+
|
|
295
|
+
data = {
|
|
296
|
+
"name": "Louis",
|
|
297
|
+
"traits": [
|
|
298
|
+
"brave",
|
|
299
|
+
"diligent"
|
|
300
|
+
],
|
|
301
|
+
"father": {
|
|
302
|
+
"name": "Charles"
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
obj = objectify(data)
|
|
307
|
+
print(obj.traits[1])
|
|
308
|
+
print(obj.father.name)
|
|
309
|
+
```
|
|
287
310
|
|
|
288
311
|
### `parse_all_locales`
|
|
289
312
|
|
|
@@ -518,9 +541,9 @@ Paradox scripting languages include many practical exceptions, formatting varian
|
|
|
518
541
|
|
|
519
542
|
### Parsing
|
|
520
543
|
|
|
521
|
-
* `parse_text(text, return_text_on_error=False, comments=False, dates=False, filename=None, is_global=False)`
|
|
522
|
-
* `parse_file(path, output_dir=None, encoding="utf_8_sig", base_dir=None, save=False, comments=False, dates=False, is_global=False, patch=None)`
|
|
523
|
-
* `parse_all_files(path, output_dir=None, encoding="utf_8_sig", keep_data=False, save=False, comments=False, dates=False, variables_first=True)`
|
|
544
|
+
* `parse_text(text, return_text_on_error=False, comments=False, dates=False, as_object=False, filename=None, is_global=False)`
|
|
545
|
+
* `parse_file(path, output_dir=None, encoding="utf_8_sig", base_dir=None, save=False, comments=False, dates=False, as_object=False, is_global=False, patch=None)`
|
|
546
|
+
* `parse_all_files(path, output_dir=None, encoding="utf_8_sig", keep_data=False, save=False, comments=False, dates=False, as_object=False, variables_first=True)`
|
|
524
547
|
|
|
525
548
|
### Reversion
|
|
526
549
|
|
|
@@ -533,6 +556,7 @@ Paradox scripting languages include many practical exceptions, formatting varian
|
|
|
533
556
|
* `convert_date(date, key=None)`
|
|
534
557
|
* `read_file(path, encoding="utf_8_sig")`
|
|
535
558
|
* `walk(obj, *from_keys)`
|
|
559
|
+
* `objectify(item, name="Object", default=None)`
|
|
536
560
|
* `parse_all_locales(path, encoding="utf_8_sig", language="english", save=False)`
|
|
537
561
|
* `load_variables(filepath="_variables.json")`
|
|
538
562
|
* `save_variables(filepath="_variables.json")`
|
|
@@ -253,6 +253,29 @@ from ckparser import walk
|
|
|
253
253
|
for value, path in walk(data):
|
|
254
254
|
print(path, value)
|
|
255
255
|
```
|
|
256
|
+
|
|
257
|
+
### `objectify`
|
|
258
|
+
|
|
259
|
+
Transform a dictionary datastructure into an object-like dictionary which allows a better inspection.
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
from ckparser import objectify
|
|
263
|
+
|
|
264
|
+
data = {
|
|
265
|
+
"name": "Louis",
|
|
266
|
+
"traits": [
|
|
267
|
+
"brave",
|
|
268
|
+
"diligent"
|
|
269
|
+
],
|
|
270
|
+
"father": {
|
|
271
|
+
"name": "Charles"
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
obj = objectify(data)
|
|
276
|
+
print(obj.traits[1])
|
|
277
|
+
print(obj.father.name)
|
|
278
|
+
```
|
|
256
279
|
|
|
257
280
|
### `parse_all_locales`
|
|
258
281
|
|
|
@@ -487,9 +510,9 @@ Paradox scripting languages include many practical exceptions, formatting varian
|
|
|
487
510
|
|
|
488
511
|
### Parsing
|
|
489
512
|
|
|
490
|
-
* `parse_text(text, return_text_on_error=False, comments=False, dates=False, filename=None, is_global=False)`
|
|
491
|
-
* `parse_file(path, output_dir=None, encoding="utf_8_sig", base_dir=None, save=False, comments=False, dates=False, is_global=False, patch=None)`
|
|
492
|
-
* `parse_all_files(path, output_dir=None, encoding="utf_8_sig", keep_data=False, save=False, comments=False, dates=False, variables_first=True)`
|
|
513
|
+
* `parse_text(text, return_text_on_error=False, comments=False, dates=False, as_object=False, filename=None, is_global=False)`
|
|
514
|
+
* `parse_file(path, output_dir=None, encoding="utf_8_sig", base_dir=None, save=False, comments=False, dates=False, as_object=False, is_global=False, patch=None)`
|
|
515
|
+
* `parse_all_files(path, output_dir=None, encoding="utf_8_sig", keep_data=False, save=False, comments=False, dates=False, as_object=False, variables_first=True)`
|
|
493
516
|
|
|
494
517
|
### Reversion
|
|
495
518
|
|
|
@@ -502,6 +525,7 @@ Paradox scripting languages include many practical exceptions, formatting varian
|
|
|
502
525
|
* `convert_date(date, key=None)`
|
|
503
526
|
* `read_file(path, encoding="utf_8_sig")`
|
|
504
527
|
* `walk(obj, *from_keys)`
|
|
528
|
+
* `objectify(item, name="Object", default=None)`
|
|
505
529
|
* `parse_all_locales(path, encoding="utf_8_sig", language="english", save=False)`
|
|
506
530
|
* `load_variables(filepath="_variables.json")`
|
|
507
531
|
* `save_variables(filepath="_variables.json")`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ckparser
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: Parse Paradox Jomini data files to Python/JSON and revert them back experimentally.
|
|
5
5
|
Author: Marc Debureaux (debnet)
|
|
6
6
|
License-Expression: MIT
|
|
@@ -284,6 +284,29 @@ from ckparser import walk
|
|
|
284
284
|
for value, path in walk(data):
|
|
285
285
|
print(path, value)
|
|
286
286
|
```
|
|
287
|
+
|
|
288
|
+
### `objectify`
|
|
289
|
+
|
|
290
|
+
Transform a dictionary datastructure into an object-like dictionary which allows a better inspection.
|
|
291
|
+
|
|
292
|
+
```python
|
|
293
|
+
from ckparser import objectify
|
|
294
|
+
|
|
295
|
+
data = {
|
|
296
|
+
"name": "Louis",
|
|
297
|
+
"traits": [
|
|
298
|
+
"brave",
|
|
299
|
+
"diligent"
|
|
300
|
+
],
|
|
301
|
+
"father": {
|
|
302
|
+
"name": "Charles"
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
obj = objectify(data)
|
|
307
|
+
print(obj.traits[1])
|
|
308
|
+
print(obj.father.name)
|
|
309
|
+
```
|
|
287
310
|
|
|
288
311
|
### `parse_all_locales`
|
|
289
312
|
|
|
@@ -518,9 +541,9 @@ Paradox scripting languages include many practical exceptions, formatting varian
|
|
|
518
541
|
|
|
519
542
|
### Parsing
|
|
520
543
|
|
|
521
|
-
* `parse_text(text, return_text_on_error=False, comments=False, dates=False, filename=None, is_global=False)`
|
|
522
|
-
* `parse_file(path, output_dir=None, encoding="utf_8_sig", base_dir=None, save=False, comments=False, dates=False, is_global=False, patch=None)`
|
|
523
|
-
* `parse_all_files(path, output_dir=None, encoding="utf_8_sig", keep_data=False, save=False, comments=False, dates=False, variables_first=True)`
|
|
544
|
+
* `parse_text(text, return_text_on_error=False, comments=False, dates=False, as_object=False, filename=None, is_global=False)`
|
|
545
|
+
* `parse_file(path, output_dir=None, encoding="utf_8_sig", base_dir=None, save=False, comments=False, dates=False, as_object=False, is_global=False, patch=None)`
|
|
546
|
+
* `parse_all_files(path, output_dir=None, encoding="utf_8_sig", keep_data=False, save=False, comments=False, dates=False, as_object=False, variables_first=True)`
|
|
524
547
|
|
|
525
548
|
### Reversion
|
|
526
549
|
|
|
@@ -533,6 +556,7 @@ Paradox scripting languages include many practical exceptions, formatting varian
|
|
|
533
556
|
* `convert_date(date, key=None)`
|
|
534
557
|
* `read_file(path, encoding="utf_8_sig")`
|
|
535
558
|
* `walk(obj, *from_keys)`
|
|
559
|
+
* `objectify(item, name="Object", default=None)`
|
|
536
560
|
* `parse_all_locales(path, encoding="utf_8_sig", language="english", save=False)`
|
|
537
561
|
* `load_variables(filepath="_variables.json")`
|
|
538
562
|
* `save_variables(filepath="_variables.json")`
|
|
@@ -41,7 +41,7 @@ import re
|
|
|
41
41
|
import time
|
|
42
42
|
|
|
43
43
|
# Script version
|
|
44
|
-
__version__ = "0.3.
|
|
44
|
+
__version__ = "0.3.3"
|
|
45
45
|
|
|
46
46
|
# Logger (because logging is awesome)
|
|
47
47
|
logger = logging.getLogger(__name__)
|
|
@@ -104,9 +104,11 @@ regex_inner_string = re.compile(r"\|(?P<index>\d+)\|")
|
|
|
104
104
|
# Regex to remove comments in files
|
|
105
105
|
regex_comment = re.compile(r"(?P<space>\s*)(?P<comment>#.*)", re.MULTILINE)
|
|
106
106
|
# Regex to fix blocks with no equal sign
|
|
107
|
-
regex_missing = re.compile(r"
|
|
107
|
+
regex_missing = re.compile(r"^(\s*)([\w\.]+)\s+([{])", re.MULTILINE)
|
|
108
108
|
# Regex to remove "list" prefix
|
|
109
109
|
regex_list = re.compile(r"\s*=\s*list\s+([\{\"\|])", re.MULTILINE)
|
|
110
|
+
# Regex to split brackets on new lines
|
|
111
|
+
regex_jump = re.compile(r"(\s*)([{}])")
|
|
110
112
|
# Regex for color blocks (color = [rgb|hsv] { x y z })
|
|
111
113
|
regex_color = re.compile(r"=\s*(?P<type>\w+)\s*{", re.MULTILINE)
|
|
112
114
|
# Regex to parse items with format key=value
|
|
@@ -118,7 +120,7 @@ regex_line = re.compile(r"\"?(?P<key>[^\s\"]+)\"?\s*(?P<operator>[?!<=>]+)\s*(li
|
|
|
118
120
|
# Regex to parse independent items in a list
|
|
119
121
|
regex_item = re.compile(r"(\"[^\"]+\"|[\d\.]+|[^\s]+)")
|
|
120
122
|
# Regex to parse dates
|
|
121
|
-
regex_date = re.compile(r"^(
|
|
123
|
+
regex_date = re.compile(r"^(?P<year>\d{1,4})\.(?P<month>[1-9]|1[0-2])\.(?P<day>[1-9]|[12]\d|3[01])$")
|
|
122
124
|
# Regex to remove empty lines
|
|
123
125
|
regex_empty = re.compile(r"(\n\s*\n)+", re.MULTILINE)
|
|
124
126
|
# Regex to parse locale files
|
|
@@ -129,6 +131,8 @@ regex_keyword = re.compile(r"(" + "|".join(map(re.escape, sorted(keywords, key=l
|
|
|
129
131
|
regex_count = re.compile(r"([?!<=>]+)\s*([§\n]+)\s*\{")
|
|
130
132
|
# Regex for string indexes
|
|
131
133
|
regex_index = re.compile(r"\|(\d+)\|")
|
|
134
|
+
# Regex to clean line numbers
|
|
135
|
+
regex_clean = re.compile(r"^§+\n", re.MULTILINE)
|
|
132
136
|
# Regex for variables
|
|
133
137
|
regex_variables = re.compile(r"\b([\w]+)\b")
|
|
134
138
|
|
|
@@ -188,14 +192,23 @@ def read_file(path, encoding="utf_8_sig"):
|
|
|
188
192
|
return file.read()
|
|
189
193
|
|
|
190
194
|
|
|
191
|
-
def parse_text(
|
|
195
|
+
def parse_text(
|
|
196
|
+
text,
|
|
197
|
+
return_text_on_error=False,
|
|
198
|
+
comments=False,
|
|
199
|
+
dates=False,
|
|
200
|
+
as_object=False,
|
|
201
|
+
filename=None,
|
|
202
|
+
is_global=False,
|
|
203
|
+
):
|
|
192
204
|
"""
|
|
193
205
|
Parse raw text
|
|
194
206
|
:param text: Text to parse
|
|
195
207
|
:param return_text_on_error: (default false) Return working text document if parsing fails
|
|
196
208
|
:param comments: (default false) Include comments?
|
|
197
209
|
:param dates: (default false) Include dates?
|
|
198
|
-
:param
|
|
210
|
+
:param as_object: (default false) Return data as an object-like instead of dict
|
|
211
|
+
:param filename: (default none) file which is parsed (only for debugging)
|
|
199
212
|
:param is_global: (default false) Are variables global?
|
|
200
213
|
:return: Parsed data as dictionary
|
|
201
214
|
"""
|
|
@@ -238,12 +251,12 @@ def parse_text(text, return_text_on_error=False, comments=False, dates=False, fi
|
|
|
238
251
|
if missings := regex_missing.findall(text):
|
|
239
252
|
if filename:
|
|
240
253
|
logger.warning(f"Filename: {filename}")
|
|
241
|
-
for key, val in missings:
|
|
254
|
+
for _, key, val in missings:
|
|
242
255
|
logger.warning(f"Potential missing `=` operator between `{key}` and `{val}` needs to be fixed.")
|
|
243
|
-
text = regex_missing.sub(r"\g<1>=\g<
|
|
256
|
+
text = regex_missing.sub(r"\g<1>\g<2>=\g<3>", text)
|
|
244
257
|
text = regex_color.sub(r"={\n\g<1>", text)
|
|
245
258
|
text = regex_list.sub(r"|list=\g<1>", text)
|
|
246
|
-
text =
|
|
259
|
+
text = regex_jump.sub(r"\n\g<1>\g<2>\n", text)
|
|
247
260
|
text = regex_inline.sub(r"\g<1>\n", text)
|
|
248
261
|
text = regex_block.sub(r"\g<2>\g<4>{", text)
|
|
249
262
|
text = regex_empty.sub(r"\n", text)
|
|
@@ -418,10 +431,14 @@ def parse_text(text, return_text_on_error=False, comments=False, dates=False, fi
|
|
|
418
431
|
nodes.pop()
|
|
419
432
|
# If line is a list or list item
|
|
420
433
|
else:
|
|
434
|
+
# Assuming the parsed file is just an array
|
|
435
|
+
if len(nodes) == 1 and not node:
|
|
436
|
+
root = node = []
|
|
437
|
+
nodes = [("", root)]
|
|
421
438
|
# Ensure previous data are treated as list
|
|
422
439
|
if not isinstance(node, list):
|
|
423
440
|
if len(nodes) < 2:
|
|
424
|
-
raise SyntaxError("Incorrect file format or syntax!")
|
|
441
|
+
raise SyntaxError("Incorrect file format or unknown syntax error!")
|
|
425
442
|
_, prev = nodes[-2]
|
|
426
443
|
if node_name:
|
|
427
444
|
if node and isinstance(node, dict):
|
|
@@ -472,12 +489,12 @@ def parse_text(text, return_text_on_error=False, comments=False, dates=False, fi
|
|
|
472
489
|
result = None
|
|
473
490
|
if isinstance(result, float):
|
|
474
491
|
result = round(result, 5)
|
|
475
|
-
node.append({"@type": "formula", "@value":
|
|
492
|
+
node.append({"@type": "formula", "@value": item, "@result": result})
|
|
476
493
|
elif item.startswith("@"):
|
|
477
494
|
if (result := variables.get(item)) is None:
|
|
478
495
|
if filename:
|
|
479
496
|
logger.warning(f"Filename: {filename}")
|
|
480
|
-
logger.warning(f'Value for "{
|
|
497
|
+
logger.warning(f'Value for "{item}" cannot be found (line: {line_number})')
|
|
481
498
|
node.append({"@type": "variable", "@value": item, "@result": result})
|
|
482
499
|
elif dates and regex_date.fullmatch(item):
|
|
483
500
|
item = convert_date(item) or item
|
|
@@ -494,8 +511,8 @@ def parse_text(text, return_text_on_error=False, comments=False, dates=False, fi
|
|
|
494
511
|
logger.error(f"Line {line_number}: {line_text}")
|
|
495
512
|
logger.error(f"Parse error: {error}")
|
|
496
513
|
logger.debug("Exception:", exc_info=True)
|
|
497
|
-
return text if return_text_on_error else None
|
|
498
|
-
return root
|
|
514
|
+
return regex_clean.sub("", text) if return_text_on_error else None
|
|
515
|
+
return objectify(root) if as_object else root
|
|
499
516
|
|
|
500
517
|
|
|
501
518
|
def parse_file(
|
|
@@ -506,6 +523,7 @@ def parse_file(
|
|
|
506
523
|
save=False,
|
|
507
524
|
comments=False,
|
|
508
525
|
dates=False,
|
|
526
|
+
as_object=False,
|
|
509
527
|
is_global=False,
|
|
510
528
|
patch=None,
|
|
511
529
|
):
|
|
@@ -516,12 +534,16 @@ def parse_file(
|
|
|
516
534
|
:param encoding: Encoding used to read file
|
|
517
535
|
:param base_dir: Base directory (for debug)
|
|
518
536
|
:param save: (default false) Save parsed file in output directory
|
|
519
|
-
:param comments: Include comments?
|
|
520
|
-
:param dates: Include dates?
|
|
537
|
+
:param comments: (default false) Include comments?
|
|
538
|
+
:param dates: (default false) Include dates?
|
|
539
|
+
:param as_object: (default false) Return data as an object-like instead of dict
|
|
521
540
|
:param is_global: (default false) Are file's variables global?
|
|
522
541
|
:param patch: String replacement patterns
|
|
523
542
|
:return: Parsed data as dictionary or text if parsing fails
|
|
524
543
|
"""
|
|
544
|
+
if save and as_object:
|
|
545
|
+
logger.warning("`as_object` disabled bacause only dicts can be converted to JSON.")
|
|
546
|
+
as_object = False
|
|
525
547
|
start_time = time.monotonic()
|
|
526
548
|
if base_dir:
|
|
527
549
|
base_dir = os.sep.join(str(base_dir).rstrip(os.sep).split(os.sep)[:-1]) + os.sep
|
|
@@ -535,7 +557,13 @@ def parse_file(
|
|
|
535
557
|
filename = os.path.join(base_dir, os.path.basename(path))
|
|
536
558
|
logger.debug(f"Parsing {filename}")
|
|
537
559
|
data = parse_text(
|
|
538
|
-
text,
|
|
560
|
+
text,
|
|
561
|
+
return_text_on_error=True,
|
|
562
|
+
comments=comments,
|
|
563
|
+
dates=dates,
|
|
564
|
+
as_object=as_object,
|
|
565
|
+
filename=filename,
|
|
566
|
+
is_global=is_global,
|
|
539
567
|
)
|
|
540
568
|
if save:
|
|
541
569
|
filename, _ = os.path.splitext(os.path.basename(path))
|
|
@@ -565,6 +593,7 @@ def parse_all_files(
|
|
|
565
593
|
save=False,
|
|
566
594
|
comments=False,
|
|
567
595
|
dates=False,
|
|
596
|
+
as_object=False,
|
|
568
597
|
variables_first=True,
|
|
569
598
|
_variables_only=False,
|
|
570
599
|
):
|
|
@@ -575,8 +604,9 @@ def parse_all_files(
|
|
|
575
604
|
:param encoding: Encoding used to read files
|
|
576
605
|
:param keep_data: (default false) Return parsed data of all files in a dictionary
|
|
577
606
|
:param save: (default false) Save every parsed data in output directory
|
|
578
|
-
:param comments: Include comments?
|
|
579
|
-
:param dates: Include dates?
|
|
607
|
+
:param comments: (default false) Include comments?
|
|
608
|
+
:param dates: (default false) Include dates?
|
|
609
|
+
:param as_object: (default false) Return data as an object-like instead of dict
|
|
580
610
|
:param variables_first: Try to parse variables first
|
|
581
611
|
:return: Dictionary (key: file, value: parsed data if keep_data=True)
|
|
582
612
|
"""
|
|
@@ -592,6 +622,7 @@ def parse_all_files(
|
|
|
592
622
|
save=save,
|
|
593
623
|
comments=comments,
|
|
594
624
|
dates=dates,
|
|
625
|
+
as_object=as_object,
|
|
595
626
|
variables_first=False,
|
|
596
627
|
_variables_only=True,
|
|
597
628
|
)
|
|
@@ -612,6 +643,7 @@ def parse_all_files(
|
|
|
612
643
|
save=save,
|
|
613
644
|
comments=comments,
|
|
614
645
|
dates=dates,
|
|
646
|
+
as_object=as_object,
|
|
615
647
|
is_global=_variables_only,
|
|
616
648
|
)
|
|
617
649
|
if isinstance(data, str):
|
|
@@ -683,6 +715,48 @@ def walk(obj, *from_keys):
|
|
|
683
715
|
yield obj, from_keys
|
|
684
716
|
|
|
685
717
|
|
|
718
|
+
def objectify(data, name="Object", default=None):
|
|
719
|
+
"""
|
|
720
|
+
Convert a dictionary to an object-like dictionary
|
|
721
|
+
:param data: dictionary
|
|
722
|
+
:param name: name of the object
|
|
723
|
+
:param default: default value for unknown attributes
|
|
724
|
+
:return: object-like dictionary
|
|
725
|
+
"""
|
|
726
|
+
|
|
727
|
+
def _getattr(s, k):
|
|
728
|
+
try:
|
|
729
|
+
object.__getattribute__(s, k)
|
|
730
|
+
except AttributeError:
|
|
731
|
+
return s.get(k, default)
|
|
732
|
+
|
|
733
|
+
if isinstance(data, list):
|
|
734
|
+
return [
|
|
735
|
+
objectify(item)
|
|
736
|
+
for item in data
|
|
737
|
+
if not (isinstance(item, str) and item.startswith("@") and item.endswith("@"))
|
|
738
|
+
]
|
|
739
|
+
elif isinstance(data, dict):
|
|
740
|
+
name = (data.get("@type") or name).title()
|
|
741
|
+
slots = set()
|
|
742
|
+
attrs = dict(__getattr__=lambda s, k: _getattr(s, k), __slots__=slots)
|
|
743
|
+
subdata = {}
|
|
744
|
+
for key, value in data.items():
|
|
745
|
+
if key.startswith("#"):
|
|
746
|
+
continue
|
|
747
|
+
skey = key.replace("@", "_").replace(":", "_")
|
|
748
|
+
if match := regex_date.match(skey):
|
|
749
|
+
skey = "_".join(match.groups())
|
|
750
|
+
skey = f"_{skey}" if key != skey or key[0].isdigit() else skey
|
|
751
|
+
slots.add(skey)
|
|
752
|
+
if isinstance(value, (list, dict)):
|
|
753
|
+
subdata[key] = subdata[skey] = objectify(value)
|
|
754
|
+
continue
|
|
755
|
+
subdata[key] = subdata[skey] = value
|
|
756
|
+
return type(name, (dict,), attrs)(subdata)
|
|
757
|
+
return data
|
|
758
|
+
|
|
759
|
+
|
|
686
760
|
# Tags which are always a list when reverting
|
|
687
761
|
list_keys_rules = [
|
|
688
762
|
# Colors
|
|
@@ -729,6 +803,8 @@ def revert(obj, from_key=None, prev_key=None, depth=-1, sep="\t", sort=False):
|
|
|
729
803
|
elif depth > 0:
|
|
730
804
|
lines.append(f"{tabs}{{")
|
|
731
805
|
for key, value in sorted(obj.items()) if sort else obj.items():
|
|
806
|
+
if key.startswith("_"):
|
|
807
|
+
continue
|
|
732
808
|
lines.extend(revert(value, from_key=key, prev_key=from_key, depth=depth + 1, sep=sep, sort=sort))
|
|
733
809
|
if from_key or depth > 0:
|
|
734
810
|
lines.append(f"{tabs}}}")
|
|
@@ -736,11 +812,13 @@ def revert(obj, from_key=None, prev_key=None, depth=-1, sep="\t", sort=False):
|
|
|
736
812
|
if from_key and (from_key.lower() == "this" or not any(regex.match(from_key) for regex in list_keys_rules)):
|
|
737
813
|
for value in sorted(obj) if sort else obj:
|
|
738
814
|
lines.extend(revert(value, from_key=from_key, prev_key=prev_key, depth=depth, sep=sep, sort=sort))
|
|
739
|
-
elif not any(isinstance(o, (dict, list)) for o in obj)
|
|
815
|
+
elif not any(isinstance(o, (dict, list)) for o in obj) and not any(
|
|
816
|
+
o.startswith("#") and o.endswith("#") for o in obj if isinstance(o, str)
|
|
817
|
+
):
|
|
740
818
|
prefix = f"{tabs}{from_key} = {{"
|
|
741
819
|
# Only for color modes
|
|
742
820
|
if from_key == "color" and len(obj) == 4 and isinstance(obj[0], str):
|
|
743
|
-
prefix = f"{tabs}{from_key} {obj[0]}
|
|
821
|
+
prefix = f"{tabs}{from_key} = {obj[0]} {{"
|
|
744
822
|
obj = obj[1:]
|
|
745
823
|
func = functools.partial(revert_value, from_key=from_key, prev_key=prev_key, sep=sep, sort=sort)
|
|
746
824
|
values = " ".join(map(str, map(func, obj)))
|
|
@@ -783,7 +861,7 @@ def revert_value(value, from_key=None, prev_key=None, **kwargs):
|
|
|
783
861
|
return "yes" if value else "no"
|
|
784
862
|
elif isinstance(value, str):
|
|
785
863
|
if value.startswith("#") and value.endswith("#"):
|
|
786
|
-
return f"#{value}"
|
|
864
|
+
return f"#{value.strip("#")}"
|
|
787
865
|
elif (
|
|
788
866
|
" " in value
|
|
789
867
|
or (value.startswith("$") and value.endswith("$"))
|
|
@@ -874,6 +952,22 @@ def save_variables(filepath="_variables.json"):
|
|
|
874
952
|
json_dump(global_variables, file, sort_keys=True)
|
|
875
953
|
|
|
876
954
|
|
|
955
|
+
def enable_logging(debug=False, file=False):
|
|
956
|
+
logger.setLevel(logging.DEBUG)
|
|
957
|
+
formatter = logging.Formatter("[%(asctime)s] %(levelname)s: %(message)s")
|
|
958
|
+
console_handler = logging.StreamHandler()
|
|
959
|
+
console_handler.setLevel(logging.INFO)
|
|
960
|
+
console_handler.setFormatter(formatter)
|
|
961
|
+
logger.addHandler(console_handler)
|
|
962
|
+
if debug:
|
|
963
|
+
console_handler.setLevel(logging.DEBUG)
|
|
964
|
+
if file:
|
|
965
|
+
file_handler = logging.FileHandler("ckparser.log")
|
|
966
|
+
file_handler.setLevel(logging.INFO)
|
|
967
|
+
file_handler.setFormatter(formatter)
|
|
968
|
+
logger.addHandler(file_handler)
|
|
969
|
+
|
|
970
|
+
|
|
877
971
|
def main():
|
|
878
972
|
"""
|
|
879
973
|
Command-line main entrypoint
|
|
@@ -890,18 +984,7 @@ def main():
|
|
|
890
984
|
parser.add_argument("--debug", action="store_true", help="debug mode?")
|
|
891
985
|
args = parser.parse_args()
|
|
892
986
|
|
|
893
|
-
|
|
894
|
-
formatter = logging.Formatter("[%(asctime)s] %(levelname)s: %(message)s")
|
|
895
|
-
console_handler = logging.StreamHandler()
|
|
896
|
-
console_handler.setLevel(logging.INFO)
|
|
897
|
-
console_handler.setFormatter(formatter)
|
|
898
|
-
logger.addHandler(console_handler)
|
|
899
|
-
if args.debug:
|
|
900
|
-
console_handler.setLevel(logging.DEBUG)
|
|
901
|
-
file_handler = logging.FileHandler("ckparser.log")
|
|
902
|
-
file_handler.setLevel(logging.INFO)
|
|
903
|
-
file_handler.setFormatter(formatter)
|
|
904
|
-
logger.addHandler(file_handler)
|
|
987
|
+
enable_logging(debug=args.debug, file=True)
|
|
905
988
|
|
|
906
989
|
if args.revert:
|
|
907
990
|
if os.path.isdir(args.path):
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ckparser"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.3"
|
|
8
8
|
description = "Parse Paradox Jomini data files to Python/JSON and revert them back experimentally."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|