ckparser 0.1__tar.gz → 0.2__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ckparser
3
- Version: 0.1
3
+ Version: 0.2
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
@@ -132,10 +132,10 @@ Parse a directory recursively and write JSON output:
132
132
  python -m ckparser my_mod/common --output output
133
133
  ```
134
134
 
135
- Include comments in the parsed output:
135
+ Include comments and/or dates in the parsed output:
136
136
 
137
137
  ```bash
138
- python -m ckparser my_mod/common --comments
138
+ python -m ckparser my_mod/common --comments --dates
139
139
  ```
140
140
 
141
141
  Revert a JSON file back to Jomini format:
@@ -412,6 +412,14 @@ With `comments=True` or `--comments`, the parser attempts to preserve them in an
412
412
 
413
413
  Comment preservation should be considered practical rather than perfectly lossless.
414
414
 
415
+ ### Dates
416
+
417
+ By default, dates are not converted in Python `datetime.date` objects in parsing.
418
+
419
+ While convenient, Jomini date format (`Y[YYY].M[M].D[D]`) does not enforce validation and can be invalid.
420
+
421
+ With `dates=True` or `--dates`, the parser attempts to convert these dates if possible and keeps invalid dates as text.
422
+
415
423
  ### Localization parsing
416
424
 
417
425
  `ckparser` also includes a dedicated parser for Paradox localization files:
@@ -510,9 +518,9 @@ Paradox scripting languages include many practical exceptions, formatting varian
510
518
 
511
519
  ### Parsing
512
520
 
513
- * `parse_text(text, return_text_on_error=False, comments=False, filename=None, is_global=False)`
514
- * `parse_file(path, output_dir=None, encoding="utf_8_sig", base_dir=None, save=False, comments=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, variables_first=True)`
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)`
516
524
 
517
525
  ### Reversion
518
526
 
@@ -101,10 +101,10 @@ Parse a directory recursively and write JSON output:
101
101
  python -m ckparser my_mod/common --output output
102
102
  ```
103
103
 
104
- Include comments in the parsed output:
104
+ Include comments and/or dates in the parsed output:
105
105
 
106
106
  ```bash
107
- python -m ckparser my_mod/common --comments
107
+ python -m ckparser my_mod/common --comments --dates
108
108
  ```
109
109
 
110
110
  Revert a JSON file back to Jomini format:
@@ -381,6 +381,14 @@ With `comments=True` or `--comments`, the parser attempts to preserve them in an
381
381
 
382
382
  Comment preservation should be considered practical rather than perfectly lossless.
383
383
 
384
+ ### Dates
385
+
386
+ By default, dates are not converted in Python `datetime.date` objects in parsing.
387
+
388
+ While convenient, Jomini date format (`Y[YYY].M[M].D[D]`) does not enforce validation and can be invalid.
389
+
390
+ With `dates=True` or `--dates`, the parser attempts to convert these dates if possible and keeps invalid dates as text.
391
+
384
392
  ### Localization parsing
385
393
 
386
394
  `ckparser` also includes a dedicated parser for Paradox localization files:
@@ -479,9 +487,9 @@ Paradox scripting languages include many practical exceptions, formatting varian
479
487
 
480
488
  ### Parsing
481
489
 
482
- * `parse_text(text, return_text_on_error=False, comments=False, filename=None, is_global=False)`
483
- * `parse_file(path, output_dir=None, encoding="utf_8_sig", base_dir=None, save=False, comments=False, is_global=False, patch=None)`
484
- * `parse_all_files(path, output_dir=None, encoding="utf_8_sig", keep_data=False, save=False, comments=False, variables_first=True)`
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)`
485
493
 
486
494
  ### Reversion
487
495
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ckparser
3
- Version: 0.1
3
+ Version: 0.2
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
@@ -132,10 +132,10 @@ Parse a directory recursively and write JSON output:
132
132
  python -m ckparser my_mod/common --output output
133
133
  ```
134
134
 
135
- Include comments in the parsed output:
135
+ Include comments and/or dates in the parsed output:
136
136
 
137
137
  ```bash
138
- python -m ckparser my_mod/common --comments
138
+ python -m ckparser my_mod/common --comments --dates
139
139
  ```
140
140
 
141
141
  Revert a JSON file back to Jomini format:
@@ -412,6 +412,14 @@ With `comments=True` or `--comments`, the parser attempts to preserve them in an
412
412
 
413
413
  Comment preservation should be considered practical rather than perfectly lossless.
414
414
 
415
+ ### Dates
416
+
417
+ By default, dates are not converted in Python `datetime.date` objects in parsing.
418
+
419
+ While convenient, Jomini date format (`Y[YYY].M[M].D[D]`) does not enforce validation and can be invalid.
420
+
421
+ With `dates=True` or `--dates`, the parser attempts to convert these dates if possible and keeps invalid dates as text.
422
+
415
423
  ### Localization parsing
416
424
 
417
425
  `ckparser` also includes a dedicated parser for Paradox localization files:
@@ -510,9 +518,9 @@ Paradox scripting languages include many practical exceptions, formatting varian
510
518
 
511
519
  ### Parsing
512
520
 
513
- * `parse_text(text, return_text_on_error=False, comments=False, filename=None, is_global=False)`
514
- * `parse_file(path, output_dir=None, encoding="utf_8_sig", base_dir=None, save=False, comments=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, variables_first=True)`
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)`
516
524
 
517
525
  ### Reversion
518
526
 
@@ -28,6 +28,7 @@ Author: Marc Debureaux (debnet)
28
28
  Project: https://github.com/debnet/ckparser
29
29
  License: MIT
30
30
  """
31
+
31
32
  import argparse
32
33
  import ast
33
34
  import colorsys
@@ -40,7 +41,7 @@ import re
40
41
  import time
41
42
 
42
43
  # Script version
43
- __version__ = "0.1"
44
+ __version__ = "0.2"
44
45
 
45
46
  # Logger (because logging is awesome)
46
47
  logger = logging.getLogger(__name__)
@@ -52,6 +53,28 @@ except ImportError:
52
53
  detect = None
53
54
  logger.warning("chardet not installed, encoding detection is disabled!")
54
55
 
56
+
57
+ class JominiJSONEncoder(json.JSONEncoder):
58
+ # JSON specific encoder for dates
59
+ def default(self, obj):
60
+ if isinstance(obj, datetime.date):
61
+ return f"{obj.year}.{obj.month}.{obj.day}"
62
+ return super().default(obj)
63
+
64
+
65
+ def jomini_object_hook(obj):
66
+ # JSON specific decoder for dates
67
+ for key, value in obj.items():
68
+ if isinstance(value, str) and regex_date.match(value):
69
+ obj[key] = convert_date(value) or value
70
+ return obj
71
+
72
+
73
+ json_load = functools.partial(json.load, object_hook=jomini_object_hook)
74
+ json_loads = functools.partial(json.loads, object_hook=jomini_object_hook)
75
+ json_dump = functools.partial(json.dump, cls=JominiJSONEncoder, indent=4, ensure_ascii=False)
76
+ json_dumps = functools.partial(json.dumps, cls=JominiJSONEncoder, indent=4, ensure_ascii=False)
77
+
55
78
  # Boolean transformation
56
79
  booleans = {"yes": True, "no": False}
57
80
  # Tags which must be aggregate as a list in JSON (don't hesitate to add more if needed)
@@ -84,6 +107,8 @@ regex_values = re.compile(r"(([?!<=>]+)\s*\n+)|(\n+\s*([?!<=>]+))")
84
107
  regex_line = re.compile(r"\"?(?P<key>[^\s\"]+)\"?\s*(?P<operator>[?!<=>]+)\s*(list\s*)?(?P<value>.*)")
85
108
  # Regex to parse independent items in a list
86
109
  regex_item = re.compile(r"(\"[^\"]+\"|[\d\.]+|[^\s]+)")
110
+ # Regex to parse dates
111
+ regex_date = re.compile(r"^(?:\d{1,4})\.(?:[1-9]|1[0-2])\.(?:[1-9]|[12]\d|3[01])$")
87
112
  # Regex to remove empty lines
88
113
  regex_empty = re.compile(r"(\n\s*\n)+", re.MULTILINE)
89
114
  # Regex to parse locale files
@@ -151,12 +176,13 @@ def read_file(path, encoding="utf_8_sig"):
151
176
  return file.read()
152
177
 
153
178
 
154
- def parse_text(text, return_text_on_error=False, comments=False, filename=None, is_global=False):
179
+ def parse_text(text, return_text_on_error=False, comments=False, dates=False, filename=None, is_global=False):
155
180
  """
156
181
  Parse raw text
157
182
  :param text: Text to parse
158
183
  :param return_text_on_error: (default false) Return working text document if parsing fails
159
184
  :param comments: (default false) Include comments?
185
+ :param dates: (default false) Include dates?
160
186
  :param filename: (default none) Filename (only for debugging)
161
187
  :param is_global: (default false) Are variables global?
162
188
  :return: Parsed data as dictionary
@@ -251,10 +277,13 @@ def parse_text(text, return_text_on_error=False, comments=False, filename=None,
251
277
  value = val
252
278
  elif value:
253
279
  # Try to convert value to Python value
254
- try:
255
- value = ast.literal_eval(value)
256
- except: # noqa
257
- pass
280
+ if dates and regex_date.match(value):
281
+ value = convert_date(value) or value
282
+ else:
283
+ try:
284
+ value = ast.literal_eval(value)
285
+ except: # noqa
286
+ pass
258
287
  # If key is duplicate with direct value
259
288
  if key in node:
260
289
  if node[key] != value: # Avoid single duplicates
@@ -426,11 +455,15 @@ def parse_text(text, return_text_on_error=False, comments=False, filename=None,
426
455
  logger.warning(f"Filename: {filename}")
427
456
  logger.warning(f'Value for "{value}" cannot be found (line: {line_number})')
428
457
  node.append({"@type": "variable", "@value": item, "@result": result})
458
+ elif dates and regex_date.match(item):
459
+ item = convert_date(item) or item
460
+ node.append(item)
429
461
  else:
430
462
  try:
431
- node.append(ast.literal_eval(item))
463
+ item = ast.literal_eval(item)
432
464
  except: # noqa
433
- node.append(item)
465
+ pass
466
+ node.append(item)
434
467
  except Exception as error:
435
468
  if filename:
436
469
  logger.error(f"Filename: {filename}")
@@ -448,6 +481,7 @@ def parse_file(
448
481
  base_dir=None,
449
482
  save=False,
450
483
  comments=False,
484
+ dates=False,
451
485
  is_global=False,
452
486
  patch=None,
453
487
  ):
@@ -459,6 +493,7 @@ def parse_file(
459
493
  :param base_dir: Base directory (for debug)
460
494
  :param save: (default false) Save parsed file in output directory
461
495
  :param comments: Include comments?
496
+ :param dates: Include dates?
462
497
  :param is_global: (default false) Are file's variables global?
463
498
  :param patch: String replacement patterns
464
499
  :return: Parsed data as dictionary or text if parsing fails
@@ -475,7 +510,9 @@ def parse_file(
475
510
  text = re.sub(pattern, replacement, text)
476
511
  filename = os.path.join(base_dir, os.path.basename(path)).replace(os.sep, "/")
477
512
  logger.debug(f"Parsing {filename}")
478
- data = parse_text(text, return_text_on_error=True, comments=comments, filename=filename, is_global=is_global)
513
+ data = parse_text(
514
+ text, return_text_on_error=True, comments=comments, dates=dates, filename=filename, is_global=is_global
515
+ )
479
516
  if save:
480
517
  filename, _ = os.path.splitext(os.path.basename(path))
481
518
  directory = os.path.join(output_dir or "output", *base_dir.split("/")).replace(os.sep, "/")
@@ -490,7 +527,7 @@ def parse_file(
490
527
  else:
491
528
  filename = os.path.join(directory, filename + ".json")
492
529
  with open(filename, "w") as file:
493
- json.dump(data, file, indent=4)
530
+ json_dump(data, file)
494
531
  total_time = time.monotonic() - start_time
495
532
  logger.debug(f"Elapsed time: {total_time:0.3f}s!")
496
533
  return data
@@ -503,6 +540,7 @@ def parse_all_files(
503
540
  keep_data=False,
504
541
  save=False,
505
542
  comments=False,
543
+ dates=False,
506
544
  variables_first=True,
507
545
  _variables_only=False,
508
546
  ):
@@ -514,6 +552,7 @@ def parse_all_files(
514
552
  :param keep_data: (default false) Return parsed data of all files in a dictionary
515
553
  :param save: (default false) Save every parsed data in output directory
516
554
  :param comments: Include comments?
555
+ :param dates: Include dates?
517
556
  :param variables_first: Try to parse variables first
518
557
  :return: Dictionary (key: file, value: parsed data if keep_data=True)
519
558
  """
@@ -522,12 +561,13 @@ def parse_all_files(
522
561
  if variables_first and not _variables_only:
523
562
  success.update(
524
563
  parse_all_files(
525
- path,
526
- output_dir,
527
- encoding,
528
- keep_data,
529
- save,
530
- comments,
564
+ path=path,
565
+ output_dir=output_dir,
566
+ encoding=encoding,
567
+ keep_data=keep_data,
568
+ save=save,
569
+ comments=comments,
570
+ dates=dates,
531
571
  variables_first=False,
532
572
  _variables_only=True,
533
573
  )
@@ -547,6 +587,7 @@ def parse_all_files(
547
587
  base_dir=path,
548
588
  save=save,
549
589
  comments=comments,
590
+ dates=dates,
550
591
  is_global=_variables_only,
551
592
  )
552
593
  if isinstance(data, str):
@@ -561,13 +602,14 @@ def parse_all_files(
561
602
  return success
562
603
 
563
604
 
564
- def parse_all_locales(path, encoding="utf_8_sig", language="english", save=False):
605
+ def parse_all_locales(path, encoding="utf_8_sig", language="english", save=False, filepath="_locales.json"):
565
606
  """
566
607
  Parse all locales strings
567
608
  :param path: Path where to find locale files
568
609
  :param encoding: Encoding for reading files
569
610
  :param language: Target language
570
611
  :param save: (default false) save locales in file
612
+ :param filepath: locales output file location
571
613
  :return: Locales in dictionary
572
614
  """
573
615
  locales = {}
@@ -595,8 +637,8 @@ def parse_all_locales(path, encoding="utf_8_sig", language="english", save=False
595
637
  key, _, value = match.groups()
596
638
  locales[key] = value
597
639
  if save:
598
- with open("_locales.json", "w") as file:
599
- json.dump(locales, file, indent=4, sort_keys=True)
640
+ with open(filepath, "w") as file:
641
+ json_dump(locales, file, sort_keys=True)
600
642
  return locales
601
643
 
602
644
 
@@ -729,6 +771,8 @@ def revert_value(value, from_key=None, prev_key=None, **kwargs):
729
771
  return f'"{value}"'
730
772
  elif isinstance(value, dict):
731
773
  value = revert(value, from_key=from_key, prev_key=prev_key, depth=0, **kwargs)
774
+ elif isinstance(value, datetime.date):
775
+ value = f"{value.year}.{value.month}.{value.day}"
732
776
  return value
733
777
 
734
778
 
@@ -769,7 +813,7 @@ def revert_file(path, output_dir=None, encoding="utf_8_sig", base_dir=None, save
769
813
  base_dir = os.path.dirname(path.replace(base_dir, ""))
770
814
  base_dir = base_dir or "."
771
815
  with open(path) as file:
772
- data = json.load(file)
816
+ data = json_load(file)
773
817
  filename = os.path.join(str(base_dir), os.path.basename(path))
774
818
  logger.debug(f"Reverting {filename}")
775
819
  text = revert(data)
@@ -793,7 +837,7 @@ def load_variables(filepath="_variables.json"):
793
837
  if not os.path.exists(filepath):
794
838
  return
795
839
  with open(filepath) as file:
796
- global_variables = json.load(file)
840
+ global_variables = json_load(file)
797
841
 
798
842
 
799
843
  def save_variables(filepath="_variables.json"):
@@ -803,7 +847,7 @@ def save_variables(filepath="_variables.json"):
803
847
  if not global_variables:
804
848
  return
805
849
  with open(filepath, "w") as file:
806
- json.dump(global_variables, file, indent=4, sort_keys=True)
850
+ json_dump(global_variables, file, sort_keys=True)
807
851
 
808
852
 
809
853
  def main():
@@ -818,6 +862,7 @@ def main():
818
862
  parser.add_argument("--output", type=str, help="output directory for parsing results")
819
863
  parser.add_argument("--revert", action="store_true", help="revert JSON files?")
820
864
  parser.add_argument("--comments", action="store_true", help="include comments?")
865
+ parser.add_argument("--dates", action="store_true", help="include dates?")
821
866
  parser.add_argument("--debug", action="store_true", help="debug mode?")
822
867
  args = parser.parse_args()
823
868
 
@@ -847,6 +892,7 @@ def main():
847
892
  encoding=args.encoding,
848
893
  output_dir=args.output,
849
894
  comments=args.comments,
895
+ dates=args.dates,
850
896
  save=True,
851
897
  )
852
898
  else:
@@ -855,6 +901,7 @@ def main():
855
901
  encoding=args.encoding,
856
902
  output_dir=args.output,
857
903
  comments=args.comments,
904
+ dates=args.dates,
858
905
  save=True,
859
906
  )
860
907
  save_variables()
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ckparser"
7
- version = "0.1"
7
+ version = "0.2"
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