omlish 0.0.0.dev447__py3-none-any.whl → 0.0.0.dev493__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 (265) hide show
  1. omlish/.omlish-manifests.json +12 -0
  2. omlish/README.md +199 -0
  3. omlish/__about__.py +21 -16
  4. omlish/argparse/all.py +17 -9
  5. omlish/argparse/cli.py +16 -3
  6. omlish/argparse/utils.py +21 -0
  7. omlish/asyncs/asyncio/rlock.py +110 -0
  8. omlish/asyncs/asyncio/sync.py +43 -0
  9. omlish/asyncs/asyncio/utils.py +2 -0
  10. omlish/asyncs/sync.py +25 -0
  11. omlish/bootstrap/_marshal.py +1 -1
  12. omlish/bootstrap/diag.py +12 -21
  13. omlish/bootstrap/main.py +2 -5
  14. omlish/bootstrap/sys.py +27 -28
  15. omlish/cexts/__init__.py +0 -0
  16. omlish/cexts/include/omlish/omlish.hh +1 -0
  17. omlish/collections/__init__.py +13 -1
  18. omlish/collections/attrregistry.py +210 -0
  19. omlish/collections/cache/impl.py +1 -0
  20. omlish/collections/identity.py +1 -0
  21. omlish/collections/mappings.py +28 -0
  22. omlish/collections/trie.py +5 -1
  23. omlish/collections/utils.py +77 -0
  24. omlish/concurrent/all.py +2 -1
  25. omlish/concurrent/futures.py +25 -0
  26. omlish/concurrent/threadlets.py +1 -1
  27. omlish/daemons/reparent.py +2 -3
  28. omlish/daemons/spawning.py +2 -3
  29. omlish/dataclasses/__init__.py +2 -0
  30. omlish/dataclasses/impl/api/classes/decorator.py +3 -0
  31. omlish/dataclasses/impl/api/classes/make.py +3 -0
  32. omlish/dataclasses/impl/concerns/repr.py +15 -2
  33. omlish/dataclasses/impl/configs.py +97 -37
  34. omlish/dataclasses/impl/generation/compilation.py +21 -19
  35. omlish/dataclasses/impl/generation/globals.py +1 -0
  36. omlish/dataclasses/impl/generation/ops.py +1 -0
  37. omlish/dataclasses/impl/generation/plans.py +2 -17
  38. omlish/dataclasses/impl/generation/processor.py +106 -25
  39. omlish/dataclasses/impl/processing/base.py +8 -0
  40. omlish/dataclasses/impl/processing/driving.py +19 -7
  41. omlish/dataclasses/specs.py +1 -0
  42. omlish/dataclasses/tools/modifiers.py +5 -0
  43. omlish/diag/_pycharm/runhack.py +1 -1
  44. omlish/diag/cmds/__init__.py +0 -0
  45. omlish/diag/{lslocks.py → cmds/lslocks.py} +6 -6
  46. omlish/diag/{lsof.py → cmds/lsof.py} +6 -6
  47. omlish/diag/{ps.py → cmds/ps.py} +6 -6
  48. omlish/diag/pycharm.py +16 -2
  49. omlish/diag/pydevd.py +58 -40
  50. omlish/diag/replserver/console.py +1 -1
  51. omlish/dispatch/__init__.py +18 -12
  52. omlish/dispatch/methods.py +50 -140
  53. omlish/dom/rendering.py +1 -1
  54. omlish/formats/dotenv.py +1 -1
  55. omlish/formats/json/stream/__init__.py +13 -0
  56. omlish/funcs/guard.py +225 -0
  57. omlish/graphs/dot/rendering.py +1 -1
  58. omlish/http/all.py +44 -4
  59. omlish/http/clients/asyncs.py +33 -27
  60. omlish/http/clients/base.py +17 -1
  61. omlish/http/clients/coro/__init__.py +0 -0
  62. omlish/http/clients/coro/sync.py +171 -0
  63. omlish/http/clients/default.py +208 -29
  64. omlish/http/clients/executor.py +56 -0
  65. omlish/http/clients/httpx.py +72 -11
  66. omlish/http/clients/middleware.py +181 -0
  67. omlish/http/clients/sync.py +33 -27
  68. omlish/http/clients/syncasync.py +49 -0
  69. omlish/http/clients/urllib.py +6 -3
  70. omlish/http/coro/client/connection.py +15 -6
  71. omlish/http/coro/io.py +2 -0
  72. omlish/http/flasky/__init__.py +40 -0
  73. omlish/http/flasky/_compat.py +2 -0
  74. omlish/http/flasky/api.py +82 -0
  75. omlish/http/flasky/app.py +203 -0
  76. omlish/http/flasky/cvs.py +59 -0
  77. omlish/http/flasky/requests.py +20 -0
  78. omlish/http/flasky/responses.py +23 -0
  79. omlish/http/flasky/routes.py +23 -0
  80. omlish/http/flasky/types.py +15 -0
  81. omlish/http/urls.py +67 -0
  82. omlish/inject/__init__.py +57 -29
  83. omlish/inject/_dataclasses.py +5148 -0
  84. omlish/inject/binder.py +11 -52
  85. omlish/inject/eagers.py +2 -0
  86. omlish/inject/elements.py +27 -0
  87. omlish/inject/helpers/__init__.py +0 -0
  88. omlish/inject/{utils.py → helpers/constfn.py} +3 -3
  89. omlish/inject/{tags.py → helpers/id.py} +2 -2
  90. omlish/inject/helpers/late.py +76 -0
  91. omlish/inject/{managed.py → helpers/managed.py} +10 -10
  92. omlish/inject/helpers/multis.py +143 -0
  93. omlish/inject/helpers/wrappers.py +54 -0
  94. omlish/inject/impl/elements.py +54 -21
  95. omlish/inject/impl/injector.py +29 -25
  96. omlish/inject/impl/inspect.py +10 -1
  97. omlish/inject/impl/maysync.py +3 -4
  98. omlish/inject/impl/multis.py +3 -0
  99. omlish/inject/impl/sync.py +3 -4
  100. omlish/inject/injector.py +31 -2
  101. omlish/inject/inspect.py +35 -0
  102. omlish/inject/maysync.py +2 -4
  103. omlish/inject/multis.py +8 -0
  104. omlish/inject/overrides.py +3 -3
  105. omlish/inject/privates.py +6 -0
  106. omlish/inject/providers.py +3 -2
  107. omlish/inject/sync.py +5 -4
  108. omlish/io/buffers.py +118 -0
  109. omlish/io/readers.py +29 -0
  110. omlish/iterators/transforms.py +2 -2
  111. omlish/lang/__init__.py +180 -97
  112. omlish/lang/_asyncs.cc +186 -0
  113. omlish/lang/asyncs.py +17 -0
  114. omlish/lang/casing.py +11 -0
  115. omlish/lang/contextmanagers.py +28 -4
  116. omlish/lang/functions.py +31 -22
  117. omlish/lang/imports/_capture.cc +11 -9
  118. omlish/lang/imports/capture.py +363 -170
  119. omlish/lang/imports/proxy.py +455 -152
  120. omlish/lang/lazyglobals.py +22 -9
  121. omlish/lang/params.py +17 -0
  122. omlish/lang/recursion.py +0 -1
  123. omlish/lang/sequences.py +124 -0
  124. omlish/lifecycles/README.md +30 -0
  125. omlish/lifecycles/__init__.py +87 -13
  126. omlish/lifecycles/_dataclasses.py +1388 -0
  127. omlish/lifecycles/base.py +178 -64
  128. omlish/lifecycles/contextmanagers.py +113 -4
  129. omlish/lifecycles/controller.py +150 -87
  130. omlish/lifecycles/injection.py +143 -0
  131. omlish/lifecycles/listeners.py +56 -0
  132. omlish/lifecycles/managed.py +142 -0
  133. omlish/lifecycles/manager.py +218 -93
  134. omlish/lifecycles/states.py +2 -0
  135. omlish/lifecycles/transitions.py +3 -0
  136. omlish/lifecycles/unwrap.py +57 -0
  137. omlish/lite/abstract.py +54 -24
  138. omlish/lite/asyncs.py +2 -2
  139. omlish/lite/attrops.py +2 -0
  140. omlish/lite/contextmanagers.py +4 -4
  141. omlish/lite/dataclasses.py +44 -0
  142. omlish/lite/maybes.py +8 -0
  143. omlish/lite/maysync.py +1 -5
  144. omlish/lite/pycharm.py +1 -1
  145. omlish/lite/typing.py +24 -0
  146. omlish/logs/_amalg.py +1 -1
  147. omlish/logs/all.py +25 -11
  148. omlish/logs/asyncs.py +73 -0
  149. omlish/logs/base.py +101 -12
  150. omlish/logs/contexts.py +4 -1
  151. omlish/logs/lists.py +125 -0
  152. omlish/logs/modules.py +19 -1
  153. omlish/logs/std/loggers.py +6 -1
  154. omlish/logs/std/noisy.py +11 -9
  155. omlish/logs/{standard.py → std/standard.py} +3 -4
  156. omlish/logs/utils.py +17 -2
  157. omlish/manifests/loading.py +2 -1
  158. omlish/marshal/__init__.py +33 -13
  159. omlish/marshal/_dataclasses.py +2774 -0
  160. omlish/marshal/base/configs.py +12 -0
  161. omlish/marshal/base/contexts.py +36 -21
  162. omlish/marshal/base/funcs.py +8 -11
  163. omlish/marshal/base/options.py +8 -0
  164. omlish/marshal/base/registries.py +146 -44
  165. omlish/marshal/base/types.py +40 -16
  166. omlish/marshal/composite/iterables.py +33 -20
  167. omlish/marshal/composite/literals.py +20 -18
  168. omlish/marshal/composite/mappings.py +36 -23
  169. omlish/marshal/composite/maybes.py +29 -19
  170. omlish/marshal/composite/newtypes.py +16 -16
  171. omlish/marshal/composite/optionals.py +14 -14
  172. omlish/marshal/composite/special.py +15 -15
  173. omlish/marshal/composite/unions/__init__.py +0 -0
  174. omlish/marshal/composite/unions/literals.py +93 -0
  175. omlish/marshal/composite/unions/primitives.py +103 -0
  176. omlish/marshal/factories/invalidate.py +18 -68
  177. omlish/marshal/factories/method.py +26 -0
  178. omlish/marshal/factories/moduleimport/factories.py +22 -65
  179. omlish/marshal/factories/multi.py +13 -25
  180. omlish/marshal/factories/recursive.py +42 -56
  181. omlish/marshal/factories/typecache.py +29 -74
  182. omlish/marshal/factories/typemap.py +42 -43
  183. omlish/marshal/objects/dataclasses.py +129 -106
  184. omlish/marshal/objects/marshal.py +18 -14
  185. omlish/marshal/objects/namedtuples.py +48 -42
  186. omlish/marshal/objects/unmarshal.py +19 -15
  187. omlish/marshal/polymorphism/marshal.py +9 -11
  188. omlish/marshal/polymorphism/metadata.py +16 -5
  189. omlish/marshal/polymorphism/standard.py +13 -1
  190. omlish/marshal/polymorphism/unions.py +15 -105
  191. omlish/marshal/polymorphism/unmarshal.py +9 -10
  192. omlish/marshal/singular/enums.py +14 -18
  193. omlish/marshal/standard.py +10 -6
  194. omlish/marshal/trivial/any.py +1 -1
  195. omlish/marshal/trivial/forbidden.py +21 -26
  196. omlish/metadata.py +23 -1
  197. omlish/os/forkhooks.py +4 -0
  198. omlish/os/pidfiles/pinning.py +2 -2
  199. omlish/reflect/__init__.py +43 -26
  200. omlish/reflect/ops.py +10 -1
  201. omlish/reflect/types.py +1 -0
  202. omlish/secrets/marshal.py +1 -1
  203. omlish/specs/jmespath/__init__.py +12 -3
  204. omlish/specs/jmespath/_dataclasses.py +2893 -0
  205. omlish/specs/jmespath/ast.py +1 -1
  206. omlish/specs/jsonrpc/__init__.py +13 -0
  207. omlish/specs/jsonrpc/_marshal.py +32 -23
  208. omlish/specs/jsonrpc/conns.py +10 -7
  209. omlish/specs/jsonschema/_marshal.py +1 -1
  210. omlish/specs/jsonschema/keywords/__init__.py +7 -0
  211. omlish/specs/jsonschema/keywords/_dataclasses.py +1644 -0
  212. omlish/specs/openapi/_marshal.py +31 -22
  213. omlish/sql/__init__.py +24 -5
  214. omlish/sql/{tabledefs/alchemy.py → alchemy/tabledefs.py} +2 -2
  215. omlish/sql/api/dbapi.py +1 -1
  216. omlish/sql/dbapi/__init__.py +15 -0
  217. omlish/sql/{dbapi.py → dbapi/drivers.py} +2 -2
  218. omlish/sql/queries/__init__.py +3 -0
  219. omlish/sql/queries/_marshal.py +2 -2
  220. omlish/sql/queries/rendering.py +1 -1
  221. omlish/sql/tabledefs/_marshal.py +1 -1
  222. omlish/subprocesses/base.py +4 -0
  223. omlish/subprocesses/editor.py +1 -1
  224. omlish/sync.py +155 -21
  225. omlish/term/alt.py +60 -0
  226. omlish/term/confirm.py +8 -8
  227. omlish/term/pager.py +235 -0
  228. omlish/term/terminfo.py +935 -0
  229. omlish/term/termstate.py +67 -0
  230. omlish/term/vt100/terminal.py +0 -3
  231. omlish/testing/pytest/plugins/asyncs/fixtures.py +4 -1
  232. omlish/testing/pytest/plugins/asyncs/plugin.py +2 -0
  233. omlish/testing/pytest/plugins/skips.py +2 -1
  234. omlish/testing/unittest/main.py +3 -3
  235. omlish/text/docwrap/__init__.py +3 -0
  236. omlish/text/docwrap/__main__.py +11 -0
  237. omlish/text/docwrap/api.py +83 -0
  238. omlish/text/docwrap/cli.py +91 -0
  239. omlish/text/docwrap/groups.py +84 -0
  240. omlish/text/docwrap/lists.py +167 -0
  241. omlish/text/docwrap/parts.py +146 -0
  242. omlish/text/docwrap/reflowing.py +106 -0
  243. omlish/text/docwrap/rendering.py +151 -0
  244. omlish/text/docwrap/utils.py +11 -0
  245. omlish/text/docwrap/wrapping.py +59 -0
  246. omlish/text/filecache.py +2 -2
  247. omlish/text/lorem.py +6 -0
  248. omlish/text/parts.py +2 -2
  249. omlish/text/textwrap.py +51 -0
  250. omlish/typedvalues/marshal.py +85 -59
  251. omlish/typedvalues/values.py +2 -1
  252. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/METADATA +36 -32
  253. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/RECORD +260 -199
  254. omlish/dataclasses/impl/generation/mangling.py +0 -18
  255. omlish/funcs/match.py +0 -227
  256. omlish/lifecycles/abstract.py +0 -86
  257. omlish/marshal/factories/match.py +0 -34
  258. omlish/marshal/factories/simple.py +0 -28
  259. /omlish/inject/{impl → helpers}/proxy.py +0 -0
  260. /omlish/inject/impl/{providers2.py → providersmap.py} +0 -0
  261. /omlish/sql/{abc.py → dbapi/abc.py} +0 -0
  262. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/WHEEL +0 -0
  263. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/entry_points.txt +0 -0
  264. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/licenses/LICENSE +0 -0
  265. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/top_level.txt +0 -0
@@ -10,6 +10,8 @@ import typing as ta
10
10
  from .... import check
11
11
  from .... import lang
12
12
  from ...specs import ClassSpec
13
+ from ..configs import DEFAULT_NAMED_PACKAGE_CONFIG
14
+ from ..configs import NamedPackageConfig
13
15
 
14
16
 
15
17
  T = ta.TypeVar('T')
@@ -33,6 +35,7 @@ class ProcessingContext:
33
35
  cs: ClassSpec,
34
36
  item_factories: ta.Mapping[type, ProcessingContextItemFactory],
35
37
  *,
38
+ pkg_cfg: NamedPackageConfig = DEFAULT_NAMED_PACKAGE_CONFIG,
36
39
  options: ta.Sequence[ProcessingOption] | None = None,
37
40
  ) -> None:
38
41
  super().__init__()
@@ -40,6 +43,7 @@ class ProcessingContext:
40
43
  self._cls = cls
41
44
  self._cs = cs
42
45
  self._item_factories = item_factories
46
+ self._pkg_cfg = pkg_cfg
43
47
 
44
48
  options_dct: dict = {}
45
49
  for o in options or ():
@@ -57,6 +61,10 @@ class ProcessingContext:
57
61
  def cs(self) -> ClassSpec:
58
62
  return self._cs
59
63
 
64
+ @property
65
+ def pkg_cfg(self) -> NamedPackageConfig:
66
+ return self._pkg_cfg
67
+
60
68
  def __getitem__(self, ty: type[T]) -> T:
61
69
  try:
62
70
  return self._items[ty]
@@ -1,11 +1,12 @@
1
1
  import contextlib
2
2
  import contextvars
3
+ import sys
3
4
  import typing as ta
4
5
 
5
6
  from .... import lang
6
7
  from ...specs import ClassSpec
7
8
  from .. import concerns as _concerns # noqa # imported for registration
8
- from ..configs import DEFAULT_PACKAGE_CONFIG
9
+ from ..configs import DEFAULT_NAMED_PACKAGE_CONFIG
9
10
  from ..configs import PACKAGE_CONFIG_CACHE
10
11
  from ..generation import processor as gp
11
12
  from .base import ProcessingContext
@@ -36,26 +37,36 @@ def processing_options_context(*opts: ProcessingOption) -> ta.Iterator[None]:
36
37
  ##
37
38
 
38
39
 
40
+ def _is_pkg_init_mod(mod_name: str) -> bool:
41
+ if (mod_obj := sys.modules.get(mod_name)) is None:
42
+ return False
43
+ if (mod_spec := getattr(mod_obj, '__spec__', None)) is None:
44
+ return False
45
+ return bool(mod_spec.submodule_search_locations)
46
+
47
+
39
48
  def drive_cls_processing(
40
49
  cls: type,
41
50
  cs: ClassSpec,
42
51
  *,
43
52
  plan_only: bool = False,
44
- verbose: bool = False,
53
+ warn: bool = False,
54
+ debug: bool = False,
45
55
  ) -> type:
46
56
  options: list[ProcessingOption] = list(_OPTIONS_CONTEXT_VAR.get())
47
57
  if plan_only:
48
58
  options.append(gp.PlanOnly(True))
49
- if verbose:
50
- options.append(gp.Verbose(True))
59
+ if warn or debug:
60
+ options.append(gp.Verbosity(warn=warn, debug=debug))
51
61
 
52
62
  #
53
63
 
54
- pkg_config = DEFAULT_PACKAGE_CONFIG
55
64
  cls_mod = cls.__module__
56
- if '.' in cls_mod:
65
+ if _is_pkg_init_mod(cls_mod):
66
+ cls_pkg = cls_mod
67
+ else:
57
68
  cls_pkg = cls_mod.rpartition('.')[0]
58
- pkg_cfg = PACKAGE_CONFIG_CACHE.get(cls_pkg, pkg_config) # noqa
69
+ pkg_cfg = lang.coalesce(PACKAGE_CONFIG_CACHE.get(cls_pkg), DEFAULT_NAMED_PACKAGE_CONFIG)
59
70
 
60
71
  #
61
72
 
@@ -63,6 +74,7 @@ def drive_cls_processing(
63
74
  cls,
64
75
  cs,
65
76
  all_processing_context_item_factories(),
77
+ pkg_cfg=pkg_cfg,
66
78
  options=options,
67
79
  )
68
80
 
@@ -216,6 +216,7 @@ class ClassSpec(_SpecBase, lang.Final):
216
216
 
217
217
  repr_id: bool = False
218
218
  terse_repr: bool = False
219
+ default_repr_fn: ReprFn | None = None
219
220
 
220
221
  allow_redundant_decorator: bool = False
221
222
 
@@ -3,6 +3,7 @@ import typing as ta
3
3
 
4
4
  from ... import check
5
5
  from ... import lang
6
+ from ...lite.dataclasses import is_immediate_dataclass
6
7
 
7
8
 
8
9
  if ta.TYPE_CHECKING:
@@ -40,6 +41,8 @@ def update_fields(
40
41
  def inner(cls):
41
42
  if issubclass(cls, meta.DataMeta):
42
43
  raise TypeError('update_fields() cannot be used on DataMeta subclasses')
44
+ if is_immediate_dataclass(cls):
45
+ raise TypeError('update_fields() cannot be used on already processed dataclasses')
43
46
 
44
47
  if fields is None:
45
48
  for a, v in list(cls.__dict__.items()):
@@ -51,6 +54,8 @@ def update_fields(
51
54
  try:
52
55
  v = cls.__dict__[a]
53
56
  except KeyError:
57
+ if hasattr(cls, a):
58
+ raise TypeError('update_fields() cannot be used on parent dataclass fields') from None
54
59
  v = dc.field()
55
60
  else:
56
61
  if not isinstance(v, dc.Field):
@@ -95,7 +95,7 @@ _BOOL_ENV_VAR_VALUES = {
95
95
  def _get_opt_env_bool(n, d): # type: (str | None, bool) -> bool
96
96
  if n is None or n not in os.environ:
97
97
  return d
98
- return _BOOL_ENV_VAR_VALUES[os.environ[n]]
98
+ return _BOOL_ENV_VAR_VALUES[os.environ[n].lower()]
99
99
 
100
100
 
101
101
  def _get_env_path_list(k): # type: (str) -> list[str]
File without changes
@@ -7,12 +7,12 @@ import dataclasses as dc
7
7
  import json
8
8
  import typing as ta
9
9
 
10
- from ..lite.check import check
11
- from ..lite.marshal import OBJ_MARSHALER_FIELD_KEY
12
- from ..lite.marshal import unmarshal_obj
13
- from ..subprocesses.run import SubprocessRun
14
- from ..subprocesses.run import SubprocessRunnable
15
- from ..subprocesses.run import SubprocessRunOutput
10
+ from ...lite.check import check
11
+ from ...lite.marshal import OBJ_MARSHALER_FIELD_KEY
12
+ from ...lite.marshal import unmarshal_obj
13
+ from ...subprocesses.run import SubprocessRun
14
+ from ...subprocesses.run import SubprocessRunnable
15
+ from ...subprocesses.run import SubprocessRunOutput
16
16
 
17
17
 
18
18
  ##
@@ -7,12 +7,12 @@ import dataclasses as dc
7
7
  import enum
8
8
  import typing as ta
9
9
 
10
- from ..lite.check import check
11
- from ..lite.dataclasses import dataclass_repr_omit_falsey
12
- from ..lite.marshal import OBJ_MARSHALER_OMIT_IF_NONE
13
- from ..subprocesses.run import SubprocessRun
14
- from ..subprocesses.run import SubprocessRunnable
15
- from ..subprocesses.run import SubprocessRunOutput
10
+ from ...lite.check import check
11
+ from ...lite.dataclasses import dataclass_repr_omit_falsey
12
+ from ...lite.marshal import OBJ_MARSHALER_OMIT_IF_NONE
13
+ from ...subprocesses.run import SubprocessRun
14
+ from ...subprocesses.run import SubprocessRunnable
15
+ from ...subprocesses.run import SubprocessRunOutput
16
16
 
17
17
 
18
18
  ##
@@ -4,12 +4,12 @@ import dataclasses as dc
4
4
  import os
5
5
  import typing as ta
6
6
 
7
- from ..lite.check import check
8
- from ..lite.timeouts import Timeout
9
- from ..subprocesses.run import SubprocessRun
10
- from ..subprocesses.run import SubprocessRunnable
11
- from ..subprocesses.run import SubprocessRunOutput
12
- from ..subprocesses.sync import subprocesses
7
+ from ...lite.check import check
8
+ from ...lite.timeouts import Timeout
9
+ from ...subprocesses.run import SubprocessRun
10
+ from ...subprocesses.run import SubprocessRunnable
11
+ from ...subprocesses.run import SubprocessRunOutput
12
+ from ...subprocesses.sync import subprocesses
13
13
 
14
14
 
15
15
  ##
omlish/diag/pycharm.py CHANGED
@@ -191,9 +191,23 @@ def pycharm_remote_debugger_attach(prd: PycharmRemoteDebugger) -> None:
191
191
  else:
192
192
  pydevd_pycharm = _import_pydevd_pycharm(version=version)
193
193
 
194
+ import inspect
195
+ st_sig = inspect.signature(pydevd_pycharm.settrace)
196
+
197
+ kw: dict = {}
198
+ if 'stdoutToServer' in st_sig.parameters:
199
+ kw.update(
200
+ stdoutToServer=True,
201
+ stderrToServer=True,
202
+ )
203
+ else:
204
+ kw.update(
205
+ stdout_to_server=True,
206
+ stderr_to_server=True,
207
+ )
208
+
194
209
  pydevd_pycharm.settrace(
195
210
  host,
196
211
  port=prd.port,
197
- stdoutToServer=True,
198
- stderrToServer=True,
212
+ **kw,
199
213
  )
omlish/diag/pydevd.py CHANGED
@@ -6,7 +6,7 @@ an already-debugging PyCharm instance to debug PySpark jobs.
6
6
  TODO:
7
7
  - https://www.jetbrains.com/help/pycharm/remote-debugging-with-product.html#
8
8
 
9
- ==
9
+ ====
10
10
 
11
11
  https://www.jetbrains.com/help/pycharm/remote-debugging-with-product.html#remote-debug-config ->
12
12
 
@@ -25,13 +25,34 @@ buf = textwrap.dedent(f'''
25
25
  stderrToServer=True,
26
26
  )
27
27
  ''') + '\n' * 2 + buf
28
+
29
+ ====
30
+
31
+ TODO: monkeypatch:
32
+
33
+ /Applications/PyCharm.app/Contents/plugins/python-ce/helpers/pydev/_pydev_bundle/pydev_monkey.py ::
34
+
35
+ def starts_with_python_shebang(path):
36
+ try:
37
+ with open(path) as f:
38
+ for line in f:
39
+ line = line.strip()
40
+ if line:
41
+ for name in PYTHON_NAMES:
42
+ if line.startswith('#!/usr/bin/env %s' % name):
43
+ return True
44
+ return False
45
+ except (UnicodeDecodeError, IsADirectoryError): # <-- Add catch for `IsADirectoryError`
46
+ return False
47
+ except:
48
+ traceback.print_exc()
49
+ return False
28
50
  """
29
51
  import json
30
52
  import os
31
53
  import sys
32
54
  import tempfile
33
55
  import textwrap
34
- import threading
35
56
  import types
36
57
  import typing as ta
37
58
 
@@ -119,7 +140,19 @@ def _pydevd() -> types.ModuleType | None:
119
140
 
120
141
 
121
142
  def is_present() -> bool:
122
- return _pydevd() is not None
143
+ # FIXME: try to use `lang.can_import('pydevd'), but raises with:
144
+ # INTERNALERROR> File "/Users/spinlock/src/wrmsr/omlish/omlish/lang/imports/resolving.py", line 16, in can_import
145
+ # INTERNALERROR> spec = importlib.util.find_spec(name, package)
146
+ # INTERNALERROR> File "<frozen importlib.util>", line 111, in find_spec
147
+ # INTERNALERROR> ValueError: pydevd.__spec__ is None
148
+ # Really want to avoid actually importing pydevd due to side-effects, slowness, and even a pkg_resources deprecation
149
+ # warning...
150
+ try:
151
+ __import__('pydevd')
152
+ except ImportError:
153
+ return False
154
+ else:
155
+ return True
123
156
 
124
157
 
125
158
  def get_setup() -> dict | None:
@@ -243,23 +276,23 @@ def maybe_reexec(
243
276
  bootstrap_path = os.path.join(tmpdir, 'bootstrap.py')
244
277
  with open(bootstrap_path, 'w') as f:
245
278
  f.write(textwrap.dedent(f"""
246
- import sys
247
- old_paths = set(sys.path)
248
- for new_path in {sys.path!r}:
249
- if new_path not in old_paths:
250
- sys.path.insert(0, new_path)
251
-
252
- _stderr_write = sys.stderr.write
253
- def stderr_write(*args, **kwargs):
254
- code = sys._getframe(1).f_code
255
- if code is not None and code.co_filename and code.co_filename.endswith('/pydev_log.py'):
256
- return
257
- _stderr_write(*args, **kwargs)
258
- sys.stderr.write = stderr_write
259
-
260
- sys.argv = {args[1:]!r}
261
- import runpy
262
- runpy.run_path({args[1]!r}, run_name='__main__')
279
+ import sys
280
+ old_paths = set(sys.path)
281
+ for new_path in {sys.path!r}:
282
+ if new_path not in old_paths:
283
+ sys.path.insert(0, new_path)
284
+
285
+ _stderr_write = sys.stderr.write
286
+ def stderr_write(*args, **kwargs):
287
+ code = sys._getframe(1).f_code
288
+ if code is not None and code.co_filename and code.co_filename.endswith('/pydev_log.py'):
289
+ return
290
+ _stderr_write(*args, **kwargs)
291
+ sys.stderr.write = stderr_write
292
+
293
+ sys.argv = {args[1:]!r}
294
+ import runpy
295
+ runpy.run_path({args[1]!r}, run_name='__main__')
263
296
  """))
264
297
  args = [args[0], bootstrap_path]
265
298
 
@@ -272,29 +305,14 @@ def debug_unhandled_exception(exc_info: ta.Any = None) -> None:
272
305
 
273
306
  try:
274
307
  import pydevd
275
- from pydevd import pydevd_tracing
276
308
 
277
309
  except ImportError:
278
310
  return
279
311
 
280
- exctype, value, traceback = exc_info
281
- frames = []
282
- while traceback:
283
- frames.append(traceback.tb_frame)
284
- traceback = traceback.tb_next
285
-
286
- thread = threading.current_thread()
287
- frames_by_id = {id(frame): frame for frame in frames}
288
- frame = frames[-1]
289
- exception = (exctype, value, traceback)
290
-
291
- if hasattr(thread, 'additional_info'):
292
- thread.additional_info.pydev_message = 'server exception'
293
- try:
294
- debugger = pydevd.debugger # noqa
295
- except AttributeError:
296
- debugger = pydevd.get_global_debugger() # noqa
312
+ et, e, tb = exc_info
297
313
 
298
- pydevd_tracing.SetTrace(None)
314
+ while tb.tb_next is not None:
315
+ tb = tb.tb_next
316
+ original_frame = tb.tb_frame
299
317
 
300
- debugger.stop_on_unhandled_exception(thread, frame, frames_by_id, exception)
318
+ pydevd.settrace(stop_at_frame=original_frame, suspend=True)
@@ -173,7 +173,7 @@ class InteractiveSocketConsole:
173
173
 
174
174
  # Case 3 (complete)
175
175
  try:
176
- node = ast.parse(source) # type: ignore
176
+ node = ast.parse(source)
177
177
  except (OverflowError, SyntaxError, ValueError):
178
178
  return True
179
179
 
@@ -1,12 +1,18 @@
1
- from .dispatch import ( # noqa
2
- Dispatcher,
3
- )
4
-
5
- from .functions import ( # noqa
6
- function,
7
- )
8
-
9
- from .methods import ( # noqa
10
- install_method,
11
- method,
12
- )
1
+ from .. import lang as _lang
2
+
3
+
4
+ with _lang.auto_proxy_init(globals()):
5
+ ##
6
+
7
+ from .dispatch import ( # noqa
8
+ Dispatcher,
9
+ )
10
+
11
+ from .functions import ( # noqa
12
+ function,
13
+ )
14
+
15
+ from .methods import ( # noqa
16
+ install_method,
17
+ method,
18
+ )