psr-factory 5.0.0b21__py3-none-win_amd64.whl → 5.0.0b67__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():
136
+ """Checks if the module was 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():
109
143
  """Checks if the module was initialized."""
110
- global _initialized
111
- if not _initialized:
112
- _initialize()
113
- return _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,7 +1075,7 @@ 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):
@@ -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()
@@ -1236,6 +1326,34 @@ class DataObject(_BaseObject):
1236
1326
  def type(self):
1237
1327
  raise AttributeError("do not delete type")
1238
1328
 
1329
+ @property
1330
+ def key(self) -> str:
1331
+ err = Error()
1332
+ size = factorylib.lib.psrd_object_get_key(self._hdr, None,
1333
+ 0, err.handler())
1334
+ if err.code != 0:
1335
+ raise FactoryException(err.what)
1336
+ buffer = ctypes.create_string_buffer(size)
1337
+ factorylib.lib.psrd_object_get_key(self._hdr, buffer,
1338
+ size, err.handler())
1339
+ if err.code == 0:
1340
+ return _from_c_str(buffer.value)
1341
+ raise FactoryException(err.what)
1342
+
1343
+ @key.setter
1344
+ def key(self, value: str):
1345
+ err = Error()
1346
+ factorylib.lib.psrd_object_set_key(self._hdr,
1347
+ _c_str(value),
1348
+ _bytes(value),
1349
+ err.handler())
1350
+ if err.code != 0:
1351
+ raise FactoryException(err.what)
1352
+
1353
+ @key.deleter
1354
+ def key(self):
1355
+ raise AttributeError("do not delete key")
1356
+
1239
1357
  @property
1240
1358
  def name(self) -> str:
1241
1359
  err = Error()
@@ -1296,7 +1414,7 @@ class DataObject(_BaseObject):
1296
1414
  class Context(DataObject):
1297
1415
  @staticmethod
1298
1416
  def default_context() -> "Context":
1299
- _check_initialized()
1417
+ _check_basic_data_initialized()
1300
1418
  context = Context()
1301
1419
  err = Error()
1302
1420
  ref = factorylib.lib.psrd_get_default_context(err.handler())
@@ -1346,7 +1464,7 @@ class Study(_BaseObject):
1346
1464
  def __copy__(self):
1347
1465
  raise NotImplementedError()
1348
1466
 
1349
- def __deepcopy__(self, memodict=None):
1467
+ def __deepcopy__(self, memo_dict=None):
1350
1468
  dest = Study()
1351
1469
  _err = Error()
1352
1470
  ref = factorylib.lib.psrd_study_clone(self.handler(),
@@ -1357,7 +1475,8 @@ class Study(_BaseObject):
1357
1475
  return dest
1358
1476
 
1359
1477
  def create(self, object_type: str) -> DataObject:
1360
- _check_initialized()
1478
+ _check_basic_data_initialized()
1479
+ _check_study_data_initialized()
1361
1480
  return create(object_type, self.context)
1362
1481
 
1363
1482
 
@@ -1371,7 +1490,8 @@ class Study(_BaseObject):
1371
1490
  @staticmethod
1372
1491
  def create_object(model_or_context: Union[str, Context, dict, None],
1373
1492
  blocks: Optional[int] = None):
1374
- _check_initialized()
1493
+ _check_basic_data_initialized()
1494
+ _check_study_data_initialized()
1375
1495
  err = Error()
1376
1496
  context = _get_context(model_or_context, blocks)
1377
1497
  study = Study()
@@ -1382,12 +1502,13 @@ class Study(_BaseObject):
1382
1502
  return study
1383
1503
 
1384
1504
  @staticmethod
1385
- def load(study_path: Union[str, pathlib.Path], model_or_context: Union[str, Context, None],
1505
+ def load(study_path: PathLike, model_or_context: Union[str, Context, None],
1386
1506
  settings_only: bool = False,
1387
1507
  options: Optional[Union[dict, "Value", "DataObject"]] = None):
1388
1508
  if not isinstance(options, (DataObject, type(None))):
1389
1509
  raise TypeError("options must be a DataObject or None.")
1390
- _check_initialized()
1510
+ _check_basic_data_initialized()
1511
+ _check_study_data_initialized()
1391
1512
  study_path = str(study_path)
1392
1513
  context = _get_context(model_or_context, None)
1393
1514
  error = Error()
@@ -1404,7 +1525,7 @@ class Study(_BaseObject):
1404
1525
  raise FactoryException(error.what)
1405
1526
  return study
1406
1527
 
1407
- def save(self, output_path: Union[str, pathlib.Path],
1528
+ def save(self, output_path: PathLike,
1408
1529
  options: Optional[Union[dict, Value, DataObject]] = None):
1409
1530
  output_path = str(output_path)
1410
1531
  error = Error()
@@ -1416,7 +1537,7 @@ class Study(_BaseObject):
1416
1537
  if error.code != 0:
1417
1538
  raise FactoryException(error.what)
1418
1539
 
1419
- def save_settings(self, output_path: Union[str, pathlib.Path],
1540
+ def save_settings(self, output_path: PathLike,
1420
1541
  options: Optional[Union[dict, Value, DataObject]] = None):
1421
1542
  output_path = str(output_path)
1422
1543
  error = Error()
@@ -1430,7 +1551,8 @@ class Study(_BaseObject):
1430
1551
 
1431
1552
  @property
1432
1553
  def context(self) -> "Context":
1433
- _check_initialized()
1554
+ _check_basic_data_initialized()
1555
+ _check_study_data_initialized()
1434
1556
  obj = Context()
1435
1557
  error = Error()
1436
1558
  ref = factorylib.lib.psrd_study_context(self._hdr,
@@ -1440,7 +1562,7 @@ class Study(_BaseObject):
1440
1562
  obj._hdr = ref
1441
1563
  return obj
1442
1564
 
1443
- def get(self, expression: str) -> Union[int, float, dt.datetime, str, "DataObject", list, None]:
1565
+ def get(self, expression: str) -> ValueLike:
1444
1566
  value = Value()
1445
1567
  error = Error()
1446
1568
  factorylib.lib.psrd_study_get_value(self._hdr,
@@ -1451,8 +1573,8 @@ class Study(_BaseObject):
1451
1573
  raise FactoryException(error.what)
1452
1574
  return value.get()
1453
1575
 
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):
1576
+ def get_at(self, expression: str, range_expr: DateLike) -> ValueLike:
1577
+ if not isinstance(range_expr, (str, dt.datetime)):
1456
1578
  raise FactoryException("range_expr must be a string or datetime object.")
1457
1579
  value = Value()
1458
1580
  error = Error()
@@ -1467,7 +1589,7 @@ class Study(_BaseObject):
1467
1589
  raise FactoryException(error.what)
1468
1590
  return value.get()
1469
1591
 
1470
- def as_dict(self) -> Dict[str, any]:
1592
+ def as_dict(self) -> Dict[str, ValueLike]:
1471
1593
  value_dict = ValueDict()
1472
1594
  error = Error()
1473
1595
  handler = factorylib.lib.psrd_study_get_as_dict(self._hdr,
@@ -1515,6 +1637,33 @@ class Study(_BaseObject):
1515
1637
  object_list._hdr = ref
1516
1638
  return object_list.to_list()
1517
1639
 
1640
+ def get_by_key(self) -> Optional[DataObject]:
1641
+ object_value = Value()
1642
+ error = Error()
1643
+ factorylib.lib.psrd_study_get_object_by_key(self._hdr,
1644
+ object_value.handler(),
1645
+ error.handler())
1646
+ if error.code != 0:
1647
+ raise FactoryException(error.what)
1648
+ obj = object_value.get()
1649
+ if isinstance(obj, DataObject):
1650
+ return obj
1651
+ return None
1652
+
1653
+ def get_key_object_map(self) -> Dict[str, DataObject]:
1654
+ object_dict = ValueDict()
1655
+ error = Error()
1656
+ ref = factorylib.lib.psrd_study_get_key_object_map(self._hdr,
1657
+ error.handler())
1658
+ if error.code != 0 or ref is None:
1659
+ raise FactoryException(error.what)
1660
+ object_dict._hdr = ref
1661
+ result = {}
1662
+ for key, value in object_dict.to_dict().items():
1663
+ if isinstance(value, DataObject):
1664
+ result[key] = value
1665
+ return result
1666
+
1518
1667
  def find(self, expression: str) -> List[DataObject]:
1519
1668
  object_list = ValueList(False)
1520
1669
  error = Error()
@@ -1575,7 +1724,7 @@ class Study(_BaseObject):
1575
1724
  object_list._hdr = ref
1576
1725
  return object_list.to_list()
1577
1726
 
1578
- def set(self, expression: str, value: any):
1727
+ def set(self, expression: str, value: ValueLike):
1579
1728
  error = Error()
1580
1729
  value_object = Value()
1581
1730
  value_object.set(value)
@@ -1587,7 +1736,7 @@ class Study(_BaseObject):
1587
1736
  if error.code != 0:
1588
1737
  raise FactoryException(error.what)
1589
1738
 
1590
- def set_at(self, expression: str, range_expr: Union[str, dt.datetime], value: any):
1739
+ def set_at(self, expression: str, range_expr: DateLike, value: ValueLike):
1591
1740
  if not isinstance(range_expr, (str, dt.datetime)):
1592
1741
  raise FactoryException("range_expr must be a string or datetime object.")
1593
1742
  error = Error()
@@ -1604,7 +1753,7 @@ class Study(_BaseObject):
1604
1753
  if error.code != 0:
1605
1754
  raise FactoryException(error.what)
1606
1755
 
1607
- def get_df(self, expression: str) -> Union["pandas.DataFrame", "polars.DataFrame", "DataFrame"]:
1756
+ def get_df(self, expression: str) -> DataFrameLike:
1608
1757
  error = Error()
1609
1758
  df = DataFrame()
1610
1759
  factorylib.lib.psrd_study_get_table(self._hdr, df.handler(),
@@ -1617,7 +1766,7 @@ class Study(_BaseObject):
1617
1766
  df_builder.build_dataframe(df)
1618
1767
  return df_builder.build_desired_dataframe_type()
1619
1768
 
1620
- def get_objects_values(self, object_type: str, columns: List[str]) -> Union["pandas.DataFrame", "polars.DataFrame", "DataFrame"]:
1769
+ def get_objects_values(self, object_type: str, columns: List[str]) -> DataFrameLike:
1621
1770
  error = Error()
1622
1771
  df = DataFrame()
1623
1772
  columns_list = Value()
@@ -1632,7 +1781,7 @@ class Study(_BaseObject):
1632
1781
  df_builder.build_dataframe(df)
1633
1782
  return df_builder.build_desired_dataframe_type()
1634
1783
 
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"]:
1784
+ def get_objects_values_at(self, object_type: str, columns: List[str], range_value: DateLike) -> DataFrameLike:
1636
1785
  error = Error()
1637
1786
  df = DataFrame()
1638
1787
  range_object = Value()
@@ -1650,7 +1799,7 @@ class Study(_BaseObject):
1650
1799
  df_builder.build_dataframe(df)
1651
1800
  return df_builder.build_desired_dataframe_type()
1652
1801
 
1653
- def properties(self) -> Dict[str, PropertyDescription]:
1802
+ def descriptions(self) -> Dict[str, PropertyDescription]:
1654
1803
  error = Error()
1655
1804
  value = ctypes.c_long()
1656
1805
  factorylib.lib.psrd_study_property_description_count(self._hdr,
@@ -1671,6 +1820,29 @@ class Study(_BaseObject):
1671
1820
  properties[var.name] = var
1672
1821
  return properties
1673
1822
 
1823
+ def description(self, name: str) -> Optional[PropertyDescription]:
1824
+ _err = Error()
1825
+ var = PropertyDescription()
1826
+ ref = factorylib.lib.psrd_study_get_property_description_by_name(self._hdr,
1827
+ _c_str(name),
1828
+ _bytes(name),
1829
+ _err.handler())
1830
+ if _err.code != 0:
1831
+ raise FactoryException(_err.what)
1832
+ if ref is not None:
1833
+ var._hdr = ref
1834
+ return var
1835
+ return None
1836
+
1837
+ def has_property(self, expression: str) -> bool:
1838
+ _err = Error()
1839
+ bool_value = ctypes.c_bool()
1840
+ factorylib.lib.psrd_study_has_property(self._hdr, _c_str(expression), _bytes(expression),
1841
+ ctypes.byref(bool_value), _err.handler())
1842
+ if _err.code != 0:
1843
+ raise FactoryException(_err.what)
1844
+ return bool(bool_value.value)
1845
+
1674
1846
  def set_df(self, dataframe_like):
1675
1847
  if not _has_pandas():
1676
1848
  raise ModuleNotFoundError("pandas required.")
@@ -1865,6 +2037,8 @@ class _DataFrameBuilder:
1865
2037
  if self._error.code != 0:
1866
2038
  raise FactoryException(self._error.what)
1867
2039
  # convert array values to python datetime
2040
+ if _date_transform is None:
2041
+ raise FactoryException("Factory is not initialized correctly.")
1868
2042
  self.indices[index].values = [dt.datetime.fromtimestamp(value - _date_transform, dt.UTC) for value in array_values]
1869
2043
  else:
1870
2044
  array_values = (ctypes.c_int * rows_count)()
@@ -1895,7 +2069,7 @@ class _DataFrameBuilder:
1895
2069
  self.columns[column].values = array_values
1896
2070
  self._not_built = False
1897
2071
 
1898
- def build_desired_dataframe_type(self, **kwargs) -> Union["pandas.DataFrame", "polars.DataFrame", "DataFrame"]:
2072
+ def build_desired_dataframe_type(self, **kwargs) -> DataFrameLike:
1899
2073
  if _default_dataframe_type == "pandas":
1900
2074
  return self.build_pandas_dataframe(**kwargs)
1901
2075
  elif _default_dataframe_type == "polars":
@@ -1949,6 +2123,8 @@ class _DataFrameBuilder:
1949
2123
  table_data_indices = []
1950
2124
 
1951
2125
  self.column_names = table_data.columns
2126
+ if len(self.column_names) != len(set(self.column_names)):
2127
+ raise FactoryException("DataFrame contains repeated column names.")
1952
2128
  self.index_names = [index.name for index in table_data_indices]
1953
2129
  self.column_types = [_pandas_dtype_to_column_type(dtype) for dtype in table_data.dtypes]
1954
2130
  self.index_types = [_pandas_dtype_to_column_type(index.dtype) for index in table_data_indices]
@@ -1989,9 +2165,9 @@ class _DataFrameBuilder:
1989
2165
  break
1990
2166
 
1991
2167
  if replaced_name:
1992
- for i, name in enumerate(self.index_names):
2168
+ for i_index, name in enumerate(self.index_names):
1993
2169
  if name == "date":
1994
- self.index_names[i] = None
2170
+ self.index_names[i_index] = None
1995
2171
  # check index value types
1996
2172
  index_convert_to = {}
1997
2173
  index_fast_set = {}
@@ -2024,6 +2200,8 @@ class _DataFrameBuilder:
2024
2200
  # TODO: check if original dataframe values is unaltered
2025
2201
  index_values = index_values.astype('datetime64[s]').astype(dt.datetime)
2026
2202
  # for each value, convert to timestamp
2203
+ if _date_transform is None:
2204
+ raise FactoryException("Factory is not initialized correctly.")
2027
2205
  for ix, x in enumerate(index_values):
2028
2206
  index_values[ix] = int(x.replace(tzinfo=dt.timezone.utc).timestamp() + _date_transform)
2029
2207
  # convert to int64
@@ -2156,6 +2334,8 @@ class _DataFrameBuilder:
2156
2334
  index_names = ("year", "week", "month", "hour", "scenario", "block", "stage")
2157
2335
  column_index = 0
2158
2336
  data_columns = table_data.columns[:]
2337
+ if len(self.column_names) != len(set(self.column_names)):
2338
+ raise FactoryException("DataFrame contains repeated column names.")
2159
2339
  index_columns = []
2160
2340
  while column_index < len(data_columns):
2161
2341
  if data_columns[column_index] in index_names:
@@ -2256,9 +2436,9 @@ class DataFrame(_BaseObject):
2256
2436
  factorylib.lib.psrd_free_table(self._hdr)
2257
2437
 
2258
2438
  @staticmethod
2259
- def load_from_file(input_file: Union[str, pathlib.Path], options: Optional[Union[dict, Value, DataObject]] = None) -> "DataFrame":
2439
+ def load_from_file(input_file: PathLike, options: Optional[Union[dict, Value, DataObject]] = None) -> "DataFrame":
2260
2440
  input_file = str(input_file)
2261
- _check_initialized()
2441
+ _check_basic_data_initialized()
2262
2442
  error = Error()
2263
2443
  df = DataFrame()
2264
2444
  options_value = _get_arg_object(options)
@@ -2272,8 +2452,8 @@ class DataFrame(_BaseObject):
2272
2452
  return df
2273
2453
 
2274
2454
  @staticmethod
2275
- def from_dataframe(df: Union["pandas.DataFrame", "polars.DataFrame", "DataFrame"], **kwargs) -> "DataFrame":
2276
- _check_initialized()
2455
+ def from_dataframe(df: DataFrameLike) -> "DataFrame":
2456
+ _check_basic_data_initialized()
2277
2457
  df_builder = _DataFrameBuilder()
2278
2458
  if isinstance(df, DataFrame):
2279
2459
  # FIXME: implement this
@@ -2287,7 +2467,7 @@ class DataFrame(_BaseObject):
2287
2467
  return df_builder.build_from_polars(dataframe_like)
2288
2468
  raise ImportError("Pandas or polars is not available. Please install pandas to use this feature.")
2289
2469
 
2290
- def save(self, output_file: Union[str, pathlib.Path], options: Optional[Union[dict, Value, DataObject]] = None):
2470
+ def save(self, output_file: PathLike, options: Optional[Union[dict, Value, DataObject]] = None):
2291
2471
  output_file = str(output_file)
2292
2472
  error = Error()
2293
2473
  options_value = _get_arg_object(options)
@@ -2309,7 +2489,7 @@ class DataFrame(_BaseObject):
2309
2489
  return df_builder.build_polars_dataframe(use_object_dtype=False)
2310
2490
 
2311
2491
 
2312
- def get(self, expression: str) -> Union[int, float, dt.datetime, str, "DataObject", list, None]:
2492
+ def get(self, expression: str) -> ValueLike:
2313
2493
  value = Value()
2314
2494
  error = Error()
2315
2495
  factorylib.lib.psrd_table_get_property(self._hdr,
@@ -2320,7 +2500,7 @@ class DataFrame(_BaseObject):
2320
2500
  raise FactoryException(error.what)
2321
2501
  return value.get()
2322
2502
 
2323
- def set(self, expression: str, value: any):
2503
+ def set(self, expression: str, value: ValueLike):
2324
2504
  error = Error()
2325
2505
  value_object = Value()
2326
2506
  value_object.set(value)
@@ -2331,14 +2511,36 @@ class DataFrame(_BaseObject):
2331
2511
  if error.code != 0:
2332
2512
  raise FactoryException(error.what)
2333
2513
 
2514
+ def as_dict(self) -> Dict[str, ValueLike]:
2515
+ value_dict = ValueDict()
2516
+ error = Error()
2517
+ handler = factorylib.lib.psrd_table_get_as_dict(self._hdr,
2518
+ error.handler())
2519
+ if error.code != 0 or handler is None:
2520
+ raise FactoryException(error.what)
2521
+ value_dict._hdr = handler
2522
+ return value_dict.to_dict()
2523
+
2524
+ def from_dict(self, input_dict: Dict[str, any]):
2525
+ value_dict = ValueDict.from_dict(input_dict)
2526
+ error = Error()
2527
+ factorylib.lib.psrd_table_set_from_dict(self._hdr, value_dict.handler(),
2528
+ error.handler())
2529
+ if error.code != 0:
2530
+ raise FactoryException(error.what)
2531
+
2334
2532
 
2335
- def load_dataframe(input_file: Union[str, pathlib.Path], **kwargs) -> DataFrame:
2533
+ def load_dataframe(input_file: PathLike, **kwargs) -> DataFrame:
2336
2534
  options = kwargs.get("options", None)
2337
2535
  return DataFrame.load_from_file(input_file, options)
2338
2536
 
2339
2537
 
2340
- def create_dataframe(data: Union["pandas.DataFrame", "polars.DataFrame", "DataFrame"], **kwargs) -> DataFrame:
2341
- return DataFrame.from_dataframe(data, **kwargs)
2538
+ def create_dataframe(data: Union[DataFrameLike, dict]) -> DataFrame:
2539
+ if isinstance(data, dict):
2540
+ df = DataFrame()
2541
+ df.from_dict(data)
2542
+ return df
2543
+ return DataFrame.from_dataframe(data)
2342
2544
 
2343
2545
 
2344
2546
  def _load_library():
@@ -2350,11 +2552,10 @@ def _load_library():
2350
2552
  return _loaded
2351
2553
 
2352
2554
 
2353
-
2354
- def _initialize():
2355
- global _initialized
2356
- global _initialized_lock
2357
- with _initialized_lock:
2555
+ def _initialize_basic_data():
2556
+ global _basic_data_initialized
2557
+ global _basic_data_initialized_lock
2558
+ with _basic_data_initialized_lock:
2358
2559
  _check_loaded()
2359
2560
  error = Error()
2360
2561
 
@@ -2380,16 +2581,38 @@ def _initialize():
2380
2581
  if error.code != 0:
2381
2582
  raise FactoryException(error.what)
2382
2583
 
2584
+ factorylib.lib.psrd_initialize_basic_data(error.handler())
2585
+ if error.code != 0:
2586
+ raise FactoryException(error.what)
2587
+ _basic_data_initialized = True
2588
+
2589
+ _initialize_constants()
2590
+
2591
+
2592
+ def _initialize_study_data():
2593
+ global _study_data_initialized
2594
+ global _study_data_initialized_lock
2595
+ with _study_data_initialized_lock:
2596
+ _check_loaded()
2597
+ error = Error()
2598
+
2383
2599
  # Where to look for pmd and pmk files
2384
2600
  module_path = os.path.dirname(__file__)
2385
- factorylib.lib.psrd_initialize(_c_str(module_path),
2386
- _bytes(module_path),
2387
- error.handler())
2601
+ factorylib.lib.psrd_initialize_study_data(_c_str(module_path), _bytes(module_path), error.handler())
2388
2602
  if error.code != 0:
2389
2603
  raise FactoryException(error.what)
2390
- _initialized = True
2604
+ _study_data_initialized = True
2391
2605
 
2392
2606
 
2607
+ def _initialize_constants():
2608
+ global _constants_initialized
2609
+ global _constants_initialized_lock
2610
+ with _constants_initialized_lock:
2611
+ global _date_transform
2612
+ _check_basic_data_initialized()
2613
+ _date_transform = int(get_constant("DATE_TRANSFORM"))
2614
+ _constants_initialized = True
2615
+
2393
2616
  def _unload():
2394
2617
  error = Error()
2395
2618
  factorylib.lib.psrd_unload(error.handler())
@@ -2421,44 +2644,69 @@ def create_study(*args, **kwargs) -> Study:
2421
2644
  return Study.create_object(model_or_context, blocks)
2422
2645
 
2423
2646
 
2424
- def load_study(study_path: Union[str, pathlib.Path],
2647
+ def load_study(study_path: PathLike,
2425
2648
  model_or_context: Union[str, Context, None] = None,
2426
2649
  options: Optional[DataObject] = None) -> Study:
2427
2650
  settings_only = False
2428
2651
  return Study.load(study_path, model_or_context, settings_only, options)
2429
2652
 
2430
2653
 
2431
- def load_study_settings(study_path: Union[str, pathlib.Path],
2654
+ def load_study_settings(study_path: PathLike,
2432
2655
  model_or_context: Union[str, Context, None] = None,
2433
2656
  options: Optional[DataObject] = None) -> Study:
2434
2657
  settings_only = True
2435
2658
  return Study.load(study_path, model_or_context, settings_only, options)
2436
2659
 
2437
2660
 
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())
2661
+ def create(type_name: str, model_or_context: Union[str, Context, None] = None) -> DataObject:
2662
+ _check_basic_data_initialized()
2663
+ if type_name not in _TYPES_WITHOUT_CONTEXT:
2664
+ _check_study_data_initialized()
2665
+ error = Error()
2666
+ data_object = DataObject()
2667
+ context = _get_context(model_or_context, None)
2668
+ context_handler = context.handler() if context is not None else None
2669
+ handler = factorylib.lib.psrd_create(_c_str(type_name),
2670
+ context_handler,
2671
+ error.handler())
2672
+ else:
2673
+ error = Error()
2674
+ data_object = DataObject()
2675
+ context_handler = None
2676
+ handler = factorylib.lib.psrd_create(_c_str(type_name),
2677
+ context_handler,
2678
+ error.handler())
2449
2679
  if error.code != 0 or handler is None:
2450
2680
  raise FactoryException(error.what)
2451
2681
  data_object._hdr = handler
2452
2682
  return data_object
2453
2683
 
2454
2684
 
2685
+
2686
+ def convert_output(input_path: PathLike, output_path: PathLike, **kwargs):
2687
+ _check_basic_data_initialized()
2688
+ options: Optional[Union[dict, Value, DataObject]] = kwargs.get("options", None)
2689
+ input_path = str(input_path)
2690
+ output_path = str(output_path)
2691
+ error = Error()
2692
+ options_value = _get_arg_object(options)
2693
+ factorylib.lib.psrd_convert_output(_c_str(input_path),
2694
+ _bytes(input_path),
2695
+ _c_str(output_path),
2696
+ _bytes(output_path),
2697
+ options_value.handler(),
2698
+ error.handler())
2699
+ if error.code != 0:
2700
+ raise FactoryException(error.what)
2701
+
2702
+
2455
2703
  def get_default_context() -> "Context":
2456
- _check_initialized()
2704
+ _check_basic_data_initialized()
2457
2705
  return Context.default_context()
2458
2706
 
2459
2707
 
2460
2708
  def get_new_context() -> "Context":
2461
- _check_initialized()
2709
+ _check_basic_data_initialized()
2462
2710
  return Context.create()
2463
2711
 
2464
2712