amalgam-lang 20.0.2__py3-none-macosx_12_0_arm64.whl → 25.2.5__py3-none-macosx_12_0_arm64.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.

Potentially problematic release.


This version of amalgam-lang might be problematic. Click here for more details.

amalgam/api.py CHANGED
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from base64 import b64encode
3
4
  from ctypes import (
4
5
  _Pointer, Array, byref, c_bool, c_char, c_char_p, c_size_t, c_uint64, c_void_p,
5
- cast, cdll, POINTER, Structure
6
+ cast, cdll, POINTER, Structure, string_at
6
7
  )
7
8
  from datetime import datetime
8
9
  import gc
@@ -28,7 +29,9 @@ class _LoadEntityStatus(Structure):
28
29
  _fields_ = [
29
30
  ("loaded", c_bool),
30
31
  ("message", POINTER(c_char)),
31
- ("version", POINTER(c_char))
32
+ ("version", POINTER(c_char)),
33
+ ("entity_path", POINTER(POINTER(c_char))),
34
+ ("entity_path_len", c_size_t)
32
35
  ]
33
36
 
34
37
 
@@ -53,10 +56,14 @@ class LoadEntityStatus:
53
56
  self.loaded = True
54
57
  self.message = ""
55
58
  self.version = ""
59
+ self.entity_path = []
56
60
  else:
57
61
  self.loaded = bool(c_status.loaded)
58
62
  self.message = api.char_p_to_str(c_status.message)
59
63
  self.version = api.char_p_to_str(c_status.version)
64
+ self.entity_path = [api.char_p_to_str(c_status.entity_path[i]) for i in range(c_status.entity_path_len)]
65
+ api.amlg.DeleteString(cast(c_status.entity_path, c_char_p))
66
+
60
67
 
61
68
  def __str__(self) -> str:
62
69
  """
@@ -67,7 +74,11 @@ class LoadEntityStatus:
67
74
  str
68
75
  The human-readable string representation.
69
76
  """
70
- return f"{self.loaded},\"{self.message}\",\"{self.version}\""
77
+ ep = ""
78
+ if self.entity_path:
79
+ eps = (f'"{step}"' for step in self.entity_path)
80
+ ep = f",[{','.join(eps)}]"
81
+ return f"{self.loaded},\"{self.message}\",\"{self.version}\"{ep}"
71
82
 
72
83
 
73
84
  class _ResultWithLog(Structure):
@@ -237,8 +248,7 @@ class Amalgam:
237
248
  )
238
249
  counter += 1
239
250
 
240
- self.trace = open(self.execution_trace_filepath, 'w+',
241
- encoding='utf-8')
251
+ self.trace = self.execution_trace_filepath.open("wb+")
242
252
  _logger.debug("Opening Amalgam trace file: "
243
253
  f"{self.execution_trace_filepath}")
244
254
  else:
@@ -253,7 +263,7 @@ class Amalgam:
253
263
  self.set_max_num_threads(max_num_threads)
254
264
  self.gc_interval = gc_interval
255
265
  self.op_count = 0
256
- self.load_command_log_entry = None
266
+ self.load_command_log_entry: bytes | None = None
257
267
 
258
268
  @classmethod
259
269
  def _get_allowed_postfixes(cls, library_dir: Path) -> list[str]:
@@ -470,7 +480,7 @@ class Amalgam:
470
480
  The maximum number of threads that Amalgam is configured to use.
471
481
  """
472
482
  self.amlg.GetMaxNumThreads.restype = c_size_t
473
- self._log_execution("GET_MAX_NUM_THREADS")
483
+ self._log_execution(b"GET_MAX_NUM_THREADS")
474
484
  result = self.amlg.GetMaxNumThreads()
475
485
  self._log_reply(result)
476
486
 
@@ -492,7 +502,7 @@ class Amalgam:
492
502
  self.amlg.SetMaxNumThreads.argtypes = [c_size_t]
493
503
  self.amlg.SetMaxNumThreads.restype = c_void_p
494
504
 
495
- self._log_execution(f"SET_MAX_NUM_THREADS {max_num_threads}")
505
+ self._log_execution(f"SET_MAX_NUM_THREADS {max_num_threads}".encode())
496
506
  result = self.amlg.SetMaxNumThreads(max_num_threads)
497
507
  self._log_reply(result)
498
508
 
@@ -511,7 +521,7 @@ class Amalgam:
511
521
  _logger.debug(f"Execution trace file being reset: "
512
522
  f"{self.execution_trace_filepath} to be closed ...")
513
523
  # Write exit command.
514
- self.trace.write("EXIT\n")
524
+ self.trace.write(b"EXIT\n")
515
525
  self.trace.close()
516
526
  self.execution_trace_filepath = Path(self.execution_trace_dir, file)
517
527
 
@@ -523,12 +533,12 @@ class Amalgam:
523
533
  self.execution_trace_dir, f'{file}.{counter}')
524
534
  counter += 1
525
535
 
526
- self.trace = open(self.execution_trace_filepath, 'w+')
536
+ self.trace = self.execution_trace_filepath.open("wb+")
527
537
  _logger.debug(f"New trace file: {self.execution_trace_filepath} "
528
538
  f"opened.")
529
539
  # Write load command used to instantiate the amalgam instance.
530
540
  if self.load_command_log_entry is not None:
531
- self.trace.write(self.load_command_log_entry + "\n")
541
+ self.trace.write(self.load_command_log_entry + b"\n")
532
542
  self.trace.flush()
533
543
 
534
544
  def __str__(self) -> str:
@@ -543,7 +553,7 @@ class Amalgam:
543
553
  getattr(self, 'trace', None) is not None
544
554
  ):
545
555
  try:
546
- self.trace.write("EXIT\n")
556
+ self.trace.write(b"EXIT\n")
547
557
  except Exception: # noqa - deliberately broad
548
558
  pass
549
559
 
@@ -559,7 +569,7 @@ class Amalgam:
559
569
  The raw reply string to log.
560
570
  """
561
571
  if self.trace:
562
- self.trace.write("# NOTE >" + str(comment) + "\n")
572
+ self.trace.write(b"# NOTE >" + comment.encode() + b"\n")
563
573
  self.trace.flush()
564
574
 
565
575
  def _log_reply(self, reply: t.Any):
@@ -574,7 +584,13 @@ class Amalgam:
574
584
  The raw reply string to log.
575
585
  """
576
586
  if self.trace:
577
- self.trace.write("# RESULT >" + str(reply) + "\n")
587
+ if isinstance(reply, ResultWithLog):
588
+ if reply.json:
589
+ self.trace.write(b"# RESULT >" + reply.json + b"\n")
590
+ if reply.log:
591
+ self.trace.write(b"# LOG >" + reply.log + b"\n")
592
+ else:
593
+ self.trace.write(b"# RESULT >" + str(reply).encode() + b"\n")
578
594
  self.trace.flush()
579
595
 
580
596
  def _log_time(self, label: str):
@@ -588,11 +604,11 @@ class Amalgam:
588
604
  """
589
605
  if self.trace:
590
606
  dt = datetime.now()
591
- self.trace.write(f"# TIME {label} {dt:%Y-%m-%d %H:%M:%S},"
592
- f"{f'{dt:%f}'[:3]}\n")
607
+ time_str = f"{label} {dt:%Y-%m-%d %H:%M:%S},{f'{dt:%f}'[:3]}"
608
+ self.trace.write(b"# TIME " + time_str.encode() + b"\n")
593
609
  self.trace.flush()
594
610
 
595
- def _log_execution(self, execution_string: str):
611
+ def _log_execution(self, execution_string: bytes):
596
612
  """
597
613
  Log an execution string.
598
614
 
@@ -601,7 +617,7 @@ class Amalgam:
601
617
 
602
618
  Parameters
603
619
  ----------
604
- execution_string : str
620
+ execution_string : bytes
605
621
  A formatted string that can be piped into an amalgam command line
606
622
  process for use in debugging.
607
623
 
@@ -610,9 +626,35 @@ class Amalgam:
610
626
  string passed is valid.
611
627
  """
612
628
  if self.trace:
613
- self.trace.write(execution_string + "\n")
629
+ self.trace.write(execution_string + b"\n")
614
630
  self.trace.flush()
615
631
 
632
+ def _log_execution_std(self, command: bytes, *args: str, suffix: str | bytes | None = None) -> None:
633
+ """
634
+ Log an execution string in a canonical form.
635
+
636
+ This looks like ``COMMAND "arg" "arg" suffix``.
637
+
638
+ Parameters
639
+ ----------
640
+ command : bytes
641
+ The trace file command string.
642
+ args : str
643
+ Arbitrary parameters to the command, will have double quotes escaped.
644
+ suffix : bytes, optional
645
+ Arbitrary binary data to append to the command string unprocessed.
646
+ """
647
+ if not self.trace:
648
+ return
649
+ words = [b"\"" + self.escape_double_quotes(arg).encode() + b"\"" for arg in args]
650
+ words.insert(0, command)
651
+ if isinstance(suffix, str):
652
+ words.append(suffix.encode())
653
+ elif isinstance(suffix, bytes):
654
+ words.append(suffix)
655
+ self.trace.write(b" ".join(words) + b"\n")
656
+ self.trace.flush()
657
+
616
658
  def gc(self):
617
659
  """Force garbage collection when called if self.force_gc is set."""
618
660
  if (
@@ -720,10 +762,7 @@ class Amalgam:
720
762
  handle_buf = self.str_to_char_p(handle)
721
763
  label_buf = self.str_to_char_p(label)
722
764
 
723
- self._log_execution((
724
- f"GET_JSON_FROM_LABEL \"{self.escape_double_quotes(handle)}\" "
725
- f"\"{self.escape_double_quotes(label)}\""
726
- ))
765
+ self._log_execution_std(b"GET_JSON_FROM_LABEL", handle, label)
727
766
  result = self.char_p_to_bytes(self.amlg.GetJSONPtrFromLabel(handle_buf, label_buf))
728
767
  self._log_reply(result)
729
768
 
@@ -757,11 +796,7 @@ class Amalgam:
757
796
  label_buf = self.str_to_char_p(label)
758
797
  json_buf = self.str_to_char_p(json)
759
798
 
760
- self._log_execution((
761
- f"SET_JSON_TO_LABEL \"{self.escape_double_quotes(handle)}\" "
762
- f"\"{self.escape_double_quotes(label)}\" "
763
- f"{json}"
764
- ))
799
+ self._log_execution_std(b"SET_JSON_TO_LABEL", handle, label, suffix=json)
765
800
  self.amlg.SetJSONToLabel(handle_buf, label_buf, json_buf)
766
801
  self._log_reply(None)
767
802
 
@@ -779,7 +814,8 @@ class Amalgam:
779
814
  persist: bool = False,
780
815
  json_file_params: str = "",
781
816
  write_log: str = "",
782
- print_log: str = ""
817
+ print_log: str = "",
818
+ entity_path: list[str] | None = None,
783
819
  ) -> LoadEntityStatus:
784
820
  """
785
821
  Load an entity from an amalgam source file.
@@ -805,6 +841,9 @@ class Amalgam:
805
841
  print_log : str, default ""
806
842
  Path to the print log. If empty string, the print log is
807
843
  not generated.
844
+ entity_path: list[str], optional
845
+ If provided and non-empty, load the content into an entity
846
+ contained within `handle` at this path.
808
847
 
809
848
  Returns
810
849
  -------
@@ -812,7 +851,8 @@ class Amalgam:
812
851
  Status of LoadEntity call.
813
852
  """
814
853
  self.amlg.LoadEntity.argtypes = [
815
- c_char_p, c_char_p, c_char_p, c_bool, c_char_p, c_char_p, c_char_p]
854
+ c_char_p, c_char_p, c_char_p, c_bool, c_char_p, c_char_p, c_char_p, POINTER(c_char_p),
855
+ c_size_t]
816
856
  self.amlg.LoadEntity.restype = _LoadEntityStatus
817
857
  handle_buf = self.str_to_char_p(handle)
818
858
  file_path_buf = self.str_to_char_p(file_path)
@@ -820,18 +860,26 @@ class Amalgam:
820
860
  json_file_params_buf = self.str_to_char_p(json_file_params)
821
861
  write_log_buf = self.str_to_char_p(write_log)
822
862
  print_log_buf = self.str_to_char_p(print_log)
863
+ entity_path_p = None
864
+ entity_path_len = 0
823
865
 
824
- load_command_log_entry = (
866
+ if entity_path is not None and len(entity_path) > 0:
867
+ entity_path_len = len(entity_path)
868
+ entity_path_p = (c_char_p * entity_path_len)()
869
+ for i, entry in enumerate(entity_path):
870
+ entity_path_p[i] = cast(self.str_to_char_p(entry), c_char_p)
871
+
872
+ self.load_command_log_entry = (
825
873
  f"LOAD_ENTITY \"{self.escape_double_quotes(handle)}\" "
826
874
  f"\"{self.escape_double_quotes(file_path)}\" "
827
875
  f"\"{self.escape_double_quotes(file_type)}\" {str(persist).lower()} "
828
876
  f"{json_lib.dumps(json_file_params)} "
829
- f"\"{write_log}\" \"{print_log}\""
830
- )
831
- self._log_execution(load_command_log_entry)
877
+ f"\"{write_log}\" \"{print_log}\" \"\" \"{' '.join(entity_path or [])}\""
878
+ ).encode()
879
+ self._log_execution(self.load_command_log_entry)
832
880
  result = LoadEntityStatus(self, self.amlg.LoadEntity(
833
881
  handle_buf, file_path_buf, file_type_buf, persist,
834
- json_file_params_buf, write_log_buf, print_log_buf))
882
+ json_file_params_buf, write_log_buf, print_log_buf, entity_path_p, entity_path_len))
835
883
  self._log_reply(result)
836
884
 
837
885
  del handle_buf
@@ -840,6 +888,98 @@ class Amalgam:
840
888
  del json_file_params_buf
841
889
  del write_log_buf
842
890
  del print_log_buf
891
+ if entity_path_p is not None:
892
+ for i in range(entity_path_len):
893
+ entry_buf = entity_path_p[i]
894
+ del entry_buf
895
+ del entity_path_p
896
+ self.gc()
897
+
898
+ return result
899
+
900
+ def load_entity_from_memory(
901
+ self,
902
+ handle: str,
903
+ contents: bytes,
904
+ *,
905
+ file_type: str,
906
+ json_file_params: str = "",
907
+ write_log: str = "",
908
+ print_log: str = "",
909
+ entity_path: list[str] | None = None,
910
+ ) -> LoadEntityStatus:
911
+ """
912
+ Load an entity from an in-memory buffer.
913
+
914
+ Parameters
915
+ ----------
916
+ handle : str
917
+ The handle to assign the entity.
918
+ contents : bytes
919
+ The content to load.
920
+ file_type : str
921
+ The type of file to store, typically ``"amlg"`` for plain-text Amalgam or ``"caml"``
922
+ for binary compressed Amalgam.
923
+ json_file_params : str, default ""
924
+ Either empty string or a string of json specifying a set of key-value pairs
925
+ which are parameters specific to the file type. See Amalgam documentation
926
+ for details of allowed parameters.
927
+ write_log : str, default ""
928
+ Path to the write log. If empty string, the write log is
929
+ not generated.
930
+ print_log : str, default ""
931
+ Path to the print log. If empty string, the print log is
932
+ not generated.
933
+ entity_path: list[str], optional
934
+ If provided and non-empty, load the content into an entity
935
+ contained within `handle` at this path.
936
+
937
+ Returns
938
+ -------
939
+ LoadEntityStatus
940
+ Status of LoadEntity call.
941
+ """
942
+ self.amlg.LoadEntityFromMemory.argtypes = [
943
+ c_char_p, c_void_p, c_size_t, c_char_p, c_bool, c_char_p, c_char_p, c_char_p, POINTER(c_char_p),
944
+ c_size_t]
945
+ self.amlg.LoadEntityFromMemory.restype = _LoadEntityStatus
946
+ handle_buf = self.str_to_char_p(handle)
947
+ file_type_buf = self.str_to_char_p(file_type)
948
+ json_file_params_buf = self.str_to_char_p(json_file_params)
949
+ write_log_buf = self.str_to_char_p(write_log)
950
+ print_log_buf = self.str_to_char_p(print_log)
951
+ entity_path_p = None
952
+ entity_path_len = 0
953
+
954
+ if entity_path is not None and len(entity_path) > 0:
955
+ entity_path_len = len(entity_path)
956
+ entity_path_p = (c_char_p * entity_path_len)()
957
+ for i, entry in enumerate(entity_path):
958
+ entity_path_p[i] = cast(self.str_to_char_p(entry), c_char_p)
959
+
960
+ self.load_command_log_entry = (
961
+ f"LOAD_ENTITY_FROM_MEMORY \"{self.escape_double_quotes(handle)}\" "
962
+ f"\"{b64encode(contents).decode()}\" "
963
+ f"\"{self.escape_double_quotes(file_type)}\" false "
964
+ f"{json_lib.dumps(json_file_params)} "
965
+ f"\"{write_log}\" \"{print_log}\" \"\" \"{' '.join(entity_path or [])}\""
966
+ ).encode()
967
+ self._log_execution(self.load_command_log_entry)
968
+ result = LoadEntityStatus(self, self.amlg.LoadEntityFromMemory(
969
+ handle_buf, contents, len(contents), file_type_buf, False,
970
+ json_file_params_buf, write_log_buf, print_log_buf, entity_path_p, entity_path_len))
971
+ self._log_reply(result)
972
+
973
+ del handle_buf
974
+ del file_type_buf
975
+ del json_file_params_buf
976
+ del write_log_buf
977
+ del print_log_buf
978
+ if entity_path_p is not None:
979
+ for i in range(entity_path_len):
980
+ entry_buf = entity_path_p[i]
981
+ del entry_buf
982
+ del entity_path_p
843
983
  self.gc()
844
984
 
845
985
  return result
@@ -865,7 +1005,7 @@ class Amalgam:
865
1005
  self.amlg.VerifyEntity.restype = _LoadEntityStatus
866
1006
  file_path_buf = self.str_to_char_p(file_path)
867
1007
 
868
- self._log_execution(f"VERIFY_ENTITY \"{self.escape_double_quotes(file_path)}\"")
1008
+ self._log_execution_std(b"VERIFY_ENTITY", file_path)
869
1009
  result = LoadEntityStatus(self, self.amlg.VerifyEntity(file_path_buf))
870
1010
  self._log_reply(result)
871
1011
 
@@ -895,7 +1035,7 @@ class Amalgam:
895
1035
  self.amlg.GetEntityPermissions.restype = POINTER(c_char)
896
1036
  handle_buf = self.str_to_char_p(handle)
897
1037
 
898
- self._log_execution(f"GET_ENTITY_PERMISSIONS \"{self.escape_double_quotes(handle)}\"")
1038
+ self._log_execution_std(b"GET_ENTITY_PERMISSIONS", handle)
899
1039
  result = self.char_p_to_bytes(self.amlg.GetEntityPermissions(handle_buf))
900
1040
  self._log_reply(result)
901
1041
 
@@ -931,11 +1071,7 @@ class Amalgam:
931
1071
  handle_buf = self.str_to_char_p(handle)
932
1072
  json_permissions_buf = self.str_to_char_p(json_permissions)
933
1073
 
934
- set_permissions_log_entry = (
935
- f"SET_ENTITY_PERMISSIONS \"{self.escape_double_quotes(handle)}\" "
936
- f"{json_permissions}"
937
- )
938
- self._log_execution(set_permissions_log_entry)
1074
+ self._log_execution_std(b"SET_ENTITY_PERMISSIONS", handle, suffix=json_permissions)
939
1075
  result = self.amlg.SetEntityPermissions(handle_buf, json_permissions_buf)
940
1076
  self._log_reply(result)
941
1077
 
@@ -999,15 +1135,13 @@ class Amalgam:
999
1135
  write_log_buf = self.str_to_char_p(write_log)
1000
1136
  print_log_buf = self.str_to_char_p(print_log)
1001
1137
 
1002
- clone_command_log_entry = (
1003
- f'CLONE_ENTITY "{self.escape_double_quotes(handle)}" '
1004
- f'"{self.escape_double_quotes(clone_handle)}" '
1005
- f"\"{self.escape_double_quotes(file_path)}\" "
1006
- f"\"{self.escape_double_quotes(file_type)}\" {str(persist).lower()} "
1007
- f"{json_lib.dumps(json_file_params)} "
1008
- f"\"{write_log}\" \"{print_log}\""
1138
+ self._log_execution_std(b"CLONE_ENTITY", handle, clone_handle, file_path, file_type,
1139
+ suffix=(
1140
+ f"{str(persist).lower()} "
1141
+ f"{json_lib.dumps(json_file_params)} "
1142
+ f"\"{write_log}\" \"{print_log}\""
1143
+ )
1009
1144
  )
1010
- self._log_execution(clone_command_log_entry)
1011
1145
  result = self.amlg.CloneEntity(
1012
1146
  handle_buf, clone_handle_buf, file_path_buf, file_type_buf, persist,
1013
1147
  json_file_params_buf, write_log_buf, print_log_buf)
@@ -1032,7 +1166,8 @@ class Amalgam:
1032
1166
  file_type: str = "",
1033
1167
  persist: bool = False,
1034
1168
  json_file_params: str = "",
1035
- ):
1169
+ entity_path: list[str] | None = None,
1170
+ ) -> bool:
1036
1171
  """
1037
1172
  Store entity to the file type specified within file_path.
1038
1173
 
@@ -1051,30 +1186,122 @@ class Amalgam:
1051
1186
  Either empty string or a string of json specifying a set of key-value pairs
1052
1187
  which are parameters specific to the file type. See Amalgam documentation
1053
1188
  for details of allowed parameters.
1189
+ entity_path: list[str], optional
1190
+ If provided and non-empty, store the content from an entity
1191
+ contained within `handle` at this path.
1192
+
1193
+ Returns
1194
+ -------
1195
+ bool
1196
+ True if the entity file was saved successfully, False otherwise.
1054
1197
  """
1055
1198
  self.amlg.StoreEntity.argtypes = [
1056
- c_char_p, c_char_p, c_char_p, c_bool, c_char_p]
1199
+ c_char_p, c_char_p, c_char_p, c_bool, c_char_p, POINTER(c_char_p), c_size_t]
1200
+ self.amlg.StoreEntity.restype = c_bool
1057
1201
  handle_buf = self.str_to_char_p(handle)
1058
1202
  file_path_buf = self.str_to_char_p(file_path)
1059
1203
  file_type_buf = self.str_to_char_p(file_type)
1060
1204
  json_file_params_buf = self.str_to_char_p(json_file_params)
1061
-
1062
- store_command_log_entry = (
1063
- f"STORE_ENTITY \"{self.escape_double_quotes(handle)}\" "
1064
- f"\"{self.escape_double_quotes(file_path)}\" "
1065
- f"\"{self.escape_double_quotes(file_type)}\" {str(persist).lower()} "
1066
- f"{json_lib.dumps(json_file_params)} "
1067
- )
1068
- self._log_execution(store_command_log_entry)
1069
- self.amlg.StoreEntity(
1070
- handle_buf, file_path_buf, file_type_buf, persist, json_file_params_buf)
1071
- self._log_reply(None)
1205
+ entity_path_p = None
1206
+ entity_path_len = 0
1207
+
1208
+ if entity_path is not None and len(entity_path) > 0:
1209
+ entity_path_len = len(entity_path)
1210
+ entity_path_p = (c_char_p * entity_path_len)()
1211
+ for i, entry in enumerate(entity_path):
1212
+ entity_path_p[i] = self.str_to_char_p(entry)
1213
+
1214
+ self._log_execution_std(b"STORE_ENTITY", handle, file_path, file_type,
1215
+ suffix=f"{str(persist).lower()} {json_lib.dumps(json_file_params)} \"{' '.join(entity_path or [])}\"")
1216
+ result = self.amlg.StoreEntity(
1217
+ handle_buf, file_path_buf, file_type_buf, persist, json_file_params_buf, entity_path_p, entity_path_len)
1218
+ self._log_reply(result)
1072
1219
 
1073
1220
  del handle_buf
1074
1221
  del file_path_buf
1075
1222
  del file_type_buf
1076
1223
  del json_file_params_buf
1224
+ if entity_path_p is not None:
1225
+ for i in range(entity_path_len):
1226
+ entry_buf = entity_path_p[i]
1227
+ del entry_buf
1228
+ del entity_path_p
1229
+ self.gc()
1230
+ return result
1231
+
1232
+ def store_entity_to_memory(
1233
+ self,
1234
+ handle: str,
1235
+ *,
1236
+ file_type: str,
1237
+ json_file_params: str = "",
1238
+ entity_path: list[str] | None = None,
1239
+ ) -> bytes | None:
1240
+ """
1241
+ Store entity to memory as a bytes object.
1242
+
1243
+ Parameters
1244
+ ----------
1245
+ handle : str
1246
+ The handle of the amalgam entity.
1247
+ file_type : str
1248
+ The type of file to store, typically ``"amlg"`` for plain-text Amalgam or ``"caml"``
1249
+ for binary compressed Amalgam.
1250
+ json_file_params : str, default ""
1251
+ Either empty string or a string of json specifying a set of key-value pairs
1252
+ which are parameters specific to the file type. See Amalgam documentation
1253
+ for details of allowed parameters.
1254
+ entity_path: list[str], optional
1255
+ If provided and non-empty, store the content from an entity
1256
+ contained within `handle` at this path.
1257
+
1258
+ Returns
1259
+ -------
1260
+ bytes | None
1261
+ The serialized entity contents, or None on an error.
1262
+ """
1263
+ self.amlg.StoreEntityToMemory.argtypes = [
1264
+ c_char_p, POINTER(c_void_p), POINTER(c_size_t), c_char_p, c_bool, c_char_p, POINTER(c_char_p), c_size_t]
1265
+ self.amlg.StoreEntityToMemory.restype = c_bool
1266
+ handle_buf = self.str_to_char_p(handle)
1267
+ data_p = c_void_p(None)
1268
+ data_len = c_size_t(0)
1269
+ file_type_buf = self.str_to_char_p(file_type)
1270
+ json_file_params_buf = self.str_to_char_p(json_file_params)
1271
+ entity_path_p = None
1272
+ entity_path_len = 0
1273
+
1274
+ if entity_path is not None and len(entity_path) > 0:
1275
+ entity_path_len = len(entity_path)
1276
+ entity_path_p = (c_char_p * entity_path_len)()
1277
+ for i, entry in enumerate(entity_path):
1278
+ entity_path_p[i] = self.str_to_char_p(entry)
1279
+
1280
+ self._log_execution_std(b"STORE_ENTITY_TO_MEMORY", handle, file_type,
1281
+ suffix=f"false {json_lib.dumps(json_file_params)} \"{' '.join(entity_path or [])}\"")
1282
+ self.amlg.StoreEntityToMemory(
1283
+ handle_buf, byref(data_p), byref(data_len), file_type_buf, False, json_file_params_buf, entity_path_p, entity_path_len)
1284
+
1285
+ result = None
1286
+ if data_p.value is not None:
1287
+ result = string_at(data_p.value, data_len.value)
1288
+ self.amlg.DeleteString.argtypes = [c_char_p]
1289
+ self.amlg.DeleteString.restype = None
1290
+ self.amlg.DeleteString(cast(data_p, c_char_p))
1291
+ self._log_reply(b64encode(result).decode())
1292
+ else:
1293
+ self._log_reply(None)
1294
+
1295
+ del handle_buf
1296
+ del file_type_buf
1297
+ del json_file_params_buf
1298
+ if entity_path_p is not None:
1299
+ for i in range(entity_path_len):
1300
+ entry_buf = entity_path_p[i]
1301
+ del entry_buf
1302
+ del entity_path_p
1077
1303
  self.gc()
1304
+ return result
1078
1305
 
1079
1306
  def destroy_entity(
1080
1307
  self,
@@ -1091,7 +1318,7 @@ class Amalgam:
1091
1318
  self.amlg.DestroyEntity.argtypes = [c_char_p]
1092
1319
  handle_buf = self.str_to_char_p(handle)
1093
1320
 
1094
- self._log_execution(f"DESTROY_ENTITY \"{self.escape_double_quotes(handle)}\"")
1321
+ self._log_execution_std(b"DESTROY_ENTITY", handle)
1095
1322
  self.amlg.DestroyEntity(handle_buf)
1096
1323
  self._log_reply(None)
1097
1324
 
@@ -1124,8 +1351,7 @@ class Amalgam:
1124
1351
  handle_buf = self.str_to_char_p(handle)
1125
1352
  rand_seed_buf = self.str_to_char_p(rand_seed)
1126
1353
 
1127
- self._log_execution(f'SET_RANDOM_SEED "{self.escape_double_quotes(handle)}" '
1128
- f'"{self.escape_double_quotes(rand_seed)}"')
1354
+ self._log_execution_std(b"SET_RANDOM_SEED", handle, rand_seed)
1129
1355
  result = self.amlg.SetRandomSeed(handle_buf, rand_seed_buf)
1130
1356
  self._log_reply(None)
1131
1357
 
@@ -1186,12 +1412,7 @@ class Amalgam:
1186
1412
  json_buf = self.str_to_char_p(json)
1187
1413
 
1188
1414
  self._log_time("EXECUTION START")
1189
- self._log_execution((
1190
- "EXECUTE_ENTITY_JSON "
1191
- f"\"{self.escape_double_quotes(handle)}\" "
1192
- f"\"{self.escape_double_quotes(label)}\" "
1193
- f"{json}"
1194
- ))
1415
+ self._log_execution_std(b"EXECUTE_ENTITY_JSON", handle, label, suffix=json)
1195
1416
  result = self.char_p_to_bytes(self.amlg.ExecuteEntityJsonPtr(
1196
1417
  handle_buf, label_buf, json_buf))
1197
1418
  self._log_time("EXECUTION STOP")
@@ -1236,12 +1457,7 @@ class Amalgam:
1236
1457
  json_buf = self.str_to_char_p(json)
1237
1458
 
1238
1459
  self._log_time("EXECUTION START")
1239
- self._log_execution((
1240
- "EXECUTE_ENTITY_JSON_LOGGED "
1241
- f"\"{self.escape_double_quotes(handle)}\" "
1242
- f"\"{self.escape_double_quotes(label)}\" "
1243
- f"{json}"
1244
- ))
1460
+ self._log_execution_std(b"EXECUTE_ENTITY_JSON_LOGGED", handle, label, suffix=json)
1245
1461
  result = ResultWithLog.from_c_result(self, self.amlg.ExecuteEntityJsonPtrLogged(
1246
1462
  handle_buf, label_buf, json_buf))
1247
1463
  self._log_time("EXECUTION STOP")
@@ -1256,7 +1472,7 @@ class Amalgam:
1256
1472
  def eval_on_entity(
1257
1473
  self,
1258
1474
  handle: str,
1259
- amlg: str
1475
+ amlg: str | bytes
1260
1476
  ) -> bytes:
1261
1477
  """
1262
1478
  Execute arbitrary Amalgam code against an entity.
@@ -1265,7 +1481,7 @@ class Amalgam:
1265
1481
  ----------
1266
1482
  handle : str
1267
1483
  The handle of the amalgam entity.
1268
- amlg : str
1484
+ amlg : str | bytes
1269
1485
  The code to execute.
1270
1486
 
1271
1487
  Returns
@@ -1281,11 +1497,7 @@ class Amalgam:
1281
1497
  amlg_buf = self.str_to_char_p(amlg)
1282
1498
 
1283
1499
  self._log_time("EXECUTION START")
1284
- self._log_execution((
1285
- "EVAL_ON_ENTITY "
1286
- f"\"{self.escape_double_quotes(handle)}\" "
1287
- f"\"{self.escape_double_quotes(amlg)}\""
1288
- ))
1500
+ self._log_execution_std(b"EVAL_ON_ENTITY", handle, str(amlg))
1289
1501
  result = self.char_p_to_bytes(self.amlg.EvalOnEntity(
1290
1502
  handle_buf, amlg_buf))
1291
1503
  self._log_time("EXECUTION STOP")
Binary file
Binary file
Binary file
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "63.0.2"
2
+ "version": "71.0.0"
3
3
  }
amalgam/lib/version.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "version": {
3
- "amalgam": "63.0.2",
4
- "amalgam_sha": "ef44c8dc3bccad4141cec35f32852a35ebe667da",
5
- "amalgam_url": "https://github.com/howsoai/amalgam/releases/tag/63.0.2",
3
+ "amalgam": "71.0.0",
4
+ "amalgam_sha": "9a65d4226a5166e9e3968057a603287096fa7005",
5
+ "amalgam_url": "https://github.com/howsoai/amalgam/releases/tag/71.0.0",
6
6
  "amalgam_build_date": "",
7
7
  "amalgam_display_title": ""
8
8
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amalgam-lang
3
- Version: 20.0.2
3
+ Version: 25.2.5
4
4
  Summary: A direct interface with Amalgam compiled DLL, dylib, or so.
5
5
  Author: Howso Incorporated
6
6
  Author-email: support@howso.com
@@ -675,7 +675,6 @@ Classifier: Intended Audience :: Science/Research
675
675
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
676
676
  Classifier: License :: OSI Approved :: GNU Affero General Public License v3
677
677
  Classifier: Programming Language :: Python :: 3
678
- Classifier: Programming Language :: Python :: 3.9
679
678
  Classifier: Programming Language :: Python :: 3.10
680
679
  Classifier: Programming Language :: Python :: 3.11
681
680
  Classifier: Programming Language :: Python :: 3.12
@@ -686,7 +685,7 @@ Classifier: Operating System :: Microsoft :: Windows
686
685
  Classifier: Operating System :: Microsoft :: Windows :: Windows 10
687
686
  Classifier: Operating System :: Microsoft :: Windows :: Windows 11
688
687
  Classifier: Operating System :: POSIX :: Linux
689
- Requires-Python: >=3.9
688
+ Requires-Python: >=3.10
690
689
  Description-Content-Type: text/markdown
691
690
  License-File: LICENSE.txt
692
691
  Provides-Extra: dev
@@ -714,7 +713,7 @@ Coding in [Amalgam](https://github.com/howsoai/amalgam) can be done natively as
714
713
 
715
714
  ## Supported Platforms
716
715
 
717
- Compatible with Python versions: 3.9, 3.10, 3.11, 3.12, and 3.13.
716
+ Compatible with Python versions: 3.10, 3.11, 3.12, and 3.13.
718
717
 
719
718
  #### Operating Systems
720
719
 
@@ -0,0 +1,12 @@
1
+ amalgam/__init__.py,sha256=oHu7Zr4eGDUqj93pLwz8t7gLa8lpAx6Q-xbGiJ3nJx0,18
2
+ amalgam/api.py,sha256=2LeiTkl6FLdd-MDDHjkWAl7vLOnI3eUXF1FeV9fEkGQ,53792
3
+ amalgam/lib/version.json,sha256=KLIEcoEJPWvPnfdbzhK0grsTolOYwMz_29lG2g-qcas,250
4
+ amalgam/lib/darwin/arm64/amalgam-mt.dylib,sha256=8XphNYzG6P31ZviKFc_8NEudhf1T3B-7vmuinM77lh4,3362896
5
+ amalgam/lib/darwin/arm64/amalgam-omp.dylib,sha256=La4iFFCNoyplm64Ps5EEiVt8fnEmOYDA-emB2RVQsBk,3700560
6
+ amalgam/lib/darwin/arm64/amalgam-st.dylib,sha256=Hsu5IeZBCnk-8b6ozrkMUXkeW-VNVEDQ4UO8b8tnvI0,3144768
7
+ amalgam/lib/darwin/arm64/docs/version.json,sha256=DGQGMrgbSvJpyyK_YIzD8RF4xP8dAjQgiIsJ3sWHv3w,25
8
+ amalgam_lang-25.2.5.dist-info/licenses/LICENSE.txt,sha256=2xqHuoHohba7gpcZZKtOICRjzeKsQANXG8WoV9V35KM,33893
9
+ amalgam_lang-25.2.5.dist-info/METADATA,sha256=0V6bApR_dTvfvRdqJleSkpeEeUrLxNdXSE2jzDoqwRQ,43832
10
+ amalgam_lang-25.2.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
+ amalgam_lang-25.2.5.dist-info/top_level.txt,sha256=rmPHU144SyaB25u5-FAQyECAQnJ39NvuJEcKXMRcdBo,8
12
+ amalgam_lang-25.2.5.dist-info/RECORD,,
@@ -1,12 +0,0 @@
1
- amalgam/__init__.py,sha256=oHu7Zr4eGDUqj93pLwz8t7gLa8lpAx6Q-xbGiJ3nJx0,18
2
- amalgam/api.py,sha256=zS2h1oKrNmXNERicQt6dcpjrOQ9XVm5PTf37AIDlMTI,44799
3
- amalgam/lib/version.json,sha256=W2qXXdNREbzHXVO0UKN4ajwB-IFPwD5DNKlzgGpmGK0,250
4
- amalgam/lib/darwin/arm64/amalgam-mt.dylib,sha256=9qVrSakUqC11nGbfSR3SuZVArDKfz3PaGmpHIcYLK0w,2951696
5
- amalgam/lib/darwin/arm64/amalgam-omp.dylib,sha256=eRAzV9wSZCrTgwV1j1e1DPFN5675RMwGJx397HZH-qE,3313728
6
- amalgam/lib/darwin/arm64/amalgam-st.dylib,sha256=y-mcPzMjfBCaQmeeNYL59dc5YDL15r5S5LcEp9Ab-GU,2783280
7
- amalgam/lib/darwin/arm64/docs/version.json,sha256=Yg7pfVbtDLaoqfdzh_PyMWGT90q46seEaJ3NSDe-4QU,25
8
- amalgam_lang-20.0.2.dist-info/licenses/LICENSE.txt,sha256=2xqHuoHohba7gpcZZKtOICRjzeKsQANXG8WoV9V35KM,33893
9
- amalgam_lang-20.0.2.dist-info/METADATA,sha256=jML8rmgRW4YKfGGdxONHp1nqsPSr3GNiDGGZeYN7S00,43886
10
- amalgam_lang-20.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
- amalgam_lang-20.0.2.dist-info/top_level.txt,sha256=rmPHU144SyaB25u5-FAQyECAQnJ39NvuJEcKXMRcdBo,8
12
- amalgam_lang-20.0.2.dist-info/RECORD,,