omlish 0.0.0.dev4__py3-none-any.whl → 0.0.0.dev6__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.

Potentially problematic release.


This version of omlish might be problematic. Click here for more details.

Files changed (143) hide show
  1. omlish/__about__.py +1 -1
  2. omlish/__init__.py +1 -1
  3. omlish/asyncs/__init__.py +10 -4
  4. omlish/asyncs/anyio.py +142 -12
  5. omlish/asyncs/asyncio.py +23 -0
  6. omlish/asyncs/asyncs.py +9 -6
  7. omlish/asyncs/bridge.py +316 -0
  8. omlish/asyncs/flavors.py +27 -1
  9. omlish/asyncs/trio_asyncio.py +28 -18
  10. omlish/c3.py +1 -1
  11. omlish/cached.py +1 -2
  12. omlish/collections/__init__.py +5 -1
  13. omlish/collections/cache/impl.py +1 -1
  14. omlish/collections/identity.py +7 -0
  15. omlish/collections/indexed.py +1 -1
  16. omlish/collections/utils.py +38 -6
  17. omlish/configs/__init__.py +5 -0
  18. omlish/configs/classes.py +53 -0
  19. omlish/configs/strings.py +94 -0
  20. omlish/dataclasses/__init__.py +9 -0
  21. omlish/dataclasses/impl/api.py +1 -1
  22. omlish/dataclasses/impl/as_.py +1 -1
  23. omlish/dataclasses/impl/copy.py +30 -0
  24. omlish/dataclasses/impl/exceptions.py +6 -0
  25. omlish/dataclasses/impl/fields.py +25 -25
  26. omlish/dataclasses/impl/init.py +5 -3
  27. omlish/dataclasses/impl/main.py +3 -0
  28. omlish/dataclasses/impl/metaclass.py +6 -1
  29. omlish/dataclasses/impl/order.py +1 -1
  30. omlish/dataclasses/impl/reflect.py +15 -2
  31. omlish/dataclasses/utils.py +44 -0
  32. omlish/defs.py +1 -1
  33. omlish/diag/__init__.py +4 -0
  34. omlish/diag/procfs.py +31 -3
  35. omlish/diag/procstats.py +32 -0
  36. omlish/{testing → diag}/pydevd.py +35 -0
  37. omlish/diag/replserver/console.py +3 -3
  38. omlish/diag/replserver/server.py +6 -5
  39. omlish/diag/threads.py +86 -0
  40. omlish/dispatch/_dispatch2.py +65 -0
  41. omlish/dispatch/_dispatch3.py +104 -0
  42. omlish/docker.py +20 -1
  43. omlish/fnpairs.py +37 -18
  44. omlish/graphs/dags.py +113 -0
  45. omlish/graphs/domination.py +268 -0
  46. omlish/graphs/trees.py +2 -2
  47. omlish/http/__init__.py +25 -0
  48. omlish/http/asgi.py +132 -0
  49. omlish/http/collections.py +15 -0
  50. omlish/http/consts.py +47 -5
  51. omlish/http/cookies.py +194 -0
  52. omlish/http/dates.py +70 -0
  53. omlish/http/encodings.py +6 -0
  54. omlish/http/json.py +273 -0
  55. omlish/http/sessions.py +204 -0
  56. omlish/inject/__init__.py +51 -17
  57. omlish/inject/binder.py +185 -5
  58. omlish/inject/bindings.py +3 -36
  59. omlish/inject/eagers.py +2 -8
  60. omlish/inject/elements.py +30 -9
  61. omlish/inject/exceptions.py +3 -3
  62. omlish/inject/impl/elements.py +65 -31
  63. omlish/inject/impl/injector.py +20 -2
  64. omlish/inject/impl/inspect.py +33 -5
  65. omlish/inject/impl/multis.py +74 -0
  66. omlish/inject/impl/origins.py +75 -0
  67. omlish/inject/impl/{private.py → privates.py} +2 -2
  68. omlish/inject/impl/providers.py +19 -39
  69. omlish/inject/{proxy.py → impl/proxy.py} +2 -2
  70. omlish/inject/impl/scopes.py +7 -2
  71. omlish/inject/injector.py +9 -4
  72. omlish/inject/inspect.py +18 -0
  73. omlish/inject/keys.py +11 -23
  74. omlish/inject/listeners.py +26 -0
  75. omlish/inject/managed.py +76 -10
  76. omlish/inject/multis.py +120 -0
  77. omlish/inject/origins.py +27 -0
  78. omlish/inject/overrides.py +5 -4
  79. omlish/inject/{private.py → privates.py} +6 -10
  80. omlish/inject/providers.py +12 -85
  81. omlish/inject/scopes.py +20 -9
  82. omlish/inject/types.py +2 -8
  83. omlish/iterators.py +13 -0
  84. omlish/lang/__init__.py +12 -2
  85. omlish/lang/cached.py +2 -2
  86. omlish/lang/classes/restrict.py +3 -2
  87. omlish/lang/classes/simple.py +18 -8
  88. omlish/lang/classes/virtual.py +2 -2
  89. omlish/lang/contextmanagers.py +75 -2
  90. omlish/lang/datetimes.py +6 -5
  91. omlish/lang/descriptors.py +131 -0
  92. omlish/lang/functions.py +18 -28
  93. omlish/lang/imports.py +11 -2
  94. omlish/lang/iterables.py +20 -1
  95. omlish/lang/typing.py +6 -0
  96. omlish/lifecycles/__init__.py +34 -0
  97. omlish/lifecycles/abstract.py +43 -0
  98. omlish/lifecycles/base.py +51 -0
  99. omlish/lifecycles/contextmanagers.py +74 -0
  100. omlish/lifecycles/controller.py +116 -0
  101. omlish/lifecycles/manager.py +161 -0
  102. omlish/lifecycles/states.py +43 -0
  103. omlish/lifecycles/transitions.py +64 -0
  104. omlish/logs/formatters.py +1 -1
  105. omlish/logs/utils.py +1 -1
  106. omlish/marshal/__init__.py +4 -0
  107. omlish/marshal/datetimes.py +1 -1
  108. omlish/marshal/naming.py +4 -0
  109. omlish/marshal/objects.py +1 -0
  110. omlish/marshal/polymorphism.py +4 -4
  111. omlish/reflect.py +139 -18
  112. omlish/secrets/__init__.py +7 -0
  113. omlish/secrets/marshal.py +41 -0
  114. omlish/secrets/passwords.py +120 -0
  115. omlish/secrets/secrets.py +47 -0
  116. omlish/serde/__init__.py +0 -0
  117. omlish/serde/dotenv.py +574 -0
  118. omlish/{json.py → serde/json.py} +4 -2
  119. omlish/serde/props.py +604 -0
  120. omlish/serde/yaml.py +223 -0
  121. omlish/sql/dbs.py +1 -1
  122. omlish/sql/duckdb.py +136 -0
  123. omlish/sql/sqlean.py +17 -0
  124. omlish/sync.py +70 -0
  125. omlish/term.py +7 -2
  126. omlish/testing/pytest/__init__.py +8 -2
  127. omlish/testing/pytest/helpers.py +0 -24
  128. omlish/testing/pytest/inject/harness.py +4 -4
  129. omlish/testing/pytest/marks.py +45 -0
  130. omlish/testing/pytest/plugins/__init__.py +3 -0
  131. omlish/testing/pytest/plugins/asyncs.py +136 -0
  132. omlish/testing/pytest/plugins/managermarks.py +60 -0
  133. omlish/testing/pytest/plugins/pydevd.py +1 -1
  134. omlish/testing/testing.py +10 -0
  135. omlish/text/delimit.py +4 -0
  136. omlish/text/glyphsplit.py +92 -0
  137. {omlish-0.0.0.dev4.dist-info → omlish-0.0.0.dev6.dist-info}/METADATA +1 -1
  138. omlish-0.0.0.dev6.dist-info/RECORD +240 -0
  139. {omlish-0.0.0.dev4.dist-info → omlish-0.0.0.dev6.dist-info}/WHEEL +1 -1
  140. omlish/configs/props.py +0 -64
  141. omlish-0.0.0.dev4.dist-info/RECORD +0 -195
  142. {omlish-0.0.0.dev4.dist-info → omlish-0.0.0.dev6.dist-info}/LICENSE +0 -0
  143. {omlish-0.0.0.dev4.dist-info → omlish-0.0.0.dev6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,74 @@
1
+ import types
2
+ import typing as ta
3
+
4
+ from .. import dataclasses as dc
5
+ from .. import defs
6
+ from .. import lang
7
+ from .base import Lifecycle
8
+ from .controller import LifecycleController
9
+ from .states import LifecycleState
10
+ from .states import LifecycleStates
11
+
12
+
13
+ LifecycleT = ta.TypeVar('LifecycleT', bound='Lifecycle')
14
+ ContextManagerT = ta.TypeVar('ContextManagerT', bound=ta.ContextManager)
15
+
16
+
17
+ @dc.dataclass(frozen=True)
18
+ class ContextManagerLifecycle(Lifecycle, lang.Final, ta.Generic[ContextManagerT]):
19
+ cm: ContextManagerT
20
+
21
+ @ta.override
22
+ def lifecycle_start(self) -> None:
23
+ self.cm.__enter__()
24
+
25
+ @ta.override
26
+ def lifecycle_stop(self) -> None:
27
+ self.cm.__exit__(None, None, None)
28
+
29
+
30
+ class LifecycleContextManager(ta.Generic[LifecycleT]):
31
+
32
+ def __init__(self, lifecycle: LifecycleT) -> None:
33
+ super().__init__()
34
+ self._lifecycle = lifecycle
35
+ self._controller = lifecycle if isinstance(lifecycle, LifecycleController) else LifecycleController(lifecycle)
36
+
37
+ defs.repr('lifecycle', 'state')
38
+
39
+ @property
40
+ def lifecycle(self) -> LifecycleT:
41
+ return self._lifecycle
42
+
43
+ @property
44
+ def controller(self) -> LifecycleController:
45
+ return self._controller
46
+
47
+ @property
48
+ def state(self) -> LifecycleState:
49
+ return self._controller.state
50
+
51
+ def __enter__(self) -> ta.Self:
52
+ try:
53
+ self._controller.lifecycle_construct()
54
+ self._controller.lifecycle_start()
55
+ except Exception:
56
+ self._controller.lifecycle_destroy()
57
+ raise
58
+ return self
59
+
60
+ def __exit__(
61
+ self,
62
+ exc_type: type[BaseException] | None,
63
+ exc_val: BaseException | None,
64
+ exc_tb: types.TracebackType | None,
65
+ ) -> bool | None:
66
+ try:
67
+ if self._controller.state is LifecycleStates.STARTED:
68
+ self._controller.lifecycle_stop()
69
+ except Exception:
70
+ self._controller.lifecycle_destroy()
71
+ raise
72
+ else:
73
+ self._controller.lifecycle_destroy()
74
+ return None
@@ -0,0 +1,116 @@
1
+ import typing as ta
2
+
3
+ from .. import check
4
+ from .. import defs
5
+ from .. import lang
6
+ from .base import Lifecycle
7
+ from .states import LifecycleState
8
+ from .states import LifecycleStates
9
+ from .transitions import LifecycleTransition
10
+ from .transitions import LifecycleTransitions
11
+
12
+
13
+ LifecycleT = ta.TypeVar('LifecycleT', bound='Lifecycle')
14
+
15
+
16
+ class LifecycleListener(ta.Generic[LifecycleT]):
17
+
18
+ def on_starting(self, obj: LifecycleT) -> None:
19
+ pass
20
+
21
+ def on_started(self, obj: LifecycleT) -> None:
22
+ pass
23
+
24
+ def on_stopping(self, obj: LifecycleT) -> None:
25
+ pass
26
+
27
+ def on_stopped(self, obj: LifecycleT) -> None:
28
+ pass
29
+
30
+
31
+ class LifecycleController(Lifecycle, ta.Generic[LifecycleT]):
32
+
33
+ def __init__(
34
+ self,
35
+ lifecycle: LifecycleT,
36
+ *,
37
+ lock: lang.DefaultLockable = None,
38
+ ) -> None:
39
+ super().__init__()
40
+
41
+ self._lifecycle: LifecycleT = check.isinstance(lifecycle, Lifecycle) # type: ignore
42
+ self._lock = lang.default_lock(lock, False)
43
+
44
+ self._state = LifecycleStates.NEW
45
+ self._listeners: list[LifecycleListener[LifecycleT]] = []
46
+
47
+ defs.repr('lifecycle', 'state')
48
+
49
+ @property
50
+ def lifecycle(self) -> LifecycleT:
51
+ return self._lifecycle
52
+
53
+ @property
54
+ def state(self) -> LifecycleState:
55
+ return self._state
56
+
57
+ def add_listener(self, listener: LifecycleListener[LifecycleT]) -> 'LifecycleController':
58
+ self._listeners.append(check.isinstance(listener, LifecycleListener)) # type: ignore
59
+ return self
60
+
61
+ def _advance(
62
+ self,
63
+ transition: LifecycleTransition,
64
+ lifecycle_fn: ta.Callable[[], None],
65
+ pre_listener_fn: ta.Callable[[LifecycleListener[LifecycleT]], ta.Callable[[LifecycleT], None]] | None = None, # noqa
66
+ post_listener_fn: ta.Callable[[LifecycleListener[LifecycleT]], ta.Callable[[LifecycleT], None]] | None = None, # noqa
67
+ ) -> None:
68
+ with self._lock():
69
+ if pre_listener_fn is not None:
70
+ for listener in self._listeners:
71
+ pre_listener_fn(listener)(self._lifecycle)
72
+ check.state(self._state in transition.old)
73
+ self._state = transition.new_intermediate
74
+ try:
75
+ lifecycle_fn()
76
+ except Exception:
77
+ self._state = transition.new_failed
78
+ raise
79
+ self._state = transition.new_succeeded
80
+ if post_listener_fn is not None:
81
+ for listener in self._listeners:
82
+ post_listener_fn(listener)(self._lifecycle)
83
+
84
+ ##
85
+
86
+ @ta.override
87
+ def lifecycle_construct(self) -> None:
88
+ self._advance(
89
+ LifecycleTransitions.CONSTRUCT,
90
+ self._lifecycle.lifecycle_construct,
91
+ )
92
+
93
+ @ta.override
94
+ def lifecycle_start(self) -> None:
95
+ self._advance(
96
+ LifecycleTransitions.START,
97
+ self._lifecycle.lifecycle_start,
98
+ lambda l: l.on_starting,
99
+ lambda l: l.on_started,
100
+ )
101
+
102
+ @ta.override
103
+ def lifecycle_stop(self) -> None:
104
+ self._advance(
105
+ LifecycleTransitions.STOP,
106
+ self._lifecycle.lifecycle_stop,
107
+ lambda l: l.on_stopping,
108
+ lambda l: l.on_stopped,
109
+ )
110
+
111
+ @ta.override
112
+ def lifecycle_destroy(self) -> None:
113
+ self._advance(
114
+ LifecycleTransitions.DESTROY,
115
+ self._lifecycle.lifecycle_destroy,
116
+ )
@@ -0,0 +1,161 @@
1
+ import typing as ta
2
+
3
+ from .. import check
4
+ from .. import collections as col
5
+ from .. import dataclasses as dc
6
+ from .. import lang
7
+ from .abstract import AbstractLifecycle
8
+ from .base import Lifecycle
9
+ from .controller import LifecycleController
10
+ from .states import LifecycleState
11
+ from .states import LifecycleStateError
12
+ from .states import LifecycleStates
13
+
14
+
15
+ class LifecycleManager(AbstractLifecycle):
16
+
17
+ @dc.dataclass(frozen=True)
18
+ class Entry(lang.Final):
19
+ controller: LifecycleController
20
+ dependencies: ta.MutableSet['LifecycleManager.Entry'] = dc.field(default_factory=col.IdentitySet)
21
+ dependents: ta.MutableSet['LifecycleManager.Entry'] = dc.field(default_factory=col.IdentitySet)
22
+
23
+ def __init__(
24
+ self,
25
+ *,
26
+ lock: lang.DefaultLockable = None,
27
+ ) -> None:
28
+ super().__init__()
29
+
30
+ self._lock = lang.default_lock(lock, False)
31
+
32
+ self._entries_by_lifecycle: ta.MutableMapping[Lifecycle, LifecycleManager.Entry] = col.IdentityKeyDict()
33
+
34
+ self._controller = LifecycleController(self._lifecycle, lock=self._lock)
35
+
36
+ @property
37
+ def controller(self) -> LifecycleController:
38
+ return self._controller
39
+
40
+ @property
41
+ def state(self) -> LifecycleState:
42
+ return self._controller.state
43
+
44
+ @staticmethod
45
+ def _get_controller(lifecycle: Lifecycle) -> LifecycleController:
46
+ if isinstance(lifecycle, LifecycleController):
47
+ return lifecycle
48
+ # elif isinstance(lifecycle, AbstractLifecycle):
49
+ # return lifecycle.lifecycle_controller
50
+ elif isinstance(lifecycle, Lifecycle):
51
+ return LifecycleController(lifecycle)
52
+ else:
53
+ raise TypeError(lifecycle)
54
+
55
+ def _add_internal(self, lifecycle: Lifecycle, dependencies: ta.Iterable[Lifecycle]) -> Entry:
56
+ check.state(self.state < LifecycleStates.STOPPING and not self.state.is_failed)
57
+
58
+ check.isinstance(lifecycle, Lifecycle)
59
+ try:
60
+ entry = self._entries_by_lifecycle[lifecycle]
61
+ except KeyError:
62
+ controller = self._get_controller(lifecycle)
63
+ entry = self._entries_by_lifecycle[lifecycle] = LifecycleManager.Entry(controller)
64
+
65
+ for dep in dependencies:
66
+ check.isinstance(dep, Lifecycle)
67
+ dep_entry = self._add_internal(dep, [])
68
+ entry.dependencies.add(dep_entry)
69
+ dep_entry.dependents.add(entry)
70
+
71
+ return entry
72
+
73
+ def add(
74
+ self,
75
+ lifecycle: Lifecycle,
76
+ dependencies: ta.Iterable[Lifecycle] = (),
77
+ ) -> Entry:
78
+ check.state(self.state < LifecycleStates.STOPPING and not self.state.is_failed)
79
+
80
+ with self._lock():
81
+ entry = self._add_internal(lifecycle, dependencies)
82
+
83
+ if self.state >= LifecycleStates.CONSTRUCTING:
84
+ def rec(e):
85
+ if e.controller.state < LifecycleStates.CONSTRUCTED:
86
+ for dep in e.dependencies:
87
+ rec(dep)
88
+ e.controller.lifecycle_construct()
89
+ rec(entry)
90
+
91
+ if self.state >= LifecycleStates.STARTING:
92
+ def rec(e):
93
+ if e.controller.state < LifecycleStates.STARTED:
94
+ for dep in e.dependencies:
95
+ rec(dep)
96
+ e.controller.lifecycle_start()
97
+ rec(entry)
98
+
99
+ return entry
100
+
101
+ ##
102
+
103
+ @ta.override
104
+ def _lifecycle_construct(self) -> None:
105
+ def rec(entry: LifecycleManager.Entry) -> None:
106
+ for dep in entry.dependencies:
107
+ rec(dep)
108
+
109
+ if entry.controller.state.is_failed:
110
+ raise LifecycleStateError(entry.controller)
111
+
112
+ if entry.controller.state < LifecycleStates.CONSTRUCTED:
113
+ entry.controller.lifecycle_construct()
114
+
115
+ for entry in self._entries_by_lifecycle.values():
116
+ rec(entry)
117
+
118
+ @ta.override
119
+ def _lifecycle_start(self) -> None:
120
+ def rec(entry: LifecycleManager.Entry) -> None:
121
+ for dep in entry.dependencies:
122
+ rec(dep)
123
+
124
+ if entry.controller.state.is_failed:
125
+ raise LifecycleStateError(entry.controller)
126
+
127
+ if entry.controller.state < LifecycleStates.CONSTRUCTED:
128
+ entry.controller.lifecycle_construct()
129
+
130
+ if entry.controller.state < LifecycleStates.STARTED:
131
+ entry.controller.lifecycle_start()
132
+
133
+ for entry in self._entries_by_lifecycle.values():
134
+ rec(entry)
135
+
136
+ @ta.override
137
+ def _lifecycle_stop(self) -> None:
138
+ def rec(entry: LifecycleManager.Entry) -> None:
139
+ for dep in entry.dependents:
140
+ rec(dep)
141
+
142
+ if entry.controller.state.is_failed:
143
+ raise LifecycleStateError(entry.controller)
144
+
145
+ if entry.controller.state is LifecycleStates.STARTED:
146
+ entry.controller.lifecycle_stop()
147
+
148
+ for entry in self._entries_by_lifecycle.values():
149
+ rec(entry)
150
+
151
+ @ta.override
152
+ def _lifecycle_destroy(self) -> None:
153
+ def rec(entry: LifecycleManager.Entry) -> None:
154
+ for dep in entry.dependents:
155
+ rec(dep)
156
+
157
+ if entry.controller.state < LifecycleStates.DESTROYED:
158
+ entry.controller.lifecycle_destroy()
159
+
160
+ for entry in self._entries_by_lifecycle.values():
161
+ rec(entry)
@@ -0,0 +1,43 @@
1
+ import functools
2
+
3
+ from .. import check
4
+ from .. import dataclasses as dc
5
+ from .. import lang
6
+
7
+
8
+ class LifecycleStateError(Exception):
9
+ pass
10
+
11
+
12
+ @dc.dataclass(frozen=True, eq=False)
13
+ @functools.total_ordering
14
+ class LifecycleState(lang.Final):
15
+ name: str
16
+ phase: int
17
+ is_failed: bool
18
+
19
+ def __lt__(self, other):
20
+ return self.phase < check.isinstance(other, LifecycleState).phase
21
+
22
+ def __le__(self, other):
23
+ return self.phase <= check.isinstance(other, LifecycleState).phase
24
+
25
+
26
+ class LifecycleStates(lang.Namespace):
27
+ NEW = LifecycleState('NEW', 0, False)
28
+
29
+ CONSTRUCTING = LifecycleState('CONSTRUCTING', 1, False)
30
+ FAILED_CONSTRUCTING = LifecycleState('FAILED_CONSTRUCTING', 2, True)
31
+ CONSTRUCTED = LifecycleState('CONSTRUCTED', 3, False)
32
+
33
+ STARTING = LifecycleState('STARTING', 4, False)
34
+ FAILED_STARTING = LifecycleState('FAILED_STARTING', 5, True)
35
+ STARTED = LifecycleState('STARTED', 6, False)
36
+
37
+ STOPPING = LifecycleState('STOPPING', 7, False)
38
+ FAILED_STOPPING = LifecycleState('FAILED_STOPPING', 8, True)
39
+ STOPPED = LifecycleState('STOPPED', 9, False)
40
+
41
+ DESTROYING = LifecycleState('DESTROYING', 10, False)
42
+ FAILED_DESTROYING = LifecycleState('FAILED_DESTROYING', 11, True)
43
+ DESTROYED = LifecycleState('DESTROYED', 12, False)
@@ -0,0 +1,64 @@
1
+ from .. import check
2
+ from .. import dataclasses as dc
3
+ from .. import lang
4
+ from .states import LifecycleState
5
+ from .states import LifecycleStates
6
+
7
+
8
+ @dc.dataclass(frozen=True)
9
+ class LifecycleTransition(lang.Final):
10
+ old: frozenset[LifecycleState]
11
+ new_intermediate: LifecycleState
12
+ new_failed: LifecycleState
13
+ new_succeeded: LifecycleState
14
+
15
+ def __post_init__(self) -> None:
16
+ dc.maybe_post_init(super())
17
+ check.unique([*self.old, self.new_intermediate, self.new_succeeded, self.new_failed])
18
+ check.arg(all(self.new_intermediate > o for o in self.old))
19
+ check.arg(self.new_failed > self.new_intermediate)
20
+ check.arg(self.new_succeeded > self.new_failed)
21
+
22
+
23
+ class LifecycleTransitions(lang.Namespace):
24
+ CONSTRUCT = LifecycleTransition(
25
+ frozenset([LifecycleStates.NEW]),
26
+ LifecycleStates.CONSTRUCTING,
27
+ LifecycleStates.FAILED_CONSTRUCTING,
28
+ LifecycleStates.CONSTRUCTED,
29
+ )
30
+
31
+ START = LifecycleTransition(
32
+ frozenset([LifecycleStates.CONSTRUCTED]),
33
+ LifecycleStates.STARTING,
34
+ LifecycleStates.FAILED_STARTING,
35
+ LifecycleStates.STARTED,
36
+ )
37
+
38
+ STOP = LifecycleTransition(
39
+ frozenset([LifecycleStates.STARTED]),
40
+ LifecycleStates.STOPPING,
41
+ LifecycleStates.FAILED_STOPPING,
42
+ LifecycleStates.STOPPED,
43
+ )
44
+
45
+ DESTROY = LifecycleTransition(
46
+ frozenset([
47
+ LifecycleStates.NEW,
48
+
49
+ LifecycleStates.CONSTRUCTING,
50
+ LifecycleStates.FAILED_CONSTRUCTING,
51
+ LifecycleStates.CONSTRUCTED,
52
+
53
+ LifecycleStates.STARTING,
54
+ LifecycleStates.FAILED_STARTING,
55
+ LifecycleStates.STARTED,
56
+
57
+ LifecycleStates.STOPPING,
58
+ LifecycleStates.FAILED_STOPPING,
59
+ LifecycleStates.STOPPED,
60
+ ]),
61
+ LifecycleStates.DESTROYING,
62
+ LifecycleStates.FAILED_DESTROYING,
63
+ LifecycleStates.DESTROYED,
64
+ )
omlish/logs/formatters.py CHANGED
@@ -5,8 +5,8 @@ import datetime
5
5
  import logging
6
6
  import typing as ta
7
7
 
8
- from .. import json
9
8
  from .. import term
9
+ from ..serde import json
10
10
 
11
11
 
12
12
  class StandardLogFormatter(logging.Formatter):
omlish/logs/utils.py CHANGED
@@ -12,7 +12,7 @@ def error_logging(log=log): # noqa
12
12
  try:
13
13
  return fn(*args, **kwargs)
14
14
  except Exception:
15
- log.exception(f'Error in {fn!r}')
15
+ log.exception('Error in %r', fn)
16
16
  raise
17
17
 
18
18
  return inner
@@ -50,3 +50,7 @@ from .standard import ( # noqa
50
50
  STANDARD_UNMARSHALER_FACTORIES,
51
51
  new_standard_unmarshaler_factory,
52
52
  )
53
+
54
+ from .values import ( # noqa
55
+ Value,
56
+ )
@@ -67,7 +67,7 @@ class DatetimeUnmarshaler(Unmarshaler):
67
67
 
68
68
  for fmt in self.fmts:
69
69
  try:
70
- return datetime.datetime.strptime(v, fmt)
70
+ return datetime.datetime.strptime(v, fmt) # FIXME: timezone # noqa
71
71
  except ValueError:
72
72
  pass
73
73
 
omlish/marshal/naming.py CHANGED
@@ -7,6 +7,7 @@ from .base import Option
7
7
  class Naming(Option, enum.Enum):
8
8
  SNAKE = 'snake'
9
9
  CAMEL = 'camel'
10
+ LOW_CAMEL = 'low_camel'
10
11
 
11
12
 
12
13
  def translate_name(n: str, e: Naming) -> str:
@@ -14,4 +15,7 @@ def translate_name(n: str, e: Naming) -> str:
14
15
  return lang.snake_case(n)
15
16
  if e is Naming.CAMEL:
16
17
  return lang.camel_case(n)
18
+ if e is Naming.LOW_CAMEL:
19
+ r = lang.camel_case(n)
20
+ return (r[0].lower() + r[1:]) if r else r
17
21
  raise ValueError(e)
omlish/marshal/objects.py CHANGED
@@ -2,6 +2,7 @@
2
2
  TODO:
3
3
  - cfg naming
4
4
  - adapters for dataclasses / namedtuples / user objects (as confitured)
5
+ - mro-merge ObjectMetadata
5
6
  """
6
7
  import collections.abc
7
8
  import typing as ta
@@ -97,7 +97,7 @@ def polymorphism_from_subclasses(ty: type, *, naming: Naming | None = None) -> P
97
97
 
98
98
 
99
99
  @dc.dataclass(frozen=True)
100
- class PolymorphismMarshaler(Marshaler):
100
+ class DictKeyPolymorphismMarshaler(Marshaler):
101
101
  m: ta.Mapping[type, tuple[str, Marshaler]]
102
102
 
103
103
  def marshal(self, ctx: MarshalContext, o: ta.Any | None) -> Value:
@@ -111,7 +111,7 @@ class PolymorphismMarshalerFactory(MarshalerFactory):
111
111
 
112
112
  def __call__(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler | None:
113
113
  if rty is self.p.ty:
114
- return PolymorphismMarshaler({
114
+ return DictKeyPolymorphismMarshaler({
115
115
  i.ty: (i.tag, ctx.make(i.ty))
116
116
  for i in self.p.impls
117
117
  })
@@ -122,7 +122,7 @@ class PolymorphismMarshalerFactory(MarshalerFactory):
122
122
 
123
123
 
124
124
  @dc.dataclass(frozen=True)
125
- class PolymorphismUnmarshaler(Unmarshaler):
125
+ class DictKeyPolymorphismUnmarshaler(Unmarshaler):
126
126
  m: ta.Mapping[str, Unmarshaler]
127
127
 
128
128
  def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any | None:
@@ -138,7 +138,7 @@ class PolymorphismUnmarshalerFactory(UnmarshalerFactory):
138
138
 
139
139
  def __call__(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler | None:
140
140
  if rty is self.p.ty:
141
- return PolymorphismUnmarshaler({
141
+ return DictKeyPolymorphismUnmarshaler({
142
142
  t: u
143
143
  for i in self.p.impls
144
144
  for u in [ctx.make(i.ty)]