PythonX runtime owner for the embedded Gralkor stack.
Two responsibilities, both in init/1:
Reap redislite orphans.
falkordblite(loaded into PythonX in this BEAM) spawns aredis-servergrandchild. A hard BEAM SIGKILL leaves it orphaned. SIGKILL anything matchingredislite/bin/redis-serverbefore we boot — safe because this runs before our own Python init, so anything matching is by definition not ours-yet, and the path is unique to falkordblite.Smoke-import
graphiti_corethrough PythonX so any venv / import failure surfaces at boot rather than on the first real call.
Pythonx's interpreter + venv materialisation are configured in
config/config.exs via :pythonx, :uv_init and start automatically with
the :pythonx OTP application; Gralkor.Python does not duplicate that
work.
See ex-python-runtime in gralkor/TEST_TREES.md.
Summary
Functions
Returns a specification to start this module under a supervisor.
Spin up a daemon-thread asyncio event loop and stash it on asyncio as
_gralkor_loop plus a _gralkor_run(coro) helper that submits onto it.
SIGKILL every pid the listing function returns. Pure plumbing — accepts injected list/kill functions so the unit test doesn't have to spawn real redis processes.
Try to import graphiti_core via Pythonx; surface any failure as
{:error, _}.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec install_async_runtime() :: :ok | {:error, term()}
Spin up a daemon-thread asyncio event loop and stash it on asyncio as
_gralkor_loop plus a _gralkor_run(coro) helper that submits onto it.
Must run once per Pythonx interpreter, before any code that calls into
graphiti via asyncio._gralkor_run. Idempotent — the second call is a
no-op.
Why: Pythonx.eval creates a fresh event loop per asyncio.run call.
AsyncFalkorDB (and any redis-async connection) binds its connections to
the loop they were created on; reusing them on a different loop raises
"Future attached to a different loop". The spike measured the alternative
pattern (Step 6 in pythonx-spike/spike.exs) at ~56µs per call vs ~112µs
for asyncio.run — and, crucially, it shares one loop across all calls
so connection reuse works.
SIGKILL every pid the listing function returns. Pure plumbing — accepts injected list/kill functions so the unit test doesn't have to spawn real redis processes.
@spec smoke_import_graphiti() :: :ok | {:error, term()}
Try to import graphiti_core via Pythonx; surface any failure as
{:error, _}.