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/data_domain.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from core_10x.resource import TS_STORE, ResourceRequirements
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _raise_type_error(cls, **kwargs):
|
|
7
|
+
raise TypeError(f'{cls} - subclassing is not allowed')
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DataDomain:
|
|
11
|
+
s_dir = {}
|
|
12
|
+
|
|
13
|
+
s_resource_requirements = {}
|
|
14
|
+
|
|
15
|
+
def __init_subclass__(cls, **kwargs):
|
|
16
|
+
name = cls.name()
|
|
17
|
+
ed = DataDomain.s_dir.get(name)
|
|
18
|
+
assert ed is None, f"DataDomain '{name}' is already registered in {ed}"
|
|
19
|
+
DataDomain.s_dir[name] = cls
|
|
20
|
+
|
|
21
|
+
cls.__init_subclass__ = lambda **kwargs: _raise_type_error(cls, **kwargs)
|
|
22
|
+
category: str
|
|
23
|
+
rrs = cls.s_resource_requirements
|
|
24
|
+
for category, resource_reqs in cls.__dict__.items():
|
|
25
|
+
if isinstance(resource_reqs, ResourceRequirements):
|
|
26
|
+
assert category.isupper(), f"Category '{category}' must be an uppercase name"
|
|
27
|
+
resource_reqs.domain = cls
|
|
28
|
+
resource_reqs.category = category
|
|
29
|
+
rrs[category] = resource_reqs
|
|
30
|
+
|
|
31
|
+
assert rrs, f"DataDomain '{cls} must define at least one category"
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def name(cls: str) -> str:
|
|
35
|
+
return re.sub(r'([a-z])([A-Z])', r'\1_\2', cls.__qualname__).upper()
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def resource_requirement(cls, category: str, throw: bool = True) -> ResourceRequirements:
|
|
39
|
+
rr = cls.s_resource_requirements.get(category)
|
|
40
|
+
if rr is None and throw:
|
|
41
|
+
raise ValueError(f"DataDomain {cls} - unknown category '{category}'")
|
|
42
|
+
|
|
43
|
+
return rr
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class GeneralDomain(DataDomain):
|
|
47
|
+
GENERAL = TS_STORE()
|
|
48
|
+
...
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from core_10x.environment_variables import EnvVars
|
|
2
|
+
from core_10x.global_cache import cache
|
|
3
|
+
from core_10x.py_class import PyClass
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# ===================================================================================================================================
|
|
7
|
+
# Data Domain Bindings
|
|
8
|
+
#
|
|
9
|
+
# from core_10x.data_domain import GeneralDomain
|
|
10
|
+
# from xxx.yyy.mdu_domain import MDU
|
|
11
|
+
# ...
|
|
12
|
+
# DATA_DOMAIN_BINDINGS = dict(
|
|
13
|
+
# dev = {
|
|
14
|
+
# GeneralDomain: dict(
|
|
15
|
+
# GENERAL = R(TS_STORE.MONGO_DB, hostname = 'dev.mongo.general.io', tsl = True, ...),
|
|
16
|
+
# ),
|
|
17
|
+
#
|
|
18
|
+
# MDU: dict(
|
|
19
|
+
# SYMBOLOGY = R(REL_DB.ORACLE_DB, hostname = 'dev.oracle.io', a = '...', b = '...'),
|
|
20
|
+
# MKT_CLOSE_CLUSTER = R(CLOUD_CLUSTER.RAY_CLUSTER, hostname = 'dev.ray1.io', ...),
|
|
21
|
+
# ),
|
|
22
|
+
# },
|
|
23
|
+
#
|
|
24
|
+
# prod = {
|
|
25
|
+
# GeneralDomain: dict(
|
|
26
|
+
# GENERAL = R(TS_STORE.MONGO_DB, hostname = 'prod.mongo.general.io', tsl = True, ...),
|
|
27
|
+
# ),
|
|
28
|
+
#
|
|
29
|
+
# MDU: dict(
|
|
30
|
+
# SYMBOLOGY = R(REL_DB.ORACLE_DB, hostname = 'prod.oracle.io', a = '...', b = '...'),
|
|
31
|
+
# MKT_CLOSE_CLUSTER = R(CLOUD_CLUSTER.RAY_CLUSTER, hostname = 'prod.ray2.io', ...),
|
|
32
|
+
# ),
|
|
33
|
+
# },
|
|
34
|
+
#
|
|
35
|
+
# )
|
|
36
|
+
# ===================================================================================================================================
|
|
37
|
+
class DataDomainBinder:
|
|
38
|
+
@staticmethod
|
|
39
|
+
@cache
|
|
40
|
+
def all_bindings() -> dict:
|
|
41
|
+
dd_bindings_symbol = EnvVars.data_domain_bindings
|
|
42
|
+
if not dd_bindings_symbol:
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
return PyClass.find_symbol(dd_bindings_symbol)
|
core_10x/directory.py
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from core_10x.entity import Entity
|
|
7
|
+
from core_10x.py_class import PyClass
|
|
8
|
+
from core_10x.xnone import XNone
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Directory:
|
|
12
|
+
@staticmethod
|
|
13
|
+
def _dir_class(value):
|
|
14
|
+
if isinstance(value, Entity):
|
|
15
|
+
return DxEntity
|
|
16
|
+
|
|
17
|
+
if inspect.isclass(value) and issubclass(value, Entity):
|
|
18
|
+
return DxClass
|
|
19
|
+
|
|
20
|
+
return Directory
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def instance(value: Any = None, **kwargs) -> Directory:
|
|
24
|
+
dir_class = Directory._dir_class(value)
|
|
25
|
+
return dir_class(value=value, **kwargs)
|
|
26
|
+
|
|
27
|
+
# fmt: off
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
name: str = '',
|
|
31
|
+
value = None,
|
|
32
|
+
members: dict = None,
|
|
33
|
+
parent: Directory = None
|
|
34
|
+
):
|
|
35
|
+
# fmt: on
|
|
36
|
+
self.check_dir_value(value)
|
|
37
|
+
|
|
38
|
+
self.name = name
|
|
39
|
+
self.value = value
|
|
40
|
+
self.members = members or {}
|
|
41
|
+
self.parent = parent
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def check_dir_value(cls, value):
|
|
45
|
+
f = getattr(value, '__hash__', None)
|
|
46
|
+
assert f, f'Directory value must be hashable ({value})'
|
|
47
|
+
|
|
48
|
+
def show_value(self) -> str:
|
|
49
|
+
return str(self.value) if self.value else self.name
|
|
50
|
+
|
|
51
|
+
def is_leaf(self) -> bool:
|
|
52
|
+
return not self.members
|
|
53
|
+
|
|
54
|
+
def subdirs(self) -> dict:
|
|
55
|
+
return self.members
|
|
56
|
+
|
|
57
|
+
def _add_subdir(self, subdir: Directory):
|
|
58
|
+
self.members[subdir.value] = subdir
|
|
59
|
+
subdir.parent = self
|
|
60
|
+
|
|
61
|
+
def _get_or_add_subdir(self, value, **subdir_values):
|
|
62
|
+
subdir = self.members.get(value)
|
|
63
|
+
if subdir is None:
|
|
64
|
+
subdir = Directory.instance(value, **subdir_values)
|
|
65
|
+
subdir.parent = self
|
|
66
|
+
self.members[value] = subdir
|
|
67
|
+
|
|
68
|
+
return subdir
|
|
69
|
+
|
|
70
|
+
def insert(self, value, *path, **subdir_values):
|
|
71
|
+
if not path:
|
|
72
|
+
self._get_or_add_subdir(value, **subdir_values)
|
|
73
|
+
else:
|
|
74
|
+
p_value = path[0]
|
|
75
|
+
subdir = self._get_or_add_subdir(p_value)
|
|
76
|
+
subdir.insert(value, *path[1:], **subdir_values)
|
|
77
|
+
|
|
78
|
+
def insert_many(self, values: list, **subdir_values):
|
|
79
|
+
for value in values:
|
|
80
|
+
self._get_or_add_subdir(value, **subdir_values)
|
|
81
|
+
|
|
82
|
+
def subdir_at(self, *path) -> Directory:
|
|
83
|
+
if not path:
|
|
84
|
+
return self
|
|
85
|
+
|
|
86
|
+
subdir = self.members.get(path[0])
|
|
87
|
+
return subdir.subdir_at(*path[1:]) if subdir else None
|
|
88
|
+
|
|
89
|
+
def remove(self, value, *path) -> bool:
|
|
90
|
+
subdir = self.subdir_at(*path)
|
|
91
|
+
if not subdir:
|
|
92
|
+
return False
|
|
93
|
+
|
|
94
|
+
return self.members.pop(value, XNone) is not XNone
|
|
95
|
+
|
|
96
|
+
def remove_everywhere(self, value):
|
|
97
|
+
self.remove(value)
|
|
98
|
+
for subdir in self.members.values():
|
|
99
|
+
subdir.remove(value)
|
|
100
|
+
|
|
101
|
+
def _find_paths(self, value, paths_found: list, *current_path):
|
|
102
|
+
found = self.members.get(value)
|
|
103
|
+
if found:
|
|
104
|
+
paths_found.append((*current_path, value))
|
|
105
|
+
|
|
106
|
+
for sub_value, subdir in self.members.items():
|
|
107
|
+
subdir._find_paths(value, paths_found, *current_path, sub_value)
|
|
108
|
+
|
|
109
|
+
def find_paths(self, value) -> list:
|
|
110
|
+
paths_found = [()] if value == self.value else []
|
|
111
|
+
self._find_paths(value, paths_found)
|
|
112
|
+
return paths_found
|
|
113
|
+
|
|
114
|
+
def _flatten(self, res: dict, *current_path):
|
|
115
|
+
path = *current_path, self.value
|
|
116
|
+
label = self.name
|
|
117
|
+
if not label:
|
|
118
|
+
label = self.show_value()
|
|
119
|
+
res[path] = label
|
|
120
|
+
for subdir in self.members.values():
|
|
121
|
+
subdir._flatten(res, *path)
|
|
122
|
+
|
|
123
|
+
def flatten(self, with_root: bool = False) -> dict:
|
|
124
|
+
res = {}
|
|
125
|
+
if with_root:
|
|
126
|
+
self._flatten(res)
|
|
127
|
+
else:
|
|
128
|
+
for subdir in self.members.values():
|
|
129
|
+
subdir._flatten(res)
|
|
130
|
+
|
|
131
|
+
return res
|
|
132
|
+
|
|
133
|
+
def choices(self, with_root: bool = False, path_delimiter: str = '/') -> dict:
|
|
134
|
+
f = self.flatten(with_root = with_root)
|
|
135
|
+
v_n_map = { path[-1]: name for path, name in f.items() }
|
|
136
|
+
res = {}
|
|
137
|
+
cr = 1
|
|
138
|
+
for path in f.keys():
|
|
139
|
+
label = path_delimiter.join(v_n_map[p] for p in path)
|
|
140
|
+
value = path[-1]
|
|
141
|
+
already_in = res.get(label)
|
|
142
|
+
if already_in:
|
|
143
|
+
if already_in == value:
|
|
144
|
+
continue
|
|
145
|
+
|
|
146
|
+
label = f'{label}({cr})'
|
|
147
|
+
cr += 1
|
|
148
|
+
|
|
149
|
+
res[label] = value
|
|
150
|
+
|
|
151
|
+
return res
|
|
152
|
+
|
|
153
|
+
def contains(self, value) -> bool:
|
|
154
|
+
if self.value == value:
|
|
155
|
+
return True
|
|
156
|
+
|
|
157
|
+
for subdir in self.members.values():
|
|
158
|
+
if subdir.contains(value):
|
|
159
|
+
return True
|
|
160
|
+
|
|
161
|
+
return False
|
|
162
|
+
|
|
163
|
+
def is_value_contained(self, value, by_other_value) -> bool:
|
|
164
|
+
if self.value == by_other_value:
|
|
165
|
+
return self.contains(value)
|
|
166
|
+
|
|
167
|
+
for subdir in self.members.values():
|
|
168
|
+
if subdir.is_value_contained(value, by_other_value):
|
|
169
|
+
return True
|
|
170
|
+
|
|
171
|
+
return False
|
|
172
|
+
|
|
173
|
+
def _collect_leaf_values(self, res: set, data_accessor):
|
|
174
|
+
if self.is_leaf():
|
|
175
|
+
res.add(data_accessor(self.value))
|
|
176
|
+
else:
|
|
177
|
+
for subdir in self.members.values():
|
|
178
|
+
subdir._collect_leaf_values(res, data_accessor)
|
|
179
|
+
|
|
180
|
+
def leaf_values(self, data_accessor): #-- data_accessor: callable(leaf_value)
|
|
181
|
+
res = set()
|
|
182
|
+
self._collect_leaf_values(res, data_accessor)
|
|
183
|
+
return list(res)
|
|
184
|
+
|
|
185
|
+
@classmethod
|
|
186
|
+
def define(cls, value_name, members: list) -> Directory:
|
|
187
|
+
if isinstance(value_name, tuple): #-- ( value, name ) is given
|
|
188
|
+
assert len(value_name) == 2, 'a pair of ( value, name ) is expected'
|
|
189
|
+
value, name = value_name
|
|
190
|
+
|
|
191
|
+
else: #-- just a value
|
|
192
|
+
value = value_name
|
|
193
|
+
name = None
|
|
194
|
+
|
|
195
|
+
dir = Directory.instance(value, name = name)
|
|
196
|
+
|
|
197
|
+
sub_value_name = None
|
|
198
|
+
for elem in members:
|
|
199
|
+
if not sub_value_name:
|
|
200
|
+
sub_value_name = elem
|
|
201
|
+
continue
|
|
202
|
+
|
|
203
|
+
if isinstance(elem, list): #-- sub dirs are given
|
|
204
|
+
sudir = cls.define(sub_value_name, elem)
|
|
205
|
+
dir._add_subdir(sudir)
|
|
206
|
+
sub_value_name = None
|
|
207
|
+
|
|
208
|
+
else: #-- no sub dirs are given, just value_name
|
|
209
|
+
sudir = cls.define(sub_value_name, [])
|
|
210
|
+
dir._add_subdir(sudir)
|
|
211
|
+
sub_value_name = elem
|
|
212
|
+
|
|
213
|
+
if sub_value_name:
|
|
214
|
+
sudir = cls.define(sub_value_name, [])
|
|
215
|
+
dir._add_subdir(sudir)
|
|
216
|
+
|
|
217
|
+
return dir
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class DxEntity(Directory):
|
|
221
|
+
def __init__(self, **kwargs):
|
|
222
|
+
super().__init__(**kwargs)
|
|
223
|
+
self.value: Entity
|
|
224
|
+
self.name = self.value.id()
|
|
225
|
+
|
|
226
|
+
@classmethod
|
|
227
|
+
def check_dir_value(cls, value):
|
|
228
|
+
assert isinstance(value, Entity), f'entity is expected ({value})'
|
|
229
|
+
|
|
230
|
+
def show_value(self) -> str:
|
|
231
|
+
return self.value.id() # -- TODO: id_repr()
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class DxClass(Directory):
|
|
235
|
+
def __init__(self, **kwargs):
|
|
236
|
+
super().__init__(**kwargs)
|
|
237
|
+
cls = self.value
|
|
238
|
+
self.name = PyClass.name(cls) if cls else ''
|
|
239
|
+
|
|
240
|
+
@classmethod
|
|
241
|
+
def check_dir_value(cls, value):
|
|
242
|
+
assert inspect.isclass(value) and issubclass(value, Entity), f'subclass of Entity is expected ({value})'
|
|
243
|
+
|
|
244
|
+
# def matching_entities(self) -> dict:
|
|
245
|
+
# """
|
|
246
|
+
# Applicable for an entity class with a "reasonable" number of entities in the collection
|
|
247
|
+
# TODO: need to provide a different mechanism (a view) for huge collections
|
|
248
|
+
# """
|
|
249
|
+
# cls = self.value
|
|
250
|
+
# return { e.id(): e for e in cls.sharedInstances( _cache = self.cache_only(), **self.filters() ) }
|
core_10x/entity.py
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from py10x_core import OsUser
|
|
5
|
+
|
|
6
|
+
from core_10x.global_cache import cache
|
|
7
|
+
from core_10x.rc import RC
|
|
8
|
+
from core_10x.xdate_time import XDateTime, date, datetime
|
|
9
|
+
from core_10x.resource import Resource
|
|
10
|
+
|
|
11
|
+
# ===================================================================================================================================
|
|
12
|
+
# date_format: str = default_value
|
|
13
|
+
#
|
|
14
|
+
# def date_format_apply(cls, fmt: str):
|
|
15
|
+
# XDateTime.set_default_format(fmt)
|
|
16
|
+
#
|
|
17
|
+
# datetime_format: str
|
|
18
|
+
#
|
|
19
|
+
# def datetime_format_get(cls):
|
|
20
|
+
# return f'{cls.date_format} %H:%M%S'
|
|
21
|
+
#
|
|
22
|
+
# def datetime_format_apply(cls, value):
|
|
23
|
+
# ...
|
|
24
|
+
# ===================================================================================================================================
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class classproperty(property):
|
|
28
|
+
def __get__(self, obj, objtype: type = None):
|
|
29
|
+
return self.fget(objtype)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class _EnvVars:
|
|
33
|
+
# fmt: off
|
|
34
|
+
s_converters = {
|
|
35
|
+
bool: lambda s: ast.literal_eval(s.lower().capitalize()),
|
|
36
|
+
int: lambda s: ast.literal_eval(s),
|
|
37
|
+
float: lambda s: ast.literal_eval(s),
|
|
38
|
+
str: lambda s: s,
|
|
39
|
+
date: lambda s: XDateTime.str_to_date(s),
|
|
40
|
+
datetime: lambda s: XDateTime.str_to_datetime(s),
|
|
41
|
+
#Resource: lambda s: Resource
|
|
42
|
+
}
|
|
43
|
+
# fmt: on
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def _getter(cls, data_type, var_name: str, f_get, f_apply):
|
|
47
|
+
# print(var_name)
|
|
48
|
+
str_value = os.getenv(var_name)
|
|
49
|
+
if str_value is not None:
|
|
50
|
+
f_convert = cls.s_converters.get(data_type)
|
|
51
|
+
assert f_convert, f'Unknown data type {data_type}'
|
|
52
|
+
try:
|
|
53
|
+
value = f_convert(str_value)
|
|
54
|
+
except Exception as e:
|
|
55
|
+
raise TypeError(f'Variable {var_name} - could not convert {str_value} to {data_type}') from e
|
|
56
|
+
else:
|
|
57
|
+
try:
|
|
58
|
+
value = f_get(cls)
|
|
59
|
+
except Exception as e:
|
|
60
|
+
rc = RC(False) # -- capture the exc
|
|
61
|
+
raise RuntimeError(f'{cls}.{var_name} - failed while getting a value\n{rc.error()}') from e
|
|
62
|
+
|
|
63
|
+
if f_apply:
|
|
64
|
+
try:
|
|
65
|
+
f_apply.__get__(cls)(value)
|
|
66
|
+
except Exception as e:
|
|
67
|
+
rc = RC(False) # -- capture the exc
|
|
68
|
+
raise ValueError(f'{cls}.{var_name} - failed while applying value: {value}\n{rc.error()}') from e
|
|
69
|
+
|
|
70
|
+
return value
|
|
71
|
+
|
|
72
|
+
@classmethod
|
|
73
|
+
def full_getter(cls, data_type, var_name: str, f_get, f_apply):
|
|
74
|
+
f = lambda cls: cls._getter(data_type, var_name, f_get, f_apply)
|
|
75
|
+
# return classmethod(cache(f))
|
|
76
|
+
return cache(f)
|
|
77
|
+
|
|
78
|
+
s_env_name: str = None
|
|
79
|
+
assert_var = None
|
|
80
|
+
|
|
81
|
+
def __init_subclass__(cls, env_name: str = None, **kwargs):
|
|
82
|
+
assert env_name, 'env_name is required'
|
|
83
|
+
|
|
84
|
+
cls.s_env_name = env_name
|
|
85
|
+
annotations = cls.__annotations__
|
|
86
|
+
assert annotations, 'No variables are defined'
|
|
87
|
+
|
|
88
|
+
cls_dict = cls.__dict__
|
|
89
|
+
for name, data_type in annotations.items():
|
|
90
|
+
def_value = cls_dict.get(name)
|
|
91
|
+
if def_value is None: # -- no default value, let's locate the getter
|
|
92
|
+
f_get_name = f'{name}_get'
|
|
93
|
+
f_get = cls_dict.get(f_get_name)
|
|
94
|
+
assert f_get, f'Variable {name} must define either a default value or a getter {f_get_name}(cls)'
|
|
95
|
+
# -- TODO: check signature: f_get(cls)
|
|
96
|
+
else:
|
|
97
|
+
f_get = lambda cls, def_value=def_value: def_value
|
|
98
|
+
|
|
99
|
+
f_apply_name = f'{name}_apply'
|
|
100
|
+
f_apply = cls_dict.get(f_apply_name) # -- f(cls, value)
|
|
101
|
+
|
|
102
|
+
var_name = f'{env_name}_{name.upper()}'
|
|
103
|
+
full_getter = cls.full_getter(data_type, var_name, f_get, f_apply)
|
|
104
|
+
setattr(cls, name, classproperty(full_getter))
|
|
105
|
+
|
|
106
|
+
cls.assert_var = cls.AssertVar(cls)
|
|
107
|
+
|
|
108
|
+
class AssertVar:
|
|
109
|
+
def __init__(self, env_vars_class):
|
|
110
|
+
self.env_vars_class = env_vars_class
|
|
111
|
+
|
|
112
|
+
def __getattr__(self, item):
|
|
113
|
+
value = getattr(self.env_vars_class, item)
|
|
114
|
+
if not value:
|
|
115
|
+
raise AssertionError(f'{self.env_vars_class.s_env_name}_{item.upper()} is not defined')
|
|
116
|
+
return value
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class EnvVars(_EnvVars, env_name='XX'):
|
|
120
|
+
# fmt: off
|
|
121
|
+
build_area: str
|
|
122
|
+
parent_build_area: str = 'dev'
|
|
123
|
+
|
|
124
|
+
master_password_key: str = 'XX_MASTER_PASSWORD'
|
|
125
|
+
|
|
126
|
+
examples_ts_store_uri: str = '' #-- e.g., mongodb://localhost/examples
|
|
127
|
+
vault_ts_store_uri: str = '' #-- if defined, used to store/auto retrieve security credentials for each user/resource
|
|
128
|
+
main_ts_store_uri: str = '' #-- e.g., 'mongodb://localhost:27018/main'
|
|
129
|
+
use_ts_store_per_class: bool = True #-- use TsStore per Traitable class associations
|
|
130
|
+
functional_account_prefix: str = 'xx' #-- used in user names to distinguish a regular user name from a functional account
|
|
131
|
+
|
|
132
|
+
date_format: str = XDateTime.FORMAT_ISO
|
|
133
|
+
|
|
134
|
+
sdlc_area: str
|
|
135
|
+
# fmt: on
|
|
136
|
+
|
|
137
|
+
def build_area_get(self) -> str:
|
|
138
|
+
return OsUser.me.name()
|
|
139
|
+
|
|
140
|
+
@classmethod
|
|
141
|
+
def date_format_apply(cls, value):
|
|
142
|
+
XDateTime.set_default_format(value)
|
|
143
|
+
|
|
144
|
+
def sdlc_area_get(self) -> str:
|
|
145
|
+
ba = self.build_area
|
|
146
|
+
pba = self.parent_build_area
|
|
147
|
+
return f'{ba}/{pba}' if pba else ba
|
core_10x/exec_control.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from py10x_core import BProcessContext
|
|
2
|
+
from py10x_core import BTraitableProcessor as BTP # noqa: N817
|
|
3
|
+
|
|
4
|
+
# TODO: rename DEBUG to TYPE_CHECK?
|
|
5
|
+
# TODO: make CONVERT/DEBUG mode default?
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def CHANGE_MODE(debug: bool = -1, convert_values: bool = -1): # noqa: N802
|
|
9
|
+
return BTP.create(-1, convert_values, debug, True, False)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def DEFAULT_CACHE(debug: bool = -1, convert_values: bool = -1): # noqa: N802
|
|
13
|
+
return BTP.create(0, convert_values, debug, False, True)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def GRAPH_ON(debug: bool = -1, convert_values: bool = -1): # noqa: N802
|
|
17
|
+
return BTP.create(1, convert_values, debug, False, False)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def GRAPH_OFF(debug: bool = -1, convert_values: bool = -1): # noqa: N802
|
|
21
|
+
return BTP.create(0, convert_values, debug, False, False)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def DEBUG_ON(convert_values: bool = -1): # noqa: N802
|
|
25
|
+
return BTP.create(-1, convert_values, 1, True, False)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def DEBUG_OFF(convert_values: bool = -1): # noqa: N802
|
|
29
|
+
return BTP.create(-1, convert_values, 0, True, False)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def CONVERT_VALUES_ON(debug: bool = -1): # noqa: N802
|
|
33
|
+
return BTP.create(-1, 1, debug, True, False)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def CONVERT_VALUES_OFF(debug: bool = -1): # noqa: N802
|
|
37
|
+
return BTP.create(-1, 0, debug, True, False)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def INTERACTIVE(): # noqa: N802
|
|
41
|
+
return BTP.create_interactive()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# noinspection PyMethodOverriding
|
|
45
|
+
class ProcessContext(BProcessContext):
|
|
46
|
+
@staticmethod
|
|
47
|
+
def set_flags(flags: int) -> int:
|
|
48
|
+
old_flags = BProcessContext.BPC.flags()
|
|
49
|
+
BProcessContext.BPC.set_flags(flags)
|
|
50
|
+
return old_flags
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
def reset_flags(flags: int) -> int:
|
|
54
|
+
old_flags = BProcessContext.BPC.flags()
|
|
55
|
+
BProcessContext.BPC.reset_flags(flags)
|
|
56
|
+
return old_flags
|
|
57
|
+
|
|
58
|
+
@staticmethod
|
|
59
|
+
def replace_flags(flags: int) -> int:
|
|
60
|
+
old_flags = BProcessContext.BPC.flags()
|
|
61
|
+
BProcessContext.BPC.replace_flags(flags)
|
|
62
|
+
return old_flags
|
|
63
|
+
|
|
64
|
+
@staticmethod
|
|
65
|
+
def flags() -> int:
|
|
66
|
+
return BProcessContext.BPC.flags()
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class FlagsContext:
|
|
70
|
+
s_default_flags = 0
|
|
71
|
+
|
|
72
|
+
def __init__(self, flags=None):
|
|
73
|
+
self._flags = flags if flags is not None else self.s_default_flags
|
|
74
|
+
|
|
75
|
+
def __enter__(self):
|
|
76
|
+
self._old_flags = ProcessContext.set_flags(self._flags)
|
|
77
|
+
return self
|
|
78
|
+
|
|
79
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
80
|
+
ProcessContext.BPC.replace_flags(self._old_flags)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class CACHE_ONLY(FlagsContext):
|
|
84
|
+
s_default_flags = ProcessContext.CACHE_ONLY
|
|
File without changes
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class T:
|
|
5
|
+
def __init__(self, name, get, *args):
|
|
6
|
+
self.name = name
|
|
7
|
+
self.get = get
|
|
8
|
+
self.args = args
|
|
9
|
+
|
|
10
|
+
def __get__(self, instance, owner):
|
|
11
|
+
if not self.args:
|
|
12
|
+
return self.get(instance)
|
|
13
|
+
|
|
14
|
+
return functools.partial(self.get, self)
|
|
15
|
+
|
|
16
|
+
class E:
|
|
17
|
+
def __init__(self, fn, ln):
|
|
18
|
+
self.fn = fn
|
|
19
|
+
self.ln = ln
|
|
20
|
+
|
|
21
|
+
def full_name(self):
|
|
22
|
+
return f'{self.ln}, {self.fn}'
|
|
23
|
+
|
|
24
|
+
def w(self, q):
|
|
25
|
+
return q * 10
|
|
26
|
+
|
|
27
|
+
name = T('name', full_name )
|
|
28
|
+
weight = T('weight', w, 10 )
|
|
29
|
+
|
|
30
|
+
if __name__ == '__main__':
|
|
31
|
+
e = E('Sasha', 'Davidovich')
|
|
32
|
+
|
|
33
|
+
print(e.name)
|
|
34
|
+
print(e.weight(5))
|