fix(silo): opening assembly does not pull linked part documents #337

Closed
opened 2026-02-26 17:15:04 +00:00 by forbes · 1 comment
Owner

Problem

Opening a Silo assembly via any path other than Silo Pull opens the assembly document but does not download the linked part files it references. FreeCAD's PropertyXLink resolution then fails silently — the assembly opens with broken or missing links, and the parts are invisible.

Affected Code Paths

Dependency pulling (_pull_dependencies()) is only wired into the Silo_Pull command (silo_commands.py:1399-1410). All other entry points that open documents skip it entirely:

Entry Point Location Pulls deps?
Silo_Pull command silo_commands.py:1267 Yes (via _pull_dependencies)
Silo_Open dialog (_on_selected) silo_commands.py:750 No — calls _sync.open_item() or FreeCAD.openDocument() directly
SiloOrigin.openDocument() silo_origin.py:308 No — calls _sync.open_item() or FreeCAD.openDocument() directly
Activity pane double-click silo_commands.py:3195 No
Activity pane context menu Open silo_commands.py:3230 No
BOM pane open silo_commands.py:4058 No

Root Cause

_sync.open_item() (silo_commands.py:634-646) either opens an existing local file or creates a new document from the database item metadata. It has no awareness of BOM/assembly dependencies:

def open_item(self, part_number):
    existing_path = find_file_by_part_number(part_number)
    if existing_path and existing_path.exists():
        return FreeCAD.openDocument(str(existing_path))
    item = self.client.get_item(part_number)
    return self.create_document_for_item(item, save=True)

When FreeCAD opens an assembly .FCStd, its PropertyXLink resolver looks for the linked documents on disk relative to the assembly file location. If those files were never pulled, the links fail and the parts don't appear.

Proposed Fix

Option A: Centralize dependency pulling in open_item()

Make open_item() BOM-aware — if the item is an assembly, call _pull_dependencies() before opening the document. This fixes all code paths at once since they all funnel through open_item() (or should).

def open_item(self, part_number):
    existing_path = find_file_by_part_number(part_number)
    item = self.client.get_item(part_number)
    if item.get('item_type') == 'assembly':
        _pull_dependencies(part_number)
    if existing_path and existing_path.exists():
        return FreeCAD.openDocument(str(existing_path))
    return self.create_document_for_item(item, save=True)

Option B: Add a post-open hook that pulls missing dependencies

Register a slotOpenDocument observer that checks whether the opened document is a Silo-tracked assembly and pulls any missing BOM children before FreeCAD resolves links.

Recommendation

Option A is simpler and more predictable. It ensures dependencies are pulled before the document opens (matching the working Silo_Pull behavior). The direct FreeCAD.openDocument() calls in the activity pane and Silo_Open should also be routed through open_item() so they benefit from the centralized logic.

Additional Considerations

  • _pull_dependencies() already handles recursion (nested assemblies) and skips existing files, so it is safe to call unconditionally on assemblies
  • Progress feedback: open_item() currently has no progress callback — opening large assemblies with many un-pulled dependencies could appear to hang. Consider adding a progress dialog.
  • The get_bom() API call is required to resolve dependencies — this needs a network connection. Offline behavior should degrade gracefully (open what is available locally).
  • The Silo_Open dialog and activity pane bypass open_item() entirely when a local file exists — these should be updated to route through open_item() as well.
## Problem Opening a Silo assembly via any path other than **Silo Pull** opens the assembly document but does not download the linked part files it references. FreeCAD's PropertyXLink resolution then fails silently — the assembly opens with broken or missing links, and the parts are invisible. ## Affected Code Paths Dependency pulling (_pull_dependencies()) is **only** wired into the Silo_Pull command (silo_commands.py:1399-1410). All other entry points that open documents skip it entirely: | Entry Point | Location | Pulls deps? | |---|---|---| | Silo_Pull command | silo_commands.py:1267 | Yes (via _pull_dependencies) | | Silo_Open dialog (_on_selected) | silo_commands.py:750 | **No** — calls _sync.open_item() or FreeCAD.openDocument() directly | | SiloOrigin.openDocument() | silo_origin.py:308 | **No** — calls _sync.open_item() or FreeCAD.openDocument() directly | | Activity pane double-click | silo_commands.py:3195 | **No** | | Activity pane context menu Open | silo_commands.py:3230 | **No** | | BOM pane open | silo_commands.py:4058 | **No** | ## Root Cause _sync.open_item() (silo_commands.py:634-646) either opens an existing local file or creates a new document from the database item metadata. It has no awareness of BOM/assembly dependencies: def open_item(self, part_number): existing_path = find_file_by_part_number(part_number) if existing_path and existing_path.exists(): return FreeCAD.openDocument(str(existing_path)) item = self.client.get_item(part_number) return self.create_document_for_item(item, save=True) When FreeCAD opens an assembly .FCStd, its PropertyXLink resolver looks for the linked documents on disk relative to the assembly file location. If those files were never pulled, the links fail and the parts don't appear. ## Proposed Fix ### Option A: Centralize dependency pulling in open_item() Make open_item() BOM-aware — if the item is an assembly, call _pull_dependencies() before opening the document. This fixes all code paths at once since they all funnel through open_item() (or should). def open_item(self, part_number): existing_path = find_file_by_part_number(part_number) item = self.client.get_item(part_number) if item.get('item_type') == 'assembly': _pull_dependencies(part_number) if existing_path and existing_path.exists(): return FreeCAD.openDocument(str(existing_path)) return self.create_document_for_item(item, save=True) ### Option B: Add a post-open hook that pulls missing dependencies Register a slotOpenDocument observer that checks whether the opened document is a Silo-tracked assembly and pulls any missing BOM children before FreeCAD resolves links. ### Recommendation **Option A** is simpler and more predictable. It ensures dependencies are pulled before the document opens (matching the working Silo_Pull behavior). The direct FreeCAD.openDocument() calls in the activity pane and Silo_Open should also be routed through open_item() so they benefit from the centralized logic. ## Additional Considerations - _pull_dependencies() already handles recursion (nested assemblies) and skips existing files, so it is safe to call unconditionally on assemblies - Progress feedback: open_item() currently has no progress callback — opening large assemblies with many un-pulled dependencies could appear to hang. Consider adding a progress dialog. - The get_bom() API call is required to resolve dependencies — this needs a network connection. Offline behavior should degrade gracefully (open what is available locally). - The Silo_Open dialog and activity pane bypass open_item() entirely when a local file exists — these should be updated to route through open_item() as well.
forbes added the bug label 2026-02-26 17:15:04 +00:00
Author
Owner

Fix implemented in silo-mod PR #51.

Changes:

  • New _pull_dependencies() function downloads BOM children to canonical paths before opening
  • open_item() detects assemblies and calls _pull_dependencies() automatically
  • Silo_Open routes through open_item() when part_number is available
  • Silo_Pull pulls dependencies with progress dialog after main file download

Once the silo-mod PR is merged, the submodule pointer in this repo will be updated.

Fix implemented in silo-mod PR [#51](https://git.kindred-systems.com/kindred/silo-mod/pulls/51). **Changes:** - New `_pull_dependencies()` function downloads BOM children to canonical paths before opening - `open_item()` detects assemblies and calls `_pull_dependencies()` automatically - `Silo_Open` routes through `open_item()` when `part_number` is available - `Silo_Pull` pulls dependencies with progress dialog after main file download Once the silo-mod PR is merged, the submodule pointer in this repo will be updated.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: kindred/create#337