omlish 0.0.0.dev470__py3-none-any.whl → 0.0.0.dev471__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.
- omlish/__about__.py +2 -2
 - omlish/inject/__init__.py +4 -0
 - omlish/inject/elements.py +17 -0
 - omlish/inject/impl/elements.py +17 -2
 - omlish/inject/impl/injector.py +17 -19
 - omlish/inject/impl/maysync.py +3 -4
 - omlish/inject/impl/sync.py +3 -4
 - omlish/inject/injector.py +31 -2
 - omlish/inject/maysync.py +2 -4
 - omlish/inject/sync.py +5 -4
 - omlish/lang/imports/proxy.py +10 -1
 - omlish/term/pager.py +235 -0
 - omlish/term/terminfo.py +935 -0
 - omlish/term/termstate.py +67 -0
 - {omlish-0.0.0.dev470.dist-info → omlish-0.0.0.dev471.dist-info}/METADATA +1 -1
 - {omlish-0.0.0.dev470.dist-info → omlish-0.0.0.dev471.dist-info}/RECORD +21 -18
 - /omlish/inject/impl/{providers2.py → providersmap.py} +0 -0
 - {omlish-0.0.0.dev470.dist-info → omlish-0.0.0.dev471.dist-info}/WHEEL +0 -0
 - {omlish-0.0.0.dev470.dist-info → omlish-0.0.0.dev471.dist-info}/entry_points.txt +0 -0
 - {omlish-0.0.0.dev470.dist-info → omlish-0.0.0.dev471.dist-info}/licenses/LICENSE +0 -0
 - {omlish-0.0.0.dev470.dist-info → omlish-0.0.0.dev471.dist-info}/top_level.txt +0 -0
 
    
        omlish/__about__.py
    CHANGED
    
    
    
        omlish/inject/__init__.py
    CHANGED
    
    
    
        omlish/inject/elements.py
    CHANGED
    
    | 
         @@ -6,6 +6,12 @@ from .. import lang 
     | 
|
| 
       6 
6 
     | 
    
         
             
            from .impl.origins import HasOriginsImpl
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         | 
| 
      
 9 
     | 
    
         
            +
            if ta.TYPE_CHECKING:
         
     | 
| 
      
 10 
     | 
    
         
            +
                from .impl import elements as _elements
         
     | 
| 
      
 11 
     | 
    
         
            +
            else:
         
     | 
| 
      
 12 
     | 
    
         
            +
                _elements = lang.proxy_import('.impl.elements', __package__)
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
       9 
15 
     | 
    
         
             
            ##
         
     | 
| 
       10 
16 
     | 
    
         | 
| 
       11 
17 
     | 
    
         | 
| 
         @@ -74,3 +80,14 @@ def iter_elements(*args: Elemental) -> ta.Iterator[Element]: 
     | 
|
| 
       74 
80 
     | 
    
         
             
                        yield from a
         
     | 
| 
       75 
81 
     | 
    
         
             
                    else:
         
     | 
| 
       76 
82 
     | 
    
         
             
                        raise TypeError(a)
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
            ##
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
            class CollectedElements(lang.PackageSealed, lang.Abstract):
         
     | 
| 
      
 89 
     | 
    
         
            +
                pass
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
            def collect_elements(es: Elements | CollectedElements) -> CollectedElements:
         
     | 
| 
      
 93 
     | 
    
         
            +
                return _elements.collect_elements(es)
         
     | 
    
        omlish/inject/impl/elements.py
    CHANGED
    
    | 
         @@ -27,6 +27,7 @@ from ... import collections as col 
     | 
|
| 
       27 
27 
     | 
    
         
             
            from ... import lang
         
     | 
| 
       28 
28 
     | 
    
         
             
            from ..bindings import Binding
         
     | 
| 
       29 
29 
     | 
    
         
             
            from ..eagers import Eager
         
     | 
| 
      
 30 
     | 
    
         
            +
            from ..elements import CollectedElements
         
     | 
| 
       30 
31 
     | 
    
         
             
            from ..elements import Element
         
     | 
| 
       31 
32 
     | 
    
         
             
            from ..elements import Elements
         
     | 
| 
       32 
33 
     | 
    
         
             
            from ..errors import ConflictingKeyError
         
     | 
| 
         @@ -47,7 +48,7 @@ from .multis import make_multi_provider_impl 
     | 
|
| 
       47 
48 
     | 
    
         
             
            from .origins import Origins
         
     | 
| 
       48 
49 
     | 
    
         
             
            from .origins import set_origins
         
     | 
| 
       49 
50 
     | 
    
         
             
            from .providers import ProviderImpl
         
     | 
| 
       50 
     | 
    
         
            -
            from . 
     | 
| 
      
 51 
     | 
    
         
            +
            from .providersmap import make_provider_impl
         
     | 
| 
       51 
52 
     | 
    
         
             
            from .scopes import make_scope_impl
         
     | 
| 
       52 
53 
     | 
    
         | 
| 
       53 
54 
     | 
    
         | 
| 
         @@ -80,7 +81,7 @@ _NON_BINDING_ELEMENT_TYPES: tuple[type[Element], ...] = ( 
     | 
|
| 
       80 
81 
     | 
    
         
             
            )
         
     | 
| 
       81 
82 
     | 
    
         | 
| 
       82 
83 
     | 
    
         | 
| 
       83 
     | 
    
         
            -
            class ElementCollection(lang.Final):
         
     | 
| 
      
 84 
     | 
    
         
            +
            class ElementCollection(CollectedElements, lang.Final):
         
     | 
| 
       84 
85 
     | 
    
         
             
                def __init__(self, es: Elements) -> None:
         
     | 
| 
       85 
86 
     | 
    
         
             
                    super().__init__()
         
     | 
| 
       86 
87 
     | 
    
         | 
| 
         @@ -208,6 +209,10 @@ class ElementCollection(lang.Final): 
     | 
|
| 
       208 
209 
     | 
    
         | 
| 
       209 
210 
     | 
    
         
             
                ##
         
     | 
| 
       210 
211 
     | 
    
         | 
| 
      
 212 
     | 
    
         
            +
                @lang.cached_function
         
     | 
| 
      
 213 
     | 
    
         
            +
                def scope_binding_scopes(self) -> ta.Sequence[Scope]:
         
     | 
| 
      
 214 
     | 
    
         
            +
                    return [sb.scope for sb in self.elements_of_type(ScopeBinding)]
         
     | 
| 
      
 215 
     | 
    
         
            +
             
     | 
| 
       211 
216 
     | 
    
         
             
                @lang.cached_function
         
     | 
| 
       212 
217 
     | 
    
         
             
                def eager_keys_by_scope(self) -> ta.Mapping[Scope, ta.Sequence[Key]]:
         
     | 
| 
       213 
218 
     | 
    
         
             
                    bim = self.binding_impl_map()
         
     | 
| 
         @@ -216,3 +221,13 @@ class ElementCollection(lang.Final): 
     | 
|
| 
       216 
221 
     | 
    
         
             
                        bi = bim[e.key]
         
     | 
| 
       217 
222 
     | 
    
         
             
                        ret.setdefault(bi.scope, []).append(e.key)
         
     | 
| 
       218 
223 
     | 
    
         
             
                    return ret
         
     | 
| 
      
 224 
     | 
    
         
            +
             
     | 
| 
      
 225 
     | 
    
         
            +
             
     | 
| 
      
 226 
     | 
    
         
            +
            ##
         
     | 
| 
      
 227 
     | 
    
         
            +
             
     | 
| 
      
 228 
     | 
    
         
            +
             
     | 
| 
      
 229 
     | 
    
         
            +
            def collect_elements(es: Elements | CollectedElements) -> ElementCollection:
         
     | 
| 
      
 230 
     | 
    
         
            +
                if isinstance(es, CollectedElements):
         
     | 
| 
      
 231 
     | 
    
         
            +
                    return check.isinstance(es, ElementCollection)
         
     | 
| 
      
 232 
     | 
    
         
            +
                else:
         
     | 
| 
      
 233 
     | 
    
         
            +
                    return ElementCollection(es)
         
     | 
    
        omlish/inject/impl/injector.py
    CHANGED
    
    | 
         @@ -2,14 +2,12 @@ 
     | 
|
| 
       2 
2 
     | 
    
         
             
            TODO:
         
     | 
| 
       3 
3 
     | 
    
         
             
             - ** can currently bind in a child/private scope shadowing an external parent binding **
         
     | 
| 
       4 
4 
     | 
    
         
             
             - better source tracking
         
     | 
| 
       5 
     | 
    
         
            -
             - cache/export ElementCollections lol
         
     | 
| 
       6 
5 
     | 
    
         
             
             - scope bindings, auto in root
         
     | 
| 
       7 
6 
     | 
    
         
             
             - injector-internal / blacklisted bindings (Injector itself, default scopes) without rebuilding ElementCollection
         
     | 
| 
       8 
7 
     | 
    
         
             
             - config - proxies, impl select, etc
         
     | 
| 
       9 
8 
     | 
    
         
             
              - config is probably shared with ElementCollection... but not 'bound', must be shared everywhere
         
     | 
| 
       10 
9 
     | 
    
         
             
              - InjectorRoot object?
         
     | 
| 
       11 
10 
     | 
    
         
             
             - ** eagers in any scope, on scope init/open
         
     | 
| 
       12 
     | 
    
         
            -
             - injection listeners
         
     | 
| 
       13 
11 
     | 
    
         
             
             - unions - raise on ambiguous - usecase: sql.AsyncEngineLike
         
     | 
| 
       14 
12 
     | 
    
         
             
             - multiple live request scopes on single injector - use private injectors?
         
     | 
| 
       15 
13 
     | 
    
         
             
             - more listeners - UnboundKeyListener
         
     | 
| 
         @@ -24,7 +22,7 @@ import weakref 
     | 
|
| 
       24 
22 
     | 
    
         
             
            from ... import check
         
     | 
| 
       25 
23 
     | 
    
         
             
            from ... import lang
         
     | 
| 
       26 
24 
     | 
    
         
             
            from ...logs import all as logs
         
     | 
| 
       27 
     | 
    
         
            -
            from ..elements import  
     | 
| 
      
 25 
     | 
    
         
            +
            from ..elements import CollectedElements
         
     | 
| 
       28 
26 
     | 
    
         
             
            from ..errors import CyclicDependencyError
         
     | 
| 
       29 
27 
     | 
    
         
             
            from ..errors import UnboundKeyError
         
     | 
| 
       30 
28 
     | 
    
         
             
            from ..injector import AsyncInjector
         
     | 
| 
         @@ -33,7 +31,6 @@ from ..keys import Key 
     | 
|
| 
       33 
31 
     | 
    
         
             
            from ..keys import as_key
         
     | 
| 
       34 
32 
     | 
    
         
             
            from ..listeners import ProvisionListener
         
     | 
| 
       35 
33 
     | 
    
         
             
            from ..listeners import ProvisionListenerBinding
         
     | 
| 
       36 
     | 
    
         
            -
            from ..scopes import ScopeBinding
         
     | 
| 
       37 
34 
     | 
    
         
             
            from ..scopes import Singleton
         
     | 
| 
       38 
35 
     | 
    
         
             
            from ..scopes import ThreadScope
         
     | 
| 
       39 
36 
     | 
    
         
             
            from ..types import Scope
         
     | 
| 
         @@ -61,14 +58,12 @@ DEFAULT_SCOPES: list[Scope] = [ 
     | 
|
| 
       61 
58 
     | 
    
         
             
            class AsyncInjectorImpl(AsyncInjector, lang.Final):
         
     | 
| 
       62 
59 
     | 
    
         
             
                def __init__(
         
     | 
| 
       63 
60 
     | 
    
         
             
                        self,
         
     | 
| 
       64 
     | 
    
         
            -
                        ec:  
     | 
| 
      
 61 
     | 
    
         
            +
                        ec: CollectedElements,
         
     | 
| 
       65 
62 
     | 
    
         
             
                        p: ta.Optional['AsyncInjectorImpl'] = None,
         
     | 
| 
       66 
63 
     | 
    
         
             
                        *,
         
     | 
| 
       67 
64 
     | 
    
         
             
                        internal_consts: dict[Key, ta.Any] | None = None,
         
     | 
| 
       68 
65 
     | 
    
         
             
                ) -> None:
         
     | 
| 
       69 
     | 
    
         
            -
                     
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
                    self._ec = check.isinstance(ec, ElementCollection)
         
     | 
| 
      
 66 
     | 
    
         
            +
                    self._ec = (ec := check.isinstance(ec, ElementCollection))
         
     | 
| 
       72 
67 
     | 
    
         
             
                    self._p: AsyncInjectorImpl | None = check.isinstance(p, (AsyncInjectorImpl, None))
         
     | 
| 
       73 
68 
     | 
    
         | 
| 
       74 
69 
     | 
    
         
             
                    self._internal_consts: dict[Key, ta.Any] = {
         
     | 
| 
         @@ -77,28 +72,31 @@ class AsyncInjectorImpl(AsyncInjector, lang.Final): 
     | 
|
| 
       77 
72 
     | 
    
         
             
                    }
         
     | 
| 
       78 
73 
     | 
    
         | 
| 
       79 
74 
     | 
    
         
             
                    self._bim = ec.binding_impl_map()
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
       80 
76 
     | 
    
         
             
                    self._ekbs = ec.eager_keys_by_scope()
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
       81 
78 
     | 
    
         
             
                    self._pls: tuple[ProvisionListener, ...] = tuple(
         
     | 
| 
       82 
79 
     | 
    
         
             
                        b.listener  # type: ignore[attr-defined]
         
     | 
| 
       83 
80 
     | 
    
         
             
                        for b in itertools.chain(
         
     | 
| 
       84 
81 
     | 
    
         
             
                            ec.elements_of_type(ProvisionListenerBinding),
         
     | 
| 
       85 
     | 
    
         
            -
                             
     | 
| 
      
 82 
     | 
    
         
            +
                            p._pls if p is not None else (),  # noqa
         
     | 
| 
       86 
83 
     | 
    
         
             
                        )
         
     | 
| 
       87 
84 
     | 
    
         
             
                    )
         
     | 
| 
       88 
85 
     | 
    
         | 
| 
       89 
     | 
    
         
            -
                    self._cs: weakref.WeakSet[AsyncInjectorImpl] | None = None  # noqa
         
     | 
| 
       90 
86 
     | 
    
         
             
                    self._root: AsyncInjectorImpl = p._root if p is not None else self  # noqa
         
     | 
| 
       91 
87 
     | 
    
         | 
| 
       92 
     | 
    
         
            -
                    self.__cur_req: AsyncInjectorImpl._Request | None = None
         
     | 
| 
       93 
     | 
    
         
            -
             
     | 
| 
       94 
     | 
    
         
            -
                    ss = [
         
     | 
| 
       95 
     | 
    
         
            -
                        *DEFAULT_SCOPES,
         
     | 
| 
       96 
     | 
    
         
            -
                        *[sb.scope for sb in ec.elements_of_type(ScopeBinding)],
         
     | 
| 
       97 
     | 
    
         
            -
                    ]
         
     | 
| 
       98 
88 
     | 
    
         
             
                    self._scopes: dict[Scope, ScopeImpl] = {
         
     | 
| 
       99 
     | 
    
         
            -
                        s: make_scope_impl(s) 
     | 
| 
      
 89 
     | 
    
         
            +
                        s: make_scope_impl(s)
         
     | 
| 
      
 90 
     | 
    
         
            +
                        for s in itertools.chain(
         
     | 
| 
      
 91 
     | 
    
         
            +
                            DEFAULT_SCOPES,
         
     | 
| 
      
 92 
     | 
    
         
            +
                            ec.scope_binding_scopes(),
         
     | 
| 
      
 93 
     | 
    
         
            +
                        )
         
     | 
| 
       100 
94 
     | 
    
         
             
                    }
         
     | 
| 
       101 
95 
     | 
    
         | 
| 
      
 96 
     | 
    
         
            +
                _cs: weakref.WeakSet['AsyncInjectorImpl'] | None = None  # noqa
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                __cur_req: ta.Optional['AsyncInjectorImpl._Request'] = None
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
       102 
100 
     | 
    
         
             
                #
         
     | 
| 
       103 
101 
     | 
    
         | 
| 
       104 
102 
     | 
    
         
             
                _has_run_init: bool = False
         
     | 
| 
         @@ -259,7 +257,7 @@ class AsyncInjectorImpl(AsyncInjector, lang.Final): 
     | 
|
| 
       259 
257 
     | 
    
         
             
                    return obj(**kws)
         
     | 
| 
       260 
258 
     | 
    
         | 
| 
       261 
259 
     | 
    
         | 
| 
       262 
     | 
    
         
            -
            async def create_async_injector( 
     | 
| 
       263 
     | 
    
         
            -
                i = AsyncInjectorImpl( 
     | 
| 
      
 260 
     | 
    
         
            +
            async def create_async_injector(ce: CollectedElements) -> AsyncInjector:
         
     | 
| 
      
 261 
     | 
    
         
            +
                i = AsyncInjectorImpl(ce)
         
     | 
| 
       264 
262 
     | 
    
         
             
                await i._init()  # noqa
         
     | 
| 
       265 
263 
     | 
    
         
             
                return i
         
     | 
    
        omlish/inject/impl/maysync.py
    CHANGED
    
    | 
         @@ -1,13 +1,12 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            import typing as ta
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            from ... import lang
         
     | 
| 
       4 
     | 
    
         
            -
            from ..elements import  
     | 
| 
      
 4 
     | 
    
         
            +
            from ..elements import CollectedElements
         
     | 
| 
       5 
5 
     | 
    
         
             
            from ..injector import AsyncInjector
         
     | 
| 
       6 
6 
     | 
    
         
             
            from ..inspect import KwargsTarget
         
     | 
| 
       7 
7 
     | 
    
         
             
            from ..keys import Key
         
     | 
| 
       8 
8 
     | 
    
         
             
            from ..maysync import MaysyncInjector
         
     | 
| 
       9 
9 
     | 
    
         
             
            from ..sync import Injector
         
     | 
| 
       10 
     | 
    
         
            -
            from .elements import ElementCollection
         
     | 
| 
       11 
10 
     | 
    
         
             
            from .injector import AsyncInjectorImpl
         
     | 
| 
       12 
11 
     | 
    
         | 
| 
       13 
12 
     | 
    
         | 
| 
         @@ -30,10 +29,10 @@ class MaysyncInjectorImpl(MaysyncInjector, lang.Final): 
     | 
|
| 
       30 
29 
     | 
    
         
             
                    return lang.run_maysync(self._ai.inject(obj))
         
     | 
| 
       31 
30 
     | 
    
         | 
| 
       32 
31 
     | 
    
         | 
| 
       33 
     | 
    
         
            -
            def create_maysync_injector( 
     | 
| 
      
 32 
     | 
    
         
            +
            def create_maysync_injector(ce: CollectedElements) -> MaysyncInjector:
         
     | 
| 
       34 
33 
     | 
    
         
             
                si = MaysyncInjectorImpl()
         
     | 
| 
       35 
34 
     | 
    
         
             
                ai = AsyncInjectorImpl(
         
     | 
| 
       36 
     | 
    
         
            -
                     
     | 
| 
      
 35 
     | 
    
         
            +
                    ce,
         
     | 
| 
       37 
36 
     | 
    
         
             
                    internal_consts={
         
     | 
| 
       38 
37 
     | 
    
         
             
                        Key(MaysyncInjector): si,
         
     | 
| 
       39 
38 
     | 
    
         
             
                        Key(Injector): si,
         
     | 
    
        omlish/inject/impl/sync.py
    CHANGED
    
    | 
         @@ -1,12 +1,11 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            import typing as ta
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            from ... import lang
         
     | 
| 
       4 
     | 
    
         
            -
            from ..elements import  
     | 
| 
      
 4 
     | 
    
         
            +
            from ..elements import CollectedElements
         
     | 
| 
       5 
5 
     | 
    
         
             
            from ..injector import AsyncInjector
         
     | 
| 
       6 
6 
     | 
    
         
             
            from ..inspect import KwargsTarget
         
     | 
| 
       7 
7 
     | 
    
         
             
            from ..keys import Key
         
     | 
| 
       8 
8 
     | 
    
         
             
            from ..sync import Injector
         
     | 
| 
       9 
     | 
    
         
            -
            from .elements import ElementCollection
         
     | 
| 
       10 
9 
     | 
    
         
             
            from .injector import AsyncInjectorImpl
         
     | 
| 
       11 
10 
     | 
    
         | 
| 
       12 
11 
     | 
    
         | 
| 
         @@ -29,10 +28,10 @@ class InjectorImpl(Injector, lang.Final): 
     | 
|
| 
       29 
28 
     | 
    
         
             
                    return lang.sync_await(self._ai.inject(obj))
         
     | 
| 
       30 
29 
     | 
    
         | 
| 
       31 
30 
     | 
    
         | 
| 
       32 
     | 
    
         
            -
            def create_injector( 
     | 
| 
      
 31 
     | 
    
         
            +
            def create_injector(ce: CollectedElements) -> Injector:
         
     | 
| 
       33 
32 
     | 
    
         
             
                si = InjectorImpl()
         
     | 
| 
       34 
33 
     | 
    
         
             
                ai = AsyncInjectorImpl(
         
     | 
| 
       35 
     | 
    
         
            -
                     
     | 
| 
      
 34 
     | 
    
         
            +
                    ce,
         
     | 
| 
       36 
35 
     | 
    
         
             
                    internal_consts={
         
     | 
| 
       37 
36 
     | 
    
         
             
                        Key(Injector): si,
         
     | 
| 
       38 
37 
     | 
    
         
             
                    },
         
     | 
    
        omlish/inject/injector.py
    CHANGED
    
    | 
         @@ -1,9 +1,12 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            import abc
         
     | 
| 
       2 
2 
     | 
    
         
             
            import typing as ta
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
      
 4 
     | 
    
         
            +
            from .. import check
         
     | 
| 
       4 
5 
     | 
    
         
             
            from .. import lang
         
     | 
| 
      
 6 
     | 
    
         
            +
            from .elements import CollectedElements
         
     | 
| 
       5 
7 
     | 
    
         
             
            from .elements import Elemental
         
     | 
| 
       6 
8 
     | 
    
         
             
            from .elements import as_elements
         
     | 
| 
      
 9 
     | 
    
         
            +
            from .elements import collect_elements
         
     | 
| 
       7 
10 
     | 
    
         
             
            from .inspect import KwargsTarget
         
     | 
| 
       8 
11 
     | 
    
         
             
            from .keys import Key
         
     | 
| 
       9 
12 
     | 
    
         | 
| 
         @@ -44,5 +47,31 @@ class AsyncInjector(lang.Abstract): 
     | 
|
| 
       44 
47 
     | 
    
         
             
                    return self.provide(target)
         
     | 
| 
       45 
48 
     | 
    
         | 
| 
       46 
49 
     | 
    
         | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
      
 50 
     | 
    
         
            +
            ##
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
            @ta.final
         
     | 
| 
      
 54 
     | 
    
         
            +
            class _InjectorCreator(ta.Generic[T]):
         
     | 
| 
      
 55 
     | 
    
         
            +
                def __init__(self, fac: ta.Callable[[CollectedElements], T]) -> None:
         
     | 
| 
      
 56 
     | 
    
         
            +
                    self._fac = fac
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                @ta.overload
         
     | 
| 
      
 59 
     | 
    
         
            +
                def __call__(self, es: CollectedElements, /) -> T: ...
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                @ta.overload
         
     | 
| 
      
 62 
     | 
    
         
            +
                def __call__(self, *es: Elemental) -> T: ...
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                def __call__(self, arg0, *argv):
         
     | 
| 
      
 65 
     | 
    
         
            +
                    ce: CollectedElements
         
     | 
| 
      
 66 
     | 
    
         
            +
                    if isinstance(arg0, CollectedElements):
         
     | 
| 
      
 67 
     | 
    
         
            +
                        check.arg(not argv)
         
     | 
| 
      
 68 
     | 
    
         
            +
                        ce = arg0
         
     | 
| 
      
 69 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 70 
     | 
    
         
            +
                        ce = collect_elements(as_elements(arg0, *argv))
         
     | 
| 
      
 71 
     | 
    
         
            +
                    return self._fac(ce)
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
            ##
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
            create_async_injector = _InjectorCreator[ta.Awaitable[AsyncInjector]](lambda ce: _injector.create_async_injector(ce))
         
     | 
    
        omlish/inject/maysync.py
    CHANGED
    
    | 
         @@ -1,8 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            import typing as ta
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            from .. import lang
         
     | 
| 
       4 
     | 
    
         
            -
            from . 
     | 
| 
       5 
     | 
    
         
            -
            from .elements import as_elements
         
     | 
| 
      
 4 
     | 
    
         
            +
            from .injector import _InjectorCreator
         
     | 
| 
       6 
5 
     | 
    
         
             
            from .sync import Injector
         
     | 
| 
       7 
6 
     | 
    
         | 
| 
       8 
7 
     | 
    
         | 
| 
         @@ -25,5 +24,4 @@ class MaysyncInjector(Injector, lang.Abstract): 
     | 
|
| 
       25 
24 
     | 
    
         
             
            ##
         
     | 
| 
       26 
25 
     | 
    
         | 
| 
       27 
26 
     | 
    
         | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
                return _maysync.create_maysync_injector(as_elements(*args))
         
     | 
| 
      
 27 
     | 
    
         
            +
            create_maysync_injector = _InjectorCreator[MaysyncInjector](lambda ce: _maysync.create_maysync_injector(ce))
         
     | 
    
        omlish/inject/sync.py
    CHANGED
    
    | 
         @@ -2,8 +2,7 @@ import abc 
     | 
|
| 
       2 
2 
     | 
    
         
             
            import typing as ta
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            from .. import lang
         
     | 
| 
       5 
     | 
    
         
            -
            from . 
     | 
| 
       6 
     | 
    
         
            -
            from .elements import as_elements
         
     | 
| 
      
 5 
     | 
    
         
            +
            from .injector import _InjectorCreator
         
     | 
| 
       7 
6 
     | 
    
         
             
            from .inspect import KwargsTarget
         
     | 
| 
       8 
7 
     | 
    
         
             
            from .keys import Key
         
     | 
| 
       9 
8 
     | 
    
         | 
| 
         @@ -44,5 +43,7 @@ class Injector(lang.Abstract): 
     | 
|
| 
       44 
43 
     | 
    
         
             
                    return self.provide(target)
         
     | 
| 
       45 
44 
     | 
    
         | 
| 
       46 
45 
     | 
    
         | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
      
 46 
     | 
    
         
            +
            ##
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
            create_injector = _InjectorCreator[Injector](lambda ce: _sync.create_injector(ce))
         
     | 
    
        omlish/lang/imports/proxy.py
    CHANGED
    
    | 
         @@ -342,6 +342,8 @@ def proxy_import( 
     | 
|
| 
       342 
342 
     | 
    
         
             
                    spec: str,
         
     | 
| 
       343 
343 
     | 
    
         
             
                    package: str | None = None,
         
     | 
| 
       344 
344 
     | 
    
         
             
                    extras: ta.Iterable[str] | None = None,
         
     | 
| 
      
 345 
     | 
    
         
            +
                    *,
         
     | 
| 
      
 346 
     | 
    
         
            +
                    no_cache: bool = False,
         
     | 
| 
       345 
347 
     | 
    
         
             
            ) -> types.ModuleType:
         
     | 
| 
       346 
348 
     | 
    
         
             
                """'Legacy' proxy import mechanism."""
         
     | 
| 
       347 
349 
     | 
    
         | 
| 
         @@ -352,12 +354,19 @@ def proxy_import( 
     | 
|
| 
       352 
354 
     | 
    
         | 
| 
       353 
355 
     | 
    
         
             
                def __getattr__(att):  # noqa
         
     | 
| 
       354 
356 
     | 
    
         
             
                    nonlocal omod
         
     | 
| 
      
 357 
     | 
    
         
            +
             
     | 
| 
       355 
358 
     | 
    
         
             
                    if omod is None:
         
     | 
| 
       356 
359 
     | 
    
         
             
                        omod = importlib.import_module(spec, package=package)
         
     | 
| 
       357 
360 
     | 
    
         
             
                        if extras:
         
     | 
| 
       358 
361 
     | 
    
         
             
                            for x in extras:
         
     | 
| 
       359 
362 
     | 
    
         
             
                                importlib.import_module(f'{spec}.{x}', package=package)
         
     | 
| 
       360 
     | 
    
         
            -
             
     | 
| 
      
 363 
     | 
    
         
            +
             
     | 
| 
      
 364 
     | 
    
         
            +
                    v = getattr(omod, att)
         
     | 
| 
      
 365 
     | 
    
         
            +
             
     | 
| 
      
 366 
     | 
    
         
            +
                    if not no_cache:
         
     | 
| 
      
 367 
     | 
    
         
            +
                        setattr(lmod, att, v)
         
     | 
| 
      
 368 
     | 
    
         
            +
             
     | 
| 
      
 369 
     | 
    
         
            +
                    return v
         
     | 
| 
       361 
370 
     | 
    
         | 
| 
       362 
371 
     | 
    
         
             
                lmod = types.ModuleType(spec)
         
     | 
| 
       363 
372 
     | 
    
         
             
                lmod.__getattr__ = __getattr__  # type: ignore[method-assign]
         
     | 
    
        omlish/term/pager.py
    ADDED
    
    | 
         @@ -0,0 +1,235 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # ruff: noqa: S602 S605
         
     | 
| 
      
 2 
     | 
    
         
            +
            #
         
     | 
| 
      
 3 
     | 
    
         
            +
            #   Copyright 2000-2008 Michael Hudson-Doyle <micahel@gmail.com>
         
     | 
| 
      
 4 
     | 
    
         
            +
            #                       Armin Rigo
         
     | 
| 
      
 5 
     | 
    
         
            +
            #
         
     | 
| 
      
 6 
     | 
    
         
            +
            #                   All Rights Reserved
         
     | 
| 
      
 7 
     | 
    
         
            +
            #
         
     | 
| 
      
 8 
     | 
    
         
            +
            #
         
     | 
| 
      
 9 
     | 
    
         
            +
            # Permission to use, copy, modify, and distribute this software and its documentation for any purpose is hereby granted
         
     | 
| 
      
 10 
     | 
    
         
            +
            # without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and
         
     | 
| 
      
 11 
     | 
    
         
            +
            # this permission notice appear in supporting documentation.
         
     | 
| 
      
 12 
     | 
    
         
            +
            #
         
     | 
| 
      
 13 
     | 
    
         
            +
            # THE AUTHOR MICHAEL HUDSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
         
     | 
| 
      
 14 
     | 
    
         
            +
            # MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES
         
     | 
| 
      
 15 
     | 
    
         
            +
            # OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
         
     | 
| 
      
 16 
     | 
    
         
            +
            # OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
         
     | 
| 
      
 17 
     | 
    
         
            +
            #
         
     | 
| 
      
 18 
     | 
    
         
            +
            # https://github.com/python/cpython/tree/bcced02604f845b2b71d0a1dd95f95366bd7774d/Lib/_pyrepl
         
     | 
| 
      
 19 
     | 
    
         
            +
            import io
         
     | 
| 
      
 20 
     | 
    
         
            +
            import os
         
     | 
| 
      
 21 
     | 
    
         
            +
            import re
         
     | 
| 
      
 22 
     | 
    
         
            +
            import subprocess
         
     | 
| 
      
 23 
     | 
    
         
            +
            import sys
         
     | 
| 
      
 24 
     | 
    
         
            +
            import tempfile
         
     | 
| 
      
 25 
     | 
    
         
            +
            import termios
         
     | 
| 
      
 26 
     | 
    
         
            +
            import tty
         
     | 
| 
      
 27 
     | 
    
         
            +
            import typing as ta
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            from .. import check
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            ##
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
            class Pager(ta.Protocol):
         
     | 
| 
      
 36 
     | 
    
         
            +
                def __call__(self, text: str, title: str = '') -> None:
         
     | 
| 
      
 37 
     | 
    
         
            +
                    ...
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
            def get_pager() -> Pager:
         
     | 
| 
      
 41 
     | 
    
         
            +
                """Decide what method to use for paging through text."""
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                if not hasattr(sys.stdin, 'isatty'):
         
     | 
| 
      
 44 
     | 
    
         
            +
                    return plain_pager
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                if not hasattr(sys.stdout, 'isatty'):
         
     | 
| 
      
 47 
     | 
    
         
            +
                    return plain_pager
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                if not sys.stdin.isatty() or not sys.stdout.isatty():
         
     | 
| 
      
 50 
     | 
    
         
            +
                    return plain_pager
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                if sys.platform == 'emscripten':
         
     | 
| 
      
 53 
     | 
    
         
            +
                    return plain_pager
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                use_pager = os.environ.get('MANPAGER') or os.environ.get('PAGER')
         
     | 
| 
      
 56 
     | 
    
         
            +
                if use_pager:
         
     | 
| 
      
 57 
     | 
    
         
            +
                    if sys.platform == 'win32':  # pipes completely broken in Windows
         
     | 
| 
      
 58 
     | 
    
         
            +
                        raise OSError
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                    elif os.environ.get('TERM') in ('dumb', 'emacs'):
         
     | 
| 
      
 61 
     | 
    
         
            +
                        return lambda text, title='': pipe_pager(plain(text), use_pager, title)
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 64 
     | 
    
         
            +
                        return lambda text, title='': pipe_pager(text, use_pager, title)
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                if os.environ.get('TERM') in ('dumb', 'emacs'):
         
     | 
| 
      
 67 
     | 
    
         
            +
                    return plain_pager
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                if sys.platform == 'win32':
         
     | 
| 
      
 70 
     | 
    
         
            +
                    return lambda text, title='': tempfile_pager(plain(text), 'more <')
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                if hasattr(os, 'system') and os.system('(pager) 2>/dev/null') == 0:
         
     | 
| 
      
 73 
     | 
    
         
            +
                    return lambda text, title='': pipe_pager(text, 'pager', title)
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0:
         
     | 
| 
      
 76 
     | 
    
         
            +
                    return lambda text, title='': pipe_pager(text, 'less', title)
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                import tempfile
         
     | 
| 
      
 79 
     | 
    
         
            +
                (fd, filename) = tempfile.mkstemp()
         
     | 
| 
      
 80 
     | 
    
         
            +
                os.close(fd)
         
     | 
| 
      
 81 
     | 
    
         
            +
                try:
         
     | 
| 
      
 82 
     | 
    
         
            +
                    if hasattr(os, 'system') and os.system(f'more "{filename}"') == 0:
         
     | 
| 
      
 83 
     | 
    
         
            +
                        return lambda text, title='': pipe_pager(text, 'more', title)
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 86 
     | 
    
         
            +
                        return tty_pager
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                finally:
         
     | 
| 
      
 89 
     | 
    
         
            +
                    os.unlink(filename)
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
            def escape_stdout(text: str) -> str:
         
     | 
| 
      
 93 
     | 
    
         
            +
                # Escape non-encodable characters to avoid encoding errors later
         
     | 
| 
      
 94 
     | 
    
         
            +
                encoding = getattr(sys.stdout, 'encoding', None) or 'utf-8'
         
     | 
| 
      
 95 
     | 
    
         
            +
                return text.encode(encoding, 'backslashreplace').decode(encoding)
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
            def escape_less(s: str) -> str:
         
     | 
| 
      
 99 
     | 
    
         
            +
                return re.sub(r'([?:.%\\])', r'\\\1', s)
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
            def plain(text: str) -> str:
         
     | 
| 
      
 103 
     | 
    
         
            +
                """Remove boldface formatting from text."""
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                return re.sub('.\b', '', text)
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
            def tty_pager(text: str, title: str = '') -> None:
         
     | 
| 
      
 109 
     | 
    
         
            +
                """Page through text on a text terminal."""
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                lines = plain(escape_stdout(text)).split('\n')
         
     | 
| 
      
 112 
     | 
    
         
            +
                has_tty = False
         
     | 
| 
      
 113 
     | 
    
         
            +
                try:
         
     | 
| 
      
 114 
     | 
    
         
            +
                    fd = sys.stdin.fileno()
         
     | 
| 
      
 115 
     | 
    
         
            +
                    old = termios.tcgetattr(fd)
         
     | 
| 
      
 116 
     | 
    
         
            +
                    tty.setcbreak(fd)
         
     | 
| 
      
 117 
     | 
    
         
            +
                    has_tty = True
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                    def getchar() -> str:
         
     | 
| 
      
 120 
     | 
    
         
            +
                        return sys.stdin.read(1)
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                except (ImportError, AttributeError, io.UnsupportedOperation):
         
     | 
| 
      
 123 
     | 
    
         
            +
                    def getchar() -> str:
         
     | 
| 
      
 124 
     | 
    
         
            +
                        return sys.stdin.readline()[:-1][:1]
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                try:
         
     | 
| 
      
 127 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 128 
     | 
    
         
            +
                        h = int(os.environ.get('LINES', '0'))
         
     | 
| 
      
 129 
     | 
    
         
            +
                    except ValueError:
         
     | 
| 
      
 130 
     | 
    
         
            +
                        h = 0
         
     | 
| 
      
 131 
     | 
    
         
            +
                    if h <= 1:
         
     | 
| 
      
 132 
     | 
    
         
            +
                        h = 25
         
     | 
| 
      
 133 
     | 
    
         
            +
                    r = inc = h - 1
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                    sys.stdout.write('\n'.join(lines[:inc]) + '\n')
         
     | 
| 
      
 136 
     | 
    
         
            +
                    while lines[r:]:
         
     | 
| 
      
 137 
     | 
    
         
            +
                        sys.stdout.write('-- more --')
         
     | 
| 
      
 138 
     | 
    
         
            +
                        sys.stdout.flush()
         
     | 
| 
      
 139 
     | 
    
         
            +
                        c = getchar()
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                        if c in ('q', 'Q'):
         
     | 
| 
      
 142 
     | 
    
         
            +
                            sys.stdout.write('\r          \r')
         
     | 
| 
      
 143 
     | 
    
         
            +
                            break
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                        elif c in ('\r', '\n'):
         
     | 
| 
      
 146 
     | 
    
         
            +
                            sys.stdout.write('\r          \r' + lines[r] + '\n')
         
     | 
| 
      
 147 
     | 
    
         
            +
                            r = r + 1
         
     | 
| 
      
 148 
     | 
    
         
            +
                            continue
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
                        if c in ('b', 'B', '\x1b'):
         
     | 
| 
      
 151 
     | 
    
         
            +
                            r = r - inc - inc
         
     | 
| 
      
 152 
     | 
    
         
            +
                            if r < 0:
         
     | 
| 
      
 153 
     | 
    
         
            +
                                r = 0
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                        sys.stdout.write('\n' + '\n'.join(lines[r:r + inc]) + '\n')
         
     | 
| 
      
 156 
     | 
    
         
            +
                        r = r + inc
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                finally:
         
     | 
| 
      
 159 
     | 
    
         
            +
                    if has_tty:
         
     | 
| 
      
 160 
     | 
    
         
            +
                        termios.tcsetattr(fd, termios.TCSAFLUSH, old)  # noqa
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
            def plain_pager(text: str, title: str = '') -> None:
         
     | 
| 
      
 164 
     | 
    
         
            +
                """Simply print unformatted text. This is the ultimate fallback."""
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
      
 166 
     | 
    
         
            +
                sys.stdout.write(plain(escape_stdout(text)))
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
            def pipe_pager(text: str, cmd: str, title: str = '') -> None:
         
     | 
| 
      
 170 
     | 
    
         
            +
                """Page through text by feeding it to another program."""
         
     | 
| 
      
 171 
     | 
    
         
            +
             
     | 
| 
      
 172 
     | 
    
         
            +
                env = os.environ.copy()
         
     | 
| 
      
 173 
     | 
    
         
            +
             
     | 
| 
      
 174 
     | 
    
         
            +
                if title:
         
     | 
| 
      
 175 
     | 
    
         
            +
                    title += ' '
         
     | 
| 
      
 176 
     | 
    
         
            +
                esc_title = escape_less(title)
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
                prompt_string = (
         
     | 
| 
      
 179 
     | 
    
         
            +
                    f' {esc_title}'
         
     | 
| 
      
 180 
     | 
    
         
            +
                    '?ltline %lt?L/%L.'
         
     | 
| 
      
 181 
     | 
    
         
            +
                    ':byte %bB?s/%s.'
         
     | 
| 
      
 182 
     | 
    
         
            +
                    '.'
         
     | 
| 
      
 183 
     | 
    
         
            +
                    '?e (END):?pB %pB\\%..'
         
     | 
| 
      
 184 
     | 
    
         
            +
                    ' (press h for help or q to quit)'
         
     | 
| 
      
 185 
     | 
    
         
            +
                )
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
      
 187 
     | 
    
         
            +
                env['LESS'] = f'-RmPm{prompt_string}$PM{prompt_string}$'
         
     | 
| 
      
 188 
     | 
    
         
            +
             
     | 
| 
      
 189 
     | 
    
         
            +
                proc = subprocess.Popen(
         
     | 
| 
      
 190 
     | 
    
         
            +
                    cmd,
         
     | 
| 
      
 191 
     | 
    
         
            +
                    shell=True,
         
     | 
| 
      
 192 
     | 
    
         
            +
                    stdin=subprocess.PIPE,
         
     | 
| 
      
 193 
     | 
    
         
            +
                    errors='backslashreplace',
         
     | 
| 
      
 194 
     | 
    
         
            +
                    env=env,
         
     | 
| 
      
 195 
     | 
    
         
            +
                )
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
                try:
         
     | 
| 
      
 198 
     | 
    
         
            +
                    with check.not_none(proc.stdin) as pipe:
         
     | 
| 
      
 199 
     | 
    
         
            +
                        try:
         
     | 
| 
      
 200 
     | 
    
         
            +
                            pipe.write(text)
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
                        except KeyboardInterrupt:
         
     | 
| 
      
 203 
     | 
    
         
            +
                            # We've hereby abandoned whatever text hasn't been written, but the pager is still in control of the
         
     | 
| 
      
 204 
     | 
    
         
            +
                            # terminal.
         
     | 
| 
      
 205 
     | 
    
         
            +
                            pass
         
     | 
| 
      
 206 
     | 
    
         
            +
             
     | 
| 
      
 207 
     | 
    
         
            +
                except OSError:
         
     | 
| 
      
 208 
     | 
    
         
            +
                    pass  # Ignore broken pipes caused by quitting the pager program.
         
     | 
| 
      
 209 
     | 
    
         
            +
             
     | 
| 
      
 210 
     | 
    
         
            +
                while True:
         
     | 
| 
      
 211 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 212 
     | 
    
         
            +
                        proc.wait()
         
     | 
| 
      
 213 
     | 
    
         
            +
                        break
         
     | 
| 
      
 214 
     | 
    
         
            +
             
     | 
| 
      
 215 
     | 
    
         
            +
                    except KeyboardInterrupt:
         
     | 
| 
      
 216 
     | 
    
         
            +
                        # Ignore ctl-c like the pager itself does. Otherwise the pager is left running and the terminal is in raw
         
     | 
| 
      
 217 
     | 
    
         
            +
                        # mode and unusable.
         
     | 
| 
      
 218 
     | 
    
         
            +
                        pass
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
      
 220 
     | 
    
         
            +
             
     | 
| 
      
 221 
     | 
    
         
            +
            def tempfile_pager(text: str, cmd: str, title: str = '') -> None:
         
     | 
| 
      
 222 
     | 
    
         
            +
                """Page through text by invoking a program on a temporary file."""
         
     | 
| 
      
 223 
     | 
    
         
            +
             
     | 
| 
      
 224 
     | 
    
         
            +
                with tempfile.TemporaryDirectory() as tempdir:
         
     | 
| 
      
 225 
     | 
    
         
            +
                    filename = os.path.join(tempdir, 'pydoc.out')
         
     | 
| 
      
 226 
     | 
    
         
            +
             
     | 
| 
      
 227 
     | 
    
         
            +
                    with open(
         
     | 
| 
      
 228 
     | 
    
         
            +
                            filename,
         
     | 
| 
      
 229 
     | 
    
         
            +
                            'w',
         
     | 
| 
      
 230 
     | 
    
         
            +
                            errors='backslashreplace',
         
     | 
| 
      
 231 
     | 
    
         
            +
                            encoding=os.device_encoding(0) if sys.platform == 'win32' else None,
         
     | 
| 
      
 232 
     | 
    
         
            +
                    ) as file:
         
     | 
| 
      
 233 
     | 
    
         
            +
                        file.write(text)
         
     | 
| 
      
 234 
     | 
    
         
            +
             
     | 
| 
      
 235 
     | 
    
         
            +
                    os.system(cmd + ' "' + filename + '"')
         
     |