omlish 0.0.0.dev228__py3-none-any.whl → 0.0.0.dev230__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 (47) hide show
  1. omlish/__about__.py +2 -2
  2. omlish/collections/__init__.py +15 -15
  3. omlish/collections/frozen.py +0 -2
  4. omlish/collections/identity.py +0 -3
  5. omlish/collections/indexed.py +0 -2
  6. omlish/collections/mappings.py +0 -3
  7. omlish/collections/ordered.py +0 -2
  8. omlish/collections/persistent/__init__.py +0 -0
  9. omlish/collections/sorted/__init__.py +0 -0
  10. omlish/collections/{skiplist.py → sorted/skiplist.py} +0 -1
  11. omlish/collections/{sorted.py → sorted/sorted.py} +2 -5
  12. omlish/collections/unmodifiable.py +0 -2
  13. omlish/dispatch/dispatch.py +4 -1
  14. omlish/dispatch/methods.py +4 -0
  15. omlish/formats/json/__init__.py +5 -0
  16. omlish/io/compress/brotli.py +3 -3
  17. omlish/io/fdio/__init__.py +3 -0
  18. omlish/marshal/__init__.py +10 -10
  19. omlish/marshal/base.py +1 -1
  20. omlish/marshal/standard.py +2 -2
  21. omlish/marshal/trivial/__init__.py +0 -0
  22. omlish/marshal/{forbidden.py → trivial/forbidden.py} +7 -7
  23. omlish/marshal/{nop.py → trivial/nop.py} +5 -5
  24. omlish/os/deathpacts/__init__.py +15 -0
  25. omlish/os/deathpacts/base.py +76 -0
  26. omlish/os/deathpacts/heartbeatfile.py +85 -0
  27. omlish/os/{death.py → deathpacts/pipe.py} +25 -93
  28. omlish/os/forkhooks.py +55 -31
  29. omlish/os/pidfiles/manager.py +11 -44
  30. omlish/reflect/__init__.py +1 -0
  31. omlish/reflect/inspect.py +43 -0
  32. omlish/sql/queries/__init__.py +4 -4
  33. omlish/sql/queries/rendering2.py +248 -0
  34. omlish/text/parts.py +26 -23
  35. {omlish-0.0.0.dev228.dist-info → omlish-0.0.0.dev230.dist-info}/METADATA +1 -1
  36. {omlish-0.0.0.dev228.dist-info → omlish-0.0.0.dev230.dist-info}/RECORD +46 -40
  37. omlish/formats/json/delimted.py +0 -4
  38. /omlish/collections/{persistent.py → persistent/persistent.py} +0 -0
  39. /omlish/collections/{treap.py → persistent/treap.py} +0 -0
  40. /omlish/collections/{treapmap.py → persistent/treapmap.py} +0 -0
  41. /omlish/marshal/{utils.py → proxy.py} +0 -0
  42. /omlish/marshal/{singular → trivial}/any.py +0 -0
  43. /omlish/sql/queries/{building.py → std.py} +0 -0
  44. {omlish-0.0.0.dev228.dist-info → omlish-0.0.0.dev230.dist-info}/LICENSE +0 -0
  45. {omlish-0.0.0.dev228.dist-info → omlish-0.0.0.dev230.dist-info}/WHEEL +0 -0
  46. {omlish-0.0.0.dev228.dist-info → omlish-0.0.0.dev230.dist-info}/entry_points.txt +0 -0
  47. {omlish-0.0.0.dev228.dist-info → omlish-0.0.0.dev230.dist-info}/top_level.txt +0 -0
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev228'
2
- __revision__ = '00e3d1141f5caccfbfa74099183b2d12485e1b0d'
1
+ __version__ = '0.0.0.dev230'
2
+ __revision__ = 'ed2672ad6a2fb4718c57e281c5ce91fdc7998a6f'
3
3
 
4
4
 
5
5
  #
@@ -76,39 +76,39 @@ from .ordered import ( # noqa
76
76
  OrderedSet,
77
77
  )
78
78
 
79
- from .persistent import ( # noqa
79
+ from .persistent.persistent import ( # noqa
80
80
  PersistentMap,
81
81
  )
82
82
 
83
83
  if _ta.TYPE_CHECKING:
84
- from .skiplist import ( # noqa
84
+ from .persistent.treapmap import ( # noqa
85
+ TreapMap,
86
+ new_treap_map,
87
+ )
88
+ else:
89
+ _lang.proxy_init(globals(), '.persistent.treapmap', [
90
+ 'TreapMap',
91
+ 'new_treap_map',
92
+ ])
93
+
94
+ if _ta.TYPE_CHECKING:
95
+ from .sorted.skiplist import ( # noqa
85
96
  SkipList,
86
97
  SkipListDict,
87
98
  )
88
99
  else:
89
- _lang.proxy_init(globals(), '.skiplist', [
100
+ _lang.proxy_init(globals(), '.sorted.skiplist', [
90
101
  'SkipList',
91
102
  'SkipListDict',
92
103
  ])
93
104
 
94
- from .sorted import ( # noqa
105
+ from .sorted.sorted import ( # noqa
95
106
  SortedCollection,
96
107
  SortedListDict,
97
108
  SortedMapping,
98
109
  SortedMutableMapping,
99
110
  )
100
111
 
101
- if _ta.TYPE_CHECKING:
102
- from .treapmap import ( # noqa
103
- TreapMap,
104
- new_treap_map,
105
- )
106
- else:
107
- _lang.proxy_init(globals(), '.treapmap', [
108
- 'TreapMap',
109
- 'new_treap_map',
110
- ])
111
-
112
112
  from .unmodifiable import ( # noqa
113
113
  Unmodifiable,
114
114
  UnmodifiableMapping,
@@ -16,7 +16,6 @@ class Frozen(ta.Hashable, abc.ABC):
16
16
 
17
17
 
18
18
  class FrozenDict(ta.Mapping[K, V], Frozen):
19
-
20
19
  def __new__(cls, *args: ta.Any, **kwargs: ta.Any) -> 'FrozenDict[K, V]': # noqa
21
20
  if len(args) == 1 and Frozen in type(args[0]).__bases__:
22
21
  return args[0]
@@ -73,7 +72,6 @@ class FrozenDict(ta.Mapping[K, V], Frozen):
73
72
 
74
73
 
75
74
  class FrozenList(ta.Sequence[T], Frozen):
76
-
77
75
  def __init__(self, it: ta.Iterable[T] | None = None) -> None:
78
76
  super().__init__()
79
77
 
@@ -13,7 +13,6 @@ V = ta.TypeVar('V')
13
13
 
14
14
 
15
15
  class IdentityWrapper(ta.Generic[T]):
16
-
17
16
  def __init__(self, value: T) -> None:
18
17
  super().__init__()
19
18
  self._value = value
@@ -36,7 +35,6 @@ class IdentityWrapper(ta.Generic[T]):
36
35
 
37
36
 
38
37
  class IdentityKeyDict(ta.MutableMapping[K, V]):
39
-
40
38
  def __init__(self, *args, **kwargs) -> None:
41
39
  super().__init__()
42
40
  self._dict: dict[int, tuple[K, V]] = {}
@@ -70,7 +68,6 @@ class IdentityKeyDict(ta.MutableMapping[K, V]):
70
68
 
71
69
 
72
70
  class IdentitySet(ta.MutableSet[T]):
73
-
74
71
  def __init__(self, init: ta.Iterable[T] | None = None) -> None:
75
72
  super().__init__()
76
73
  self._dict: dict[int, T] = {}
@@ -8,7 +8,6 @@ T = ta.TypeVar('T')
8
8
 
9
9
 
10
10
  class IndexedSeq(ta.Sequence[T]):
11
-
12
11
  def __init__(self, it: ta.Iterable[T], *, identity: bool = False) -> None:
13
12
  super().__init__()
14
13
 
@@ -42,7 +41,6 @@ class IndexedSeq(ta.Sequence[T]):
42
41
 
43
42
 
44
43
  class IndexedSetSeq(ta.Sequence[ta.AbstractSet[T]]):
45
-
46
44
  def __init__(self, it: ta.Iterable[ta.Iterable[T]], *, identity: bool = False) -> None:
47
45
  super().__init__()
48
46
 
@@ -49,7 +49,6 @@ def yield_dict_init(*args, **kwargs) -> ta.Iterable[tuple[ta.Any, ta.Any]]:
49
49
 
50
50
 
51
51
  class TypeMap(ta.Generic[T]):
52
-
53
52
  def __init__(self, items: ta.Iterable[T] = ()) -> None:
54
53
  super().__init__()
55
54
 
@@ -79,7 +78,6 @@ class TypeMap(ta.Generic[T]):
79
78
 
80
79
 
81
80
  class DynamicTypeMap(ta.Generic[V]):
82
-
83
81
  def __init__(self, items: ta.Iterable[V] = (), *, weak: bool = False) -> None:
84
82
  super().__init__()
85
83
 
@@ -115,7 +113,6 @@ class DynamicTypeMap(ta.Generic[V]):
115
113
 
116
114
 
117
115
  class MissingDict(dict[K, V]):
118
-
119
116
  def __init__(self, missing_fn: ta.Callable[[K], V]) -> None:
120
117
  if not callable(missing_fn):
121
118
  raise TypeError(missing_fn)
@@ -5,7 +5,6 @@ T = ta.TypeVar('T')
5
5
 
6
6
 
7
7
  class OrderedSet(ta.MutableSet[T]):
8
-
9
8
  def __init__(self, iterable: ta.Iterable[T] | None = None) -> None:
10
9
  super().__init__()
11
10
  self._dct: dict[T, ta.Any] = {}
@@ -56,7 +55,6 @@ class OrderedSet(ta.MutableSet[T]):
56
55
 
57
56
 
58
57
  class OrderedFrozenSet(ta.FrozenSet[T]): # noqa
59
-
60
58
  _list: ta.Sequence[T]
61
59
 
62
60
  def __new__(cls, items: ta.Iterable[T]) -> frozenset[T]: # type: ignore
File without changes
File without changes
@@ -188,6 +188,5 @@ class SkipList(SortedCollection[T]):
188
188
 
189
189
 
190
190
  class SkipListDict(SortedListDict[K, V]):
191
-
192
191
  def __init__(self, *args, **kwargs) -> None:
193
192
  super().__init__(SkipList(comparator=SortedListDict._item_comparator), *args, **kwargs) # noqa
@@ -1,8 +1,8 @@
1
1
  import abc
2
2
  import typing as ta
3
3
 
4
- from .. import lang
5
- from .mappings import yield_dict_init
4
+ from ... import lang
5
+ from ..mappings import yield_dict_init
6
6
 
7
7
 
8
8
  T = ta.TypeVar('T')
@@ -12,7 +12,6 @@ V = ta.TypeVar('V')
12
12
 
13
13
 
14
14
  class SortedCollection(lang.Abstract, ta.Collection[T]):
15
-
16
15
  Comparator = ta.Callable[[U, U], int]
17
16
 
18
17
  @staticmethod
@@ -55,7 +54,6 @@ class SortedCollection(lang.Abstract, ta.Collection[T]):
55
54
 
56
55
 
57
56
  class SortedMapping(ta.Mapping[K, V]):
58
-
59
57
  @abc.abstractmethod
60
58
  def items(self) -> ta.Iterator[tuple[K, V]]: # type: ignore
61
59
  raise NotImplementedError
@@ -78,7 +76,6 @@ class SortedMutableMapping(ta.MutableMapping[K, V], SortedMapping[K, V]):
78
76
 
79
77
 
80
78
  class SortedListDict(SortedMutableMapping[K, V]):
81
-
82
79
  @staticmethod
83
80
  def _item_comparator(a: tuple[K, V], b: tuple[K, V]) -> int:
84
81
  return SortedCollection.default_comparator(a[0], b[0])
@@ -13,7 +13,6 @@ class Unmodifiable(lang.Abstract):
13
13
 
14
14
 
15
15
  class UnmodifiableSequence(ta.Sequence[T], Unmodifiable, lang.Final):
16
-
17
16
  def __init__(self, target: ta.Sequence[T]) -> None:
18
17
  super().__init__()
19
18
 
@@ -65,7 +64,6 @@ class UnmodifiableSequence(ta.Sequence[T], Unmodifiable, lang.Final):
65
64
 
66
65
 
67
66
  class UnmodifiableSet(ta.AbstractSet[T], Unmodifiable, lang.Final):
68
-
69
67
  def __init__(self, target: ta.AbstractSet[T]) -> None:
70
68
  super().__init__()
71
69
 
@@ -31,7 +31,10 @@ def get_impl_func_cls_set(func: ta.Callable) -> frozenset[type]:
31
31
  else:
32
32
  return check.isinstance(a, type)
33
33
 
34
- _, cls = next(iter(ta.get_type_hints(func).items()))
34
+ # Exclude 'return' to support difficult to handle return types - they are unimportant.
35
+ # TODO: only get hints for first arg - requires inspection, which requires chopping off `self`, which can be tricky.
36
+ _, cls = next(iter(rfl.get_filtered_type_hints(func, exclude=['return']).items()))
37
+
35
38
  rty = rfl.type_(cls)
36
39
  if isinstance(rty, rfl.Union):
37
40
  ret = frozenset(erase(arg) for arg in rty.args)
@@ -81,6 +81,10 @@ class Method:
81
81
  mro_dct = lang.build_mro_dict(instance_cls, owner_cls)
82
82
  seen: ta.Mapping[ta.Any, str] = {}
83
83
  for nam, att in mro_dct.items():
84
+ try:
85
+ hash(att)
86
+ except TypeError:
87
+ continue
84
88
  if att in self._impls:
85
89
  try:
86
90
  ex_nam = seen[att]
@@ -1,4 +1,9 @@
1
1
  # ruff: noqa: I001
2
+ """
3
+ TODO:
4
+ - delimited.py / jsonl
5
+ - + record separators ala https://en.wikipedia.org/wiki/JSON_streaming
6
+ """
2
7
  import typing as _ta
3
8
 
4
9
  from ... import lang as _lang
@@ -27,9 +27,9 @@ class BrotliCompression(Compression):
27
27
  return brotli.compress(
28
28
  d,
29
29
  **(dict(mode=self.mode) if self.mode is not None else {}),
30
- **(dict(mode=self.quality) if self.quality is not None else {}),
31
- **(dict(mode=self.lgwin) if self.lgwin is not None else {}),
32
- **(dict(mode=self.lgblock) if self.lgblock is not None else {}),
30
+ **(dict(quality=self.quality) if self.quality is not None else {}),
31
+ **(dict(lgwin=self.lgwin) if self.lgwin is not None else {}),
32
+ **(dict(lgblock=self.lgblock) if self.lgblock is not None else {}),
33
33
  )
34
34
 
35
35
  def decompress(self, d: bytes) -> bytes:
@@ -1 +1,4 @@
1
1
  # @omlish-lite
2
+ """
3
+ Basically the old asyncore system, which still has usecases over asyncio (such as single-threaded, forking code).
4
+ """
@@ -34,11 +34,6 @@ from .exceptions import ( # noqa
34
34
  UnhandledTypeError,
35
35
  )
36
36
 
37
- from .forbidden import ( # noqa
38
- ForbiddenTypeMarshalerFactory,
39
- ForbiddenTypeUnmarshalerFactory,
40
- )
41
-
42
37
  from .global_ import ( # noqa
43
38
  GLOBAL_REGISTRY,
44
39
 
@@ -54,11 +49,6 @@ from .naming import ( # noqa
54
49
  translate_name,
55
50
  )
56
51
 
57
- from .nop import ( # noqa
58
- NOP_MARSHALER_UNMARSHALER,
59
- NopMarshalerUnmarshaler,
60
- )
61
-
62
52
  from .objects.helpers import ( # noqa
63
53
  update_field_metadata,
64
54
  update_fields_metadata,
@@ -127,6 +117,16 @@ from .standard import ( # noqa
127
117
  new_standard_unmarshaler_factory,
128
118
  )
129
119
 
120
+ from .trivial.forbidden import ( # noqa
121
+ ForbiddenTypeMarshalerFactory,
122
+ ForbiddenTypeUnmarshalerFactory,
123
+ )
124
+
125
+ from .trivial.nop import ( # noqa
126
+ NOP_MARSHALER_UNMARSHALER,
127
+ NopMarshalerUnmarshaler,
128
+ )
129
+
130
130
  from .values import ( # noqa
131
131
  Value,
132
132
  )
omlish/marshal/base.py CHANGED
@@ -95,9 +95,9 @@ from .exceptions import UnhandledTypeError
95
95
  from .factories import RecursiveTypeFactory
96
96
  from .factories import TypeCacheFactory
97
97
  from .factories import TypeMapFactory
98
+ from .proxy import _Proxy
98
99
  from .registries import Registry
99
100
  from .registries import RegistryItem
100
- from .utils import _Proxy
101
101
  from .values import Value
102
102
 
103
103
 
@@ -23,8 +23,6 @@ from .objects.namedtuples import NamedtupleMarshalerFactory
23
23
  from .objects.namedtuples import NamedtupleUnmarshalerFactory
24
24
  from .polymorphism.unions import PrimitiveUnionMarshalerFactory
25
25
  from .polymorphism.unions import PrimitiveUnionUnmarshalerFactory
26
- from .singular.any import ANY_MARSHALER_FACTORY
27
- from .singular.any import ANY_UNMARSHALER_FACTORY
28
26
  from .singular.base64 import BASE64_MARSHALER_FACTORY
29
27
  from .singular.base64 import BASE64_UNMARSHALER_FACTORY
30
28
  from .singular.datetimes import DATETIME_MARSHALER_FACTORY
@@ -37,6 +35,8 @@ from .singular.primitives import PRIMITIVE_MARSHALER_FACTORY
37
35
  from .singular.primitives import PRIMITIVE_UNMARSHALER_FACTORY
38
36
  from .singular.uuids import UUID_MARSHALER_FACTORY
39
37
  from .singular.uuids import UUID_UNMARSHALER_FACTORY
38
+ from .trivial.any import ANY_MARSHALER_FACTORY
39
+ from .trivial.any import ANY_UNMARSHALER_FACTORY
40
40
 
41
41
 
42
42
  ##
File without changes
@@ -1,13 +1,13 @@
1
1
  import dataclasses as dc
2
2
  import typing as ta
3
3
 
4
- from .. import reflect as rfl
5
- from ..funcs import match as mfs
6
- from .base import MarshalContext
7
- from .base import Marshaler
8
- from .base import UnmarshalContext
9
- from .base import Unmarshaler
10
- from .exceptions import ForbiddenTypeError
4
+ from ... import reflect as rfl
5
+ from ...funcs import match as mfs
6
+ from ..base import MarshalContext
7
+ from ..base import Marshaler
8
+ from ..base import UnmarshalContext
9
+ from ..base import Unmarshaler
10
+ from ..exceptions import ForbiddenTypeError
11
11
 
12
12
 
13
13
  C = ta.TypeVar('C')
@@ -1,10 +1,10 @@
1
1
  import typing as ta
2
2
 
3
- from .base import MarshalContext
4
- from .base import Marshaler
5
- from .base import UnmarshalContext
6
- from .base import Unmarshaler
7
- from .values import Value
3
+ from ..base import MarshalContext
4
+ from ..base import Marshaler
5
+ from ..base import UnmarshalContext
6
+ from ..base import Unmarshaler
7
+ from ..values import Value
8
8
 
9
9
 
10
10
  class NopMarshalerUnmarshaler(Marshaler, Unmarshaler):
@@ -0,0 +1,15 @@
1
+ from .base import ( # noqa
2
+ Deathpact,
3
+ NopDeathpact,
4
+
5
+ BaseDeathpact,
6
+ )
7
+
8
+ from .heartbeatfile import ( # noqa
9
+ HeartbeatFileDeathpact,
10
+ )
11
+
12
+ from .pipe import ( # noqa
13
+ PipeDeathpact,
14
+ ForkAwarePipeDeathpact,
15
+ )
@@ -0,0 +1,76 @@
1
+ import abc
2
+ import os
3
+ import signal
4
+ import sys
5
+ import time
6
+ import typing as ta
7
+
8
+
9
+ ##
10
+
11
+
12
+ class Deathpact(abc.ABC):
13
+ @abc.abstractmethod
14
+ def poll(self) -> None:
15
+ raise NotImplementedError
16
+
17
+
18
+ class NopDeathpact(Deathpact):
19
+ def poll(self) -> None:
20
+ pass
21
+
22
+
23
+ ##
24
+
25
+
26
+ class BaseDeathpact(Deathpact, abc.ABC):
27
+ def __init__(
28
+ self,
29
+ *,
30
+ interval_s: float = .5,
31
+ signal: int | None = signal.SIGTERM, # noqa
32
+ output: ta.Literal['stdout', 'stderr'] | None = 'stderr',
33
+ on_die: ta.Callable[[], None] | None = None,
34
+ ) -> None:
35
+ super().__init__()
36
+
37
+ self._interval_s = interval_s
38
+ self._signal = signal
39
+ self._output = output
40
+ self._on_die = on_die
41
+
42
+ self._last_check_t: float | None = None
43
+
44
+ def _print(self, msg: str) -> None:
45
+ match self._output:
46
+ case 'stdout':
47
+ f = sys.stdout
48
+ case 'stderr':
49
+ f = sys.stderr
50
+ case _:
51
+ return
52
+ print(f'{self} pid={os.getpid()}: {msg}', file=f)
53
+
54
+ def die(self) -> None:
55
+ self._print('Triggered! Process terminating!')
56
+
57
+ if self._on_die is not None:
58
+ self._on_die()
59
+
60
+ if self._signal is not None:
61
+ os.kill(os.getpid(), self._signal)
62
+
63
+ sys.exit(1)
64
+
65
+ @abc.abstractmethod
66
+ def should_die(self) -> bool:
67
+ raise NotImplementedError
68
+
69
+ def maybe_die(self) -> None:
70
+ if self.should_die():
71
+ self.die()
72
+
73
+ def poll(self) -> None:
74
+ if self._last_check_t is None or (time.monotonic() - self._last_check_t) >= self._interval_s:
75
+ self.maybe_die()
76
+ self._last_check_t = time.monotonic()
@@ -0,0 +1,85 @@
1
+ """
2
+ TODO:
3
+ - chaining
4
+ """
5
+ import os
6
+ import time
7
+ import typing as ta
8
+
9
+ from ... import check
10
+ from ..forkhooks import ProcessOriginTracker
11
+ from ..temp import make_temp_file
12
+ from .base import BaseDeathpact
13
+
14
+
15
+ ##
16
+
17
+
18
+ class HeartbeatFileDeathpact(BaseDeathpact):
19
+ def __init__(
20
+ self,
21
+ path: str,
22
+ ttl_s: float = 10.,
23
+ **kwargs: ta.Any,
24
+ ) -> None:
25
+ super().__init__(**kwargs)
26
+
27
+ self._path = path
28
+ self._ttl_s = ttl_s
29
+
30
+ self._process_origin = ProcessOriginTracker()
31
+
32
+ @property
33
+ def path(self) -> str:
34
+ return self._path
35
+
36
+ def is_parent(self) -> bool:
37
+ return self._process_origin.is_in_origin_process()
38
+
39
+ #
40
+
41
+ def __enter__(self) -> ta.Self:
42
+ check.state(self.is_parent())
43
+
44
+ self.update()
45
+
46
+ return self
47
+
48
+ def close(self) -> None:
49
+ if self.is_parent():
50
+ try:
51
+ os.unlink(self._path)
52
+ except FileNotFoundError:
53
+ pass
54
+
55
+ def __exit__(self, exc_type, exc_val, exc_tb):
56
+ self.close()
57
+
58
+ #
59
+
60
+ @classmethod
61
+ def _now(cls) -> float:
62
+ return time.monotonic()
63
+
64
+ def update(self) -> None:
65
+ check.state(self.is_parent())
66
+
67
+ new = make_temp_file()
68
+ with open(new, 'w') as f:
69
+ f.write(str(self._now()))
70
+
71
+ # FIXME: same filesystem
72
+ os.replace(new, self._path)
73
+
74
+ def read(self) -> float:
75
+ try:
76
+ with open(self._path) as f:
77
+ return float(f.read())
78
+ except FileNotFoundError:
79
+ return float('-inf')
80
+
81
+ def age(self) -> float:
82
+ return self._now() - self.read()
83
+
84
+ def should_die(self) -> bool:
85
+ return self.age() >= self._ttl_s