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
@@ -0,0 +1,484 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import date
4
+
5
+ import pytest
6
+ from core_10x.rdate import (
7
+ BIZDAY_ROLL_RULE,
8
+ PROPAGATE_DATES,
9
+ TENOR_FREQUENCY,
10
+ RDate,
11
+ )
12
+
13
+
14
+ class TestRDate:
15
+ """Unit tests for RDate class."""
16
+
17
+ def test_init_with_symbol(self):
18
+ """Test RDate constructor with symbol strings."""
19
+ # Test basic symbols
20
+ rd = RDate('3M')
21
+ assert rd.freq == TENOR_FREQUENCY.MONTH
22
+ assert rd.count == 3
23
+
24
+ rd = RDate('1Y')
25
+ assert rd.freq == TENOR_FREQUENCY.YEAR
26
+ assert rd.count == 1
27
+
28
+ rd = RDate('6Q')
29
+ assert rd.freq == TENOR_FREQUENCY.QUARTER
30
+ assert rd.count == 6
31
+
32
+ rd = RDate('2W')
33
+ assert rd.freq == TENOR_FREQUENCY.WEEK
34
+ assert rd.count == 2
35
+
36
+ rd = RDate('5C')
37
+ assert rd.freq == TENOR_FREQUENCY.CALDAY
38
+ assert rd.count == 5
39
+
40
+ # Test negative values
41
+ rd = RDate('-2M')
42
+ assert rd.freq == TENOR_FREQUENCY.MONTH
43
+ assert rd.count == -2
44
+
45
+ def test_init_with_freq_and_count(self):
46
+ """Test RDate constructor with frequency and count parameters."""
47
+ rd = RDate(freq=TENOR_FREQUENCY.MONTH, count=3)
48
+ assert rd.freq == TENOR_FREQUENCY.MONTH
49
+ assert rd.count == 3
50
+
51
+ # Test default count
52
+ rd = RDate(freq=TENOR_FREQUENCY.YEAR)
53
+ assert rd.freq == TENOR_FREQUENCY.YEAR
54
+ assert rd.count == 1
55
+
56
+ def test_init_invalid_symbol(self):
57
+ """Test RDate constructor with invalid symbol raises AttributeError."""
58
+ with pytest.raises(AttributeError, match="Invalid tenor symbol '3X'"):
59
+ RDate('3X')
60
+
61
+ def test_init_missing_freq(self):
62
+ """Test RDate constructor without freq parameter raises AssertionError."""
63
+ with pytest.raises(AssertionError, match='freq must be a valid TENOR_FREQUENCY'):
64
+ RDate(count=3)
65
+
66
+ def test_symbol(self):
67
+ """Test symbol property returns correct string representation."""
68
+ rd = RDate('3M')
69
+ assert rd.symbol() == '3M'
70
+
71
+ rd = RDate('1Y')
72
+ assert rd.symbol() == '1Y'
73
+
74
+ rd = RDate('-2Q')
75
+ assert rd.symbol() == '-2Q'
76
+
77
+ def test_to_str(self):
78
+ """Test to_str method returns symbol."""
79
+ rd = RDate('5C')
80
+ assert rd.to_str() == '5C'
81
+
82
+ def test_serialize_deserialize(self):
83
+ """Test serialization and deserialization."""
84
+ rd = RDate('3M')
85
+ serialized = rd.serialize(embed=True)
86
+ assert serialized == '3M'
87
+
88
+ deserialized = RDate.deserialize(serialized)
89
+ assert deserialized.freq == rd.freq
90
+ assert deserialized.count == rd.count
91
+
92
+ def test_from_str(self):
93
+ """Test from_str class method."""
94
+ rd = RDate.from_str('2Y')
95
+ assert rd.freq == TENOR_FREQUENCY.YEAR
96
+ assert rd.count == 2
97
+
98
+ def test_from_any_xstr(self):
99
+ """Test from_any_xstr class method."""
100
+ # Test with RDate instance
101
+ rd1 = RDate('3M')
102
+ rd2 = RDate.from_any_xstr(rd1)
103
+ assert rd2.freq == rd1.freq
104
+ assert rd2.count == rd1.count
105
+
106
+ # Test with tuple
107
+ rd3 = RDate.from_any_xstr((TENOR_FREQUENCY.YEAR, 5))
108
+ assert rd3.freq == TENOR_FREQUENCY.YEAR
109
+ assert rd3.count == 5
110
+
111
+ # Test invalid type
112
+ with pytest.raises(AssertionError, match='unexpected type'):
113
+ RDate.from_any_xstr('invalid')
114
+
115
+ def test_same_values(self):
116
+ """Test same_values class method."""
117
+ rd1 = RDate('3M')
118
+ rd2 = RDate('3M')
119
+ rd3 = RDate('6M')
120
+
121
+ assert RDate.same_values(rd1, rd2) is True
122
+ assert RDate.same_values(rd1, rd3) is False
123
+
124
+ def test_apply_basic(self):
125
+ """Test apply method with basic calendar operations."""
126
+ rd = RDate('3M')
127
+ start_date = date(2023, 1, 15)
128
+
129
+ # Create a mock calendar that treats all days as business days
130
+ class MockCalendar:
131
+ def is_bizday(self, d):
132
+ return True
133
+
134
+ def next_bizday(self, d):
135
+ return d
136
+
137
+ def prev_bizday(self, d):
138
+ return d
139
+
140
+ def advance_bizdays(self, d, count):
141
+ from datetime import timedelta
142
+
143
+ return d + timedelta(days=count)
144
+
145
+ cal = MockCalendar()
146
+
147
+ # Test with NO_ROLL rule
148
+ result = rd.apply(start_date, cal, BIZDAY_ROLL_RULE.NO_ROLL)
149
+ expected = date(2023, 4, 15) # 3 months later
150
+ assert result == expected
151
+
152
+ def test_apply_bizday(self):
153
+ """Test apply method with business day frequency."""
154
+ rd = RDate(freq=TENOR_FREQUENCY.BIZDAY, count=5)
155
+
156
+ class MockCalendar:
157
+ def is_bizday(self, d):
158
+ return True
159
+
160
+ def next_bizday(self, d):
161
+ return d
162
+
163
+ def prev_bizday(self, d):
164
+ return d
165
+
166
+ def advance_bizdays(self, d, count):
167
+ from datetime import timedelta
168
+
169
+ return d + timedelta(days=count)
170
+
171
+ cal = MockCalendar()
172
+
173
+ start_date = date(2023, 1, 15)
174
+ result = rd.apply(start_date, cal, BIZDAY_ROLL_RULE.NO_ROLL)
175
+ expected = date(2023, 1, 20) # 5 business days later
176
+ assert result == expected
177
+
178
+ def test_apply_rule(self):
179
+ """Test apply_rule class method."""
180
+ start_date = date(2023, 1, 15)
181
+
182
+ class MockCalendar:
183
+ def is_bizday(self, d):
184
+ return True
185
+
186
+ def next_bizday(self, d):
187
+ return d
188
+
189
+ def prev_bizday(self, d):
190
+ return d
191
+
192
+ def advance_bizdays(self, d, count):
193
+ from datetime import timedelta
194
+
195
+ return d + timedelta(days=count)
196
+
197
+ cal = MockCalendar()
198
+
199
+ # Apply multiple tenors: 3M then 1Y
200
+ result = RDate.apply_rule(start_date, cal, BIZDAY_ROLL_RULE.NO_ROLL, '3M,1Y')
201
+ expected = date(2024, 4, 15) # 3M from Jan 15 = Apr 15, then 1Y = Apr 15 2024
202
+ assert result == expected
203
+
204
+ def test_conversion_freq_multiplier(self):
205
+ """Test conversion_freq_multiplier method."""
206
+ # Same frequency
207
+ rd1 = RDate('3M')
208
+ rd2 = RDate('6M')
209
+ assert rd1.conversion_freq_multiplier(rd2.freq) == 1.0
210
+
211
+ # Different frequencies
212
+ rd_year = RDate('1Y')
213
+ rd_month = RDate('12M')
214
+ assert rd_year.conversion_freq_multiplier(rd_month.freq) == 12
215
+
216
+ rd_quarter = RDate('1Q')
217
+ assert rd_year.conversion_freq_multiplier(rd_quarter.freq) == 4
218
+
219
+ # Invalid conversions
220
+ rd_bizday = RDate(freq=TENOR_FREQUENCY.BIZDAY, count=1)
221
+ with pytest.raises(ValueError, match='cannot convert'):
222
+ rd_year.conversion_freq_multiplier(rd_bizday.freq)
223
+
224
+ def test_equate_freq(self):
225
+ """Test equate_freq method."""
226
+ # Same frequency
227
+ rd1 = RDate('3M')
228
+ rd2 = RDate('6M')
229
+ eq1, eq2 = rd1.equate_freq(rd2)
230
+ assert eq1.freq == eq2.freq == TENOR_FREQUENCY.MONTH
231
+ assert eq1.count == 3
232
+ assert eq2.count == 6
233
+
234
+ # Different frequencies - convert to common frequency
235
+ rd_year = RDate('1Y')
236
+ rd_month = RDate('12M')
237
+ eq1, eq2 = rd_year.equate_freq(rd_month)
238
+ assert eq1.freq == eq2.freq == TENOR_FREQUENCY.MONTH
239
+ assert eq1.count == 12 # 1Y = 12M
240
+ assert eq2.count == 12
241
+
242
+ def test_multadd(self):
243
+ """Test multadd method."""
244
+ rd = RDate('6M')
245
+
246
+ # Multiply by scalar
247
+ result = rd.multadd(2.0, 0)
248
+ assert result.freq == TENOR_FREQUENCY.MONTH
249
+ assert result.count == 12
250
+
251
+ # Add another RDate
252
+ rd2 = RDate('3M')
253
+ result = rd.multadd(1.0, rd2)
254
+ assert result.freq == TENOR_FREQUENCY.MONTH
255
+ assert result.count == 9
256
+
257
+ # Invalid addition
258
+ with pytest.raises(ValueError, match='cannot add a non-zero number'):
259
+ rd.multadd(1.0, 5)
260
+
261
+ # Invalid type
262
+ with pytest.raises(ValueError, match='cannot calc a linear combination'):
263
+ rd.multadd(1.0, [])
264
+
265
+ def test_mathematical_operations(self):
266
+ """Test mathematical operations (__mul__, __rmul__, __truediv__, __add__)."""
267
+ rd = RDate('6M')
268
+
269
+ # Multiplication
270
+ result = rd * 2
271
+ assert result.freq == TENOR_FREQUENCY.MONTH
272
+ assert result.count == 12
273
+
274
+ result = 3 * rd
275
+ assert result.freq == TENOR_FREQUENCY.MONTH
276
+ assert result.count == 18
277
+
278
+ # Division by scalar
279
+ result = rd / 2
280
+ assert result.freq == TENOR_FREQUENCY.MONTH
281
+ assert result.count == 3
282
+
283
+ # Division by another RDate
284
+ rd2 = RDate('3M')
285
+ result = rd / rd2
286
+ assert result == 2.0
287
+
288
+ # Addition
289
+ result = rd + rd2
290
+ assert result.freq == TENOR_FREQUENCY.MONTH
291
+ assert result.count == 9
292
+
293
+ def test_from_tenors(self):
294
+ """Test from_tenors class method."""
295
+ tenors = RDate.from_tenors('3M,1Y,6Q')
296
+ assert len(tenors) == 3
297
+ assert tenors[0].symbol() == '3M'
298
+ assert tenors[1].symbol() == '1Y'
299
+ assert tenors[2].symbol() == '6Q'
300
+
301
+ # Test with custom delimiter
302
+ tenors = RDate.from_tenors('3M;1Y;6Q', delim=';')
303
+ assert len(tenors) == 3
304
+
305
+ def test_add_bizdays(self):
306
+ """Test add_bizdays class method."""
307
+ start_date = date(2023, 1, 15)
308
+
309
+ class MockCalendar:
310
+ def is_bizday(self, d):
311
+ return True
312
+
313
+ def next_bizday(self, d):
314
+ return d
315
+
316
+ def prev_bizday(self, d):
317
+ return d
318
+
319
+ def advance_bizdays(self, d, count):
320
+ from datetime import timedelta
321
+
322
+ return d + timedelta(days=count)
323
+
324
+ cal = MockCalendar()
325
+
326
+ result = RDate.add_bizdays(start_date, 5, cal, BIZDAY_ROLL_RULE.NO_ROLL)
327
+ expected = date(2023, 1, 20)
328
+ assert result == expected
329
+
330
+ def test_roll_to_bizday(self):
331
+ """Test roll_to_bizday class method."""
332
+ test_date = date(2023, 1, 15)
333
+
334
+ class MockCalendar:
335
+ def is_bizday(self, d):
336
+ return True
337
+
338
+ def next_bizday(self, d):
339
+ return d
340
+
341
+ def prev_bizday(self, d):
342
+ return d
343
+
344
+ cal = MockCalendar()
345
+
346
+ result = RDate.roll_to_bizday(test_date, cal, BIZDAY_ROLL_RULE.NO_ROLL)
347
+ assert result == test_date # NO_ROLL returns the date as-is
348
+
349
+ def test_relop(self):
350
+ """Test relop class method."""
351
+ rd1 = RDate('3M')
352
+ rd2 = RDate('6M')
353
+ test_date = date(2023, 1, 15)
354
+
355
+ class MockCalendar:
356
+ def is_bizday(self, d):
357
+ return True
358
+
359
+ def next_bizday(self, d):
360
+ return d
361
+
362
+ def prev_bizday(self, d):
363
+ return d
364
+
365
+ cal = MockCalendar()
366
+
367
+ # Test less than
368
+ result = RDate.relop(RDate.RELOP.LT, rd1, rd2, test_date, cal, BIZDAY_ROLL_RULE.NO_ROLL)
369
+ # 3M from Jan 15 = Apr 15, 6M from Jan 15 = Jul 15, so Apr 15 < Jul 15
370
+ assert result is True
371
+
372
+ # Test equal
373
+ rd3 = RDate('3M')
374
+ result = RDate.relop(RDate.RELOP.EQ, rd1, rd3, test_date, cal, BIZDAY_ROLL_RULE.NO_ROLL)
375
+ assert result is True
376
+
377
+ def test_dates_schedule_forward(self):
378
+ """Test dates_schedule method with forward propagation."""
379
+ rd = RDate('3M')
380
+ start_date = date(2023, 1, 15)
381
+ end_date = date(2023, 7, 15)
382
+
383
+ class MockCalendar:
384
+ def is_bizday(self, d):
385
+ return True
386
+
387
+ def next_bizday(self, d):
388
+ return d
389
+
390
+ def prev_bizday(self, d):
391
+ return d
392
+
393
+ cal = MockCalendar()
394
+
395
+ dates = rd.dates_schedule(start_date, end_date, cal, BIZDAY_ROLL_RULE.NO_ROLL, PROPAGATE_DATES.FORWARD, allow_stub=True)
396
+
397
+ # Should generate dates: Jan 15, Apr 15, Jul 15
398
+ expected = [date(2023, 1, 15), date(2023, 4, 15), date(2023, 7, 15)]
399
+ assert dates == expected
400
+
401
+ def test_dates_schedule_backward(self):
402
+ """Test dates_schedule method with backward propagation."""
403
+ rd = RDate('3M')
404
+ start_date = date(2023, 1, 15)
405
+ end_date = date(2023, 7, 15)
406
+
407
+ class MockCalendar:
408
+ def is_bizday(self, d):
409
+ return True
410
+
411
+ def next_bizday(self, d):
412
+ return d
413
+
414
+ def prev_bizday(self, d):
415
+ return d
416
+
417
+ cal = MockCalendar()
418
+
419
+ dates = rd.dates_schedule(start_date, end_date, cal, BIZDAY_ROLL_RULE.NO_ROLL, PROPAGATE_DATES.BACKWARD, allow_stub=True)
420
+
421
+ # Should generate dates: Jul 15, Apr 15, Jan 15 (backward from end)
422
+ expected = [date(2023, 1, 15), date(2023, 4, 15), date(2023, 7, 15)]
423
+ assert dates == expected
424
+
425
+ def test_period_dates(self):
426
+ """Test period_dates method."""
427
+ rd = RDate('3M')
428
+ start_date = date(2023, 1, 15)
429
+ end_date = date(2023, 7, 15)
430
+
431
+ class MockCalendar:
432
+ def is_bizday(self, d):
433
+ return True
434
+
435
+ def next_bizday(self, d):
436
+ return d
437
+
438
+ def prev_bizday(self, d):
439
+ return d
440
+
441
+ cal = MockCalendar()
442
+
443
+ start_dates, end_dates, all_dates = rd.period_dates(
444
+ start_date, end_date, cal, BIZDAY_ROLL_RULE.NO_ROLL, PROPAGATE_DATES.FORWARD, allow_stub=True
445
+ )
446
+
447
+ expected_all = [date(2023, 1, 15), date(2023, 4, 15), date(2023, 7, 15)]
448
+ expected_starts = [date(2023, 1, 15), date(2023, 4, 15)]
449
+ expected_ends = [date(2023, 4, 15), date(2023, 7, 15)]
450
+
451
+ assert all_dates == expected_all
452
+ assert start_dates == expected_starts
453
+ assert end_dates == expected_ends
454
+
455
+ def test_period_dates_for_tenor(self):
456
+ """Test period_dates_for_tenor method."""
457
+ rd = RDate('3M') # frequency
458
+ tenor = RDate('9M') # total tenor
459
+ start_date = date(2023, 1, 15)
460
+
461
+ class MockCalendar:
462
+ def is_bizday(self, d):
463
+ return True
464
+
465
+ def next_bizday(self, d):
466
+ return d
467
+
468
+ def prev_bizday(self, d):
469
+ return d
470
+
471
+ cal = MockCalendar()
472
+
473
+ start_dates, end_dates, all_dates = rd.period_dates_for_tenor(
474
+ start_date, tenor, cal, BIZDAY_ROLL_RULE.NO_ROLL, PROPAGATE_DATES.FORWARD, allow_stub=True
475
+ )
476
+
477
+ # Start at Jan 15, end at Oct 15 (9M later), with 3M periods
478
+ expected_all = [date(2023, 1, 15), date(2023, 4, 15), date(2023, 7, 15), date(2023, 10, 15)]
479
+ expected_starts = [date(2023, 1, 15), date(2023, 4, 15), date(2023, 7, 15)]
480
+ expected_ends = [date(2023, 4, 15), date(2023, 7, 15), date(2023, 10, 15)]
481
+
482
+ assert all_dates == expected_all
483
+ assert start_dates == expected_starts
484
+ assert end_dates == expected_ends
@@ -0,0 +1,80 @@
1
+ import traceback
2
+
3
+ import pytest
4
+
5
+ from core_10x.rc import RC
6
+ from core_10x.trait_definition import T
7
+ from core_10x.trait_method_error import TraitMethodError
8
+ from core_10x.traitable import Traitable, AnonymousTraitable
9
+
10
+
11
+ OUTPUTS = ['exception', 'object', 'args', 'value']
12
+ GROUPS = [
13
+ {'serialize_nx': lambda x: x.serialize_object(False)},
14
+ {'z_deserialize': lambda x: x.__class__.deserialize({'z': 2000}), 'y_get': lambda x: x.y},
15
+ {'x_get': lambda x: x.x(0)},
16
+ {'x_set': lambda x: x.set_value('x', 100, 10)},
17
+ ]
18
+
19
+ bombing_methods = {'_'.join(OUTPUTS[: i + 1]): group for i, group in enumerate(GROUPS)}
20
+
21
+
22
+ @pytest.mark.parametrize(argnames=['cnt', 'key'], argvalues=enumerate(bombing_methods.keys()))
23
+ def test_trait_method_error(cnt, key):
24
+ class X(AnonymousTraitable):
25
+ x: int
26
+ y: int
27
+ z: int = T(1000)
28
+ t: Traitable = T()
29
+
30
+ @classmethod
31
+ def bombing_method(cls, x):
32
+ raise KeyError(x + 1)
33
+
34
+ def x_get(self, x):
35
+ return self.bombing_method(x)
36
+
37
+ def y_get(self):
38
+ return self.bombing_method(0)
39
+
40
+ def x_set(self, trait, value, x) -> RC:
41
+ return self.bombing_method(value)
42
+
43
+ @classmethod
44
+ def y_serialize(cls, value):
45
+ return cls.bombing_method(value)
46
+
47
+ @classmethod
48
+ def y_deserialize(cls, trait, value):
49
+ return cls.bombing_method(value)
50
+
51
+ x = X()
52
+ x.t = X()
53
+ for m, f in bombing_methods[key].items():
54
+ try:
55
+ f(x)
56
+ except TraitMethodError as e:
57
+ e_str = str(e)
58
+ if cnt:
59
+ assert 'KeyError' in e_str
60
+ else:
61
+ assert 'TypeError' in e_str
62
+ assert f'value = {x.t}' in e_str
63
+ assert (
64
+ "original exception = TypeError: test_trait_method_error.<locals>.X - anonymous' instance may not be serialized as external reference"
65
+ in e_str
66
+ )
67
+
68
+ for i, output in enumerate(key.split('_')):
69
+ expected = i < cnt + 1
70
+ if cnt or (not expected and output != 'value'):
71
+ assert (f' {output} =' in e_str) is expected, f'did{" not" if expected else ""} find {output} for {key} in {e_str}'
72
+ if cnt:
73
+ assert f"Failed in <class 'test_trait_method_error.test_trait_method_error.<locals>.X'>.{m.split('_')[0]}.{m}" in e_str
74
+
75
+ tb_str = traceback.format_exc()
76
+ if cnt:
77
+ assert 'f(x)' in tb_str
78
+ assert f"'{m}': lambda x: x." in tb_str
79
+ assert 'bombing_method(' in tb_str
80
+ assert 'raise KeyError(x + 1)' in tb_str
@@ -0,0 +1,19 @@
1
+ from core_10x.traitable import RT, M, Trait, Traitable
2
+
3
+
4
+ class A(Traitable):
5
+ name: str = RT()
6
+
7
+
8
+ class B(A):
9
+ name: int = M()
10
+
11
+
12
+ def name_ui_flags(cls):
13
+ t: Trait = cls.trait('name')
14
+ return t.ui_hint.flags
15
+
16
+
17
+ def test_uihint_modification():
18
+ flags = (name_ui_flags(A), name_ui_flags(B))
19
+ assert flags[0] == flags[1]