httk.core.project.members ========================= .. py:module:: httk.core.project.members .. autoapi-nested-parse:: The on-disk registry of a project's members, and the handler protocol. A *member* is a self-contained subtree a project holds whose internals another module owns — a workflow workspace is the first one. The project records only that a member of a given *kind* lives at a given path; everything about what the member contains, how it is sealed, and how it is checked is delegated to the handler that module registers for the kind (see :mod:`httk.core.register.members`). Core owns the verbs — seal, manifest, repair, verify — and this on-disk registry that tells those verbs which subtrees to hand off and to whom. It interprets a member no further than its path and kind. Attributes ---------- .. autoapisummary:: httk.core.project.members.MEMBERS_FORMAT httk.core.project.members.MEMBERS_FORMAT_VERSION Classes ------- .. autoapisummary:: httk.core.project.members.ProjectMember httk.core.project.members.ProjectMemberHandler Functions --------- .. autoapisummary:: httk.core.project.members.members_path httk.core.project.members.project_members httk.core.project.members.register_project_member httk.core.project.members.unregister_project_member httk.core.project.members.update_project_member_path httk.core.project.members.set_project_member_name Module Contents --------------- .. py:data:: MEMBERS_FORMAT :value: 'httk-project-members' .. py:data:: MEMBERS_FORMAT_VERSION :value: 1 .. py:class:: ProjectMember One member a project holds: a subtree of a given kind. :param path: The member's posix relpath below the project root (``"."`` is the project root itself). :param kind: The member kind, whose handler owns the member's internals. :param name: The member's recorded name, or ``None`` when it has none. .. py:attribute:: path :type: str .. py:attribute:: kind :type: str .. py:attribute:: name :type: str | None :value: None .. py:class:: ProjectMemberHandler Bases: :py:obj:`Protocol` What core needs from the module that owns one member kind. Every method takes the member's own root — ``project_root / member.path`` — rather than the project, so a handler never has to rediscover where it lives. The manifest, repair, and verify verbs core owns call exactly these; a member seals through its own module, and core only records the resulting digest. .. py:method:: manifest_exclusions(project_root, member_relpath) Return the manifest exclusions this member contributes. These are ``fnmatch`` patterns on posix relpaths *below the project root* (not the member root): the member decides which of its own internals — control directories, working scratch — the project manifest must leave out, while its payload files stay covered. :param project_root: The project root the patterns are relative to. :param member_relpath: This member's relpath below the project root. :return: The exclusion patterns this member contributes. .. py:method:: seal_digest(member_root) Return this member's identifier and the SHA-256 of its seal bytes. The digest is what a project seal records for the member, so a project seal transitively pins the member without re-hashing its payload. :param member_root: This member's root directory. :return: The member identifier and the hex SHA-256 of its seal file. :raises httk.core.project.sealing.SealError: If the member is unsealed. .. py:method:: verify(member_root, *, trusted_keys, deep) Verify this member and return its report entries. Each entry is a mapping in the whole-tree verification shape — ``level``, ``subject``, ``valid``, ``verdict``, ``reason``, ``signers``, ``missing_signers``, and ``discrepancies`` (a list of ``{kind, path}``) — so the project report concatenates a member's entries with its own. :param member_root: This member's root directory. :param trusted_keys: Trust anchors to classify the signers against. :param deep: Whether to recurse into every seal the member references. :return: The member's verification entries. .. py:method:: repair(member_root, *, apply) Repair this member, or report only, returning its findings. Each finding is a mapping in the repair shape — ``check``, ``status``, ``message``, ``repairable``, ``repaired``, ``action``, ``details`` — so they concatenate with core's own anchor findings. Repairs are applied by default; ``apply=False`` is a dry run that mutates nothing. :param member_root: This member's root directory. :param apply: Whether to apply repairs; ``False`` reports only. :return: The member's repair findings. .. py:method:: scan_project(project_root, *, apply, adopt) Optionally scan the whole project for members of this kind. Core calls this once per registered kind at project scope, whether or not any member of the kind is registered, so a handler can surface members present on disk but missing from ``members.json`` — the exact rescue an empty registry needs. Repairs are applied by default; ``apply=False`` is a dry run that mutates nothing, and *adopt* tells the scan whether to also (re)establish members' machine-local links on this machine. It is optional: core invokes it only when the handler defines it. Findings use the same mapping shape as :meth:`repair`. :param project_root: The project root to scan. :param apply: Whether to apply repairs; ``False`` reports only. :param adopt: Whether to adopt members of this kind on this machine. :return: The project-scope findings for this kind. .. py:method:: adopt(member_root, *, name) Optionally (re)establish this member's local links on this machine. Adoption is the act of rebuilding whatever per-user or machine-local links a member needs to be usable *here* — for example, httk-workflow registers the member's workspace in the per-user name registry under its recorded *name*. It is idempotent and never mutates sealed state. Core invokes it only when the handler defines it, passing the member's recorded name; findings use the same mapping shape as :meth:`repair`. :param member_root: This member's root directory. :param name: The member's recorded name, or ``None`` when it has none. :return: The adoption findings for this member. .. py:method:: guard(member_root) Return a context manager fencing the member while it is snapshotted. A member that must be quiescent to be described faithfully — a workspace with running jobs — returns a guard that acquires that quiescence and raises if it cannot. A member with nothing to fence returns :func:`contextlib.nullcontext`. :param member_root: This member's root directory. :return: A context manager held around the snapshot. .. py:function:: members_path(project_root) Return where a project's member registry lives. :param project_root: The project root whose member registry path to build. :return: The ``httk_project/members.json`` path. .. py:function:: project_members(project_root) Return the members registered in a project, in recorded order. A missing registry is an empty project, not an error. :param project_root: The project root whose members to read. :return: The registered members. :raises ValueError: If the registry file is malformed. .. py:function:: register_project_member(project_root, path, kind, *, name = None) Record that a member of *kind* lives at *path*, idempotently. The path is normalized to a posix relpath below the project root and a path outside the project is refused. Registering the same path again replaces its kind and name. :param project_root: The project root whose registry to update. :param path: The member's path, absolute or relative to the project root. :param kind: The member kind whose handler owns the member. :param name: The member's recorded name, or ``None`` for none. :return: The members after the update. :raises httk.core.project.sealing.SealedError: If the project is sealed. :raises ValueError: If the path is outside the project or the name duplicates another member's. .. py:function:: unregister_project_member(project_root, path) Remove the member recorded at *path*. :param project_root: The project root whose registry to update. :param path: The member's path, absolute or relative to the project root. :return: The members after the removal. :raises httk.core.project.sealing.SealedError: If the project is sealed. :raises ValueError: If the path is outside the project or no member is recorded there. .. py:function:: update_project_member_path(project_root, old, new) Move the member recorded at *old* to *new*, keeping its kind. :param project_root: The project root whose registry to update. :param old: The member's current path, absolute or relative to the root. :param new: The member's new path, absolute or relative to the root. :return: The members after the move. :raises httk.core.project.sealing.SealedError: If the project is sealed. :raises ValueError: If a path is outside the project or no member is at *old*. .. py:function:: set_project_member_name(project_root, path, name) Set (or clear) the recorded name of the member at *path*. :param project_root: The project root whose registry to update. :param path: The member's path, absolute or relative to the project root. :param name: The name to record, or ``None`` to clear it. :return: The members after the update. :raises httk.core.project.sealing.SealedError: If the project is sealed. :raises ValueError: If no member is at *path* or the name duplicates another member's.