omlish 0.0.0.dev447__py3-none-any.whl → 0.0.0.dev484__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 (226) hide show
  1. omlish/.omlish-manifests.json +12 -0
  2. omlish/__about__.py +15 -15
  3. omlish/argparse/all.py +17 -9
  4. omlish/argparse/cli.py +16 -3
  5. omlish/argparse/utils.py +21 -0
  6. omlish/asyncs/asyncio/rlock.py +110 -0
  7. omlish/asyncs/asyncio/sync.py +43 -0
  8. omlish/asyncs/asyncio/utils.py +2 -0
  9. omlish/asyncs/sync.py +25 -0
  10. omlish/bootstrap/_marshal.py +1 -1
  11. omlish/bootstrap/diag.py +12 -21
  12. omlish/bootstrap/main.py +2 -5
  13. omlish/bootstrap/sys.py +27 -28
  14. omlish/cexts/__init__.py +0 -0
  15. omlish/cexts/include/omlish/omlish.hh +1 -0
  16. omlish/collections/__init__.py +13 -1
  17. omlish/collections/attrregistry.py +210 -0
  18. omlish/collections/cache/impl.py +1 -0
  19. omlish/collections/identity.py +1 -0
  20. omlish/collections/mappings.py +28 -0
  21. omlish/collections/trie.py +5 -1
  22. omlish/collections/utils.py +77 -0
  23. omlish/concurrent/all.py +2 -1
  24. omlish/concurrent/futures.py +25 -0
  25. omlish/concurrent/threadlets.py +1 -1
  26. omlish/daemons/reparent.py +2 -3
  27. omlish/daemons/spawning.py +2 -3
  28. omlish/dataclasses/__init__.py +2 -0
  29. omlish/dataclasses/impl/api/classes/decorator.py +3 -0
  30. omlish/dataclasses/impl/api/classes/make.py +3 -0
  31. omlish/dataclasses/impl/concerns/repr.py +15 -2
  32. omlish/dataclasses/impl/configs.py +97 -37
  33. omlish/dataclasses/impl/generation/compilation.py +21 -19
  34. omlish/dataclasses/impl/generation/globals.py +1 -0
  35. omlish/dataclasses/impl/generation/ops.py +1 -0
  36. omlish/dataclasses/impl/generation/processor.py +105 -24
  37. omlish/dataclasses/impl/processing/base.py +8 -0
  38. omlish/dataclasses/impl/processing/driving.py +8 -8
  39. omlish/dataclasses/specs.py +1 -0
  40. omlish/dataclasses/tools/modifiers.py +5 -0
  41. omlish/diag/cmds/__init__.py +0 -0
  42. omlish/diag/{lslocks.py → cmds/lslocks.py} +6 -6
  43. omlish/diag/{lsof.py → cmds/lsof.py} +6 -6
  44. omlish/diag/{ps.py → cmds/ps.py} +6 -6
  45. omlish/diag/pycharm.py +16 -2
  46. omlish/diag/pydevd.py +58 -40
  47. omlish/diag/replserver/console.py +1 -1
  48. omlish/dispatch/__init__.py +18 -12
  49. omlish/dispatch/methods.py +50 -140
  50. omlish/dom/rendering.py +1 -1
  51. omlish/formats/dotenv.py +1 -1
  52. omlish/formats/json/stream/__init__.py +13 -0
  53. omlish/funcs/guard.py +225 -0
  54. omlish/graphs/dot/rendering.py +1 -1
  55. omlish/http/all.py +44 -4
  56. omlish/http/clients/asyncs.py +33 -27
  57. omlish/http/clients/base.py +17 -1
  58. omlish/http/clients/coro/__init__.py +0 -0
  59. omlish/http/clients/coro/sync.py +171 -0
  60. omlish/http/clients/default.py +208 -29
  61. omlish/http/clients/executor.py +56 -0
  62. omlish/http/clients/httpx.py +72 -11
  63. omlish/http/clients/middleware.py +181 -0
  64. omlish/http/clients/sync.py +33 -27
  65. omlish/http/clients/syncasync.py +49 -0
  66. omlish/http/clients/urllib.py +6 -3
  67. omlish/http/coro/client/connection.py +15 -6
  68. omlish/http/coro/io.py +2 -0
  69. omlish/http/flasky/__init__.py +40 -0
  70. omlish/http/flasky/_compat.py +2 -0
  71. omlish/http/flasky/api.py +82 -0
  72. omlish/http/flasky/app.py +203 -0
  73. omlish/http/flasky/cvs.py +59 -0
  74. omlish/http/flasky/requests.py +20 -0
  75. omlish/http/flasky/responses.py +23 -0
  76. omlish/http/flasky/routes.py +23 -0
  77. omlish/http/flasky/types.py +15 -0
  78. omlish/http/urls.py +67 -0
  79. omlish/inject/__init__.py +38 -18
  80. omlish/inject/_dataclasses.py +4986 -0
  81. omlish/inject/binder.py +4 -48
  82. omlish/inject/elements.py +27 -0
  83. omlish/inject/helpers/__init__.py +0 -0
  84. omlish/inject/{utils.py → helpers/constfn.py} +3 -3
  85. omlish/inject/{tags.py → helpers/id.py} +2 -2
  86. omlish/inject/helpers/multis.py +143 -0
  87. omlish/inject/helpers/wrappers.py +54 -0
  88. omlish/inject/impl/elements.py +47 -17
  89. omlish/inject/impl/injector.py +20 -19
  90. omlish/inject/impl/inspect.py +10 -1
  91. omlish/inject/impl/maysync.py +3 -4
  92. omlish/inject/impl/multis.py +3 -0
  93. omlish/inject/impl/sync.py +3 -4
  94. omlish/inject/injector.py +31 -2
  95. omlish/inject/inspect.py +35 -0
  96. omlish/inject/maysync.py +2 -4
  97. omlish/inject/multis.py +8 -0
  98. omlish/inject/overrides.py +3 -3
  99. omlish/inject/privates.py +6 -0
  100. omlish/inject/providers.py +3 -2
  101. omlish/inject/sync.py +5 -4
  102. omlish/io/buffers.py +118 -0
  103. omlish/io/readers.py +29 -0
  104. omlish/iterators/transforms.py +2 -2
  105. omlish/lang/__init__.py +178 -97
  106. omlish/lang/_asyncs.cc +186 -0
  107. omlish/lang/asyncs.py +17 -0
  108. omlish/lang/casing.py +11 -0
  109. omlish/lang/contextmanagers.py +28 -4
  110. omlish/lang/functions.py +31 -22
  111. omlish/lang/imports/_capture.cc +11 -9
  112. omlish/lang/imports/capture.py +363 -170
  113. omlish/lang/imports/proxy.py +455 -152
  114. omlish/lang/lazyglobals.py +22 -9
  115. omlish/lang/params.py +17 -0
  116. omlish/lang/recursion.py +0 -1
  117. omlish/lang/sequences.py +124 -0
  118. omlish/lite/abstract.py +54 -24
  119. omlish/lite/asyncs.py +2 -2
  120. omlish/lite/attrops.py +2 -0
  121. omlish/lite/contextmanagers.py +4 -4
  122. omlish/lite/dataclasses.py +44 -0
  123. omlish/lite/maybes.py +8 -0
  124. omlish/lite/maysync.py +1 -5
  125. omlish/lite/pycharm.py +1 -1
  126. omlish/lite/typing.py +6 -0
  127. omlish/logs/all.py +1 -1
  128. omlish/logs/utils.py +1 -1
  129. omlish/manifests/loading.py +2 -1
  130. omlish/marshal/__init__.py +33 -13
  131. omlish/marshal/_dataclasses.py +2774 -0
  132. omlish/marshal/base/configs.py +12 -0
  133. omlish/marshal/base/contexts.py +36 -21
  134. omlish/marshal/base/funcs.py +8 -11
  135. omlish/marshal/base/options.py +8 -0
  136. omlish/marshal/base/registries.py +146 -44
  137. omlish/marshal/base/types.py +40 -16
  138. omlish/marshal/composite/iterables.py +33 -20
  139. omlish/marshal/composite/literals.py +20 -18
  140. omlish/marshal/composite/mappings.py +36 -23
  141. omlish/marshal/composite/maybes.py +29 -19
  142. omlish/marshal/composite/newtypes.py +16 -16
  143. omlish/marshal/composite/optionals.py +14 -14
  144. omlish/marshal/composite/special.py +15 -15
  145. omlish/marshal/composite/unions/__init__.py +0 -0
  146. omlish/marshal/composite/unions/literals.py +93 -0
  147. omlish/marshal/composite/unions/primitives.py +103 -0
  148. omlish/marshal/factories/invalidate.py +18 -68
  149. omlish/marshal/factories/method.py +26 -0
  150. omlish/marshal/factories/moduleimport/factories.py +22 -65
  151. omlish/marshal/factories/multi.py +13 -25
  152. omlish/marshal/factories/recursive.py +42 -56
  153. omlish/marshal/factories/typecache.py +29 -74
  154. omlish/marshal/factories/typemap.py +42 -43
  155. omlish/marshal/objects/dataclasses.py +129 -106
  156. omlish/marshal/objects/marshal.py +18 -14
  157. omlish/marshal/objects/namedtuples.py +48 -42
  158. omlish/marshal/objects/unmarshal.py +19 -15
  159. omlish/marshal/polymorphism/marshal.py +9 -11
  160. omlish/marshal/polymorphism/metadata.py +16 -5
  161. omlish/marshal/polymorphism/standard.py +13 -1
  162. omlish/marshal/polymorphism/unions.py +15 -105
  163. omlish/marshal/polymorphism/unmarshal.py +9 -10
  164. omlish/marshal/singular/enums.py +14 -18
  165. omlish/marshal/standard.py +10 -6
  166. omlish/marshal/trivial/any.py +1 -1
  167. omlish/marshal/trivial/forbidden.py +21 -26
  168. omlish/metadata.py +23 -1
  169. omlish/os/forkhooks.py +4 -0
  170. omlish/os/pidfiles/pinning.py +2 -2
  171. omlish/reflect/types.py +1 -0
  172. omlish/secrets/marshal.py +1 -1
  173. omlish/specs/jmespath/__init__.py +12 -3
  174. omlish/specs/jmespath/_dataclasses.py +2893 -0
  175. omlish/specs/jmespath/ast.py +1 -1
  176. omlish/specs/jsonrpc/__init__.py +13 -0
  177. omlish/specs/jsonrpc/_marshal.py +32 -23
  178. omlish/specs/jsonrpc/conns.py +10 -7
  179. omlish/specs/jsonschema/_marshal.py +1 -1
  180. omlish/specs/jsonschema/keywords/__init__.py +7 -0
  181. omlish/specs/jsonschema/keywords/_dataclasses.py +1644 -0
  182. omlish/specs/openapi/_marshal.py +31 -22
  183. omlish/sql/{tabledefs/alchemy.py → alchemy/tabledefs.py} +2 -2
  184. omlish/sql/queries/_marshal.py +2 -2
  185. omlish/sql/queries/rendering.py +1 -1
  186. omlish/sql/tabledefs/_marshal.py +1 -1
  187. omlish/subprocesses/base.py +4 -0
  188. omlish/subprocesses/editor.py +1 -1
  189. omlish/sync.py +155 -21
  190. omlish/term/alt.py +60 -0
  191. omlish/term/confirm.py +8 -8
  192. omlish/term/pager.py +235 -0
  193. omlish/term/terminfo.py +935 -0
  194. omlish/term/termstate.py +67 -0
  195. omlish/term/vt100/terminal.py +0 -3
  196. omlish/testing/pytest/plugins/asyncs/fixtures.py +4 -1
  197. omlish/testing/pytest/plugins/skips.py +2 -1
  198. omlish/testing/unittest/main.py +3 -3
  199. omlish/text/docwrap/__init__.py +3 -0
  200. omlish/text/docwrap/__main__.py +11 -0
  201. omlish/text/docwrap/api.py +83 -0
  202. omlish/text/docwrap/cli.py +86 -0
  203. omlish/text/docwrap/groups.py +84 -0
  204. omlish/text/docwrap/lists.py +167 -0
  205. omlish/text/docwrap/parts.py +146 -0
  206. omlish/text/docwrap/reflowing.py +106 -0
  207. omlish/text/docwrap/rendering.py +151 -0
  208. omlish/text/docwrap/utils.py +11 -0
  209. omlish/text/docwrap/wrapping.py +59 -0
  210. omlish/text/filecache.py +2 -2
  211. omlish/text/lorem.py +6 -0
  212. omlish/text/parts.py +2 -2
  213. omlish/text/textwrap.py +51 -0
  214. omlish/typedvalues/marshal.py +85 -59
  215. omlish/typedvalues/values.py +2 -1
  216. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/METADATA +29 -28
  217. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/RECORD +222 -171
  218. omlish/dataclasses/impl/generation/mangling.py +0 -18
  219. omlish/funcs/match.py +0 -227
  220. omlish/marshal/factories/match.py +0 -34
  221. omlish/marshal/factories/simple.py +0 -28
  222. /omlish/inject/impl/{providers2.py → providersmap.py} +0 -0
  223. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/WHEEL +0 -0
  224. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/entry_points.txt +0 -0
  225. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/licenses/LICENSE +0 -0
  226. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,106 @@
1
+ """
2
+ FIXME:
3
+ - use wrapping.py with all its todos
4
+ """
5
+ import dataclasses as dc
6
+ import textwrap
7
+ import typing as ta
8
+
9
+ from ... import lang
10
+ from ..textwrap import TextwrapOpts
11
+ from .parts import Blank
12
+ from .parts import Block
13
+ from .parts import Indent
14
+ from .parts import List
15
+ from .parts import Part
16
+ from .parts import Text
17
+ from .parts import blockify
18
+ from .parts import unblockify
19
+ from .utils import all_same
20
+
21
+
22
+ ##
23
+
24
+
25
+ class TextReflower(ta.Protocol):
26
+ def __call__(self, strs: ta.Sequence[str], current_indent: int) -> ta.Sequence[str]: ...
27
+
28
+
29
+ class NopReflower:
30
+ def __call__(self, strs: ta.Sequence[str], current_indent: int = 0) -> ta.Sequence[str]:
31
+ return strs
32
+
33
+
34
+ @dc.dataclass(frozen=True)
35
+ class JoiningReflower:
36
+ sep: str = ' '
37
+
38
+ def __call__(self, strs: ta.Sequence[str], current_indent: int = 0) -> ta.Sequence[str]:
39
+ return [self.sep.join(strs)]
40
+
41
+
42
+ @dc.dataclass(frozen=True)
43
+ class TextwrapReflower:
44
+ sep: str = ' '
45
+ opts: TextwrapOpts = TextwrapOpts()
46
+
47
+ def __call__(self, strs: ta.Sequence[str], current_indent: int = 0) -> ta.Sequence[str]:
48
+ return textwrap.wrap(
49
+ self.sep.join(strs),
50
+ **dc.asdict(dc.replace(
51
+ self.opts,
52
+ width=self.opts.width - current_indent,
53
+ )),
54
+ )
55
+
56
+
57
+ ##
58
+
59
+
60
+ def reflow_block_text(
61
+ root: Part,
62
+ reflower: TextReflower = JoiningReflower(),
63
+ ) -> Part:
64
+ def rec(p: Part, ci: int) -> Part:
65
+ if isinstance(p, Blank):
66
+ return p
67
+
68
+ elif isinstance(p, Text):
69
+ if (rf := reflower([p.s], ci)) != [p.s]:
70
+ p = blockify(*map(Text, rf)) # noqa
71
+ return p
72
+
73
+ elif isinstance(p, Indent):
74
+ if (np := rec(p.p, ci + p.n)) is not p.p:
75
+ p = Indent(p.n, np) # type: ignore[arg-type]
76
+ return p
77
+
78
+ elif isinstance(p, List):
79
+ ne = [rec(e, ci + len(p.d) + 1) for e in p.es]
80
+ if not all_same(ne, p.es):
81
+ p = List(p.d, ne)
82
+ return p
83
+
84
+ elif not isinstance(p, Block):
85
+ raise TypeError(p)
86
+
87
+ ps = [rec(c, ci) for c in p.ps]
88
+
89
+ ps = list(unblockify(blockify(*ps)))
90
+
91
+ new: list[Part | list[str]] = []
92
+ for c in ps:
93
+ if isinstance(c, Text):
94
+ if new and isinstance(x := new[-1], list):
95
+ x.append(c.s)
96
+ else:
97
+ new.append([c.s])
98
+ else:
99
+ new.append(c)
100
+
101
+ return blockify(*lang.flatmap(
102
+ lambda x: map(Text, reflower(x, ci)) if isinstance(x, list) else [x], # noqa
103
+ new,
104
+ ))
105
+
106
+ return rec(root, 0)
@@ -0,0 +1,151 @@
1
+ import functools
2
+ import io
3
+ import typing as ta
4
+
5
+ from ... import check
6
+ from .parts import Blank
7
+ from .parts import Block
8
+ from .parts import Indent
9
+ from .parts import List
10
+ from .parts import Part
11
+ from .parts import Text
12
+
13
+
14
+ ##
15
+
16
+
17
+ @functools.lru_cache
18
+ def _indent_str(n: int) -> str:
19
+ return ' ' * n
20
+
21
+
22
+ ##
23
+
24
+
25
+ def render_to(root: Part, out: ta.Callable[[str], ta.Any]) -> None:
26
+ i_stk: list[int] = [0]
27
+ ci = 0
28
+ need_nl = False
29
+
30
+ def write(
31
+ s: str,
32
+ ti: int | None = None,
33
+ endl: bool = False,
34
+ ) -> None:
35
+ check.not_in('\n', s)
36
+
37
+ nonlocal need_nl
38
+ nonlocal ci
39
+ if need_nl:
40
+ check.state(ci == 0)
41
+ out('\n')
42
+ need_nl = False
43
+
44
+ if ti is None:
45
+ ti = i_stk[-1]
46
+ if ci < ti:
47
+ out(_indent_str(ti - ci))
48
+ ci = ti
49
+
50
+ out(s)
51
+
52
+ if endl:
53
+ need_nl = True
54
+ ci = 0
55
+
56
+ def rec(p: Part) -> None:
57
+ nonlocal ci
58
+
59
+ if isinstance(p, Blank):
60
+ check.state(ci == 0)
61
+ out('\n')
62
+
63
+ elif isinstance(p, Text):
64
+ write(p.s, endl=True)
65
+
66
+ elif isinstance(p, Block):
67
+ for c in p.ps:
68
+ rec(c)
69
+
70
+ elif isinstance(p, Indent):
71
+ i_stk.append(i_stk[-1] + p.n)
72
+ rec(p.p)
73
+ i_stk.pop()
74
+
75
+ elif isinstance(p, List):
76
+ i_stk.append((li := i_stk[-1]) + len(p.d) + 1)
77
+ sd = p.d + ' '
78
+ for e in p.es:
79
+ if isinstance(e, Blank):
80
+ rec(e)
81
+ else:
82
+ check.state(ci == 0)
83
+ write(sd, li)
84
+ ci += len(sd)
85
+ rec(e)
86
+ i_stk.pop()
87
+
88
+ else:
89
+ raise TypeError(p)
90
+
91
+ rec(root)
92
+ check.state(i_stk == [0])
93
+
94
+
95
+ def render(root: Part) -> str:
96
+ buf = io.StringIO()
97
+ render_to(root, buf.write)
98
+ return buf.getvalue()
99
+
100
+
101
+ ##
102
+
103
+
104
+ def dump_to(root: Part, out: ta.Callable[[str], ta.Any]) -> None:
105
+ i = 0
106
+
107
+ def write(s: str) -> None:
108
+ out(_indent_str(i * 2))
109
+ out(s)
110
+ out('\n')
111
+
112
+ def rec(p: Part) -> None:
113
+ nonlocal i
114
+
115
+ if isinstance(p, Blank):
116
+ write('blank')
117
+
118
+ elif isinstance(p, Text):
119
+ write(f'text:{p.s!r}')
120
+
121
+ elif isinstance(p, Block):
122
+ write(f'block/{len(p.ps)}')
123
+ i += 1
124
+ for c in p.ps:
125
+ rec(c)
126
+ i -= 1
127
+
128
+ elif isinstance(p, Indent):
129
+ write(f'indent:{p.n}')
130
+ i += 1
131
+ rec(p.p)
132
+ i -= 1
133
+
134
+ elif isinstance(p, List):
135
+ write(f'list/{len(p.es)}:{p.d!r}')
136
+ i += 1
137
+ for e in p.es:
138
+ rec(e)
139
+ i -= 1
140
+
141
+ else:
142
+ raise TypeError(p)
143
+
144
+ rec(root)
145
+ check.state(i == 0)
146
+
147
+
148
+ def dump(root: Part) -> str:
149
+ buf = io.StringIO()
150
+ dump_to(root, buf.write)
151
+ return buf.getvalue()
@@ -0,0 +1,11 @@
1
+ import typing as ta
2
+
3
+
4
+ T = ta.TypeVar('T')
5
+
6
+
7
+ ##
8
+
9
+
10
+ def all_same(l: ta.Sequence[T], r: ta.Sequence[T]) -> bool:
11
+ return len(l) == len(r) and all(x is y for x, y in zip(l, r, strict=True))
@@ -0,0 +1,59 @@
1
+ """
2
+ TODO:
3
+ - a reflowing.TextReflower
4
+ - import textwrap lol
5
+ - obviously handle hyphens, underscores, etc
6
+ - optionally preserve/normalize inter-sentence spaces - '. ' vs '. '
7
+ - detect if 'intentionally' smaller than current remaining line width, if so do not merge.
8
+ - maybe if only ending with punctuation?
9
+ - detect 'matched pairs' of 'quotes'? to preserve whitespace? `foo bar` ...
10
+ - how to escape it lol - if we see any \\` do we give up?
11
+ """
12
+ import re
13
+ import typing as ta
14
+
15
+
16
+ SpanKind: ta.TypeAlias = ta.Literal[
17
+ 'word',
18
+ 'space',
19
+ 'symbol',
20
+ ]
21
+
22
+
23
+ ##
24
+
25
+
26
+ class Span(ta.NamedTuple):
27
+ k: SpanKind
28
+ s: str
29
+
30
+ def __repr__(self) -> str:
31
+ return f'{self.k}:{self.s!r}'
32
+
33
+
34
+ _SPAN_PAT = re.compile(r'(\w+)|(\s+)')
35
+
36
+
37
+ def split_line_spans(s: str) -> list[Span]:
38
+ spans: list[Span] = []
39
+ p = 0
40
+ for m in _SPAN_PAT.finditer(s):
41
+ l, r = m.span()
42
+ if p < l:
43
+ spans.append(Span('symbol', s[p:l]))
44
+ if m.group(1):
45
+ spans.append(Span('word', m.group(1)))
46
+ else:
47
+ spans.append(Span('space', m.group(2)))
48
+ p = r
49
+ if p < len(s):
50
+ spans.append(Span('symbol', s[p:]))
51
+ return spans
52
+
53
+
54
+ def _main() -> None:
55
+ print(split_line_spans('hi i am a string! this has a hy-phen.'))
56
+
57
+
58
+ if __name__ == '__main__':
59
+ _main()
omlish/text/filecache.py CHANGED
@@ -2,10 +2,10 @@
2
2
  Features:
3
3
  - get (line, offset)
4
4
  - invalidate
5
- - filewatcher
5
+ - TODO: filewatcher
6
6
  - linecache interop
7
7
  - tokenize.open - detect encoding - only for .py
8
- - directory / path_parts tree?
8
+ - TODO: directory / path_parts tree?
9
9
 
10
10
  TODO:
11
11
  - read raw, decode (detect)
omlish/text/lorem.py ADDED
@@ -0,0 +1,6 @@
1
+ LOREM = (
2
+ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore '
3
+ 'magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo '
4
+ 'consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. '
5
+ 'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
6
+ )
omlish/text/parts.py CHANGED
@@ -75,7 +75,7 @@ class PartTransform:
75
75
  def __call__(self, part: Part | None) -> Part:
76
76
  return self._transform(part)
77
77
 
78
- @dispatch.method
78
+ @dispatch.method(instance_cache=True)
79
79
  def _transform(self, part: Part | None) -> Part:
80
80
  raise TypeError(part)
81
81
 
@@ -214,7 +214,7 @@ class PartRenderer:
214
214
  def __call__(self, part: Part | None) -> None:
215
215
  return self._render(part)
216
216
 
217
- @dispatch.method
217
+ @dispatch.method()
218
218
  def _render(self, part: Part | None) -> None:
219
219
  raise TypeError(part)
220
220
 
@@ -0,0 +1,51 @@
1
+ import dataclasses as dc
2
+
3
+
4
+ ##
5
+
6
+
7
+ @dc.dataclass(frozen=True)
8
+ class TextwrapOpts:
9
+ # The maximum width of wrapped lines (unless break_long_words is false).
10
+ width: int = 70 # noqa
11
+
12
+ _: dc.KW_ONLY
13
+
14
+ # String that will be prepended to the first line of wrapped output. Counts towards the line's width.
15
+ initial_indent: str = ''
16
+
17
+ # String that will be prepended to all lines save the first of wrapped output; also counts towards each line's
18
+ # width.
19
+ subsequent_indent: str = ''
20
+
21
+ # Expand tabs in input text to spaces before further processing. Each tab will become 0 .. 'tabsize' spaces,
22
+ # depending on its position in its line. If false, each tab is treated as a single character.
23
+ expand_tabs: bool = True
24
+
25
+ # Expand tabs in input text to 0 .. 'tabsize' spaces, unless 'expand_tabs' is false.
26
+ tabsize: int = 8
27
+
28
+ # Replace all whitespace characters in the input text by spaces after tab expansion. Note that if expand_tabs is
29
+ # false and replace_whitespace is true, every tab will be converted to a single space!
30
+ replace_whitespace: bool = True
31
+
32
+ # Ensure that sentence-ending punctuation is always followed by two spaces. Off by default because the algorithm is
33
+ # (unavoidably) imperfect.
34
+ fix_sentence_endings: bool = False
35
+
36
+ # Break words longer than 'width'. If false, those words will not be broken, and some lines might be longer than
37
+ # 'width'.
38
+ break_long_words: bool = True
39
+
40
+ # Allow breaking hyphenated words. If true, wrapping will occur preferably on whitespaces and right after hyphens
41
+ # part of compound words.
42
+ break_on_hyphens: bool = True
43
+
44
+ # Drop leading and trailing whitespace from lines.
45
+ drop_whitespace: bool = True
46
+
47
+ # Truncate wrapped lines.
48
+ max_lines: int | None = None
49
+
50
+ # Append to the last line of truncated text.
51
+ placeholder: str = ' [...]'
@@ -1,9 +1,10 @@
1
+ import typing as ta
2
+
1
3
  from .. import check
2
4
  from .. import dataclasses as dc
3
5
  from .. import lang
4
6
  from .. import marshal as msh
5
7
  from .. import reflect as rfl
6
- from ..funcs import match as mfs
7
8
  from .collection import TypedValues
8
9
  from .reflect import reflect_typed_values_impls
9
10
  from .values import ScalarTypedValue
@@ -14,60 +15,77 @@ from .values import TypedValue
14
15
 
15
16
 
16
17
  def _build_typed_value_poly(rty: rfl.Type) -> msh.Polymorphism:
17
- ty: type[TypedValue] = check.issubclass(check.isinstance(rty, type), TypedValue)
18
+ ty: type[TypedValue] = check.issubclass(check.isinstance(rty, type), TypedValue) # noqa
18
19
  check.state(lang.is_abstract_class(ty))
19
20
  return msh.polymorphism_from_subclasses(
20
21
  ty,
21
22
  naming=msh.Naming.SNAKE,
22
- strip_suffix='auto',
23
+ strip_suffix=msh.AutoStripSuffix,
23
24
  )
24
25
 
25
26
 
26
- class TypedValueMarshalerFactory(msh.MarshalerFactoryMatchClass):
27
- @mfs.simple(lambda _, ctx, rty: (
28
- isinstance(rty, type) and
29
- issubclass(rty, ScalarTypedValue) and
30
- not lang.is_abstract_class(rty)
31
- ))
32
- def _build_scalar(self, ctx: msh.MarshalContext, rty: rfl.Type) -> msh.Marshaler:
33
- dc_rfl = dc.reflect(check.isinstance(rty, type))
34
- v_rty = check.single(dc_rfl.fields_inspection.generic_replaced_field_annotations.values())
35
- v_m = ctx.make(v_rty)
36
- return msh.WrappedMarshaler(lambda _, o: o.v, v_m)
37
-
38
- @mfs.simple(lambda _, ctx, rty: (
39
- isinstance(rty, type) and
40
- issubclass(rty, TypedValue) and
41
- lang.is_abstract_class(rty)
42
- ))
43
- def _build_abstract(self, ctx: msh.MarshalContext, rty: rfl.Type) -> msh.Marshaler:
44
- return msh.make_polymorphism_marshaler(
27
+ class TypedValueMarshalerFactory(msh.MarshalerFactoryMethodClass):
28
+ @msh.MarshalerFactoryMethodClass.make_marshaler.register
29
+ def _make_scalar(self, ctx: msh.MarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], msh.Marshaler] | None:
30
+ if not (
31
+ isinstance(rty, type) and
32
+ issubclass(rty, ScalarTypedValue) and
33
+ not lang.is_abstract_class(rty)
34
+ ):
35
+ return None
36
+
37
+ def inner() -> msh.Marshaler:
38
+ dc_rfl = dc.reflect(check.isinstance(rty, type))
39
+ v_rty = check.single(dc_rfl.fields_inspection.generic_replaced_field_annotations.values())
40
+ v_m = ctx.make_marshaler(v_rty)
41
+ return msh.WrappedMarshaler(lambda _, o: o.v, v_m)
42
+
43
+ return inner
44
+
45
+ @msh.MarshalerFactoryMethodClass.make_marshaler.register
46
+ def _make_abstract(self, ctx: msh.MarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], msh.Marshaler] | None:
47
+ if not (
48
+ isinstance(rty, type) and
49
+ issubclass(rty, TypedValue) and
50
+ lang.is_abstract_class(rty)
51
+ ):
52
+ return None
53
+
54
+ return lambda: msh.make_polymorphism_marshaler(
45
55
  _build_typed_value_poly(rty).impls,
46
56
  msh.WrapperTypeTagging(),
47
57
  ctx,
48
58
  )
49
59
 
50
60
 
51
- class TypedValueUnmarshalerFactory(msh.UnmarshalerFactoryMatchClass):
52
- @mfs.simple(lambda _, ctx, rty: (
53
- isinstance(rty, type) and
54
- issubclass(rty, ScalarTypedValue) and
55
- not lang.is_abstract_class(rty)
56
- ))
57
- def _build_scalar(self, ctx: msh.UnmarshalContext, rty: rfl.Type) -> msh.Unmarshaler:
58
- rty = check.isinstance(rty, type)
59
- dc_rfl = dc.reflect(rty)
60
- v_rty = check.single(dc_rfl.fields_inspection.generic_replaced_field_annotations.values())
61
- v_u = ctx.make(v_rty)
62
- return msh.WrappedUnmarshaler(lambda _, v: rty(v), v_u)
63
-
64
- @mfs.simple(lambda _, ctx, rty: (
65
- isinstance(rty, type) and
66
- issubclass(rty, TypedValue) and
67
- lang.is_abstract_class(rty)
68
- ))
69
- def _build_abstract(self, ctx: msh.UnmarshalContext, rty: rfl.Type) -> msh.Unmarshaler:
70
- return msh.make_polymorphism_unmarshaler(
61
+ class TypedValueUnmarshalerFactory(msh.UnmarshalerFactoryMethodClass):
62
+ @msh.UnmarshalerFactoryMethodClass.make_unmarshaler.register
63
+ def _make_scalar(self, ctx: msh.UnmarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], msh.Unmarshaler] | None:
64
+ if not (
65
+ isinstance(rty, type) and
66
+ issubclass(rty, ScalarTypedValue) and
67
+ not lang.is_abstract_class(rty)
68
+ ):
69
+ return None
70
+
71
+ def inner() -> msh.Unmarshaler:
72
+ dc_rfl = dc.reflect(rty)
73
+ v_rty = check.single(dc_rfl.fields_inspection.generic_replaced_field_annotations.values())
74
+ v_u = ctx.make_unmarshaler(v_rty)
75
+ return msh.WrappedUnmarshaler(lambda _, v: rty(v), v_u)
76
+
77
+ return inner
78
+
79
+ @msh.UnmarshalerFactoryMethodClass.make_unmarshaler.register
80
+ def _make_abstract(self, ctx: msh.UnmarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], msh.Unmarshaler] | None: # noqa
81
+ if not (
82
+ isinstance(rty, type) and
83
+ issubclass(rty, TypedValue) and
84
+ lang.is_abstract_class(rty)
85
+ ):
86
+ return None
87
+
88
+ return lambda: msh.make_polymorphism_unmarshaler(
71
89
  _build_typed_value_poly(rty).impls,
72
90
  msh.WrapperTypeTagging(),
73
91
  ctx,
@@ -99,7 +117,7 @@ def _build_typed_values_impls(rty: rfl.Type) -> msh.Impls:
99
117
  #
100
118
 
101
119
 
102
- def build_typed_values_marshaler(ctx: msh.MarshalContext, rty: rfl.Type) -> msh.Marshaler:
120
+ def build_typed_values_marshaler(ctx: msh.MarshalFactoryContext, rty: rfl.Type) -> msh.Marshaler:
103
121
  tv_m = msh.make_polymorphism_marshaler(
104
122
  msh.Impls(_build_typed_values_impls(rty)),
105
123
  msh.WrapperTypeTagging(),
@@ -108,20 +126,24 @@ def build_typed_values_marshaler(ctx: msh.MarshalContext, rty: rfl.Type) -> msh.
108
126
  return msh.IterableMarshaler(tv_m)
109
127
 
110
128
 
111
- class TypedValuesMarshalerFactory(msh.MarshalerFactoryMatchClass):
112
- @mfs.simple(lambda _, ctx, rty: isinstance(rty, rfl.Generic) and rty.cls is TypedValues)
113
- def _build(self, ctx: msh.MarshalContext, rty: rfl.Type) -> msh.Marshaler:
114
- return build_typed_values_marshaler(ctx, rty)
129
+ class TypedValuesMarshalerFactory(msh.MarshalerFactoryMethodClass):
130
+ @msh.MarshalerFactoryMethodClass.make_marshaler.register
131
+ def _make_generic(self, ctx: msh.MarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], msh.Marshaler] | None:
132
+ if not (isinstance(rty, rfl.Generic) and rty.cls is TypedValues):
133
+ return None
134
+ return lambda: build_typed_values_marshaler(ctx, rty)
115
135
 
116
- @mfs.simple(lambda _, ctx, rty: rty is TypedValues)
117
- def _build_concrete(self, ctx: msh.MarshalContext, rty: rfl.Type) -> msh.Marshaler:
118
- raise NotImplementedError
136
+ @msh.MarshalerFactoryMethodClass.make_marshaler.register
137
+ def _make_concrete(self, ctx: msh.MarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], msh.Marshaler] | None:
138
+ if rty is not TypedValues:
139
+ return None
140
+ return lambda: lang.raise_(NotImplementedError)
119
141
 
120
142
 
121
143
  #
122
144
 
123
145
 
124
- def build_typed_values_unmarshaler(ctx: msh.UnmarshalContext, rty: rfl.Type) -> msh.Unmarshaler:
146
+ def build_typed_values_unmarshaler(ctx: msh.UnmarshalFactoryContext, rty: rfl.Type) -> msh.Unmarshaler:
125
147
  tv_u = msh.make_polymorphism_unmarshaler(
126
148
  msh.Impls(_build_typed_values_impls(rty)),
127
149
  msh.WrapperTypeTagging(),
@@ -130,21 +152,25 @@ def build_typed_values_unmarshaler(ctx: msh.UnmarshalContext, rty: rfl.Type) ->
130
152
  return msh.IterableUnmarshaler(lambda it: TypedValues(*it), tv_u) # noqa
131
153
 
132
154
 
133
- class TypedValuesUnmarshalerFactory(msh.UnmarshalerFactoryMatchClass):
134
- @mfs.simple(lambda _, ctx, rty: isinstance(rty, rfl.Generic) and rty.cls is TypedValues)
135
- def _build(self, ctx: msh.UnmarshalContext, rty: rfl.Type) -> msh.Unmarshaler:
136
- return build_typed_values_unmarshaler(ctx, rty)
155
+ class TypedValuesUnmarshalerFactory(msh.UnmarshalerFactoryMethodClass):
156
+ @msh.UnmarshalerFactoryMethodClass.make_unmarshaler.register
157
+ def _build(self, ctx: msh.UnmarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], msh.Unmarshaler] | None:
158
+ if not (isinstance(rty, rfl.Generic) and rty.cls is TypedValues):
159
+ return None
160
+ return lambda: build_typed_values_unmarshaler(ctx, rty)
137
161
 
138
- @mfs.simple(lambda _, ctx, rty: rty is TypedValues)
139
- def _build_concrete(self, ctx: msh.UnmarshalContext, rty: rfl.Type) -> msh.Unmarshaler:
140
- raise NotImplementedError
162
+ @msh.UnmarshalerFactoryMethodClass.make_unmarshaler.register
163
+ def _build_concrete(self, ctx: msh.UnmarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], msh.Unmarshaler] | None: # noqa
164
+ if rty is not TypedValues:
165
+ return None
166
+ return lambda: lang.raise_(NotImplementedError)
141
167
 
142
168
 
143
169
  ##
144
170
 
145
171
 
146
172
  @lang.static_init
147
- def _install_standard_marshalling() -> None:
173
+ def _install_standard_marshaling() -> None:
148
174
  msh.install_standard_factories(
149
175
  TypedValueMarshalerFactory(),
150
176
  TypedValueUnmarshalerFactory(),
@@ -11,7 +11,8 @@ T = ta.TypeVar('T')
11
11
 
12
12
 
13
13
  class TypedValue(lang.Abstract):
14
- pass
14
+ def __bool__(self) -> ta.NoReturn:
15
+ raise TypeError(f"Cannot convert {self!r} to bool - use '.v' or 'is not None'.")
15
16
 
16
17
 
17
18
  ##