Skip to content

solidworks_mcp.adapters.solidworks.rebuild_inspector

solidworks_mcp.adapters.solidworks.rebuild_inspector

Collect rebuild errors/warnings from the active SolidWorks model (COM side).

Feeds :mod:rebuild_diagnostics, which turns each raw issue into a description and fix hint. Two sources, in the order chosen from the live probe (R2026x, 2026-10-13, see the #106 change's design.md):

  1. IModelDocExtension::GetWhatsWrong with three byref VARIANT out-params - one call returns every flagged feature, its code and warning flag.
  2. Fallback: walk the feature tree calling IFeature::GetErrorCode2(byref IsWarning) on each unsuppressed feature.

Attributes

_FEATURE_MEMBERS module-attribute

_FEATURE_MEMBERS = ('GetTypeName2', 'GetParents', 'GetNextFeature', 'GetFirstSubFeature', 'GetNextSubFeature', 'GetErrorCode2', 'IsSuppressed', 'GetSpecificFeature2')

_MAX_FEATURES module-attribute

_MAX_FEATURES = 10000

Classes

ParentInfo dataclass

ParentInfo(name: str, type: str = '', segment_count: int | None = None)

A feature the failing feature depends on.

Attributes:

Name Type Description
name str

The parent feature or sketch name.

type str

Its GetTypeName2 value ("" when unknown).

segment_count int | None

For sketches, how many segments it has; None when not a sketch or not readable.

RawRebuildIssue dataclass

RawRebuildIssue(feature: str, type: str, code: int, is_warning: bool, parents: list[ParentInfo] = list())

One raw GetWhatsWrong entry as collected from COM.

Functions:

_as_list

_as_list(value: Any) -> list[Any]

Normalise a COM array/scalar/None return into a list.

Source code in src/solidworks_mcp/adapters/solidworks/com_utils.py
def as_list(value: Any) -> list[Any]:
    """Normalise a COM array/scalar/None return into a list."""
    if value is None:
        return []
    if isinstance(value, (list, tuple)):
        return list(value)
    return [value]

_attempt

_attempt(adapter: Any, fn: Any, default: Any = None) -> Any

Run fn through the adapter's _attempt (exceptions -> default).

Source code in src/solidworks_mcp/adapters/solidworks/com_utils.py
def attempt(adapter: Any, fn: Any, default: Any = None) -> Any:
    """Run ``fn`` through the adapter's ``_attempt`` (exceptions -> default)."""
    return adapter._attempt(fn, default=default)

_byref_bool

_byref_bool() -> Any

Return a byref bool VARIANT (e.g. IFeature::GetErrorCode2(IsWarning)).

Returns:

Name Type Description
Any Any

The byref VARIANT, or a _ByrefFallback(False) without pywin32.

Source code in src/solidworks_mcp/adapters/solidworks/io.py
def _byref_bool() -> Any:
    """Return a byref bool VARIANT (e.g. ``IFeature::GetErrorCode2(IsWarning)``).

    Returns:
        Any: The byref VARIANT, or a ``_ByrefFallback(False)`` without pywin32.
    """
    variant_ctor = getattr(getattr(win32com, "client", None), "VARIANT", None)
    if not callable(variant_ctor):
        return _ByrefFallback(False)
    return variant_ctor(
        int(getattr(pythoncom, "VT_BYREF", 0)) | int(getattr(pythoncom, "VT_BOOL", 0)),
        False,
    )

_byref_variant

_byref_variant() -> Any

Return a byref VARIANT for an array/variant out-parameter.

IModelDocExtension::GetWhatsWrong fills three of these (features, error codes, warning flags); confirmed live on R2026x that late binding accepts VARIANT(VT_BYREF | VT_VARIANT, None) and exposes the result on .value. pythoncom.Missing is rejected ("can not be converted to a COM VARIANT").

Returns:

Name Type Description
Any Any

The byref VARIANT, or a _ByrefFallback(None) without pywin32.

Source code in src/solidworks_mcp/adapters/solidworks/io.py
def _byref_variant() -> Any:
    """Return a byref VARIANT for an array/variant out-parameter.

    ``IModelDocExtension::GetWhatsWrong`` fills three of these (features,
    error codes, warning flags); confirmed live on R2026x that late binding
    accepts ``VARIANT(VT_BYREF | VT_VARIANT, None)`` and exposes the result on
    ``.value``. ``pythoncom.Missing`` is rejected ("can not be converted to a
    COM VARIANT").

    Returns:
        Any: The byref VARIANT, or a ``_ByrefFallback(None)`` without pywin32.
    """
    variant_ctor = getattr(getattr(win32com, "client", None), "VARIANT", None)
    if not callable(variant_ctor):
        return _ByrefFallback(None)
    return variant_ctor(
        int(getattr(pythoncom, "VT_BYREF", 0))
        | int(getattr(pythoncom, "VT_VARIANT", 0)),
        None,
    )

_check

_check(adapter: Any, feature: Any, issues: list[RawRebuildIssue]) -> None

Append feature's rebuild issue (if any) to issues.

Source code in src/solidworks_mcp/adapters/solidworks/rebuild_inspector.py
def _check(adapter: Any, feature: Any, issues: list[RawRebuildIssue]) -> None:
    """Append ``feature``'s rebuild issue (if any) to ``issues``."""
    if _attempt(adapter, lambda: feature.IsSuppressed(), False):
        return
    is_warning = _byref_bool()
    code = _attempt(adapter, lambda: feature.GetErrorCode2(is_warning), 0)
    if isinstance(code, (int, float)) and int(code) != 0:
        issues.append(_issue(adapter, feature, int(code), bool(is_warning.value)))

_flag

_flag(adapter: Any, obj: Any, *names: str) -> None

Flag just names as methods on obj (no-op for None/fakes).

Flagging a whole interface costs ~27 ms per fresh dispatch (see _WALK_MEMBERS in pywin32_adapter); SolidWorks hands back a fresh dispatch on most calls, so flag only what is about to be read.

Source code in src/solidworks_mcp/adapters/solidworks/com_utils.py
def flag(adapter: Any, obj: Any, *names: str) -> None:
    """Flag just ``names`` as methods on ``obj`` (no-op for ``None``/fakes).

    Flagging a whole interface costs ~27 ms per fresh dispatch (see
    ``_WALK_MEMBERS`` in pywin32_adapter); SolidWorks hands back a fresh
    dispatch on most calls, so flag only what is about to be read.
    """
    attempt(adapter, lambda: _sw_type_info.flag_members(obj, *names), 0)

_from_tree_walk

_from_tree_walk(adapter: Any, model: Any) -> list[RawRebuildIssue]

Source 2: per-feature GetErrorCode2 over the unsuppressed tree.

Visits each top-level feature and its sub-features (e.g. the sketch an extrude or cut absorbed), since a sketch error lives on the sketch.

Source code in src/solidworks_mcp/adapters/solidworks/rebuild_inspector.py
def _from_tree_walk(adapter: Any, model: Any) -> list[RawRebuildIssue]:
    """Source 2: per-feature GetErrorCode2 over the unsuppressed tree.

    Visits each top-level feature and its sub-features (e.g. the sketch an
    extrude or cut absorbed), since a sketch error lives on the sketch.
    """
    issues: list[RawRebuildIssue] = []
    feature = _attempt(adapter, lambda: model.FirstFeature())
    guard = 0
    while feature is not None and guard < _MAX_FEATURES:
        guard += 1
        _flag(adapter, feature, *_FEATURE_MEMBERS)
        current = feature
        _check(adapter, current, issues)
        sub = _attempt(adapter, lambda f=current: f.GetFirstSubFeature())
        while sub is not None and guard < _MAX_FEATURES:
            guard += 1
            _flag(adapter, sub, *_FEATURE_MEMBERS)
            sub_current = sub
            _check(adapter, sub_current, issues)
            sub = _attempt(adapter, lambda s=sub_current: s.GetNextSubFeature())
        feature = _attempt(adapter, lambda f=current: f.GetNextFeature())
    return issues

_from_whats_wrong

_from_whats_wrong(adapter: Any, model: Any) -> list[RawRebuildIssue] | None

Source 1: one GetWhatsWrong call. None when the call is unusable.

Source code in src/solidworks_mcp/adapters/solidworks/rebuild_inspector.py
def _from_whats_wrong(adapter: Any, model: Any) -> list[RawRebuildIssue] | None:
    """Source 1: one GetWhatsWrong call. None when the call is unusable."""
    ext = _attempt(adapter, lambda: model.Extension)
    if ext is None:
        return None
    _flag(adapter, ext, "GetWhatsWrongCount", "GetWhatsWrong")
    count = _attempt(adapter, lambda: ext.GetWhatsWrongCount())
    if isinstance(count, (int, float)) and int(count) == 0:
        return []
    features, codes, warnings = _byref_variant(), _byref_variant(), _byref_variant()
    if _attempt(adapter, lambda: ext.GetWhatsWrong(features, codes, warnings)) is None:
        return None
    feats = _as_list(features.value)
    errs = _as_list(codes.value)
    warns = _as_list(warnings.value)
    if not feats or len(errs) != len(feats):
        return None
    warns = (warns + [False] * len(feats))[: len(feats)]
    return [
        _issue(adapter, f, int(c), bool(w))
        for f, c, w in zip(feats, errs, warns, strict=True)
        if f is not None
    ]

_issue

_issue(adapter: Any, feature: Any, code: int, is_warning: bool) -> RawRebuildIssue
Source code in src/solidworks_mcp/adapters/solidworks/rebuild_inspector.py
def _issue(adapter: Any, feature: Any, code: int, is_warning: bool) -> RawRebuildIssue:
    _flag(adapter, feature, *_FEATURE_MEMBERS)
    return RawRebuildIssue(
        feature=str(_attempt(adapter, lambda: feature.Name, "") or ""),
        type=str(_attempt(adapter, lambda: feature.GetTypeName2(), "") or ""),
        code=int(code),
        is_warning=bool(is_warning),
        parents=_parents(adapter, feature),
    )

_parents

_parents(adapter: Any, feature: Any) -> list[ParentInfo]

Describe the features/sketches feature depends on.

Source code in src/solidworks_mcp/adapters/solidworks/rebuild_inspector.py
def _parents(adapter: Any, feature: Any) -> list[ParentInfo]:
    """Describe the features/sketches ``feature`` depends on."""
    out = []
    for parent in _as_list(_attempt(adapter, lambda: feature.GetParents())):
        _flag(adapter, parent, *_FEATURE_MEMBERS)
        name = str(_attempt(adapter, lambda p=parent: p.Name, "") or "")
        kind = str(_attempt(adapter, lambda p=parent: p.GetTypeName2(), "") or "")
        segments = (
            _sketch_segment_count(adapter, parent) if kind == "ProfileFeature" else None
        )
        out.append(ParentInfo(name=name, type=kind, segment_count=segments))
    return out

_sketch_segment_count

_sketch_segment_count(adapter: Any, feature: Any) -> int | None

Segment count of a sketch feature, or None when not readable.

Source code in src/solidworks_mcp/adapters/solidworks/rebuild_inspector.py
def _sketch_segment_count(adapter: Any, feature: Any) -> int | None:
    """Segment count of a sketch feature, or None when not readable."""
    sketch = _attempt(adapter, lambda: feature.GetSpecificFeature2())
    if sketch is None:
        return None
    _flag(adapter, sketch, "GetSketchSegments")
    segments = _attempt(adapter, lambda: sketch.GetSketchSegments(), default=False)
    if segments is False:
        return None
    return len(_as_list(segments))

collect_rebuild_issues

collect_rebuild_issues(adapter: Any, model: Any) -> list[RawRebuildIssue]

Return every rebuild error/warning on model after a rebuild.

Parameters:

Name Type Description Default
adapter Any

A connected adapter (for _attempt).

required
model Any

The active IModelDoc2.

required

Returns:

Type Description
list[RawRebuildIssue]

list[RawRebuildIssue]: Empty when the model is clean.

Source code in src/solidworks_mcp/adapters/solidworks/rebuild_inspector.py
def collect_rebuild_issues(adapter: Any, model: Any) -> list[RawRebuildIssue]:
    """Return every rebuild error/warning on ``model`` after a rebuild.

    Args:
        adapter: A connected adapter (for ``_attempt``).
        model: The active ``IModelDoc2``.

    Returns:
        list[RawRebuildIssue]: Empty when the model is clean.
    """
    issues = _from_whats_wrong(adapter, model)
    if issues is None:
        issues = _from_tree_walk(adapter, model)
    return issues