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,168 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+
5
+ from cryptography.hazmat.backends import default_backend
6
+ from cryptography.hazmat.primitives import hashes, serialization
7
+ from cryptography.hazmat.primitives.asymmetric import padding, rsa
8
+ from cryptography.hazmat.primitives.serialization import (
9
+ load_pem_private_key,
10
+ load_pem_public_key,
11
+ )
12
+
13
+ from core_10x.global_cache import cache
14
+
15
+ # fmt: off
16
+ ROOT = '.xx'
17
+ PUBLIC_EXP = 65537
18
+ KEY_SIZE = 2048
19
+ PRIVATE_KEY_FILE = 'private.pem'
20
+ PUBLIC_KEY_FILE = 'public.pem'
21
+ ENCODING = 'utf-8'
22
+ # fmt: on
23
+
24
+
25
+ class SecKeys:
26
+ @classmethod
27
+ @cache
28
+ def home_dir(cls) -> str:
29
+ dir = f'{os.path.expanduser("~")}/{ROOT}'
30
+ if os.path.exists(dir):
31
+ assert os.path.isdir(dir), f'{dir} is not a directory'
32
+ else:
33
+ os.mkdir(dir)
34
+
35
+ return dir
36
+
37
+ @classmethod
38
+ def generate_keys(cls, pwd=None) -> tuple:
39
+ private_key = rsa.generate_private_key(public_exponent=PUBLIC_EXP, key_size=KEY_SIZE, backend=default_backend())
40
+ public_key = private_key.public_key()
41
+
42
+ if pwd:
43
+ format = serialization.PrivateFormat.PKCS8
44
+ algo = serialization.BestAvailableEncryption(bytes(pwd, encoding=ENCODING))
45
+ else:
46
+ format = serialization.PrivateFormat.TraditionalOpenSSL
47
+ algo = serialization.NoEncryption()
48
+
49
+ private_key_pem = private_key.private_bytes(encoding=serialization.Encoding.PEM, format=format, encryption_algorithm=algo)
50
+ public_key_pem = public_key.public_bytes(encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo)
51
+
52
+ return (private_key_pem, public_key_pem)
53
+
54
+ @classmethod
55
+ def create_keys(cls, keys: tuple = None) -> tuple:
56
+ private_key, public_key = keys if keys else cls.generate_keys()
57
+ home_dir = cls.home_dir()
58
+ try:
59
+ with open(f'{home_dir}/{PRIVATE_KEY_FILE}', 'wb') as f:
60
+ f.write(private_key)
61
+ os.chmod(f.name, 0o600)
62
+
63
+ with open(f'{home_dir}/{PUBLIC_KEY_FILE}', 'wb') as f:
64
+ f.write(public_key)
65
+ os.chmod(f.name, 0o600)
66
+
67
+ cls.keys.clear() # -- cls.keys() will then read from the files
68
+ return (private_key, public_key)
69
+
70
+ except Exception:
71
+ return (None, None)
72
+
73
+ @classmethod
74
+ @cache
75
+ def keys(cls) -> tuple:
76
+ home_dir = cls.home_dir()
77
+ file = f'{home_dir}/{PRIVATE_KEY_FILE}'
78
+ if os.path.exists(file):
79
+ try:
80
+ with open(file, 'rb') as f:
81
+ private_key = f.read()
82
+
83
+ with open(f'{home_dir}/{PUBLIC_KEY_FILE}', 'rb') as f:
84
+ public_key = f.read()
85
+
86
+ return (private_key, public_key)
87
+
88
+ except Exception:
89
+ pass
90
+
91
+ return (None, None)
92
+
93
+ @classmethod
94
+ def encrypt(cls, message, public_key: bytes = None) -> bytes | None:
95
+ if type(message) is str:
96
+ message = bytes(message, encoding=ENCODING)
97
+
98
+ if public_key is None:
99
+ _, public_key = cls.keys()
100
+ if public_key is None:
101
+ return None
102
+
103
+ public_key = load_pem_public_key(public_key)
104
+ # fmt: off
105
+ return public_key.encrypt(
106
+ message,
107
+ padding.OAEP(
108
+ mgf = padding.MGF1(algorithm = hashes.SHA256()),
109
+ algorithm = hashes.SHA256(),
110
+ label = None
111
+ )
112
+ )
113
+ # fmt: on
114
+
115
+ @classmethod
116
+ def decrypt(cls, encrypted_message: bytes, to_str=True):
117
+ private_key, _ = cls.keys()
118
+ if private_key is None:
119
+ return None
120
+
121
+ private_key = load_pem_private_key(private_key, password=None)
122
+ # fmt: off
123
+ res = private_key.decrypt(
124
+ encrypted_message,
125
+ padding.OAEP(
126
+ mgf = padding.MGF1(algorithm = hashes.SHA256()),
127
+ algorithm = hashes.SHA256(),
128
+ label = None
129
+ )
130
+ )
131
+ # fmt: on
132
+
133
+ if to_str:
134
+ res = res.decode(encoding=ENCODING)
135
+
136
+ return res
137
+
138
+ @classmethod
139
+ def encrypt_private_key(cls, password) -> bytes | None:
140
+ if type(password) is str:
141
+ password = bytes(password, encoding=ENCODING)
142
+
143
+ private_key, _ = cls.keys()
144
+ if private_key is None:
145
+ return None
146
+
147
+ private_key = load_pem_private_key(private_key, password=None)
148
+ # fmt: off
149
+ return private_key.private_bytes(
150
+ encoding = serialization.Encoding.PEM,
151
+ format = serialization.PrivateFormat.PKCS8,
152
+ encryption_algorithm = serialization.BestAvailableEncryption(password),
153
+ )
154
+ # fmt: on
155
+
156
+ @classmethod
157
+ def decrypt_private_key(cls, private_key_with_password, password) -> bytes:
158
+ if type(password) is str:
159
+ password = bytes(password, encoding=ENCODING)
160
+
161
+ pk = load_pem_private_key(private_key_with_password, password=password)
162
+ # fmt: off
163
+ return pk.private_bytes(
164
+ encoding = serialization.Encoding.PEM,
165
+ format = serialization.PrivateFormat.TraditionalOpenSSL,
166
+ encryption_algorithm = serialization.NoEncryption(),
167
+ )
168
+ # fmt: on
@@ -0,0 +1,56 @@
1
+ from datetime import datetime
2
+
3
+ from core_10x.xnone import XNone
4
+ from core_10x.resource import Resource
5
+ from core_10x.vault.vault_traitable import VaultTraitable, T, RT
6
+ from core_10x.vault.vault_user import VaultUser
7
+
8
+ class ResourceAccessor(VaultTraitable):
9
+ # fmt: off
10
+ username: str = T(T.ID)
11
+ resource_uri: str = T(T.ID)
12
+
13
+ login: str = T()
14
+ password: bytes = T()
15
+
16
+ last_updated: datetime = T(T.EVAL_ONCE)
17
+
18
+ user: VaultUser = RT(T.EVAL_ONCE)
19
+ resource: Resource = RT(T.EVAL_ONCE)
20
+ # fmt: on
21
+
22
+ def username_get(self) -> str:
23
+ return VaultUser.myname()
24
+
25
+ def last_updated_get(self) -> datetime:
26
+ return datetime.utcnow()
27
+
28
+ def user_get(self) -> VaultUser:
29
+ return VaultUser.existing_instance(user_id = self.username)
30
+
31
+ def resource_get(self) -> Resource:
32
+ return Resource.instance_from_uri(
33
+ self.resource_uri,
34
+ username = self.username,
35
+ password = self.user.sec_keys.decrypt_text(self.password)
36
+ )
37
+
38
+ class Vault:
39
+ @classmethod
40
+ def save_resource_accessor(cls, resource_uri: str, password: str, login: str = None, username: str = XNone):
41
+ if login is None:
42
+ login = username
43
+
44
+ ra = ResourceAccessor(username = username, resource_uri = resource_uri)
45
+ user = ra.user
46
+ ra.set_values(
47
+ login = login,
48
+ password = user.sec_keys.encrypt_text(password)
49
+ ).throw()
50
+
51
+ ra.save().throw()
52
+
53
+ #-- strictly speaking this method isn't necessary as it just returning existing_instance()
54
+ @classmethod
55
+ def retrieve_resource_accessor(cls, resource_uri: str, username: str = XNone) -> ResourceAccessor:
56
+ return ResourceAccessor.existing_instance(username = username, resource_uri = resource_uri)
@@ -0,0 +1,56 @@
1
+ import keyring
2
+
3
+ from py10x_core import OsUser
4
+
5
+ from core_10x.traitable import Traitable, T, RT, RC, RC_TRUE, TsStore, cache
6
+ from core_10x.environment_variables import EnvVars
7
+
8
+
9
+ class VaultTraitable(Traitable):
10
+ @classmethod
11
+ @cache
12
+ def retrieve_master_password(cls) -> str:
13
+ username = OsUser.me.name()
14
+ pwd = keyring.get_password(EnvVars.master_password_key, username)
15
+ if pwd is None:
16
+ raise OSError(f'XX MasterPassword for {username} is not found')
17
+
18
+ return pwd
19
+
20
+ @classmethod
21
+ def keep_master_password(cls, password: str):
22
+ username = OsUser.me.name()
23
+ keyring.set_password(EnvVars.master_password_key, username, password)
24
+
25
+ @classmethod
26
+ @cache
27
+ def retrieve_vault_password(cls, vault_uri: str) -> str:
28
+ username = OsUser.me.name()
29
+ pwd = keyring.get_password(vault_uri, username)
30
+ if pwd is None:
31
+ raise OSError(f'Password for {username} @ {vault_uri} is not found')
32
+
33
+ return pwd
34
+
35
+ @classmethod
36
+ def keep_vault_password(cls, vault_uri: str, password: str):
37
+ username = OsUser.me.name()
38
+ keyring.set_password(vault_uri, username, password)
39
+
40
+ @classmethod
41
+ @cache
42
+ def store_per_class(cls,) -> TsStore:
43
+ uri = EnvVars.assert_var.vault_ts_store_uri
44
+ spec = TsStore.spec_from_uri(uri)
45
+ kwargs = spec.kwargs
46
+ ts_class = spec.resource_class
47
+ hostname = kwargs[ts_class.HOSTNAME_TAG]
48
+ is_running, with_auth = ts_class.is_running_with_auth(hostname)
49
+ if not is_running or not with_auth:
50
+ raise OSError(f'Vault host {hostname} must be run with auth')
51
+
52
+ username = OsUser.me.name()
53
+ kwargs[ts_class.USERNAME_TAG] = username
54
+ kwargs[ts_class.PASSWORD_TAG] = cls.retrieve_master_password()
55
+
56
+ return ts_class.instance(**kwargs)
@@ -0,0 +1,70 @@
1
+ from core_10x.environment_variables import EnvVars
2
+ from core_10x.ts_store import TsStore
3
+ from core_10x.vault.vault_traitable import VaultTraitable, T, RT, RC, OsUser, cache
4
+ from core_10x.vault.sec_keys import SecKeys
5
+
6
+ class VaultUser(VaultTraitable):
7
+ # fmt: off
8
+ user_id: str = T(T.ID) // 'OS login'
9
+ suspended: bool = T(False)
10
+
11
+ private_key_encrypted: bytes = T()
12
+ public_key: bytes = T()
13
+
14
+ sec_keys: SecKeys = RT(T.EVAL_ONCE)
15
+ # fmt: on
16
+
17
+ def user_id_get(self) -> str:
18
+ return self.__class__.myname()
19
+
20
+ def sec_keys_get(self) -> SecKeys:
21
+ return SecKeys(self.private_key_encrypted, self.public_key, VaultTraitable.retrieve_master_password())
22
+
23
+ @classmethod
24
+ def create_new(cls, master_password: str, save = True) -> 'VaultUser':
25
+ VaultTraitable.keep_master_password(master_password)
26
+ private_key_pem, public_key_pem = SecKeys.generate_keys()
27
+ me = VaultUser()
28
+ me.public_key = public_key_pem
29
+ me.private_key_encrypted = SecKeys.encrypt_private_key(private_key_pem, master_password)
30
+
31
+ if save:
32
+ me.save().throw()
33
+
34
+ return me
35
+
36
+ @classmethod
37
+ def new_vault(cls, vault_uri: str, password: str, master_password: str = None):
38
+ username = cls.myname()
39
+ vault_db = TsStore.instance_from_uri(vault_uri, username = username, password = password)
40
+ with vault_db:
41
+ me = cls.existing_instance(user_id = username, _throw = False)
42
+ if not me:
43
+ if not master_password:
44
+ raise AssertionError('Master password is required')
45
+ me = cls.create_new(master_password)
46
+ else:
47
+ try:
48
+ existing_master_password = cls.retrieve_master_password()
49
+ except Exception:
50
+ existing_master_password = None
51
+ if not existing_master_password:
52
+ raise AssertionError('No existing MasterPassword found')
53
+ if existing_master_password != master_password:
54
+ raise AssertionError('MasterPassword provided does not match the stored one')
55
+
56
+ cls.keep_vault_password(vault_uri, password)
57
+
58
+ @classmethod
59
+ def is_functional_account(cls, user_id: str) -> bool:
60
+ return user_id.split('-', 1) == EnvVars.functional_account_prefix
61
+
62
+ @classmethod
63
+ @cache
64
+ def myname(cls) -> str:
65
+ return OsUser.me.name()
66
+
67
+ @classmethod
68
+ @cache
69
+ def me(cls) -> 'VaultUser':
70
+ return cls.existing_instance(user_id=cls.myname())
core_10x/xdate_time.py ADDED
@@ -0,0 +1,136 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import date, datetime
4
+
5
+ import dateutil.parser
6
+
7
+ MIN_CANONICAL_DATE = 10000101
8
+
9
+
10
+ class XDateTime:
11
+ """
12
+ All datetime values are in UTC time zone!
13
+ """
14
+
15
+ @staticmethod
16
+ def int_to_date(v: int) -> date:
17
+ """
18
+ ordinal or canonical (<yyyy><mm><dd>)
19
+ """
20
+ if v >= MIN_CANONICAL_DATE:
21
+ day = v % 100
22
+ ym = v // 100
23
+
24
+ month = ym % 100
25
+ year = ym // 100
26
+
27
+ return date(year=year, month=month, day=day)
28
+
29
+ return date.fromordinal(v)
30
+
31
+ @staticmethod
32
+ def date_to_int(v: date, ordinal: bool = True) -> int:
33
+ return v.toordinal() if ordinal else (10000 * v.year + 100 * v.month + v.day)
34
+
35
+ # fmt: off
36
+ FORMAT_X10 = '%Y%m%d'
37
+ FORMAT_ISO = '%Y-%m-%d'
38
+ FORMAT_US = '%m/%d/%Y'
39
+ FORMAT_EU = '%d.%m.%Y'
40
+ # fmt: on
41
+
42
+ s_default_format = FORMAT_X10
43
+ formats = [s_default_format, FORMAT_X10, FORMAT_ISO, FORMAT_US, FORMAT_EU]
44
+
45
+ @classmethod
46
+ def set_default_format(cls, fmt: str):
47
+ cls.s_default_format = fmt
48
+ cls.formats[0] = fmt
49
+
50
+ @staticmethod
51
+ def str_to_date(v: str, format: str = '') -> date | None:
52
+ if format:
53
+ try:
54
+ return datetime.strptime(v, format).date()
55
+ except Exception:
56
+ return None
57
+
58
+ for fmt in XDateTime.formats:
59
+ try:
60
+ return datetime.strptime(v, fmt).date()
61
+ except Exception:
62
+ continue
63
+
64
+ try:
65
+ return dateutil.parser.parse(v).date()
66
+ except Exception:
67
+ pass
68
+
69
+ formats_to_str = (
70
+ f'{formats[0]} %H:%M:%S',
71
+ f'{formats[0]} %H:%M:%S.%f',
72
+ )
73
+
74
+ @staticmethod
75
+ def datetime_to_str(v: datetime, with_ms: bool = False) -> str:
76
+ fmt = XDateTime.formats_to_str[with_ms]
77
+ return v.strftime(fmt)
78
+
79
+ @staticmethod
80
+ def date_to_str(v: date, format: str = '') -> str:
81
+ if not format:
82
+ format = XDateTime.formats[0]
83
+ return v.strftime(format)
84
+
85
+ # fmt: off
86
+ date_converters = {
87
+ date: lambda v: v,
88
+ datetime: lambda v: date(v.year, v.month, v.day),
89
+ int: int_to_date,
90
+ str: str_to_date,
91
+ }
92
+ # fmt: on
93
+ @classmethod
94
+ def to_date(cls, v) -> date | None:
95
+ fn = cls.date_converters.get(type(v))
96
+ return fn(v) if fn else None
97
+
98
+ dt_format = ('%H:%M', '%H:%M:%S')
99
+
100
+ @staticmethod
101
+ def str_to_datetime(v: str) -> datetime | None:
102
+ parts = v.split(' ')
103
+ try:
104
+ date_part, time_part = parts
105
+ d = XDateTime.str_to_date(date_part)
106
+ if d is not None:
107
+ num_colons = time_part.count(':')
108
+ fmt = XDateTime.dt_format[num_colons]
109
+ if time_part.find('.') != -1:
110
+ fmt = fmt + '.%f'
111
+ return datetime.strptime(time_part, fmt)
112
+ except Exception:
113
+ pass
114
+
115
+ try:
116
+ return dateutil.parser.parse(v)
117
+ except Exception:
118
+ pass
119
+
120
+ @staticmethod
121
+ def date_to_datetime(d: date) -> datetime:
122
+ return datetime(year=d.year, month=d.month, day=d.day)
123
+
124
+ # fmt: off
125
+ datetime_converters = {
126
+ datetime: lambda v: v,
127
+ date: lambda v: datetime(year = v.year, month = v.month, day = v.day),
128
+ int: lambda v: XDateTime.date_to_datetime(XDateTime.int_to_date(v)),
129
+ str: str_to_datetime,
130
+ }
131
+ # fmt: on
132
+
133
+ @classmethod
134
+ def to_datetime(cls, v) -> datetime:
135
+ fn = cls.datetime_converters.get(type(v))
136
+ return fn(v) if fn else None
core_10x/xnone.py ADDED
@@ -0,0 +1,71 @@
1
+ class XNoneType:
2
+ """
3
+ XNone is the 10x alternative to None representing a special empty object.
4
+ As opposed to None, XNone may be used in many built-in python operations without extra checking, e.g.
5
+
6
+ a_dict = XNone
7
+ for key, value in a_dict.items():
8
+ ...
9
+
10
+ The above loop will merely exit on the first iteration.
11
+
12
+ - all arithmetic operations with XNone will return XNone
13
+ - all relational operations with XNone will return True or False accordingly
14
+ - a call to XNone(*args, **kwargs) will return XNone
15
+ """
16
+
17
+ def __getattr__(self, item): return self
18
+ def __setattr__(self, key, value): raise AttributeError('May not setattr to XNone')
19
+ def __getitem__(self, item): return self
20
+ def __setitem__(self, key, value): raise TypeError('\'XNone\' object does not support item assignment')
21
+ def __call__(self, *args, **kwargs): return self
22
+ def __hash__(self): return id(self)
23
+
24
+ def __int__(self): return self
25
+ def __bool__(self): return False
26
+ def __float__(self): return self
27
+
28
+ def __repr__(self): return 'XNone'
29
+ def __str__(self): return ''
30
+
31
+ def __and__(self, other): return False
32
+ def __or__(self, other): return other
33
+ def __xor__(self, other): return other
34
+ def __invert__(self): return None #-- the opposite to XNone
35
+ def __lshift__(self, other): return self
36
+ def __rshift__(self, other): return self
37
+
38
+ def __len__(self): return 0
39
+ def __iter__(self): return self
40
+ def __next__(self): raise StopIteration
41
+
42
+ def keys(self): return self
43
+ def values(self): return self
44
+ def items(self): return self
45
+
46
+ def __eq__(self, other): return other is self
47
+ def __ne__(self, other): return other is not self
48
+ def __lt__(self, other): return other is not self
49
+ def __le__(self, other): return True
50
+ def __gt__(self, other): return False
51
+ def __ge__(self, other): return other is self
52
+ def __abs__(self): return self
53
+ def __neg__(self): return self
54
+ def __add__(self, other): return self
55
+ def __radd__(self, other): return self
56
+ def __sub__(self, other): return self
57
+ def __rsub__(self, other): return self
58
+ def __mul__(self, other): return self
59
+ def __rmul__(self, other): return self
60
+ def __pow__(self, power, modulo = None): return self
61
+ def __truediv__(self, other): return self
62
+ def __rtruediv__(self, other): return self
63
+ def __floordiv__(self, other): return self
64
+ def __rfloordiv__(self, other): return self
65
+
66
+ s_serialized = chr(8)
67
+
68
+ def __init_subclass__(cls, **kwargs):
69
+ raise TypeError('May not derive from XNone')
70
+
71
+ XNone = XNoneType()