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.
- omlish/.omlish-manifests.json +12 -0
- omlish/__about__.py +15 -15
- omlish/argparse/all.py +17 -9
- omlish/argparse/cli.py +16 -3
- omlish/argparse/utils.py +21 -0
- omlish/asyncs/asyncio/rlock.py +110 -0
- omlish/asyncs/asyncio/sync.py +43 -0
- omlish/asyncs/asyncio/utils.py +2 -0
- omlish/asyncs/sync.py +25 -0
- omlish/bootstrap/_marshal.py +1 -1
- omlish/bootstrap/diag.py +12 -21
- omlish/bootstrap/main.py +2 -5
- omlish/bootstrap/sys.py +27 -28
- omlish/cexts/__init__.py +0 -0
- omlish/cexts/include/omlish/omlish.hh +1 -0
- omlish/collections/__init__.py +13 -1
- omlish/collections/attrregistry.py +210 -0
- omlish/collections/cache/impl.py +1 -0
- omlish/collections/identity.py +1 -0
- omlish/collections/mappings.py +28 -0
- omlish/collections/trie.py +5 -1
- omlish/collections/utils.py +77 -0
- omlish/concurrent/all.py +2 -1
- omlish/concurrent/futures.py +25 -0
- omlish/concurrent/threadlets.py +1 -1
- omlish/daemons/reparent.py +2 -3
- omlish/daemons/spawning.py +2 -3
- omlish/dataclasses/__init__.py +2 -0
- omlish/dataclasses/impl/api/classes/decorator.py +3 -0
- omlish/dataclasses/impl/api/classes/make.py +3 -0
- omlish/dataclasses/impl/concerns/repr.py +15 -2
- omlish/dataclasses/impl/configs.py +97 -37
- omlish/dataclasses/impl/generation/compilation.py +21 -19
- omlish/dataclasses/impl/generation/globals.py +1 -0
- omlish/dataclasses/impl/generation/ops.py +1 -0
- omlish/dataclasses/impl/generation/processor.py +105 -24
- omlish/dataclasses/impl/processing/base.py +8 -0
- omlish/dataclasses/impl/processing/driving.py +8 -8
- omlish/dataclasses/specs.py +1 -0
- omlish/dataclasses/tools/modifiers.py +5 -0
- omlish/diag/cmds/__init__.py +0 -0
- omlish/diag/{lslocks.py → cmds/lslocks.py} +6 -6
- omlish/diag/{lsof.py → cmds/lsof.py} +6 -6
- omlish/diag/{ps.py → cmds/ps.py} +6 -6
- omlish/diag/pycharm.py +16 -2
- omlish/diag/pydevd.py +58 -40
- omlish/diag/replserver/console.py +1 -1
- omlish/dispatch/__init__.py +18 -12
- omlish/dispatch/methods.py +50 -140
- omlish/dom/rendering.py +1 -1
- omlish/formats/dotenv.py +1 -1
- omlish/formats/json/stream/__init__.py +13 -0
- omlish/funcs/guard.py +225 -0
- omlish/graphs/dot/rendering.py +1 -1
- omlish/http/all.py +44 -4
- omlish/http/clients/asyncs.py +33 -27
- omlish/http/clients/base.py +17 -1
- omlish/http/clients/coro/__init__.py +0 -0
- omlish/http/clients/coro/sync.py +171 -0
- omlish/http/clients/default.py +208 -29
- omlish/http/clients/executor.py +56 -0
- omlish/http/clients/httpx.py +72 -11
- omlish/http/clients/middleware.py +181 -0
- omlish/http/clients/sync.py +33 -27
- omlish/http/clients/syncasync.py +49 -0
- omlish/http/clients/urllib.py +6 -3
- omlish/http/coro/client/connection.py +15 -6
- omlish/http/coro/io.py +2 -0
- omlish/http/flasky/__init__.py +40 -0
- omlish/http/flasky/_compat.py +2 -0
- omlish/http/flasky/api.py +82 -0
- omlish/http/flasky/app.py +203 -0
- omlish/http/flasky/cvs.py +59 -0
- omlish/http/flasky/requests.py +20 -0
- omlish/http/flasky/responses.py +23 -0
- omlish/http/flasky/routes.py +23 -0
- omlish/http/flasky/types.py +15 -0
- omlish/http/urls.py +67 -0
- omlish/inject/__init__.py +38 -18
- omlish/inject/_dataclasses.py +4986 -0
- omlish/inject/binder.py +4 -48
- omlish/inject/elements.py +27 -0
- omlish/inject/helpers/__init__.py +0 -0
- omlish/inject/{utils.py → helpers/constfn.py} +3 -3
- omlish/inject/{tags.py → helpers/id.py} +2 -2
- omlish/inject/helpers/multis.py +143 -0
- omlish/inject/helpers/wrappers.py +54 -0
- omlish/inject/impl/elements.py +47 -17
- omlish/inject/impl/injector.py +20 -19
- omlish/inject/impl/inspect.py +10 -1
- omlish/inject/impl/maysync.py +3 -4
- omlish/inject/impl/multis.py +3 -0
- omlish/inject/impl/sync.py +3 -4
- omlish/inject/injector.py +31 -2
- omlish/inject/inspect.py +35 -0
- omlish/inject/maysync.py +2 -4
- omlish/inject/multis.py +8 -0
- omlish/inject/overrides.py +3 -3
- omlish/inject/privates.py +6 -0
- omlish/inject/providers.py +3 -2
- omlish/inject/sync.py +5 -4
- omlish/io/buffers.py +118 -0
- omlish/io/readers.py +29 -0
- omlish/iterators/transforms.py +2 -2
- omlish/lang/__init__.py +178 -97
- omlish/lang/_asyncs.cc +186 -0
- omlish/lang/asyncs.py +17 -0
- omlish/lang/casing.py +11 -0
- omlish/lang/contextmanagers.py +28 -4
- omlish/lang/functions.py +31 -22
- omlish/lang/imports/_capture.cc +11 -9
- omlish/lang/imports/capture.py +363 -170
- omlish/lang/imports/proxy.py +455 -152
- omlish/lang/lazyglobals.py +22 -9
- omlish/lang/params.py +17 -0
- omlish/lang/recursion.py +0 -1
- omlish/lang/sequences.py +124 -0
- omlish/lite/abstract.py +54 -24
- omlish/lite/asyncs.py +2 -2
- omlish/lite/attrops.py +2 -0
- omlish/lite/contextmanagers.py +4 -4
- omlish/lite/dataclasses.py +44 -0
- omlish/lite/maybes.py +8 -0
- omlish/lite/maysync.py +1 -5
- omlish/lite/pycharm.py +1 -1
- omlish/lite/typing.py +6 -0
- omlish/logs/all.py +1 -1
- omlish/logs/utils.py +1 -1
- omlish/manifests/loading.py +2 -1
- omlish/marshal/__init__.py +33 -13
- omlish/marshal/_dataclasses.py +2774 -0
- omlish/marshal/base/configs.py +12 -0
- omlish/marshal/base/contexts.py +36 -21
- omlish/marshal/base/funcs.py +8 -11
- omlish/marshal/base/options.py +8 -0
- omlish/marshal/base/registries.py +146 -44
- omlish/marshal/base/types.py +40 -16
- omlish/marshal/composite/iterables.py +33 -20
- omlish/marshal/composite/literals.py +20 -18
- omlish/marshal/composite/mappings.py +36 -23
- omlish/marshal/composite/maybes.py +29 -19
- omlish/marshal/composite/newtypes.py +16 -16
- omlish/marshal/composite/optionals.py +14 -14
- omlish/marshal/composite/special.py +15 -15
- omlish/marshal/composite/unions/__init__.py +0 -0
- omlish/marshal/composite/unions/literals.py +93 -0
- omlish/marshal/composite/unions/primitives.py +103 -0
- omlish/marshal/factories/invalidate.py +18 -68
- omlish/marshal/factories/method.py +26 -0
- omlish/marshal/factories/moduleimport/factories.py +22 -65
- omlish/marshal/factories/multi.py +13 -25
- omlish/marshal/factories/recursive.py +42 -56
- omlish/marshal/factories/typecache.py +29 -74
- omlish/marshal/factories/typemap.py +42 -43
- omlish/marshal/objects/dataclasses.py +129 -106
- omlish/marshal/objects/marshal.py +18 -14
- omlish/marshal/objects/namedtuples.py +48 -42
- omlish/marshal/objects/unmarshal.py +19 -15
- omlish/marshal/polymorphism/marshal.py +9 -11
- omlish/marshal/polymorphism/metadata.py +16 -5
- omlish/marshal/polymorphism/standard.py +13 -1
- omlish/marshal/polymorphism/unions.py +15 -105
- omlish/marshal/polymorphism/unmarshal.py +9 -10
- omlish/marshal/singular/enums.py +14 -18
- omlish/marshal/standard.py +10 -6
- omlish/marshal/trivial/any.py +1 -1
- omlish/marshal/trivial/forbidden.py +21 -26
- omlish/metadata.py +23 -1
- omlish/os/forkhooks.py +4 -0
- omlish/os/pidfiles/pinning.py +2 -2
- omlish/reflect/types.py +1 -0
- omlish/secrets/marshal.py +1 -1
- omlish/specs/jmespath/__init__.py +12 -3
- omlish/specs/jmespath/_dataclasses.py +2893 -0
- omlish/specs/jmespath/ast.py +1 -1
- omlish/specs/jsonrpc/__init__.py +13 -0
- omlish/specs/jsonrpc/_marshal.py +32 -23
- omlish/specs/jsonrpc/conns.py +10 -7
- omlish/specs/jsonschema/_marshal.py +1 -1
- omlish/specs/jsonschema/keywords/__init__.py +7 -0
- omlish/specs/jsonschema/keywords/_dataclasses.py +1644 -0
- omlish/specs/openapi/_marshal.py +31 -22
- omlish/sql/{tabledefs/alchemy.py → alchemy/tabledefs.py} +2 -2
- omlish/sql/queries/_marshal.py +2 -2
- omlish/sql/queries/rendering.py +1 -1
- omlish/sql/tabledefs/_marshal.py +1 -1
- omlish/subprocesses/base.py +4 -0
- omlish/subprocesses/editor.py +1 -1
- omlish/sync.py +155 -21
- omlish/term/alt.py +60 -0
- omlish/term/confirm.py +8 -8
- omlish/term/pager.py +235 -0
- omlish/term/terminfo.py +935 -0
- omlish/term/termstate.py +67 -0
- omlish/term/vt100/terminal.py +0 -3
- omlish/testing/pytest/plugins/asyncs/fixtures.py +4 -1
- omlish/testing/pytest/plugins/skips.py +2 -1
- omlish/testing/unittest/main.py +3 -3
- omlish/text/docwrap/__init__.py +3 -0
- omlish/text/docwrap/__main__.py +11 -0
- omlish/text/docwrap/api.py +83 -0
- omlish/text/docwrap/cli.py +86 -0
- omlish/text/docwrap/groups.py +84 -0
- omlish/text/docwrap/lists.py +167 -0
- omlish/text/docwrap/parts.py +146 -0
- omlish/text/docwrap/reflowing.py +106 -0
- omlish/text/docwrap/rendering.py +151 -0
- omlish/text/docwrap/utils.py +11 -0
- omlish/text/docwrap/wrapping.py +59 -0
- omlish/text/filecache.py +2 -2
- omlish/text/lorem.py +6 -0
- omlish/text/parts.py +2 -2
- omlish/text/textwrap.py +51 -0
- omlish/typedvalues/marshal.py +85 -59
- omlish/typedvalues/values.py +2 -1
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/METADATA +29 -28
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/RECORD +222 -171
- omlish/dataclasses/impl/generation/mangling.py +0 -18
- omlish/funcs/match.py +0 -227
- omlish/marshal/factories/match.py +0 -34
- omlish/marshal/factories/simple.py +0 -28
- /omlish/inject/impl/{providers2.py → providersmap.py} +0 -0
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/top_level.txt +0 -0
omlish/lang/imports/capture.py
CHANGED
|
@@ -9,10 +9,13 @@ Insufficient alt impls:
|
|
|
9
9
|
|
|
10
10
|
Possible alt impls:
|
|
11
11
|
- aot static analysis, codegen, compare, if valid skip ctxmgr body and inject proxies, otherwise warn and run
|
|
12
|
+
- or just gen code inline, if ta.TYPE_CHECKING: .... else: # @omlish-generate-auto-proxy-import/init
|
|
13
|
+
- generate classic `foo = _lang.proxy_import('.foo', __package__)` blocks
|
|
12
14
|
"""
|
|
13
15
|
import builtins
|
|
14
16
|
import contextlib
|
|
15
17
|
import functools
|
|
18
|
+
import importlib.util
|
|
16
19
|
import sys
|
|
17
20
|
import threading
|
|
18
21
|
import types
|
|
@@ -44,14 +47,27 @@ class ImportCaptureErrors:
|
|
|
44
47
|
return f'{self.__class__.__qualname__}(module={self.module!r}, name={self.name!r})'
|
|
45
48
|
|
|
46
49
|
class ImportError(ImportCaptureError): # noqa
|
|
47
|
-
def __init__(
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
name: str,
|
|
53
|
+
*,
|
|
54
|
+
level: int | None = None,
|
|
55
|
+
from_list: ta.Sequence[str] | None,
|
|
56
|
+
) -> None:
|
|
48
57
|
super().__init__()
|
|
49
58
|
|
|
50
|
-
self.
|
|
59
|
+
self.name = name
|
|
60
|
+
self.level = level
|
|
51
61
|
self.from_list = from_list
|
|
52
62
|
|
|
53
63
|
def __repr__(self) -> str:
|
|
54
|
-
return
|
|
64
|
+
return ''.join([
|
|
65
|
+
f'{self.__class__.__qualname__}(',
|
|
66
|
+
f'name={self.name!r}',
|
|
67
|
+
*([f', level={self.level!r}'] if self.level is not None else []),
|
|
68
|
+
*([f', from_list={self.from_list!r}'] if self.from_list is not None else []),
|
|
69
|
+
')',
|
|
70
|
+
])
|
|
55
71
|
|
|
56
72
|
class ImportStarForbiddenError(ImportError):
|
|
57
73
|
pass
|
|
@@ -60,7 +76,7 @@ class ImportCaptureErrors:
|
|
|
60
76
|
pass
|
|
61
77
|
|
|
62
78
|
class UnreferencedImportsError(ImportCaptureError):
|
|
63
|
-
def __init__(self, unreferenced: ta.
|
|
79
|
+
def __init__(self, unreferenced: ta.Sequence[str]) -> None:
|
|
64
80
|
super().__init__()
|
|
65
81
|
|
|
66
82
|
self.unreferenced = unreferenced
|
|
@@ -76,136 +92,138 @@ class ImportCaptureErrors:
|
|
|
76
92
|
|
|
77
93
|
|
|
78
94
|
class _ImportCaptureHook:
|
|
79
|
-
class ModuleSpec(ta.NamedTuple):
|
|
80
|
-
name: str
|
|
81
|
-
level: int
|
|
82
|
-
|
|
83
|
-
def __str__(self) -> str:
|
|
84
|
-
return f'{"." * self.level}{self.name}'
|
|
85
|
-
|
|
86
|
-
def __repr__(self) -> str:
|
|
87
|
-
return repr(str(self))
|
|
88
|
-
|
|
89
95
|
def __init__(
|
|
90
96
|
self,
|
|
91
97
|
*,
|
|
98
|
+
package: str | None = None,
|
|
92
99
|
forbid_uncaptured_imports: bool = False,
|
|
93
100
|
) -> None:
|
|
94
101
|
super().__init__()
|
|
95
102
|
|
|
103
|
+
self._package = package
|
|
96
104
|
self._forbid_uncaptured_imports = forbid_uncaptured_imports
|
|
97
105
|
|
|
98
|
-
self.
|
|
106
|
+
self._modules_by_name: dict[str, _ImportCaptureHook._Module] = {}
|
|
99
107
|
self._modules_by_module_obj: dict[types.ModuleType, _ImportCaptureHook._Module] = {}
|
|
100
108
|
|
|
101
|
-
self._attrs: dict[_ImportCaptureHook._ModuleAttr, tuple[_ImportCaptureHook._Module, str]] = {}
|
|
102
|
-
|
|
103
109
|
#
|
|
104
110
|
|
|
105
|
-
class
|
|
111
|
+
class _Module:
|
|
106
112
|
def __init__(
|
|
107
113
|
self,
|
|
108
|
-
module: '_ImportCaptureHook._Module',
|
|
109
114
|
name: str,
|
|
115
|
+
getattr_handler: ta.Callable[['_ImportCaptureHook._Module', str], ta.Any],
|
|
116
|
+
*,
|
|
117
|
+
parent: ta.Optional['_ImportCaptureHook._Module'] = None,
|
|
110
118
|
) -> None:
|
|
111
119
|
super().__init__()
|
|
112
120
|
|
|
113
|
-
|
|
114
|
-
|
|
121
|
+
if name.startswith('.'):
|
|
122
|
+
raise ImportCaptureError
|
|
115
123
|
|
|
116
|
-
|
|
117
|
-
|
|
124
|
+
self.name = name
|
|
125
|
+
self.parent = parent
|
|
118
126
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
self,
|
|
122
|
-
spec: '_ImportCaptureHook.ModuleSpec',
|
|
123
|
-
*,
|
|
124
|
-
getattr_handler: ta.Callable[['_ImportCaptureHook._Module', str], ta.Any] | None = None,
|
|
125
|
-
) -> None:
|
|
126
|
-
super().__init__()
|
|
127
|
+
self.base_name = name.rpartition('.')[2]
|
|
128
|
+
self.root: _ImportCaptureHook._Module = parent.root if parent is not None else self # noqa
|
|
127
129
|
|
|
128
|
-
self.
|
|
130
|
+
self.children: dict[str, _ImportCaptureHook._Module] = {}
|
|
131
|
+
self.descendants: set[_ImportCaptureHook._Module] = set()
|
|
129
132
|
|
|
130
|
-
self.module_obj = types.ModuleType(f'<{self.__class__.__qualname__}: {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
+
self.module_obj = types.ModuleType(f'<{self.__class__.__qualname__}: {name}>')
|
|
134
|
+
self.module_obj.__file__ = None
|
|
135
|
+
self.module_obj.__getattr__ = functools.partial(getattr_handler, self) # type: ignore[method-assign] # noqa
|
|
133
136
|
self.initial_module_dict = dict(self.module_obj.__dict__)
|
|
134
137
|
|
|
135
|
-
self.
|
|
136
|
-
self.
|
|
138
|
+
self.explicit = False
|
|
139
|
+
self.immediate = False
|
|
137
140
|
|
|
138
141
|
def __repr__(self) -> str:
|
|
139
|
-
return f'{self.__class__.__name__}
|
|
142
|
+
return f'{self.__class__.__name__}<{self.name}{"!" if self.immediate else "+" if self.explicit else ""}>'
|
|
140
143
|
|
|
141
|
-
|
|
144
|
+
def set_explicit(self) -> None:
|
|
145
|
+
cur: _ImportCaptureHook._Module | None = self
|
|
146
|
+
while cur is not None and not cur.explicit:
|
|
147
|
+
cur.explicit = True
|
|
148
|
+
cur = cur.parent
|
|
149
|
+
|
|
150
|
+
#
|
|
151
|
+
|
|
152
|
+
@property
|
|
153
|
+
def _modules(self) -> ta.Sequence[_Module]:
|
|
154
|
+
return sorted(self._modules_by_name.values(), key=lambda m: m.name)
|
|
155
|
+
|
|
156
|
+
def _get_or_make_module(self, name: str) -> _Module:
|
|
142
157
|
try:
|
|
143
|
-
return self.
|
|
158
|
+
return self._modules_by_name[name]
|
|
144
159
|
except KeyError:
|
|
145
160
|
pass
|
|
146
161
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
162
|
+
parent: _ImportCaptureHook._Module | None = None
|
|
163
|
+
if '.' in name:
|
|
164
|
+
rest, _, attr = name.rpartition('.')
|
|
165
|
+
parent = self._get_or_make_module(rest)
|
|
166
|
+
if attr in parent.children:
|
|
167
|
+
raise ImportCaptureErrors.AttrError(rest, attr)
|
|
168
|
+
|
|
169
|
+
module = _ImportCaptureHook._Module(
|
|
170
|
+
name,
|
|
171
|
+
self._handle_module_getattr,
|
|
172
|
+
parent=parent,
|
|
150
173
|
)
|
|
151
|
-
self.
|
|
174
|
+
self._modules_by_name[name] = module
|
|
152
175
|
self._modules_by_module_obj[module.module_obj] = module
|
|
176
|
+
|
|
177
|
+
if parent is not None:
|
|
178
|
+
parent.children[module.base_name] = module
|
|
179
|
+
setattr(parent.module_obj, module.base_name, module.module_obj)
|
|
180
|
+
parent.root.descendants.add(module)
|
|
181
|
+
|
|
153
182
|
return module
|
|
154
183
|
|
|
155
|
-
def
|
|
156
|
-
if attr in module.
|
|
157
|
-
raise ImportCaptureErrors.AttrError(
|
|
184
|
+
def _make_child_module(self, module: _Module, attr: str) -> _Module:
|
|
185
|
+
if attr in module.children:
|
|
186
|
+
raise ImportCaptureErrors.AttrError(module.name, attr)
|
|
158
187
|
|
|
159
|
-
|
|
160
|
-
if not module.spec.name:
|
|
161
|
-
if not module.spec.level:
|
|
162
|
-
raise ImportCaptureError
|
|
163
|
-
cs = _ImportCaptureHook.ModuleSpec(attr, module.spec.level)
|
|
164
|
-
cm = self._get_or_make_module(cs)
|
|
165
|
-
cm.imported_whole = True
|
|
166
|
-
v = cm.module_obj
|
|
188
|
+
return self._get_or_make_module(f'{module.name}.{attr}')
|
|
167
189
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
190
|
+
#
|
|
191
|
+
|
|
192
|
+
def _handle_module_getattr(self, module: _Module, attr: str) -> ta.Any:
|
|
193
|
+
if not module.explicit:
|
|
194
|
+
raise ImportCaptureErrors.AttrError(module.name, attr)
|
|
172
195
|
|
|
173
|
-
module.
|
|
174
|
-
setattr(module.module_obj, attr, v)
|
|
175
|
-
return v
|
|
196
|
+
return self._make_child_module(module, attr).module_obj
|
|
176
197
|
|
|
177
198
|
def _handle_import(
|
|
178
199
|
self,
|
|
179
|
-
|
|
200
|
+
name: str,
|
|
180
201
|
*,
|
|
181
202
|
from_list: ta.Sequence[str] | None,
|
|
182
|
-
) ->
|
|
183
|
-
|
|
184
|
-
if module.spec.level or not module.spec.name:
|
|
185
|
-
raise ImportCaptureError
|
|
203
|
+
) -> types.ModuleType:
|
|
204
|
+
module = self._get_or_make_module(name)
|
|
186
205
|
|
|
187
|
-
|
|
206
|
+
if from_list is None:
|
|
207
|
+
module.set_explicit()
|
|
208
|
+
module.root.immediate = True
|
|
209
|
+
return module.root.module_obj
|
|
188
210
|
|
|
189
211
|
else:
|
|
190
212
|
for attr in from_list:
|
|
191
213
|
if attr == '*':
|
|
192
|
-
raise ImportCaptureErrors.ImportStarForbiddenError(
|
|
214
|
+
raise ImportCaptureErrors.ImportStarForbiddenError(module.name, from_list=from_list)
|
|
215
|
+
|
|
216
|
+
if (cm := module.children.get(attr)) is None:
|
|
217
|
+
cm = self._make_child_module(module, attr)
|
|
218
|
+
cm.set_explicit()
|
|
219
|
+
cm.immediate = True
|
|
220
|
+
continue
|
|
193
221
|
|
|
194
222
|
x = getattr(module.module_obj, attr)
|
|
223
|
+
if x is not cm.module_obj or x not in self._modules_by_module_obj:
|
|
224
|
+
raise ImportCaptureErrors.AttrError(module.name, attr)
|
|
195
225
|
|
|
196
|
-
|
|
197
|
-
if x is not module.contents.get(attr):
|
|
198
|
-
bad = True
|
|
199
|
-
if isinstance(x, _ImportCaptureHook._ModuleAttr):
|
|
200
|
-
if self._attrs[x] != (module, attr):
|
|
201
|
-
bad = True
|
|
202
|
-
elif isinstance(x, types.ModuleType):
|
|
203
|
-
if x not in self._modules_by_module_obj:
|
|
204
|
-
bad = True
|
|
205
|
-
else:
|
|
206
|
-
bad = True
|
|
207
|
-
if bad:
|
|
208
|
-
raise ImportCaptureErrors.AttrError(str(module.spec), attr)
|
|
226
|
+
return module.module_obj
|
|
209
227
|
|
|
210
228
|
#
|
|
211
229
|
|
|
@@ -225,16 +243,16 @@ class _ImportCaptureHook:
|
|
|
225
243
|
):
|
|
226
244
|
return None
|
|
227
245
|
|
|
228
|
-
|
|
229
|
-
|
|
246
|
+
if level:
|
|
247
|
+
if not self._package:
|
|
248
|
+
raise ImportCaptureError
|
|
249
|
+
name = importlib.util.resolve_name(('.' * level) + name, self._package)
|
|
230
250
|
|
|
231
|
-
self._handle_import(
|
|
232
|
-
|
|
251
|
+
return self._handle_import(
|
|
252
|
+
name,
|
|
233
253
|
from_list=from_list,
|
|
234
254
|
)
|
|
235
255
|
|
|
236
|
-
return module.module_obj
|
|
237
|
-
|
|
238
256
|
@ta.final
|
|
239
257
|
@contextlib.contextmanager
|
|
240
258
|
def hook_context(
|
|
@@ -269,18 +287,24 @@ class _ImportCaptureHook:
|
|
|
269
287
|
self,
|
|
270
288
|
mod_globals: ta.MutableMapping[str, ta.Any], # noqa
|
|
271
289
|
) -> None:
|
|
272
|
-
for m in self.
|
|
290
|
+
for m in self._modules_by_name.values():
|
|
291
|
+
if m.immediate and not m.explicit:
|
|
292
|
+
raise ImportCaptureError
|
|
293
|
+
|
|
294
|
+
if not m.explicit and m.children:
|
|
295
|
+
raise ImportCaptureError
|
|
296
|
+
|
|
273
297
|
for a, o in m.module_obj.__dict__.items():
|
|
274
298
|
try:
|
|
275
299
|
i = m.initial_module_dict[a]
|
|
276
300
|
|
|
277
301
|
except KeyError:
|
|
278
|
-
if o is not m.
|
|
279
|
-
raise ImportCaptureErrors.AttrError(
|
|
302
|
+
if o is not m.children[a].module_obj:
|
|
303
|
+
raise ImportCaptureErrors.AttrError(m.name, a) from None
|
|
280
304
|
|
|
281
305
|
else:
|
|
282
306
|
if o != i:
|
|
283
|
-
raise ImportCaptureErrors.AttrError(
|
|
307
|
+
raise ImportCaptureErrors.AttrError(m.name, a)
|
|
284
308
|
|
|
285
309
|
#
|
|
286
310
|
|
|
@@ -290,24 +314,20 @@ class _ImportCaptureHook:
|
|
|
290
314
|
*,
|
|
291
315
|
collect_unreferenced: bool = False,
|
|
292
316
|
) -> 'ImportCapture.Captured':
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
rem_whole_mods: set[_ImportCaptureHook._Module] = set()
|
|
296
|
-
rem_mod_attrs: set[_ImportCaptureHook._ModuleAttr] = set()
|
|
317
|
+
rem_explicit_mods: set[_ImportCaptureHook._Module] = set()
|
|
297
318
|
if collect_unreferenced:
|
|
298
|
-
|
|
299
|
-
|
|
319
|
+
rem_explicit_mods.update(
|
|
320
|
+
m for m in self._modules_by_name.values()
|
|
321
|
+
if m.immediate
|
|
322
|
+
and m.parent is not None # No good way to tell if user did `import a.b.c` or `import a.b.c as c`
|
|
323
|
+
)
|
|
300
324
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
m, a = self._attrs[obj]
|
|
305
|
-
except KeyError:
|
|
306
|
-
raise ImportCaptureErrors.AttrError(None, attr) from None
|
|
307
|
-
dct.setdefault(m, []).append((a, attr))
|
|
308
|
-
rem_mod_attrs.discard(obj)
|
|
325
|
+
#
|
|
326
|
+
|
|
327
|
+
dct: dict[_ImportCaptureHook._Module, list[tuple[str | None, str]]] = {}
|
|
309
328
|
|
|
310
|
-
|
|
329
|
+
for attr, obj in mod_globals.items():
|
|
330
|
+
if isinstance(obj, _ImportCaptureHook._Module):
|
|
311
331
|
raise ImportCaptureErrors.AttrError(None, attr) from None
|
|
312
332
|
|
|
313
333
|
elif isinstance(obj, types.ModuleType):
|
|
@@ -315,41 +335,80 @@ class _ImportCaptureHook:
|
|
|
315
335
|
m = self._modules_by_module_obj[obj]
|
|
316
336
|
except KeyError:
|
|
317
337
|
continue
|
|
318
|
-
if not m.imported_whole:
|
|
319
|
-
raise RuntimeError(f'ImportCapture module {m.spec!r} not imported_whole')
|
|
320
|
-
dct.setdefault(m, []).append((None, attr))
|
|
321
|
-
rem_whole_mods.discard(m)
|
|
322
|
-
|
|
323
|
-
lst: list[ImportCapture.Import] = []
|
|
324
|
-
for m, ts in dct.items():
|
|
325
|
-
if not m.spec.name:
|
|
326
|
-
if not m.spec.level:
|
|
327
|
-
raise ImportCaptureError
|
|
328
|
-
for imp_attr, as_attr in ts:
|
|
329
|
-
if not imp_attr:
|
|
330
|
-
raise RuntimeError
|
|
331
|
-
lst.append(ImportCapture.Import(
|
|
332
|
-
'.' * m.spec.level + imp_attr,
|
|
333
|
-
[(None, as_attr)],
|
|
334
|
-
))
|
|
335
338
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
339
|
+
if m.explicit:
|
|
340
|
+
dct.setdefault(m, []).append((None, attr))
|
|
341
|
+
if m in rem_explicit_mods:
|
|
342
|
+
# Remove everything reachable from this root *except* items imported immediately, such as
|
|
343
|
+
# `from x import y` - those still need to be immediately reachable.
|
|
344
|
+
rem_explicit_mods -= {dm for dm in m.descendants if not dm.immediate}
|
|
345
|
+
rem_explicit_mods.remove(m)
|
|
346
|
+
|
|
347
|
+
else:
|
|
348
|
+
p = m.parent
|
|
349
|
+
if p is None or not p.explicit:
|
|
350
|
+
raise ImportCaptureError
|
|
351
|
+
dct.setdefault(p, []).append((m.base_name, attr))
|
|
352
|
+
|
|
353
|
+
#
|
|
354
|
+
|
|
355
|
+
mods: dict[str, ImportCapture.Module] = {}
|
|
356
|
+
|
|
357
|
+
def build_import_module(m: _ImportCaptureHook._Module) -> ImportCapture.Module:
|
|
358
|
+
children: dict[str, ImportCapture.Module] = {}
|
|
359
|
+
attrs: list[str] = []
|
|
360
|
+
for cm in sorted(m.children.values(), key=lambda cm: cm.name):
|
|
361
|
+
if not cm.explicit:
|
|
362
|
+
attrs.append(cm.base_name)
|
|
363
|
+
else:
|
|
364
|
+
children[cm.base_name] = build_import_module(cm)
|
|
365
|
+
|
|
366
|
+
mod = ImportCapture.Module(
|
|
367
|
+
m.name,
|
|
368
|
+
children or None,
|
|
369
|
+
attrs or None,
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
if m.parent is None:
|
|
373
|
+
mod.parent = None
|
|
374
|
+
for c in children.values():
|
|
375
|
+
c.parent = mod
|
|
376
|
+
|
|
377
|
+
mods[mod.name] = mod
|
|
378
|
+
return mod
|
|
379
|
+
|
|
380
|
+
root_mods: dict[str, ImportCapture.Module] = {
|
|
381
|
+
m.base_name: build_import_module(m)
|
|
382
|
+
for m in self._modules_by_name.values()
|
|
383
|
+
if m.parent is None
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
mods = dict(sorted(mods.items(), key=lambda t: t[0]))
|
|
387
|
+
root_mods = dict(sorted(root_mods.items(), key=lambda t: t[0]))
|
|
388
|
+
|
|
389
|
+
#
|
|
390
|
+
|
|
391
|
+
imps: list[ImportCapture.Import] = []
|
|
392
|
+
|
|
393
|
+
for m, ts in sorted(dct.items(), key=lambda t: t[0].name):
|
|
394
|
+
imps.append(ImportCapture.Import(
|
|
395
|
+
mods[m.name],
|
|
396
|
+
[r for l, r in ts if l is None] or None,
|
|
397
|
+
[(l, r) for l, r in ts if l is not None] or None,
|
|
398
|
+
))
|
|
399
|
+
|
|
400
|
+
#
|
|
401
|
+
|
|
402
|
+
unreferenced: list[str] | None = None
|
|
403
|
+
if collect_unreferenced and rem_explicit_mods:
|
|
404
|
+
unreferenced = sorted(m.name for m in rem_explicit_mods)
|
|
350
405
|
|
|
351
406
|
return ImportCapture.Captured(
|
|
352
|
-
|
|
407
|
+
{i.module.name: i for i in imps},
|
|
408
|
+
|
|
409
|
+
mods,
|
|
410
|
+
root_mods,
|
|
411
|
+
|
|
353
412
|
unreferenced,
|
|
354
413
|
)
|
|
355
414
|
|
|
@@ -358,6 +417,14 @@ class _ImportCaptureHook:
|
|
|
358
417
|
|
|
359
418
|
|
|
360
419
|
class _AbstractBuiltinsImportCaptureHook(_ImportCaptureHook):
|
|
420
|
+
def __init__(
|
|
421
|
+
self,
|
|
422
|
+
*,
|
|
423
|
+
_frame: types.FrameType | None = None,
|
|
424
|
+
**kwargs: ta.Any,
|
|
425
|
+
) -> None:
|
|
426
|
+
super().__init__(**kwargs)
|
|
427
|
+
|
|
361
428
|
def _new_import(
|
|
362
429
|
self,
|
|
363
430
|
old_import,
|
|
@@ -377,8 +444,9 @@ class _AbstractBuiltinsImportCaptureHook(_ImportCaptureHook):
|
|
|
377
444
|
|
|
378
445
|
if self._forbid_uncaptured_imports:
|
|
379
446
|
raise ImportCaptureErrors.UncapturedImportForbiddenError(
|
|
380
|
-
|
|
381
|
-
|
|
447
|
+
name,
|
|
448
|
+
level=level,
|
|
449
|
+
from_list=fromlist,
|
|
382
450
|
)
|
|
383
451
|
|
|
384
452
|
return old_import(
|
|
@@ -513,11 +581,24 @@ class _SomewhatThreadSafeGlobalBuiltinsImportCaptureHook(_AbstractBuiltinsImport
|
|
|
513
581
|
#
|
|
514
582
|
|
|
515
583
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
584
|
+
_cext_: ta.Any
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
def _cext() -> ta.Any:
|
|
588
|
+
global _cext_
|
|
589
|
+
try:
|
|
590
|
+
return _cext_
|
|
591
|
+
except NameError:
|
|
592
|
+
pass
|
|
593
|
+
|
|
594
|
+
cext: ta.Any
|
|
595
|
+
try:
|
|
596
|
+
from . import _capture as cext # type: ignore
|
|
597
|
+
except ImportError:
|
|
598
|
+
cext = None
|
|
599
|
+
|
|
600
|
+
_cext_ = cext
|
|
601
|
+
return cext
|
|
521
602
|
|
|
522
603
|
|
|
523
604
|
class _FrameBuiltinsImportCaptureHook(_AbstractBuiltinsImportCaptureHook):
|
|
@@ -537,7 +618,7 @@ class _FrameBuiltinsImportCaptureHook(_AbstractBuiltinsImportCaptureHook):
|
|
|
537
618
|
frame: types.FrameType,
|
|
538
619
|
new_builtins: dict[str, ta.Any],
|
|
539
620
|
) -> bool:
|
|
540
|
-
return
|
|
621
|
+
return _cext()._set_frame_builtins(frame, frame.f_builtins, new_builtins) # noqa
|
|
541
622
|
|
|
542
623
|
@contextlib.contextmanager
|
|
543
624
|
def _hook_context(
|
|
@@ -567,39 +648,155 @@ class _FrameBuiltinsImportCaptureHook(_AbstractBuiltinsImportCaptureHook):
|
|
|
567
648
|
#
|
|
568
649
|
|
|
569
650
|
|
|
651
|
+
_CAPTURE_IMPLS: ta.Mapping[str, type[_AbstractBuiltinsImportCaptureHook]] = {
|
|
652
|
+
'cext': _FrameBuiltinsImportCaptureHook,
|
|
653
|
+
'somewhat_safe': _SomewhatThreadSafeGlobalBuiltinsImportCaptureHook,
|
|
654
|
+
'unsafe': _UnsafeGlobalBuiltinsImportCaptureHook,
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
|
|
570
658
|
def _new_import_capture_hook(
|
|
571
659
|
mod_globals: ta.MutableMapping[str, ta.Any], # noqa
|
|
572
660
|
*,
|
|
573
661
|
stack_offset: int = 0,
|
|
662
|
+
capture_impl: str | None = None,
|
|
574
663
|
**kwargs: ta.Any,
|
|
575
664
|
) -> '_ImportCaptureHook':
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
665
|
+
if '_frame' not in kwargs:
|
|
666
|
+
frame: types.FrameType | None = sys._getframe(1 + stack_offset) # noqa
|
|
667
|
+
if frame is None or frame.f_globals is not mod_globals:
|
|
668
|
+
raise ImportCaptureError("Can't find importing frame")
|
|
669
|
+
kwargs['_frame'] = frame
|
|
670
|
+
|
|
671
|
+
kwargs.setdefault('package', mod_globals.get('__package__'))
|
|
579
672
|
|
|
580
|
-
|
|
581
|
-
|
|
673
|
+
cls: type[_AbstractBuiltinsImportCaptureHook]
|
|
674
|
+
if capture_impl is not None:
|
|
675
|
+
cls = _CAPTURE_IMPLS[capture_impl]
|
|
676
|
+
elif _cext() is not None:
|
|
677
|
+
cls = _FrameBuiltinsImportCaptureHook
|
|
678
|
+
else:
|
|
679
|
+
cls = _SomewhatThreadSafeGlobalBuiltinsImportCaptureHook
|
|
582
680
|
|
|
583
|
-
return
|
|
681
|
+
return cls(**kwargs)
|
|
584
682
|
|
|
585
683
|
|
|
586
684
|
##
|
|
587
685
|
|
|
588
686
|
|
|
687
|
+
ImportCaptureModuleKind: ta.TypeAlias = ta.Literal[
|
|
688
|
+
'parent',
|
|
689
|
+
'terminal',
|
|
690
|
+
'leaf',
|
|
691
|
+
]
|
|
692
|
+
|
|
693
|
+
|
|
589
694
|
class ImportCapture:
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
695
|
+
@ta.final
|
|
696
|
+
class Module:
|
|
697
|
+
def __init__(
|
|
698
|
+
self,
|
|
699
|
+
name: str,
|
|
700
|
+
children: ta.Mapping[str, 'ImportCapture.Module'] | None = None,
|
|
701
|
+
attrs: ta.Sequence[str] | None = None,
|
|
702
|
+
) -> None:
|
|
703
|
+
self.name = name
|
|
704
|
+
self.children = children
|
|
705
|
+
self.attrs = attrs
|
|
706
|
+
|
|
707
|
+
self.base_name = name.rpartition('.')[2]
|
|
708
|
+
|
|
709
|
+
if not self.children and not self.attrs:
|
|
710
|
+
self.kind = 'leaf'
|
|
711
|
+
elif not self.children or all(c.kind == 'leaf' for c in self.children.values()):
|
|
712
|
+
self.kind = 'terminal'
|
|
713
|
+
else:
|
|
714
|
+
self.kind = 'parent'
|
|
715
|
+
|
|
716
|
+
parent: ta.Optional['ImportCapture.Module']
|
|
717
|
+
|
|
718
|
+
kind: ImportCaptureModuleKind
|
|
719
|
+
|
|
720
|
+
def __repr__(self) -> str:
|
|
721
|
+
return ''.join([
|
|
722
|
+
f'{self.__class__.__name__}(',
|
|
723
|
+
f'{self.name!r}',
|
|
724
|
+
f', :{self.kind}',
|
|
725
|
+
*([f', children=[{", ".join(map(repr, self.children))}]'] if self.children else []),
|
|
726
|
+
*([f', attrs={self.attrs!r}'] if self.attrs else []),
|
|
727
|
+
')',
|
|
728
|
+
])
|
|
729
|
+
|
|
730
|
+
_root: 'ImportCapture.Module'
|
|
731
|
+
|
|
732
|
+
@property
|
|
733
|
+
def root(self) -> 'ImportCapture.Module':
|
|
734
|
+
try:
|
|
735
|
+
return self._root
|
|
736
|
+
except AttributeError:
|
|
737
|
+
pass
|
|
738
|
+
|
|
739
|
+
root = self
|
|
740
|
+
while root.parent is not None:
|
|
741
|
+
root = root.parent
|
|
742
|
+
self._root = root
|
|
743
|
+
return root
|
|
744
|
+
|
|
745
|
+
@ta.final
|
|
746
|
+
class Import:
|
|
747
|
+
def __init__(
|
|
748
|
+
self,
|
|
749
|
+
module: 'ImportCapture.Module',
|
|
750
|
+
as_: ta.Sequence[str] | None,
|
|
751
|
+
attrs: ta.Sequence[tuple[str, str]] | None, # ('foo', 'bar') -> `import foo as bar` - explicitly not a dict # noqa
|
|
752
|
+
) -> None:
|
|
753
|
+
self.module = module
|
|
754
|
+
self.as_ = as_
|
|
755
|
+
self.attrs = attrs
|
|
593
756
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
757
|
+
def __repr__(self) -> str:
|
|
758
|
+
return ''.join([
|
|
759
|
+
f'{self.__class__.__name__}(',
|
|
760
|
+
f'{self.module.name!r}',
|
|
761
|
+
*([f', as_={self.as_!r}'] if self.as_ else []),
|
|
762
|
+
*([f', attrs={self.attrs!r}'] if self.attrs else []),
|
|
763
|
+
')',
|
|
764
|
+
])
|
|
765
|
+
|
|
766
|
+
@ta.final
|
|
767
|
+
class Captured:
|
|
768
|
+
def __init__(
|
|
769
|
+
self,
|
|
770
|
+
|
|
771
|
+
imports: ta.Mapping[str, 'ImportCapture.Import'],
|
|
772
|
+
|
|
773
|
+
modules: ta.Mapping[str, 'ImportCapture.Module'],
|
|
774
|
+
root_modules: ta.Mapping[str, 'ImportCapture.Module'],
|
|
775
|
+
|
|
776
|
+
unreferenced: ta.Sequence[str] | None,
|
|
777
|
+
) -> None:
|
|
778
|
+
self.imports = imports
|
|
779
|
+
|
|
780
|
+
self.modules = modules
|
|
781
|
+
self.root_modules = root_modules
|
|
782
|
+
|
|
783
|
+
self.unreferenced = unreferenced
|
|
597
784
|
|
|
598
785
|
@property
|
|
599
786
|
def attrs(self) -> ta.Iterator[str]:
|
|
600
|
-
for pi in self.imports:
|
|
601
|
-
|
|
602
|
-
yield
|
|
787
|
+
for pi in self.imports.values():
|
|
788
|
+
if pi.as_:
|
|
789
|
+
yield from pi.as_
|
|
790
|
+
if pi.attrs:
|
|
791
|
+
for _, a in pi.attrs:
|
|
792
|
+
yield a
|
|
793
|
+
|
|
794
|
+
EMPTY_CAPTURED: ta.ClassVar[Captured] = Captured(
|
|
795
|
+
{},
|
|
796
|
+
{},
|
|
797
|
+
{},
|
|
798
|
+
None,
|
|
799
|
+
)
|
|
603
800
|
|
|
604
801
|
#
|
|
605
802
|
|
|
@@ -649,7 +846,7 @@ class ImportCapture:
|
|
|
649
846
|
def capture(
|
|
650
847
|
self,
|
|
651
848
|
*,
|
|
652
|
-
unreferenced_callback: ta.Callable[[ta.
|
|
849
|
+
unreferenced_callback: ta.Callable[[ta.Sequence[str]], None] | None = None,
|
|
653
850
|
raise_unreferenced: bool = False,
|
|
654
851
|
) -> ta.Iterator[ta.Self]:
|
|
655
852
|
if self._result_ is not None:
|
|
@@ -657,10 +854,7 @@ class ImportCapture:
|
|
|
657
854
|
|
|
658
855
|
if self._disabled:
|
|
659
856
|
self._result_ = ImportCapture._Result(
|
|
660
|
-
ImportCapture.
|
|
661
|
-
[],
|
|
662
|
-
None,
|
|
663
|
-
),
|
|
857
|
+
ImportCapture.EMPTY_CAPTURED,
|
|
664
858
|
)
|
|
665
859
|
yield self
|
|
666
860
|
return
|
|
@@ -681,9 +875,8 @@ class ImportCapture:
|
|
|
681
875
|
if raise_unreferenced:
|
|
682
876
|
raise ImportCaptureErrors.UnreferencedImportsError(blt.unreferenced)
|
|
683
877
|
|
|
684
|
-
for
|
|
685
|
-
|
|
686
|
-
del self._mod_globals[a]
|
|
878
|
+
for a in blt.attrs:
|
|
879
|
+
del self._mod_globals[a]
|
|
687
880
|
|
|
688
881
|
self._result_ = ImportCapture._Result(
|
|
689
882
|
blt,
|