Skip to content

jiti.cli.merge.source

Internal API

Documented for transparency — the supported public surface is the top-level jiti package. Internals can change in any release.

jiti.cli.merge.source

Splice a generated section into its source file.

The transform is text-preserving: it replaces the @jiti stub's exact line span with the generated implementation (helpers + public function), keeps the user's signature line, and brings along the impls' imports — minus self-imports of the module being merged into. ruff is deferred to _ruff_batch so a multi-target merge formats every touched file in one pass. Stripping @jiti also drops the runtime contract; post-merge code is plain Python.

Classes

Functions

merge_into_source

merge_into_source(
    source: str, qualname: str, own_module: str, section_body: str, file_imports: str
) -> str

Return source with the @jiti stub qualname replaced by its generated implementation.

section_body is the generated unit (private helpers + public function, no markers); file_imports is the companion's hoisted import block; own_module is the module being merged into, whose self-imports are dropped (the symbols already live in the file). The public function is spliced at the @jiti site (and re-indented if it's a class method); any module-level helpers in the section body (constants, private functions, etc.) are injected at module level near the imports — keeping them out of the class body where they'd cause syntax errors when stacked under decorators like @staticmethod.

Non-@jiti decorators stacked above @jiti (e.g. @staticmethod, @functools.cache) are preserved on the merged def — only the @jiti decorator line itself is dropped.

Source code in src/jiti/cli/merge/source.py
def merge_into_source(
    source: str, qualname: str, own_module: str, section_body: str, file_imports: str
) -> str:
    """Return `source` with the `@jiti` stub `qualname` replaced by its generated implementation.

    `section_body` is the generated unit (private helpers + public function, no markers);
    `file_imports` is the companion's hoisted import block; `own_module` is the module being
    merged into, whose self-imports are dropped (the symbols already live in the file). The
    public function is spliced at the `@jiti` site (and re-indented if it's a class method);
    any module-level helpers in the section body (constants, private functions, etc.) are
    injected at module level near the imports — keeping them out of the class body where
    they'd cause syntax errors when stacked under decorators like `@staticmethod`.

    Non-`@jiti` decorators stacked above `@jiti` (e.g. `@staticmethod`, `@functools.cache`)
    are preserved on the merged def — only the `@jiti` decorator line itself is dropped.
    """
    node = _find_jiti_def_at(ast.parse(source), qualname)
    # _find_jiti_def_at already guarantees node has a @jiti decorator; this just locates it.
    jiti_decorator = next(d for d in node.decorator_list if _is_jiti_decorator(d))
    helpers, function = _split_section(section_body, node.name)
    spliced = _splice(source.splitlines(), node, function, jiti_decorator)
    if helpers.strip():
        spliced = _inject_module_block(spliced, helpers)
    needed = _strip_self_imports(file_imports, own_module)
    if needed:
        spliced = _inject_imports(spliced, needed)
    return spliced if spliced.endswith("\n") else spliced + "\n"

write_source

write_source(path: Path, text: str) -> None

Atomically replace path. Ruff is deferred to _ruff_batch at the end of run_merge, so a multi-target merge runs ruff check and ruff format exactly once across all files.

Source code in src/jiti/cli/merge/source.py
def write_source(path: Path, text: str) -> None:
    """Atomically replace `path`. Ruff is deferred to `_ruff_batch` at the end of `run_merge`,
    so a multi-target merge runs `ruff check` and `ruff format` exactly once across all files."""
    atomic_write(path, text if text.endswith("\n") else text + "\n")

apply_section

apply_section(ref, source_path: Path) -> Path

Inline the section into source, drop the impl section. Returns source_path for ruff.

Source code in src/jiti/cli/merge/source.py
def apply_section(ref, source_path: Path) -> Path:
    """Inline the section into source, drop the impl section. Returns `source_path` for ruff."""
    imports, _ = parse_file(ref.impl_path.read_text())
    new_source = merge_into_source(
        source_path.read_text(), ref.qualname, ref.module, ref.section.body, imports
    )
    write_source(source_path, new_source)
    drop_section(ref.impl_path, ref.key)
    return source_path

resolve_wrapper

resolve_wrapper(module: str, qualname: str) -> _JitiCallable | None

Walk module.qualname and unwrap to the underlying _JitiCallable, if any.

Plain @jiti defs surface the JitiCallable directly via attribute access; stacked descriptors (@staticmethod @jiti, @property @jiti, etc.) surface their wrapper — _unwrap_to_jiti_callable peels through __wrapped__ / __func__ / fget to find the JitiCallable underneath. Same unwrap used by jiti.required_for.

Source code in src/jiti/cli/merge/source.py
def resolve_wrapper(module: str, qualname: str) -> _JitiCallable | None:
    """Walk `module.qualname` and unwrap to the underlying `_JitiCallable`, if any.

    Plain `@jiti` defs surface the JitiCallable directly via attribute access; stacked
    descriptors (`@staticmethod @jiti`, `@property @jiti`, etc.) surface their wrapper —
    `_unwrap_to_jiti_callable` peels through `__wrapped__` / `__func__` / `fget` to find
    the JitiCallable underneath. Same unwrap used by `jiti.required_for`.
    """
    target: object | None = sys.modules.get(module)
    for part in qualname.split("."):
        target = getattr(target, part, None)
        if target is None:
            return None
    return _unwrap_to_jiti_callable(target)