feat: [SG-43418] Unified context locking for Publisher dialog#219
feat: [SG-43418] Unified context locking for Publisher dialog#219chenm1adsk wants to merge 1 commit into
Conversation
Current state of tk-multi-publish2 ---- 1. No native context widget locking - locking was enforced at the DCC engine level using templates, not within tk-multi-publish2 itself. 2. No context isolation between dialogs - all dialogs fell back to the process-wide engine context singleton, so engine.change_context() called while a dialog was open could silently mutate the context of items in already-open dialogs. 3. No way for callers to pass a target context or seed properties to the dialog without relying on process-wide env vars or private APIs. Now handled natively in tk-multi-publish2 ---- These are addressed by supporting two scenarios based on whether the caller's context matches the engine's own context: **Scenario A** - Engine already in task context** (e.g. Maya opened from a task workfile, context == bundle.context): the launch context is snapshotted onto root_item at dialog init time, isolating the item tree from concurrent engine.change_context() calls. After collection, items with a fully resolved task + entity have their context widget locked (read-only). **Scenario B** - Caller passes a context different from the engine** (e.g. Loader in a project-level tk-desktop engine passing a task context, context != bundle.context): the context is stored as _pre_fill_context and applied to top-level items after collection so the context widget is pre-filled. The widget remains editable since the supplied context is a suggestion. Pre-fill must run after collection to avoid breaking plugin loading via pick_environment. show_dialog() and AppDialog.__init__ now accept context= and root_item_properties= keyword arguments so callers can pass seed properties (e.g. am_revision_id) directly.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #219 +/- ##
==========================================
- Coverage 40.81% 40.75% -0.06%
==========================================
Files 63 63
Lines 5109 5135 +26
==========================================
+ Hits 2085 2093 +8
- Misses 3024 3042 +18
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Adds first-class, dialog-scoped context handling to tk-multi-publish2 so the Publisher UI can (a) isolate its item tree from concurrent engine.change_context() calls and (b) optionally pre-fill a caller-supplied target context without relying on process-wide context mutation.
Changes:
- Extend
show_dialog()andAppDialogto acceptcontext=androot_item_properties=and forward them intoPublishManager. - Update
PublishManagerto support context snapshotting vs. post-collection pre-fill, and add a gating step to lock context editing when context is fully resolved. - Add a Desktop-specific collector hook to block publishing DCC-native files from Desktop workflows.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 12 comments.
| File | Description |
|---|---|
python/tk_multi_publish2/dialog.py |
Passes context/root seed properties into the manager and applies pre-fill + lock-gating after collection; includes doc/comment updates. |
python/tk_multi_publish2/api/manager.py |
Adds context snapshot/pre-fill support plus context-lock gating APIs; adjusts initialization and introduces new post-collection helpers. |
python/tk_multi_publish2/__init__.py |
Extends show_dialog() API to accept and forward context and root_item_properties. |
hooks/flowam/collector_desktop.py |
New hook to block DCC file types from Desktop publishing and provide user-facing guidance. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ) | ||
| return None | ||
|
|
||
| return self._collect_file(parent_item, path) |
| file_info = self.parent.util.get_file_path_components(path) | ||
| extension = file_info["extension"] |
|
|
||
| if extension in dcc_extensions: | ||
| # Get the file type display name | ||
| file_type = "Unknown" |
| # create a publish manager - snapshot the launch context onto the root | ||
| # item so that this dialog is isolated from concurrent engine.change_context() calls. | ||
| # An explicit context takes priority over the current engine context. |
| :param context: Optional sgtk.Context to pin this dialog to. When set, | ||
| overrides the current engine context for this dialog's item tree. | ||
| Useful for callers that cannot call engine.change_context() first. |
| :param context: Optional sgtk.Context to snapshot onto the root item. | ||
| When set, item context resolution stops at the root and never falls | ||
| back to the process-wide engine singleton - isolating this dialog | ||
| from concurrent context changes. |
| if self._pre_fill_context: | ||
| for item in self._tree.root_item.children: | ||
| if item._context is None: | ||
| item._context = self._pre_fill_context |
| @property | ||
| def context(self): | ||
| """Returns the execution context of the manager.""" | ||
| return self._bundle.context | ||
|
|
||
| @property |
| """Lock context widget for items whose context is fully resolved. | ||
|
|
||
| Only applied when engine already in task context. When the caller | ||
| passed a different context, the context is a suggestion and the | ||
| user should still be able to change it. |
| def apply_pre_fill_context(self): | ||
| """Apply pre-fill context to top-level items after collection. | ||
|
|
||
| Only applied when the caller passed a context that differs from | ||
| bundle.context (e.g. Loader passing a Task context into a | ||
| project-level engine). When the engine is already in task context, | ||
| items inherit it via the fallback chain and no pre-fill is needed. | ||
| Skips items where the collector already set an explicit context. | ||
| """ | ||
| if self._pre_fill_context: | ||
| for item in self._tree.root_item.children: | ||
| if item._context is None: | ||
| item._context = self._pre_fill_context | ||
|
|
||
| def apply_context_lock_gate(self): | ||
| """Lock context widget for items whose context is fully resolved. | ||
|
|
||
| Only applied when engine already in task context. When the caller | ||
| passed a different context, the context is a suggestion and the | ||
| user should still be able to change it. | ||
| """ | ||
| if self._pre_fill_context is not None: | ||
| return | ||
| for item in self._tree.root_item.children: | ||
| ctx = item.context | ||
| if ctx and ctx.entity and ctx.task: | ||
| item.context_change_allowed = False |
Current state of tk-multi-publish2
No native context widget locking - locking was enforced at the DCC engine level using templates, not within tk-multi-publish2 itself.
No context isolation between dialogs - all dialogs fell back to the process-wide engine context singleton, so engine.change_context() called while a dialog was open could silently mutate the context of items in already-open dialogs.
No way for callers to pass a target context or seed properties to the dialog without relying on process-wide env vars or private APIs.
Now handled natively in tk-multi-publish2
These are addressed by supporting two scenarios based on whether the caller's context matches the engine's own context:
Scenario A - Engine already in task context** (e.g. Maya opened from
a task workfile, context == bundle.context): the launch context is
snapshotted onto root_item at dialog init time, isolating the item tree
from concurrent engine.change_context() calls. After collection, items
with a fully resolved task + entity have their context widget locked
(read-only).
Scenario B - Caller passes a context different from the engine**
(e.g. Loader in a project-level tk-desktop engine passing a task
context, context != bundle.context): the context is stored as
_pre_fill_context and applied to top-level items after collection so
the context widget is pre-filled. The widget remains editable since the
supplied context is a suggestion. Pre-fill must run after collection to
avoid breaking plugin loading via pick_environment.
show_dialog() and AppDialog.init now accept context= and root_item_properties= keyword arguments so callers can pass seed properties (e.g. am_revision_id) directly.