sync_stub¶
Sync ifcopenshell_wrapper.pyi with the compiled ifcopenshell_wrapper.py by editing only the entries that are unambiguous to add or remove - never by regenerating the file wholesale.
- Auto-applies:
top-level symbols (classes, functions, constants) present in the wrapper but missing from the stub -> inserted, alphabetically among neighbouring tracked entries. A brand-new class is rendered with its full member set (there’s no existing curated body to preserve).
top-level symbols in the stub that no longer exist in the wrapper at all -> removed.
for a class whose own declaration (name + bases) matches on both sides and whose body isn’t a single-line …: plain members (methods, bare attributes - not @property/@staticmethod wrappers) present in the wrapper’s class but missing from the stub’s -> inserted; members in the stub’s class no longer present on the wrapper’s -> removed.
- Never auto-applies - reported instead, left completely untouched:
a top-level symbol whose declaration differs between stub and wrapper under the same name (e.g. a function’s parameter list, or a class’s base classes).
__init__, in every case (present on both sides with a different signature, or missing from one side entirely) - this is almost always where hand-curated constructor signatures live, since SWIG always emits generic *args for overloaded C++ constructors.
a class member that differs under the same name but isn’t a plain def/attribute, or any member of a class whose own declaration didn’t match.
anything that only looks addable/removable in the narrow view this tool parses but is actually still present on the other side in a form it doesn’t parse (chiefly property()/staticmethod() wrapper assignments, and the raw getter/setter method(s) such a wrapper consumes) - checked against validate_stub.py’s own fuller canonicalisation before anything is added or removed, so this never causes data loss.
any class where, after the safe edits above, its member set still doesn’t fully match the wrapper’s (typically for the property/ staticmethod reason above) - reported so a human can look, using validate_stub.py’s own diff for that class.
This is exactly where a hand-curated stub is expected to diverge from raw SWIG output on purpose - a mechanical tool can’t tell that apart from real drift, so it leaves those lines untouched and reports them for a human to judge instead of guessing.
Everything else in the file - the license header, comments, blank lines, docstrings, curated signatures, import order - is left byte-for-byte untouched: this script tracks a line cursor through the original source and only ever substitutes the exact line ranges of the entries it’s confident about, it never rewrites the file from scratch.
- Usage:
python sync_stub.py # dry run: prints a report, writes nothing python sync_stub.py –write # applies the safe add/remove edits in place
Module Contents¶
- class sync_stub.Entry¶
- canonical: validate_stub.SubnameType¶
- is_init: bool = False¶
- node: ast.stmt¶
- recursable: bool = False¶
- class sync_stub.Report¶
- sync_stub.identity_of(value: validate_stub.SubnameType) str¶
Bare identity name for a get_names_tree()-style canonical value or top-level key, so entries can be matched by name even when their full signature differs (or when one side’s form - e.g. a property()-wrapped Assign - isn’t something iter_simple_entries() looks at directly).
- sync_stub.iter_simple_entries(body: list) list[Entry]¶
The subset of a module’s or class’s statements this tool is willing to reason about for auto-add/auto-remove: classes, plain function defs (including directly @decorated ones), and plain (non-property()/ staticmethod()-wrapped) assignments. Everything else - imports, bare docstrings/expressions, private names, trivial __init__(self), a property()/staticmethod() wrapper assignment itself, and the raw getter/setter method(s) it wraps (SWIG emits both, e.g. a calc_surface_area_ method alongside surface_area = property(calc_surface_area_)) - is left alone entirely by never being reported as an Entry at all.
- sync_stub.main() None¶
- sync_stub.render_entry(canonical: validate_stub.SubnameType, indent: str) list[str]¶
Render a canonical name/signature value as stub text, matching generate_stub.py’s own per-line rendering so a freshly-inserted entry parses back to the same canonical value get_names_tree() would compute.
- sync_stub.render_new_top_level(canonical: str, indent: str, wrapper_tree: dict) list[str]¶
Render a brand-new top-level entry. For a class this can’t just be the header line - there’s no existing curated body to preserve, so the whole class is rendered fresh from the wrapper’s true canonical subname set (the same full set validate_stub.py itself would compute) - it’s all new content either way, same as generate_stub.py would produce.
- sync_stub.splice_body(nodes: list[ast.stmt], source_lines: list[str], stub_tree: dict, wrapper_tree: dict, wrapper_class_bodies: dict[str, list], wrapper_entries_by_id: dict[str, Entry], indent: str, report: Report, scope: str, start_line: int, scope_end_line: int, class_key: str | None = None) list[str]¶
- sync_stub.LICENSE_HEADER_START = '# IfcOpenShell - IFC toolkit and geometry engine'¶