Skip to content

utils

Provides publicly utility functions.

Functions:

mark_thread_pydev_do_not_trace

mark_thread_pydev_do_not_trace(thread: Thread | None = None, *, remove=False) -> None

Modifies the given thread's attributes to hide or unhide it from the debugger (e.g., debugpy).

Source code in src/async_kernel/utils.py
58
59
60
61
62
def mark_thread_pydev_do_not_trace(thread: threading.Thread | None = None, *, remove=False) -> None:
    """Modifies the given thread's attributes to hide or unhide it from the debugger (e.g., debugpy)."""
    thread = thread or threading.current_thread()
    thread.pydev_do_not_trace = not remove  # pyright: ignore[reportAttributeAccessIssue]
    thread.is_pydev_daemon_thread = not remove  # pyright: ignore[reportAttributeAccessIssue]

get_kernel

get_kernel() -> Kernel

Get the current kernel.

Source code in src/async_kernel/utils.py
65
66
67
68
69
70
71
72
def get_kernel() -> Kernel:
    """Get the current kernel."""
    import async_kernel.interface  # noqa: PLC0415

    if not async_kernel.interface.Interface.initialized():
        msg = "A kernel interface is not started!"
        raise RuntimeError(msg)
    return async_kernel.interface.Interface.instance().kernel

get_job

get_job() -> Job[Any]

Get the job for the current context.

Raises:

  • LookupError –

    If there is no job in the current context.

Source code in src/async_kernel/utils.py
80
81
82
83
84
85
86
def get_job() -> Job[Any]:
    """Get the job for the current context.

    Raises:
        LookupError: If there is no job in the current context.
    """
    return _job_var.get()

get_parent_message

get_parent_message(job: Job[T_content_co] | None = None) -> Message[T_content_co] | None

Get the parent message for the current context.

Source code in src/async_kernel/utils.py
89
90
91
92
93
94
def get_parent_message(job: Job[T_content_co] | None = None, /) -> Message[T_content_co] | None:
    """Get the parent message for the current context."""
    try:
        return (job or get_job()).get("msg")
    except LookupError:
        return None

get_subshell_id

get_subshell_id() -> str | None

Get the subshell_id for the current context.

Source code in src/async_kernel/utils.py
97
98
99
def get_subshell_id() -> str | None:
    """Get the `subshell_id` for the current context."""
    return get_kernel().shell.subshell_id

subshell_context

subshell_context(subshell_id: str | None) -> Generator[None, Any, None]

A context manager to work in the context of a shell or subshell.

Parameters:

  • subshell_id

    (str | None) –

    An existing subshell or the main shell if subshell_id is None.

Source code in src/async_kernel/utils.py
102
103
104
105
106
107
108
109
110
@contextmanager
def subshell_context(subshell_id: str | None) -> Generator[None, Any, None]:
    """A context manager to work in the context of a shell or subshell.

    Args:
        subshell_id: An existing subshell or the main shell if subshell_id is None.
    """
    with get_kernel().get_shell(subshell_id).context():
        yield

get_metadata

get_metadata(job: Job | None = None) -> dict[str, Any] | None

Gets the metadata for the current context.

Source code in src/async_kernel/utils.py
113
114
115
116
117
118
def get_metadata(job: Job | None = None, /) -> dict[str, Any] | None:
    """Gets the metadata for the current context."""
    try:
        return (job or _job_var.get())["msg"]["metadata"]
    except Exception:
        return None

get_tags

get_tags(job: Job | None = None) -> list[str]

Gets the tags for the current context.

Source code in src/async_kernel/utils.py
121
122
123
124
125
126
def get_tags(job: Job | None = None, /) -> list[str]:
    """Gets the tags for the current context."""
    try:
        return get_metadata(job)["tags"]  # pyright: ignore[reportOptionalSubscript]
    except Exception:
        return []

get_tag_value

get_tag_value(
    tag: Tags, default: _TagType, /, *, tags: Iterable[str] | None = None
) -> _TagType

Get the value for the tag from a collection of tags.

Parameters:

  • tag

    (Tags) –

    The tag to get the value from.

  • default

    (_TagType) –

    The default value if a tag is not found. The default is also used to determine the type for conversion of the value.

  • tags

    (Iterable[str] | None, default: None ) –

    A list of tags to search. When not provided get_tags is used.

The tag value is the value trailing behind =. The value is transformed according to the type of the default.

Source code in src/async_kernel/utils.py
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
def get_tag_value(tag: Tags, default: _TagType, /, *, tags: Iterable[str] | None = None) -> _TagType:
    """Get the value for the tag from a collection of tags.

    Args:
        tag: The tag to get the value from.
        default: The default value if a tag is not found. The default is also used to determine the type for conversion of the value.
        tags: A list of tags to search. When not provided [get_tags][] is used.

    The tag value is the value trailing behind <tag>=<value>. The value is transformed according to
    the type of the default.
    """
    for t in tags if tags is not None else get_tags():
        if t == tag:
            return tag.get_value(t, default)
    return default

setattr_nested

setattr_nested(
    obj: object, name: str, value: str | Any, *, _return_value=False
) -> dict[str, Any]

Replace an existing nested attribute/trait of an object.

If the attribute name contains dots, it is interpreted as a nested attribute. For example, if name is "a.b.c", then the code will attempt to set obj.a.b.c to value.

Parameters:

  • obj

    (object) –

    The object to set the attribute on.

  • name

    (str) –

    The name of the attribute to set.

  • value

    (str | Any) –

    The value to set the attribute to.

Returns:

  • dict[str, Any] –

    The mapping of the name to the set value if the value has been set.

  • dict[str, Any] –

    An empty dict indicates the value was not set.

Source code in src/async_kernel/utils.py
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
def setattr_nested(obj: object, name: str, value: str | Any, *, _return_value=False) -> dict[str, Any]:
    """Replace an existing nested attribute/trait of an object.

    If the attribute name contains dots, it is interpreted as a nested attribute.
    For example, if name is "a.b.c", then the code will attempt to set obj.a.b.c to value.

    Args:
        obj: The object to set the attribute on.
        name: The name of the attribute to set.
        value: The value to set the attribute to.

    Returns:
        The mapping of the name to the set value if the value has been set.
        An empty dict indicates the value was not set.
    """
    if len(bits := name.split(".")) > 1:
        try:
            obj = getattr(obj, bits[0])
        except Exception:
            return {}
        value = setattr_nested(obj, ".".join(bits[1:]), value, _return_value=True)
        return value if _return_value else {name: value}
    if (isinstance(obj, traitlets.HasTraits) and obj.has_trait(name)) or hasattr(obj, name):
        try:
            setattr(obj, name, value)
        except Exception:
            setattr(obj, name, eval(value))
        return value if _return_value else {name: value}  # pyright: ignore[reportReturnType]
    return {}

apply_settings

apply_settings(obj: object, settings: Mapping[str, Any]) -> dict[str, Any]

Apply the settings onto the object.

Returns:

  • dict ( dict[str, Any] ) –

    A copy of the settings that were applied.

Notes
  • If flags are included using the pattern 'flags': iterable(str), the flags are interpreted as boolean values. Generally, it is preferred to specify the value in the settings explicitly.
Source code in src/async_kernel/utils.py
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
def apply_settings(obj: object, settings: Mapping[str, Any]) -> dict[str, Any]:
    """Apply the settings onto the object.

    Returns:
        dict: A copy of the settings that were applied.

    Notes:
        - If flags are included using the pattern `'flags': iterable(str)`, the flags
            are interpreted as boolean values. Generally, it is preferred to specify
            the value in the settings explicitly.
    """
    values = {}
    for k, v in settings.items():
        if k == "flags":
            for flag in (f.strip("-") for f in v):
                values.update(setattr_nested(obj, flag.strip("no-"), not flag.startswith("no-")))
        else:
            values.update(setattr_nested(obj, k, v))
    return values

error_to_content

error_to_content(error: BaseException) -> ErrorReply

Convert the error to a dict.

ref: https://jupyter-client.readthedocs.io/en/stable/messaging.html#request-reply

Source code in src/async_kernel/utils.py
206
207
208
209
210
211
212
213
214
215
216
def error_to_content(error: BaseException, /) -> t_content.ErrorReply:
    """Convert the error to a dict.

    ref: https://jupyter-client.readthedocs.io/en/stable/messaging.html#request-reply
    """
    return {
        "status": "error",
        "ename": type(error).__name__,
        "evalue": str(error),
        "traceback": traceback.format_exception(error),
    }

redirect_stdout

redirect_stdout(stream: _SupportsRedirectT) -> Generator[_SupportsRedirectT, Any, None]

Re-direct sys.stdout generated in the current context.

See Also

- [contextlib.redirect_stdout][]
Source code in src/async_kernel/utils.py
219
220
221
222
223
224
225
226
227
228
229
230
231
@contextmanager
def redirect_stdout(stream: _SupportsRedirectT, /) -> Generator[_SupportsRedirectT, Any, None]:
    """Re-direct [sys.stdout][] generated in the current context.

    **See Also**

        - [contextlib.redirect_stdout][]
    """
    token = _stdout_context.set(stream)
    try:
        yield stream
    finally:
        _stdout_context.reset(token)

redirect_stderr

redirect_stderr(stream: _SupportsRedirectT) -> Generator[_SupportsRedirectT, Any, None]

Re-direct sys.stderr generated in the current context.

See Also

- [contextlib.redirect_stderr][]
Source code in src/async_kernel/utils.py
234
235
236
237
238
239
240
241
242
243
244
245
246
@contextmanager
def redirect_stderr(stream: _SupportsRedirectT, /) -> Generator[_SupportsRedirectT, Any, None]:
    """Re-direct [sys.stderr][] generated in the current context.

    **See Also**

        - [contextlib.redirect_stderr][]
    """
    token = _stderr_context.set(stream)
    try:
        yield stream
    finally:
        _stderr_context.reset(token)

show_result

show_result(show: bool = True) -> Generator[None, Any, None]

A context where show_result is enabled/disabled.

Source code in src/async_kernel/utils.py
249
250
251
252
253
254
255
256
257
@contextmanager
def show_result(show: bool = True, /) -> Generator[None, Any, None]:
    """A context where `show_result` is enabled/disabled."""
    token = _show_result_context.set(show)
    try:
        yield
    finally:
        if token:
            _show_result_context.reset(token)

get_context_hash

get_context_hash(level: int = 0) -> int

Get the hash of the call stack at a certain level.

This is useful to discriminate a call context in the dunder methods __enter__, __exit__, __aenter__ and __aexit__.

Parameters:

  • level

    (int, default: 0 ) –

    The level up the frame stack to use starting outside the current frame.

Tips
  • When supporting re-entrant contexts, a list or count should be kept.
  • Check the hash has not already been used to prevent re-entrance.
  • Use atomic operations or an instance lock for thread safety, (adjust the )
Source code in src/async_kernel/utils.py
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
def get_context_hash(level: int = 0) -> int:
    """Get the hash of the call stack at a certain level.

    This is useful to discriminate a call context in the dunder methods `__enter__`,
     `__exit__`, `__aenter__` and `__aexit__`.

    Args:
        level: The level up the frame stack to use starting outside the current frame.

    Tips:
        - When supporting re-entrant contexts, a list or count should be kept.
        - Check the hash has not already been used to prevent re-entrance.
        - Use atomic operations or an instance lock for thread safety, (adjust
        the )
    """
    assert level >= 0
    return hash(sys._getframe(level + 2))  # pyright: ignore[reportPrivateUsage]