stubpy.stub_merge¶
stubpy.stub_merge¶
Incremental stub update — merge newly generated stubs with an existing
.pyi file while preserving manually edited content.
The merge system wraps auto-generated sections with marker comments:
# stubpy: auto-generated begin
... (generated content) ...
# stubpy: auto-generated end
On subsequent runs only the content between the markers is replaced. Content outside the markers (manual additions, hand-tuned overloads, etc.) is preserved unchanged.
Marker rules¶
Case-insensitive and whitespace-lenient:
# Stubpy : Auto-Generated Beginand#stubpy:auto-generated beginare both valid.Multiple pairs in one file are supported. Each
begin/endpair is replaced independently.Half-open markers are handled gracefully: - An
endwithout a precedingbegin→ the start of the file istreated as the implicit
begin(the generated header replaced).A
beginwithout a followingend→ the end of the file is treated as the implicitend(everything after the begin replaced).
Content outside all marker pairs is left untouched.
The markers are added to new stubs automatically by wrap_generated().
When merging into an existing file that has no markers at all, the entire
existing file content is left unchanged and the new content is appended
inside a fresh marker pair — giving the developer a chance to review the
difference before committing.
Public API¶
wrap_generated()Wrap a newly generated stub body with
begin/endmarkers.merge_stubs()Merge a freshly generated stub into an existing
.pyifile.
- BEGIN_MARKER = '# stubpy: auto-generated begin'¶
Canonical form written into new stubs.
- wrap_generated(content: str) str[source]¶
Wrap content between canonical begin/end markers.
The markers are added exactly once — they are not added again if content already contains them.
- Parameters:
content (str) – A complete stub file string, typically the output of
generate_stub().- Returns:
str – content with
# stubpy: auto-generated beginprepended and# stubpy: auto-generated endappended (with trailing newline).
Examples
>>> wrapped = wrap_generated("x: int\n") >>> wrapped.startswith("# stubpy: auto-generated begin") True >>> wrapped.strip().endswith("# stubpy: auto-generated end") True
- merge_stubs(existing: str, generated: str) str[source]¶
Merge generated stub content into existing
.pyicontent.Only the regions inside marker pairs are replaced. Everything outside the markers is left untouched.
Strategy when no markers exist in existing¶
The entire existing file is left as-is and the new content is appended as a new marked section. This is intentionally conservative — on the first run after adding markers the developer reviews the diff.
- param existing:
Current content of the
.pyifile on disk.- type existing:
str
- param generated:
Freshly generated stub content (already passed through
wrap_generated()).- type generated:
str
- returns:
str – Merged content suitable for writing back to disk.
Examples
>>> existing = ( ... "# manual stuff\n" ... "# stubpy: auto-generated begin\n" ... "x: int\n" ... "# stubpy: auto-generated end\n" ... "# more manual\n" ... ) >>> generated = "# stubpy: auto-generated begin\ny: str\n# stubpy: auto-generated end\n" >>> result = merge_stubs(existing, generated) >>> "y: str" in result True >>> "# manual stuff" in result True >>> "# more manual" in result True >>> "x: int" not in result True
- read_and_merge(output_path: Path, generated: str) str[source]¶
Read output_path (if it exists) and merge generated into it.
- Parameters:
output_path (Path) – Path to the existing
.pyifile. When the file does not exist, generated (wrapped in markers) is returned directly.generated (str) – Freshly generated stub content from
generate_stub().
- Returns:
str – Final merged content ready to be written to output_path.