py10x-universe 0.1.3__py3-none-any.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.
Files changed (214) hide show
  1. core_10x/__init__.py +42 -0
  2. core_10x/backbone/__init__.py +0 -0
  3. core_10x/backbone/backbone_store.py +59 -0
  4. core_10x/backbone/backbone_traitable.py +30 -0
  5. core_10x/backbone/backbone_user.py +66 -0
  6. core_10x/backbone/bound_data_domain.py +49 -0
  7. core_10x/backbone/namespace.py +101 -0
  8. core_10x/backbone/vault.py +38 -0
  9. core_10x/code_samples/__init__.py +0 -0
  10. core_10x/code_samples/_package_manifest.py +3 -0
  11. core_10x/code_samples/directories.py +181 -0
  12. core_10x/code_samples/person.py +76 -0
  13. core_10x/concrete_traits.py +356 -0
  14. core_10x/conftest.py +12 -0
  15. core_10x/curve.py +321 -0
  16. core_10x/data_domain.py +48 -0
  17. core_10x/data_domain_binder.py +45 -0
  18. core_10x/directory.py +250 -0
  19. core_10x/entity.py +8 -0
  20. core_10x/entity_filter.py +5 -0
  21. core_10x/environment_variables.py +147 -0
  22. core_10x/exec_control.py +84 -0
  23. core_10x/experimental/__init__.py +0 -0
  24. core_10x/experimental/data_protocol_ex.py +34 -0
  25. core_10x/global_cache.py +121 -0
  26. core_10x/manual_tests/__init__.py +0 -0
  27. core_10x/manual_tests/calendar_test.py +35 -0
  28. core_10x/manual_tests/ctor_update_bug.py +58 -0
  29. core_10x/manual_tests/debug_graph_on.py +17 -0
  30. core_10x/manual_tests/debug_graphoff_inside_graph_on.py +28 -0
  31. core_10x/manual_tests/enum_bits_test.py +17 -0
  32. core_10x/manual_tests/env_vars_trivial_test.py +12 -0
  33. core_10x/manual_tests/existing_traitable.py +33 -0
  34. core_10x/manual_tests/k10x_test1.py +13 -0
  35. core_10x/manual_tests/named_constant_test.py +121 -0
  36. core_10x/manual_tests/nucleus_trivial_test.py +42 -0
  37. core_10x/manual_tests/polars_test.py +14 -0
  38. core_10x/manual_tests/py_class_test.py +4 -0
  39. core_10x/manual_tests/rc_test.py +42 -0
  40. core_10x/manual_tests/rdate_test.py +12 -0
  41. core_10x/manual_tests/reference_serialization_bug.py +19 -0
  42. core_10x/manual_tests/resource_trivial_test.py +10 -0
  43. core_10x/manual_tests/store_uri_test.py +6 -0
  44. core_10x/manual_tests/trait_definition_test.py +19 -0
  45. core_10x/manual_tests/trait_filter_test.py +15 -0
  46. core_10x/manual_tests/trait_flag_modification_test.py +42 -0
  47. core_10x/manual_tests/trait_modification_bug.py +26 -0
  48. core_10x/manual_tests/traitable_as_of_test.py +82 -0
  49. core_10x/manual_tests/traitable_heir_test.py +39 -0
  50. core_10x/manual_tests/traitable_history_test.py +41 -0
  51. core_10x/manual_tests/traitable_serialization_test.py +54 -0
  52. core_10x/manual_tests/traitable_trivial_test.py +71 -0
  53. core_10x/manual_tests/trivial_graph_test.py +16 -0
  54. core_10x/manual_tests/ts_class_association_test.py +64 -0
  55. core_10x/manual_tests/ts_trivial_test.py +35 -0
  56. core_10x/named_constant.py +425 -0
  57. core_10x/nucleus.py +81 -0
  58. core_10x/package_manifest.py +85 -0
  59. core_10x/package_refactoring.py +153 -0
  60. core_10x/py_class.py +431 -0
  61. core_10x/rc.py +155 -0
  62. core_10x/rdate.py +339 -0
  63. core_10x/resource.py +189 -0
  64. core_10x/roman_number.py +67 -0
  65. core_10x/testlib/__init__.py +0 -0
  66. core_10x/testlib/test_store.py +240 -0
  67. core_10x/testlib/traitable_history_tests.py +787 -0
  68. core_10x/testlib/ts_tests.py +280 -0
  69. core_10x/trait.py +377 -0
  70. core_10x/trait_definition.py +176 -0
  71. core_10x/trait_filter.py +205 -0
  72. core_10x/trait_method_error.py +36 -0
  73. core_10x/traitable.py +1082 -0
  74. core_10x/traitable_cli.py +153 -0
  75. core_10x/traitable_heir.py +33 -0
  76. core_10x/traitable_id.py +31 -0
  77. core_10x/ts_store.py +172 -0
  78. core_10x/ts_store_type.py +26 -0
  79. core_10x/ts_union.py +147 -0
  80. core_10x/ui_hint.py +153 -0
  81. core_10x/unit_tests/test_concrete_traits.py +156 -0
  82. core_10x/unit_tests/test_converters.py +51 -0
  83. core_10x/unit_tests/test_curve.py +157 -0
  84. core_10x/unit_tests/test_directory.py +54 -0
  85. core_10x/unit_tests/test_documentation.py +172 -0
  86. core_10x/unit_tests/test_environment_variables.py +15 -0
  87. core_10x/unit_tests/test_filters.py +239 -0
  88. core_10x/unit_tests/test_graph.py +348 -0
  89. core_10x/unit_tests/test_named_constant.py +98 -0
  90. core_10x/unit_tests/test_rc.py +11 -0
  91. core_10x/unit_tests/test_rdate.py +484 -0
  92. core_10x/unit_tests/test_trait_method_error.py +80 -0
  93. core_10x/unit_tests/test_trait_modification.py +19 -0
  94. core_10x/unit_tests/test_traitable.py +959 -0
  95. core_10x/unit_tests/test_traitable_history.py +1 -0
  96. core_10x/unit_tests/test_ts_store.py +1 -0
  97. core_10x/unit_tests/test_ts_union.py +369 -0
  98. core_10x/unit_tests/test_ui_nodes.py +81 -0
  99. core_10x/unit_tests/test_xxcalendar.py +471 -0
  100. core_10x/vault/__init__.py +0 -0
  101. core_10x/vault/sec_keys.py +133 -0
  102. core_10x/vault/security_keys_old.py +168 -0
  103. core_10x/vault/vault.py +56 -0
  104. core_10x/vault/vault_traitable.py +56 -0
  105. core_10x/vault/vault_user.py +70 -0
  106. core_10x/xdate_time.py +136 -0
  107. core_10x/xnone.py +71 -0
  108. core_10x/xxcalendar.py +228 -0
  109. infra_10x/__init__.py +0 -0
  110. infra_10x/manual_tests/__init__.py +0 -0
  111. infra_10x/manual_tests/test_misc.py +16 -0
  112. infra_10x/manual_tests/test_prepare_filter_and_pipeline.py +25 -0
  113. infra_10x/mongodb_admin.py +111 -0
  114. infra_10x/mongodb_store.py +346 -0
  115. infra_10x/mongodb_utils.py +129 -0
  116. infra_10x/unit_tests/conftest.py +13 -0
  117. infra_10x/unit_tests/test_mongo_db.py +36 -0
  118. infra_10x/unit_tests/test_mongo_history.py +1 -0
  119. py10x_universe-0.1.3.dist-info/METADATA +406 -0
  120. py10x_universe-0.1.3.dist-info/RECORD +214 -0
  121. py10x_universe-0.1.3.dist-info/WHEEL +4 -0
  122. py10x_universe-0.1.3.dist-info/licenses/LICENSE +21 -0
  123. ui_10x/__init__.py +0 -0
  124. ui_10x/apps/__init__.py +0 -0
  125. ui_10x/apps/collection_editor_app.py +100 -0
  126. ui_10x/choice.py +212 -0
  127. ui_10x/collection_editor.py +135 -0
  128. ui_10x/concrete_trait_widgets.py +220 -0
  129. ui_10x/conftest.py +8 -0
  130. ui_10x/entity_stocker.py +173 -0
  131. ui_10x/examples/__init__.py +0 -0
  132. ui_10x/examples/_guess_word_data.py +14076 -0
  133. ui_10x/examples/collection_editor.py +17 -0
  134. ui_10x/examples/date_selector.py +14 -0
  135. ui_10x/examples/entity_stocker.py +18 -0
  136. ui_10x/examples/guess_word.py +392 -0
  137. ui_10x/examples/message_box.py +20 -0
  138. ui_10x/examples/multi_choice.py +17 -0
  139. ui_10x/examples/py_data_browser.py +66 -0
  140. ui_10x/examples/radiobox.py +29 -0
  141. ui_10x/examples/single_choice.py +31 -0
  142. ui_10x/examples/style_sheet.py +47 -0
  143. ui_10x/examples/trivial_entity_editor.py +18 -0
  144. ui_10x/platform.py +20 -0
  145. ui_10x/platform_interface.py +517 -0
  146. ui_10x/py_data_browser.py +249 -0
  147. ui_10x/qt6/__init__.py +0 -0
  148. ui_10x/qt6/conftest.py +8 -0
  149. ui_10x/qt6/manual_tests/__init__.py +0 -0
  150. ui_10x/qt6/manual_tests/basic_test.py +35 -0
  151. ui_10x/qt6/platform_implementation.py +275 -0
  152. ui_10x/qt6/utils.py +665 -0
  153. ui_10x/rio/__init__.py +0 -0
  154. ui_10x/rio/apps/examples/examples/__init__.py +22 -0
  155. ui_10x/rio/apps/examples/examples/components/__init__.py +3 -0
  156. ui_10x/rio/apps/examples/examples/components/collection_editor.py +15 -0
  157. ui_10x/rio/apps/examples/examples/pages/collection_editor.py +21 -0
  158. ui_10x/rio/apps/examples/examples/pages/login_page.py +88 -0
  159. ui_10x/rio/apps/examples/examples/pages/style_sheet.py +21 -0
  160. ui_10x/rio/apps/examples/rio.toml +14 -0
  161. ui_10x/rio/component_builder.py +497 -0
  162. ui_10x/rio/components/__init__.py +9 -0
  163. ui_10x/rio/components/group_box.py +31 -0
  164. ui_10x/rio/components/labeled_checkbox.py +18 -0
  165. ui_10x/rio/components/line_edit.py +37 -0
  166. ui_10x/rio/components/radio_button.py +32 -0
  167. ui_10x/rio/components/separator.py +24 -0
  168. ui_10x/rio/components/splitter.py +121 -0
  169. ui_10x/rio/components/tree_view.py +75 -0
  170. ui_10x/rio/conftest.py +35 -0
  171. ui_10x/rio/internals/__init__.py +0 -0
  172. ui_10x/rio/internals/app.py +192 -0
  173. ui_10x/rio/manual_tests/__init__.py +0 -0
  174. ui_10x/rio/manual_tests/basic_test.py +24 -0
  175. ui_10x/rio/manual_tests/splitter.py +27 -0
  176. ui_10x/rio/platform_implementation.py +91 -0
  177. ui_10x/rio/style_sheet.py +53 -0
  178. ui_10x/rio/unit_tests/test_collection_editor.py +68 -0
  179. ui_10x/rio/unit_tests/test_internals.py +630 -0
  180. ui_10x/rio/unit_tests/test_style_sheet.py +37 -0
  181. ui_10x/rio/widgets/__init__.py +46 -0
  182. ui_10x/rio/widgets/application.py +109 -0
  183. ui_10x/rio/widgets/button.py +48 -0
  184. ui_10x/rio/widgets/button_group.py +60 -0
  185. ui_10x/rio/widgets/calendar.py +23 -0
  186. ui_10x/rio/widgets/checkbox.py +24 -0
  187. ui_10x/rio/widgets/dialog.py +137 -0
  188. ui_10x/rio/widgets/group_box.py +27 -0
  189. ui_10x/rio/widgets/layout.py +34 -0
  190. ui_10x/rio/widgets/line_edit.py +37 -0
  191. ui_10x/rio/widgets/list.py +105 -0
  192. ui_10x/rio/widgets/message_box.py +70 -0
  193. ui_10x/rio/widgets/scroll_area.py +31 -0
  194. ui_10x/rio/widgets/spacer.py +6 -0
  195. ui_10x/rio/widgets/splitter.py +45 -0
  196. ui_10x/rio/widgets/text_edit.py +28 -0
  197. ui_10x/rio/widgets/tree.py +89 -0
  198. ui_10x/rio/widgets/unit_tests/test_button.py +101 -0
  199. ui_10x/rio/widgets/unit_tests/test_button_group.py +33 -0
  200. ui_10x/rio/widgets/unit_tests/test_calendar.py +114 -0
  201. ui_10x/rio/widgets/unit_tests/test_checkbox.py +109 -0
  202. ui_10x/rio/widgets/unit_tests/test_group_box.py +158 -0
  203. ui_10x/rio/widgets/unit_tests/test_label.py +43 -0
  204. ui_10x/rio/widgets/unit_tests/test_line_edit.py +140 -0
  205. ui_10x/rio/widgets/unit_tests/test_list.py +146 -0
  206. ui_10x/table_header_view.py +305 -0
  207. ui_10x/table_view.py +174 -0
  208. ui_10x/trait_editor.py +189 -0
  209. ui_10x/trait_widget.py +131 -0
  210. ui_10x/traitable_editor.py +200 -0
  211. ui_10x/traitable_view.py +131 -0
  212. ui_10x/unit_tests/conftest.py +8 -0
  213. ui_10x/unit_tests/test_platform.py +9 -0
  214. ui_10x/utils.py +661 -0
core_10x/ui_hint.py ADDED
@@ -0,0 +1,153 @@
1
+ import copy
2
+ import functools
3
+
4
+ from py10x_core import BTraitFlags as T
5
+
6
+ from core_10x.named_constant import Enum
7
+
8
+
9
+ class UiHint:
10
+ # fmt: off
11
+ #-- Flags
12
+ HIDDEN = 0x1
13
+ READ_ONLY = 0x2
14
+ SELECT_ONLY = 0x4
15
+ SEPARATOR = 0x8
16
+
17
+ #-- Stylesheet Attributes
18
+ FG_COLOR = 'color'
19
+ BG_COLOR = 'background-color'
20
+ FONT = 'font-family'
21
+ FONT_STYLE = 'font-style'
22
+ FONT_WEIGHT = 'font-weight'
23
+ BORDER_WEIGHT = 'border-weight'
24
+ BORDER_WIDTH = 'border-width'
25
+ BORDER_COLOR = 'border-color'
26
+ BORDER_STYLE = 'border-style'
27
+
28
+ class WIDGET_TYPE(Enum):
29
+ NONE = ()
30
+ LINE = ()
31
+ TEXT = ()
32
+ TEXT4LIST = ()
33
+ PASSWORD = ()
34
+ CHECK = ()
35
+ CHOICE = ()
36
+ PIXMAP = ()
37
+ PUSH = ()
38
+ FILE = ()
39
+ # fmt: on
40
+ @staticmethod
41
+ def partial(widget_type_fn, **params):
42
+ return functools.partial(widget_type_fn, **params)
43
+
44
+ def __init__(self, label: str = None, flags: int = 0x0, tip: str = None, widget_type: WIDGET_TYPE = None, **params):
45
+ self.label: str = label
46
+ self.flags: int = flags
47
+ self.tip: str = tip
48
+ self.widget_type = widget_type
49
+ self.params: dict = params
50
+
51
+ def adjust(self, trait):
52
+ if self.label is None:
53
+ self.label = ' '.join(p.capitalize() for p in trait.name.split('_'))
54
+
55
+ if self.tip is None:
56
+ self.tip = self.label
57
+
58
+ if self.widget_type is None:
59
+ self.widget_type = trait.s_ui_hint.widget_type
60
+
61
+ trait_flags = trait.flags
62
+ if trait_flags & T.HIDDEN.value():
63
+ self.flags |= self.HIDDEN
64
+ if trait_flags & T.READONLY.value():
65
+ self.flags |= self.READ_ONLY
66
+
67
+ def flags_on(self, flags: int) -> bool:
68
+ return bool(self.flags & flags)
69
+
70
+ def set_reset_flags(self, to_set: int, to_reset: int = 0x0):
71
+ self.flags = (self.flags | to_set) & ~to_reset
72
+
73
+ def param(self, param_name: str, default_value):
74
+ return self.params.get(param_name, default_value)
75
+
76
+ @staticmethod
77
+ def line(label: str = None, flags: int = 0x0, tip: str = None, min_width: int = 1, align_h: int = 1, **kwargs):
78
+ return Ui(label=label, flags=flags, tip=tip, widget_type=Ui.WIDGET_TYPE.LINE, min_width=min_width, align_h=align_h, **kwargs)
79
+
80
+ @staticmethod
81
+ def text(label: str = None, flags: int = 0x0, tip: str = None, min_width: int = 1, **kwargs):
82
+ return Ui(label=label, flags=flags, tip=tip, widget_type=Ui.WIDGET_TYPE.TEXT, min_width=min_width, **kwargs)
83
+
84
+ @staticmethod
85
+ def text4list(label: str = None, flags: int = 0x0, tip: str = None, min_width: int = 1, **kwargs):
86
+ return Ui(label=label, flags=flags, tip=tip, widget_type=Ui.WIDGET_TYPE.TEXT4LIST, min_width=min_width, **kwargs)
87
+
88
+ @staticmethod
89
+ def password(label: str = None, flags: int = 0x0, tip: str = None, min_width: int = 10, **kwargs):
90
+ return Ui(label=label, flags=flags, tip=tip, widget_type=Ui.WIDGET_TYPE.PASSWORD, min_width=min_width, **kwargs)
91
+
92
+ @staticmethod
93
+ def check(label: str = None, flags: int = 0x0, tip: str = None, right_label: bool = False, align_h: int = 1, **kwargs):
94
+ return Ui(label=label, flags=flags, tip=tip, widget_type=Ui.WIDGET_TYPE.CHECK, right_label=right_label, align_h=align_h, **kwargs)
95
+
96
+ @staticmethod
97
+ def choice(label: str = None, flags: int = 0x0, tip: str = None, align_h: int = 1, **kwargs):
98
+ return Ui(label=label, flags=flags, tip=tip, widget_type=Ui.WIDGET_TYPE.CHOICE, align_h=align_h, **kwargs)
99
+
100
+ @staticmethod
101
+ def pixmap(label: str = None, flags: int = 0x0, tip: str = None, w: int = 10, h: int = 10, **kwargs):
102
+ return Ui(label=label, flags=flags, tip=tip, widget_type=Ui.WIDGET_TYPE.PIXMAP, w=w, h=h, **kwargs)
103
+
104
+ @staticmethod
105
+ def button(label: str = None, flags: int = 0x0, tip: str = None, min_width: int = 1, **kwargs):
106
+ return Ui(label=label, flags=flags, tip=tip, widget_type=Ui.WIDGET_TYPE.PUSH, min_width=min_width, **kwargs)
107
+
108
+ @staticmethod
109
+ def file(label: str = None, flags: int = 0x0, tip: str = None, min_width: int = 1, **kwargs):
110
+ return Ui(label=label, flags=flags, tip=tip, widget_type=Ui.WIDGET_TYPE.FILE, min_width=min_width, **kwargs)
111
+
112
+
113
+ class UiHintModification(UiHint):
114
+ # fmt: off
115
+ def __init__(
116
+ self,
117
+ label: str = None,
118
+ flags: int = None,
119
+ tip: str = None,
120
+ widget_type: UiHint.WIDGET_TYPE = None,
121
+ **params,
122
+ ):
123
+ # fmt: on
124
+ super().__init__(label, flags = flags, tip = tip, widget_type = widget_type, **params)
125
+
126
+ def apply(self, ui_hint: UiHint) -> UiHint:
127
+ hint = copy.deepcopy(ui_hint)
128
+ if self.label is not None:
129
+ hint.label = self.label
130
+
131
+ flags = self.flags
132
+ if flags is not None:
133
+ if isinstance(flags, int): #-- to_set
134
+ hint.flags |= flags
135
+ elif isinstance(flags, tuple): #-- (to_set, to_reset)
136
+ hint.flags = (hint.flags | flags[0]) & ~flags[1]
137
+
138
+ if self.tip is not None:
139
+ hint.tip = self.tip
140
+
141
+ if self.widget_type is not None:
142
+ hint.widget_type = self.widget_type
143
+
144
+ hint.params.update(self.params)
145
+
146
+ return hint
147
+
148
+
149
+ # fmt: off
150
+ UiHint.NONE = UiHint(widget_type = UiHint.WIDGET_TYPE.NONE)
151
+ Ui = UiHint
152
+ UiMod = UiHintModification
153
+ # fmt: on
@@ -0,0 +1,156 @@
1
+ from types import NoneType
2
+
3
+ import pytest
4
+ from core_10x.exec_control import CACHE_ONLY, CONVERT_VALUES_ON, DEBUG_ON
5
+ from core_10x.trait_definition import T
6
+ from core_10x.trait_method_error import TraitMethodError
7
+ from core_10x.traitable import Traitable
8
+ from core_10x.xnone import XNone, XNoneType
9
+
10
+
11
+ def _test_i(data_type: type, values, expected_values):
12
+ assert len(values) == len(expected_values)
13
+
14
+ class TestableTraitable(Traitable):
15
+ xid: int = T(T.ID, default=1)
16
+ value: data_type = T(1) # noinspection PyTypeHints
17
+
18
+ p = TestableTraitable(xid=1)
19
+ for v, ev in zip(values, expected_values, strict=True):
20
+ if isinstance(ev, type) and issubclass(ev, Exception):
21
+ try:
22
+ p.value = v
23
+ except Exception as ex:
24
+ if not isinstance(ex, ev):
25
+ pytest.fail(f'Got {ex} instead of {ev} value {v} for type {data_type}')
26
+ else:
27
+ pytest.fail(f'Expected exception {ev} for value {v} for type {data_type}; converted to {p.value}')
28
+ else:
29
+ p.value = v
30
+ assert p.value == ev
31
+ if ev is None:
32
+ with pytest.raises(ValueError):
33
+ p.serialize_object()
34
+ elif isinstance(ev, data_type):
35
+ assert p.T.value.from_any(p.serialize_object()['value']) == ev
36
+
37
+
38
+ def _test(data_type, values, expected_types):
39
+ with CACHE_ONLY(): # TODO: remove when db access is sorted
40
+ _test_i(data_type, values, expected_types)
41
+
42
+
43
+ def generic_test(data_type, values, convert_expected, allowed_types=()):
44
+ allowed_types = (data_type, NoneType, XNoneType, *allowed_types)
45
+ debug_expected = [
46
+ v if isinstance(v, allowed_types) else e if isinstance(e, Exception) else TypeError for v, e in zip(values, convert_expected, strict=True)
47
+ ]
48
+ _test(data_type, values, values)
49
+ with DEBUG_ON():
50
+ _test(data_type, values, debug_expected)
51
+ with CONVERT_VALUES_ON():
52
+ _test(data_type, values, convert_expected)
53
+ with DEBUG_ON():
54
+ _test(data_type, values, convert_expected)
55
+
56
+
57
+ def test_int_trait():
58
+ data_type = int
59
+ values = [180, 180.1, '180', 'abc', None, XNone, '']
60
+ convert_expected = [180, 180, 180, TypeError, None, XNone, TypeError]
61
+ generic_test(data_type, values, convert_expected)
62
+ _test(data_type, values, values)
63
+
64
+
65
+ def test_float_trait():
66
+ data_type = float
67
+ values = [180, 180.1, '180', 'abc', None, XNone, '']
68
+ convert_expected = [180.0, 180.1, 180.0, TypeError, None, XNone, TypeError]
69
+ generic_test(data_type, values, convert_expected, (int, float))
70
+ _test(data_type, values, values)
71
+
72
+
73
+ def test_bool_trait():
74
+ data_type = bool
75
+ values = [True, False, 1, 0, 'yes', 'no', 'abc', None, XNone, '', 'true', 'false', '1', '0', 'on', 'off']
76
+ convert_expected = [
77
+ True,
78
+ False,
79
+ True,
80
+ False,
81
+ True,
82
+ False,
83
+ TypeError,
84
+ None,
85
+ XNone,
86
+ False,
87
+ True,
88
+ False,
89
+ True,
90
+ False,
91
+ True,
92
+ False,
93
+ ]
94
+ generic_test(data_type, values, convert_expected)
95
+
96
+
97
+ def test_datetime_trait():
98
+ from datetime import datetime
99
+
100
+ data_type = datetime
101
+ values = [
102
+ datetime(2020, 1, 1),
103
+ '2020-01-01T01:02:03',
104
+ '2020-01-01 00:00:00',
105
+ '2020-01-01',
106
+ 1577836800,
107
+ None,
108
+ XNone,
109
+ 'abc',
110
+ '',
111
+ 'Feb 1, 2020 1:2:3',
112
+ ]
113
+ convert_expected = [
114
+ datetime(2020, 1, 1),
115
+ datetime(2020, 1, 1, 1, 2, 3),
116
+ datetime(2020, 1, 1),
117
+ datetime(2020, 1, 1),
118
+ TraitMethodError,
119
+ None,
120
+ XNone,
121
+ TypeError,
122
+ TypeError,
123
+ datetime(2020, 2, 1, 1, 2, 3),
124
+ ]
125
+ generic_test(data_type, values, convert_expected)
126
+
127
+
128
+ def test_date_trait():
129
+ from datetime import date
130
+
131
+ data_type = date
132
+ values = [
133
+ date(2020, 1, 1),
134
+ '2020-01-01T00:00:00',
135
+ '2020-01-01 00:00:00',
136
+ '2020-01-01',
137
+ 1577836800,
138
+ None,
139
+ XNone,
140
+ 'abc',
141
+ '',
142
+ 'Feb 1, 2020',
143
+ ]
144
+ convert_expected = [
145
+ date(2020, 1, 1),
146
+ date(2020, 1, 1),
147
+ date(2020, 1, 1),
148
+ date(2020, 1, 1),
149
+ TraitMethodError,
150
+ None,
151
+ XNone,
152
+ TypeError,
153
+ TypeError,
154
+ date(2020, 2, 1),
155
+ ]
156
+ generic_test(data_type, values, convert_expected)
@@ -0,0 +1,51 @@
1
+ """
2
+ Unit tests for converter methods in py10x traitables.
3
+ Tests verify that converter methods are called when appropriate.
4
+ """
5
+
6
+ import pytest
7
+ from core_10x.exec_control import CONVERT_VALUES_OFF, CONVERT_VALUES_ON, DEBUG_OFF, DEBUG_ON, GRAPH_OFF, GRAPH_ON
8
+ from core_10x.traitable import Traitable
9
+
10
+
11
+ class Person(Traitable):
12
+ """Test traitable with converter methods."""
13
+
14
+ # Runtime traitable: omit T() so no storage context is required
15
+ name: bytes
16
+ status: str
17
+
18
+ def name_from_str(self, trait, value: str) -> bytes:
19
+ """Convert from string - title case the name."""
20
+ return value.title().encode()
21
+
22
+ def name_from_any_xstr(self, trait, value) -> bytes:
23
+ """Convert from non-string - convert to string and title case."""
24
+ return str(value).title().encode()
25
+
26
+ def status_from_any_xstr(self, trait, value) -> bytes:
27
+ """Convert any value to lowercase string."""
28
+ return str(value).lower()
29
+
30
+
31
+ @pytest.mark.parametrize('graph_mode', [GRAPH_OFF, GRAPH_ON])
32
+ @pytest.mark.parametrize('debug_mode', [DEBUG_OFF, DEBUG_ON])
33
+ def test_converter_usage_with_execution_modes(graph_mode, debug_mode):
34
+ """Test that converters work with different execution mode combinations."""
35
+ with graph_mode(), debug_mode():
36
+ with CONVERT_VALUES_ON():
37
+ person = Person(name='john', status=True)
38
+ assert person.name == b'John'
39
+ assert person.status == 'true'
40
+ with CONVERT_VALUES_OFF():
41
+ person = Person()
42
+ if debug_mode is DEBUG_ON:
43
+ with pytest.raises(TypeError):
44
+ person.status = True
45
+ with pytest.raises(TypeError):
46
+ person.name = 'john'
47
+ else:
48
+ person.name = 'john'
49
+ person.status = True
50
+ assert person.status is True
51
+ assert person.name == 'john'
@@ -0,0 +1,157 @@
1
+ from __future__ import annotations
2
+
3
+ import math
4
+ from datetime import date
5
+
6
+ import pytest
7
+ from core_10x.curve import IP_KIND, Curve, CurveParams, DateCurve, TwoFuncInterpolator
8
+
9
+
10
+ class TestCurve:
11
+ def test_update_inserts_sorted_and_overwrites(self):
12
+ c = Curve()
13
+
14
+ # Insert out of order to check internal sorting
15
+ c.update(5, 50.0, reset=False)
16
+ c.update(1, 10.0, reset=False)
17
+ c.update(3, 30.0, reset=False)
18
+
19
+ # Overwrite existing point
20
+ c.update(3, 35.0, reset=False)
21
+
22
+ assert c.times == [1, 3, 5]
23
+ assert c.values == [10.0, 35.0, 50.0]
24
+
25
+ def test_value_linear_interpolation(self):
26
+ c = Curve()
27
+ c.update_many([0.0, 1.0], [0.0, 2.0], reset=True)
28
+
29
+ # Ensure interpolation is allowed for all t by setting a non-None beginning_of_time
30
+ c.beginning_of_time = 0.0
31
+
32
+ # Exact nodes
33
+ assert c.value(0.0) == pytest.approx(0.0)
34
+ assert c.value(1.0) == pytest.approx(2.0)
35
+
36
+ # Linear interpolation between nodes
37
+ assert c.value(0.5) == pytest.approx(1.0)
38
+
39
+ def test_no_interp_mode(self):
40
+ c = Curve()
41
+ c.update_many([0.0, 1.0], [10.0, 20.0], reset=True)
42
+
43
+ params = CurveParams()
44
+ params.ip_kind = IP_KIND.NO_INTERP
45
+ c.params = params
46
+
47
+ # Direct lookup for existing times
48
+ assert c.value(0.0) == pytest.approx(10.0)
49
+ assert c.value(1.0) == pytest.approx(20.0)
50
+
51
+ # Missing time returns NaN
52
+ v = c.value(0.5)
53
+ assert math.isnan(v)
54
+
55
+ def test_remove_and_perturb(self):
56
+ c = Curve()
57
+ c.update_many([0, 1, 2], [10.0, 20.0, 30.0], reset=True)
58
+
59
+ # Remove existing point
60
+ assert c.remove(1) is True
61
+ assert c.times == [0, 2]
62
+ assert c.values == [10.0, 30.0]
63
+
64
+ # Removing non-existing point
65
+ assert c.remove(100) is False
66
+
67
+ # Perturb existing point
68
+ c.beginning_of_time = 0 # allow interpolation/value evaluation
69
+ before = c.value(2)
70
+ c.perturb(2, before + 5.0, perturb_existing_only=True)
71
+ assert c.value(2) == pytest.approx(before + 5.0)
72
+
73
+ # Perturb_shift and perturb_proportional reuse value()
74
+ val_before_shift = c.value(0)
75
+ c.perturb_shift(0, 3.0, perturb_existing_only=True)
76
+ assert c.value(0) == pytest.approx(val_before_shift + 3.0)
77
+
78
+ val_before_prop = c.value(0)
79
+ c.perturb_proportional(0, 2.0, perturb_existing_only=True)
80
+ assert c.value(0) == pytest.approx(val_before_prop * 2.0)
81
+
82
+ # perturb_existing_only should assert for unknown point
83
+ with pytest.raises(AssertionError):
84
+ c.perturb(100, 1.0, perturb_existing_only=True)
85
+
86
+
87
+ class TestTwoFuncInterpolator:
88
+ def test_requires_in_func_or_in_func_on_arrays(self):
89
+ with pytest.raises(AssertionError):
90
+ TwoFuncInterpolator(None, lambda t, v: v)
91
+
92
+ def test_composed_interpolation(self):
93
+ # in_func multiplies values by 2, out_func divides by 2 -- net effect is identity
94
+ def in_func_on_arrays(xs, ys):
95
+ return [2.0 * y for y in ys]
96
+
97
+ def out_func(t, v):
98
+ return v / 2.0
99
+
100
+ tf = TwoFuncInterpolator(
101
+ in_func=None,
102
+ out_func=out_func,
103
+ in_func_on_arrays=in_func_on_arrays,
104
+ )
105
+
106
+ xs = [0.0, 1.0]
107
+ ys = [0.0, 10.0]
108
+
109
+ ip = tf(xs, ys, kind='linear', fill_value='extrapolate', bounds_error=False)
110
+
111
+ # At mid-point we should recover the simple linear interpolation of original ys
112
+ assert ip(0.5) == pytest.approx(5.0)
113
+
114
+
115
+ class TestDateCurve:
116
+ def test_dates_set_and_get(self):
117
+ dc = DateCurve()
118
+
119
+ d1 = date(2023, 1, 1)
120
+ d2 = date(2023, 1, 5)
121
+ dc.dates = [d1, d2]
122
+
123
+ # Internal representation must be integer days from epoch
124
+ assert isinstance(dc.times[0], int)
125
+ assert dc.dates == [d1, d2]
126
+
127
+ def test_update_and_value(self):
128
+ dc = DateCurve()
129
+
130
+ d1 = date(2023, 1, 1)
131
+ d2 = date(2023, 1, 11)
132
+
133
+ dc.update(d1, 0.0, reset=False)
134
+ dc.update(d2, 10.0, reset=True)
135
+
136
+ # Allow interpolation across the whole range
137
+ dc.beginning_of_time = dc._to_number(d1)
138
+
139
+ assert dc.value(d1) == pytest.approx(0.0)
140
+ assert dc.value(d2) == pytest.approx(10.0)
141
+
142
+ mid_date = d1 + (d2 - d1) / 2
143
+ assert dc.value(mid_date) == pytest.approx(5.0)
144
+
145
+ def test_beginning_of_time_helpers(self):
146
+ dc = DateCurve()
147
+
148
+ some_date = date(2020, 6, 15)
149
+ num = dc._to_number(some_date)
150
+ assert isinstance(num, int)
151
+ assert dc._from_number(num) == some_date
152
+
153
+ # beginning_of_time_set stores numeric value but exposes date via helper
154
+ rc = dc.beginning_of_time_set('beginning_of_time', some_date)
155
+ assert rc
156
+ assert isinstance(dc.beginning_of_time, int)
157
+ assert dc.beginning_of_time_as_date() == some_date
@@ -0,0 +1,54 @@
1
+ from core_10x.code_samples.directories import ANIMALS, FISH
2
+
3
+
4
+ def test_flatten():
5
+ f1 = ANIMALS.flatten()
6
+ assert f1 == {
7
+ ('Microorganisms',): 'Microorganisms',
8
+ ('Microorganisms', 'Single Cell'): 'Single Cell',
9
+ ('Microorganisms', 'Multi Cell'): 'Multi Cell',
10
+ ('Mollusks',): 'Mollusks',
11
+ ('Fishes',): 'Fishes',
12
+ ('Fishes', 'Salt Water'): 'Salt Water',
13
+ ('Fishes', 'Salt Water', 'Beluga'): 'Beluga',
14
+ ('Fishes', 'Fresh Water'): 'Fresh Water',
15
+ ('Amphibia',): 'Amphibia',
16
+ ('Reptiles',): 'Reptiles',
17
+ ('Birds',): 'Birds',
18
+ ('Mammals',): 'Mammals',
19
+ ('Mammals', 'Cats'): 'Cats',
20
+ ('Mammals', 'Dogs'): 'Dogs',
21
+ ('Mammals', 'Bears'): 'Bears',
22
+ ('Mammals', 'Whales'): 'Whales',
23
+ ('Mammals', 'Whales', 'Bluewhale'): 'Bluewhale',
24
+ ('Mammals', 'Whales', 'Orca'): 'Orca',
25
+ ('Mammals', 'Whales', 'Spermwhale'): 'Spermwhale',
26
+ ('Mammals', 'Whales', 'Beluga'): 'Beluga',
27
+ }
28
+
29
+ f2 = ANIMALS.flatten(with_root=True)
30
+ assert f2 == {('Animals',): 'Animals', **{('Animals', *k): v for k, v in f1.items()}}
31
+
32
+
33
+ def test_choices():
34
+ c1 = FISH.choices()
35
+ assert c1 == {
36
+ 'Pike Family': 'PkF',
37
+ 'Pike Family/Northern Pike': 'NPK',
38
+ 'Pike Family/Muskie': 'MSK',
39
+ 'Pike Family/Pickerel': 'PKL',
40
+ 'Perch Family': 'PeF',
41
+ 'Perch Family/Common Perch': 'PCH',
42
+ 'Perch Family/Yellow Perch': 'YPH',
43
+ 'Perch Family/Walleye': 'WLY',
44
+ 'Perch Family/Sagger': 'SGR',
45
+ 'Carp Family': 'CrF',
46
+ 'Carp Family/Common Carp': 'CRP',
47
+ 'Carp Family/Wild Carp': 'WCP',
48
+ 'Carp Family/Ide': 'IDE',
49
+ 'Carp Family/Bream': 'BRM',
50
+ 'Carp Family/Roach': 'RCH',
51
+ }
52
+
53
+ c2 = FISH.choices(with_root=True)
54
+ assert c2 == {'Fresh Water Fish': 'FWF', **{f'Fresh Water Fish/{k}': v for k, v in c1.items()}}