psr-factory 5.0.0b16__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,6 +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
+
56
+
57
+ _default_dataframe_type: str = "pandas"
58
+
59
+ _TYPES_WITHOUT_CONTEXT = ("Context", "ConvertOutputOptions", "DataFrameLoadOptions", "DataFrameSaveOptions", "DataFrameMetadata", "StudyLoadOptions", "StudySaveOptions")
60
+
33
61
 
34
62
  def _has_pandas() -> bool:
35
63
  """Check if pandas is available."""
@@ -68,10 +96,31 @@ def _has_polars() -> bool:
68
96
  return _HAS_POLARS
69
97
 
70
98
 
71
- # States whether the library is initialized.
72
- # It should be to correctly load and build models.
73
- _initialized = False
74
- _initialized_lock = threading.Lock()
99
+ def set_default_dataframe_type(df_type: str):
100
+ """Set the default dataframe type to be used by the library."""
101
+ global _default_dataframe_type
102
+ df_type = df_type.lower()
103
+ if df_type not in ["pandas", "polars", "factory"]:
104
+ raise ValueError("Unsupported dataframe type. Supported types are 'pandas' and 'polars'.")
105
+ if df_type == "pandas" and not _has_pandas():
106
+ raise ValueError("Pandas is not installed. Please install it to use this dataframe type.")
107
+ if df_type == "polars" and not _has_polars():
108
+ raise ValueError("Polars is not installed. Please install it to use this dataframe type.")
109
+ _default_dataframe_type = df_type
110
+
111
+
112
+ def get_default_dataframe_type() -> str:
113
+ """Get the default dataframe type used by the library."""
114
+ return _default_dataframe_type
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()
121
+
122
+ _constants_initialized = False
123
+ _constants_initialized_lock = threading.Lock()
75
124
 
76
125
  _loaded = False
77
126
  _loaded_lock = threading.Lock()
@@ -80,16 +129,22 @@ _loaded_lock = threading.Lock()
80
129
  _preferred_encoding = locale.getpreferredencoding()
81
130
 
82
131
  # Internal date epoch
83
- # TODO: make it a library call.
84
- _date_transform = 12622780800
132
+ _date_transform: Optional[int] = None
133
+
85
134
 
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
86
141
 
87
- def _check_initialized():
142
+ def _check_study_data_initialized():
88
143
  """Checks if the module was initialized."""
89
- global _initialized
90
- if not _initialized:
91
- _initialize()
92
- return _initialized
144
+ global _study_data_initialized
145
+ if not _study_data_initialized:
146
+ _initialize_study_data()
147
+ return _study_data_initialized
93
148
 
94
149
 
95
150
  def _check_loaded() -> bool:
@@ -119,6 +174,9 @@ def _bytes(value: str) -> int:
119
174
  class FactoryException(Exception):
120
175
  pass
121
176
 
177
+ class FactoryLicenseError(Exception):
178
+ pass
179
+
122
180
 
123
181
  class LogLevel(enum.Enum):
124
182
  NOTSET = 0
@@ -168,11 +226,23 @@ def version() -> str:
168
226
  return _version_long()
169
227
 
170
228
 
171
- def build_version() -> str:
229
+ def short_version() -> str:
172
230
  """Returns short library version."""
173
231
  return _version_short()
174
232
 
175
233
 
234
+ def check_license() -> Tuple[bool, str]:
235
+ """Returns True if license is valid and active."""
236
+ _check_loaded()
237
+ error = Error()
238
+ factorylib.lib.psrd_check_license(error.handler())
239
+ valid = error.code == 0
240
+ invalid = error.code == 15
241
+ if not valid and not invalid:
242
+ raise FactoryException("Error checking license: " + error.what)
243
+ return valid, error.what
244
+
245
+
176
246
  def get_log_level() -> LogLevel:
177
247
  """Get log level."""
178
248
  _check_loaded()
@@ -222,9 +292,11 @@ def set_diagnostics_mode(value: Union[bool, int]):
222
292
 
223
293
 
224
294
  def diagnostics() -> str:
225
- global _initialized
226
- global _initialized_lock
227
- 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:
228
300
  """Get diagnostics information."""
229
301
  py_diagnostics = f"Python version: {sys.version}\n" \
230
302
  f"Python encoding: {sys.getdefaultencoding()}\n" \
@@ -243,42 +315,49 @@ def diagnostics() -> str:
243
315
  buffer, size, error.handler())
244
316
  if error.code != 0:
245
317
  raise FactoryException(error.what)
246
- _initialized = True
318
+ _basic_data_initialized = True
319
+ _study_data_initialized = True
247
320
  return py_diagnostics + _from_c_str(buffer.value)
248
321
 
249
322
 
250
- def get_setting(key: str) -> Union[str, int, float, bool]:
251
- global _initialized
252
- global _initialized_lock
253
- with _initialized_lock:
254
- _check_loaded()
255
- error = Error()
256
- value = Value()
257
- factorylib.lib.psrd_get_global_setting(_c_str(key),
258
- _bytes(key),
259
- value.handler(),
260
- error.handler())
261
- if error.code != 0:
262
- raise FactoryException(error.what)
263
- 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()
264
334
 
265
335
 
266
- def set_setting(key: str, value: Union[str, int, float, bool]):
267
- global _initialized
268
- global _initialized_lock
269
- with _initialized_lock:
270
- _check_loaded()
271
- error = Error()
272
- _value = Value()
273
- _value.set(value)
274
- factorylib.lib.psrd_set_global_setting(_c_str(key),
275
- _bytes(key),
276
- _value.handler(),
277
- error.handler())
278
- if error.code != 0:
279
- 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()
280
347
 
281
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)
360
+
282
361
 
283
362
  def _get_context(models_or_context: Union[str, list, dict, "Context", None],
284
363
  blocks: Optional[int] = None) -> "Value":
@@ -292,7 +371,7 @@ def _get_context(models_or_context: Union[str, list, dict, "Context", None],
292
371
  elif isinstance(models_or_context, str):
293
372
  context["Models"] = [models_or_context, ]
294
373
  else:
295
- raise TypeError("Unexpected type for profile_or_context argument.")
374
+ raise TypeError("Unexpected type for model_or_context argument.")
296
375
  if blocks is not None and isinstance(blocks, int):
297
376
  if isinstance(blocks, Context):
298
377
  context.set("Blocks", blocks)
@@ -442,7 +521,7 @@ class ValueDict(_BaseObject):
442
521
  if self._hdr is not None:
443
522
  factorylib.lib.psrd_free_dict(self._hdr)
444
523
 
445
- 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:
446
525
  if not isinstance(key, Value):
447
526
  old_key = key
448
527
  key = Value()
@@ -457,7 +536,7 @@ class ValueDict(_BaseObject):
457
536
  raise FactoryException(error.what)
458
537
  return value.get()
459
538
 
460
- def __contains__(self, key: Union["Value", bool, int, float, dt.datetime, str, "DataObject", list, dict, None]) -> bool:
539
+ def __contains__(self, key: ValueLike) -> bool:
461
540
  if not isinstance(key, Value):
462
541
  old_key = key
463
542
  key = Value()
@@ -490,7 +569,7 @@ class ValueDict(_BaseObject):
490
569
  def __iter__(self):
491
570
  return self
492
571
 
493
- def __next__(self) -> "Value":
572
+ def __next__(self) -> ValueLike:
494
573
  if self.index >= self.dict_obj.__len__():
495
574
  raise StopIteration
496
575
  factorylib.lib.psrd_dict_get_key_by_index(self.dict_obj._hdr, self.index,
@@ -514,7 +593,7 @@ class ValueDict(_BaseObject):
514
593
  def __iter__(self):
515
594
  return self
516
595
 
517
- def __next__(self) -> "Value":
596
+ def __next__(self) -> ValueLike:
518
597
  if self.index >= self.dict_obj.__len__():
519
598
  raise StopIteration
520
599
  factorylib.lib.psrd_dict_get_value_by_index(self.dict_obj._hdr, self.index,
@@ -539,7 +618,7 @@ class ValueDict(_BaseObject):
539
618
  def __iter__(self):
540
619
  return self
541
620
 
542
- def __next__(self) -> Tuple["Value", "Value"]:
621
+ def __next__(self) -> Tuple[ValueLike, ValueLike]:
543
622
  if self.index >= self.dict_obj.__len__():
544
623
  raise StopIteration
545
624
  factorylib.lib.psrd_dict_get_by_index(self.dict_obj._hdr, self.index,
@@ -613,7 +692,7 @@ class Value(_BaseObject):
613
692
  if self._hdr is not None:
614
693
  factorylib.lib.psrd_free_value(self._hdr)
615
694
 
616
- def get(self) -> Union[bool, int, float, dt.datetime, str, "DataObject", list, dict, None]:
695
+ def get(self) -> ValueLike:
617
696
  _err = Error()
618
697
  uint_value = ctypes.c_long()
619
698
  factorylib.lib.psrd_value_get_type(self._hdr,
@@ -631,13 +710,13 @@ class Value(_BaseObject):
631
710
  raise FactoryException(_err.what)
632
711
  return int(int_value.value)
633
712
  elif var_type == ValueType.INT64.value:
634
- long_value = ctypes.c_long()
713
+ long_value = ctypes.c_longlong()
635
714
  factorylib.lib.psrd_value_get_int64(self._hdr,
636
715
  ctypes.byref(long_value),
637
716
  _err.handler())
638
717
  if _err.code != 0:
639
718
  raise FactoryException(_err.what)
640
- return int(int_value.value)
719
+ return int(long_value.value)
641
720
  elif var_type == ValueType.FLOAT32.value:
642
721
  float_value = ctypes.c_float()
643
722
  factorylib.lib.psrd_value_get_float32(self._hdr,
@@ -670,7 +749,8 @@ class Value(_BaseObject):
670
749
  _err.handler())
671
750
  if _err.code != 0:
672
751
  raise FactoryException(_err.what)
673
-
752
+ if _date_transform is None:
753
+ raise FactoryException("Factory is not initialized correctly.")
674
754
  return dt.datetime.fromtimestamp(date_value.value - _date_transform, dt.timezone.utc)
675
755
 
676
756
  elif var_type == ValueType.BOOL.value:
@@ -712,7 +792,7 @@ class Value(_BaseObject):
712
792
  else:
713
793
  raise NotImplementedError()
714
794
 
715
- def set(self, value: Union[bool, int, float, dt.datetime, str, "DataObject", list, dict, None]):
795
+ def set(self, value: ValueLike):
716
796
  _err = Error()
717
797
  if isinstance(value, bool):
718
798
  factorylib.lib.psrd_value_set_bool(self._hdr, value,
@@ -720,6 +800,8 @@ class Value(_BaseObject):
720
800
  if _err.code != 0:
721
801
  raise FactoryException(_err.what)
722
802
  elif isinstance(value, dt.datetime):
803
+ if _date_transform is None:
804
+ raise FactoryException("Factory is not initialized correctly.")
723
805
  value.replace(tzinfo=dt.timezone.utc)
724
806
  date_epoch = int(value.timestamp()) + _date_transform
725
807
  factorylib.lib.psrd_value_set_date(self._hdr, date_epoch,
@@ -784,7 +866,7 @@ class Value(_BaseObject):
784
866
  raise FactoryException(_err.what)
785
867
 
786
868
  else:
787
- raise FactoryException(f"Unsupported type \"{type(value).name}\" for value.")
869
+ raise FactoryException(f"Unsupported type \"{type(value).__name__}\" for value.")
788
870
 
789
871
 
790
872
  class PropertyDescription(_BaseObject):
@@ -799,11 +881,11 @@ class PropertyDescription(_BaseObject):
799
881
  @property
800
882
  def name(self) -> str:
801
883
  _err = Error()
802
- # TODO: determine size automatically
803
- 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)
804
887
  buffer = ctypes.create_string_buffer(size)
805
- factorylib.lib.psrd_property_description_get_name(self._hdr, buffer, size,
806
- _err.handler())
888
+ factorylib.lib.psrd_property_description_get_name(self._hdr, buffer, size, _err.handler())
807
889
  if _err.code != 0:
808
890
  raise FactoryException(_err.what)
809
891
  return _from_c_str(buffer.value)
@@ -819,8 +901,9 @@ class PropertyDescription(_BaseObject):
819
901
  @property
820
902
  def alt_name(self) -> str:
821
903
  _err = Error()
822
- # TODO: determine size automatically
823
- 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)
824
907
  buffer = ctypes.create_string_buffer(size)
825
908
  factorylib.lib.psrd_property_description_get_alternative_name(
826
909
  self._hdr, buffer, size, _err.handler())
@@ -836,6 +919,26 @@ class PropertyDescription(_BaseObject):
836
919
  def alt_name(self):
837
920
  raise AttributeError("do not delete alt_name")
838
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
+
839
942
  def is_dynamic(self) -> bool:
840
943
  _err = Error()
841
944
  value = ctypes.c_bool()
@@ -888,8 +991,9 @@ class PropertyDescription(_BaseObject):
888
991
  dimensions_count = int(value.value)
889
992
 
890
993
  for i_dim in range(dimensions_count):
891
- # TODO: get size automatically
892
- 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)
893
997
  buffer = ctypes.create_string_buffer(size)
894
998
  factorylib.lib.psrd_property_description_get_dimension_name(self._hdr,
895
999
  i_dim, buffer,
@@ -898,7 +1002,6 @@ class PropertyDescription(_BaseObject):
898
1002
  if _err.code != 0:
899
1003
  raise FactoryException(_err.what)
900
1004
  name = _from_c_str(buffer.value)
901
-
902
1005
  factorylib.lib.psrd_property_description_get_dimension_size(self._hdr,
903
1006
  i_dim,
904
1007
  ctypes.byref(value),
@@ -931,7 +1034,6 @@ class PropertyDescription(_BaseObject):
931
1034
  return f"Property {self.name} with dimensions {self.dimensions()}"
932
1035
 
933
1036
 
934
-
935
1037
  class DataObject(_BaseObject):
936
1038
  def __init__(self):
937
1039
  super().__init__()
@@ -973,7 +1075,7 @@ class DataObject(_BaseObject):
973
1075
  dest._hdr = ref
974
1076
  return dest
975
1077
 
976
- def __deepcopy__(self, memodict=None):
1078
+ def __deepcopy__(self, memo_dict=None):
977
1079
  raise NotImplementedError()
978
1080
 
979
1081
  def __repr__(self):
@@ -994,7 +1096,8 @@ class DataObject(_BaseObject):
994
1096
 
995
1097
  @property
996
1098
  def context(self) -> "Context":
997
- _check_initialized()
1099
+ _check_basic_data_initialized()
1100
+ _check_study_data_initialized()
998
1101
  obj = Context()
999
1102
  _err = Error()
1000
1103
  ref = factorylib.lib.psrd_object_context(self._hdr,
@@ -1004,7 +1107,7 @@ class DataObject(_BaseObject):
1004
1107
  obj._hdr = ref
1005
1108
  return obj
1006
1109
 
1007
- def properties(self) -> Dict[str, PropertyDescription]:
1110
+ def descriptions(self) -> Dict[str, PropertyDescription]:
1008
1111
  _err = Error()
1009
1112
  value = ctypes.c_long()
1010
1113
  factorylib.lib.psrd_object_property_description_count(self._hdr,
@@ -1025,7 +1128,30 @@ class DataObject(_BaseObject):
1025
1128
  properties[var.name] = var
1026
1129
  return properties
1027
1130
 
1028
- 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:
1029
1155
  value = Value()
1030
1156
  _err = Error()
1031
1157
  factorylib.lib.psrd_object_get_value(self._hdr,
@@ -1036,7 +1162,7 @@ class DataObject(_BaseObject):
1036
1162
  raise FactoryException(_err.what)
1037
1163
  return value.get()
1038
1164
 
1039
- 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:
1040
1166
  if not isinstance(range_expr, (str, dt.datetime)):
1041
1167
  raise FactoryException("range_expr must be a string or datetime object.")
1042
1168
  _value = Value()
@@ -1052,7 +1178,7 @@ class DataObject(_BaseObject):
1052
1178
  raise FactoryException(_err.what)
1053
1179
  return _value.get()
1054
1180
 
1055
- def as_dict(self) -> Dict[str, any]:
1181
+ def as_dict(self) -> Dict[str, ValueLike]:
1056
1182
  value_dict = ValueDict()
1057
1183
  error = Error()
1058
1184
  handler = factorylib.lib.psrd_object_get_as_dict(self._hdr,
@@ -1070,7 +1196,7 @@ class DataObject(_BaseObject):
1070
1196
  if error.code != 0:
1071
1197
  raise FactoryException(error.what)
1072
1198
 
1073
- def get_df(self, expression: str) -> "pandas.DataFrame":
1199
+ def get_df(self, expression: str) -> DataFrameLike:
1074
1200
  _err = Error()
1075
1201
  _df = DataFrame()
1076
1202
  factorylib.lib.psrd_object_get_table(self._hdr, _df.handler(),
@@ -1081,7 +1207,7 @@ class DataObject(_BaseObject):
1081
1207
  raise FactoryException(_err.what)
1082
1208
  df_builder = _DataFrameBuilder()
1083
1209
  df_builder.build_dataframe(_df)
1084
- return df_builder.build_pandas_dataframe()
1210
+ return df_builder.build_desired_dataframe_type()
1085
1211
 
1086
1212
  def set(self, expression: str, value):
1087
1213
  _err = Error()
@@ -1094,7 +1220,7 @@ class DataObject(_BaseObject):
1094
1220
  if _err.code != 0:
1095
1221
  raise FactoryException(_err.what)
1096
1222
 
1097
- def set_at(self, expression: str, range_expr: Union[str, dt.datetime], value):
1223
+ def set_at(self, expression: str, range_expr: DateLike, value):
1098
1224
  if not isinstance(range_expr, (str, dt.datetime)):
1099
1225
  raise FactoryException("range_expr must be a string or datetime object.")
1100
1226
  _err = Error()
@@ -1200,6 +1326,34 @@ class DataObject(_BaseObject):
1200
1326
  def type(self):
1201
1327
  raise AttributeError("do not delete type")
1202
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
+
1203
1357
  @property
1204
1358
  def name(self) -> str:
1205
1359
  err = Error()
@@ -1260,7 +1414,7 @@ class DataObject(_BaseObject):
1260
1414
  class Context(DataObject):
1261
1415
  @staticmethod
1262
1416
  def default_context() -> "Context":
1263
- _check_initialized()
1417
+ _check_basic_data_initialized()
1264
1418
  context = Context()
1265
1419
  err = Error()
1266
1420
  ref = factorylib.lib.psrd_get_default_context(err.handler())
@@ -1310,7 +1464,7 @@ class Study(_BaseObject):
1310
1464
  def __copy__(self):
1311
1465
  raise NotImplementedError()
1312
1466
 
1313
- def __deepcopy__(self, memodict=None):
1467
+ def __deepcopy__(self, memo_dict=None):
1314
1468
  dest = Study()
1315
1469
  _err = Error()
1316
1470
  ref = factorylib.lib.psrd_study_clone(self.handler(),
@@ -1321,7 +1475,8 @@ class Study(_BaseObject):
1321
1475
  return dest
1322
1476
 
1323
1477
  def create(self, object_type: str) -> DataObject:
1324
- _check_initialized()
1478
+ _check_basic_data_initialized()
1479
+ _check_study_data_initialized()
1325
1480
  return create(object_type, self.context)
1326
1481
 
1327
1482
 
@@ -1333,11 +1488,12 @@ class Study(_BaseObject):
1333
1488
  return copy.deepcopy(self)
1334
1489
 
1335
1490
  @staticmethod
1336
- def create_object(profile_or_context: Union[str, Context, dict, None],
1491
+ def create_object(model_or_context: Union[str, Context, dict, None],
1337
1492
  blocks: Optional[int] = None):
1338
- _check_initialized()
1493
+ _check_basic_data_initialized()
1494
+ _check_study_data_initialized()
1339
1495
  err = Error()
1340
- context = _get_context(profile_or_context, blocks)
1496
+ context = _get_context(model_or_context, blocks)
1341
1497
  study = Study()
1342
1498
  study._hdr = factorylib.lib.psrd_study_create(context.handler(),
1343
1499
  err.handler())
@@ -1346,15 +1502,16 @@ class Study(_BaseObject):
1346
1502
  return study
1347
1503
 
1348
1504
  @staticmethod
1349
- def load(study_path: Union[str, pathlib.Path], profile_or_context: Union[str, Context, None],
1505
+ def load(study_path: PathLike, model_or_context: Union[str, Context, None],
1350
1506
  settings_only: bool = False,
1351
1507
  options: Optional[Union[dict, "Value", "DataObject"]] = None):
1352
1508
  if not isinstance(options, (DataObject, type(None))):
1353
1509
  raise TypeError("options must be a DataObject or None.")
1354
- _check_initialized()
1510
+ _check_basic_data_initialized()
1511
+ _check_study_data_initialized()
1355
1512
  study_path = str(study_path)
1356
- context = _get_context(profile_or_context, None)
1357
- err = Error()
1513
+ context = _get_context(model_or_context, None)
1514
+ error = Error()
1358
1515
  options_value = _get_arg_object(options)
1359
1516
  study = Study()
1360
1517
  if not settings_only:
@@ -1363,75 +1520,76 @@ class Study(_BaseObject):
1363
1520
  load_fn = factorylib.lib.psrd_study_load_settings
1364
1521
  study._hdr = load_fn(_c_str(study_path), _bytes(study_path),
1365
1522
  options_value.handler(), context.handler(),
1366
- err.handler())
1367
- if err.code != 0:
1368
- raise FactoryException(err.what)
1523
+ error.handler())
1524
+ if error.code != 0:
1525
+ raise FactoryException(error.what)
1369
1526
  return study
1370
1527
 
1371
- def save(self, output_path: Union[str, pathlib.Path],
1528
+ def save(self, output_path: PathLike,
1372
1529
  options: Optional[Union[dict, Value, DataObject]] = None):
1373
1530
  output_path = str(output_path)
1374
- _err = Error()
1531
+ error = Error()
1375
1532
  options_value = _get_arg_object(options)
1376
1533
  factorylib.lib.psrd_study_save(self._hdr,
1377
1534
  _c_str(output_path), _bytes(output_path),
1378
1535
  options_value.handler(),
1379
- _err.handler())
1380
- if _err.code != 0:
1381
- raise FactoryException(_err.what)
1536
+ error.handler())
1537
+ if error.code != 0:
1538
+ raise FactoryException(error.what)
1382
1539
 
1383
- def save_settings(self, output_path: Union[str, pathlib.Path],
1540
+ def save_settings(self, output_path: PathLike,
1384
1541
  options: Optional[Union[dict, Value, DataObject]] = None):
1385
1542
  output_path = str(output_path)
1386
- _err = Error()
1543
+ error = Error()
1387
1544
  options_value = _get_arg_object(options)
1388
1545
  factorylib.lib.psrd_study_save_settings(self._hdr, _c_str(output_path),
1389
1546
  _bytes(output_path),
1390
1547
  options_value.handler(),
1391
- _err.handler())
1392
- if _err.code != 0:
1393
- raise FactoryException(_err.what)
1548
+ error.handler())
1549
+ if error.code != 0:
1550
+ raise FactoryException(error.what)
1394
1551
 
1395
1552
  @property
1396
1553
  def context(self) -> "Context":
1397
- _check_initialized()
1554
+ _check_basic_data_initialized()
1555
+ _check_study_data_initialized()
1398
1556
  obj = Context()
1399
- _err = Error()
1557
+ error = Error()
1400
1558
  ref = factorylib.lib.psrd_study_context(self._hdr,
1401
- _err.handler())
1402
- if _err.code != 0 or ref is None:
1403
- raise FactoryException(_err.what)
1559
+ error.handler())
1560
+ if error.code != 0 or ref is None:
1561
+ raise FactoryException(error.what)
1404
1562
  obj._hdr = ref
1405
1563
  return obj
1406
1564
 
1407
- def get(self, expression: str) -> Union[int, float, dt.datetime, str, "DataObject", list, None]:
1565
+ def get(self, expression: str) -> ValueLike:
1408
1566
  value = Value()
1409
- _err = Error()
1567
+ error = Error()
1410
1568
  factorylib.lib.psrd_study_get_value(self._hdr,
1411
1569
  _c_str(expression),
1412
1570
  value.handler(),
1413
- _err.handler())
1414
- if _err.code != 0:
1415
- raise FactoryException(_err.what)
1571
+ error.handler())
1572
+ if error.code != 0:
1573
+ raise FactoryException(error.what)
1416
1574
  return value.get()
1417
1575
 
1418
- def get_at(self, expression: str, range_expr: Union[str, dt.datetime]) -> Union[int, float, dt.datetime, str, "DataObject", list, None]:
1419
- 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)):
1420
1578
  raise FactoryException("range_expr must be a string or datetime object.")
1421
- _value = Value()
1422
- _err = Error()
1423
- _range = Value()
1424
- _range.set(range_expr)
1579
+ value = Value()
1580
+ error = Error()
1581
+ range_value = Value()
1582
+ range_value.set(range_expr)
1425
1583
  factorylib.lib.psrd_study_get_value_at(self._hdr,
1426
1584
  _c_str(expression),
1427
- _range.handler(),
1428
- _value.handler(),
1429
- _err.handler())
1430
- if _err.code != 0:
1431
- raise FactoryException(_err.what)
1432
- return _value.get()
1585
+ range_value.handler(),
1586
+ value.handler(),
1587
+ error.handler())
1588
+ if error.code != 0:
1589
+ raise FactoryException(error.what)
1590
+ return value.get()
1433
1591
 
1434
- def as_dict(self) -> Dict[str, any]:
1592
+ def as_dict(self) -> Dict[str, ValueLike]:
1435
1593
  value_dict = ValueDict()
1436
1594
  error = Error()
1437
1595
  handler = factorylib.lib.psrd_study_get_as_dict(self._hdr,
@@ -1452,22 +1610,22 @@ class Study(_BaseObject):
1452
1610
  def add(self, obj: DataObject):
1453
1611
  if not isinstance(obj, DataObject):
1454
1612
  raise TypeError("obj must be a DataObject.")
1455
- _err = Error()
1613
+ error = Error()
1456
1614
  factorylib.lib.psrd_study_add(self._hdr,
1457
1615
  obj.handler(),
1458
- _err.handler())
1459
- if _err.code != 0:
1460
- raise FactoryException(_err.what)
1616
+ error.handler())
1617
+ if error.code != 0:
1618
+ raise FactoryException(error.what)
1461
1619
 
1462
1620
  def remove(self, obj: DataObject):
1463
1621
  if not isinstance(obj, DataObject):
1464
1622
  raise TypeError("obj must be a DataObject.")
1465
- _err = Error()
1623
+ error = Error()
1466
1624
  factorylib.lib.psrd_study_remove(self._hdr,
1467
1625
  obj.handler(),
1468
- _err.handler())
1469
- if _err.code != 0:
1470
- raise FactoryException(_err.what)
1626
+ error.handler())
1627
+ if error.code != 0:
1628
+ raise FactoryException(error.what)
1471
1629
 
1472
1630
  def get_all_objects(self) -> List[DataObject]:
1473
1631
  object_list = ValueList(False)
@@ -1479,185 +1637,232 @@ class Study(_BaseObject):
1479
1637
  object_list._hdr = ref
1480
1638
  return object_list.to_list()
1481
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
+
1482
1667
  def find(self, expression: str) -> List[DataObject]:
1483
1668
  object_list = ValueList(False)
1484
- _err = Error()
1485
- ref = factorylib.lib.psrd_study_find(self._hdr,
1669
+ error = Error()
1670
+ handler = factorylib.lib.psrd_study_find(self._hdr,
1486
1671
  _c_str(expression),
1487
- _err.handler())
1488
- if _err.code != 0 or ref is None:
1489
- raise FactoryException(_err.what)
1490
- object_list._hdr = ref
1672
+ error.handler())
1673
+ if error.code != 0 or handler is None:
1674
+ raise FactoryException(error.what)
1675
+ object_list._hdr = handler
1491
1676
  return object_list.to_list()
1492
1677
 
1493
- def find_by_name(self, type_name: str, name: Optional[str] = None) -> List[DataObject]:
1494
- if name is None:
1495
- warnings.warn(DeprecationWarning("Starting from Factory 4.0.28 "
1496
- "the second argument 'name' must be provided.\n"
1497
- "Use find_by_name(type_name, name)"))
1498
-
1499
- type_name, name_str = type_name.split(".", 1)
1500
- name = name_str
1678
+ def find_by_name(self, type_name: str, name_or_pattern: str) -> List[DataObject]:
1679
+ if name_or_pattern is None or name_or_pattern == "":
1680
+ raise DeprecationWarning("Starting from Factory 4.0.28 "
1681
+ "the second argument 'name_or_pattern' must be provided.\n"
1682
+ "Use find_by_name(type_name, name_or_pattern)")
1501
1683
  object_list = ValueList(False)
1502
- _err = Error()
1503
- if name.strip() == "":
1504
- name = "*"
1505
- expression = f"{type_name}.{name}"
1506
- ref = factorylib.lib.psrd_study_find(self._hdr,
1507
- _c_str(expression),
1508
- _err.handler())
1509
- if _err.code != 0 or ref is None:
1510
- raise FactoryException(_err.what)
1511
- object_list._hdr = ref
1684
+ error = Error()
1685
+ expression = f"{type_name}.{name_or_pattern}"
1686
+ handler = factorylib.lib.psrd_study_find(self._hdr,
1687
+ _c_str(expression),
1688
+ error.handler())
1689
+ if error.code != 0 or handler is None:
1690
+ raise FactoryException(error.what)
1691
+ object_list._hdr = handler
1512
1692
  return object_list.to_list()
1513
1693
 
1514
- def find_by_code(self, type_name: str, code: Optional[int] = None) -> List[DataObject]:
1694
+ def find_by_code(self, type_name: str, code: int) -> List[DataObject]:
1515
1695
  if code is None:
1516
- warnings.warn(DeprecationWarning("Starting from Factory 4.0.9 "
1517
- "the second argument 'code' must be provided.\n"
1518
- "Use find_by_code(type_name, code)"))
1519
-
1520
- type_name, code_str = type_name.split(".", 1)
1521
- code = int(code_str)
1696
+ raise DeprecationWarning("Starting from Factory 4.0.9 "
1697
+ "the second argument 'code' must be provided.\n"
1698
+ "Use find_by_code(type_name, code)")
1522
1699
 
1523
1700
  object_list = ValueList(False)
1524
- _err = Error()
1525
- ref = factorylib.lib.psrd_study_find_by_code(self._hdr,
1526
- _c_str(type_name),
1527
- code,
1528
- _err.handler())
1529
- if _err.code != 0 or ref is None:
1530
- raise FactoryException(_err.what)
1531
- object_list._hdr = ref
1701
+ error = Error()
1702
+ handler = factorylib.lib.psrd_study_find_by_code(self._hdr,
1703
+ _c_str(type_name),
1704
+ code,
1705
+ error.handler())
1706
+ if error.code != 0 or handler is None:
1707
+ raise FactoryException(error.what)
1708
+ object_list._hdr = handler
1532
1709
  return object_list.to_list()
1533
1710
 
1534
- def find_by_id(self, expression: str) -> List[DataObject]:
1711
+ def find_by_id(self, type_name: str, id_or_pattern: str) -> List[DataObject]:
1712
+ if id_or_pattern is None or id_or_pattern == "":
1713
+ raise DeprecationWarning("Starting from Factory 5.0.0 "
1714
+ "the second argument 'id' must be provided.\n"
1715
+ "Use find_by_id(type_name, id)")
1716
+ expression = f"{type_name}.{id_or_pattern}"
1535
1717
  object_list = ValueList(False)
1536
- _err = Error()
1718
+ error = Error()
1537
1719
  ref = factorylib.lib.psrd_study_find_by_id(self._hdr,
1538
1720
  _c_str(expression),
1539
- _err.handler())
1540
- if _err.code != 0 or ref is None:
1541
- raise FactoryException(_err.what)
1721
+ error.handler())
1722
+ if error.code != 0 or ref is None:
1723
+ raise FactoryException(error.what)
1542
1724
  object_list._hdr = ref
1543
1725
  return object_list.to_list()
1544
1726
 
1545
- def set(self, expression: str, value):
1546
- _err = Error()
1547
- _val = Value()
1548
- _val.set(value)
1727
+ def set(self, expression: str, value: ValueLike):
1728
+ error = Error()
1729
+ value_object = Value()
1730
+ value_object.set(value)
1549
1731
  factorylib.lib.psrd_study_set_value(self._hdr,
1550
1732
  _c_str(expression),
1551
1733
  _bytes(expression),
1552
- _val.handler(),
1553
- _err.handler())
1554
- if _err.code != 0:
1555
- raise FactoryException(_err.what)
1734
+ value_object.handler(),
1735
+ error.handler())
1736
+ if error.code != 0:
1737
+ raise FactoryException(error.what)
1556
1738
 
1557
- def set_at(self, expression: str, range_expr: Union[str, dt.datetime], value):
1739
+ def set_at(self, expression: str, range_expr: DateLike, value: ValueLike):
1558
1740
  if not isinstance(range_expr, (str, dt.datetime)):
1559
1741
  raise FactoryException("range_expr must be a string or datetime object.")
1560
- _err = Error()
1561
- _value = Value()
1562
- _value.set(value)
1563
- _range = Value()
1564
- _range.set(range_expr)
1742
+ error = Error()
1743
+ value_object = Value()
1744
+ value_object.set(value)
1745
+ range_value = Value()
1746
+ range_value.set(range_expr)
1565
1747
  factorylib.lib.psrd_study_set_value_at(self._hdr,
1566
1748
  _c_str(expression),
1567
1749
  _bytes(expression),
1568
- _range.handler(),
1569
- _value.handler(),
1570
- _err.handler())
1571
- if _err.code != 0:
1572
- raise FactoryException(_err.what)
1750
+ range_value.handler(),
1751
+ value_object.handler(),
1752
+ error.handler())
1753
+ if error.code != 0:
1754
+ raise FactoryException(error.what)
1573
1755
 
1574
- def get_df(self, expression: str) -> "pandas.DataFrame":
1575
- _err = Error()
1576
- _df = DataFrame()
1577
- factorylib.lib.psrd_study_get_table(self._hdr, _df.handler(),
1756
+ def get_df(self, expression: str) -> DataFrameLike:
1757
+ error = Error()
1758
+ df = DataFrame()
1759
+ factorylib.lib.psrd_study_get_table(self._hdr, df.handler(),
1578
1760
  _c_str(expression),
1579
1761
  _bytes(expression),
1580
- _err.handler())
1581
- if _err.code != 0:
1582
- raise FactoryException(_err.what)
1762
+ error.handler())
1763
+ if error.code != 0:
1764
+ raise FactoryException(error.what)
1583
1765
  df_builder = _DataFrameBuilder()
1584
- df_builder.build_dataframe(_df)
1585
- return df_builder.build_pandas_dataframe()
1766
+ df_builder.build_dataframe(df)
1767
+ return df_builder.build_desired_dataframe_type()
1586
1768
 
1587
- def get_objects_values(self, object_type: str, columns: List[str]) -> "pandas.DataFrame":
1588
- _err = Error()
1589
- _table = DataFrame()
1769
+ def get_objects_values(self, object_type: str, columns: List[str]) -> DataFrameLike:
1770
+ error = Error()
1771
+ df = DataFrame()
1590
1772
  columns_list = Value()
1591
1773
  columns_list.set(columns)
1592
- factorylib.lib.psrd_study_get_objects_values(self._hdr, _table.handler(),
1774
+ factorylib.lib.psrd_study_get_objects_values(self._hdr, df.handler(),
1593
1775
  _c_str(object_type),
1594
1776
  columns_list.handler(),
1595
- _err.handler())
1596
- if _err.code != 0:
1597
- raise FactoryException(_err.what)
1777
+ error.handler())
1778
+ if error.code != 0:
1779
+ raise FactoryException(error.what)
1598
1780
  df_builder = _DataFrameBuilder()
1599
- df_builder.build_dataframe(_table)
1600
- return df_builder.build_pandas_dataframe()
1781
+ df_builder.build_dataframe(df)
1782
+ return df_builder.build_desired_dataframe_type()
1601
1783
 
1602
- def get_objects_values_at(self, object_type: str, columns: List[str], range: Union[str, dt.datetime]) -> "pandas.DataFrame":
1603
- _err = Error()
1604
- _df = DataFrame()
1605
- _range = Value()
1606
- _range.set(range)
1784
+ def get_objects_values_at(self, object_type: str, columns: List[str], range_value: DateLike) -> DataFrameLike:
1785
+ error = Error()
1786
+ df = DataFrame()
1787
+ range_object = Value()
1788
+ range_object.set(range_value)
1607
1789
  columns_list = Value()
1608
1790
  columns_list.set(columns)
1609
- factorylib.lib.psrd_study_get_objects_values_at(self._hdr, _df.handler(),
1791
+ factorylib.lib.psrd_study_get_objects_values_at(self._hdr, df.handler(),
1610
1792
  _c_str(object_type),
1611
1793
  columns_list.handler(),
1612
- _range.handler(),
1613
- _err.handler())
1614
- if _err.code != 0:
1615
- raise FactoryException(_err.what)
1794
+ range_object.handler(),
1795
+ error.handler())
1796
+ if error.code != 0:
1797
+ raise FactoryException(error.what)
1616
1798
  df_builder = _DataFrameBuilder()
1617
- df_builder.build_dataframe(_df)
1618
- return df_builder.build_pandas_dataframe()
1799
+ df_builder.build_dataframe(df)
1800
+ return df_builder.build_desired_dataframe_type()
1619
1801
 
1620
- def properties(self) -> Dict[str, PropertyDescription]:
1621
- _err = Error()
1802
+ def descriptions(self) -> Dict[str, PropertyDescription]:
1803
+ error = Error()
1622
1804
  value = ctypes.c_long()
1623
1805
  factorylib.lib.psrd_study_property_description_count(self._hdr,
1624
1806
  ctypes.byref(value),
1625
- _err.handler())
1626
- if _err.code != 0:
1627
- raise FactoryException(_err.what)
1807
+ error.handler())
1808
+ if error.code != 0:
1809
+ raise FactoryException(error.what)
1628
1810
  var_count = int(value.value)
1629
1811
  properties = {}
1630
1812
  for i_var in range(var_count):
1631
1813
  var = PropertyDescription()
1632
1814
  ref = factorylib.lib.psrd_study_get_property_description(self._hdr,
1633
1815
  i_var,
1634
- _err.handler())
1635
- if _err.code != 0 or ref is None:
1636
- raise FactoryException(_err.what)
1816
+ error.handler())
1817
+ if error.code != 0 or ref is None:
1818
+ raise FactoryException(error.what)
1637
1819
  var._hdr = ref
1638
1820
  properties[var.name] = var
1639
1821
  return properties
1640
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
+
1641
1846
  def set_df(self, dataframe_like):
1642
1847
  if not _has_pandas():
1643
1848
  raise ModuleNotFoundError("pandas required.")
1644
1849
  dataframe_like = pandas.api.interchange.from_dataframe(dataframe_like)
1645
1850
  df_builder = _DataFrameBuilder()
1646
- _df = df_builder.build_from_pandas(dataframe_like)
1647
- _err = Error()
1648
- factorylib.lib.psrd_study_set_table(self._hdr, _df.handler(),
1649
- _err.handler())
1650
- if _err.code != 0:
1651
- raise FactoryException(_err.what)
1851
+ df = df_builder.build_from_pandas(dataframe_like)
1852
+ error = Error()
1853
+ factorylib.lib.psrd_study_set_table(self._hdr, df.handler(),
1854
+ error.handler())
1855
+ if error.code != 0:
1856
+ raise FactoryException(error.what)
1652
1857
 
1653
1858
  def clear_values(self, expression: str):
1654
- _err = Error()
1859
+ error = Error()
1655
1860
  factorylib.lib.psrd_study_clear_values(self._hdr,
1656
1861
  _c_str(expression),
1657
1862
  _bytes(expression),
1658
- _err.handler())
1659
- if _err.code != 0:
1660
- raise FactoryException(_err.what)
1863
+ error.handler())
1864
+ if error.code != 0:
1865
+ raise FactoryException(error.what)
1661
1866
 
1662
1867
 
1663
1868
  def _is_int64(value):
@@ -1670,6 +1875,40 @@ def _is_float64(value):
1670
1875
  return isinstance(value, (numpy.float64, float)) or (isinstance(value, numpy.ndarray) and value.dtype == numpy.float64)
1671
1876
 
1672
1877
 
1878
+ def _pandas_dtype_to_column_type(dtype: str) -> int:
1879
+ if dtype == "object":
1880
+ return 0
1881
+ elif dtype == "int32":
1882
+ return 1
1883
+ elif dtype == "int64":
1884
+ return 2
1885
+ elif dtype == "float32":
1886
+ return 3
1887
+ elif dtype == "float64":
1888
+ return 4
1889
+ elif dtype == "string":
1890
+ return 5
1891
+ elif dtype == "datetime64[ns]":
1892
+ return 6
1893
+ else:
1894
+ raise FactoryException(f"Unsupported pandas dtype \"{dtype}\".")
1895
+
1896
+
1897
+ def _polars_dtype_to_column_type(dtype: "polars.datatypes.classes.DataTypeClass") -> int:
1898
+ if dtype == polars.Int32:
1899
+ return 1
1900
+ if dtype == polars.Int64:
1901
+ return 2
1902
+ if dtype == polars.Float64:
1903
+ return 3
1904
+ if dtype == polars.Float64:
1905
+ return 4
1906
+ if dtype == polars.String:
1907
+ return 5
1908
+ else:
1909
+ raise FactoryException(f"Unsupported polars dtype \"{dtype}\".")
1910
+
1911
+
1673
1912
  class _DataFrameBuilder:
1674
1913
  def __init__(self):
1675
1914
  self.indices: List[Optional[_TableColumn]] = []
@@ -1680,67 +1919,34 @@ class _DataFrameBuilder:
1680
1919
  self.index_types: List[int] = []
1681
1920
  self._not_built = True
1682
1921
  self._value: Optional[Value] = None
1683
- self._err: Optional[Error] = None
1684
-
1685
- def _pandas_dtype_to_column_type(self, dtype: str) -> int:
1686
- if dtype == "object":
1687
- return 0
1688
- elif dtype == "int32":
1689
- return 1
1690
- elif dtype == "int64":
1691
- return 2
1692
- elif dtype == "float32":
1693
- return 3
1694
- elif dtype == "float64":
1695
- return 4
1696
- elif dtype == "string":
1697
- return 5
1698
- elif dtype == "datetime64[ns]":
1699
- return 6
1700
- else:
1701
- raise FactoryException(f"Unsupported pandas dtype \"{dtype}\".")
1702
-
1703
- def _polars_dtype_to_column_type(self, dtype: "polars.datatypes.classes.DataTypeClass") -> int:
1704
- if dtype == polars.Int32:
1705
- return 1
1706
- if dtype == polars.Int64:
1707
- return 2
1708
- if dtype == polars.Float64:
1709
- return 3
1710
- if dtype == polars.Float64:
1711
- return 4
1712
- if dtype == polars.String:
1713
- return 5
1714
- else:
1715
- raise FactoryException(f"Unsupported polars dtype \"{dtype}\".")
1716
-
1922
+ self._error: Optional[Error] = None
1717
1923
 
1718
1924
  def build_dataframe(self, df: "DataFrame"):
1719
- self._err = Error()
1925
+ self._error = Error()
1720
1926
  name_buffer_length = 200
1721
- _columns_count = ctypes.c_long()
1927
+ columns_count = ctypes.c_long()
1722
1928
  factorylib.lib.psrd_table_columns_count(df.handler(),
1723
- ctypes.byref(_columns_count),
1724
- self._err.handler())
1725
- if self._err.code != 0:
1726
- raise FactoryException(self._err.what)
1727
- columns_count = _columns_count.value
1929
+ ctypes.byref(columns_count),
1930
+ self._error.handler())
1931
+ if self._error.code != 0:
1932
+ raise FactoryException(self._error.what)
1933
+ columns_count = columns_count.value
1728
1934
 
1729
- _rows_count = ctypes.c_long()
1935
+ rows_count = ctypes.c_long()
1730
1936
  factorylib.lib.psrd_table_rows_count(df.handler(),
1731
- ctypes.byref(_rows_count),
1732
- self._err.handler())
1733
- if self._err.code != 0:
1734
- raise FactoryException(self._err.what)
1735
- rows_count = _rows_count.value
1937
+ ctypes.byref(rows_count),
1938
+ self._error.handler())
1939
+ if self._error.code != 0:
1940
+ raise FactoryException(self._error.what)
1941
+ rows_count = rows_count.value
1736
1942
 
1737
- _indices_count = ctypes.c_long()
1943
+ indices_count = ctypes.c_long()
1738
1944
  factorylib.lib.psrd_table_index_count(df.handler(),
1739
- ctypes.byref(_indices_count),
1740
- self._err.handler())
1741
- if self._err.code != 0:
1742
- raise FactoryException(self._err.what)
1743
- indices_count = _indices_count.value
1945
+ ctypes.byref(indices_count),
1946
+ self._error.handler())
1947
+ if self._error.code != 0:
1948
+ raise FactoryException(self._error.what)
1949
+ indices_count = indices_count.value
1744
1950
 
1745
1951
  value = Value()
1746
1952
  buffer = ctypes.create_string_buffer(name_buffer_length)
@@ -1749,27 +1955,27 @@ class _DataFrameBuilder:
1749
1955
  for index in range(indices_count):
1750
1956
  factorylib.lib.psrd_table_index_get_name(df.handler(), index,
1751
1957
  buffer, name_buffer_length,
1752
- self._err.handler())
1753
- if self._err.code != 0:
1754
- raise FactoryException(self._err.what)
1958
+ self._error.handler())
1959
+ if self._error.code != 0:
1960
+ raise FactoryException(self._error.what)
1755
1961
  self.indices[index].name = _from_c_str(buffer.value)
1756
1962
 
1757
1963
  self.indices[index].values = [None] * rows_count
1758
1964
  for i_row in range(0, rows_count):
1759
1965
  factorylib.lib.psrd_table_index_get_value(df.handler(), index,
1760
1966
  i_row, value.handler(),
1761
- self._err.handler())
1762
- if self._err.code != 0:
1763
- raise FactoryException(self._err.what)
1967
+ self._error.handler())
1968
+ if self._error.code != 0:
1969
+ raise FactoryException(self._error.what)
1764
1970
  self.indices[index].values[i_row] = value.get()
1765
1971
 
1766
1972
  self.columns = [_TableColumn() for _ in range(columns_count)]
1767
1973
  for column in range(columns_count):
1768
1974
  factorylib.lib.psrd_table_column_get_name(df.handler(), column,
1769
1975
  buffer, name_buffer_length,
1770
- self._err.handler())
1771
- if self._err.code != 0:
1772
- raise FactoryException(self._err.what)
1976
+ self._error.handler())
1977
+ if self._error.code != 0:
1978
+ raise FactoryException(self._error.what)
1773
1979
  self.columns[column].name = _from_c_str(buffer.value)
1774
1980
 
1775
1981
  self.columns[column].values = [None] * rows_count
@@ -1777,38 +1983,38 @@ class _DataFrameBuilder:
1777
1983
  factorylib.lib.psrd_table_column_get_value(df.handler(),
1778
1984
  column, row,
1779
1985
  value.handler(),
1780
- self._err.handler())
1781
- if self._err.code != 0:
1782
- raise FactoryException(self._err.what)
1986
+ self._error.handler())
1987
+ if self._error.code != 0:
1988
+ raise FactoryException(self._error.what)
1783
1989
  self.columns[column].values[row] = value.get()
1784
1990
  self._not_built = False
1785
1991
 
1786
1992
  def build_dataframe_of_integral_types(self, df: "DataFrame"):
1787
- self._err = Error()
1993
+ self._error = Error()
1788
1994
  name_buffer_length = 200
1789
- _columns_count = ctypes.c_long()
1995
+ columns_count = ctypes.c_long()
1790
1996
  factorylib.lib.psrd_table_columns_count(df.handler(),
1791
- ctypes.byref(_columns_count),
1792
- self._err.handler())
1793
- if self._err.code != 0:
1794
- raise FactoryException(self._err.what)
1795
- columns_count = _columns_count.value
1997
+ ctypes.byref(columns_count),
1998
+ self._error.handler())
1999
+ if self._error.code != 0:
2000
+ raise FactoryException(self._error.what)
2001
+ columns_count = columns_count.value
1796
2002
 
1797
- _rows_count = ctypes.c_long()
2003
+ rows_count = ctypes.c_long()
1798
2004
  factorylib.lib.psrd_table_rows_count(df.handler(),
1799
- ctypes.byref(_rows_count),
1800
- self._err.handler())
1801
- if self._err.code != 0:
1802
- raise FactoryException(self._err.what)
1803
- rows_count = _rows_count.value
2005
+ ctypes.byref(rows_count),
2006
+ self._error.handler())
2007
+ if self._error.code != 0:
2008
+ raise FactoryException(self._error.what)
2009
+ rows_count = rows_count.value
1804
2010
 
1805
- _indices_count = ctypes.c_long()
2011
+ indices_count = ctypes.c_long()
1806
2012
  factorylib.lib.psrd_table_index_count(df.handler(),
1807
- ctypes.byref(_indices_count),
1808
- self._err.handler())
1809
- if self._err.code != 0:
1810
- raise FactoryException(self._err.what)
1811
- indices_count = _indices_count.value
2013
+ ctypes.byref(indices_count),
2014
+ self._error.handler())
2015
+ if self._error.code != 0:
2016
+ raise FactoryException(self._error.what)
2017
+ indices_count = indices_count.value
1812
2018
 
1813
2019
  buffer = ctypes.create_string_buffer(name_buffer_length)
1814
2020
 
@@ -1816,9 +2022,9 @@ class _DataFrameBuilder:
1816
2022
  for index in range(indices_count):
1817
2023
  factorylib.lib.psrd_table_index_get_name(df.handler(), index,
1818
2024
  buffer, name_buffer_length,
1819
- self._err.handler())
1820
- if self._err.code != 0:
1821
- raise FactoryException(self._err.what)
2025
+ self._error.handler())
2026
+ if self._error.code != 0:
2027
+ raise FactoryException(self._error.what)
1822
2028
  index_name = _from_c_str(buffer.value)
1823
2029
  self.indices[index].name = index_name
1824
2030
  self.indices[index].values = [None] * rows_count
@@ -1827,40 +2033,52 @@ class _DataFrameBuilder:
1827
2033
  factorylib.lib.psrd_table_index_get_date_values(df.handler(),
1828
2034
  index,
1829
2035
  array_values,
1830
- self._err.handler())
1831
- if self._err.code != 0:
1832
- raise FactoryException(self._err.what)
2036
+ self._error.handler())
2037
+ if self._error.code != 0:
2038
+ raise FactoryException(self._error.what)
1833
2039
  # convert array values to python datetime
2040
+ if _date_transform is None:
2041
+ raise FactoryException("Factory is not initialized correctly.")
1834
2042
  self.indices[index].values = [dt.datetime.fromtimestamp(value - _date_transform, dt.UTC) for value in array_values]
1835
2043
  else:
1836
2044
  array_values = (ctypes.c_int * rows_count)()
1837
2045
  factorylib.lib.psrd_table_index_get_int32_values(df.handler(),
1838
2046
  index,
1839
2047
  array_values,
1840
- self._err.handler())
1841
- if self._err.code != 0:
1842
- raise FactoryException(self._err.what)
2048
+ self._error.handler())
2049
+ if self._error.code != 0:
2050
+ raise FactoryException(self._error.what)
1843
2051
  self.indices[index].values = array_values
1844
2052
 
1845
2053
  self.columns = [_TableColumn() for _ in range(columns_count)]
1846
2054
  for column in range(columns_count):
1847
2055
  factorylib.lib.psrd_table_column_get_name(df.handler(), column,
1848
2056
  buffer, name_buffer_length,
1849
- self._err.handler())
1850
- if self._err.code != 0:
1851
- raise FactoryException(self._err.what)
2057
+ self._error.handler())
2058
+ if self._error.code != 0:
2059
+ raise FactoryException(self._error.what)
1852
2060
  self.columns[column].name = _from_c_str(buffer.value)
1853
2061
 
1854
2062
  array_values = (ctypes.c_double * rows_count)()
1855
2063
  factorylib.lib.psrd_table_column_get_float64_values(df.handler(),
1856
2064
  column,
1857
2065
  array_values,
1858
- self._err.handler())
1859
- if self._err.code != 0:
1860
- raise FactoryException(self._err.what)
2066
+ self._error.handler())
2067
+ if self._error.code != 0:
2068
+ raise FactoryException(self._error.what)
1861
2069
  self.columns[column].values = array_values
1862
2070
  self._not_built = False
1863
2071
 
2072
+ def build_desired_dataframe_type(self, **kwargs) -> DataFrameLike:
2073
+ if _default_dataframe_type == "pandas":
2074
+ return self.build_pandas_dataframe(**kwargs)
2075
+ elif _default_dataframe_type == "polars":
2076
+ return self.build_polars_dataframe(**kwargs)
2077
+ elif _default_dataframe_type == "factory":
2078
+ raise NotImplementedError("Returning a psr.factory.DataFrame not implemented yet.")
2079
+ else:
2080
+ raise FactoryException(f"Unsupported default dataframe type \"{_default_dataframe_type}\".")
2081
+
1864
2082
  def build_pandas_dataframe(self, **kwargs) -> "pandas.DataFrame":
1865
2083
  use_object_dtype = kwargs.get("use_object_dtype", True)
1866
2084
  if not _has_pandas():
@@ -1905,16 +2123,18 @@ class _DataFrameBuilder:
1905
2123
  table_data_indices = []
1906
2124
 
1907
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.")
1908
2128
  self.index_names = [index.name for index in table_data_indices]
1909
- self.column_types = [self._pandas_dtype_to_column_type(dtype) for dtype in table_data.dtypes]
1910
- self.index_types = [self._pandas_dtype_to_column_type(index.dtype) for index in table_data_indices]
2129
+ self.column_types = [_pandas_dtype_to_column_type(dtype) for dtype in table_data.dtypes]
2130
+ self.index_types = [_pandas_dtype_to_column_type(index.dtype) for index in table_data_indices]
1911
2131
  replaced_name = False
1912
2132
  for i, name in enumerate(self.index_names):
1913
2133
  if name is None:
1914
2134
  self.index_names[i] = 'date'
1915
2135
  replaced_name = True
1916
2136
  rows = len(table_data.index)
1917
- _df = self._pre_build_generic(rows, self.index_types, self.column_types)
2137
+ df = self._pre_build_generic(rows, self.index_types, self.column_types)
1918
2138
 
1919
2139
  test_conversion_types = {
1920
2140
  pandas.api.types.is_integer_dtype: numpy.int32,
@@ -1945,9 +2165,9 @@ class _DataFrameBuilder:
1945
2165
  break
1946
2166
 
1947
2167
  if replaced_name:
1948
- for i, name in enumerate(self.index_names):
2168
+ for i_index, name in enumerate(self.index_names):
1949
2169
  if name == "date":
1950
- self.index_names[i] = None
2170
+ self.index_names[i_index] = None
1951
2171
  # check index value types
1952
2172
  index_convert_to = {}
1953
2173
  index_fast_set = {}
@@ -1980,61 +2200,63 @@ class _DataFrameBuilder:
1980
2200
  # TODO: check if original dataframe values is unaltered
1981
2201
  index_values = index_values.astype('datetime64[s]').astype(dt.datetime)
1982
2202
  # for each value, convert to timestamp
2203
+ if _date_transform is None:
2204
+ raise FactoryException("Factory is not initialized correctly.")
1983
2205
  for ix, x in enumerate(index_values):
1984
2206
  index_values[ix] = int(x.replace(tzinfo=dt.timezone.utc).timestamp() + _date_transform)
1985
2207
  # convert to int64
1986
2208
  index_values = index_values.astype(numpy.int64)
1987
2209
  ptr = index_values.ctypes.data_as(ctypes.POINTER(convert_to_ctype[convert_to_type]))
1988
- factorylib.lib.psrd_table_index_set_date_values(_df.handler(),
2210
+ factorylib.lib.psrd_table_index_set_date_values(df.handler(),
1989
2211
  i_index,
1990
2212
  ptr,
1991
- self._err.handler())
1992
- if self._err.code != 0:
1993
- raise FactoryException(self._err.what)
2213
+ self._error.handler())
2214
+ if self._error.code != 0:
2215
+ raise FactoryException(self._error.what)
1994
2216
  elif convert_to_type == numpy.int32:
1995
2217
  ptr = index_values.ctypes.data_as(ctypes.POINTER(convert_to_ctype[convert_to_type]))
1996
- factorylib.lib.psrd_table_index_set_int32_values(_df.handler(),
2218
+ factorylib.lib.psrd_table_index_set_int32_values(df.handler(),
1997
2219
  i_index,
1998
2220
  ptr,
1999
- self._err.handler())
2000
- if self._err.code != 0:
2001
- raise FactoryException(self._err.what)
2221
+ self._error.handler())
2222
+ if self._error.code != 0:
2223
+ raise FactoryException(self._error.what)
2002
2224
  elif convert_to_type == numpy.int64:
2003
2225
  ptr = index_values.ctypes.data_as(ctypes.POINTER(convert_to_ctype[convert_to_type]))
2004
- factorylib.lib.psrd_table_index_set_int64_values(_df.handler(),
2226
+ factorylib.lib.psrd_table_index_set_int64_values(df.handler(),
2005
2227
  i_index,
2006
2228
  ptr,
2007
- self._err.handler())
2008
- if self._err.code != 0:
2009
- raise FactoryException(self._err.what)
2229
+ self._error.handler())
2230
+ if self._error.code != 0:
2231
+ raise FactoryException(self._error.what)
2010
2232
  elif convert_to_type == numpy.float32:
2011
2233
  ptr = index_values.ctypes.data_as(ctypes.POINTER(convert_to_ctype[convert_to_type]))
2012
- factorylib.lib.psrd_table_index_set_float32_values(_df.handler(),
2013
- i_index,
2014
- ptr,
2015
- self._err.handler())
2016
- if self._err.code != 0:
2017
- raise FactoryException(self._err.what)
2234
+ factorylib.lib.psrd_table_index_set_float32_values(df.handler(),
2235
+ i_index,
2236
+ ptr,
2237
+ self._error.handler())
2238
+ if self._error.code != 0:
2239
+ raise FactoryException(self._error.what)
2018
2240
  elif convert_to_type == numpy.float64:
2019
2241
  ptr = index_values.ctypes.data_as(ctypes.POINTER(convert_to_ctype[convert_to_type]))
2020
- factorylib.lib.psrd_table_index_set_float64_values(_df.handler(),
2242
+ factorylib.lib.psrd_table_index_set_float64_values(df.handler(),
2021
2243
  i_index,
2022
2244
  ptr,
2023
- self._err.handler())
2024
- if self._err.code != 0:
2025
- raise FactoryException(self._err.what)
2245
+ self._error.handler())
2246
+ if self._error.code != 0:
2247
+ raise FactoryException(self._error.what)
2026
2248
  else:
2027
2249
  raise FactoryException("Unsupported index type: " + str(convert_to_type))
2028
2250
  else:
2029
2251
  for i_row, column_value in enumerate(index_values):
2030
2252
  self._value.set(column_value)
2031
- factorylib.lib.psrd_table_index_set_value(_df.handler(),
2253
+ factorylib.lib.psrd_table_index_set_value(df.handler(),
2032
2254
  i_index,
2033
2255
  i_row,
2034
2256
  self._value.handler(),
2035
- self._err.handler())
2036
- if self._err.code != 0:
2037
- raise FactoryException(self._err.what)
2257
+ self._error.handler())
2258
+ if self._error.code != 0:
2259
+ raise FactoryException(self._error.what)
2038
2260
 
2039
2261
  for i_column, column_name in enumerate(self.column_names):
2040
2262
  if column_name in column_convert_to.keys():
@@ -2045,49 +2267,48 @@ class _DataFrameBuilder:
2045
2267
  if column_name in column_fast_set.keys() and column_fast_set[column_name]:
2046
2268
  if convert_to_type == numpy.float32:
2047
2269
  ptr = column_values.ctypes.data_as(ctypes.POINTER(convert_to_ctype[convert_to_type]))
2048
- factorylib.lib.psrd_table_column_set_float32_values(_df.handler(),
2270
+ factorylib.lib.psrd_table_column_set_float32_values(df.handler(),
2049
2271
  i_column,
2050
2272
  ptr,
2051
- self._err.handler())
2052
- if self._err.code != 0:
2053
- raise FactoryException(self._err.code)
2273
+ self._error.handler())
2274
+ if self._error.code != 0:
2275
+ raise FactoryException(self._error.code)
2054
2276
  if convert_to_type == numpy.float64:
2055
2277
  ptr = column_values.ctypes.data_as(ctypes.POINTER(convert_to_ctype[convert_to_type]))
2056
- factorylib.lib.psrd_table_column_set_float64_values(_df.handler(),
2278
+ factorylib.lib.psrd_table_column_set_float64_values(df.handler(),
2057
2279
  i_column,
2058
2280
  ptr,
2059
- self._err.handler())
2060
- if self._err.code != 0:
2061
- raise FactoryException(self._err.what)
2281
+ self._error.handler())
2282
+ if self._error.code != 0:
2283
+ raise FactoryException(self._error.what)
2062
2284
  elif convert_to_type == numpy.int32:
2063
2285
  ptr = column_values.ctypes.data_as(ctypes.POINTER(convert_to_ctype[convert_to_type]))
2064
- factorylib.lib.psrd_table_column_set_int32_values(_df.handler(),
2286
+ factorylib.lib.psrd_table_column_set_int32_values(df.handler(),
2065
2287
  i_column,
2066
2288
  ptr,
2067
- self._err.handler())
2068
- if self._err.code != 0:
2069
- raise FactoryException(self._err.what)
2289
+ self._error.handler())
2290
+ if self._error.code != 0:
2291
+ raise FactoryException(self._error.what)
2070
2292
  elif convert_to_type == numpy.int64:
2071
2293
  ptr = column_values.ctypes.data_as(ctypes.POINTER(convert_to_ctype[convert_to_type]))
2072
- factorylib.lib.psrd_table_column_set_int64_values(_df.handler(),
2294
+ factorylib.lib.psrd_table_column_set_int64_values(df.handler(),
2073
2295
  i_column,
2074
2296
  ptr,
2075
- self._err.handler())
2076
- if self._err.code != 0:
2077
- raise FactoryException(self._err.what)
2297
+ self._error.handler())
2298
+ if self._error.code != 0:
2299
+ raise FactoryException(self._error.what)
2078
2300
  else:
2079
2301
  column_values = table_data[column_name]
2080
2302
  for i_row, column_value in enumerate(column_values):
2081
2303
  self._value.set(column_value)
2082
- factorylib.lib.psrd_table_column_set_value(_df.handler(),
2304
+ factorylib.lib.psrd_table_column_set_value(df.handler(),
2083
2305
  i_column,
2084
2306
  i_row,
2085
2307
  self._value.handler(),
2086
- self._err.handler())
2087
- if self._err.code != 0:
2088
- raise FactoryException(self._err.what)
2089
- return _df
2090
-
2308
+ self._error.handler())
2309
+ if self._error.code != 0:
2310
+ raise FactoryException(self._error.what)
2311
+ return df
2091
2312
 
2092
2313
  def build_polars_dataframe(self, **kwargs) -> "polars.DataFrame":
2093
2314
  use_object_dtype = kwargs.get("use_object_dtype", True)
@@ -2110,75 +2331,76 @@ class _DataFrameBuilder:
2110
2331
 
2111
2332
  def build_from_polars(self, table_data: "polars.DataFrame") -> "DataFrame":
2112
2333
  # check if the table has indices and if its multi-index or common index
2113
- _index_names = ("year", "week", "month", "hour", "scenario", "block", "stage")
2334
+ index_names = ("year", "week", "month", "hour", "scenario", "block", "stage")
2114
2335
  column_index = 0
2115
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.")
2116
2339
  index_columns = []
2117
2340
  while column_index < len(data_columns):
2118
- if data_columns[column_index] in _index_names:
2341
+ if data_columns[column_index] in index_names:
2119
2342
  index_columns.append(data_columns.pop(column_index))
2120
2343
  continue
2121
2344
  column_index += 1
2122
- self.column_types = [self._polars_dtype_to_column_type(table_data[column_name].dtype) for column_name in data_columns]
2123
- self.index_types = [self._polars_dtype_to_column_type(table_data[index_name].dtype) for index_name in index_columns]
2345
+ self.column_types = [_polars_dtype_to_column_type(table_data[column_name].dtype) for column_name in data_columns]
2346
+ self.index_types = [_polars_dtype_to_column_type(table_data[index_name].dtype) for index_name in index_columns]
2124
2347
 
2125
2348
  self.column_names = data_columns
2126
2349
  self.index_names = index_columns
2127
2350
  rows = table_data.height
2128
- _df = self._pre_build_generic(rows, self.index_types, self.column_types)
2351
+ df = self._pre_build_generic(rows, self.index_types, self.column_types)
2129
2352
 
2130
2353
  for i_row, all_row_values in enumerate(table_data.iter_rows()):
2131
2354
  index = all_row_values[:len(index_columns)]
2132
2355
  row_values = all_row_values[len(index_columns):]
2133
- self._set_row_values(_df, i_row, index, row_values)
2134
- return _df
2356
+ self._set_row_values(df, i_row, index, row_values)
2357
+ return df
2135
2358
 
2136
2359
  def _pre_build_generic(self, rows: int, index_types: List[int], column_types: List[int]) -> "DataFrame":
2137
- _df = DataFrame()
2138
- self._err = Error()
2360
+ df = DataFrame()
2361
+ self._error = Error()
2139
2362
  self._value = Value()
2140
2363
 
2141
- factorylib.lib.psrd_table_resize(_df.handler(), rows,
2142
- self._err.handler())
2143
- if self._err.code != 0:
2144
- raise FactoryException(self._err.what)
2364
+ factorylib.lib.psrd_table_resize(df.handler(), rows,
2365
+ self._error.handler())
2366
+ if self._error.code != 0:
2367
+ raise FactoryException(self._error.what)
2145
2368
 
2146
2369
  for i_index, index_type in enumerate(index_types):
2147
- factorylib.lib.psrd_table_configure_index(_df.handler(),
2370
+ factorylib.lib.psrd_table_configure_index(df.handler(),
2148
2371
  i_index,
2149
2372
  index_type,
2150
- self._err.handler())
2151
- if self._err.code != 0:
2152
- raise FactoryException(self._err.what)
2373
+ self._error.handler())
2374
+ if self._error.code != 0:
2375
+ raise FactoryException(self._error.what)
2153
2376
  for i_column, column_type in enumerate(column_types):
2154
- factorylib.lib.psrd_table_configure_column(_df.handler(),
2377
+ factorylib.lib.psrd_table_configure_column(df.handler(),
2155
2378
  i_column,
2156
2379
  column_type,
2157
- self._err.handler())
2158
- if self._err.code != 0:
2159
- raise FactoryException(self._err.what)
2380
+ self._error.handler())
2381
+ if self._error.code != 0:
2382
+ raise FactoryException(self._error.what)
2160
2383
 
2161
2384
  # Set column names
2162
2385
  for i_column, column_name in enumerate(self.column_names):
2163
- factorylib.lib.psrd_table_column_set_name(_df.handler(),
2386
+ factorylib.lib.psrd_table_column_set_name(df.handler(),
2164
2387
  i_column,
2165
2388
  _c_str(column_name),
2166
2389
  _bytes(column_name),
2167
- self._err.handler())
2168
- if self._err.code != 0:
2169
- raise FactoryException(self._err.what)
2390
+ self._error.handler())
2391
+ if self._error.code != 0:
2392
+ raise FactoryException(self._error.what)
2170
2393
 
2171
2394
  # Set index names
2172
2395
  for i_index, index_name in enumerate(self.index_names):
2173
- factorylib.lib.psrd_table_index_set_name(_df.handler(),
2396
+ factorylib.lib.psrd_table_index_set_name(df.handler(),
2174
2397
  i_index,
2175
2398
  _c_str(index_name),
2176
2399
  _bytes(index_name),
2177
- self._err.handler())
2178
- if self._err.code != 0:
2179
- raise FactoryException(self._err.what)
2180
-
2181
- return _df
2400
+ self._error.handler())
2401
+ if self._error.code != 0:
2402
+ raise FactoryException(self._error.what)
2403
+ return df
2182
2404
 
2183
2405
  def _set_row_values(self, df: "DataFrame", i_row: int, index_values: List[Union[int, float, str]], column_values: List[Union[int, float, str]]):
2184
2406
  self._value = Value()
@@ -2188,9 +2410,9 @@ class _DataFrameBuilder:
2188
2410
  i_index,
2189
2411
  i_row,
2190
2412
  self._value.handler(),
2191
- self._err.handler())
2192
- if self._err.code != 0:
2193
- raise FactoryException(self._err.what)
2413
+ self._error.handler())
2414
+ if self._error.code != 0:
2415
+ raise FactoryException(self._error.what)
2194
2416
 
2195
2417
  for i_column, column_value in enumerate(column_values):
2196
2418
  self._value.set(column_value)
@@ -2198,9 +2420,9 @@ class _DataFrameBuilder:
2198
2420
  i_column,
2199
2421
  i_row,
2200
2422
  self._value.handler(),
2201
- self._err.handler())
2202
- if self._err.code != 0:
2203
- raise FactoryException(self._err.what)
2423
+ self._error.handler())
2424
+ if self._error.code != 0:
2425
+ raise FactoryException(self._error.what)
2204
2426
 
2205
2427
 
2206
2428
  class DataFrame(_BaseObject):
@@ -2214,25 +2436,28 @@ class DataFrame(_BaseObject):
2214
2436
  factorylib.lib.psrd_free_table(self._hdr)
2215
2437
 
2216
2438
  @staticmethod
2217
- 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":
2218
2440
  input_file = str(input_file)
2219
- _check_initialized()
2220
- _err = Error()
2221
- table = DataFrame()
2441
+ _check_basic_data_initialized()
2442
+ error = Error()
2443
+ df = DataFrame()
2222
2444
  options_value = _get_arg_object(options)
2223
- factorylib.lib.psrd_table_load(table.handler(),
2445
+ factorylib.lib.psrd_table_load(df.handler(),
2224
2446
  _c_str(input_file),
2225
2447
  _bytes(input_file),
2226
2448
  options_value.handler(),
2227
- _err.handler())
2228
- if _err.code != 0:
2229
- raise FactoryException(_err.what)
2230
- return table
2449
+ error.handler())
2450
+ if error.code != 0:
2451
+ raise FactoryException(error.what)
2452
+ return df
2231
2453
 
2232
2454
  @staticmethod
2233
- def from_dataframe(df: Union["pandas.DataFrame", "polars.DataFrame"], **kwargs) -> "DataFrame":
2234
- _check_initialized()
2455
+ def from_dataframe(df: DataFrameLike) -> "DataFrame":
2456
+ _check_basic_data_initialized()
2235
2457
  df_builder = _DataFrameBuilder()
2458
+ if isinstance(df, DataFrame):
2459
+ # FIXME: implement this
2460
+ raise NotImplementedError("Creating a DataFrame from another psr.factory.DataFrame is not implemented.")
2236
2461
  if _has_pandas() and isinstance(df, pandas.DataFrame):
2237
2462
  dataframe_like = pandas.api.interchange.from_dataframe(df)
2238
2463
  return df_builder.build_from_pandas(dataframe_like)
@@ -2242,16 +2467,16 @@ class DataFrame(_BaseObject):
2242
2467
  return df_builder.build_from_polars(dataframe_like)
2243
2468
  raise ImportError("Pandas or polars is not available. Please install pandas to use this feature.")
2244
2469
 
2245
- 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):
2246
2471
  output_file = str(output_file)
2247
- _err = Error()
2472
+ error = Error()
2248
2473
  options_value = _get_arg_object(options)
2249
2474
  factorylib.lib.psrd_table_save(self._hdr, _c_str(output_file),
2250
2475
  _bytes(output_file),
2251
2476
  options_value.handler(),
2252
- _err.handler())
2253
- if _err.code != 0:
2254
- raise FactoryException(_err.what)
2477
+ error.handler())
2478
+ if error.code != 0:
2479
+ raise FactoryException(error.what)
2255
2480
 
2256
2481
  def to_pandas(self) -> "pandas.DataFrame":
2257
2482
  df_builder = _DataFrameBuilder()
@@ -2264,36 +2489,58 @@ class DataFrame(_BaseObject):
2264
2489
  return df_builder.build_polars_dataframe(use_object_dtype=False)
2265
2490
 
2266
2491
 
2267
- def get(self, expression: str) -> Union[int, float, dt.datetime, str, "DataObject", list, None]:
2492
+ def get(self, expression: str) -> ValueLike:
2268
2493
  value = Value()
2269
- _err = Error()
2494
+ error = Error()
2270
2495
  factorylib.lib.psrd_table_get_property(self._hdr,
2271
2496
  _c_str(expression),
2272
2497
  value.handler(),
2273
- _err.handler())
2274
- if _err.code != 0:
2275
- raise FactoryException(_err.what)
2498
+ error.handler())
2499
+ if error.code != 0:
2500
+ raise FactoryException(error.what)
2276
2501
  return value.get()
2277
2502
 
2278
- def set(self, expression: str, value):
2279
- _err = Error()
2280
- _val = Value()
2281
- _val.set(value)
2503
+ def set(self, expression: str, value: ValueLike):
2504
+ error = Error()
2505
+ value_object = Value()
2506
+ value_object.set(value)
2282
2507
  factorylib.lib.psrd_table_set_property(self._hdr, _c_str(expression),
2283
2508
  _bytes(expression),
2284
- _val.handler(),
2285
- _err.handler())
2286
- if _err.code != 0:
2287
- raise FactoryException(_err.what)
2509
+ value_object.handler(),
2510
+ error.handler())
2511
+ if error.code != 0:
2512
+ raise FactoryException(error.what)
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)
2288
2531
 
2289
2532
 
2290
- def load_dataframe(input_file: Union[str, pathlib.Path], **kwargs) -> DataFrame:
2533
+ def load_dataframe(input_file: PathLike, **kwargs) -> DataFrame:
2291
2534
  options = kwargs.get("options", None)
2292
2535
  return DataFrame.load_from_file(input_file, options)
2293
2536
 
2294
2537
 
2295
- def create_dataframe(data: "pandas.DataFrame", **kwargs) -> DataFrame:
2296
- 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)
2297
2544
 
2298
2545
 
2299
2546
  def _load_library():
@@ -2305,13 +2552,12 @@ def _load_library():
2305
2552
  return _loaded
2306
2553
 
2307
2554
 
2308
-
2309
- def _initialize():
2310
- global _initialized
2311
- global _initialized_lock
2312
- 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:
2313
2559
  _check_loaded()
2314
- _err = Error()
2560
+ error = Error()
2315
2561
 
2316
2562
  # Set binding info
2317
2563
  map_prop_values = {
@@ -2325,44 +2571,66 @@ def _initialize():
2325
2571
  "BASE_PREFIX": f"{sys.base_prefix}",
2326
2572
  "REAL_PREFIX": f"{sys.prefix}",
2327
2573
  }
2328
- for prop, value in map_prop_values.items():
2329
- _value = Value()
2330
- _value.set(value)
2574
+ for prop, prop_value in map_prop_values.items():
2575
+ value_object = Value()
2576
+ value_object.set(prop_value)
2331
2577
  factorylib.lib.psrd_set_binding_property(_c_str(prop),
2332
2578
  _bytes(prop),
2333
- _value.handler(),
2334
- _err.handler())
2335
- if _err.code != 0:
2336
- raise FactoryException(_err.what)
2579
+ value_object.handler(),
2580
+ error.handler())
2581
+ if error.code != 0:
2582
+ raise FactoryException(error.what)
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()
2337
2598
 
2338
2599
  # Where to look for pmd and pmk files
2339
2600
  module_path = os.path.dirname(__file__)
2340
- factorylib.lib.psrd_initialize(_c_str(module_path),
2341
- _bytes(module_path),
2342
- _err.handler())
2343
- if _err.code != 0:
2344
- raise FactoryException(_err.what)
2345
- _initialized = True
2601
+ factorylib.lib.psrd_initialize_study_data(_c_str(module_path), _bytes(module_path), error.handler())
2602
+ if error.code != 0:
2603
+ raise FactoryException(error.what)
2604
+ _study_data_initialized = True
2346
2605
 
2347
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
+
2348
2616
  def _unload():
2349
- _err = Error()
2350
- factorylib.lib.psrd_unload(_err.handler())
2351
- if _err.code != 0:
2352
- raise FactoryException(_err.what)
2617
+ error = Error()
2618
+ factorylib.lib.psrd_unload(error.handler())
2619
+ if error.code != 0:
2620
+ raise FactoryException(error.what)
2353
2621
 
2354
2622
 
2355
2623
  def help(context: str = "") -> str:
2356
- _err = Error()
2624
+ error = Error()
2357
2625
  size = factorylib.lib.psrd_help(_c_str(context), _bytes(context),
2358
- None, 0, _err.handler())
2359
- if _err.code != 0:
2360
- raise FactoryException(_err.what)
2626
+ None, 0, error.handler())
2627
+ if error.code != 0:
2628
+ raise FactoryException(error.what)
2361
2629
  buffer = ctypes.create_string_buffer(size)
2362
2630
  factorylib.lib.psrd_help(_c_str(context), _bytes(context),
2363
- buffer, size, _err.handler())
2364
- if _err.code != 0:
2365
- raise FactoryException(_err.what)
2631
+ buffer, size, error.handler())
2632
+ if error.code != 0:
2633
+ raise FactoryException(error.what)
2366
2634
  return _from_c_str(buffer.value)
2367
2635
 
2368
2636
 
@@ -2370,52 +2638,75 @@ def create_study(*args, **kwargs) -> Study:
2370
2638
  blocks = kwargs.get("blocks", None)
2371
2639
  models = kwargs.get("models", None)
2372
2640
  context = kwargs.get("context", None) if len(args) == 0 else args[0]
2373
- profile = kwargs.get("profile", None)
2374
- if models is None and profile is not None and len(profile) > 0:
2375
- models = [profile, ]
2376
- profile_or_context = models if models is not None and len(models) > 0 \
2377
- else context
2378
- return Study.create_object(profile_or_context, blocks)
2641
+ if "profile" in kwargs:
2642
+ raise DeprecationWarning("The 'profile' argument is deprecated. Use 'models' instead.")
2643
+ model_or_context = models if models is not None and len(models) > 0 else context
2644
+ return Study.create_object(model_or_context, blocks)
2379
2645
 
2380
2646
 
2381
- def load_study(study_path: Union[str, pathlib.Path],
2382
- profile_or_context: Union[str, Context, None] = None,
2647
+ def load_study(study_path: PathLike,
2648
+ model_or_context: Union[str, Context, None] = None,
2383
2649
  options: Optional[DataObject] = None) -> Study:
2384
2650
  settings_only = False
2385
- return Study.load(study_path, profile_or_context, settings_only, options)
2651
+ return Study.load(study_path, model_or_context, settings_only, options)
2386
2652
 
2387
2653
 
2388
- def load_study_settings(study_path: Union[str, pathlib.Path],
2389
- profile_or_context: Union[str, Context, None] = None,
2654
+ def load_study_settings(study_path: PathLike,
2655
+ model_or_context: Union[str, Context, None] = None,
2390
2656
  options: Optional[DataObject] = None) -> Study:
2391
2657
  settings_only = True
2392
- return Study.load(study_path, profile_or_context, settings_only, options)
2393
-
2394
-
2395
- def create(class_name: str,
2396
- profile_or_context: Union[str, Context, None] = None) -> Optional[DataObject]:
2397
- _check_initialized()
2398
- obj = DataObject()
2399
- _err = Error()
2400
- context = _get_context(profile_or_context, None) \
2401
- if class_name != "Context" else None
2402
- context_hdr = context.handler() if context is not None else None
2403
- ref = factorylib.lib.psrd_create(_c_str(class_name),
2404
- context_hdr,
2405
- _err.handler())
2406
- if _err.code != 0 or ref is None:
2407
- raise FactoryException(_err.what)
2408
- obj._hdr = ref
2409
- return obj
2658
+ return Study.load(study_path, model_or_context, settings_only, options)
2659
+
2660
+
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())
2679
+ if error.code != 0 or handler is None:
2680
+ raise FactoryException(error.what)
2681
+ data_object._hdr = handler
2682
+ return data_object
2683
+
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)
2410
2701
 
2411
2702
 
2412
2703
  def get_default_context() -> "Context":
2413
- _check_initialized()
2704
+ _check_basic_data_initialized()
2414
2705
  return Context.default_context()
2415
2706
 
2416
2707
 
2417
2708
  def get_new_context() -> "Context":
2418
- _check_initialized()
2709
+ _check_basic_data_initialized()
2419
2710
  return Context.create()
2420
2711
 
2421
2712