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.
- core_10x/__init__.py +42 -0
- core_10x/backbone/__init__.py +0 -0
- core_10x/backbone/backbone_store.py +59 -0
- core_10x/backbone/backbone_traitable.py +30 -0
- core_10x/backbone/backbone_user.py +66 -0
- core_10x/backbone/bound_data_domain.py +49 -0
- core_10x/backbone/namespace.py +101 -0
- core_10x/backbone/vault.py +38 -0
- core_10x/code_samples/__init__.py +0 -0
- core_10x/code_samples/_package_manifest.py +3 -0
- core_10x/code_samples/directories.py +181 -0
- core_10x/code_samples/person.py +76 -0
- core_10x/concrete_traits.py +356 -0
- core_10x/conftest.py +12 -0
- core_10x/curve.py +321 -0
- core_10x/data_domain.py +48 -0
- core_10x/data_domain_binder.py +45 -0
- core_10x/directory.py +250 -0
- core_10x/entity.py +8 -0
- core_10x/entity_filter.py +5 -0
- core_10x/environment_variables.py +147 -0
- core_10x/exec_control.py +84 -0
- core_10x/experimental/__init__.py +0 -0
- core_10x/experimental/data_protocol_ex.py +34 -0
- core_10x/global_cache.py +121 -0
- core_10x/manual_tests/__init__.py +0 -0
- core_10x/manual_tests/calendar_test.py +35 -0
- core_10x/manual_tests/ctor_update_bug.py +58 -0
- core_10x/manual_tests/debug_graph_on.py +17 -0
- core_10x/manual_tests/debug_graphoff_inside_graph_on.py +28 -0
- core_10x/manual_tests/enum_bits_test.py +17 -0
- core_10x/manual_tests/env_vars_trivial_test.py +12 -0
- core_10x/manual_tests/existing_traitable.py +33 -0
- core_10x/manual_tests/k10x_test1.py +13 -0
- core_10x/manual_tests/named_constant_test.py +121 -0
- core_10x/manual_tests/nucleus_trivial_test.py +42 -0
- core_10x/manual_tests/polars_test.py +14 -0
- core_10x/manual_tests/py_class_test.py +4 -0
- core_10x/manual_tests/rc_test.py +42 -0
- core_10x/manual_tests/rdate_test.py +12 -0
- core_10x/manual_tests/reference_serialization_bug.py +19 -0
- core_10x/manual_tests/resource_trivial_test.py +10 -0
- core_10x/manual_tests/store_uri_test.py +6 -0
- core_10x/manual_tests/trait_definition_test.py +19 -0
- core_10x/manual_tests/trait_filter_test.py +15 -0
- core_10x/manual_tests/trait_flag_modification_test.py +42 -0
- core_10x/manual_tests/trait_modification_bug.py +26 -0
- core_10x/manual_tests/traitable_as_of_test.py +82 -0
- core_10x/manual_tests/traitable_heir_test.py +39 -0
- core_10x/manual_tests/traitable_history_test.py +41 -0
- core_10x/manual_tests/traitable_serialization_test.py +54 -0
- core_10x/manual_tests/traitable_trivial_test.py +71 -0
- core_10x/manual_tests/trivial_graph_test.py +16 -0
- core_10x/manual_tests/ts_class_association_test.py +64 -0
- core_10x/manual_tests/ts_trivial_test.py +35 -0
- core_10x/named_constant.py +425 -0
- core_10x/nucleus.py +81 -0
- core_10x/package_manifest.py +85 -0
- core_10x/package_refactoring.py +153 -0
- core_10x/py_class.py +431 -0
- core_10x/rc.py +155 -0
- core_10x/rdate.py +339 -0
- core_10x/resource.py +189 -0
- core_10x/roman_number.py +67 -0
- core_10x/testlib/__init__.py +0 -0
- core_10x/testlib/test_store.py +240 -0
- core_10x/testlib/traitable_history_tests.py +787 -0
- core_10x/testlib/ts_tests.py +280 -0
- core_10x/trait.py +377 -0
- core_10x/trait_definition.py +176 -0
- core_10x/trait_filter.py +205 -0
- core_10x/trait_method_error.py +36 -0
- core_10x/traitable.py +1082 -0
- core_10x/traitable_cli.py +153 -0
- core_10x/traitable_heir.py +33 -0
- core_10x/traitable_id.py +31 -0
- core_10x/ts_store.py +172 -0
- core_10x/ts_store_type.py +26 -0
- core_10x/ts_union.py +147 -0
- core_10x/ui_hint.py +153 -0
- core_10x/unit_tests/test_concrete_traits.py +156 -0
- core_10x/unit_tests/test_converters.py +51 -0
- core_10x/unit_tests/test_curve.py +157 -0
- core_10x/unit_tests/test_directory.py +54 -0
- core_10x/unit_tests/test_documentation.py +172 -0
- core_10x/unit_tests/test_environment_variables.py +15 -0
- core_10x/unit_tests/test_filters.py +239 -0
- core_10x/unit_tests/test_graph.py +348 -0
- core_10x/unit_tests/test_named_constant.py +98 -0
- core_10x/unit_tests/test_rc.py +11 -0
- core_10x/unit_tests/test_rdate.py +484 -0
- core_10x/unit_tests/test_trait_method_error.py +80 -0
- core_10x/unit_tests/test_trait_modification.py +19 -0
- core_10x/unit_tests/test_traitable.py +959 -0
- core_10x/unit_tests/test_traitable_history.py +1 -0
- core_10x/unit_tests/test_ts_store.py +1 -0
- core_10x/unit_tests/test_ts_union.py +369 -0
- core_10x/unit_tests/test_ui_nodes.py +81 -0
- core_10x/unit_tests/test_xxcalendar.py +471 -0
- core_10x/vault/__init__.py +0 -0
- core_10x/vault/sec_keys.py +133 -0
- core_10x/vault/security_keys_old.py +168 -0
- core_10x/vault/vault.py +56 -0
- core_10x/vault/vault_traitable.py +56 -0
- core_10x/vault/vault_user.py +70 -0
- core_10x/xdate_time.py +136 -0
- core_10x/xnone.py +71 -0
- core_10x/xxcalendar.py +228 -0
- infra_10x/__init__.py +0 -0
- infra_10x/manual_tests/__init__.py +0 -0
- infra_10x/manual_tests/test_misc.py +16 -0
- infra_10x/manual_tests/test_prepare_filter_and_pipeline.py +25 -0
- infra_10x/mongodb_admin.py +111 -0
- infra_10x/mongodb_store.py +346 -0
- infra_10x/mongodb_utils.py +129 -0
- infra_10x/unit_tests/conftest.py +13 -0
- infra_10x/unit_tests/test_mongo_db.py +36 -0
- infra_10x/unit_tests/test_mongo_history.py +1 -0
- py10x_universe-0.1.3.dist-info/METADATA +406 -0
- py10x_universe-0.1.3.dist-info/RECORD +214 -0
- py10x_universe-0.1.3.dist-info/WHEEL +4 -0
- py10x_universe-0.1.3.dist-info/licenses/LICENSE +21 -0
- ui_10x/__init__.py +0 -0
- ui_10x/apps/__init__.py +0 -0
- ui_10x/apps/collection_editor_app.py +100 -0
- ui_10x/choice.py +212 -0
- ui_10x/collection_editor.py +135 -0
- ui_10x/concrete_trait_widgets.py +220 -0
- ui_10x/conftest.py +8 -0
- ui_10x/entity_stocker.py +173 -0
- ui_10x/examples/__init__.py +0 -0
- ui_10x/examples/_guess_word_data.py +14076 -0
- ui_10x/examples/collection_editor.py +17 -0
- ui_10x/examples/date_selector.py +14 -0
- ui_10x/examples/entity_stocker.py +18 -0
- ui_10x/examples/guess_word.py +392 -0
- ui_10x/examples/message_box.py +20 -0
- ui_10x/examples/multi_choice.py +17 -0
- ui_10x/examples/py_data_browser.py +66 -0
- ui_10x/examples/radiobox.py +29 -0
- ui_10x/examples/single_choice.py +31 -0
- ui_10x/examples/style_sheet.py +47 -0
- ui_10x/examples/trivial_entity_editor.py +18 -0
- ui_10x/platform.py +20 -0
- ui_10x/platform_interface.py +517 -0
- ui_10x/py_data_browser.py +249 -0
- ui_10x/qt6/__init__.py +0 -0
- ui_10x/qt6/conftest.py +8 -0
- ui_10x/qt6/manual_tests/__init__.py +0 -0
- ui_10x/qt6/manual_tests/basic_test.py +35 -0
- ui_10x/qt6/platform_implementation.py +275 -0
- ui_10x/qt6/utils.py +665 -0
- ui_10x/rio/__init__.py +0 -0
- ui_10x/rio/apps/examples/examples/__init__.py +22 -0
- ui_10x/rio/apps/examples/examples/components/__init__.py +3 -0
- ui_10x/rio/apps/examples/examples/components/collection_editor.py +15 -0
- ui_10x/rio/apps/examples/examples/pages/collection_editor.py +21 -0
- ui_10x/rio/apps/examples/examples/pages/login_page.py +88 -0
- ui_10x/rio/apps/examples/examples/pages/style_sheet.py +21 -0
- ui_10x/rio/apps/examples/rio.toml +14 -0
- ui_10x/rio/component_builder.py +497 -0
- ui_10x/rio/components/__init__.py +9 -0
- ui_10x/rio/components/group_box.py +31 -0
- ui_10x/rio/components/labeled_checkbox.py +18 -0
- ui_10x/rio/components/line_edit.py +37 -0
- ui_10x/rio/components/radio_button.py +32 -0
- ui_10x/rio/components/separator.py +24 -0
- ui_10x/rio/components/splitter.py +121 -0
- ui_10x/rio/components/tree_view.py +75 -0
- ui_10x/rio/conftest.py +35 -0
- ui_10x/rio/internals/__init__.py +0 -0
- ui_10x/rio/internals/app.py +192 -0
- ui_10x/rio/manual_tests/__init__.py +0 -0
- ui_10x/rio/manual_tests/basic_test.py +24 -0
- ui_10x/rio/manual_tests/splitter.py +27 -0
- ui_10x/rio/platform_implementation.py +91 -0
- ui_10x/rio/style_sheet.py +53 -0
- ui_10x/rio/unit_tests/test_collection_editor.py +68 -0
- ui_10x/rio/unit_tests/test_internals.py +630 -0
- ui_10x/rio/unit_tests/test_style_sheet.py +37 -0
- ui_10x/rio/widgets/__init__.py +46 -0
- ui_10x/rio/widgets/application.py +109 -0
- ui_10x/rio/widgets/button.py +48 -0
- ui_10x/rio/widgets/button_group.py +60 -0
- ui_10x/rio/widgets/calendar.py +23 -0
- ui_10x/rio/widgets/checkbox.py +24 -0
- ui_10x/rio/widgets/dialog.py +137 -0
- ui_10x/rio/widgets/group_box.py +27 -0
- ui_10x/rio/widgets/layout.py +34 -0
- ui_10x/rio/widgets/line_edit.py +37 -0
- ui_10x/rio/widgets/list.py +105 -0
- ui_10x/rio/widgets/message_box.py +70 -0
- ui_10x/rio/widgets/scroll_area.py +31 -0
- ui_10x/rio/widgets/spacer.py +6 -0
- ui_10x/rio/widgets/splitter.py +45 -0
- ui_10x/rio/widgets/text_edit.py +28 -0
- ui_10x/rio/widgets/tree.py +89 -0
- ui_10x/rio/widgets/unit_tests/test_button.py +101 -0
- ui_10x/rio/widgets/unit_tests/test_button_group.py +33 -0
- ui_10x/rio/widgets/unit_tests/test_calendar.py +114 -0
- ui_10x/rio/widgets/unit_tests/test_checkbox.py +109 -0
- ui_10x/rio/widgets/unit_tests/test_group_box.py +158 -0
- ui_10x/rio/widgets/unit_tests/test_label.py +43 -0
- ui_10x/rio/widgets/unit_tests/test_line_edit.py +140 -0
- ui_10x/rio/widgets/unit_tests/test_list.py +146 -0
- ui_10x/table_header_view.py +305 -0
- ui_10x/table_view.py +174 -0
- ui_10x/trait_editor.py +189 -0
- ui_10x/trait_widget.py +131 -0
- ui_10x/traitable_editor.py +200 -0
- ui_10x/traitable_view.py +131 -0
- ui_10x/unit_tests/conftest.py +8 -0
- ui_10x/unit_tests/test_platform.py +9 -0
- 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()}}
|