kernmini 0.1.0__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.
- kernmini/__init__.py +17 -0
- kernmini/concur.py +30 -0
- kernmini/debug.py +81 -0
- kernmini/kernel.py +990 -0
- kernmini/kernelspec.py +39 -0
- kernmini/session.py +40 -0
- kernmini/streams.py +51 -0
- kernmini-0.1.0.dist-info/METADATA +66 -0
- kernmini-0.1.0.dist-info/RECORD +12 -0
- kernmini-0.1.0.dist-info/WHEEL +5 -0
- kernmini-0.1.0.dist-info/licenses/LICENSE +202 -0
- kernmini-0.1.0.dist-info/top_level.txt +1 -0
kernmini/__init__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Everything a Jupyter kernel needs except the language.
|
|
2
|
+
|
|
3
|
+
kernmini is the language-agnostic core of a Jupyter kernel: connection files, HMAC-signed wire
|
|
4
|
+
sessions, the socket thread cast (shell/control routers, IOPub, stdin, heartbeat), busy/idle and
|
|
5
|
+
abort discipline, interrupts, subshells (JEP 91), and process lifecycle. A kernel supplies a
|
|
6
|
+
*shell*: an execution layer built by the `shell_factory` passed to `MiniKernel`/`run_kernel`.
|
|
7
|
+
See DEV.md for the shell contract; `ipymini` is the reference implementation (IPython), and a
|
|
8
|
+
minimal shell needs only `execute`, `execution_count`, `execution_context`, and
|
|
9
|
+
`set_stream_sender`.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
__version__ = "0.1.0"
|
|
13
|
+
|
|
14
|
+
from .kernel import MiniKernel, run_kernel
|
|
15
|
+
from .session import MiniSession
|
|
16
|
+
from .concur import unlock, subshell
|
|
17
|
+
from .kernelspec import install_kernelspec, install_kernelspec_dir
|
kernmini/concur.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"In-cell opt-ins for concurrent execution: unlock() and subshell()."
|
|
2
|
+
|
|
3
|
+
import contextvars
|
|
4
|
+
from contextlib import contextmanager
|
|
5
|
+
|
|
6
|
+
_release = contextvars.ContextVar("kernmini_release", default=None)
|
|
7
|
+
_subshell = contextvars.ContextVar("kernmini_subshell", default=None)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def unlock()->bool:
|
|
11
|
+
"Let queued shell messages run while the current cell awaits; irreversible for the rest of the cell."
|
|
12
|
+
release = _release.get()
|
|
13
|
+
if release is None: return False
|
|
14
|
+
release()
|
|
15
|
+
return True
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@contextmanager
|
|
19
|
+
def subshell():
|
|
20
|
+
"Run execute_requests arriving from this cell's client session in a fresh subshell while the body runs."
|
|
21
|
+
sub = _subshell.get()
|
|
22
|
+
if sub is None: raise RuntimeError("subshell() only works inside a cell running under a kernmini kernel")
|
|
23
|
+
subs = sub.kernel.subshells
|
|
24
|
+
session = ((sub.kernel.current_parent() or {}).get("header") or {}).get("session")
|
|
25
|
+
sid = subs.create()
|
|
26
|
+
try:
|
|
27
|
+
subs.set_route_override(sid, session)
|
|
28
|
+
try: yield sid
|
|
29
|
+
finally: subs.clear_route_override()
|
|
30
|
+
finally: subs.delete(sid)
|
kernmini/debug.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"Debug infrastructure for kernels: env flags, logging/faulthandler setup, message tracing, and debugger cell-filename hashing."
|
|
2
|
+
|
|
3
|
+
import faulthandler, logging, os, signal, sys, tempfile
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def envbool(name: str) -> bool:
|
|
8
|
+
v = (os.environ.get(name) or "").strip().lower()
|
|
9
|
+
return v not in ("", "0", "false", "no")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class DebugFlags:
|
|
14
|
+
enabled: bool = False
|
|
15
|
+
trace_msgs: bool = False
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def from_env(cls, prefix: str = "KERNMINI") -> "DebugFlags": return cls(enabled=envbool(f"{prefix}_DEBUG"), trace_msgs=envbool(f"{prefix}_DEBUG_MSGS"))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def setup_debug(flags: DebugFlags):
|
|
22
|
+
"Initialize debug infrastructure: logging, faulthandler, SIGUSR1 handler."
|
|
23
|
+
if not flags.enabled: return
|
|
24
|
+
root = logging.getLogger()
|
|
25
|
+
fmt = "%(asctime)s %(levelname)s %(name)s: %(message)s"
|
|
26
|
+
if not root.handlers: logging.basicConfig(level=logging.DEBUG, stream=sys.__stderr__, format=fmt)
|
|
27
|
+
faulthandler.enable(file=sys.__stderr__)
|
|
28
|
+
if hasattr(signal, "SIGUSR1"): faulthandler.register(signal.SIGUSR1, file=sys.__stderr__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def trace_msg(logger, prefix: str, msg: dict, *, enabled: bool = True):
|
|
32
|
+
"Log message flow at high level: msg_type, msg_id, subshell_id."
|
|
33
|
+
if not enabled: return
|
|
34
|
+
h = msg.get("header") or {}
|
|
35
|
+
logger.warning("%s type=%s id=%s subshell=%r", prefix, h.get("msg_type"), h.get("msg_id"), h.get("subshell_id"))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def murmur2_x86(data: str, seed: int) -> int:
|
|
39
|
+
"Return Murmur2 x86 hash of UTF-8 `data` with `seed`."
|
|
40
|
+
m = 0x5BD1E995
|
|
41
|
+
data_bytes = data.encode("utf-8")
|
|
42
|
+
length = len(data_bytes)
|
|
43
|
+
h = seed ^ length
|
|
44
|
+
rounded_end = length & 0xFFFFFFFC
|
|
45
|
+
for i in range(0, rounded_end, 4):
|
|
46
|
+
k = int.from_bytes(data_bytes[i : i + 4], "little")
|
|
47
|
+
k = (k * m) & 0xFFFFFFFF
|
|
48
|
+
k ^= k >> 24
|
|
49
|
+
k = (k * m) & 0xFFFFFFFF
|
|
50
|
+
|
|
51
|
+
h = (h * m) & 0xFFFFFFFF
|
|
52
|
+
h ^= k
|
|
53
|
+
|
|
54
|
+
val = length & 0x03
|
|
55
|
+
k = 0
|
|
56
|
+
if val >= 3: k = data_bytes[rounded_end + 2] << 16
|
|
57
|
+
if val >= 2: k |= data_bytes[rounded_end + 1] << 8
|
|
58
|
+
if val >= 1:
|
|
59
|
+
k |= data_bytes[rounded_end]
|
|
60
|
+
h ^= k
|
|
61
|
+
h = (h * m) & 0xFFFFFFFF
|
|
62
|
+
|
|
63
|
+
h ^= h >> 13
|
|
64
|
+
h = (h * m) & 0xFFFFFFFF
|
|
65
|
+
h ^= h >> 15
|
|
66
|
+
return h
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
DEBUG_HASH_SEED = 0xC70F6907
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def debug_tmp_directory() -> str: return os.path.join(tempfile.gettempdir(), f"kernmini_{os.getpid()}")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def debug_cell_filename(code: str, ext: str = ".py") -> str:
|
|
76
|
+
"Compute debug cell filename (hash matches ipykernel's scheme, so frontends map breakpoints); respects KERNMINI_CELL_NAME."
|
|
77
|
+
cell_name = os.environ.get("KERNMINI_CELL_NAME")
|
|
78
|
+
if cell_name is None:
|
|
79
|
+
name = murmur2_x86(code, DEBUG_HASH_SEED)
|
|
80
|
+
cell_name = os.path.join(debug_tmp_directory(), f"{name}{ext}")
|
|
81
|
+
return cell_name
|