omlish 0.0.0.dev244__py3-none-any.whl → 0.0.0.dev246__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 (33) hide show
  1. omlish/__about__.py +2 -2
  2. omlish/cached.py +4 -3
  3. omlish/collections/__init__.py +0 -1
  4. omlish/collections/frozen.py +2 -2
  5. omlish/collections/hasheq.py +1 -2
  6. omlish/collections/identity.py +1 -2
  7. omlish/collections/mappings.py +0 -16
  8. omlish/collections/sorted/sorted.py +1 -2
  9. omlish/daemons/services.py +7 -0
  10. omlish/dataclasses/__init__.py +3 -0
  11. omlish/dataclasses/utils.py +14 -0
  12. omlish/http/handlers.py +16 -0
  13. omlish/lang/__init__.py +46 -16
  14. omlish/lang/attrs.py +161 -0
  15. omlish/lang/cached/__init__.py +0 -0
  16. omlish/lang/{cached.py → cached/function.py} +125 -83
  17. omlish/lang/cached/property.py +118 -0
  18. omlish/lang/classes/__init__.py +0 -41
  19. omlish/lang/collections.py +50 -0
  20. omlish/marshal/__init__.py +23 -0
  21. omlish/marshal/composite/wrapped.py +26 -0
  22. omlish/marshal/objects/dataclasses.py +36 -11
  23. omlish/marshal/objects/namedtuples.py +9 -9
  24. omlish/marshal/polymorphism/marshal.py +16 -2
  25. omlish/marshal/polymorphism/unmarshal.py +16 -2
  26. omlish/os/pidfiles/pidfile.py +20 -4
  27. omlish/os/signals.py +5 -1
  28. {omlish-0.0.0.dev244.dist-info → omlish-0.0.0.dev246.dist-info}/METADATA +1 -1
  29. {omlish-0.0.0.dev244.dist-info → omlish-0.0.0.dev246.dist-info}/RECORD +33 -28
  30. {omlish-0.0.0.dev244.dist-info → omlish-0.0.0.dev246.dist-info}/WHEEL +1 -1
  31. {omlish-0.0.0.dev244.dist-info → omlish-0.0.0.dev246.dist-info}/LICENSE +0 -0
  32. {omlish-0.0.0.dev244.dist-info → omlish-0.0.0.dev246.dist-info}/entry_points.txt +0 -0
  33. {omlish-0.0.0.dev244.dist-info → omlish-0.0.0.dev246.dist-info}/top_level.txt +0 -0
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev244'
2
- __revision__ = 'e7768b1afd9f43a5f1534c2840691f48c0a89efb'
1
+ __version__ = '0.0.0.dev246'
2
+ __revision__ = '796628c78050f0ca988bfa17c2f5a5cad5ee9056'
3
3
 
4
4
 
5
5
  #
omlish/cached.py CHANGED
@@ -9,11 +9,12 @@ builtins and thus not distinguish it from a normal property.
9
9
  def p(self) -> str: ...
10
10
 
11
11
  """
12
- from .lang.cached import _CachedProperty # noqa
13
- from .lang.cached import cached_function as _cached_function
12
+ from .lang.cached.function import cached_function as _cached_function
13
+ from .lang.cached.property import cached_property as _cached_property
14
+
14
15
 
15
16
  function = _cached_function
16
17
 
17
18
  property = property # noqa
18
19
 
19
- globals()['property'] = _CachedProperty # noqa
20
+ globals()['property'] = _cached_property # noqa
@@ -63,7 +63,6 @@ from .mappings import ( # noqa
63
63
  DynamicTypeMap,
64
64
  guarded_map_update,
65
65
  multikey_dict,
66
- yield_dict_init,
67
66
  )
68
67
 
69
68
  from .ordered import ( # noqa
@@ -3,7 +3,7 @@ import collections.abc
3
3
  import itertools
4
4
  import typing as ta
5
5
 
6
- from .mappings import yield_dict_init
6
+ from .. import lang
7
7
 
8
8
 
9
9
  T = ta.TypeVar('T')
@@ -27,7 +27,7 @@ class FrozenDict(ta.Mapping[K, V], Frozen):
27
27
  if len(args) > 1:
28
28
  raise TypeError(args)
29
29
  self._dct: dict[K, V] = {}
30
- self._dct.update(yield_dict_init(*args, **kwargs))
30
+ self._dct.update(lang.yield_dict_init(*args, **kwargs))
31
31
 
32
32
  @property
33
33
  def debug(self) -> ta.Mapping[K, V]:
@@ -8,7 +8,6 @@ import dataclasses as dc
8
8
  import typing as ta
9
9
 
10
10
  from .. import lang
11
- from .mappings import yield_dict_init
12
11
 
13
12
 
14
13
  K = ta.TypeVar('K')
@@ -64,7 +63,7 @@ class HashEqMap(ta.MutableMapping[K, V]):
64
63
  self._dct: dict[int, list[HashEqMap._Node[K, V]]] = {}
65
64
  self._len = 0
66
65
 
67
- for k, v in yield_dict_init(*args, **kwargs):
66
+ for k, v in lang.yield_dict_init(*args, **kwargs):
68
67
  self[k] = v
69
68
 
70
69
  def __len__(self) -> int:
@@ -4,7 +4,6 @@ import typing as ta
4
4
  import weakref
5
5
 
6
6
  from .. import lang
7
- from .mappings import yield_dict_init
8
7
 
9
8
 
10
9
  T = ta.TypeVar('T')
@@ -38,7 +37,7 @@ class IdentityKeyDict(ta.MutableMapping[K, V]):
38
37
  def __init__(self, *args, **kwargs) -> None:
39
38
  super().__init__()
40
39
  self._dict: dict[int, tuple[K, V]] = {}
41
- for k, v in yield_dict_init(*args, **kwargs):
40
+ for k, v in lang.yield_dict_init(*args, **kwargs):
42
41
  self[k] = v
43
42
 
44
43
  @property
@@ -1,4 +1,3 @@
1
- import collections.abc
2
1
  import typing as ta
3
2
  import weakref
4
3
 
@@ -33,21 +32,6 @@ def guarded_map_update(
33
32
  return dst
34
33
 
35
34
 
36
- def yield_dict_init(*args, **kwargs) -> ta.Iterable[tuple[ta.Any, ta.Any]]:
37
- if len(args) > 1:
38
- raise TypeError
39
- if args:
40
- [src] = args
41
- if isinstance(src, collections.abc.Mapping):
42
- for k in src:
43
- yield (k, src[k])
44
- else:
45
- for k, v in src:
46
- yield (k, v)
47
- for k, v in kwargs.items():
48
- yield (k, v)
49
-
50
-
51
35
  class TypeMap(ta.Generic[T]):
52
36
  def __init__(self, items: ta.Iterable[T] = ()) -> None:
53
37
  super().__init__()
@@ -2,7 +2,6 @@ import abc
2
2
  import typing as ta
3
3
 
4
4
  from ... import lang
5
- from ..mappings import yield_dict_init
6
5
 
7
6
 
8
7
  T = ta.TypeVar('T')
@@ -83,7 +82,7 @@ class SortedListDict(SortedMutableMapping[K, V]):
83
82
  def __init__(self, impl: SortedCollection, *args, **kwargs) -> None:
84
83
  super().__init__()
85
84
  self._impl = impl
86
- for k, v in yield_dict_init(*args, **kwargs):
85
+ for k, v in lang.yield_dict_init(*args, **kwargs):
87
86
  self[k] = v
88
87
 
89
88
  @property
@@ -1,3 +1,10 @@
1
+ """
2
+ TODO:
3
+ - jsonl pidfile
4
+ - time started
5
+ - code version / revision
6
+ - venv?
7
+ """
1
8
  import abc
2
9
  import threading
3
10
  import typing as ta
@@ -108,6 +108,9 @@ from .utils import ( # noqa
108
108
  update_fields,
109
109
  update_fields_metadata,
110
110
 
111
+ shallow_astuple,
112
+ shallow_asdict,
113
+
111
114
  deep_replace,
112
115
 
113
116
  iter_items,
@@ -32,6 +32,9 @@ def fields_dict(cls_or_instance: ta.Any) -> dict[str, dc.Field]:
32
32
  return {f.name: f for f in dc.fields(cls_or_instance)}
33
33
 
34
34
 
35
+ ##
36
+
37
+
35
38
  class field_modifier: # noqa
36
39
  def __init__(self, fn: ta.Callable[[dc.Field], dc.Field]) -> None:
37
40
  super().__init__()
@@ -112,6 +115,17 @@ def update_fields_metadata(
112
115
  ##
113
116
 
114
117
 
118
+ def shallow_astuple(o: ta.Any) -> tuple[ta.Any, ...]:
119
+ return tuple(getattr(o, f.name) for f in dc.fields(o))
120
+
121
+
122
+ def shallow_asdict(o: ta.Any) -> dict[str, ta.Any]:
123
+ return {f.name: getattr(o, f.name) for f in dc.fields(o)}
124
+
125
+
126
+ ##
127
+
128
+
115
129
  def deep_replace(o: T, *args: str | ta.Callable[[ta.Any], ta.Mapping[str, ta.Any]]) -> T:
116
130
  if not args:
117
131
  return o
omlish/http/handlers.py CHANGED
@@ -79,6 +79,22 @@ class LoggingHttpHandler(HttpHandler_):
79
79
  return resp
80
80
 
81
81
 
82
+ @dc.dataclass(frozen=True)
83
+ class ExceptionLoggingHttpHandler(HttpHandler_):
84
+ handler: HttpHandler
85
+ log: logging.Logger
86
+ message: ta.Union[str, ta.Callable[[HttpHandlerRequest, BaseException], str]] = 'Error in http handler'
87
+
88
+ def __call__(self, req: HttpHandlerRequest) -> HttpHandlerResponse:
89
+ try:
90
+ return self.handler(req)
91
+ except Exception as e: # noqa
92
+ if callable(msg := self.message):
93
+ msg = msg(req, e)
94
+ self.log.exception(msg)
95
+ raise
96
+
97
+
82
98
  ##
83
99
 
84
100
 
omlish/lang/__init__.py CHANGED
@@ -1,39 +1,64 @@
1
- from .cached import ( # noqa
1
+ from .attrs import ( # noqa
2
+ AttrOps,
3
+ DictAttrOps,
4
+ STD_ATTR_OPS,
5
+ StdAttrOps,
6
+ TRANSIENT_ATTR_OPS,
7
+ TransientAttrOps,
8
+ TransientDict,
9
+ transient_delattr,
10
+ transient_getattr,
11
+ transient_setattr,
12
+ )
13
+
14
+ from .cached.function import ( # noqa
2
15
  cached_function,
3
- cached_property,
4
16
  static_init,
5
17
  )
6
18
 
7
- from .classes import ( # noqa
19
+ from .cached.property import ( # noqa
20
+ cached_property,
21
+ )
22
+
23
+ from .classes.abstract import ( # noqa
8
24
  Abstract,
9
25
  AbstractTypeError,
26
+ get_abstract_methods,
27
+ is_abstract,
28
+ is_abstract_class,
29
+ is_abstract_method,
30
+ make_abstract,
31
+ unabstract_class,
32
+ )
33
+
34
+ from .classes.restrict import ( # noqa
10
35
  AnySensitive,
11
- Callable,
12
- Descriptor,
13
36
  Final,
14
37
  FinalTypeError,
15
- LazySingleton,
16
- Marker,
17
- Namespace,
18
38
  NoBool,
19
39
  NotInstantiable,
20
40
  NotPicklable,
21
41
  PackageSealed,
22
- Picklable,
23
42
  SENSITIVE_ATTR,
24
43
  Sealed,
25
44
  SealedError,
26
45
  Sensitive,
46
+ no_bool,
47
+ )
48
+
49
+ from .classes.simple import ( # noqa
50
+ LazySingleton,
51
+ Marker,
52
+ Namespace,
27
53
  SimpleMetaDict,
28
54
  Singleton,
55
+ )
56
+
57
+ from .classes.virtual import ( # noqa
58
+ Callable,
59
+ Descriptor,
60
+ Picklable,
29
61
  Virtual,
30
- get_abstract_methods,
31
- is_abstract,
32
- is_abstract_class,
33
- is_abstract_method,
34
- make_abstract,
35
- no_bool,
36
- unabstract_class,
37
62
  virtual_check,
38
63
  )
39
64
 
@@ -52,6 +77,11 @@ from .cmp import ( # noqa
52
77
  cmp,
53
78
  )
54
79
 
80
+ from .collections import ( # noqa
81
+ merge_dicts,
82
+ yield_dict_init,
83
+ )
84
+
55
85
  from .contextmanagers import ( # noqa
56
86
  AsyncContextManager,
57
87
  ContextManaged,
omlish/lang/attrs.py ADDED
@@ -0,0 +1,161 @@
1
+ import abc
2
+ import collections.abc
3
+ import typing as ta
4
+
5
+
6
+ ##
7
+
8
+
9
+ class AttrOps(abc.ABC):
10
+ class NOT_SET: # noqa
11
+ def __new__(cls, *args, **kwargs): # noqa
12
+ raise TypeError
13
+
14
+ @abc.abstractmethod
15
+ def getattr(self, obj: ta.Any, name: str, default: ta.Any = NOT_SET) -> ta.Any:
16
+ raise NotImplementedError
17
+
18
+ @abc.abstractmethod
19
+ def setattr(self, obj: ta.Any, name: str, value: ta.Any) -> None:
20
+ raise NotImplementedError
21
+
22
+ @abc.abstractmethod
23
+ def delattr(self, obj: ta.Any, name: str) -> None:
24
+ raise NotImplementedError
25
+
26
+
27
+ ##
28
+
29
+
30
+ class StdAttrOps(AttrOps):
31
+ def getattr(self, obj: ta.Any, name: str, default: ta.Any = AttrOps.NOT_SET) -> ta.Any:
32
+ if default is AttrOps.NOT_SET:
33
+ return getattr(obj, name)
34
+ else:
35
+ return getattr(obj, name, default)
36
+
37
+ def setattr(self, obj: ta.Any, name: str, value: ta.Any) -> None:
38
+ setattr(obj, name, value)
39
+
40
+ def delattr(self, obj: ta.Any, name: str) -> None:
41
+ delattr(obj, name)
42
+
43
+
44
+ STD_ATTR_OPS = StdAttrOps()
45
+
46
+
47
+ ##
48
+
49
+
50
+ class DictAttrOps(AttrOps):
51
+ def __init__(self, dct: ta.MutableMapping[str, ta.Any] | None = None) -> None:
52
+ super().__init__()
53
+
54
+ if dct is None:
55
+ dct = {}
56
+ self._dct = dct
57
+
58
+ def getattr(self, obj: ta.Any, name: str, default: ta.Any = AttrOps.NOT_SET) -> ta.Any:
59
+ try:
60
+ return self._dct[name]
61
+ except KeyError:
62
+ if default is not AttrOps.NOT_SET:
63
+ return default
64
+ raise AttributeError(name) from None
65
+
66
+ def setattr(self, obj: ta.Any, name: str, value: ta.Any) -> None:
67
+ self._dct[name] = value
68
+
69
+ def delattr(self, obj: ta.Any, name: str) -> None:
70
+ try:
71
+ del self._dct[name]
72
+ except KeyError:
73
+ raise AttributeError(name) from None
74
+
75
+
76
+ ##
77
+
78
+
79
+ class TransientDict(collections.abc.MutableMapping):
80
+ def __init__(self) -> None:
81
+ super().__init__()
82
+
83
+ self._dct: dict = {}
84
+
85
+ def __reduce__(self):
86
+ return (TransientDict, ())
87
+
88
+ def __getitem__(self, item):
89
+ return self._dct[item]
90
+
91
+ def __setitem__(self, key, value):
92
+ self._dct[key] = value
93
+
94
+ def __delitem__(self, key):
95
+ del self._dct[key]
96
+
97
+ def __len__(self):
98
+ return len(self._dct)
99
+
100
+ def __iter__(self):
101
+ return iter(self._dct)
102
+
103
+ def clear(self):
104
+ self._dct.clear()
105
+
106
+ def items(self):
107
+ return self._dct.items()
108
+
109
+ def keys(self):
110
+ return self._dct.keys()
111
+
112
+ def values(self):
113
+ return self._dct.values()
114
+
115
+ def __contains__(self, key, /):
116
+ return super().__contains__(key)
117
+
118
+ def __eq__(self, other, /):
119
+ raise TypeError(self)
120
+
121
+
122
+ #
123
+
124
+
125
+ _TRANSIENT_DICT_ATTR = '__transient_dict__'
126
+
127
+
128
+ def _get_object_transient_dict(obj: ta.Any) -> TransientDict:
129
+ try:
130
+ return obj.__dict__[_TRANSIENT_DICT_ATTR]
131
+ except KeyError:
132
+ return obj.__dict__.setdefault(_TRANSIENT_DICT_ATTR, TransientDict())
133
+
134
+
135
+ class TransientAttrOps(AttrOps):
136
+ def getattr(self, obj: ta.Any, name: str, default: ta.Any = AttrOps.NOT_SET) -> ta.Any:
137
+ td = _get_object_transient_dict(obj)
138
+ try:
139
+ return td[name]
140
+ except KeyError:
141
+ if default is not AttrOps.NOT_SET:
142
+ return default
143
+ raise AttributeError(name) from None
144
+
145
+ def setattr(self, obj: ta.Any, name: str, value: ta.Any) -> None:
146
+ td = _get_object_transient_dict(obj)
147
+ td[name] = value
148
+
149
+ def delattr(self, obj: ta.Any, name: str) -> None:
150
+ td = _get_object_transient_dict(obj)
151
+ try:
152
+ del td[name]
153
+ except KeyError:
154
+ raise AttributeError(name) from None
155
+
156
+
157
+ TRANSIENT_ATTR_OPS = TransientAttrOps()
158
+
159
+ transient_getattr = TRANSIENT_ATTR_OPS.getattr
160
+ transient_setattr = TRANSIENT_ATTR_OPS.setattr
161
+ transient_delattr = TRANSIENT_ATTR_OPS.delattr
File without changes