omlish 0.0.0.dev2__py3-none-any.whl → 0.0.0.dev4__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 (124) hide show
  1. omlish/__about__.py +1 -2
  2. omlish/__init__.py +8 -0
  3. omlish/argparse.py +4 -4
  4. omlish/asyncs/__init__.py +23 -2
  5. omlish/asyncs/anyio.py +13 -11
  6. omlish/asyncs/asyncs.py +1 -3
  7. omlish/asyncs/flavors.py +201 -0
  8. omlish/asyncs/futures.py +10 -9
  9. omlish/asyncs/trio_asyncio.py +41 -0
  10. omlish/c3.py +1 -1
  11. omlish/check.py +3 -3
  12. omlish/collections/_abc.py +2 -0
  13. omlish/collections/_io_abc.py +4 -2
  14. omlish/collections/cache/__init__.py +1 -1
  15. omlish/collections/cache/descriptor.py +8 -8
  16. omlish/collections/cache/impl.py +24 -17
  17. omlish/collections/cache/types.py +1 -1
  18. omlish/collections/coerce.py +1 -1
  19. omlish/collections/frozen.py +6 -6
  20. omlish/collections/identity.py +3 -4
  21. omlish/collections/mappings.py +2 -2
  22. omlish/collections/ordered.py +7 -7
  23. omlish/collections/skiplist.py +1 -1
  24. omlish/collections/sorted.py +1 -1
  25. omlish/collections/treap.py +25 -0
  26. omlish/collections/treapmap.py +57 -5
  27. omlish/collections/unmodifiable.py +9 -9
  28. omlish/collections/utils.py +1 -1
  29. omlish/configs/flattening.py +7 -6
  30. omlish/configs/props.py +3 -3
  31. omlish/dataclasses/__init__.py +1 -1
  32. omlish/dataclasses/impl/__init__.py +17 -1
  33. omlish/dataclasses/impl/api.py +10 -11
  34. omlish/dataclasses/impl/as_.py +4 -4
  35. omlish/dataclasses/impl/exceptions.py +1 -1
  36. omlish/dataclasses/impl/fields.py +7 -7
  37. omlish/dataclasses/impl/frozen.py +2 -2
  38. omlish/dataclasses/impl/init.py +5 -5
  39. omlish/dataclasses/impl/internals.py +1 -1
  40. omlish/dataclasses/impl/metaclass.py +1 -1
  41. omlish/dataclasses/impl/order.py +1 -1
  42. omlish/dataclasses/impl/replace.py +1 -1
  43. omlish/dataclasses/impl/repr.py +4 -4
  44. omlish/dataclasses/impl/utils.py +6 -6
  45. omlish/defs.py +13 -17
  46. omlish/{procfs.py → diag/procfs.py} +22 -24
  47. omlish/diag/ps.py +47 -0
  48. omlish/{replserver → diag/replserver}/console.py +18 -20
  49. omlish/{replserver → diag/replserver}/server.py +8 -8
  50. omlish/dispatch/dispatch.py +5 -8
  51. omlish/dispatch/functions.py +1 -1
  52. omlish/dispatch/methods.py +4 -5
  53. omlish/docker.py +1 -1
  54. omlish/dynamic.py +10 -10
  55. omlish/fnpairs.py +400 -0
  56. omlish/graphs/trees.py +13 -13
  57. omlish/inject/__init__.py +7 -7
  58. omlish/inject/elements.py +1 -1
  59. omlish/inject/exceptions.py +7 -7
  60. omlish/inject/impl/elements.py +17 -5
  61. omlish/inject/impl/injector.py +12 -10
  62. omlish/inject/impl/inspect.py +2 -2
  63. omlish/inject/impl/scopes.py +12 -11
  64. omlish/inject/proxy.py +5 -5
  65. omlish/iterators.py +19 -24
  66. omlish/json.py +143 -6
  67. omlish/lang/__init__.py +13 -4
  68. omlish/lang/cached.py +2 -5
  69. omlish/lang/classes/__init__.py +2 -2
  70. omlish/lang/classes/abstract.py +2 -2
  71. omlish/lang/classes/restrict.py +14 -14
  72. omlish/lang/classes/simple.py +1 -1
  73. omlish/lang/classes/virtual.py +5 -5
  74. omlish/lang/clsdct.py +1 -1
  75. omlish/lang/cmp.py +2 -2
  76. omlish/lang/contextmanagers.py +12 -14
  77. omlish/lang/descriptors.py +16 -4
  78. omlish/lang/exceptions.py +1 -1
  79. omlish/lang/functions.py +58 -22
  80. omlish/lang/imports.py +22 -27
  81. omlish/lang/iterables.py +2 -2
  82. omlish/lang/maybes.py +1 -0
  83. omlish/lang/objects.py +15 -9
  84. omlish/lang/resolving.py +1 -1
  85. omlish/lang/strings.py +1 -1
  86. omlish/lang/sys.py +7 -0
  87. omlish/lang/typing.py +3 -3
  88. omlish/libc.py +9 -5
  89. omlish/logs/_abc.py +5 -1
  90. omlish/logs/filters.py +2 -0
  91. omlish/logs/formatters.py +6 -2
  92. omlish/logs/utils.py +1 -1
  93. omlish/marshal/base.py +3 -3
  94. omlish/marshal/exceptions.py +1 -1
  95. omlish/marshal/global_.py +10 -4
  96. omlish/marshal/objects.py +1 -2
  97. omlish/marshal/registries.py +3 -3
  98. omlish/marshal/utils.py +2 -2
  99. omlish/marshal/values.py +1 -1
  100. omlish/math.py +9 -9
  101. omlish/reflect.py +3 -3
  102. omlish/sql/__init__.py +9 -0
  103. omlish/sql/asyncs.py +148 -0
  104. omlish/stats.py +4 -5
  105. omlish/term.py +1 -1
  106. omlish/testing/pydevd.py +28 -6
  107. omlish/testing/pytest/inject/harness.py +1 -1
  108. omlish/testing/pytest/plugins/pydevd.py +1 -1
  109. omlish/testing/pytest/plugins/switches.py +1 -1
  110. omlish/text/delimit.py +3 -6
  111. {omlish-0.0.0.dev2.dist-info → omlish-0.0.0.dev4.dist-info}/METADATA +4 -1
  112. omlish-0.0.0.dev4.dist-info/RECORD +195 -0
  113. {omlish-0.0.0.dev2.dist-info → omlish-0.0.0.dev4.dist-info}/WHEEL +1 -1
  114. omlish/lang/classes/test/test_abstract.py +0 -89
  115. omlish/lang/classes/test/test_restrict.py +0 -71
  116. omlish/lang/classes/test/test_simple.py +0 -58
  117. omlish/lang/classes/test/test_virtual.py +0 -72
  118. omlish-0.0.0.dev2.dist-info/RECORD +0 -193
  119. /omlish/{lang/classes/test → diag}/__init__.py +0 -0
  120. /omlish/{replserver → diag/replserver}/__init__.py +0 -0
  121. /omlish/{replserver → diag/replserver}/__main__.py +0 -0
  122. /omlish/sql/{_abcs.py → _abc.py} +0 -0
  123. {omlish-0.0.0.dev2.dist-info → omlish-0.0.0.dev4.dist-info}/LICENSE +0 -0
  124. {omlish-0.0.0.dev2.dist-info → omlish-0.0.0.dev4.dist-info}/top_level.txt +0 -0
omlish/lang/objects.py CHANGED
@@ -9,18 +9,22 @@ T = ta.TypeVar('T')
9
9
 
10
10
 
11
11
  def attr_repr(obj: ta.Any, *attrs: str) -> str:
12
- return '%s(%s)' % (
13
- type(obj).__name__,
14
- ', '.join('%s=%r' % (attr, getattr(obj, attr)) for attr in attrs))
12
+ return f'{type(obj).__name__}({", ".join(f"{attr}={getattr(obj, attr)!r}" for attr in attrs)})'
15
13
 
16
14
 
17
15
  def arg_repr(*args, **kwargs) -> str:
18
16
  return ', '.join(*(
19
17
  list(map(repr, args)) +
20
- [f'{k}={repr(v)}' for k, v in kwargs.items()]
18
+ [f'{k}={v!r}' for k, v in kwargs.items()]
21
19
  ))
22
20
 
23
21
 
22
+ def opt_repr(obj: ta.Any) -> str | None:
23
+ if obj is None:
24
+ return None
25
+ return repr(obj)
26
+
27
+
24
28
  ##
25
29
 
26
30
 
@@ -29,7 +33,7 @@ def new_type(
29
33
  bases: ta.Sequence[ta.Any],
30
34
  namespace: ta.Mapping[str, ta.Any],
31
35
  **kwargs,
32
- ) -> ta.Type:
36
+ ) -> type:
33
37
  return types.new_class(
34
38
  name,
35
39
  tuple(bases),
@@ -40,12 +44,12 @@ def new_type(
40
44
 
41
45
  def super_meta(
42
46
  super_meta: ta.Any,
43
- meta: ta.Type,
47
+ meta: type,
44
48
  name: str,
45
49
  bases: ta.Sequence[ta.Any],
46
50
  namespace: ta.MutableMapping[str, ta.Any],
47
51
  **kwargs,
48
- ) -> ta.Type:
52
+ ) -> type:
49
53
  """Per types.new_class"""
50
54
  resolved_bases = types.resolve_bases(bases)
51
55
  if resolved_bases is not bases:
@@ -75,19 +79,21 @@ class SimpleProxy(ta.Generic[T]):
75
79
  if instance is None:
76
80
  return self
77
81
  setattr(object.__getattribute__(instance, '__wrapped__'), self._attr, value)
82
+ return None
78
83
 
79
84
  def __delete__(self, instance):
80
85
  if instance is None:
81
86
  return self
82
87
  delattr(object.__getattribute__(instance, '__wrapped__'), self._attr)
88
+ return None
83
89
 
84
- __wrapped_attrs__: ta.Iterable[str] = set()
90
+ __wrapped_attrs__: ta.ClassVar[ta.Iterable[str]] = ()
85
91
 
86
92
  def __init__(self, wrapped: T) -> None:
87
93
  super().__init__()
88
94
  object.__setattr__(self, '__wrapped__', wrapped)
89
95
 
90
- def __init_subclass__(cls, **kwargs):
96
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
91
97
  super().__init_subclass__(**kwargs)
92
98
 
93
99
  for attr in cls.__wrapped_attrs__:
omlish/lang/resolving.py CHANGED
@@ -45,6 +45,6 @@ def get_fqcn_cls(fqcn: str, *, nocheck: bool = False) -> type:
45
45
 
46
46
 
47
47
  class Resolvable:
48
- def __init_subclass__(cls, **kwargs):
48
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
49
49
  super().__init_subclass__(**kwargs)
50
50
  get_cls_fqcn(cls, nocheck=True)
omlish/lang/strings.py CHANGED
@@ -22,7 +22,7 @@ def camel_case(name: str) -> str:
22
22
 
23
23
  def snake_case(name: str) -> str:
24
24
  uppers: list[int | None] = [i for i, c in enumerate(name) if c.isupper()]
25
- return '_'.join([name[l:r].lower() for l, r in zip([None] + uppers, uppers + [None])]).strip('_')
25
+ return '_'.join([name[l:r].lower() for l, r in zip([None, *uppers], [*uppers, None])]).strip('_')
26
26
 
27
27
 
28
28
  def is_dunder(name: str) -> bool:
omlish/lang/sys.py ADDED
@@ -0,0 +1,7 @@
1
+ import sys
2
+
3
+
4
+ def is_gil_enabled() -> bool:
5
+ if (fn := getattr(sys, '_is_gil_enabled', None)) is not None:
6
+ return fn()
7
+ return True
omlish/lang/typing.py CHANGED
@@ -37,13 +37,13 @@ def _update_wrapper_no_anns(wrapper, wrapped):
37
37
  return wrapper
38
38
 
39
39
 
40
- def typed_lambda(ret=_MISSING, **kw):
40
+ def typed_lambda(ret=_MISSING, **kw): # noqa
41
41
  def inner(fn):
42
42
  ns = {}
43
43
  ns['__fn'] = fn
44
44
  proto = ['def __lam(']
45
45
  call = ['return __fn(']
46
- pkw = {k: v for k, v in kw.items()}
46
+ pkw = dict(kw)
47
47
  for i, (n, t) in enumerate(pkw.items()):
48
48
  if i:
49
49
  call.append(', ')
@@ -69,7 +69,7 @@ def typed_lambda(ret=_MISSING, **kw):
69
69
  return inner
70
70
 
71
71
 
72
- def typed_partial(obj, **kw):
72
+ def typed_partial(obj, **kw): # noqa
73
73
  for k in kw:
74
74
  if k.startswith('__'):
75
75
  raise NameError(k)
omlish/libc.py CHANGED
@@ -1,3 +1,7 @@
1
+ # ruff: noqa: ANN201
2
+ # ruff: noqa: N801
3
+ # ruff: noqa: N802
4
+
1
5
  import ctypes as ct
2
6
  import errno
3
7
  import platform
@@ -37,14 +41,14 @@ def lasterr() -> tuple[int, str]:
37
41
 
38
42
 
39
43
  # int raise(int sig);
40
- libc._raise = libc['raise'] # type: ignore
41
- libc._raise.restype = ct.c_int
42
- libc._raise.argtypes = [ct.c_int]
43
- _raise = libc._raise
44
+ libc._raise = libc['raise'] # type: ignore # noqa
45
+ libc._raise.restype = ct.c_int # noqa
46
+ libc._raise.argtypes = [ct.c_int] # noqa
47
+ _raise = libc._raise # noqa
44
48
 
45
49
 
46
50
  def sigtrap() -> None:
47
- libc._raise(signal.SIGTRAP)
51
+ libc._raise(signal.SIGTRAP) # noqa
48
52
 
49
53
 
50
54
  ##
omlish/logs/_abc.py CHANGED
@@ -1,3 +1,7 @@
1
+ # ruff: noqa: A002
2
+ # ruff: noqa: N802
3
+ # ruff: noqa: N815
4
+
1
5
  import types
2
6
  import typing as ta
3
7
 
@@ -181,7 +185,7 @@ class Logger(Filterer, ta.Protocol):
181
185
 
182
186
  def log(self, level: Level, msg: str, *args: ta.Any, **kwargs: ta.Any) -> None: ...
183
187
 
184
- def findCaller(self, stack_info: bool = False, stacklevel: int = 1) -> Caller: ... #
188
+ def findCaller(self, stack_info: bool = False, stacklevel: int = 1) -> Caller: ...
185
189
 
186
190
  def makeRecord(
187
191
  self,
omlish/logs/filters.py CHANGED
@@ -1,3 +1,5 @@
1
+ # ruff: noqa: ANN201
2
+
1
3
  import logging
2
4
  import threading
3
5
 
omlish/logs/formatters.py CHANGED
@@ -1,5 +1,9 @@
1
+ # ruff: noqa: ANN201
2
+ # ruff: noqa: N802
3
+
1
4
  import datetime
2
5
  import logging
6
+ import typing as ta
3
7
 
4
8
  from .. import json
5
9
  from .. import term
@@ -20,7 +24,7 @@ class StandardLogFormatter(logging.Formatter):
20
24
 
21
25
  class ColorLogFormatter(StandardLogFormatter):
22
26
 
23
- LEVEL_COLORS = {
27
+ LEVEL_COLORS: ta.Mapping[int, term.SGRs.FG] = {
24
28
  logging.WARNING: term.SGRs.FG.BRIGHT_YELLOW,
25
29
  logging.ERROR: term.SGRs.FG.BRIGHT_RED,
26
30
  logging.CRITICAL: term.SGRs.FG.BRIGHT_RED,
@@ -39,7 +43,7 @@ class ColorLogFormatter(StandardLogFormatter):
39
43
 
40
44
  class JsonLogFormatter(logging.Formatter):
41
45
 
42
- KEYS = {
46
+ KEYS: ta.Mapping[str, bool] = {
43
47
  'name': False,
44
48
  'msg': False,
45
49
  'args': False,
omlish/logs/utils.py CHANGED
@@ -5,7 +5,7 @@ import logging
5
5
  log = logging.getLogger(__name__)
6
6
 
7
7
 
8
- def error_logging(log=log):
8
+ def error_logging(log=log): # noqa
9
9
  def outer(fn):
10
10
  @functools.wraps(fn)
11
11
  def inner(*args, **kwargs):
omlish/marshal/base.py CHANGED
@@ -81,7 +81,7 @@ from .. import check
81
81
  from .. import collections as col
82
82
  from .. import lang
83
83
  from .. import reflect as rfl
84
- from .exceptions import UnhandledTypeException
84
+ from .exceptions import UnhandledTypeError
85
85
  from .factories import Factory
86
86
  from .factories import RecursiveTypeFactory
87
87
  from .registries import Registry
@@ -152,7 +152,7 @@ class MarshalContext(BaseContext, lang.Final):
152
152
  rty = rfl.type_(o)
153
153
  if (m := check.not_none(self.factory)(self, rty)) is not None: # noqa
154
154
  return m
155
- raise UnhandledTypeException(rty)
155
+ raise UnhandledTypeError(rty)
156
156
 
157
157
 
158
158
  @dc.dataclass(frozen=True)
@@ -163,7 +163,7 @@ class UnmarshalContext(BaseContext, lang.Final):
163
163
  rty = rfl.type_(o)
164
164
  if (m := check.not_none(self.factory)(self, rty)) is not None: # noqa
165
165
  return m
166
- raise UnhandledTypeException(rty)
166
+ raise UnhandledTypeError(rty)
167
167
 
168
168
 
169
169
  ##
@@ -1,7 +1,7 @@
1
1
  from .. import reflect as rfl
2
2
 
3
3
 
4
- class UnhandledTypeException(Exception):
4
+ class UnhandledTypeError(Exception):
5
5
  @property
6
6
  def rty(self) -> rfl.Type:
7
7
  return self.args[0]
omlish/marshal/global_.py CHANGED
@@ -1,8 +1,14 @@
1
- from .registries import Registry
2
- from .standard import new_standard_marshaler_factory
1
+ import typing as ta
2
+
3
3
  from .base import MarshalContext
4
4
  from .base import UnmarshalContext
5
+ from .registries import Registry
6
+ from .standard import new_standard_marshaler_factory
5
7
  from .standard import new_standard_unmarshaler_factory
8
+ from .values import Value
9
+
10
+
11
+ T = ta.TypeVar('T')
6
12
 
7
13
 
8
14
  ##
@@ -17,7 +23,7 @@ GLOBAL_REGISTRY = Registry()
17
23
  GLOBAL_MARSHALER_FACTORY = new_standard_marshaler_factory()
18
24
 
19
25
 
20
- def marshal(obj, ty=None, **kwargs):
26
+ def marshal(obj: ta.Any, ty: type | None = None, **kwargs: ta.Any) -> Value:
21
27
  mc = MarshalContext(GLOBAL_REGISTRY, factory=GLOBAL_MARSHALER_FACTORY, **kwargs)
22
28
  return mc.make(ty if ty is not None else type(obj)).marshal(mc, obj)
23
29
 
@@ -28,6 +34,6 @@ def marshal(obj, ty=None, **kwargs):
28
34
  GLOBAL_UNMARSHALER_FACTORY = new_standard_unmarshaler_factory()
29
35
 
30
36
 
31
- def unmarshal(v, ty, **kwargs):
37
+ def unmarshal(v: Value, ty: type[T], **kwargs: ta.Any) -> T:
32
38
  uc = UnmarshalContext(GLOBAL_REGISTRY, factory=GLOBAL_UNMARSHALER_FACTORY, **kwargs)
33
39
  return uc.make(ty).unmarshal(uc, v)
omlish/marshal/objects.py CHANGED
@@ -98,8 +98,7 @@ class ObjectUnmarshaler(Unmarshaler):
98
98
  if ukf is not None:
99
99
  ukf[ks] = mv # FIXME: unmarshal?
100
100
  continue
101
- else:
102
- raise
101
+ raise
103
102
  if fi.name in kw:
104
103
  raise KeyError(f'Duplicate keys for field {fi.name!r}: {ks!r}')
105
104
  kw[fi.name] = u.unmarshal(ctx, mv)
@@ -1,13 +1,13 @@
1
- import abc
2
1
  import dataclasses as dc
3
2
  import threading
4
3
  import typing as ta
5
4
 
6
5
  from .. import check
6
+ from .. import lang
7
7
  from .. import reflect as rfl
8
8
 
9
9
 
10
- class RegistryItem(abc.ABC):
10
+ class RegistryItem(lang.Abstract):
11
11
  pass
12
12
 
13
13
 
@@ -31,7 +31,7 @@ class Registry:
31
31
  super().__init__()
32
32
  self._mtx = threading.Lock()
33
33
  self._dct: dict[rfl.Type, _TypeRegistry] = {}
34
- self._ps: ta.Sequence['Registry'] = []
34
+ self._ps: ta.Sequence[Registry] = []
35
35
 
36
36
  def register(self, rty: rfl.Type, *items: RegistryItem) -> 'Registry':
37
37
  check.isinstance(rty, rfl.TYPES)
omlish/marshal/utils.py CHANGED
@@ -19,5 +19,5 @@ class _Proxy(ta.Generic[T]):
19
19
  self.__obj = obj
20
20
 
21
21
  @classmethod
22
- def _new(cls):
23
- return (p := cls()), p._set_obj
22
+ def _new(cls) -> tuple[ta.Any, ta.Callable[[ta.Any], None]]:
23
+ return (p := cls()), p._set_obj # noqa
omlish/marshal/values.py CHANGED
@@ -13,7 +13,7 @@ Any
13
13
  import typing as ta
14
14
 
15
15
 
16
- Value = ta.Union[
16
+ Value = ta.Union[ # noqa
17
17
  None,
18
18
 
19
19
  bool,
omlish/math.py CHANGED
@@ -66,7 +66,7 @@ class FixedWidthInt(int):
66
66
 
67
67
  MASK: ta.ClassVar[int]
68
68
 
69
- def __init_subclass__(cls, **kwargs):
69
+ def __init_subclass__(cls) -> None:
70
70
  super().__init_subclass__()
71
71
 
72
72
  if not isinstance(cls.BITS, int):
@@ -82,13 +82,13 @@ class FixedWidthInt(int):
82
82
  cls.MASK = (1 << cls.BITS) - 1
83
83
 
84
84
  @classmethod
85
- def clamp(cls, value):
85
+ def clamp(cls, value: int) -> int:
86
86
  return ((value - cls.MIN) & cls.MASK) + cls.MIN
87
87
 
88
- def __new__(cls, value, *args, **kwargs):
89
- return super().__new__(cls, cls.clamp(value))
88
+ def __new__(cls, value: int) -> 'FixedWidthInt':
89
+ return super().__new__(cls, cls.clamp(value)) # noqa
90
90
 
91
- SCALAR_PROXY_METHODS = {
91
+ SCALAR_PROXY_METHODS = frozenset([
92
92
  '__abs__',
93
93
  '__add__',
94
94
  '__and__',
@@ -117,12 +117,12 @@ class FixedWidthInt(int):
117
117
  '__sub__',
118
118
  '__truediv__',
119
119
  '__xor__',
120
- }
120
+ ])
121
121
 
122
- TUPLE_PROXY_METHODS = {
122
+ TUPLE_PROXY_METHODS = frozenset([
123
123
  '__divmod__',
124
124
  '__rdivmod__',
125
- }
125
+ ])
126
126
 
127
127
  for _proxy_name in SCALAR_PROXY_METHODS:
128
128
  locals()[_proxy_name] = _gen_scalar_proxy_method(_proxy_name)
@@ -130,7 +130,7 @@ class FixedWidthInt(int):
130
130
  locals()[_proxy_name] = _gen_tuple_proxy_method(_proxy_name)
131
131
  del _proxy_name
132
132
 
133
- def __repr__(self):
133
+ def __repr__(self) -> str:
134
134
  return f'{self.__class__.__name__}({int(self)})'
135
135
 
136
136
 
omlish/reflect.py CHANGED
@@ -21,7 +21,7 @@ else:
21
21
 
22
22
  _NoneType = types.NoneType # type: ignore
23
23
 
24
- _NONE_TYPE_FROZENSET: ta.FrozenSet['Type'] = frozenset([_NoneType])
24
+ _NONE_TYPE_FROZENSET: frozenset['Type'] = frozenset([_NoneType])
25
25
 
26
26
 
27
27
  _GenericAlias = ta._GenericAlias # type: ignore # noqa
@@ -99,7 +99,7 @@ Type = ta.Union[
99
99
 
100
100
 
101
101
  class Union(ta.NamedTuple):
102
- args: ta.FrozenSet[Type]
102
+ args: frozenset[Type]
103
103
 
104
104
  @property
105
105
  def is_optional(self) -> bool:
@@ -121,7 +121,7 @@ class Generic(ta.NamedTuple):
121
121
  # params2: tuple[ta.TypeVar, ...] # map[int, V] = (V,) | map[T, T] = (T,)
122
122
  obj: ta.Any
123
123
 
124
- def __repr__(self):
124
+ def __repr__(self) -> str:
125
125
  return (
126
126
  f'{self.__class__.__name__}('
127
127
  f'cls={self.cls.__name__}, '
omlish/sql/__init__.py CHANGED
@@ -0,0 +1,9 @@
1
+ from .asyncs import ( # noqa
2
+ AsyncConnection,
3
+ AsyncConnectionLike,
4
+ AsyncEngine,
5
+ AsyncEngineLike,
6
+ AsyncTransaction,
7
+ AsyncTransactionLike,
8
+ async_adapt,
9
+ )
omlish/sql/asyncs.py ADDED
@@ -0,0 +1,148 @@
1
+ """
2
+ TODO:
3
+ - Maysync impls?
4
+ - base Protocol so adapters and real sa impls can be used interchangeably (if in asyncio ctx)?
5
+ """
6
+ import contextlib
7
+ import typing as ta
8
+
9
+ import sqlalchemy as sa
10
+ import sqlalchemy.ext.asyncio as saa
11
+
12
+ from .. import asyncs as au
13
+
14
+
15
+ T = ta.TypeVar('T')
16
+ P = ta.ParamSpec('P')
17
+
18
+
19
+ AsyncEngineLike = ta.Union[saa.AsyncEngine, 'AsyncEngine']
20
+ AsyncConnectionLike = ta.Union[saa.AsyncConnection, 'AsyncConnection']
21
+ AsyncTransactionLike = ta.Union[saa.AsyncTransaction, 'AsyncTransaction']
22
+
23
+
24
+ class AsyncTransaction:
25
+ def __init__(self, underlying: saa.AsyncTransaction) -> None:
26
+ super().__init__()
27
+ self._underlying = underlying
28
+
29
+ @property
30
+ def underlying(self) -> saa.AsyncTransaction:
31
+ return self._underlying
32
+
33
+ ##
34
+
35
+ @au.mark_asyncio
36
+ async def close(self) -> None:
37
+ await au.from_asyncio(self._underlying.close)()
38
+
39
+ @au.mark_asyncio
40
+ async def rollback(self) -> None:
41
+ await au.from_asyncio(self._underlying.rollback)()
42
+
43
+ @au.mark_asyncio
44
+ async def commit(self) -> None:
45
+ await au.from_asyncio(self._underlying.commit)()
46
+
47
+
48
+ class AsyncConnection:
49
+ def __init__(self, underlying: saa.AsyncConnection) -> None:
50
+ super().__init__()
51
+ self._underlying = underlying
52
+
53
+ @property
54
+ def underlying(self) -> saa.AsyncConnection:
55
+ return self._underlying
56
+
57
+ ##
58
+
59
+ @contextlib.asynccontextmanager
60
+ @au.mark_asyncio
61
+ async def begin(self) -> ta.AsyncIterator[AsyncTransaction]:
62
+ async with au.from_asyncio_context(self._underlying.begin()) as u:
63
+ yield AsyncTransaction(u)
64
+
65
+ @au.mark_asyncio
66
+ async def execute(
67
+ self,
68
+ statement: ta.Any,
69
+ *args: ta.Any,
70
+ **kwargs: ta.Any,
71
+ ) -> sa.CursorResult[ta.Any]:
72
+ return await au.from_asyncio(self._underlying.execute)(statement, *args, **kwargs)
73
+
74
+ @au.mark_asyncio
75
+ async def run_sync(
76
+ self,
77
+ fn: ta.Callable[ta.Concatenate[sa.Connection, P], T],
78
+ *args: P.args,
79
+ **kwargs: P.kwargs,
80
+ ) -> T:
81
+ return await au.from_asyncio(self._underlying.run_sync)(fn, *args, **kwargs)
82
+
83
+
84
+ class AsyncEngine:
85
+ def __init__(self, underlying: saa.AsyncEngine) -> None:
86
+ super().__init__()
87
+ self._underlying = underlying
88
+
89
+ @property
90
+ def underlying(self) -> saa.AsyncEngine:
91
+ return self._underlying
92
+
93
+ ##
94
+
95
+ @contextlib.asynccontextmanager
96
+ @au.mark_asyncio
97
+ async def connect(self) -> ta.AsyncIterator[AsyncConnection]:
98
+ async with au.from_asyncio_context(self._underlying.connect()) as u:
99
+ yield AsyncConnection(u)
100
+
101
+ @au.mark_asyncio
102
+ async def dispose(self, close: bool = True) -> None:
103
+ await au.from_asyncio(self._underlying.dispose)(close)
104
+
105
+
106
+ ##
107
+
108
+
109
+ @ta.overload
110
+ def async_adapt(obj: AsyncEngine) -> AsyncEngine:
111
+ ...
112
+
113
+
114
+ @ta.overload
115
+ def async_adapt(obj: AsyncConnection) -> AsyncConnection:
116
+ ...
117
+
118
+
119
+ @ta.overload
120
+ def async_adapt(obj: AsyncTransaction) -> AsyncTransaction:
121
+ ...
122
+
123
+
124
+ @ta.overload
125
+ def async_adapt(obj: saa.AsyncEngine) -> AsyncEngine:
126
+ ...
127
+
128
+
129
+ @ta.overload
130
+ def async_adapt(obj: saa.AsyncConnection) -> AsyncConnection:
131
+ ...
132
+
133
+
134
+ @ta.overload
135
+ def async_adapt(obj: saa.AsyncTransaction) -> AsyncTransaction:
136
+ ...
137
+
138
+
139
+ def async_adapt(obj: ta.Any) -> ta.Any:
140
+ if isinstance(obj, (AsyncEngine, AsyncConnection, AsyncTransaction)):
141
+ return obj
142
+ if isinstance(obj, saa.AsyncTransaction):
143
+ return AsyncTransaction(obj)
144
+ if isinstance(obj, saa.AsyncConnection):
145
+ return AsyncConnection(obj)
146
+ if isinstance(obj, saa.AsyncEngine):
147
+ return AsyncEngine(obj)
148
+ raise TypeError(obj)
omlish/stats.py CHANGED
@@ -5,6 +5,7 @@ TODO:
5
5
  """
6
6
  import bisect
7
7
  import collections
8
+ import contextlib
8
9
  import dataclasses as dc
9
10
  import math
10
11
  import operator
@@ -215,7 +216,7 @@ class Stats(ta.Sequence[float]):
215
216
  else:
216
217
  bins = [float(x) for x in bins]
217
218
  if self.min < bins[0]:
218
- bins = [self.min] + bins
219
+ bins = [self.min, *bins]
219
220
 
220
221
  round_factor = 10.0 ** bin_digits
221
222
  bins = [math.floor(b * round_factor) / round_factor for b in bins]
@@ -251,7 +252,7 @@ class SamplingHistogram:
251
252
  sample_percentiles: list['SamplingHistogram.Percentile']
252
253
 
253
254
  DEFAULT_SIZE = 1000
254
- DEFAULT_PERCENTILES = [0.5, 0.75, 0.9, 0.95, 0.99]
255
+ DEFAULT_PERCENTILES = (0.5, 0.75, 0.9, 0.95, 0.99)
255
256
 
256
257
  def __init__(
257
258
  self,
@@ -291,10 +292,8 @@ class SamplingHistogram:
291
292
 
292
293
  sample_pos = None
293
294
  if self._sample_pos_queue:
294
- try:
295
+ with contextlib.suppress(IndexError):
295
296
  sample_pos = self._sample_pos_queue.pop()
296
- except IndexError:
297
- pass
298
297
  if sample_pos is None:
299
298
  sample_pos = random.randrange(0, self._size)
300
299
  self._sample[sample_pos] = entry
omlish/term.py CHANGED
@@ -208,7 +208,7 @@ BG24_RGB = ControlSequence(
208
208
  '24-Bit Background Color (RGB)')
209
209
 
210
210
 
211
- def main():
211
+ def main() -> None:
212
212
  import sys
213
213
 
214
214
  sys.stdout.write(SGR(SGRs.RESET))