omlish 0.0.0.dev4__py3-none-any.whl → 0.0.0.dev5__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 (86) hide show
  1. omlish/__about__.py +1 -1
  2. omlish/__init__.py +1 -1
  3. omlish/asyncs/__init__.py +1 -4
  4. omlish/asyncs/anyio.py +66 -0
  5. omlish/asyncs/flavors.py +27 -1
  6. omlish/asyncs/trio_asyncio.py +24 -18
  7. omlish/c3.py +1 -1
  8. omlish/cached.py +1 -2
  9. omlish/collections/__init__.py +4 -1
  10. omlish/collections/cache/impl.py +1 -1
  11. omlish/collections/indexed.py +1 -1
  12. omlish/collections/utils.py +38 -6
  13. omlish/configs/__init__.py +5 -0
  14. omlish/configs/classes.py +53 -0
  15. omlish/configs/dotenv.py +586 -0
  16. omlish/configs/props.py +589 -49
  17. omlish/dataclasses/impl/api.py +1 -1
  18. omlish/dataclasses/impl/as_.py +1 -1
  19. omlish/dataclasses/impl/fields.py +1 -0
  20. omlish/dataclasses/impl/init.py +1 -1
  21. omlish/dataclasses/impl/main.py +1 -0
  22. omlish/dataclasses/impl/metaclass.py +6 -1
  23. omlish/dataclasses/impl/order.py +1 -1
  24. omlish/dataclasses/impl/reflect.py +15 -2
  25. omlish/defs.py +1 -1
  26. omlish/diag/procfs.py +29 -1
  27. omlish/diag/procstats.py +32 -0
  28. omlish/diag/replserver/console.py +3 -3
  29. omlish/diag/replserver/server.py +6 -5
  30. omlish/diag/threads.py +86 -0
  31. omlish/docker.py +19 -0
  32. omlish/fnpairs.py +26 -18
  33. omlish/graphs/dags.py +113 -0
  34. omlish/graphs/domination.py +268 -0
  35. omlish/graphs/trees.py +2 -2
  36. omlish/http/__init__.py +25 -0
  37. omlish/http/asgi.py +131 -0
  38. omlish/http/consts.py +31 -4
  39. omlish/http/cookies.py +194 -0
  40. omlish/http/dates.py +70 -0
  41. omlish/http/encodings.py +6 -0
  42. omlish/http/json.py +273 -0
  43. omlish/http/sessions.py +197 -0
  44. omlish/inject/__init__.py +8 -2
  45. omlish/inject/bindings.py +3 -3
  46. omlish/inject/exceptions.py +3 -3
  47. omlish/inject/impl/elements.py +33 -24
  48. omlish/inject/impl/injector.py +1 -0
  49. omlish/inject/impl/multis.py +74 -0
  50. omlish/inject/impl/providers.py +19 -39
  51. omlish/inject/{proxy.py → impl/proxy.py} +2 -2
  52. omlish/inject/impl/scopes.py +1 -0
  53. omlish/inject/injector.py +1 -0
  54. omlish/inject/keys.py +3 -9
  55. omlish/inject/multis.py +70 -0
  56. omlish/inject/providers.py +23 -23
  57. omlish/inject/scopes.py +7 -3
  58. omlish/inject/types.py +0 -8
  59. omlish/iterators.py +13 -0
  60. omlish/json.py +2 -1
  61. omlish/lang/__init__.py +4 -0
  62. omlish/lang/classes/restrict.py +1 -1
  63. omlish/lang/classes/virtual.py +2 -2
  64. omlish/lang/contextmanagers.py +64 -0
  65. omlish/lang/datetimes.py +6 -5
  66. omlish/lang/functions.py +10 -0
  67. omlish/lang/imports.py +11 -2
  68. omlish/lang/typing.py +1 -0
  69. omlish/logs/utils.py +1 -1
  70. omlish/marshal/datetimes.py +1 -1
  71. omlish/reflect.py +8 -2
  72. omlish/sync.py +70 -0
  73. omlish/term.py +6 -1
  74. omlish/testing/pytest/__init__.py +5 -0
  75. omlish/testing/pytest/helpers.py +0 -24
  76. omlish/testing/pytest/inject/harness.py +1 -1
  77. omlish/testing/pytest/marks.py +48 -0
  78. omlish/testing/pytest/plugins/__init__.py +2 -0
  79. omlish/testing/pytest/plugins/managermarks.py +60 -0
  80. omlish/testing/testing.py +10 -0
  81. omlish/text/delimit.py +4 -0
  82. {omlish-0.0.0.dev4.dist-info → omlish-0.0.0.dev5.dist-info}/METADATA +1 -1
  83. {omlish-0.0.0.dev4.dist-info → omlish-0.0.0.dev5.dist-info}/RECORD +86 -69
  84. {omlish-0.0.0.dev4.dist-info → omlish-0.0.0.dev5.dist-info}/WHEEL +1 -1
  85. {omlish-0.0.0.dev4.dist-info → omlish-0.0.0.dev5.dist-info}/LICENSE +0 -0
  86. {omlish-0.0.0.dev4.dist-info → omlish-0.0.0.dev5.dist-info}/top_level.txt +0 -0
@@ -1,3 +1,4 @@
1
+ import abc
1
2
  import contextlib
2
3
  import contextvars
3
4
  import functools
@@ -50,6 +51,69 @@ NOP_CONTEXT_MANAGER = NopContextManager()
50
51
  ##
51
52
 
52
53
 
54
+ class ContextManager(abc.ABC, ta.Generic[T]):
55
+
56
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
57
+ super().__init_subclass__(**kwargs)
58
+
59
+ if not hasattr(cls.__contextmanager__, '_is_contextmanager'):
60
+ cls.__contextmanager__ = contextlib.contextmanager(cls.__contextmanager__) # type: ignore # noqa
61
+ cls.__contextmanager__._is_contextmanager = True # type: ignore # noqa
62
+
63
+ @abc.abstractmethod
64
+ def __contextmanager__(self) -> ta.Iterable[T]:
65
+ raise NotImplementedError
66
+
67
+ __contextmanager__._is_contextmanager = True # type: ignore # noqa
68
+
69
+ _contextmanager: ta.Any
70
+
71
+ def __enter__(self) -> T:
72
+ self._contextmanager = self.__contextmanager__()
73
+ return self._contextmanager.__enter__() # type: ignore
74
+
75
+ def __exit__(
76
+ self,
77
+ exc_type: type[BaseException] | None,
78
+ exc_val: BaseException | None,
79
+ exc_tb: types.TracebackType | None,
80
+ ) -> None:
81
+ return self._contextmanager.__exit__(exc_type, exc_val, exc_tb)
82
+
83
+
84
+ class AsyncContextManager(abc.ABC, ta.Generic[T]):
85
+
86
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
87
+ super().__init_subclass__(**kwargs)
88
+
89
+ if not hasattr(cls.__asynccontextmanager__, '_is_asynccontextmanager'):
90
+ cls.__asynccontextmanager__ = contextlib.asynccontextmanager(cls.__asynccontextmanager__) # type: ignore # noqa
91
+ cls.__asynccontextmanager__._is_asynccontextmanager = True # type: ignore # noqa
92
+
93
+ @abc.abstractmethod
94
+ def __asynccontextmanager__(self) -> ta.AsyncIterator[T]:
95
+ raise NotImplementedError
96
+
97
+ __asynccontextmanager__._is_asynccontextmanager = True # type: ignore # noqa
98
+
99
+ _asynccontextmanager: ta.Any
100
+
101
+ async def __aenter__(self) -> T:
102
+ self._asynccontextmanager = self.__asynccontextmanager__()
103
+ return await self._asynccontextmanager.__aenter__() # type: ignore
104
+
105
+ async def __aexit__(
106
+ self,
107
+ exc_type: type[BaseException] | None,
108
+ exc_val: BaseException | None,
109
+ exc_tb: types.TracebackType | None,
110
+ ) -> None:
111
+ return await self._asynccontextmanager.__aexit__(exc_type, exc_val, exc_tb)
112
+
113
+
114
+ ##
115
+
116
+
53
117
  @contextlib.contextmanager
54
118
  def defer(fn: ta.Callable) -> ta.Generator[ta.Callable, None, None]:
55
119
  try:
omlish/lang/datetimes.py CHANGED
@@ -18,17 +18,18 @@ def months_ago(date: datetime.date, num: int) -> datetime.date:
18
18
  return datetime.date(ago_year, ago_month, 1)
19
19
 
20
20
 
21
- def parse_date(s: str) -> datetime.date:
21
+ def parse_date(s: str, tz: datetime.timezone | None = None) -> datetime.date:
22
+
22
23
  if s.lower() in ['today', 'now']:
23
- return datetime.date.today()
24
+ return datetime.datetime.now(tz=tz)
24
25
  elif s.lower() == 'yesterday':
25
- return datetime.date.today() - datetime.timedelta(days=1)
26
+ return datetime.datetime.now(tz=tz) - datetime.timedelta(days=1)
26
27
  elif s.lower().endswith(' days ago'):
27
28
  num = int(s.split(' ', 1)[0])
28
- return datetime.date.today() - datetime.timedelta(days=num)
29
+ return datetime.datetime.now(tz=tz) - datetime.timedelta(days=num)
29
30
  elif s.lower().endswith(' months ago'):
30
31
  months = int(s.split(' ', 1)[0])
31
- return months_ago(datetime.date.today(), months)
32
+ return months_ago(datetime.datetime.now(tz=tz), months)
32
33
  else:
33
34
  return datetime.date(*map(int, s.split('-', 3)))
34
35
 
omlish/lang/functions.py CHANGED
@@ -148,6 +148,16 @@ def void(*args: ta.Any, **kwargs: ta.Any) -> ta.NoReturn:
148
148
  ##
149
149
 
150
150
 
151
+ def as_async(fn: ta.Callable[P, T]) -> ta.Callable[P, ta.Awaitable[T]]:
152
+ @functools.wraps(fn)
153
+ async def inner(*args, **kwargs):
154
+ return fn(*args, **kwargs)
155
+ return inner
156
+
157
+
158
+ ##
159
+
160
+
151
161
  _MISSING = object()
152
162
 
153
163
 
omlish/lang/imports.py CHANGED
@@ -1,6 +1,6 @@
1
1
  import contextlib
2
2
  import functools
3
- import importlib.resources
3
+ import importlib.util
4
4
  import sys
5
5
  import types
6
6
  import typing as ta
@@ -11,6 +11,13 @@ from .cached import cached_function
11
11
  ##
12
12
 
13
13
 
14
+ def can_import(name: str, package: str | None = None) -> bool:
15
+ return importlib.util.find_spec(name, package) is not None
16
+
17
+
18
+ ##
19
+
20
+
14
21
  def lazy_import(name: str, package: str | None = None) -> ta.Callable[[], ta.Any]:
15
22
  return cached_function(functools.partial(importlib.import_module, name, package=package))
16
23
 
@@ -66,6 +73,8 @@ def yield_importable(
66
73
  filter: ta.Callable[[str], bool] | None = None, # noqa
67
74
  include_special: bool = False,
68
75
  ) -> ta.Iterator[str]:
76
+ from importlib import resources
77
+
69
78
  def rec(cur):
70
79
  if cur.split('.')[-1] == '__pycache__':
71
80
  return
@@ -83,7 +92,7 @@ def yield_importable(
83
92
  if getattr(module, '__file__', None) is None:
84
93
  return
85
94
 
86
- for file in importlib.resources.files(cur).iterdir():
95
+ for file in resources.files(cur).iterdir():
87
96
  if file.is_file() and file.name.endswith('.py'):
88
97
  if not (include_special or file.name not in SPECIAL_IMPORTABLE):
89
98
  continue
omlish/lang/typing.py CHANGED
@@ -3,6 +3,7 @@ TODO:
3
3
  - typed_lambda is really kinda just 'annotate'
4
4
  - TypedLambda / TypedPartial classes, picklable, reprs
5
5
  - probably need to gen types per inst
6
+ - typed_factory
6
7
  """
7
8
  import functools
8
9
  import inspect
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
@@ -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/reflect.py CHANGED
@@ -1,5 +1,6 @@
1
1
  """
2
2
  TODO:
3
+ - callables..
3
4
  - uniform collection isinstance - items() for mappings, iter() for other
4
5
  - also check instance type in isinstance not just items lol
5
6
  - ta.Generic in mro causing trouble - omit? no longer 1:1
@@ -7,12 +8,13 @@ TODO:
7
8
  - cache __hash__ in Generic/Union
8
9
  """
9
10
  import collections.abc
10
- import typing as ta
11
11
  import types
12
+ import typing as ta
12
13
 
13
14
  from . import c3
14
15
  from . import lang
15
16
 
17
+
16
18
  if ta.TYPE_CHECKING:
17
19
  from .collections import cache
18
20
  else:
@@ -25,6 +27,7 @@ _NONE_TYPE_FROZENSET: frozenset['Type'] = frozenset([_NoneType])
25
27
 
26
28
 
27
29
  _GenericAlias = ta._GenericAlias # type: ignore # noqa
30
+ # _CallableGenericAlias = ta._CallableGenericAlias # type: ignore # noqa
28
31
  _SpecialGenericAlias = ta._SpecialGenericAlias # type: ignore # noqa
29
32
  _UnionGenericAlias = ta._UnionGenericAlias # type: ignore # noqa
30
33
 
@@ -155,7 +158,10 @@ def type_(obj: ta.Any) -> Type:
155
158
  if isinstance(obj, ta.NewType): # noqa
156
159
  return NewType(oty)
157
160
 
158
- if oty is _GenericAlias or oty is ta.GenericAlias: # type: ignore # noqa
161
+ if (
162
+ oty is _GenericAlias or
163
+ oty is ta.GenericAlias # type: ignore # noqa
164
+ ):
159
165
  origin = ta.get_origin(obj)
160
166
  args = ta.get_args(obj)
161
167
  if origin is ta.Generic:
omlish/sync.py ADDED
@@ -0,0 +1,70 @@
1
+ """
2
+ TODO:
3
+ - sync (lol) w/ asyncs.anyio
4
+ - atomics
5
+ """
6
+ import threading
7
+ import typing as ta
8
+
9
+ from . import lang
10
+
11
+
12
+ T = ta.TypeVar('T')
13
+
14
+
15
+ class Once:
16
+ def __init__(self) -> None:
17
+ super().__init__()
18
+ self._done = False
19
+ self._lock = threading.Lock()
20
+
21
+ def do(self, fn: ta.Callable[[], None]) -> bool:
22
+ if self._done:
23
+ return False
24
+ with self._lock:
25
+ if self._done:
26
+ return False # type: ignore
27
+ try:
28
+ fn()
29
+ finally:
30
+ self._done = True
31
+ return True
32
+
33
+
34
+ class Lazy(ta.Generic[T]):
35
+ def __init__(self) -> None:
36
+ super().__init__()
37
+ self._once = Once()
38
+ self._v: lang.Maybe[T] = lang.empty()
39
+
40
+ def peek(self) -> lang.Maybe[T]:
41
+ return self._v
42
+
43
+ def set(self, v: T) -> None:
44
+ self._v = lang.just(v)
45
+
46
+ def get(self, fn: ta.Callable[[], T]) -> T:
47
+ def do():
48
+ self._v = lang.just(fn())
49
+ self._once.do(do)
50
+ return self._v.must()
51
+
52
+
53
+ class LazyFn(ta.Generic[T]):
54
+ def __init__(self, fn: ta.Callable[[], T]) -> None:
55
+ super().__init__()
56
+ self._fn = fn
57
+ self._once = Once()
58
+ self._v: lang.Maybe[T] = lang.empty()
59
+
60
+ def peek(self) -> lang.Maybe[T]:
61
+ return self._v
62
+
63
+ def set(self, v: T) -> None:
64
+ self._v = lang.just(v)
65
+
66
+ def get(self) -> T:
67
+ def do():
68
+ self._v = lang.just(self._fn())
69
+ self._once.do(do)
70
+ return self._v.must()
omlish/term.py CHANGED
@@ -213,8 +213,13 @@ def main() -> None:
213
213
 
214
214
  sys.stdout.write(SGR(SGRs.RESET))
215
215
  sys.stdout.write(BG8(15) + ' ')
216
- for i in [196, 160, 124, 88, 52, 16]:
216
+ for i in range(20):
217
217
  sys.stdout.write(BG8(i) + ' ')
218
+ sys.stdout.write('\n')
219
+ for i in range(256):
220
+ if i % 12 == 0:
221
+ sys.stdout.write('\n')
222
+ sys.stdout.write(BG8(i + 16) + ' ')
218
223
  sys.stdout.write(SGR(SGRs.RESET) + '\n')
219
224
 
220
225
 
@@ -2,7 +2,12 @@ from . import plugins # noqa
2
2
 
3
3
  from .helpers import ( # noqa
4
4
  assert_raises_star,
5
+ )
6
+
7
+ from .marks import ( # noqa
8
+ drain_asyncio,
5
9
  skip_if_cant_import,
10
+ skip_if_nogil,
6
11
  skip_if_not_on_path,
7
12
  skip_if_python_version_less_than,
8
13
  )
@@ -1,28 +1,4 @@
1
1
  import contextlib
2
- import shutil
3
- import sys
4
- import typing as ta
5
-
6
- import pytest
7
-
8
- from ..testing import can_import
9
-
10
-
11
- def skip_if_cant_import(module: str, *args, **kwargs):
12
- return pytest.mark.skipif(not can_import(module, *args, **kwargs), reason=f'requires import {module}')
13
-
14
-
15
- def skip_if_not_on_path(exe: str):
16
- return pytest.mark.skipif(shutil.which(exe) is None, reason=f'requires exe on path {exe}')
17
-
18
-
19
- def skip_if_python_version_less_than(num: ta.Sequence[int]):
20
- return pytest.mark.skipif(sys.version_info < tuple(num), reason=f'python version {tuple(sys.version_info)} < {tuple(num)}') # noqa
21
-
22
-
23
- def skip_if_not_single():
24
- # [resolve_collection_argument(a) for a in session.config.args]
25
- raise NotImplementedError
26
2
 
27
3
 
28
4
  @contextlib.contextmanager
@@ -4,10 +4,10 @@ import typing as ta
4
4
 
5
5
  import pytest
6
6
 
7
- from .. import plugins
8
7
  from .... import check
9
8
  from .... import inject as inj
10
9
  from .... import lang
10
+ from .. import plugins
11
11
 
12
12
 
13
13
  T = ta.TypeVar('T')
@@ -0,0 +1,48 @@
1
+ import shutil
2
+ import sys
3
+ import sysconfig
4
+ import typing as ta
5
+
6
+ import pytest
7
+
8
+ from ... import lang # noqa
9
+ from ..testing import can_import
10
+ from .plugins.managermarks import ManagerMark # noqa
11
+
12
+
13
+ if ta.TYPE_CHECKING:
14
+ import asyncio
15
+ else:
16
+ asyncio = lang.proxy_import('asyncio')
17
+
18
+
19
+ def skip_if_cant_import(module: str, *args, **kwargs):
20
+ return pytest.mark.skipif(not can_import(module, *args, **kwargs), reason=f'requires import {module}')
21
+
22
+
23
+ def skip_if_not_on_path(exe: str):
24
+ return pytest.mark.skipif(shutil.which(exe) is None, reason=f'requires exe on path {exe}')
25
+
26
+
27
+ def skip_if_python_version_less_than(num: ta.Sequence[int]):
28
+ return pytest.mark.skipif(sys.version_info < tuple(num), reason=f'python version {tuple(sys.version_info)} < {tuple(num)}') # noqa
29
+
30
+
31
+ def skip_if_not_single():
32
+ # FIXME
33
+ # [resolve_collection_argument(a) for a in session.config.args]
34
+ raise NotImplementedError
35
+
36
+
37
+ def skip_if_nogil():
38
+ return pytest.mark.skipif(sysconfig.get_config_var('Py_GIL_DISABLED'), reason='requires gil build')
39
+
40
+
41
+ class drain_asyncio(ManagerMark): # noqa
42
+ def __call__(self, item: pytest.Function) -> ta.Iterator[None]:
43
+ loop = asyncio.get_event_loop()
44
+ try:
45
+ yield
46
+ finally:
47
+ while loop._ready or loop._scheduled: # type: ignore # noqa
48
+ loop._run_once() # type: ignore # noqa
@@ -1,11 +1,13 @@
1
1
  from . import ( # noqa
2
2
  logging,
3
+ managermarks,
3
4
  pydevd,
4
5
  repeat,
5
6
  skips,
6
7
  spacing,
7
8
  switches,
8
9
  )
10
+
9
11
  from ._registry import ( # noqa
10
12
  ALL,
11
13
  register,
@@ -0,0 +1,60 @@
1
+ import abc
2
+ import contextlib
3
+ import typing as ta
4
+
5
+ import pytest
6
+
7
+ from .... import lang
8
+ from ._registry import register
9
+
10
+
11
+ class ManagerMark(lang.Abstract):
12
+ def __init_subclass__(cls, **kwargs):
13
+ super().__init_subclass__(**kwargs)
14
+
15
+ @abc.abstractmethod
16
+ def __call__(self, item: pytest.Function) -> ta.Iterator[None]:
17
+ raise NotImplementedError
18
+
19
+
20
+ def _deep_subclasses(cls):
21
+ ret = set()
22
+
23
+ def rec(cur):
24
+ for nxt in cur.__subclasses__():
25
+ if nxt not in ret:
26
+ ret.add(nxt)
27
+ rec(nxt)
28
+
29
+ rec(cls)
30
+ return ret
31
+
32
+
33
+ @register
34
+ class ManagerMarksPlugin:
35
+
36
+ @lang.cached_function
37
+ def mark_classes(self) -> ta.Mapping[str, type[ManagerMark]]:
38
+ return {
39
+ cls.__name__: cls
40
+ for cls in _deep_subclasses(ManagerMark)
41
+ if not lang.is_abstract_class(cls)
42
+ }
43
+
44
+ def pytest_configure(self, config):
45
+ for n in self.mark_classes():
46
+ config.addinivalue_line(
47
+ 'markers',
48
+ f'{n}: mark to manage {n}',
49
+ )
50
+
51
+ @pytest.hookimpl(hookwrapper=True)
52
+ def pytest_runtest_call(self, item):
53
+ with contextlib.ExitStack() as es:
54
+ for n, cls in self.mark_classes().items():
55
+ if (m := item.get_closest_marker(n)) is None:
56
+ continue
57
+ inst = cls(*m.args, **m.kwargs)
58
+ es.enter_context(contextlib.contextmanager(inst)(item))
59
+
60
+ yield
omlish/testing/testing.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import functools
2
2
  import importlib
3
3
  import os
4
+ import sys
4
5
  import threading
5
6
  import time
6
7
  import traceback
@@ -100,3 +101,12 @@ def xfail(fn):
100
101
  traceback.print_exc()
101
102
 
102
103
  return inner
104
+
105
+
106
+ def raise_in_thread(thr: threading.Thread, exc: BaseException | type[BaseException]) -> None:
107
+ if sys.implementation.name != 'cpython':
108
+ raise RuntimeError(sys.implementation.name)
109
+
110
+ # https://github.com/python/cpython/blob/37ba7531a59a0a2b240a86f7e2adfb1b1cd8ac0c/Lib/test/test_threading.py#L182
111
+ import ctypes as ct
112
+ ct.pythonapi.PyThreadState_SetAsyncExc(ct.c_ulong(thr.ident), ct.py_object(exc)) # type: ignore
omlish/text/delimit.py CHANGED
@@ -1,3 +1,7 @@
1
+ """
2
+ TODO:
3
+ - replace with codecs.StreamReader
4
+ """
1
5
  import io
2
6
  import typing as ta
3
7
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omlish
3
- Version: 0.0.0.dev4
3
+ Version: 0.0.0.dev5
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause