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):
IModelDocExtension::GetWhatsWrong with three byref VARIANT out-params
- one call returns every flagged feature, its code and warning flag.
- 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
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
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
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
|
|
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
|