-
Notifications
You must be signed in to change notification settings - Fork 60
/
_namespaces.py
89 lines (65 loc) · 2.08 KB
/
_namespaces.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
__all__ = ("namespace_context", "namespaced_id")
from contextlib import contextmanager
from contextvars import ContextVar, Token
import functools
from typing import Awaitable, Union, Optional, TypeVar, Callable, cast
from shiny.types import MISSING, MISSING_TYPE
from ._utils import is_async_callable
_current_namespace: ContextVar[Optional[str]] = ContextVar(
"current_namespace", default=None
)
def get_current_namespace() -> Optional[str]:
"""
Get the current namespace.
"""
return _current_namespace.get()
@contextmanager
def namespace_context(ns: str):
"""
Set a namespace for the duration of the context.
"""
token: Token[Union[str, None]] = _current_namespace.set(ns)
try:
yield
finally:
_current_namespace.reset(token)
def namespaced_id(id: str, ns: Union[str, MISSING_TYPE, None] = MISSING) -> str:
"""
Namespace an ID based on the current ``Module()``'s namespace.
Parameters
----------
id
The ID to namespace.
Warning
-------
This is only provided so that htmltools can use it to namespace ids within a
``Module()`` UI function.
"""
if isinstance(ns, MISSING_TYPE):
ns = get_current_namespace()
if ns is None:
return id
else:
return ns + "_" + id
T = TypeVar("T")
Fn = Union[Callable[..., T], Callable[..., Awaitable[T]]]
def add_namespace(fn: Fn[T], ns: str) -> Fn[T]:
"""
Modify a function to use the **current** namespace when it executes.
Note
----
This is useful for marking user supplied functions to use the **current** namespace
when they execute **later on**.
"""
if is_async_callable(fn):
@functools.wraps(fn)
async def wrapper(*args: object, **kwargs: object) -> T:
with namespace_context(ns):
return await fn(*args, **kwargs)
else:
fn = cast(Callable[..., T], fn)
@functools.wraps(fn)
def wrapper(*args: object, **kwargs: object) -> T:
with namespace_context(ns):
return fn(*args, **kwargs)
return wrapper