psr-factory 5.0.0b21__py3-none-win_amd64.whl → 5.0.0b69__py3-none-win_amd64.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.
psr/factory/api.py CHANGED
@@ -2,8 +2,10 @@
2
2
  # Unauthorized copying of this file, via any medium is strictly prohibited
3
3
  # Proprietary and confidential
4
4
 
5
+ from __future__ import annotations
6
+
5
7
  import locale
6
- from typing import Dict, List, Optional, Tuple, Union
8
+ from typing import Dict, List, Optional, Tuple, Union, Any
7
9
  from types import ModuleType
8
10
  import copy
9
11
  import ctypes
@@ -16,6 +18,8 @@ import threading
16
18
  import pathlib
17
19
  import warnings
18
20
 
21
+ from typing import TypeAlias
22
+
19
23
  from . import factorylib
20
24
 
21
25
 
@@ -30,9 +34,30 @@ pandas: Optional[ModuleType] = None
30
34
  polars: Optional[ModuleType] = None
31
35
  numpy: Optional[ModuleType] = None
32
36
 
37
+ # Values returned by the library.
38
+ ValueLike: TypeAlias = Union[
39
+ bool,
40
+ int,
41
+ float,
42
+ dt.datetime,
43
+ str,
44
+ "DataObject",
45
+ List[Any],
46
+ Dict[str, Any],
47
+ None,
48
+ ]
49
+
50
+ PathLike: TypeAlias = Union[str, pathlib.Path]
51
+
52
+ DateLike: TypeAlias = Union[str, dt.datetime]
53
+
54
+ DataFrameLike: TypeAlias = Union["pandas.DataFrame", "polars.DataFrame", "DataFrame"]
55
+
33
56
 
34
57
  _default_dataframe_type: str = "pandas"
35
58
 
59
+ _TYPES_WITHOUT_CONTEXT = ("Context", "ConvertOutputOptions", "DataFrameLoadOptions", "DataFrameSaveOptions", "DataFrameMetadata", "StudyLoadOptions", "StudySaveOptions")
60
+
36
61
 
37
62
  def _has_pandas() -> bool:
38
63
  """Check if pandas is available."""
@@ -88,11 +113,14 @@ def get_default_dataframe_type() -> str:
88
113
  """Get the default dataframe type used by the library."""
89
114
  return _default_dataframe_type
90
115
 
116
+ _basic_data_initialized = False
117
+ _basic_data_initialized_lock = threading.Lock()
118
+
119
+ _study_data_initialized = False
120
+ _study_data_initialized_lock = threading.Lock()
91
121
 
92
- # States whether the library is initialized.
93
- # It should be to correctly load and build models.
94
- _initialized = False
95
- _initialized_lock = threading.Lock()
122
+ _constants_initialized = False
123
+ _constants_initialized_lock = threading.Lock()
96
124
 
97
125
  _loaded = False
98
126
  _loaded_lock = threading.Lock()
@@ -101,16 +129,22 @@ _loaded_lock = threading.Lock()
101
129
  _preferred_encoding = locale.getpreferredencoding()
102
130
 
103
131
  # Internal date epoch
104
- # TODO: make it a library call.
105
- _date_transform = 12622780800
132
+ _date_transform: Optional[int] = None
106
133
 
107
134
 
108
- def _check_initialized():
135
+ def _check_basic_data_initialized():
109
136
  """Checks if the module was initialized."""
110
- global _initialized
111
- if not _initialized:
112
- _initialize()
113
- return _initialized
137
+ global _basic_data_initialized
138
+ if not _basic_data_initialized:
139
+ _initialize_basic_data()
140
+ return _basic_data_initialized
141
+
142
+ def _check_study_data_initialized():
143
+ """Checks if the module was initialized."""
144
+ global _study_data_initialized
145
+ if not _study_data_initialized:
146
+ _initialize_study_data()
147
+ return _study_data_initialized
114
148
 
115
149
 
116
150
  def _check_loaded() -> bool:
@@ -192,7 +226,7 @@ def version() -> str:
192
226
  return _version_long()
193
227
 
194
228
 
195
- def build_version() -> str:
229
+ def short_version() -> str:
196
230
  """Returns short library version."""
197
231
  return _version_short()
198
232
 
@@ -258,9 +292,11 @@ def set_diagnostics_mode(value: Union[bool, int]):
258
292
 
259
293
 
260
294
  def diagnostics() -> str:
261
- global _initialized
262
- global _initialized_lock
263
- with _initialized_lock:
295
+ global _basic_data_initialized
296
+ global _basic_data_initialized_lock
297
+ global _study_data_initialized
298
+ global _study_data_initialized_lock
299
+ with _basic_data_initialized_lock, _study_data_initialized_lock:
264
300
  """Get diagnostics information."""
265
301
  py_diagnostics = f"Python version: {sys.version}\n" \
266
302
  f"Python encoding: {sys.getdefaultencoding()}\n" \
@@ -279,41 +315,48 @@ def diagnostics() -> str:
279
315
  buffer, size, error.handler())
280
316
  if error.code != 0:
281
317
  raise FactoryException(error.what)
282
- _initialized = True
318
+ _basic_data_initialized = True
319
+ _study_data_initialized = True
283
320
  return py_diagnostics + _from_c_str(buffer.value)
284
321
 
285
322
 
286
- def get_setting(key: str) -> Union[str, int, float, bool]:
287
- global _initialized
288
- global _initialized_lock
289
- with _initialized_lock:
290
- _check_loaded()
291
- error = Error()
292
- value = Value()
293
- factorylib.lib.psrd_get_global_setting(_c_str(key),
294
- _bytes(key),
295
- value.handler(),
296
- error.handler())
297
- if error.code != 0:
298
- raise FactoryException(error.what)
299
- return value.get()
323
+ def get_constant(key: str) -> ValueLike:
324
+ _check_loaded()
325
+ error = Error()
326
+ value = Value()
327
+ factorylib.lib.psrd_get_constant(_c_str(key),
328
+ _bytes(key),
329
+ value.handler(),
330
+ error.handler())
331
+ if error.code != 0:
332
+ raise FactoryException(error.what)
333
+ return value.get()
300
334
 
301
335
 
302
- def set_setting(key: str, value: Union[str, int, float, bool]):
303
- global _initialized
304
- global _initialized_lock
305
- with _initialized_lock:
306
- _check_loaded()
307
- error = Error()
308
- _value = Value()
309
- _value.set(value)
310
- factorylib.lib.psrd_set_global_setting(_c_str(key),
311
- _bytes(key),
312
- _value.handler(),
313
- error.handler())
314
- if error.code != 0:
315
- raise FactoryException(error.what)
336
+ def get_setting(key: str) -> ValueLike:
337
+ _check_loaded()
338
+ error = Error()
339
+ value = Value()
340
+ factorylib.lib.psrd_get_global_setting(_c_str(key),
341
+ _bytes(key),
342
+ value.handler(),
343
+ error.handler())
344
+ if error.code != 0:
345
+ raise FactoryException(error.what)
346
+ return value.get()
347
+
316
348
 
349
+ def set_setting(key: str, value: ValueLike):
350
+ _check_loaded()
351
+ error = Error()
352
+ _value = Value()
353
+ _value.set(value)
354
+ factorylib.lib.psrd_set_global_setting(_c_str(key),
355
+ _bytes(key),
356
+ _value.handler(),
357
+ error.handler())
358
+ if error.code != 0:
359
+ raise FactoryException(error.what)
317
360
 
318
361
 
319
362
  def _get_context(models_or_context: Union[str, list, dict, "Context", None],
@@ -478,7 +521,7 @@ class ValueDict(_BaseObject):
478
521
  if self._hdr is not None:
479
522
  factorylib.lib.psrd_free_dict(self._hdr)
480
523
 
481
- def __getitem__(self, key: Union["Value", bool, int, float, dt.datetime, str, "DataObject", list, dict, None]) -> Union["Value", bool, int, float, dt.datetime, str, "DataObject", list, dict, None]:
524
+ def __getitem__(self, key: ValueLike) -> ValueLike:
482
525
  if not isinstance(key, Value):
483
526
  old_key = key
484
527
  key = Value()
@@ -493,7 +536,7 @@ class ValueDict(_BaseObject):
493
536
  raise FactoryException(error.what)
494
537
  return value.get()
495
538
 
496
- def __contains__(self, key: Union["Value", bool, int, float, dt.datetime, str, "DataObject", list, dict, None]) -> bool:
539
+ def __contains__(self, key: ValueLike) -> bool:
497
540
  if not isinstance(key, Value):
498
541
  old_key = key
499
542
  key = Value()
@@ -526,7 +569,7 @@ class ValueDict(_BaseObject):
526
569
  def __iter__(self):
527
570
  return self
528
571
 
529
- def __next__(self) -> "Value":
572
+ def __next__(self) -> ValueLike:
530
573
  if self.index >= self.dict_obj.__len__():
531
574
  raise StopIteration
532
575
  factorylib.lib.psrd_dict_get_key_by_index(self.dict_obj._hdr, self.index,
@@ -550,7 +593,7 @@ class ValueDict(_BaseObject):
550
593
  def __iter__(self):
551
594
  return self
552
595
 
553
- def __next__(self) -> "Value":
596
+ def __next__(self) -> ValueLike:
554
597
  if self.index >= self.dict_obj.__len__():
555
598
  raise StopIteration
556
599
  factorylib.lib.psrd_dict_get_value_by_index(self.dict_obj._hdr, self.index,
@@ -575,7 +618,7 @@ class ValueDict(_BaseObject):
575
618
  def __iter__(self):
576
619
  return self
577
620
 
578
- def __next__(self) -> Tuple["Value", "Value"]:
621
+ def __next__(self) -> Tuple[ValueLike, ValueLike]:
579
622
  if self.index >= self.dict_obj.__len__():
580
623
  raise StopIteration
581
624
  factorylib.lib.psrd_dict_get_by_index(self.dict_obj._hdr, self.index,
@@ -649,7 +692,7 @@ class Value(_BaseObject):
649
692
  if self._hdr is not None:
650
693
  factorylib.lib.psrd_free_value(self._hdr)
651
694
 
652
- def get(self) -> Union[bool, int, float, dt.datetime, str, "DataObject", list, dict, None]:
695
+ def get(self) -> ValueLike:
653
696
  _err = Error()
654
697
  uint_value = ctypes.c_long()
655
698
  factorylib.lib.psrd_value_get_type(self._hdr,
@@ -667,13 +710,13 @@ class Value(_BaseObject):
667
710
  raise FactoryException(_err.what)
668
711
  return int(int_value.value)
669
712
  elif var_type == ValueType.INT64.value:
670
- long_value = ctypes.c_long()
713
+ long_value = ctypes.c_longlong()
671
714
  factorylib.lib.psrd_value_get_int64(self._hdr,
672
715
  ctypes.byref(long_value),
673
716
  _err.handler())
674
717
  if _err.code != 0:
675
718
  raise FactoryException(_err.what)
676
- return int(int_value.value)
719
+ return int(long_value.value)
677
720
  elif var_type == ValueType.FLOAT32.value:
678
721
  float_value = ctypes.c_float()
679
722
  factorylib.lib.psrd_value_get_float32(self._hdr,
@@ -706,7 +749,8 @@ class Value(_BaseObject):
706
749
  _err.handler())
707
750
  if _err.code != 0:
708
751
  raise FactoryException(_err.what)
709
-
752
+ if _date_transform is None:
753
+ raise FactoryException("Factory is not initialized correctly.")
710
754
  return dt.datetime.fromtimestamp(date_value.value - _date_transform, dt.timezone.utc)
711
755
 
712
756
  elif var_type == ValueType.BOOL.value:
@@ -748,7 +792,7 @@ class Value(_BaseObject):
748
792
  else:
749
793
  raise NotImplementedError()
750
794
 
751
- def set(self, value: Union[bool, int, float, dt.datetime, str, "DataObject", list, dict, None]):
795
+ def set(self, value: ValueLike):
752
796
  _err = Error()
753
797
  if isinstance(value, bool):
754
798
  factorylib.lib.psrd_value_set_bool(self._hdr, value,
@@ -756,6 +800,8 @@ class Value(_BaseObject):
756
800
  if _err.code != 0:
757
801
  raise FactoryException(_err.what)
758
802
  elif isinstance(value, dt.datetime):
803
+ if _date_transform is None:
804
+ raise FactoryException("Factory is not initialized correctly.")
759
805
  value.replace(tzinfo=dt.timezone.utc)
760
806
  date_epoch = int(value.timestamp()) + _date_transform
761
807
  factorylib.lib.psrd_value_set_date(self._hdr, date_epoch,
@@ -820,7 +866,7 @@ class Value(_BaseObject):
820
866
  raise FactoryException(_err.what)
821
867
 
822
868
  else:
823
- raise FactoryException(f"Unsupported type \"{type(value).name}\" for value.")
869
+ raise FactoryException(f"Unsupported type \"{type(value).__name__}\" for value.")
824
870
 
825
871
 
826
872
  class PropertyDescription(_BaseObject):
@@ -835,11 +881,11 @@ class PropertyDescription(_BaseObject):
835
881
  @property
836
882
  def name(self) -> str:
837
883
  _err = Error()
838
- # TODO: determine size automatically
839
- size = 300
884
+ size = factorylib.lib.psrd_property_description_get_name(self._hdr, None, 0, _err.handler())
885
+ if _err.code != 0:
886
+ raise FactoryException(_err.what)
840
887
  buffer = ctypes.create_string_buffer(size)
841
- factorylib.lib.psrd_property_description_get_name(self._hdr, buffer, size,
842
- _err.handler())
888
+ factorylib.lib.psrd_property_description_get_name(self._hdr, buffer, size, _err.handler())
843
889
  if _err.code != 0:
844
890
  raise FactoryException(_err.what)
845
891
  return _from_c_str(buffer.value)
@@ -855,8 +901,9 @@ class PropertyDescription(_BaseObject):
855
901
  @property
856
902
  def alt_name(self) -> str:
857
903
  _err = Error()
858
- # TODO: determine size automatically
859
- size = 300
904
+ size = factorylib.lib.psrd_property_description_get_alternative_name(self._hdr, None, 0, _err.handler())
905
+ if _err.code != 0:
906
+ raise FactoryException(_err.what)
860
907
  buffer = ctypes.create_string_buffer(size)
861
908
  factorylib.lib.psrd_property_description_get_alternative_name(
862
909
  self._hdr, buffer, size, _err.handler())
@@ -872,6 +919,26 @@ class PropertyDescription(_BaseObject):
872
919
  def alt_name(self):
873
920
  raise AttributeError("do not delete alt_name")
874
921
 
922
+ def is_required(self) -> bool:
923
+ _err = Error()
924
+ value = ctypes.c_bool()
925
+ factorylib.lib.psrd_property_description_is_required(self._hdr,
926
+ ctypes.byref(value),
927
+ _err.handler())
928
+ if _err.code != 0:
929
+ raise FactoryException(_err.what)
930
+ return bool(value.value)
931
+
932
+ def is_reference(self) -> bool:
933
+ _err = Error()
934
+ value = ctypes.c_bool()
935
+ factorylib.lib.psrd_property_description_is_reference(self._hdr,
936
+ ctypes.byref(value),
937
+ _err.handler())
938
+ if _err.code != 0:
939
+ raise FactoryException(_err.what)
940
+ return bool(value.value)
941
+
875
942
  def is_dynamic(self) -> bool:
876
943
  _err = Error()
877
944
  value = ctypes.c_bool()
@@ -924,8 +991,9 @@ class PropertyDescription(_BaseObject):
924
991
  dimensions_count = int(value.value)
925
992
 
926
993
  for i_dim in range(dimensions_count):
927
- # TODO: get size automatically
928
- size = 100
994
+ size = factorylib.lib.psrd_property_description_get_dimension_name(self._hdr, i_dim, None, 0, _err.handler())
995
+ if _err.code != 0:
996
+ raise FactoryException(_err.what)
929
997
  buffer = ctypes.create_string_buffer(size)
930
998
  factorylib.lib.psrd_property_description_get_dimension_name(self._hdr,
931
999
  i_dim, buffer,
@@ -934,7 +1002,6 @@ class PropertyDescription(_BaseObject):
934
1002
  if _err.code != 0:
935
1003
  raise FactoryException(_err.what)
936
1004
  name = _from_c_str(buffer.value)
937
-
938
1005
  factorylib.lib.psrd_property_description_get_dimension_size(self._hdr,
939
1006
  i_dim,
940
1007
  ctypes.byref(value),
@@ -967,7 +1034,6 @@ class PropertyDescription(_BaseObject):
967
1034
  return f"Property {self.name} with dimensions {self.dimensions()}"
968
1035
 
969
1036
 
970
-
971
1037
  class DataObject(_BaseObject):
972
1038
  def __init__(self):
973
1039
  super().__init__()
@@ -1009,16 +1075,16 @@ class DataObject(_BaseObject):
1009
1075
  dest._hdr = ref
1010
1076
  return dest
1011
1077
 
1012
- def __deepcopy__(self, memodict=None):
1078
+ def __deepcopy__(self, memo_dict=None):
1013
1079
  raise NotImplementedError()
1014
1080
 
1015
1081
  def __repr__(self):
1016
1082
  identifiers = []
1017
- if self.code != 0:
1083
+ if self.has_code():
1018
1084
  identifiers.append(f"code={self.code}")
1019
- if self.id != "":
1085
+ if self.has_id():
1020
1086
  identifiers.append(f"id={self.id.strip()}")
1021
- if self.name != "":
1087
+ if self.has_name():
1022
1088
  identifiers.append(f"name={self.name.strip()}")
1023
1089
  return f"psr.factory.DataObject({self.type}, {', '.join(identifiers)})"
1024
1090
 
@@ -1030,7 +1096,8 @@ class DataObject(_BaseObject):
1030
1096
 
1031
1097
  @property
1032
1098
  def context(self) -> "Context":
1033
- _check_initialized()
1099
+ _check_basic_data_initialized()
1100
+ _check_study_data_initialized()
1034
1101
  obj = Context()
1035
1102
  _err = Error()
1036
1103
  ref = factorylib.lib.psrd_object_context(self._hdr,
@@ -1040,7 +1107,7 @@ class DataObject(_BaseObject):
1040
1107
  obj._hdr = ref
1041
1108
  return obj
1042
1109
 
1043
- def properties(self) -> Dict[str, PropertyDescription]:
1110
+ def descriptions(self) -> Dict[str, PropertyDescription]:
1044
1111
  _err = Error()
1045
1112
  value = ctypes.c_long()
1046
1113
  factorylib.lib.psrd_object_property_description_count(self._hdr,
@@ -1061,7 +1128,30 @@ class DataObject(_BaseObject):
1061
1128
  properties[var.name] = var
1062
1129
  return properties
1063
1130
 
1064
- def get(self, expression: str) -> Union[int, float, dt.datetime, str, "DataObject", list, None]:
1131
+ def description(self, name: str) -> Optional[PropertyDescription]:
1132
+ _err = Error()
1133
+ var = PropertyDescription()
1134
+ ref = factorylib.lib.psrd_object_get_property_description_by_name(self._hdr,
1135
+ _c_str(name),
1136
+ _bytes(name),
1137
+ _err.handler())
1138
+ if _err.code != 0:
1139
+ raise FactoryException(_err.what)
1140
+ if ref is not None:
1141
+ var._hdr = ref
1142
+ return var
1143
+ return None
1144
+
1145
+ def has_property(self, expression: str) -> bool:
1146
+ _err = Error()
1147
+ bool_value = ctypes.c_bool()
1148
+ factorylib.lib.psrd_object_has_property(self._hdr, _c_str(expression), _bytes(expression),
1149
+ ctypes.byref(bool_value), _err.handler())
1150
+ if _err.code != 0:
1151
+ raise FactoryException(_err.what)
1152
+ return bool(bool_value.value)
1153
+
1154
+ def get(self, expression: str) -> ValueLike:
1065
1155
  value = Value()
1066
1156
  _err = Error()
1067
1157
  factorylib.lib.psrd_object_get_value(self._hdr,
@@ -1072,7 +1162,7 @@ class DataObject(_BaseObject):
1072
1162
  raise FactoryException(_err.what)
1073
1163
  return value.get()
1074
1164
 
1075
- def get_at(self, expression: str, range_expr: Union[str, dt.datetime]) -> Union[int, float, dt.datetime, str, "DataObject", list, None]:
1165
+ def get_at(self, expression: str, range_expr: DateLike) -> ValueLike:
1076
1166
  if not isinstance(range_expr, (str, dt.datetime)):
1077
1167
  raise FactoryException("range_expr must be a string or datetime object.")
1078
1168
  _value = Value()
@@ -1088,7 +1178,7 @@ class DataObject(_BaseObject):
1088
1178
  raise FactoryException(_err.what)
1089
1179
  return _value.get()
1090
1180
 
1091
- def as_dict(self) -> Dict[str, any]:
1181
+ def as_dict(self) -> Dict[str, ValueLike]:
1092
1182
  value_dict = ValueDict()
1093
1183
  error = Error()
1094
1184
  handler = factorylib.lib.psrd_object_get_as_dict(self._hdr,
@@ -1106,7 +1196,7 @@ class DataObject(_BaseObject):
1106
1196
  if error.code != 0:
1107
1197
  raise FactoryException(error.what)
1108
1198
 
1109
- def get_df(self, expression: str) -> Union["pandas.DataFrame", "polars.DataFrame", "DataFrame"]:
1199
+ def get_df(self, expression: str) -> DataFrameLike:
1110
1200
  _err = Error()
1111
1201
  _df = DataFrame()
1112
1202
  factorylib.lib.psrd_object_get_table(self._hdr, _df.handler(),
@@ -1130,7 +1220,7 @@ class DataObject(_BaseObject):
1130
1220
  if _err.code != 0:
1131
1221
  raise FactoryException(_err.what)
1132
1222
 
1133
- def set_at(self, expression: str, range_expr: Union[str, dt.datetime], value):
1223
+ def set_at(self, expression: str, range_expr: DateLike, value):
1134
1224
  if not isinstance(range_expr, (str, dt.datetime)):
1135
1225
  raise FactoryException("range_expr must be a string or datetime object.")
1136
1226
  _err = Error()
@@ -1147,19 +1237,19 @@ class DataObject(_BaseObject):
1147
1237
  if _err.code != 0:
1148
1238
  raise FactoryException(_err.what)
1149
1239
 
1150
- def set_df(self, dataframe_like):
1151
- if not _has_pandas():
1152
- raise ModuleNotFoundError("pandas required.")
1153
- dataframe_like = pandas.api.interchange.from_dataframe(dataframe_like)
1240
+ def set_df(self, dataframe_like: DataFrameLike):
1154
1241
  df_builder = _DataFrameBuilder()
1155
- _df = df_builder.build_from_pandas(dataframe_like)
1242
+ if _has_pandas() and isinstance(dataframe_like, pandas.DataFrame):
1243
+ _df = df_builder.build_from_pandas(dataframe_like)
1244
+ elif _has_polars() and isinstance(dataframe_like, polars.DataFrame):
1245
+ _df = df_builder.build_from_polars(dataframe_like)
1246
+ else:
1247
+ raise FactoryException("No supported DataFrame library is available.")
1156
1248
  _err = Error()
1157
- factorylib.lib.psrd_object_set_table(self._hdr, _df.handler(),
1158
- _err.handler())
1249
+ factorylib.lib.psrd_object_set_table(self._hdr, _df.handler(), _err.handler())
1159
1250
  if _err.code != 0:
1160
1251
  raise FactoryException(_err.what)
1161
1252
 
1162
-
1163
1253
  def clear_values(self, expression: str):
1164
1254
  _err = Error()
1165
1255
  factorylib.lib.psrd_object_clear_values(self._hdr,
@@ -1191,6 +1281,14 @@ class DataObject(_BaseObject):
1191
1281
  object_list._hdr = ref
1192
1282
  return object_list.to_list()
1193
1283
 
1284
+ def has_code(self) -> bool:
1285
+ _err = Error()
1286
+ bool_value = ctypes.c_bool()
1287
+ factorylib.lib.psrd_object_has_code(self._hdr, ctypes.byref(bool_value), _err.handler())
1288
+ if _err.code != 0:
1289
+ raise FactoryException(_err.what)
1290
+ return bool(bool_value.value)
1291
+
1194
1292
  @property
1195
1293
  def code(self) -> int:
1196
1294
  _err = Error()
@@ -1236,6 +1334,42 @@ class DataObject(_BaseObject):
1236
1334
  def type(self):
1237
1335
  raise AttributeError("do not delete type")
1238
1336
 
1337
+ @property
1338
+ def key(self) -> str:
1339
+ err = Error()
1340
+ size = factorylib.lib.psrd_object_get_key(self._hdr, None,
1341
+ 0, err.handler())
1342
+ if err.code != 0:
1343
+ raise FactoryException(err.what)
1344
+ buffer = ctypes.create_string_buffer(size)
1345
+ factorylib.lib.psrd_object_get_key(self._hdr, buffer,
1346
+ size, err.handler())
1347
+ if err.code == 0:
1348
+ return _from_c_str(buffer.value)
1349
+ raise FactoryException(err.what)
1350
+
1351
+ @key.setter
1352
+ def key(self, value: str):
1353
+ err = Error()
1354
+ factorylib.lib.psrd_object_set_key(self._hdr,
1355
+ _c_str(value),
1356
+ _bytes(value),
1357
+ err.handler())
1358
+ if err.code != 0:
1359
+ raise FactoryException(err.what)
1360
+
1361
+ @key.deleter
1362
+ def key(self):
1363
+ raise AttributeError("do not delete key")
1364
+
1365
+ def has_name(self) -> bool:
1366
+ _err = Error()
1367
+ bool_value = ctypes.c_bool()
1368
+ factorylib.lib.psrd_object_has_name(self._hdr, ctypes.byref(bool_value), _err.handler())
1369
+ if _err.code != 0:
1370
+ raise FactoryException(_err.what)
1371
+ return bool(bool_value.value)
1372
+
1239
1373
  @property
1240
1374
  def name(self) -> str:
1241
1375
  err = Error()
@@ -1264,6 +1398,14 @@ class DataObject(_BaseObject):
1264
1398
  def name(self):
1265
1399
  raise AttributeError("do not delete name")
1266
1400
 
1401
+ def has_id(self) -> bool:
1402
+ _err = Error()
1403
+ bool_value = ctypes.c_bool()
1404
+ factorylib.lib.psrd_object_has_id(self._hdr, ctypes.byref(bool_value), _err.handler())
1405
+ if _err.code != 0:
1406
+ raise FactoryException(_err.what)
1407
+ return bool(bool_value.value)
1408
+
1267
1409
  @property
1268
1410
  def id(self) -> str:
1269
1411
  err = Error()
@@ -1296,7 +1438,7 @@ class DataObject(_BaseObject):
1296
1438
  class Context(DataObject):
1297
1439
  @staticmethod
1298
1440
  def default_context() -> "Context":
1299
- _check_initialized()
1441
+ _check_basic_data_initialized()
1300
1442
  context = Context()
1301
1443
  err = Error()
1302
1444
  ref = factorylib.lib.psrd_get_default_context(err.handler())
@@ -1346,7 +1488,7 @@ class Study(_BaseObject):
1346
1488
  def __copy__(self):
1347
1489
  raise NotImplementedError()
1348
1490
 
1349
- def __deepcopy__(self, memodict=None):
1491
+ def __deepcopy__(self, memo_dict=None):
1350
1492
  dest = Study()
1351
1493
  _err = Error()
1352
1494
  ref = factorylib.lib.psrd_study_clone(self.handler(),
@@ -1357,7 +1499,8 @@ class Study(_BaseObject):
1357
1499
  return dest
1358
1500
 
1359
1501
  def create(self, object_type: str) -> DataObject:
1360
- _check_initialized()
1502
+ _check_basic_data_initialized()
1503
+ _check_study_data_initialized()
1361
1504
  return create(object_type, self.context)
1362
1505
 
1363
1506
 
@@ -1371,7 +1514,8 @@ class Study(_BaseObject):
1371
1514
  @staticmethod
1372
1515
  def create_object(model_or_context: Union[str, Context, dict, None],
1373
1516
  blocks: Optional[int] = None):
1374
- _check_initialized()
1517
+ _check_basic_data_initialized()
1518
+ _check_study_data_initialized()
1375
1519
  err = Error()
1376
1520
  context = _get_context(model_or_context, blocks)
1377
1521
  study = Study()
@@ -1382,12 +1526,13 @@ class Study(_BaseObject):
1382
1526
  return study
1383
1527
 
1384
1528
  @staticmethod
1385
- def load(study_path: Union[str, pathlib.Path], model_or_context: Union[str, Context, None],
1529
+ def load(study_path: PathLike, model_or_context: Union[str, Context, None],
1386
1530
  settings_only: bool = False,
1387
1531
  options: Optional[Union[dict, "Value", "DataObject"]] = None):
1388
1532
  if not isinstance(options, (DataObject, type(None))):
1389
1533
  raise TypeError("options must be a DataObject or None.")
1390
- _check_initialized()
1534
+ _check_basic_data_initialized()
1535
+ _check_study_data_initialized()
1391
1536
  study_path = str(study_path)
1392
1537
  context = _get_context(model_or_context, None)
1393
1538
  error = Error()
@@ -1404,7 +1549,7 @@ class Study(_BaseObject):
1404
1549
  raise FactoryException(error.what)
1405
1550
  return study
1406
1551
 
1407
- def save(self, output_path: Union[str, pathlib.Path],
1552
+ def save(self, output_path: PathLike,
1408
1553
  options: Optional[Union[dict, Value, DataObject]] = None):
1409
1554
  output_path = str(output_path)
1410
1555
  error = Error()
@@ -1416,7 +1561,7 @@ class Study(_BaseObject):
1416
1561
  if error.code != 0:
1417
1562
  raise FactoryException(error.what)
1418
1563
 
1419
- def save_settings(self, output_path: Union[str, pathlib.Path],
1564
+ def save_settings(self, output_path: PathLike,
1420
1565
  options: Optional[Union[dict, Value, DataObject]] = None):
1421
1566
  output_path = str(output_path)
1422
1567
  error = Error()
@@ -1430,7 +1575,8 @@ class Study(_BaseObject):
1430
1575
 
1431
1576
  @property
1432
1577
  def context(self) -> "Context":
1433
- _check_initialized()
1578
+ _check_basic_data_initialized()
1579
+ _check_study_data_initialized()
1434
1580
  obj = Context()
1435
1581
  error = Error()
1436
1582
  ref = factorylib.lib.psrd_study_context(self._hdr,
@@ -1440,7 +1586,7 @@ class Study(_BaseObject):
1440
1586
  obj._hdr = ref
1441
1587
  return obj
1442
1588
 
1443
- def get(self, expression: str) -> Union[int, float, dt.datetime, str, "DataObject", list, None]:
1589
+ def get(self, expression: str) -> ValueLike:
1444
1590
  value = Value()
1445
1591
  error = Error()
1446
1592
  factorylib.lib.psrd_study_get_value(self._hdr,
@@ -1451,8 +1597,8 @@ class Study(_BaseObject):
1451
1597
  raise FactoryException(error.what)
1452
1598
  return value.get()
1453
1599
 
1454
- def get_at(self, expression: str, range_expr: Union[str, dt.datetime]) -> Union[int, float, dt.datetime, str, "DataObject", list, None]:
1455
- if not isinstance(range_expr, str):
1600
+ def get_at(self, expression: str, range_expr: DateLike) -> ValueLike:
1601
+ if not isinstance(range_expr, (str, dt.datetime)):
1456
1602
  raise FactoryException("range_expr must be a string or datetime object.")
1457
1603
  value = Value()
1458
1604
  error = Error()
@@ -1467,7 +1613,7 @@ class Study(_BaseObject):
1467
1613
  raise FactoryException(error.what)
1468
1614
  return value.get()
1469
1615
 
1470
- def as_dict(self) -> Dict[str, any]:
1616
+ def as_dict(self) -> Dict[str, ValueLike]:
1471
1617
  value_dict = ValueDict()
1472
1618
  error = Error()
1473
1619
  handler = factorylib.lib.psrd_study_get_as_dict(self._hdr,
@@ -1515,6 +1661,33 @@ class Study(_BaseObject):
1515
1661
  object_list._hdr = ref
1516
1662
  return object_list.to_list()
1517
1663
 
1664
+ def get_by_key(self) -> Optional[DataObject]:
1665
+ object_value = Value()
1666
+ error = Error()
1667
+ factorylib.lib.psrd_study_get_object_by_key(self._hdr,
1668
+ object_value.handler(),
1669
+ error.handler())
1670
+ if error.code != 0:
1671
+ raise FactoryException(error.what)
1672
+ obj = object_value.get()
1673
+ if isinstance(obj, DataObject):
1674
+ return obj
1675
+ return None
1676
+
1677
+ def get_key_object_map(self) -> Dict[str, DataObject]:
1678
+ object_dict = ValueDict()
1679
+ error = Error()
1680
+ ref = factorylib.lib.psrd_study_get_key_object_map(self._hdr,
1681
+ error.handler())
1682
+ if error.code != 0 or ref is None:
1683
+ raise FactoryException(error.what)
1684
+ object_dict._hdr = ref
1685
+ result = {}
1686
+ for key, value in object_dict.to_dict().items():
1687
+ if isinstance(value, DataObject):
1688
+ result[key] = value
1689
+ return result
1690
+
1518
1691
  def find(self, expression: str) -> List[DataObject]:
1519
1692
  object_list = ValueList(False)
1520
1693
  error = Error()
@@ -1575,7 +1748,7 @@ class Study(_BaseObject):
1575
1748
  object_list._hdr = ref
1576
1749
  return object_list.to_list()
1577
1750
 
1578
- def set(self, expression: str, value: any):
1751
+ def set(self, expression: str, value: ValueLike):
1579
1752
  error = Error()
1580
1753
  value_object = Value()
1581
1754
  value_object.set(value)
@@ -1587,7 +1760,7 @@ class Study(_BaseObject):
1587
1760
  if error.code != 0:
1588
1761
  raise FactoryException(error.what)
1589
1762
 
1590
- def set_at(self, expression: str, range_expr: Union[str, dt.datetime], value: any):
1763
+ def set_at(self, expression: str, range_expr: DateLike, value: ValueLike):
1591
1764
  if not isinstance(range_expr, (str, dt.datetime)):
1592
1765
  raise FactoryException("range_expr must be a string or datetime object.")
1593
1766
  error = Error()
@@ -1604,7 +1777,7 @@ class Study(_BaseObject):
1604
1777
  if error.code != 0:
1605
1778
  raise FactoryException(error.what)
1606
1779
 
1607
- def get_df(self, expression: str) -> Union["pandas.DataFrame", "polars.DataFrame", "DataFrame"]:
1780
+ def get_df(self, expression: str) -> DataFrameLike:
1608
1781
  error = Error()
1609
1782
  df = DataFrame()
1610
1783
  factorylib.lib.psrd_study_get_table(self._hdr, df.handler(),
@@ -1617,7 +1790,7 @@ class Study(_BaseObject):
1617
1790
  df_builder.build_dataframe(df)
1618
1791
  return df_builder.build_desired_dataframe_type()
1619
1792
 
1620
- def get_objects_values(self, object_type: str, columns: List[str]) -> Union["pandas.DataFrame", "polars.DataFrame", "DataFrame"]:
1793
+ def get_objects_values(self, object_type: str, columns: List[str]) -> DataFrameLike:
1621
1794
  error = Error()
1622
1795
  df = DataFrame()
1623
1796
  columns_list = Value()
@@ -1632,7 +1805,7 @@ class Study(_BaseObject):
1632
1805
  df_builder.build_dataframe(df)
1633
1806
  return df_builder.build_desired_dataframe_type()
1634
1807
 
1635
- def get_objects_values_at(self, object_type: str, columns: List[str], range_value: Union[str, dt.datetime]) -> Union["pandas.DataFrame", "polars.DataFrame", "DataFrame"]:
1808
+ def get_objects_values_at(self, object_type: str, columns: List[str], range_value: DateLike) -> DataFrameLike:
1636
1809
  error = Error()
1637
1810
  df = DataFrame()
1638
1811
  range_object = Value()
@@ -1650,7 +1823,7 @@ class Study(_BaseObject):
1650
1823
  df_builder.build_dataframe(df)
1651
1824
  return df_builder.build_desired_dataframe_type()
1652
1825
 
1653
- def properties(self) -> Dict[str, PropertyDescription]:
1826
+ def descriptions(self) -> Dict[str, PropertyDescription]:
1654
1827
  error = Error()
1655
1828
  value = ctypes.c_long()
1656
1829
  factorylib.lib.psrd_study_property_description_count(self._hdr,
@@ -1671,6 +1844,29 @@ class Study(_BaseObject):
1671
1844
  properties[var.name] = var
1672
1845
  return properties
1673
1846
 
1847
+ def description(self, name: str) -> Optional[PropertyDescription]:
1848
+ _err = Error()
1849
+ var = PropertyDescription()
1850
+ ref = factorylib.lib.psrd_study_get_property_description_by_name(self._hdr,
1851
+ _c_str(name),
1852
+ _bytes(name),
1853
+ _err.handler())
1854
+ if _err.code != 0:
1855
+ raise FactoryException(_err.what)
1856
+ if ref is not None:
1857
+ var._hdr = ref
1858
+ return var
1859
+ return None
1860
+
1861
+ def has_property(self, expression: str) -> bool:
1862
+ _err = Error()
1863
+ bool_value = ctypes.c_bool()
1864
+ factorylib.lib.psrd_study_has_property(self._hdr, _c_str(expression), _bytes(expression),
1865
+ ctypes.byref(bool_value), _err.handler())
1866
+ if _err.code != 0:
1867
+ raise FactoryException(_err.what)
1868
+ return bool(bool_value.value)
1869
+
1674
1870
  def set_df(self, dataframe_like):
1675
1871
  if not _has_pandas():
1676
1872
  raise ModuleNotFoundError("pandas required.")
@@ -1733,6 +1929,8 @@ def _polars_dtype_to_column_type(dtype: "polars.datatypes.classes.DataTypeClass"
1733
1929
  return 4
1734
1930
  if dtype == polars.String:
1735
1931
  return 5
1932
+ if dtype == polars.Boolean:
1933
+ return 1 # TODO: create a boolean column type
1736
1934
  else:
1737
1935
  raise FactoryException(f"Unsupported polars dtype \"{dtype}\".")
1738
1936
 
@@ -1865,6 +2063,8 @@ class _DataFrameBuilder:
1865
2063
  if self._error.code != 0:
1866
2064
  raise FactoryException(self._error.what)
1867
2065
  # convert array values to python datetime
2066
+ if _date_transform is None:
2067
+ raise FactoryException("Factory is not initialized correctly.")
1868
2068
  self.indices[index].values = [dt.datetime.fromtimestamp(value - _date_transform, dt.UTC) for value in array_values]
1869
2069
  else:
1870
2070
  array_values = (ctypes.c_int * rows_count)()
@@ -1895,7 +2095,7 @@ class _DataFrameBuilder:
1895
2095
  self.columns[column].values = array_values
1896
2096
  self._not_built = False
1897
2097
 
1898
- def build_desired_dataframe_type(self, **kwargs) -> Union["pandas.DataFrame", "polars.DataFrame", "DataFrame"]:
2098
+ def build_desired_dataframe_type(self, **kwargs) -> DataFrameLike:
1899
2099
  if _default_dataframe_type == "pandas":
1900
2100
  return self.build_pandas_dataframe(**kwargs)
1901
2101
  elif _default_dataframe_type == "polars":
@@ -1949,6 +2149,8 @@ class _DataFrameBuilder:
1949
2149
  table_data_indices = []
1950
2150
 
1951
2151
  self.column_names = table_data.columns
2152
+ if len(self.column_names) != len(set(self.column_names)):
2153
+ raise FactoryException("DataFrame contains repeated column names.")
1952
2154
  self.index_names = [index.name for index in table_data_indices]
1953
2155
  self.column_types = [_pandas_dtype_to_column_type(dtype) for dtype in table_data.dtypes]
1954
2156
  self.index_types = [_pandas_dtype_to_column_type(index.dtype) for index in table_data_indices]
@@ -1989,9 +2191,9 @@ class _DataFrameBuilder:
1989
2191
  break
1990
2192
 
1991
2193
  if replaced_name:
1992
- for i, name in enumerate(self.index_names):
2194
+ for i_index, name in enumerate(self.index_names):
1993
2195
  if name == "date":
1994
- self.index_names[i] = None
2196
+ self.index_names[i_index] = None
1995
2197
  # check index value types
1996
2198
  index_convert_to = {}
1997
2199
  index_fast_set = {}
@@ -2024,6 +2226,8 @@ class _DataFrameBuilder:
2024
2226
  # TODO: check if original dataframe values is unaltered
2025
2227
  index_values = index_values.astype('datetime64[s]').astype(dt.datetime)
2026
2228
  # for each value, convert to timestamp
2229
+ if _date_transform is None:
2230
+ raise FactoryException("Factory is not initialized correctly.")
2027
2231
  for ix, x in enumerate(index_values):
2028
2232
  index_values[ix] = int(x.replace(tzinfo=dt.timezone.utc).timestamp() + _date_transform)
2029
2233
  # convert to int64
@@ -2133,7 +2337,7 @@ class _DataFrameBuilder:
2133
2337
  return df
2134
2338
 
2135
2339
  def build_polars_dataframe(self, **kwargs) -> "polars.DataFrame":
2136
- use_object_dtype = kwargs.get("use_object_dtype", True)
2340
+ use_object_dtype = kwargs.get("use_object_dtype", False)
2137
2341
  if not _has_polars():
2138
2342
  raise ModuleNotFoundError("polars required.")
2139
2343
  def convert_column_values(column_name:str, values):
@@ -2147,15 +2351,17 @@ class _DataFrameBuilder:
2147
2351
  data = {column.name: convert_column_values(column.name, column.values)
2148
2352
  for column in self.indices + self.columns}
2149
2353
  if use_object_dtype:
2150
- return polars.DataFrame(data=data, dtype=object)
2354
+ return polars.DataFrame({k: polars.Series(k, v, dtype=polars.Object) for k, v in data.items()})
2151
2355
  else:
2152
2356
  return polars.DataFrame(data=data)
2153
2357
 
2154
2358
  def build_from_polars(self, table_data: "polars.DataFrame") -> "DataFrame":
2155
2359
  # check if the table has indices and if its multi-index or common index
2156
- index_names = ("year", "week", "month", "hour", "scenario", "block", "stage")
2360
+ index_names = ("year", "week", "month", "hour", "scenario", "block", "stage", "date")
2157
2361
  column_index = 0
2158
2362
  data_columns = table_data.columns[:]
2363
+ if len(self.column_names) != len(set(self.column_names)):
2364
+ raise FactoryException("DataFrame contains repeated column names.")
2159
2365
  index_columns = []
2160
2366
  while column_index < len(data_columns):
2161
2367
  if data_columns[column_index] in index_names:
@@ -2256,9 +2462,9 @@ class DataFrame(_BaseObject):
2256
2462
  factorylib.lib.psrd_free_table(self._hdr)
2257
2463
 
2258
2464
  @staticmethod
2259
- def load_from_file(input_file: Union[str, pathlib.Path], options: Optional[Union[dict, Value, DataObject]] = None) -> "DataFrame":
2465
+ def load_from_file(input_file: PathLike, options: Optional[Union[dict, Value, DataObject]] = None) -> "DataFrame":
2260
2466
  input_file = str(input_file)
2261
- _check_initialized()
2467
+ _check_basic_data_initialized()
2262
2468
  error = Error()
2263
2469
  df = DataFrame()
2264
2470
  options_value = _get_arg_object(options)
@@ -2272,8 +2478,8 @@ class DataFrame(_BaseObject):
2272
2478
  return df
2273
2479
 
2274
2480
  @staticmethod
2275
- def from_dataframe(df: Union["pandas.DataFrame", "polars.DataFrame", "DataFrame"], **kwargs) -> "DataFrame":
2276
- _check_initialized()
2481
+ def from_dataframe(df: DataFrameLike) -> "DataFrame":
2482
+ _check_basic_data_initialized()
2277
2483
  df_builder = _DataFrameBuilder()
2278
2484
  if isinstance(df, DataFrame):
2279
2485
  # FIXME: implement this
@@ -2287,7 +2493,7 @@ class DataFrame(_BaseObject):
2287
2493
  return df_builder.build_from_polars(dataframe_like)
2288
2494
  raise ImportError("Pandas or polars is not available. Please install pandas to use this feature.")
2289
2495
 
2290
- def save(self, output_file: Union[str, pathlib.Path], options: Optional[Union[dict, Value, DataObject]] = None):
2496
+ def save(self, output_file: PathLike, options: Optional[Union[dict, Value, DataObject]] = None):
2291
2497
  output_file = str(output_file)
2292
2498
  error = Error()
2293
2499
  options_value = _get_arg_object(options)
@@ -2309,7 +2515,7 @@ class DataFrame(_BaseObject):
2309
2515
  return df_builder.build_polars_dataframe(use_object_dtype=False)
2310
2516
 
2311
2517
 
2312
- def get(self, expression: str) -> Union[int, float, dt.datetime, str, "DataObject", list, None]:
2518
+ def get(self, expression: str) -> ValueLike:
2313
2519
  value = Value()
2314
2520
  error = Error()
2315
2521
  factorylib.lib.psrd_table_get_property(self._hdr,
@@ -2320,7 +2526,7 @@ class DataFrame(_BaseObject):
2320
2526
  raise FactoryException(error.what)
2321
2527
  return value.get()
2322
2528
 
2323
- def set(self, expression: str, value: any):
2529
+ def set(self, expression: str, value: ValueLike):
2324
2530
  error = Error()
2325
2531
  value_object = Value()
2326
2532
  value_object.set(value)
@@ -2331,14 +2537,36 @@ class DataFrame(_BaseObject):
2331
2537
  if error.code != 0:
2332
2538
  raise FactoryException(error.what)
2333
2539
 
2540
+ def as_dict(self) -> Dict[str, ValueLike]:
2541
+ value_dict = ValueDict()
2542
+ error = Error()
2543
+ handler = factorylib.lib.psrd_table_get_as_dict(self._hdr,
2544
+ error.handler())
2545
+ if error.code != 0 or handler is None:
2546
+ raise FactoryException(error.what)
2547
+ value_dict._hdr = handler
2548
+ return value_dict.to_dict()
2549
+
2550
+ def from_dict(self, input_dict: Dict[str, any]):
2551
+ value_dict = ValueDict.from_dict(input_dict)
2552
+ error = Error()
2553
+ factorylib.lib.psrd_table_set_from_dict(self._hdr, value_dict.handler(),
2554
+ error.handler())
2555
+ if error.code != 0:
2556
+ raise FactoryException(error.what)
2334
2557
 
2335
- def load_dataframe(input_file: Union[str, pathlib.Path], **kwargs) -> DataFrame:
2558
+
2559
+ def load_dataframe(input_file: PathLike, **kwargs) -> DataFrame:
2336
2560
  options = kwargs.get("options", None)
2337
2561
  return DataFrame.load_from_file(input_file, options)
2338
2562
 
2339
2563
 
2340
- def create_dataframe(data: Union["pandas.DataFrame", "polars.DataFrame", "DataFrame"], **kwargs) -> DataFrame:
2341
- return DataFrame.from_dataframe(data, **kwargs)
2564
+ def create_dataframe(data: Union[DataFrameLike, dict]) -> DataFrame:
2565
+ if isinstance(data, dict):
2566
+ df = DataFrame()
2567
+ df.from_dict(data)
2568
+ return df
2569
+ return DataFrame.from_dataframe(data)
2342
2570
 
2343
2571
 
2344
2572
  def _load_library():
@@ -2350,11 +2578,10 @@ def _load_library():
2350
2578
  return _loaded
2351
2579
 
2352
2580
 
2353
-
2354
- def _initialize():
2355
- global _initialized
2356
- global _initialized_lock
2357
- with _initialized_lock:
2581
+ def _initialize_basic_data():
2582
+ global _basic_data_initialized
2583
+ global _basic_data_initialized_lock
2584
+ with _basic_data_initialized_lock:
2358
2585
  _check_loaded()
2359
2586
  error = Error()
2360
2587
 
@@ -2380,15 +2607,37 @@ def _initialize():
2380
2607
  if error.code != 0:
2381
2608
  raise FactoryException(error.what)
2382
2609
 
2610
+ factorylib.lib.psrd_initialize_basic_data(error.handler())
2611
+ if error.code != 0:
2612
+ raise FactoryException(error.what)
2613
+ _basic_data_initialized = True
2614
+
2615
+ _initialize_constants()
2616
+
2617
+
2618
+ def _initialize_study_data():
2619
+ global _study_data_initialized
2620
+ global _study_data_initialized_lock
2621
+ with _study_data_initialized_lock:
2622
+ _check_loaded()
2623
+ error = Error()
2624
+
2383
2625
  # Where to look for pmd and pmk files
2384
2626
  module_path = os.path.dirname(__file__)
2385
- factorylib.lib.psrd_initialize(_c_str(module_path),
2386
- _bytes(module_path),
2387
- error.handler())
2627
+ factorylib.lib.psrd_initialize_study_data(_c_str(module_path), _bytes(module_path), error.handler())
2388
2628
  if error.code != 0:
2389
2629
  raise FactoryException(error.what)
2390
- _initialized = True
2630
+ _study_data_initialized = True
2631
+
2391
2632
 
2633
+ def _initialize_constants():
2634
+ global _constants_initialized
2635
+ global _constants_initialized_lock
2636
+ with _constants_initialized_lock:
2637
+ global _date_transform
2638
+ _check_basic_data_initialized()
2639
+ _date_transform = int(get_constant("DATE_TRANSFORM"))
2640
+ _constants_initialized = True
2392
2641
 
2393
2642
  def _unload():
2394
2643
  error = Error()
@@ -2421,44 +2670,69 @@ def create_study(*args, **kwargs) -> Study:
2421
2670
  return Study.create_object(model_or_context, blocks)
2422
2671
 
2423
2672
 
2424
- def load_study(study_path: Union[str, pathlib.Path],
2673
+ def load_study(study_path: PathLike,
2425
2674
  model_or_context: Union[str, Context, None] = None,
2426
2675
  options: Optional[DataObject] = None) -> Study:
2427
2676
  settings_only = False
2428
2677
  return Study.load(study_path, model_or_context, settings_only, options)
2429
2678
 
2430
2679
 
2431
- def load_study_settings(study_path: Union[str, pathlib.Path],
2680
+ def load_study_settings(study_path: PathLike,
2432
2681
  model_or_context: Union[str, Context, None] = None,
2433
2682
  options: Optional[DataObject] = None) -> Study:
2434
2683
  settings_only = True
2435
2684
  return Study.load(study_path, model_or_context, settings_only, options)
2436
2685
 
2437
2686
 
2438
- def create(class_name: str,
2439
- model_or_context: Union[str, Context, None] = None) -> Optional[DataObject]:
2440
- _check_initialized()
2441
- data_object = DataObject()
2442
- error = Error()
2443
- context = _get_context(model_or_context, None) \
2444
- if class_name != "Context" else None
2445
- context_handler = context.handler() if context is not None else None
2446
- handler = factorylib.lib.psrd_create(_c_str(class_name),
2447
- context_handler,
2448
- error.handler())
2687
+ def create(type_name: str, model_or_context: Union[str, Context, None] = None) -> DataObject:
2688
+ _check_basic_data_initialized()
2689
+ if type_name not in _TYPES_WITHOUT_CONTEXT:
2690
+ _check_study_data_initialized()
2691
+ error = Error()
2692
+ data_object = DataObject()
2693
+ context = _get_context(model_or_context, None)
2694
+ context_handler = context.handler() if context is not None else None
2695
+ handler = factorylib.lib.psrd_create(_c_str(type_name),
2696
+ context_handler,
2697
+ error.handler())
2698
+ else:
2699
+ error = Error()
2700
+ data_object = DataObject()
2701
+ context_handler = None
2702
+ handler = factorylib.lib.psrd_create(_c_str(type_name),
2703
+ context_handler,
2704
+ error.handler())
2449
2705
  if error.code != 0 or handler is None:
2450
2706
  raise FactoryException(error.what)
2451
2707
  data_object._hdr = handler
2452
2708
  return data_object
2453
2709
 
2454
2710
 
2711
+
2712
+ def convert_output(input_path: PathLike, output_path: PathLike, **kwargs):
2713
+ _check_basic_data_initialized()
2714
+ options: Optional[Union[dict, Value, DataObject]] = kwargs.get("options", None)
2715
+ input_path = str(input_path)
2716
+ output_path = str(output_path)
2717
+ error = Error()
2718
+ options_value = _get_arg_object(options)
2719
+ factorylib.lib.psrd_convert_output(_c_str(input_path),
2720
+ _bytes(input_path),
2721
+ _c_str(output_path),
2722
+ _bytes(output_path),
2723
+ options_value.handler(),
2724
+ error.handler())
2725
+ if error.code != 0:
2726
+ raise FactoryException(error.what)
2727
+
2728
+
2455
2729
  def get_default_context() -> "Context":
2456
- _check_initialized()
2730
+ _check_basic_data_initialized()
2457
2731
  return Context.default_context()
2458
2732
 
2459
2733
 
2460
2734
  def get_new_context() -> "Context":
2461
- _check_initialized()
2735
+ _check_basic_data_initialized()
2462
2736
  return Context.create()
2463
2737
 
2464
2738