diff --git a/docs/design/in-memory-config.md b/docs/design/in-memory-config.md
new file mode 100644
index 000000000..746718c3a
--- /dev/null
+++ b/docs/design/in-memory-config.md
@@ -0,0 +1,227 @@
+# In-Memory Configuration for GUI Submit
+
+## Problem
+
+The current configuration system in deadline-cloud always reads settings from the on-disk config file (`~/.deadline/config`). When a user runs `deadline bundle gui-submit`, there is no way to pass CLI arguments like `--farm-id` or `--queue-id` to pre-populate the submission dialog. The non-GUI `bundle submit` command supports these options via `_apply_cli_options_to_config`, which creates an in-memory `ConfigParser` overlay, but this pattern is not wired into the GUI submission path.
+
+Additionally, the settings dialog (`DeadlineConfigDialog`) always writes changes directly to the on-disk config. There is no concept of editing settings that only apply to the current session.
+
+## Goals
+
+1. Allow users to pass `--farm-id`, `--queue-id`, `--profile`, and `--storage-profile-id` into `deadline bundle gui-submit`, applying them as in-memory overrides for the submission session without writing to disk.
+2. In the settings dialog opened from the submission window, allow users to choose between modifying the in-memory (session) config or the workstation (on-disk) config.
+
+## Current Architecture
+
+### Config Read/Write Flow
+
+- `config_file.read_config()` reads from `~/.deadline/config` (with caching based on mtime).
+- `config_file.get_setting(name, config=None)` reads from the on-disk config when `config=None`, or from a provided `ConfigParser` when one is passed.
+- `config_file.set_setting(name, value, config=None)` writes to disk when `config=None`, or mutates the provided `ConfigParser` in-memory when one is passed.
+- `config_file.write_config(config)` persists a `ConfigParser` to disk.
+
+### CLI to In-Memory Config (existing pattern)
+
+`_apply_cli_options_to_config(**args)` in `_common.py`:
+- Always returns a fresh `ConfigParser` copy — never `None`, never mutates the caller's config or the `read_config()` cache.
+- If any CLI args are non-None, applies overrides via `set_setting(name, value, config=config)` on the copy.
+- If no CLI args are provided, returns an unmodified copy of the disk config.
+- Used by all CLI commands (`bundle submit`, `bundle gui-submit`, `job get`, etc.).
+
+### GUI Submission Flow
+
+`bundle_gui_submit` -> `show_job_bundle_submitter()` -> creates `SubmitJobToDeadlineDialog`.
+
+The dialog's `SharedJobSettingsWidget` calls `get_setting("defaults.farm_id")` and `get_setting("defaults.queue_id")` **without** passing a config object, so it always reads from disk.
+
+The `DeadlineUIController` and `DeadlineAuthenticationStatus` singletons accept a `ConfigParser` via `set_config()`, but the submission dialog never passes one derived from CLI args.
+
+### Settings Dialog
+
+`DeadlineConfigDialog` / `DeadlineWorkstationConfigWidget`:
+- Maintains a `self.config` (copy of on-disk config) and a `self.changes` dict.
+- On `apply()`, reads fresh config from disk, applies changes, then calls `write_config()` to persist.
+- There is no concept of "session-only" changes.
+
+## Design
+
+### 1. CLI Options on `bundle gui-submit`
+
+Add `--profile`, `--farm-id`, `--queue-id`, and `--storage-profile-id` options to the `bundle_gui_submit` click command, matching what `bundle_submit` already accepts.
+
+In the command handler, use `_apply_cli_options_to_config()` to produce an in-memory `ConfigParser` with the overrides applied. Since `_apply_cli_options_to_config` always returns a fresh copy (never `None`, never mutates the cache), the result can be passed directly as the session config. Pass this config object through to `show_job_bundle_submitter()` and into `SubmitJobToDeadlineDialog`.
+
+### 2. Session Config Propagation
+
+`SubmitJobToDeadlineDialog.__init__` accepts an optional `session_config: Optional[ConfigParser]` parameter. When provided:
+
+- Stored as `self._session_config`.
+- Passed to `SharedJobSettingsWidget` via its new `config` parameter.
+- Used in `_set_submit_button_state` to check farm/queue configuration.
+- Used in `on_submit` when starting job submission (falls back to `config_file.read_config()` when `None`).
+
+When `config` is `None` (no CLI overrides), behavior is unchanged — everything reads from disk as before.
+
+### 3. Config Threading Through Widget Hierarchy
+
+During implementation we discovered that the config needs to be threaded deeper than initially expected. The full propagation chain is:
+
+```
+bundle_gui_submit (CLI)
+ -> _apply_cli_options_to_config(**args) -> ConfigParser (always a fresh copy)
+ -> show_job_bundle_submitter(session_config=...)
+ -> SubmitJobToDeadlineDialog(session_config=...) [stores as self._session_config]
+ -> SharedJobSettingsWidget(config=...) [stores as self._config]
+ -> DeadlineCloudSettingsWidget(config=...) [stores as self._config]
+ -> DeadlineFarmDisplay(config=...) [stores as self._config]
+ -> DeadlineQueueDisplay(config=...) [stores as self._config]
+ -> All get_setting() calls pass config=self._config
+ -> _set_submit_button_state() uses config=self._session_config
+ -> on_submit() uses self._session_config or config_file.read_config()
+```
+
+The `_DeadlineNamedResourceDisplay` base class and all its subclasses (`DeadlineFarmDisplay`, `DeadlineQueueDisplay`, `DeadlineStorageProfileNameDisplay`) needed to be updated to accept and use the config. These widgets call `get_setting()` both during `__init__` (to get the initial ID) and in `refresh()` / `get_item()` (to fetch current IDs for API calls). All 14 `get_setting()` call sites in `shared_job_settings_tab.py` were updated to pass `config=self._config`.
+
+### 4. Settings Dialog: Config File Menu
+
+The settings dialog has a consistent button bar in both `deadline config gui` and `bundle gui-submit`: Ok, Cancel, and "Config File" (dropdown menu).
+
+- **`deadline config gui`** (no session_config): "Save to Disk" and "Load from Disk" in the Config File menu are enabled when there are pending changes. "Load from Disk" discards pending changes and refreshes from disk. Ok = Save to Disk + close.
+- **`bundle gui-submit`** (with session_config): The "Config File" menu contains "Save to Disk" (writes session config to `~/.deadline/config`) and "Load from Disk" (reloads the session config from the on-disk config, discarding in-memory overrides). Ok = Apply pending changes to session + close.
+
+The submission dialog always provides a session config when opening the settings dialog, even if no CLI overrides were passed. If `_session_config` is `None`, `_ensure_session_config()` creates one from the current on-disk config.
+
+Implementation:
+
+- The button bar always contains Ok, Cancel, and "Config File" (dropdown menu with "Save to Disk" and "Load from Disk").
+- "Save to Disk" starts disabled and is enabled when the effective config differs from disk (session mode) or there are pending changes (workstation mode).
+- The "Config File" button itself is disabled when all its menu actions are disabled.
+- `refresh()` in `DeadlineWorkstationConfigWidget` prunes pending changes that match the base config, so changing a value back to its original disables the buttons.
+- `DeadlineConfigDialog.configure_settings()` accepts an optional `session_config: Optional[ConfigParser]` parameter.
+- `DeadlineWorkstationConfigWidget` determines its mode from whether `session_config` is provided. `refresh()` uses the session config as the base when one is present.
+- In **session mode** (when `session_config` is provided), `apply()` mutates the provided `ConfigParser` in-memory. It does **not** call `write_config()`.
+- In **workstation mode** (no `session_config`, e.g. `deadline config gui`), `apply()` behaves as before — reads from disk, applies changes, writes to disk.
+- `configure_settings()` returns a `ConfigureSettingsResult(changes_applied, session_config)` so the caller can update its state.
+
+### 5. Return Flow
+
+`_apply_settings_result` in `SubmitJobToDeadlineDialog` handles the result from the settings dialog:
+
+```python
+def _apply_settings_result(self, result):
+ """Apply the result from DeadlineConfigDialog.configure_settings()."""
+ if result.session_config is not None:
+ self._session_config = result.session_config
+ self.deadline_authentication_status.set_config(self._session_config)
+ self.shared_job_settings.set_session_config(self._session_config)
+ self.refresh_deadline_settings()
+```
+
+The submitter always refreshes when a session config is returned, regardless of whether `changes_applied` is True. This ensures the submitter picks up any changes made via Ok in the settings dialog.
+
+Both `on_settings_button_clicked` and `on_switch_profile_clicked` call `_ensure_session_config()` to guarantee a session config exists (creating one from disk if needed), pass it to `configure_settings()`, and delegate to `_apply_settings_result()`.
+
+### 6. Job ID Persistence with `persist_job_id`
+
+After a successful submission, the job ID should be written to the on-disk config so that subsequent CLI commands like `deadline job get` can find it. The config's hierarchical section structure (`profile → farm → queue → job_id`) means the job ID must be written to the section matching the farm/queue the job was submitted to — not necessarily the on-disk defaults.
+
+Previously, `create_job_from_job_bundle` in the API layer auto-persisted the job ID when `config=None`, and `bundle_submit` skipped persistence entirely when CLI overrides were present. The GUI path wrote the job ID using the on-disk defaults, which was incorrect when `--farm-id`/`--queue-id` overrides were used.
+
+The new approach:
+- **The API layer (`create_job_from_job_bundle`) still auto-persists the job ID when `config=None`**, preserving backward compatibility for public API callers (e.g. Unreal) that use on-disk defaults. When a `config` is provided, the API layer does not persist — the caller owns that responsibility.
+- **A new `config_file.persist_job_id(job_id, profile, farm_id, queue_id)` helper** handles persistence correctly. The caller extracts the profile, farm ID, and queue ID from its config (e.g. a session config with CLI overrides) and passes them explicitly. The helper builds the correct hierarchical section name and writes the job ID to the on-disk config. The on-disk farm/queue defaults are never changed.
+- **Both CLI and GUI callers** call `persist_job_id(job_id, profile=..., farm_id=..., queue_id=...)` after a successful submission, extracting the values from their session config so the section resolves correctly regardless of whether overrides were used.
+
+## Implementation Progress
+
+### Done
+
+- [x] `bundle_group.py`: Added `--profile`, `--farm-id`, `--queue-id`, `--storage-profile-id` click options to `bundle_gui_submit`. Added `_apply_cli_options_to_config(**args)` call. Passing `session_config=config` to `show_job_bundle_submitter()`.
+- [x] `_common.py`: `_apply_cli_options_to_config` always copies the input config and always returns a `ConfigParser` (never `None`, never mutates the caller's object or the `read_config()` cache).
+- [x] `config_file.py`: Added `persist_job_id(job_id, profile, farm_id, queue_id)` helper that writes the job ID to the correct hierarchical section on disk without changing on-disk farm/queue defaults.
+- [x] `bundle_group.py` `bundle_submit`: Uses `persist_job_id(job_id, profile=..., farm_id=..., queue_id=...)` to always persist the job ID to the correct section, even when CLI overrides are present.
+- [x] `bundle_group.py` `bundle_gui_submit`: Simplified — no longer needs manual pre-copy workaround since `_apply_cli_options_to_config` always copies.
+- [x] `_submit_job_bundle.py`: Scoped `set_setting("defaults.job_id", job_id)` side effect to `config is None` only (preserving backward compat for public API callers). When a `config` is provided, callers own their persistence policy.
+- [x] `job_bundle_submitter.py`: `show_job_bundle_submitter()` accepts `session_config` param and passes it to `SubmitJobToDeadlineDialog`.
+- [x] `submit_job_to_deadline_dialog.py`: `SubmitJobToDeadlineDialog.__init__` accepts `session_config: Optional[ConfigParser]`, stores as `self._session_config`. Passes to `SharedJobSettingsWidget`. `_set_submit_button_state` and `on_submit` use session config. Added `_ensure_session_config()` to lazily create a session config from disk when none was provided via CLI args. Added `_apply_settings_result()` helper to propagate updated session config to singletons and child widgets after settings dialog closes. `_submission_succeeded_signal_receiver` uses `persist_job_id(job_id, profile=..., farm_id=..., queue_id=...)` to write the job ID to the correct farm/queue section on disk.
+- [x] `shared_job_settings_tab.py`: `SharedJobSettingsWidget` accepts `config` param. All 14 `get_setting()` calls updated to pass `config=self._config`. `DeadlineCloudSettingsWidget`, `_DeadlineNamedResourceDisplay`, `DeadlineFarmDisplay`, `DeadlineQueueDisplay`, `DeadlineStorageProfileNameDisplay` all accept and thread config. Added `set_session_config()` method for updating the config after settings dialog closes.
+- [x] `deadline_config_dialog.py`: Accept `session_config` in `configure_settings()`. "Config File" dropdown menu offers "Save to Disk" and "Load from Disk". Ok applies to session (session mode) or saves to disk (workstation mode). Returns `ConfigureSettingsResult` dataclass instead of `bool`.
+- [x] Propagate session config to `DeadlineUIController` and `DeadlineAuthenticationStatus` singletons via their existing `set_config()` methods.
+- [x] Tests for the new behavior (`test/unit/deadline_client/ui/test_session_config.py`).
+
+## Affected Components Summary
+
+| Component | Change | Status |
+|---|---|---|
+| `_common.py` `_apply_cli_options_to_config` | Always copy input config. Always return `ConfigParser` (never `None`). Never mutate caller's object or `read_config()` cache. | Done |
+| `config_file.py` `persist_job_id` | New helper. Accepts explicit profile, farm_id, queue_id to build the correct hierarchical section. Writes job ID to on-disk config without changing farm/queue defaults. | Done |
+| `config/__init__.py` | Export `persist_job_id`. | Done |
+| `bundle_group.py` `bundle_submit` | Use `persist_job_id(job_id, profile=..., farm_id=..., queue_id=...)` instead of conditional `set_setting`. Job ID now always persisted to correct section even with CLI overrides. | Done |
+| `bundle_group.py` `bundle_gui_submit` | Add `--profile`, `--farm-id`, `--queue-id`, `--storage-profile-id` options. Call `_apply_cli_options_to_config()` directly (no manual pre-copy needed). Pass `session_config` to `show_job_bundle_submitter()`. | Done |
+| `_submit_job_bundle.py` `create_job_from_job_bundle` | Scoped `set_setting("defaults.job_id", job_id)` to `config is None` only. When a `config` is provided, callers own persistence via `persist_job_id`. | Done |
+| `job_bundle_submitter.py` `show_job_bundle_submitter()` | Accept optional `session_config` param, pass to `SubmitJobToDeadlineDialog`. | Done |
+| `submit_job_to_deadline_dialog.py` `SubmitJobToDeadlineDialog` | Accept optional `session_config` param. Store as `_session_config`. Propagate to child widgets. Use in `_set_submit_button_state` and `on_submit`. Use `persist_job_id` in `_submission_succeeded_signal_receiver`. | Done |
+| `shared_job_settings_tab.py` `SharedJobSettingsWidget` | Accept optional `config` param. Use `get_setting(..., config=self._config)` instead of bare `get_setting(...)`. | Done |
+| `shared_job_settings_tab.py` display widgets | `DeadlineCloudSettingsWidget`, `_DeadlineNamedResourceDisplay`, `DeadlineFarmDisplay`, `DeadlineQueueDisplay`, `DeadlineStorageProfileNameDisplay` all accept and use config. | Done |
+| `deadline_config_dialog.py` `DeadlineConfigDialog` | Accept optional `session_config`. Button bar: Ok, Cancel, "Config File" dropdown ("Save to Disk" / "Load from Disk"). Ok applies to session or saves to disk depending on mode. Return `ConfigureSettingsResult`. | Done |
+| `submit_job_to_deadline_dialog.py` settings button | Update `on_settings_button_clicked` to pass/receive session config via `_ensure_session_config()`. Propagate to singletons and child widgets via `_apply_settings_result()`. | Done |
+| `_deadline_controller.py` `DeadlineUIController` | Already supports `set_config()` — called with session config at init and after settings dialog. | Done |
+| `deadline_authentication_status.py` | Already supports `set_config()` — called with session config at init and after settings dialog. | Done |
+
+## Breaking Changes
+
+### For library consumers (`deadline.client.api`)
+
+**`create_job_from_job_bundle` auto-persist behavior is now scoped to `config=None` only.**
+
+When calling `create_job_from_job_bundle()` without passing `config=`, the function still auto-persists the job ID to `~/.deadline/config` as before — no change needed. This preserves backward compatibility for callers like Unreal that use the public API with on-disk defaults.
+
+When `config=` is provided (a `ConfigParser` object), the function no longer auto-persists the job ID. This was already the behavior before — the `config is None` check meant it only ever auto-persisted in the no-config case. Callers that pass a config and want persistence should use `persist_job_id`:
+```python
+from deadline.client.config import persist_job_id, get_setting
+job_id = create_job_from_job_bundle(..., config=my_config)
+if job_id:
+ persist_job_id(
+ job_id,
+ profile=get_setting("defaults.aws_profile_name", config=my_config),
+ farm_id=get_setting("defaults.farm_id", config=my_config),
+ queue_id=get_setting("defaults.queue_id", config=my_config),
+ )
+```
+
+### For UI plugin authors (`deadline.client.ui`)
+
+**`DeadlineConfigDialog.configure_settings()` return type changed from `bool` to `ConfigureSettingsResult`.**
+
+- **Who is affected**: Code that captures and inspects the return value of `configure_settings()`, including truthiness checks like `if configure_settings():`.
+- **Migration**: The `ConfigureSettingsResult` dataclass instance is always truthy (even when `changes_applied=False`), so `if configure_settings():` will no longer work as before. Replace with `if configure_settings().changes_applied:`. Code that discarded the return value is unaffected.
+
+### Behavioral changes (non-breaking but notable)
+
+**Job ID is now persisted even when CLI overrides are used.**
+
+Previously, `deadline bundle submit --farm-id X --queue-id Y` would submit the job but *not* save the job ID to disk. Now it always persists the job ID to the correct hierarchical section (`profile/farm-X/queue-Y`). This means `deadline job get` will find the job ID when the same farm/queue is configured, even if they were originally specified via CLI flags.
+
+**`deadline bundle gui-submit` always passes a session config to the submit dialog.**
+
+Previously, when no `--farm-id`/`--queue-id`/`--profile`/`--storage-profile-id` flags were provided, `bundle_gui_submit` passed `session_config=None` to the dialog. Now it always passes a `ConfigParser` (a copy of the disk config). The dialog's `Optional[ConfigParser]` parameter still accepts `None` for third-party submitters that don't use the CLI entry point.
+
+## Key Design Decisions
+
+1. **Reuse `_apply_cli_options_to_config`**: Rather than inventing a new mechanism, we reuse the existing pattern from `bundle submit` to create the in-memory config overlay. The function was hardened to always copy and always return a `ConfigParser`, eliminating a class of cache-mutation bugs.
+2. **ConfigParser as the session state carrier**: The `ConfigParser` object is already the abstraction used throughout the codebase. Passing it explicitly avoids introducing a new config abstraction.
+3. **No global/singleton session config**: The session config is scoped to the dialog instance and passed explicitly. This avoids side effects on other parts of the system and keeps the on-disk config as the single source of truth for non-GUI paths.
+4. **Consistent settings dialog button bar**: The button bar is identical across both entry points: Ok, Cancel, and "Config File" (dropdown menu with "Save to Disk" and "Load from Disk"). Without a session config (`deadline config gui`), "Save to Disk" and "Load from Disk" enable when there are pending changes. With a session config (`bundle gui-submit`), "Save to Disk" and "Load from Disk" enable when the effective config differs from disk. Ok saves to disk in workstation mode and applies to the in-memory session in session mode.
+5. **Session config does not persist across submissions**: If the user closes the GUI and re-runs `bundle gui-submit` without CLI args, the session config is gone. This is intentional — CLI args are ephemeral by nature.
+6. **Workstation changes in settings dialog write to disk on Ok**: This preserves the existing behavior and user expectations for workstation config.
+7. **Deep config threading required**: The config must be passed through the entire widget hierarchy, not just to `SharedJobSettingsWidget`. The display widgets (`DeadlineFarmDisplay`, `DeadlineQueueDisplay`, `DeadlineStorageProfileNameDisplay`) also call `get_setting()` and need the config for correct behavior. All 14 `get_setting()` call sites in `shared_job_settings_tab.py` were updated.
+8. **`_apply_cli_options_to_config` always returns a `ConfigParser`**: The function never returns `None`. When no CLI overrides are provided, it returns an unmodified copy of the disk config. This eliminates `config or read_config()` fallback patterns in callers and ensures the session config is always available for threading through the widget hierarchy.
+9. **`configure_settings()` returns a dataclass instead of `bool`**: The return type changed from `bool` to `ConfigureSettingsResult(changes_applied, session_config)`. This is a breaking change for callers that used the return value in a truthiness check (e.g. `if configure_settings():`), since a dataclass instance is always truthy. Existing callers in this repo (`dev_application.py`, `config_group.py`) discard the return value and are unaffected.
+10. **Session config must be re-propagated after settings dialog closes**: When the settings dialog returns an updated session config, it must be pushed to `DeadlineAuthenticationStatus`, `DeadlineUIController`, and `SharedJobSettingsWidget._config` — not just stored on the submit dialog. The `_apply_settings_result()` helper method handles this and always refreshes the submitter when a session config is returned.
+11. **Config sections are hierarchical (farm-scoped queues)**: The `ConfigParser` sections are structured like `profile-(default) farm-XXXX defaults`, meaning `queue_id` is scoped under the farm section. Overriding `--farm-id` alone causes `queue_id` to resolve to empty (the new farm section doesn't exist yet). This is correct behavior — a queue belongs to a specific farm — but means `--farm-id` and `--queue-id` should typically be passed together.
+12. **Lazy session config creation from the submitter**: The submit dialog's `_ensure_session_config()` creates a session config from the on-disk config on first use if none was provided via CLI args. This guarantees the settings dialog always operates in session mode when opened from the submitter, while `deadline config gui` (which never passes a session config) remains workstation-only.
+13. **Load from Disk is a menu action with context-dependent enablement**: In session mode, enabled when the effective config (session + pending changes) differs from disk. In workstation mode, enabled when there are pending changes (acts as a discard-changes operation). "Save to Disk" follows the same logic per mode. The "Config File" button itself is disabled when all its menu actions are disabled.
+14. **Change pruning keeps button state accurate**: `refresh()` removes pending changes whose values match the base config. This means changing a value and changing it back correctly disables Save to Disk, rather than leaving stale entries in the changes dict.
+15. **Job ID persistence moved out of the API layer for config-aware callers**: `create_job_from_job_bundle` still auto-persists the job ID when `config=None` (preserving backward compatibility for public API callers like Unreal). When a `config` is provided, the caller owns persistence via `persist_job_id`. This keeps the simple public API path working while giving CLI and GUI callers control over which section the job ID is written to.
+16. **`persist_job_id` takes explicit profile, farm_id, and queue_id**: Rather than accepting a `ConfigParser` and internally resolving the section via the dependency chain, `persist_job_id(job_id, profile, farm_id, queue_id)` makes its dependencies explicit. The caller extracts the three values from its config (via `get_setting`) and passes them directly. This makes the function easier to test, removes coupling to `_get_section_prefixes` internals, and makes the section construction transparent. For example, `deadline bundle submit --farm-id X --queue-id Y` extracts the profile, farm, and queue from the session config and passes them to `persist_job_id`, which writes the job ID under the `profile-{profile} X Y defaults` section.
+17. **`_apply_cli_options_to_config` never mutates its inputs**: The function always creates a fresh `ConfigParser` copy before applying overrides. This eliminates the footgun where the `read_config()` cache could be permanently mutated by CLI overrides, which previously required callers (like `bundle_gui_submit`) to defensively pre-copy the config.
diff --git a/src/deadline/client/api/_submit_job_bundle.py b/src/deadline/client/api/_submit_job_bundle.py
index 9e89fe14e..9d1826571 100644
--- a/src/deadline/client/api/_submit_job_bundle.py
+++ b/src/deadline/client/api/_submit_job_bundle.py
@@ -844,8 +844,10 @@ def create_job_from_job_bundle(
job_id = create_job_response["jobId"]
print_function_callback("Waiting for Job to be created...")
- # If using the default config, set the default job id so it holds the
- # most-recently submitted job.
+ # When no config was provided, the caller is using the public API with
+ # on-disk defaults — auto-persist the job ID so `deadline job get` works.
+ # When a config IS provided, the caller (CLI or GUI) owns persistence
+ # and can resolve the correct hierarchical section via persist_job_id.
if config is None:
set_setting("defaults.job_id", job_id)
diff --git a/src/deadline/client/cli/_common.py b/src/deadline/client/cli/_common.py
index b69091141..54a97e9e9 100644
--- a/src/deadline/client/cli/_common.py
+++ b/src/deadline/client/cli/_common.py
@@ -89,20 +89,21 @@ def wraps(ctx: click.Context, *args, **kwargs):
def _apply_cli_options_to_config(
*, config: Optional[ConfigParser] = None, required_options: Set[str] = set(), **args
-) -> Optional[ConfigParser]:
+) -> ConfigParser:
"""
- Modifies an AWS Deadline Cloud config object to apply standard option names to it, such as
- the AWS profile, AWS Deadline Cloud Farm, or AWS Deadline Cloud Queue to use.
+ Returns an in-memory AWS Deadline Cloud config with standard CLI option overrides applied.
+ Always returns a fresh copy — never mutates the caller's config or the read_config() cache.
Args:
- config (ConfigParser, optional): an AWS Deadline Cloud config, read by config_file.read_config().
- If not provided, loads the config from disk.
+ config (ConfigParser, optional): a base config to copy from. If not provided, loads from disk.
"""
- # Only work with a custom config if there are standard options provided
- if any(value is not None for value in args.values()):
- if config is None:
- config = config_file.read_config()
+ # Always start from a copy so we never mutate the caller's object or the cache
+ base = config if config is not None else config_file.read_config()
+ config = ConfigParser()
+ config.read_dict(base)
+ # Apply any provided standard options
+ if any(value is not None for value in args.values()):
aws_profile_name = args.pop("profile", None)
if aws_profile_name:
config_file.set_setting("defaults.aws_profile_name", aws_profile_name, config=config)
diff --git a/src/deadline/client/cli/_groups/bundle_group.py b/src/deadline/client/cli/_groups/bundle_group.py
index 21080a0b4..6c259a277 100644
--- a/src/deadline/client/cli/_groups/bundle_group.py
+++ b/src/deadline/client/cli/_groups/bundle_group.py
@@ -20,7 +20,7 @@
from botocore.exceptions import ClientError
from ... import api
-from ...config import config_file
+from ...config import config_file, get_setting, persist_job_id
from ...dataclasses import SubmitterInfo
from ....job_attachments.exceptions import (
AssetSyncError,
@@ -310,17 +310,16 @@ def _check_create_job_wait_canceled() -> bool:
click.echo("Saved job debug snapshot:")
click.echo(f" {save_debug_snapshot}")
- # Check Whether the CLI options are modifying any of the default settings that affect
- # the job id. If not, we'll save the job id submitted as the default job id.
+ # Persist the job ID to the on-disk config under the correct
+ # farm/queue section so `deadline job get` picks it up.
# If a job snapshot directory was provided, the job_id will be None.
- if (
- args.get("profile") is None
- and args.get("farm_id") is None
- and args.get("queue_id") is None
- and args.get("storage_profile_id") is None
- and job_id
- ):
- config_file.set_setting("defaults.job_id", job_id)
+ if job_id:
+ persist_job_id(
+ job_id,
+ profile=get_setting("defaults.aws_profile_name", config=config),
+ farm_id=get_setting("defaults.farm_id", config=config),
+ queue_id=get_setting("defaults.queue_id", config=config),
+ )
except AssetSyncCancelledError as exc:
if sigint_handler.continue_operation:
@@ -418,6 +417,10 @@ def _check_create_job_wait_canceled() -> bool:
'OR --submitter-info \'{"submitter_name": "MyApp", "additional_info": {"render_engine": "Cycles"}}\' '
"OR --submitter-info file://path/to/submitter.json",
)
+@click.option("--profile", help="The AWS profile to use.")
+@click.option("--farm-id", help="The farm to use.")
+@click.option("--queue-id", help="The queue to use.")
+@click.option("--storage-profile-id", help="The storage profile to use.")
@_handle_error
def bundle_gui_submit(
parameter,
@@ -439,6 +442,9 @@ def bundle_gui_submit(
Learn more about [job bundles](https://docs.aws.amazon.com/deadline-cloud/latest/developerguide/build-job-bundle.html)
"""
+ # Apply CLI options (--profile, --farm-id, --queue-id, --storage-profile-id) to an in-memory config.
+ # _apply_cli_options_to_config always returns a fresh copy, so the on-disk cache is never mutated.
+ config = _apply_cli_options_to_config(**args)
if submitter_name:
click.echo(
click.style(
@@ -473,6 +479,7 @@ def bundle_gui_submit(
submitter_info=submitter_info,
known_asset_paths=known_asset_path,
job_parameters=parameter,
+ session_config=config,
)
if not submitter:
diff --git a/src/deadline/client/config/__init__.py b/src/deadline/client/config/__init__.py
index d2bf89c45..84e20cc36 100644
--- a/src/deadline/client/config/__init__.py
+++ b/src/deadline/client/config/__init__.py
@@ -15,6 +15,7 @@
"get_setting",
"set_setting",
"clear_setting",
+ "persist_job_id",
"get_best_profile_for_farm",
"str2bool",
"DEFAULT_DEADLINE_ENDPOINT_URL",
@@ -27,5 +28,6 @@
get_setting_default,
set_setting,
clear_setting,
+ persist_job_id,
str2bool,
)
diff --git a/src/deadline/client/config/config_file.py b/src/deadline/client/config/config_file.py
index 66c1406bd..8fc439a2b 100644
--- a/src/deadline/client/config/config_file.py
+++ b/src/deadline/client/config/config_file.py
@@ -429,6 +429,30 @@ def set_setting(setting_name: str, value: str, config: Optional[ConfigParser] =
write_config(config)
+def persist_job_id(job_id: str, profile: str, farm_id: str, queue_id: str) -> None:
+ """
+ Persists a job ID to the on-disk config file under the correct
+ hierarchical section (profile / farm / queue).
+
+ The caller provides the explicit profile, farm, and queue that the job
+ was submitted to. The job ID is written into the matching section of
+ the on-disk config without changing the on-disk farm/queue defaults.
+
+ Args:
+ job_id: The job ID to persist.
+ profile: The AWS profile name used for submission.
+ farm_id: The farm ID the job was submitted to.
+ queue_id: The queue ID the job was submitted to.
+ """
+ section = f"profile-{profile} {farm_id} {queue_id} defaults"
+
+ disk_config = read_config()
+ if section not in disk_config:
+ disk_config[section] = {}
+ disk_config.set(section, "job_id", job_id)
+ write_config(disk_config)
+
+
def clear_setting(setting_name: str, config: Optional[ConfigParser] = None):
"""
Sets the value of the specified setting back to the default value.
diff --git a/src/deadline/client/ui/dialogs/__init__.py b/src/deadline/client/ui/dialogs/__init__.py
index 60545700e..7ddf63a9e 100644
--- a/src/deadline/client/ui/dialogs/__init__.py
+++ b/src/deadline/client/ui/dialogs/__init__.py
@@ -1,6 +1,7 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
__all__ = [
+ "ConfigureSettingsResult",
"DeadlineConfigDialog",
"DeadlineLoginDialog",
"SubmitJobProgressDialog",
@@ -10,7 +11,7 @@
]
from ._types import JobBundlePurpose
-from .deadline_config_dialog import DeadlineConfigDialog
+from .deadline_config_dialog import ConfigureSettingsResult, DeadlineConfigDialog
from .deadline_login_dialog import DeadlineLoginDialog
from .submit_job_progress_dialog import SubmitJobProgressDialog
from .submit_job_to_deadline_dialog import SubmitJobToDeadlineDialog
diff --git a/src/deadline/client/ui/dialogs/deadline_config_dialog.py b/src/deadline/client/ui/dialogs/deadline_config_dialog.py
index 52bb3aa43..74437111d 100644
--- a/src/deadline/client/ui/dialogs/deadline_config_dialog.py
+++ b/src/deadline/client/ui/dialogs/deadline_config_dialog.py
@@ -11,6 +11,7 @@
__all__ = ["DeadlineConfigDialog"]
from configparser import ConfigParser
+from dataclasses import dataclass
from logging import getLogger, root
from typing import Callable, Dict, List, Optional
@@ -30,6 +31,7 @@
QHBoxLayout,
QLabel,
QListWidget,
+ QMenu,
QMessageBox,
QPushButton,
QSizePolicy,
@@ -60,41 +62,66 @@
NOT_VALID_MARKER = "[NOT VALID]"
+@dataclass
+class ConfigureSettingsResult:
+ """Result from DeadlineConfigDialog.configure_settings()."""
+
+ changes_applied: bool
+ session_config: Optional[ConfigParser] = None
+
+
class DeadlineConfigDialog(QDialog):
"""
A modal dialog box for modifying the AWS Deadline Cloud local workstation
configuration.
+ When a session_config is provided, "Ok" applies changes to the in-memory
+ session and closes. "Save to Disk" writes to the on-disk config, and
+ "Load from Disk" reverts the session to match the on-disk config.
+ Without a session_config, "Ok" saves to disk and closes.
+
Example code:
DeadlineConfigDialog.configure_settings(parent=self)
"""
@staticmethod
def configure_settings(
- parent: Optional[QWidget] = None, set_profile_focus: bool = False
- ) -> bool:
+ parent: Optional[QWidget] = None,
+ set_profile_focus: bool = False,
+ session_config: Optional[ConfigParser] = None,
+ ) -> ConfigureSettingsResult:
"""
Static method that runs the Deadline Config Dialog.
Args:
parent: Parent widget
set_profile_focus: Optional boolean to set the initial focus to the profile selector
+ session_config: Optional in-memory config for session mode
- Returns True if any changes were applied, False otherwise.
+ Returns a ConfigureSettingsResult with changes_applied and the
+ (possibly modified) session_config.
"""
- deadline_config = DeadlineConfigDialog(parent=parent)
+ deadline_config = DeadlineConfigDialog(parent=parent, session_config=session_config)
if set_profile_focus:
deadline_config.config_box.aws_profiles_box.setFocus()
deadline_config.exec_()
- return deadline_config.changes_were_applied
+ return ConfigureSettingsResult(
+ changes_applied=deadline_config.changes_were_applied,
+ session_config=deadline_config._session_config,
+ )
- def __init__(self, parent: Optional[QWidget] = None) -> None:
+ def __init__(
+ self,
+ parent: Optional[QWidget] = None,
+ session_config: Optional[ConfigParser] = None,
+ ) -> None:
super().__init__(
parent=parent, f=Qt.WindowSystemMenuHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint
)
+ self._session_config = session_config
self.setWindowTitle(tr("AWS Deadline Cloud workstation configuration"))
self.deadline_authentication_status = DeadlineAuthenticationStatus.getInstance()
self._build_ui()
@@ -116,7 +143,10 @@ def sizeHint(self):
def _build_ui(self):
self.layout = QVBoxLayout(self)
- self.config_box = DeadlineWorkstationConfigWidget(parent=self)
+ self.config_box = DeadlineWorkstationConfigWidget(
+ parent=self,
+ session_config=self._session_config,
+ )
self.scrollArea = DeadlineScrollArea(self)
self.scrollArea.setWidget(self.config_box)
@@ -139,16 +169,38 @@ def _build_ui(self):
self.on_auth_status_update
)
- # We only use a Close button, not OK/Cancel, because we live update the settings.
+ # Build the button bar: Ok, Cancel, and "Config File" dropdown.
self.button_box = QDialogButtonBox(
- QDialogButtonBox.Ok | QDialogButtonBox.Cancel | QDialogButtonBox.Apply, Qt.Horizontal
+ QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal
)
self.button_box.button(QDialogButtonBox.Ok).setText(tr("Ok"))
self.button_box.button(QDialogButtonBox.Cancel).setText(tr("Cancel"))
- self.button_box.button(QDialogButtonBox.Apply).setText(tr("Apply"))
self.button_box.accepted.connect(self.accept)
self.button_box.rejected.connect(self.reject)
- self.button_box.clicked.connect(self.on_button_box_clicked)
+
+ # Config File dropdown menu with Save to Disk / Load from Disk
+ self._config_file_button = QPushButton(tr("Config File"))
+ config_file_menu = QMenu(self._config_file_button)
+ self._save_to_disk_action = config_file_menu.addAction(
+ tr("Save to Disk"), self._on_save_to_disk
+ )
+ self._save_to_disk_action.setEnabled(False)
+ self._load_from_disk_action = config_file_menu.addAction(
+ tr("Load from Disk"), self._on_load_from_disk
+ )
+ self._load_from_disk_action.setEnabled(False)
+ self._config_file_button.setMenu(config_file_menu)
+ self.button_box.addButton(self._config_file_button, QDialogButtonBox.ActionRole)
+
+ # Set initial enabled state: in session mode, enable if session differs from disk
+ if self._session_config is not None:
+ differs = not self._effective_config_matches_disk()
+ self._config_file_button.setEnabled(differs)
+ self._save_to_disk_action.setEnabled(differs)
+ self._load_from_disk_action.setEnabled(differs)
+ else:
+ self._config_file_button.setEnabled(False)
+
self.auth_status_box.logout_clicked.connect(self.on_logout)
self.auth_status_box.login_clicked.connect(self.on_login)
self.layout.addWidget(self.button_box)
@@ -160,9 +212,55 @@ def _build_ui(self):
def changes_were_applied(self) -> bool:
return self.config_box.changes_were_applied
+ def _effective_config_matches_disk(self) -> bool:
+ """Return True if the effective config (session + pending changes) matches disk."""
+ disk = config_file.read_config()
+ effective = self.config_box.config if self.config_box.config else self._session_config
+ if effective is None:
+ return True
+ return {s: dict(disk[s]) for s in disk} == {s: dict(effective[s]) for s in effective}
+
+ def _on_load_from_disk(self) -> None:
+ """Reload config from disk, discarding pending changes (and session overrides if any)."""
+ if self._session_config is not None:
+ self._session_config = ConfigParser()
+ self._session_config.read_dict(config_file.read_config())
+ self.config_box.set_session_config(self._session_config)
+ else:
+ self.config_box.changes.clear()
+ self.config_box.refresh()
+
+ def _on_save_to_disk(self) -> bool:
+ """Save the current changes to the on-disk config. Returns True on success."""
+ if not self.config_box._validate_changes("Save to Disk"):
+ return False
+ if self._session_config is not None:
+ # Session path: apply pending changes to session config, then write it all to disk
+ for setting_name, value in self.config_box.changes.items():
+ config_file.set_setting(setting_name, value, self._session_config)
+ config_file.write_config(self._session_config)
+ else:
+ # Workstation path: read from disk, apply changes, write back
+ config = config_file.read_config()
+ for setting_name, value in self.config_box.changes.items():
+ config_file.set_setting(setting_name, value, config)
+ config_file.write_config(config)
+ self.config_box.changes.clear()
+ self.config_box.changes_were_applied = True
+ self.config_box.refresh()
+ return True
+
def accept(self):
- if self.config_box.apply():
- super().accept()
+ if self._session_config is not None:
+ # Session mode: Ok = Apply to session + close
+ if not self.config_box.apply():
+ return
+ self._session_config = self.config_box.config
+ else:
+ # Workstation mode: Ok = Save to Disk + close
+ if not self._on_save_to_disk():
+ return
+ super().accept()
def reject(self):
self.deadline_authentication_status.set_config(config_file.read_config())
@@ -178,13 +276,17 @@ def on_logout(self):
self.deadline_authentication_status.refresh_status()
self.config_box.refresh()
- def on_button_box_clicked(self, button):
- if self.button_box.standardButton(button) == QDialogButtonBox.Apply:
- self.config_box.apply()
-
def on_refresh(self):
- # Enable the "Apply" button only if there are changes
- self.button_box.button(QDialogButtonBox.Apply).setEnabled(bool(self.config_box.changes))
+ has_changes = bool(self.config_box.changes)
+ differs_from_disk = not self._effective_config_matches_disk()
+ if self._session_config is not None:
+ self._save_to_disk_action.setEnabled(differs_from_disk)
+ self._load_from_disk_action.setEnabled(differs_from_disk)
+ self._config_file_button.setEnabled(differs_from_disk)
+ else:
+ self._save_to_disk_action.setEnabled(has_changes)
+ self._load_from_disk_action.setEnabled(has_changes)
+ self._config_file_button.setEnabled(has_changes)
# Update the auth status with the refreshed config
self.deadline_authentication_status.set_config(self.config_box.config)
@@ -223,12 +325,15 @@ class DeadlineWorkstationConfigWidget(QWidget):
# provides (operation_name, BaseException)
_background_exception = Signal(str, BaseException)
- def __init__(self, parent: Optional[QWidget] = None):
+ def __init__(
+ self, parent: Optional[QWidget] = None, session_config: Optional[ConfigParser] = None
+ ):
super().__init__(parent)
self.changes: dict = {}
self.config: Optional[ConfigParser] = None
self.changes_were_applied = False
+ self._session_config = session_config
# Flags to track when we're waiting for cascading list refreshes
# These prevent the list_updated handlers from interfering with manual user selections
@@ -242,6 +347,13 @@ def __init__(self, parent: Optional[QWidget] = None):
def minimumSizeHint(self):
return QSize(500, 700)
+ def set_session_config(self, session_config: ConfigParser) -> None:
+ """Replace the session config and refresh the UI."""
+ self._session_config = session_config
+ self.changes.clear()
+ self.refresh()
+ self.refresh_lists()
+
def _build_ui(self):
# Ensure the widget expands horizontally
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
@@ -755,9 +867,21 @@ def refresh(self):
"""
Refreshes all the configuration UI elements from the current config.
"""
- # Make self.config be a deep copy of the config, with changes applied
+ # Make self.config be a deep copy of the config, with changes applied.
+ # In session mode, start from the session config; otherwise from disk.
self.config = ConfigParser()
- self.config.read_dict(config_file.read_config())
+ base_config = (
+ self._session_config if self._session_config is not None else config_file.read_config()
+ )
+ self.config.read_dict(base_config)
+
+ # Remove changes that match the base config (e.g. user changed a value then changed it back)
+ for setting_name in list(self.changes):
+ if self.changes[setting_name] == config_file.get_setting(
+ setting_name, config=self.config
+ ):
+ del self.changes[setting_name]
+
for setting_name, value in self.changes.items():
config_file.set_setting(setting_name, value, self.config)
self.default_farm_box.set_config(self.config)
@@ -803,9 +927,24 @@ def refresh(self):
self.refreshed.emit()
+ def _validate_changes(self, action_name: str) -> bool:
+ """Check that no pending changes contain NOT_VALID_MARKER. Shows a warning and returns False if invalid."""
+ for setting_name, value in self.changes.items():
+ if value.startswith(NOT_VALID_MARKER):
+ QMessageBox.warning(
+ self,
+ action_name,
+ f"Cannot apply changes, {value} is not valid for setting {setting_name}",
+ )
+ return False
+ return True
+
def apply(self) -> bool:
"""
- Apply all the settings that the user has changed into the config file.
+ Apply all the settings that the user has changed.
+
+ In workstation mode, writes changes to the on-disk config file.
+ In session mode, mutates the in-memory session config without writing to disk.
Returns True if the settings were applied, False otherwise.
"""
@@ -815,19 +954,22 @@ def apply(self) -> bool:
self.default_storage_profile_box.box.currentData()
)
- for setting_name, value in self.changes.items():
- if value.startswith(NOT_VALID_MARKER):
- QMessageBox.warning( # type: ignore[call-arg]
- self,
- "Apply changes",
- f"Cannot apply changes, {value} is not valid for setting {setting_name}",
- )
- return False
-
- self.config = config_file.read_config()
+ if not self._validate_changes("Apply changes"):
+ return False
+
+ if self._session_config is not None:
+ # Session mode: mutate the in-memory config without writing to disk
+ for setting_name, value in self.changes.items():
+ config_file.set_setting(setting_name, value, self._session_config)
+ self.config = ConfigParser()
+ self.config.read_dict(self._session_config)
+ else:
+ # Workstation mode: read from disk, apply changes, write back
+ self.config = config_file.read_config()
+ for setting_name, value in self.changes.items():
+ config_file.set_setting(setting_name, value, self.config)
+ config_file.write_config(self.config)
- for setting_name, value in self.changes.items():
- config_file.set_setting(setting_name, value, self.config)
root.setLevel(config_file.get_setting("settings.log_level"))
api.get_deadline_cloud_library_telemetry_client().set_opt_out(config=self.config)
@@ -838,8 +980,6 @@ def apply(self) -> bool:
self.changes.clear()
- config_file.write_config(self.config)
-
# Refresh the GUI (writing the config file should cause this, but do this redundantly to make sure)
self.refresh()
diff --git a/src/deadline/client/ui/dialogs/submit_job_to_deadline_dialog.py b/src/deadline/client/ui/dialogs/submit_job_to_deadline_dialog.py
index 2bd2bba9a..7f15143e6 100644
--- a/src/deadline/client/ui/dialogs/submit_job_to_deadline_dialog.py
+++ b/src/deadline/client/ui/dialogs/submit_job_to_deadline_dialog.py
@@ -9,6 +9,7 @@
import os
import sys
import json
+from configparser import ConfigParser
from typing import Any, Dict, Optional, Protocol
import yaml
@@ -35,7 +36,7 @@
from ...api._session import session_context as _session_context
from ..deadline_authentication_status import DeadlineAuthenticationStatus
from .._utils import block_signals, tr
-from ...config import get_setting, set_setting, config_file
+from ...config import get_setting, config_file, persist_job_id
from ...exceptions import UserInitiatedCancel, NonValidInputError
from ...job_bundle import create_job_history_bundle_dir
from ...job_bundle.parameters import JobParameter
@@ -44,7 +45,7 @@
from ..widgets.job_attachments_tab import JobAttachmentsWidget
from ..widgets.shared_job_settings_tab import SharedJobSettingsWidget
from ..widgets.host_requirements_tab import HostRequirementsWidget
-from . import DeadlineConfigDialog, DeadlineLoginDialog
+from . import ConfigureSettingsResult, DeadlineConfigDialog, DeadlineLoginDialog
from ._types import JobBundlePurpose
from ._help_dialog import _HelpDialog
@@ -114,6 +115,7 @@ def __init__(
attachments: AssetReferences,
on_create_job_bundle_callback: OnCreateJobBundleCallback,
parent: Optional[QWidget] = None,
+ session_config: Optional[ConfigParser] = None,
f: Any = Qt.WindowFlags(),
show_host_requirements_tab: bool = False,
host_requirements: Optional[HostRequirements] = None,
@@ -123,6 +125,7 @@ def __init__(
# The Qt.Tool flag makes sure our widget stays in front of the main application window
super().__init__(parent=parent, f=f)
+ self._session_config = session_config
# Set window title with submitter package info if available
window_title = tr("Submit to AWS Deadline Cloud")
if submitter_info:
@@ -147,6 +150,8 @@ def __init__(
self.job_id = None
self.job_history_bundle_dir: Optional[str] = None
self.deadline_authentication_status = DeadlineAuthenticationStatus.getInstance()
+ if self._session_config is not None:
+ self.deadline_authentication_status.set_config(self._session_config)
self.show_host_requirements_tab = show_host_requirements_tab
self.known_asset_paths = known_asset_paths or []
self.should_close = False
@@ -166,7 +171,16 @@ def __init__(
def _submission_succeeded_signal_receiver(self, job_id: str):
self.job_id = job_id
- set_setting("defaults.job_id", job_id)
+ # Persist the job ID to the on-disk config under the correct
+ # farm/queue section so that CLI overrides (--farm-id / --queue-id)
+ # target the right place without changing the on-disk defaults.
+ config = self._session_config
+ persist_job_id(
+ job_id,
+ profile=get_setting("defaults.aws_profile_name", config=config),
+ farm_id=get_setting("defaults.farm_id", config=config),
+ queue_id=get_setting("defaults.queue_id", config=config),
+ )
def _close_event_receiver(self):
if self.submitter_info.submitter_name != "JobBundle" and self.job_id:
@@ -257,8 +271,8 @@ def _set_submit_button_state(self):
# Enable/disable the Submit button based on whether the
# AWS Deadline Cloud API is accessible and the farm+queue are configured.
api_available = self.deadline_authentication_status.api_availability is True
- farm_configured = get_setting("defaults.farm_id") != ""
- queue_configured = get_setting("defaults.queue_id") != ""
+ farm_configured = get_setting("defaults.farm_id", config=self._session_config) != ""
+ queue_configured = get_setting("defaults.queue_id", config=self._session_config) != ""
queue_valid = self.shared_job_settings.is_queue_valid()
enable = api_available and farm_configured and queue_configured and queue_valid
@@ -319,6 +333,7 @@ def _build_shared_job_settings_tab(self, initial_job_settings, initial_shared_pa
initial_settings=initial_job_settings,
initial_shared_parameter_values=initial_shared_parameter_values,
parent=self,
+ config=self._session_config,
)
self.shared_job_settings.parameter_changed.connect(self.on_shared_job_parameter_changed)
self.shared_job_settings_tab.setWidget(self.shared_job_settings)
@@ -403,13 +418,35 @@ def on_logout(self):
# not always catch a change so force a refresh here.
self.deadline_authentication_status.refresh_status()
- def on_switch_profile_clicked(self):
- if DeadlineConfigDialog.configure_settings(parent=self, set_profile_focus=True):
+ def _apply_settings_result(self, result: ConfigureSettingsResult) -> None:
+ """Apply the result from DeadlineConfigDialog.configure_settings()."""
+ if result.session_config is not None:
+ self._session_config = result.session_config
+ self.deadline_authentication_status.set_config(self._session_config)
+ self.shared_job_settings.set_session_config(self._session_config)
self.refresh_deadline_settings()
+ def _ensure_session_config(self) -> ConfigParser:
+ """Return the current session config, creating one from disk if needed."""
+ if self._session_config is None:
+ self._session_config = ConfigParser()
+ self._session_config.read_dict(config_file.read_config())
+ return self._session_config
+
+ def on_switch_profile_clicked(self):
+ result = DeadlineConfigDialog.configure_settings(
+ parent=self,
+ set_profile_focus=True,
+ session_config=self._ensure_session_config(),
+ )
+ self._apply_settings_result(result)
+
def on_settings_button_clicked(self):
- if DeadlineConfigDialog.configure_settings(parent=self):
- self.refresh_deadline_settings()
+ result = DeadlineConfigDialog.configure_settings(
+ parent=self,
+ session_config=self._ensure_session_config(),
+ )
+ self._apply_settings_result(result)
def _on_help_button_clicked(self):
"""Show the Help dialog with submitter information."""
@@ -599,7 +636,7 @@ def on_submit(self):
job_progress_dialog.start_job_submission(
job_bundle_dir=self.job_history_bundle_dir,
submitter_name=self.submitter_info.submitter_name,
- config=config_file.read_config(),
+ config=self._session_config or config_file.read_config(),
require_paths_exist=self.job_attachments.get_require_paths_exist(),
job_parameters=job_parameters,
known_asset_paths=self.known_asset_paths
diff --git a/src/deadline/client/ui/job_bundle_submitter.py b/src/deadline/client/ui/job_bundle_submitter.py
index 84a0d2ee0..4731c8edd 100644
--- a/src/deadline/client/ui/job_bundle_submitter.py
+++ b/src/deadline/client/ui/job_bundle_submitter.py
@@ -3,6 +3,7 @@
from __future__ import annotations
import copy
import os
+from configparser import ConfigParser
from logging import getLogger
from typing import Any, Optional, Dict
@@ -123,6 +124,7 @@ def show_job_bundle_submitter(
submitter_info: Optional[SubmitterInfo] = None,
known_asset_paths: Optional[list[str]] = None,
job_parameters: Optional[list[dict[str, Any]]] = None,
+ session_config: Optional[ConfigParser] = None,
) -> Optional[SubmitJobToDeadlineDialog]:
"""
Opens an AWS Deadline Cloud job submission dialog for the provided job bundle.
@@ -305,6 +307,7 @@ def on_create_job_bundle_callback(
f=f,
submitter_info=submitter_info,
known_asset_paths=known_asset_paths,
+ session_config=session_config,
)
if job_parameters:
diff --git a/src/deadline/client/ui/translations/locales/de_DE.json b/src/deadline/client/ui/translations/locales/de_DE.json
index a5cf52407..7c1cc5a1f 100644
--- a/src/deadline/client/ui/translations/locales/de_DE.json
+++ b/src/deadline/client/ui/translations/locales/de_DE.json
@@ -1,10 +1,6 @@
{
"...": "...",
"A configuration error was received while accessing credentials for the profile '{profile}'.": "Beim Zugriff auf die Anmeldeinformationen für das Profil '{profile}' ist ein Konfigurationsfehler aufgetreten.",
- "AWS Deadline Cloud API is not accessible. Check your authentication status.": "Die AWS Deadline Cloud-API ist nicht erreichbar. Überprüfen Sie Ihren Authentifizierungsstatus.",
- "AWS Deadline Cloud submission": "AWS Deadline Cloud-Übermittlung",
- "AWS Deadline Cloud workstation configuration": "AWS Deadline Cloud-Workstation-Konfiguration",
- "AWS profile": "AWS-Profil",
"About": "Über",
"Application Restart Required": "Neustart der Anwendung erforderlich",
"Add": "Hinzufügen",
@@ -22,14 +18,19 @@
"Attach input files": "Eingabedateien anhängen",
"Attribute name": "Attributname",
"Auto accept prompt defaults": "Standardwerte automatisch akzeptieren",
- "CPU architecture": "CPU-Architektur",
+ "AWS Deadline Cloud API is not accessible. Check your authentication status.": "Die AWS Deadline Cloud-API ist nicht erreichbar. Überprüfen Sie Ihren Authentifizierungsstatus.",
+ "AWS Deadline Cloud submission": "AWS Deadline Cloud-Übermittlung",
+ "AWS Deadline Cloud workstation configuration": "AWS Deadline Cloud-Workstation-Konfiguration",
+ "AWS profile": "AWS-Profil",
"Cancel": "Abbrechen",
"Canceling submission...": "Übermittlung wird abgebrochen...",
"Cannot submit job:\n\n\u2022 {issues}": "Job kann nicht übermittelt werden:\n\n\\u2022 {issues}",
"Choose job bundle directory": "Jobpaket-Verzeichnis auswählen",
"Close": "Schließen",
+ "Config File": "Konfigurationsdatei",
"Conflict resolution option": "Option zur Konfliktlösung",
"Copy": "Kopieren",
+ "CPU architecture": "CPU-Architektur",
"Current: {current_version} -> New: {latest_version}": "Aktuell: {current_version} -> Neu: {latest_version}",
"Current logging level": "Aktuelle Protokollierungsebene",
"Custom host requirements": "Benutzerdefinierte Host-Anforderungen",
@@ -50,28 +51,29 @@
"Failed to log in to AWS Deadline Cloud:
{error}": "Anmeldung bei AWS Deadline Cloud fehlgeschlagen:
{error}",
"Farm": "Farm",
"Farm settings": "Farm-Einstellungen",
- "GPU memory (GiB)": "GPU-Speicher (GiB)",
- "GPUs": "GPUs",
"General settings": "Allgemeine Einstellungen",
"General submission settings": "Allgemeine Übermittlungseinstellungen",
"Global settings": "Globale Einstellungen",
+ "GPU memory (GiB)": "GPU-Speicher (GiB)",
+ "GPUs": "GPUs",
"Hardware requirements": "Hardwareanforderungen",
"Hashing progress": "Hashing-Fortschritt",
"Help": "Hilfe",
"Host requirements": "Host-Anforderungen",
"Initial state": "Anfangszustand",
"Issue With Profile Configuration": "Problem mit Profilkonfiguration",
- "Job Properties": "Auftragseigenschaften",
- "Job Submission Confirmation": "Bestätigung der Jobübermittlung",
"Job attachments": "Arbeitsanhänge",
"Job attachments filesystem options": "Dateisystemoptionen für Jobanhänge",
"Job history directory": "Jobverlaufsverzeichnis",
+ "Job Properties": "Auftragseigenschaften",
+ "Job Submission Confirmation": "Bestätigung der Jobübermittlung",
"Job submission confirmation": "Bestätigung der Jobübermittlung",
"Job-specific settings": "Jobspezifische Einstellungen",
"Known asset paths": "Bekannte Asset-Pfade",
"Language": "Sprache",
"Language will change next time the submitter is opened": "Die Sprache wird beim nächsten Öffnen des Submitters geändert",
"Load Bundle": "Paket laden",
+ "Load from Disk": "Von Festplatte laden",
"Log in": "Anmelden",
"Log in to AWS Deadline Cloud": "Bei AWS Deadline Cloud anmelden",
"Logging you in...": "Sie werden angemeldet...",
@@ -106,6 +108,7 @@
"Require all input paths exist": "Alle Eingabepfade müssen vorhanden sein",
"Run on all available worker hosts": "Auf allen verfügbaren Worker-Hosts ausführen",
"Run on worker hosts that meet the following requirements": "Auf Worker-Hosts ausführen, die die folgenden Anforderungen erfüllen",
+ "Save to Disk": "Auf Festplatte speichern",
"Saved the submission as a job bundle:\n{path}": "Die Übermittlung wurde als Jobpaket gespeichert:\n{path}",
"Scratch space": "Temporärer Speicherplatz",
"Set max worker count": "Maximale Worker-Anzahl festlegen",
@@ -142,4 +145,4 @@
"{profile} - You are logged out.": "{profile} - Sie sind abgemeldet.",
"{profile} doesn't have access permissions to submit a job.": "{profile} hat keine Zugriffsberechtigungen zum Übermitteln eines Jobs.",
"{submitter} job submission": "{submitter}-Jobübermittlung"
-}
\ No newline at end of file
+}
diff --git a/src/deadline/client/ui/translations/locales/en_US.json b/src/deadline/client/ui/translations/locales/en_US.json
index 2a1435de7..6de41a616 100644
--- a/src/deadline/client/ui/translations/locales/en_US.json
+++ b/src/deadline/client/ui/translations/locales/en_US.json
@@ -1,10 +1,6 @@
{
"...": "...",
"A configuration error was received while accessing credentials for the profile '{profile}'.": "A configuration error was received while accessing credentials for the profile '{profile}'.",
- "AWS Deadline Cloud API is not accessible. Check your authentication status.": "AWS Deadline Cloud API is not accessible. Check your authentication status.",
- "AWS Deadline Cloud submission": "AWS Deadline Cloud submission",
- "AWS Deadline Cloud workstation configuration": "AWS Deadline Cloud workstation configuration",
- "AWS profile": "AWS profile",
"About": "About",
"Application Restart Required": "Application Restart Required",
"Add": "Add",
@@ -22,14 +18,19 @@
"Attach input files": "Attach input files",
"Attribute name": "Attribute name",
"Auto accept prompt defaults": "Auto accept prompt defaults",
- "CPU architecture": "CPU architecture",
+ "AWS Deadline Cloud API is not accessible. Check your authentication status.": "AWS Deadline Cloud API is not accessible. Check your authentication status.",
+ "AWS Deadline Cloud submission": "AWS Deadline Cloud submission",
+ "AWS Deadline Cloud workstation configuration": "AWS Deadline Cloud workstation configuration",
+ "AWS profile": "AWS profile",
"Cancel": "Cancel",
"Canceling submission...": "Canceling submission...",
"Cannot submit job:\n\n\u2022 {issues}": "Cannot submit job:\n\n\\u2022 {issues}",
"Choose job bundle directory": "Choose job bundle directory",
"Close": "Close",
+ "Config File": "Config File",
"Conflict resolution option": "Conflict resolution option",
"Copy": "Copy",
+ "CPU architecture": "CPU architecture",
"Current: {current_version} -> New: {latest_version}": "Current: {current_version} -> New: {latest_version}",
"Current logging level": "Current logging level",
"Custom host requirements": "Custom host requirements",
@@ -50,28 +51,29 @@
"Failed to log in to AWS Deadline Cloud:
{error}": "Failed to log in to AWS Deadline Cloud:
{error}",
"Farm": "Farm",
"Farm settings": "Farm settings",
- "GPU memory (GiB)": "GPU memory (GiB)",
- "GPUs": "GPUs",
"General settings": "General settings",
"General submission settings": "General submission settings",
"Global settings": "Global settings",
+ "GPU memory (GiB)": "GPU memory (GiB)",
+ "GPUs": "GPUs",
"Hardware requirements": "Hardware requirements",
"Hashing progress": "Hashing progress",
"Help": "Help",
"Host requirements": "Host requirements",
"Initial state": "Initial state",
"Issue With Profile Configuration": "Issue With Profile Configuration",
- "Job Properties": "Job Properties",
- "Job Submission Confirmation": "Job Submission Confirmation",
"Job attachments": "Job attachments",
"Job attachments filesystem options": "Job attachments filesystem options",
"Job history directory": "Job history directory",
+ "Job Properties": "Job Properties",
+ "Job Submission Confirmation": "Job Submission Confirmation",
"Job submission confirmation": "Job submission confirmation",
"Job-specific settings": "Job-specific settings",
"Known asset paths": "Known asset paths",
"Language": "Language",
"Language will change next time the submitter is opened": "Language will change next time the submitter is opened",
"Load Bundle": "Load Bundle",
+ "Load from Disk": "Load from Disk",
"Log in": "Log in",
"Log in to AWS Deadline Cloud": "Log in to AWS Deadline Cloud",
"Logging you in...": "Logging you in...",
@@ -106,6 +108,7 @@
"Require all input paths exist": "Require all input paths exist",
"Run on all available worker hosts": "Run on all available worker hosts",
"Run on worker hosts that meet the following requirements": "Run on worker hosts that meet the following requirements",
+ "Save to Disk": "Save to Disk",
"Saved the submission as a job bundle:\n{path}": "Saved the submission as a job bundle:\n{path}",
"Scratch space": "Scratch space",
"Set max worker count": "Set max worker count",
@@ -142,4 +145,4 @@
"{profile} - You are logged out.": "{profile} - You are logged out.",
"{profile} doesn't have access permissions to submit a job.": "{profile} doesn't have access permissions to submit a job.",
"{submitter} job submission": "{submitter} job submission"
-}
\ No newline at end of file
+}
diff --git a/src/deadline/client/ui/translations/locales/es_ES.json b/src/deadline/client/ui/translations/locales/es_ES.json
index 50587f1c4..255b9afbf 100644
--- a/src/deadline/client/ui/translations/locales/es_ES.json
+++ b/src/deadline/client/ui/translations/locales/es_ES.json
@@ -1,10 +1,6 @@
{
"...": "...",
"A configuration error was received while accessing credentials for the profile '{profile}'.": "Se recibió un error de configuración al acceder a las credenciales del perfil '{profile}'.",
- "AWS Deadline Cloud API is not accessible. Check your authentication status.": "No se puede acceder a la API de AWS Deadline Cloud. Compruebe su estado de autenticación.",
- "AWS Deadline Cloud submission": "Envío de AWS Deadline Cloud",
- "AWS Deadline Cloud workstation configuration": "Configuración de estación de trabajo de AWS Deadline Cloud",
- "AWS profile": "Perfil de AWS",
"About": "Acerca de",
"Application Restart Required": "Se requiere reiniciar la aplicación",
"Add": "Agregar",
@@ -22,14 +18,19 @@
"Attach input files": "Adjuntar archivos de entrada",
"Attribute name": "Nombre de atributo",
"Auto accept prompt defaults": "Aceptar automáticamente valores predeterminados",
- "CPU architecture": "Arquitectura de CPU",
+ "AWS Deadline Cloud API is not accessible. Check your authentication status.": "No se puede acceder a la API de AWS Deadline Cloud. Compruebe su estado de autenticación.",
+ "AWS Deadline Cloud submission": "Envío de AWS Deadline Cloud",
+ "AWS Deadline Cloud workstation configuration": "Configuración de estación de trabajo de AWS Deadline Cloud",
+ "AWS profile": "Perfil de AWS",
"Cancel": "Cancelar",
"Canceling submission...": "Cancelando envío...",
"Cannot submit job:\n\n\u2022 {issues}": "No se puede enviar el trabajo:\n\n\\u2022 {issues}",
"Choose job bundle directory": "Elegir directorio de paquete de trabajos",
"Close": "Cerrar",
+ "Config File": "Archivo de configuración",
"Conflict resolution option": "Opción de resolución de conflictos",
"Copy": "Copiar",
+ "CPU architecture": "Arquitectura de CPU",
"Current: {current_version} -> New: {latest_version}": "Actual: {current_version} -> Nuevo: {latest_version}",
"Current logging level": "Nivel de registro actual",
"Custom host requirements": "Requisitos de host personalizados",
@@ -50,28 +51,29 @@
"Failed to log in to AWS Deadline Cloud:
{error}": "Error al iniciar sesión en AWS Deadline Cloud:
{error}",
"Farm": "Granja",
"Farm settings": "Configuración de granja",
- "GPU memory (GiB)": "Memoria de GPU (GiB)",
- "GPUs": "GPU",
"General settings": "Configuración general",
"General submission settings": "Configuración general de envío",
"Global settings": "Configuración global",
+ "GPU memory (GiB)": "Memoria de GPU (GiB)",
+ "GPUs": "GPU",
"Hardware requirements": "Requisitos de hardware",
"Hashing progress": "Progreso de hash",
"Help": "Ayuda",
"Host requirements": "Requisitos de host",
"Initial state": "Estado inicial",
"Issue With Profile Configuration": "Problema con la configuración del perfil",
- "Job Properties": "Propiedades del trabajo",
- "Job Submission Confirmation": "Confirmación de envío de trabajo",
"Job attachments": "Adjuntos de trabajo",
"Job attachments filesystem options": "Opciones del sistema de archivos de adjuntos de trabajo",
"Job history directory": "Directorio de historial de trabajos",
+ "Job Properties": "Propiedades del trabajo",
+ "Job Submission Confirmation": "Confirmación de envío de trabajo",
"Job submission confirmation": "Confirmación de envío de trabajo",
"Job-specific settings": "Configuración específica del trabajo",
"Known asset paths": "Rutas de recursos conocidas",
"Language": "Idioma",
"Language will change next time the submitter is opened": "El idioma cambiará la próxima vez que se abra el submitter",
"Load Bundle": "Cargar paquete",
+ "Load from Disk": "Cargar desde disco",
"Log in": "Iniciar sesión",
"Log in to AWS Deadline Cloud": "Iniciar sesión en AWS Deadline Cloud",
"Logging you in...": "Iniciando sesión...",
@@ -106,6 +108,7 @@
"Require all input paths exist": "Requerir que existan todas las rutas de entrada",
"Run on all available worker hosts": "Ejecutar en todos los hosts de trabajadores disponibles",
"Run on worker hosts that meet the following requirements": "Ejecutar en hosts de trabajadores que cumplan los siguientes requisitos",
+ "Save to Disk": "Guardar en disco",
"Saved the submission as a job bundle:\n{path}": "Se guardó el envío como paquete de trabajos:\n{path}",
"Scratch space": "Espacio temporal",
"Set max worker count": "Establecer recuento máximo de trabajadores",
@@ -142,4 +145,4 @@
"{profile} - You are logged out.": "{profile} - Ha cerrado sesión.",
"{profile} doesn't have access permissions to submit a job.": "{profile} no tiene permisos de acceso para enviar un trabajo.",
"{submitter} job submission": "Envío de trabajo de {submitter}"
-}
\ No newline at end of file
+}
diff --git a/src/deadline/client/ui/translations/locales/fr_FR.json b/src/deadline/client/ui/translations/locales/fr_FR.json
index 0370c48e0..2918a5233 100644
--- a/src/deadline/client/ui/translations/locales/fr_FR.json
+++ b/src/deadline/client/ui/translations/locales/fr_FR.json
@@ -1,10 +1,6 @@
{
"...": "...",
"A configuration error was received while accessing credentials for the profile '{profile}'.": "Une erreur de configuration s'est produite lors de l'accès aux informations d'identification du profil '{profile}'.",
- "AWS Deadline Cloud API is not accessible. Check your authentication status.": "L'API AWS Deadline Cloud n'est pas accessible. Vérifiez votre statut d'authentification.",
- "AWS Deadline Cloud submission": "Soumission AWS Deadline Cloud",
- "AWS Deadline Cloud workstation configuration": "Configuration de poste de travail AWS Deadline Cloud",
- "AWS profile": "Profil AWS",
"About": "À propos",
"Application Restart Required": "Redémarrage de l'application requis",
"Add": "Ajouter",
@@ -22,14 +18,19 @@
"Attach input files": "Joindre des fichiers d'entrée",
"Attribute name": "Nom d'attribut",
"Auto accept prompt defaults": "Accepter automatiquement les valeurs par défaut",
- "CPU architecture": "Architecture CPU",
+ "AWS Deadline Cloud API is not accessible. Check your authentication status.": "L'API AWS Deadline Cloud n'est pas accessible. Vérifiez votre statut d'authentification.",
+ "AWS Deadline Cloud submission": "Soumission AWS Deadline Cloud",
+ "AWS Deadline Cloud workstation configuration": "Configuration de poste de travail AWS Deadline Cloud",
+ "AWS profile": "Profil AWS",
"Cancel": "Annuler",
"Canceling submission...": "Annulation de la soumission...",
"Cannot submit job:\n\n\u2022 {issues}": "Impossible de soumettre la tâche :\n\n\\u2022 {issues}",
"Choose job bundle directory": "Choisir le répertoire du lot de tâches",
"Close": "Fermer",
+ "Config File": "Fichier de configuration",
"Conflict resolution option": "Option de résolution de conflits",
"Copy": "Copier",
+ "CPU architecture": "Architecture CPU",
"Current: {current_version} -> New: {latest_version}": "Actuel : {current_version} -> Nouveau : {latest_version}",
"Current logging level": "Niveau de journalisation actuel",
"Custom host requirements": "Exigences d'hôte personnalisées",
@@ -50,28 +51,29 @@
"Failed to log in to AWS Deadline Cloud:
{error}": "Échec de la connexion à AWS Deadline Cloud :
{error}",
"Farm": "Ferme",
"Farm settings": "Paramètres de ferme",
- "GPU memory (GiB)": "Mémoire GPU (Gio)",
- "GPUs": "GPU",
"General settings": "Paramètres généraux",
"General submission settings": "Paramètres généraux de soumission",
"Global settings": "Paramètres globaux",
+ "GPU memory (GiB)": "Mémoire GPU (Gio)",
+ "GPUs": "GPU",
"Hardware requirements": "Exigences matérielles",
"Hashing progress": "Progression du hachage",
"Help": "Aide",
"Host requirements": "Exigences d'hôte",
"Initial state": "État initial",
"Issue With Profile Configuration": "Problème avec la configuration du profil",
- "Job Properties": "Propriétés de la tâche",
- "Job Submission Confirmation": "Confirmation de soumission de tâche",
"Job attachments": "Fichiers joints de tâche",
"Job attachments filesystem options": "Options du système de fichiers des pièces jointes aux tâches",
"Job history directory": "Répertoire d'historique des tâches",
+ "Job Properties": "Propriétés de la tâche",
+ "Job Submission Confirmation": "Confirmation de soumission de tâche",
"Job submission confirmation": "Confirmation de soumission de tâche",
"Job-specific settings": "Paramètres spécifiques à la tâche",
"Known asset paths": "Chemins d'actifs connus",
"Language": "Langue",
"Language will change next time the submitter is opened": "La langue changera lors de la prochaine ouverture du submitter",
"Load Bundle": "Charger le lot",
+ "Load from Disk": "Charger depuis le disque",
"Log in": "Se connecter",
"Log in to AWS Deadline Cloud": "Se connecter à AWS Deadline Cloud",
"Logging you in...": "Connexion en cours...",
@@ -106,6 +108,7 @@
"Require all input paths exist": "Exiger que tous les chemins d'entrée existent",
"Run on all available worker hosts": "Exécuter sur tous les hôtes de travail disponibles",
"Run on worker hosts that meet the following requirements": "Exécuter sur les hôtes de travail qui répondent aux exigences suivantes",
+ "Save to Disk": "Enregistrer sur le disque",
"Saved the submission as a job bundle:\n{path}": "La soumission a été enregistrée en tant que lot de tâches :\n{path}",
"Scratch space": "Espace temporaire",
"Set max worker count": "Définir le nombre maximal de travailleurs",
@@ -142,4 +145,4 @@
"{profile} - You are logged out.": "{profile} - Vous êtes déconnecté.",
"{profile} doesn't have access permissions to submit a job.": "{profile} n'a pas les autorisations d'accès pour soumettre une tâche.",
"{submitter} job submission": "Soumission de tâche {submitter}"
-}
\ No newline at end of file
+}
diff --git a/src/deadline/client/ui/translations/locales/id_ID.json b/src/deadline/client/ui/translations/locales/id_ID.json
index 2a066fc28..467682784 100644
--- a/src/deadline/client/ui/translations/locales/id_ID.json
+++ b/src/deadline/client/ui/translations/locales/id_ID.json
@@ -1,10 +1,6 @@
{
"...": "...",
"A configuration error was received while accessing credentials for the profile '{profile}'.": "Kesalahan konfigurasi diterima saat mengakses kredensial untuk profil '{profile}'.",
- "AWS Deadline Cloud API is not accessible. Check your authentication status.": "API AWS Deadline Cloud tidak dapat diakses. Periksa status autentikasi Anda.",
- "AWS Deadline Cloud submission": "Pengiriman AWS Deadline Cloud",
- "AWS Deadline Cloud workstation configuration": "Konfigurasi workstation AWS Deadline Cloud",
- "AWS profile": "Profil AWS",
"About": "Tentang",
"Application Restart Required": "Diperlukan restart aplikasi",
"Add": "Tambah",
@@ -22,14 +18,19 @@
"Attach input files": "Lampirkan file input",
"Attribute name": "Nama atribut",
"Auto accept prompt defaults": "Terima default secara otomatis",
- "CPU architecture": "Arsitektur CPU",
+ "AWS Deadline Cloud API is not accessible. Check your authentication status.": "API AWS Deadline Cloud tidak dapat diakses. Periksa status autentikasi Anda.",
+ "AWS Deadline Cloud submission": "Pengiriman AWS Deadline Cloud",
+ "AWS Deadline Cloud workstation configuration": "Konfigurasi workstation AWS Deadline Cloud",
+ "AWS profile": "Profil AWS",
"Cancel": "Batal",
"Canceling submission...": "Membatalkan pengiriman...",
"Cannot submit job:\n\n\u2022 {issues}": "Tidak dapat mengirim pekerjaan:\n\n\\u2022 {issues}",
"Choose job bundle directory": "Pilih direktori bundel pekerjaan",
"Close": "Tutup",
+ "Config File": "File Konfigurasi",
"Conflict resolution option": "Opsi resolusi konflik",
"Copy": "Salin",
+ "CPU architecture": "Arsitektur CPU",
"Current: {current_version} -> New: {latest_version}": "Saat ini: {current_version} -> Baru: {latest_version}",
"Current logging level": "Tingkat logging saat ini",
"Custom host requirements": "Persyaratan host kustom",
@@ -50,28 +51,29 @@
"Failed to log in to AWS Deadline Cloud:
{error}": "Gagal masuk ke AWS Deadline Cloud:
{error}",
"Farm": "Peternakan",
"Farm settings": "Pengaturan peternakan",
- "GPU memory (GiB)": "Memori GPU (GiB)",
- "GPUs": "GPU",
"General settings": "Pengaturan umum",
"General submission settings": "Pengaturan pengiriman umum",
"Global settings": "Pengaturan global",
+ "GPU memory (GiB)": "Memori GPU (GiB)",
+ "GPUs": "GPU",
"Hardware requirements": "Persyaratan perangkat keras",
"Hashing progress": "Kemajuan hashing",
"Help": "Bantuan",
"Host requirements": "Persyaratan host",
"Initial state": "Status awal",
"Issue With Profile Configuration": "Masalah dengan konfigurasi profil",
- "Job Properties": "Properti Job",
- "Job Submission Confirmation": "Konfirmasi pengiriman pekerjaan",
"Job attachments": "Lampiran Job",
"Job attachments filesystem options": "Opsi sistem file lampiran pekerjaan",
"Job history directory": "Direktori riwayat pekerjaan",
+ "Job Properties": "Properti Job",
+ "Job Submission Confirmation": "Konfirmasi pengiriman pekerjaan",
"Job submission confirmation": "Konfirmasi pengiriman pekerjaan",
"Job-specific settings": "Pengaturan khusus pekerjaan",
"Known asset paths": "Jalur aset yang diketahui",
"Language": "Bahasa",
"Language will change next time the submitter is opened": "Bahasa akan berubah saat submitter dibuka kembali",
"Load Bundle": "Muat bundel",
+ "Load from Disk": "Muat dari Disk",
"Log in": "Masuk",
"Log in to AWS Deadline Cloud": "Masuk ke AWS Deadline Cloud",
"Logging you in...": "Memasukkan Anda...",
@@ -106,6 +108,7 @@
"Require all input paths exist": "Memerlukan semua jalur input ada",
"Run on all available worker hosts": "Jalankan di semua host pekerja yang tersedia",
"Run on worker hosts that meet the following requirements": "Jalankan di host pekerja yang memenuhi persyaratan berikut",
+ "Save to Disk": "Simpan ke Disk",
"Saved the submission as a job bundle:\n{path}": "Menyimpan pengiriman sebagai bundel pekerjaan:\n{path}",
"Scratch space": "Ruang sementara",
"Set max worker count": "Atur jumlah pekerja maksimum",
@@ -142,4 +145,4 @@
"{profile} - You are logged out.": "{profile} - Anda telah keluar.",
"{profile} doesn't have access permissions to submit a job.": "{profile} tidak memiliki izin akses untuk mengirim pekerjaan.",
"{submitter} job submission": "Pengiriman pekerjaan {submitter}"
-}
\ No newline at end of file
+}
diff --git a/src/deadline/client/ui/translations/locales/it_IT.json b/src/deadline/client/ui/translations/locales/it_IT.json
index 3562adeb1..cf77e0a29 100644
--- a/src/deadline/client/ui/translations/locales/it_IT.json
+++ b/src/deadline/client/ui/translations/locales/it_IT.json
@@ -1,10 +1,6 @@
{
"...": "...",
"A configuration error was received while accessing credentials for the profile '{profile}'.": "Si è verificato un errore di configurazione durante l'accesso alle credenziali per il profilo '{profile}'.",
- "AWS Deadline Cloud API is not accessible. Check your authentication status.": "L'API AWS Deadline Cloud non è accessibile. Verifica lo stato di autenticazione.",
- "AWS Deadline Cloud submission": "Invio AWS Deadline Cloud",
- "AWS Deadline Cloud workstation configuration": "Configurazione workstation AWS Deadline Cloud",
- "AWS profile": "Profilo AWS",
"About": "Informazioni",
"Application Restart Required": "Riavvio dell'applicazione richiesto",
"Add": "Aggiungi",
@@ -22,14 +18,19 @@
"Attach input files": "Allega file di input",
"Attribute name": "Nome attributo",
"Auto accept prompt defaults": "Accetta automaticamente i valori predefiniti",
- "CPU architecture": "Architettura CPU",
+ "AWS Deadline Cloud API is not accessible. Check your authentication status.": "L'API AWS Deadline Cloud non è accessibile. Verifica lo stato di autenticazione.",
+ "AWS Deadline Cloud submission": "Invio AWS Deadline Cloud",
+ "AWS Deadline Cloud workstation configuration": "Configurazione workstation AWS Deadline Cloud",
+ "AWS profile": "Profilo AWS",
"Cancel": "Annulla",
"Canceling submission...": "Annullamento invio...",
"Cannot submit job:\n\n\u2022 {issues}": "Impossibile inviare il lavoro:\n\n\\u2022 {issues}",
"Choose job bundle directory": "Scegli directory pacchetto lavoro",
"Close": "Chiudi",
+ "Config File": "File di configurazione",
"Conflict resolution option": "Opzione di risoluzione conflitti",
"Copy": "Copia",
+ "CPU architecture": "Architettura CPU",
"Current: {current_version} -> New: {latest_version}": "Corrente: {current_version} -> Nuovo: {latest_version}",
"Current logging level": "Livello di registrazione corrente",
"Custom host requirements": "Requisiti host personalizzati",
@@ -50,28 +51,29 @@
"Failed to log in to AWS Deadline Cloud:
{error}": "Accesso ad AWS Deadline Cloud non riuscito:
{error}",
"Farm": "Farm",
"Farm settings": "Impostazioni farm",
- "GPU memory (GiB)": "Memoria GPU (GiB)",
- "GPUs": "GPU",
"General settings": "Impostazioni generali",
"General submission settings": "Impostazioni generali di invio",
"Global settings": "Impostazioni globali",
+ "GPU memory (GiB)": "Memoria GPU (GiB)",
+ "GPUs": "GPU",
"Hardware requirements": "Requisiti hardware",
"Hashing progress": "Avanzamento hashing",
"Help": "Aiuto",
"Host requirements": "Requisiti host",
"Initial state": "Stato iniziale",
"Issue With Profile Configuration": "Problema con la configurazione del profilo",
- "Job Properties": "Proprietà processo",
- "Job Submission Confirmation": "Conferma invio lavoro",
"Job attachments": "Allegati Job",
"Job attachments filesystem options": "Opzioni filesystem allegati lavoro",
"Job history directory": "Directory cronologia lavori",
+ "Job Properties": "Proprietà processo",
+ "Job Submission Confirmation": "Conferma invio lavoro",
"Job submission confirmation": "Conferma invio lavoro",
"Job-specific settings": "Impostazioni specifiche del lavoro",
"Known asset paths": "Percorsi asset noti",
"Language": "Lingua",
"Language will change next time the submitter is opened": "La lingua cambierà alla prossima apertura del submitter",
"Load Bundle": "Carica pacchetto",
+ "Load from Disk": "Carica da disco",
"Log in": "Accedi",
"Log in to AWS Deadline Cloud": "Accedi ad AWS Deadline Cloud",
"Logging you in...": "Accesso in corso...",
@@ -106,6 +108,7 @@
"Require all input paths exist": "Richiedi che tutti i percorsi di input esistano",
"Run on all available worker hosts": "Esegui su tutti gli host worker disponibili",
"Run on worker hosts that meet the following requirements": "Esegui su host worker che soddisfano i seguenti requisiti",
+ "Save to Disk": "Salva su disco",
"Saved the submission as a job bundle:\n{path}": "Invio salvato come pacchetto lavoro:\n{path}",
"Scratch space": "Spazio temporaneo",
"Set max worker count": "Imposta numero massimo di worker",
@@ -142,4 +145,4 @@
"{profile} - You are logged out.": "{profile} - Hai effettuato il logout.",
"{profile} doesn't have access permissions to submit a job.": "{profile} non dispone delle autorizzazioni di accesso per inviare un lavoro.",
"{submitter} job submission": "Invio lavoro {submitter}"
-}
\ No newline at end of file
+}
diff --git a/src/deadline/client/ui/translations/locales/ja_JP.json b/src/deadline/client/ui/translations/locales/ja_JP.json
index 8459dc03b..c2c1d9a33 100644
--- a/src/deadline/client/ui/translations/locales/ja_JP.json
+++ b/src/deadline/client/ui/translations/locales/ja_JP.json
@@ -1,10 +1,6 @@
{
"...": "...",
"A configuration error was received while accessing credentials for the profile '{profile}'.": "プロファイル '{profile}' の認証情報にアクセス中に設定エラーが発生しました。",
- "AWS Deadline Cloud API is not accessible. Check your authentication status.": "AWS Deadline Cloud API にアクセスできません。認証ステータスを確認してください。",
- "AWS Deadline Cloud submission": "AWS Deadline Cloud 送信",
- "AWS Deadline Cloud workstation configuration": "AWS Deadline Cloud ワークステーション設定",
- "AWS profile": "AWS プロファイル",
"About": "バージョン情報",
"Application Restart Required": "アプリケーションの再起動が必要です",
"Add": "追加",
@@ -22,14 +18,19 @@
"Attach input files": "入力ファイルを添付",
"Attribute name": "属性名",
"Auto accept prompt defaults": "デフォルト値を自動的に受け入れる",
- "CPU architecture": "CPU アーキテクチャ",
+ "AWS Deadline Cloud API is not accessible. Check your authentication status.": "AWS Deadline Cloud API にアクセスできません。認証ステータスを確認してください。",
+ "AWS Deadline Cloud submission": "AWS Deadline Cloud 送信",
+ "AWS Deadline Cloud workstation configuration": "AWS Deadline Cloud ワークステーション設定",
+ "AWS profile": "AWS プロファイル",
"Cancel": "キャンセル",
"Canceling submission...": "送信をキャンセルしています...",
"Cannot submit job:\n\n\u2022 {issues}": "ジョブを送信できません:\n\n\\u2022 {issues}",
"Choose job bundle directory": "ジョブバンドルディレクトリを選択",
"Close": "閉じる",
+ "Config File": "設定ファイル",
"Conflict resolution option": "競合解決オプション",
"Copy": "コピー",
+ "CPU architecture": "CPU アーキテクチャ",
"Current: {current_version} -> New: {latest_version}": "現在: {current_version} -> 新規: {latest_version}",
"Current logging level": "現在のログレベル",
"Custom host requirements": "カスタムホスト要件",
@@ -50,28 +51,29 @@
"Failed to log in to AWS Deadline Cloud:
{error}": "AWS Deadline Cloud へのログインに失敗しました:
{error}",
"Farm": "ファーム",
"Farm settings": "ファーム設定",
- "GPU memory (GiB)": "GPU メモリ (GiB)",
- "GPUs": "GPU",
"General settings": "一般設定",
"General submission settings": "一般送信設定",
"Global settings": "グローバル設定",
+ "GPU memory (GiB)": "GPU メモリ (GiB)",
+ "GPUs": "GPU",
"Hardware requirements": "ハードウェア要件",
"Hashing progress": "ハッシュ進行状況",
"Help": "ヘルプ",
"Host requirements": "ホスト要件",
"Initial state": "初期状態",
"Issue With Profile Configuration": "プロファイル設定の問題",
- "Job Properties": "ジョブプロパティ",
- "Job Submission Confirmation": "ジョブ送信の確認",
"Job attachments": "ジョブアタッチメント",
"Job attachments filesystem options": "ジョブアタッチメントファイルシステムオプション",
"Job history directory": "ジョブ履歴ディレクトリ",
+ "Job Properties": "ジョブプロパティ",
+ "Job Submission Confirmation": "ジョブ送信の確認",
"Job submission confirmation": "ジョブ送信の確認",
"Job-specific settings": "ジョブ固有の設定",
"Known asset paths": "既知のアセットパス",
"Language": "言語",
"Language will change next time the submitter is opened": "言語は次回サブミッターを開いたときに変更されます",
"Load Bundle": "バンドルを読み込む",
+ "Load from Disk": "ディスクから読み込み",
"Log in": "ログイン",
"Log in to AWS Deadline Cloud": "AWS Deadline Cloud にログイン",
"Logging you in...": "ログイン中...",
@@ -106,6 +108,7 @@
"Require all input paths exist": "すべての入力パスが存在することを要求",
"Run on all available worker hosts": "使用可能なすべてのワーカーホストで実行",
"Run on worker hosts that meet the following requirements": "次の要件を満たすワーカーホストで実行",
+ "Save to Disk": "ディスクに保存",
"Saved the submission as a job bundle:\n{path}": "送信をジョブバンドルとして保存しました:\n{path}",
"Scratch space": "一時領域",
"Set max worker count": "最大ワーカー数を設定",
@@ -142,4 +145,4 @@
"{profile} - You are logged out.": "{profile} - ログアウトしています。",
"{profile} doesn't have access permissions to submit a job.": "{profile} にはジョブを送信するアクセス許可がありません。",
"{submitter} job submission": "{submitter} ジョブ送信"
-}
\ No newline at end of file
+}
diff --git a/src/deadline/client/ui/translations/locales/ko_KR.json b/src/deadline/client/ui/translations/locales/ko_KR.json
index db3e7eb85..f256c12a2 100644
--- a/src/deadline/client/ui/translations/locales/ko_KR.json
+++ b/src/deadline/client/ui/translations/locales/ko_KR.json
@@ -1,10 +1,6 @@
{
"...": "...",
"A configuration error was received while accessing credentials for the profile '{profile}'.": "프로필 '{profile}'의 자격 증명에 액세스하는 동안 구성 오류가 발생했습니다.",
- "AWS Deadline Cloud API is not accessible. Check your authentication status.": "AWS Deadline Cloud API에 액세스할 수 없습니다. 인증 상태를 확인하세요.",
- "AWS Deadline Cloud submission": "AWS Deadline Cloud 제출",
- "AWS Deadline Cloud workstation configuration": "AWS Deadline Cloud 워크스테이션 구성",
- "AWS profile": "AWS 프로필",
"About": "정보",
"Application Restart Required": "애플리케이션 재시작 필요",
"Add": "추가",
@@ -22,14 +18,19 @@
"Attach input files": "입력 파일 연결",
"Attribute name": "속성 이름",
"Auto accept prompt defaults": "기본값 자동 수락",
- "CPU architecture": "CPU 아키텍처",
+ "AWS Deadline Cloud API is not accessible. Check your authentication status.": "AWS Deadline Cloud API에 액세스할 수 없습니다. 인증 상태를 확인하세요.",
+ "AWS Deadline Cloud submission": "AWS Deadline Cloud 제출",
+ "AWS Deadline Cloud workstation configuration": "AWS Deadline Cloud 워크스테이션 구성",
+ "AWS profile": "AWS 프로필",
"Cancel": "취소",
"Canceling submission...": "제출 취소 중...",
"Cannot submit job:\n\n\u2022 {issues}": "작업을 제출할 수 없습니다:\n\n\\u2022 {issues}",
"Choose job bundle directory": "작업 번들 디렉터리 선택",
"Close": "닫기",
+ "Config File": "구성 파일",
"Conflict resolution option": "충돌 해결 옵션",
"Copy": "복사",
+ "CPU architecture": "CPU 아키텍처",
"Current: {current_version} -> New: {latest_version}": "현재: {current_version} -> 새 버전: {latest_version}",
"Current logging level": "현재 로깅 수준",
"Custom host requirements": "사용자 지정 호스트 요구 사항",
@@ -50,28 +51,29 @@
"Failed to log in to AWS Deadline Cloud:
{error}": "AWS Deadline Cloud에 로그인하지 못했습니다:
{error}",
"Farm": "팜",
"Farm settings": "팜 설정",
- "GPU memory (GiB)": "GPU 메모리(GiB)",
- "GPUs": "GPU",
"General settings": "일반 설정",
"General submission settings": "일반 제출 설정",
"Global settings": "전역 설정",
+ "GPU memory (GiB)": "GPU 메모리(GiB)",
+ "GPUs": "GPU",
"Hardware requirements": "하드웨어 요구 사항",
"Hashing progress": "해싱 진행률",
"Help": "도움말",
"Host requirements": "호스트 요구 사항",
"Initial state": "초기 상태",
"Issue With Profile Configuration": "프로필 구성 문제",
- "Job Properties": "작업 속성",
- "Job Submission Confirmation": "작업 제출 확인",
"Job attachments": "작업 첨부 파일",
"Job attachments filesystem options": "작업 첨부 파일 파일 시스템 옵션",
"Job history directory": "작업 기록 디렉터리",
+ "Job Properties": "작업 속성",
+ "Job Submission Confirmation": "작업 제출 확인",
"Job submission confirmation": "작업 제출 확인",
"Job-specific settings": "작업별 설정",
"Known asset paths": "알려진 자산 경로",
"Language": "언어",
"Language will change next time the submitter is opened": "언어는 다음에 제출자를 열 때 변경됩니다",
"Load Bundle": "번들 로드",
+ "Load from Disk": "디스크에서 불러오기",
"Log in": "로그인",
"Log in to AWS Deadline Cloud": "AWS Deadline Cloud에 로그인",
"Logging you in...": "로그인 중...",
@@ -106,6 +108,7 @@
"Require all input paths exist": "모든 입력 경로가 존재해야 함",
"Run on all available worker hosts": "사용 가능한 모든 작업자 호스트에서 실행",
"Run on worker hosts that meet the following requirements": "다음 요구 사항을 충족하는 작업자 호스트에서 실행",
+ "Save to Disk": "디스크에 저장",
"Saved the submission as a job bundle:\n{path}": "제출을 작업 번들로 저장했습니다:\n{path}",
"Scratch space": "임시 공간",
"Set max worker count": "최대 작업자 수 설정",
@@ -142,4 +145,4 @@
"{profile} - You are logged out.": "{profile} - 로그아웃되었습니다.",
"{profile} doesn't have access permissions to submit a job.": "{profile}에 작업을 제출할 액세스 권한이 없습니다.",
"{submitter} job submission": "{submitter} 작업 제출"
-}
\ No newline at end of file
+}
diff --git a/src/deadline/client/ui/translations/locales/pt_BR.json b/src/deadline/client/ui/translations/locales/pt_BR.json
index 0a1cc251b..6ff7b7b94 100644
--- a/src/deadline/client/ui/translations/locales/pt_BR.json
+++ b/src/deadline/client/ui/translations/locales/pt_BR.json
@@ -1,10 +1,6 @@
{
"...": "...",
"A configuration error was received while accessing credentials for the profile '{profile}'.": "Um erro de configuração foi recebido ao acessar as credenciais do perfil '{profile}'.",
- "AWS Deadline Cloud API is not accessible. Check your authentication status.": "A API do AWS Deadline Cloud não está acessível. Verifique seu status de autenticação.",
- "AWS Deadline Cloud submission": "Envio do AWS Deadline Cloud",
- "AWS Deadline Cloud workstation configuration": "Configuração de estação de trabalho do AWS Deadline Cloud",
- "AWS profile": "Perfil da AWS",
"About": "Sobre",
"Application Restart Required": "Reinicialização do aplicativo necessária",
"Add": "Adicionar",
@@ -22,14 +18,19 @@
"Attach input files": "Anexar arquivos de entrada",
"Attribute name": "Nome do atributo",
"Auto accept prompt defaults": "Aceitar automaticamente padrões",
- "CPU architecture": "Arquitetura da CPU",
+ "AWS Deadline Cloud API is not accessible. Check your authentication status.": "A API do AWS Deadline Cloud não está acessível. Verifique seu status de autenticação.",
+ "AWS Deadline Cloud submission": "Envio do AWS Deadline Cloud",
+ "AWS Deadline Cloud workstation configuration": "Configuração de estação de trabalho do AWS Deadline Cloud",
+ "AWS profile": "Perfil da AWS",
"Cancel": "Cancelar",
"Canceling submission...": "Cancelando envio...",
"Cannot submit job:\n\n\u2022 {issues}": "Não é possível enviar o trabalho:\n\n\\u2022 {issues}",
"Choose job bundle directory": "Escolher diretório do pacote de tarefas",
"Close": "Fechar",
+ "Config File": "Arquivo de configuração",
"Conflict resolution option": "Opção de resolução de conflitos",
"Copy": "Copiar",
+ "CPU architecture": "Arquitetura da CPU",
"Current: {current_version} -> New: {latest_version}": "Atual: {current_version} -> Novo: {latest_version}",
"Current logging level": "Nível de registro atual",
"Custom host requirements": "Requisitos de host personalizados",
@@ -50,28 +51,29 @@
"Failed to log in to AWS Deadline Cloud:
{error}": "Falha ao fazer login no AWS Deadline Cloud:
{error}",
"Farm": "Fazenda",
"Farm settings": "Configurações da fazenda",
- "GPU memory (GiB)": "Memória da GPU (GiB)",
- "GPUs": "GPUs",
"General settings": "Configurações gerais",
"General submission settings": "Configurações gerais de envio",
"Global settings": "Configurações globais",
+ "GPU memory (GiB)": "Memória da GPU (GiB)",
+ "GPUs": "GPUs",
"Hardware requirements": "Requisitos de hardware",
"Hashing progress": "Progresso de hash",
"Help": "Ajuda",
"Host requirements": "Requisitos de host",
"Initial state": "Estado inicial",
"Issue With Profile Configuration": "Problema com a configuração do perfil",
- "Job Properties": "Propriedades do trabalho",
- "Job Submission Confirmation": "Confirmação de envio de trabalho",
"Job attachments": "Anexos de trabalho",
"Job attachments filesystem options": "Opções do sistema de arquivos de anexos de tarefas",
"Job history directory": "Diretório de histórico de trabalhos",
+ "Job Properties": "Propriedades do trabalho",
+ "Job Submission Confirmation": "Confirmação de envio de trabalho",
"Job submission confirmation": "Confirmação de envio de trabalho",
"Job-specific settings": "Configurações específicas do trabalho",
"Known asset paths": "Caminhos de ativos conhecidos",
"Language": "Idioma",
"Language will change next time the submitter is opened": "O idioma será alterado na próxima vez que o submitter for aberto",
"Load Bundle": "Carregar pacote",
+ "Load from Disk": "Carregar do disco",
"Log in": "Fazer login",
"Log in to AWS Deadline Cloud": "Fazer login no AWS Deadline Cloud",
"Logging you in...": "Fazendo login...",
@@ -106,6 +108,7 @@
"Require all input paths exist": "Exigir que todos os caminhos de entrada existam",
"Run on all available worker hosts": "Executar em todos os hosts de trabalho disponíveis",
"Run on worker hosts that meet the following requirements": "Executar em hosts de trabalho que atendam aos seguintes requisitos",
+ "Save to Disk": "Salvar em disco",
"Saved the submission as a job bundle:\n{path}": "O envio foi salvo como um pacote de tarefas:\n{path}",
"Scratch space": "Espaço temporário",
"Set max worker count": "Definir contagem máxima de trabalhadores",
@@ -142,4 +145,4 @@
"{profile} - You are logged out.": "{profile} - Você está desconectado.",
"{profile} doesn't have access permissions to submit a job.": "{profile} não tem permissões de acesso para enviar um trabalho.",
"{submitter} job submission": "Envio de trabalho {submitter}"
-}
\ No newline at end of file
+}
diff --git a/src/deadline/client/ui/translations/locales/tr_TR.json b/src/deadline/client/ui/translations/locales/tr_TR.json
index 618ede40c..e4a8f8aa6 100644
--- a/src/deadline/client/ui/translations/locales/tr_TR.json
+++ b/src/deadline/client/ui/translations/locales/tr_TR.json
@@ -1,10 +1,6 @@
{
"...": "...",
"A configuration error was received while accessing credentials for the profile '{profile}'.": "'{profile}' profili için kimlik bilgilerine erişilirken bir yapılandırma hatası alındı.",
- "AWS Deadline Cloud API is not accessible. Check your authentication status.": "AWS Deadline Cloud API'sine erişilemiyor. Kimlik doğrulama durumunuzu kontrol edin.",
- "AWS Deadline Cloud submission": "AWS Deadline Cloud gönderimi",
- "AWS Deadline Cloud workstation configuration": "AWS Deadline Cloud iş istasyonu yapılandırması",
- "AWS profile": "AWS profili",
"About": "Hakkında",
"Application Restart Required": "Uygulama yeniden başlatma gerekli",
"Add": "Ekle",
@@ -22,14 +18,19 @@
"Attach input files": "Giriş dosyalarını ekle",
"Attribute name": "Öznitelik adı",
"Auto accept prompt defaults": "Varsayılanları otomatik olarak kabul et",
- "CPU architecture": "CPU mimarisi",
+ "AWS Deadline Cloud API is not accessible. Check your authentication status.": "AWS Deadline Cloud API'sine erişilemiyor. Kimlik doğrulama durumunuzu kontrol edin.",
+ "AWS Deadline Cloud submission": "AWS Deadline Cloud gönderimi",
+ "AWS Deadline Cloud workstation configuration": "AWS Deadline Cloud iş istasyonu yapılandırması",
+ "AWS profile": "AWS profili",
"Cancel": "İptal",
"Canceling submission...": "Gönderim iptal ediliyor...",
"Cannot submit job:\n\n\u2022 {issues}": "İş gönderilemedi:\n\n\\u2022 {issues}",
"Choose job bundle directory": "İş paketi dizini seçin",
"Close": "Kapat",
+ "Config File": "Yapılandırma Dosyası",
"Conflict resolution option": "Çakışma çözümleme seçeneği",
"Copy": "Kopyala",
+ "CPU architecture": "CPU mimarisi",
"Current: {current_version} -> New: {latest_version}": "Mevcut: {current_version} -> Yeni: {latest_version}",
"Current logging level": "Geçerli günlük kaydı düzeyi",
"Custom host requirements": "Özel ana bilgisayar gereksinimleri",
@@ -50,28 +51,29 @@
"Failed to log in to AWS Deadline Cloud:
{error}": "AWS Deadline Cloud'da oturum açılamadı:
{error}",
"Farm": "Farm",
"Farm settings": "Farm ayarları",
- "GPU memory (GiB)": "GPU belleği (GiB)",
- "GPUs": "GPU'lar",
"General settings": "Genel ayarlar",
"General submission settings": "Genel gönderim ayarları",
"Global settings": "Genel ayarlar",
+ "GPU memory (GiB)": "GPU belleği (GiB)",
+ "GPUs": "GPU'lar",
"Hardware requirements": "Donanım gereksinimleri",
"Hashing progress": "Karma oluşturma ilerlemesi",
"Help": "Yardım",
"Host requirements": "Ana bilgisayar gereksinimleri",
"Initial state": "Başlangıç durumu",
"Issue With Profile Configuration": "Profil yapılandırmasıyla ilgili sorun",
- "Job Properties": "İş özellikleri",
- "Job Submission Confirmation": "İş gönderimi onayı",
"Job attachments": "İş ekleri",
"Job attachments filesystem options": "İş ekleri dosya sistemi seçenekleri",
"Job history directory": "İş geçmişi dizini",
+ "Job Properties": "İş özellikleri",
+ "Job Submission Confirmation": "İş gönderimi onayı",
"Job submission confirmation": "İş gönderimi onayı",
"Job-specific settings": "İşe özel ayarlar",
"Known asset paths": "Bilinen varlık yolları",
"Language": "Dil",
"Language will change next time the submitter is opened": "Dil, submitter bir sonraki açılışında değişecektir",
"Load Bundle": "Paket yükle",
+ "Load from Disk": "Diskten yükle",
"Log in": "Oturum aç",
"Log in to AWS Deadline Cloud": "AWS Deadline Cloud'da oturum aç",
"Logging you in...": "Oturum açılıyor...",
@@ -106,6 +108,7 @@
"Require all input paths exist": "Tüm giriş yollarının var olmasını gerektir",
"Run on all available worker hosts": "Tüm kullanılabilir çalışan ana bilgisayarlarda çalıştır",
"Run on worker hosts that meet the following requirements": "Aşağıdaki gereksinimleri karşılayan çalışan ana bilgisayarlarda çalıştır",
+ "Save to Disk": "Diske kaydet",
"Saved the submission as a job bundle:\n{path}": "Gönderim iş paketi olarak kaydedildi:\n{path}",
"Scratch space": "Geçici alan",
"Set max worker count": "Maksimum çalışan sayısını ayarla",
@@ -142,4 +145,4 @@
"{profile} - You are logged out.": "{profile} - Oturumunuz kapatıldı.",
"{profile} doesn't have access permissions to submit a job.": "{profile} bir iş göndermek için erişim izinlerine sahip değil.",
"{submitter} job submission": "{submitter} iş gönderimi"
-}
\ No newline at end of file
+}
diff --git a/src/deadline/client/ui/translations/locales/zh_CN.json b/src/deadline/client/ui/translations/locales/zh_CN.json
index 0b5a7e8e5..eaa9f69d1 100644
--- a/src/deadline/client/ui/translations/locales/zh_CN.json
+++ b/src/deadline/client/ui/translations/locales/zh_CN.json
@@ -1,10 +1,6 @@
{
"...": "...",
"A configuration error was received while accessing credentials for the profile '{profile}'.": "访问配置文件 '{profile}' 的凭证时收到配置错误。",
- "AWS Deadline Cloud API is not accessible. Check your authentication status.": "无法访问 AWS Deadline Cloud API。请检查您的身份验证状态。",
- "AWS Deadline Cloud submission": "AWS Deadline Cloud 提交",
- "AWS Deadline Cloud workstation configuration": "AWS Deadline Cloud 工作站配置",
- "AWS profile": "AWS 配置文件",
"About": "关于",
"Application Restart Required": "需要重新启动应用程序",
"Add": "添加",
@@ -22,14 +18,19 @@
"Attach input files": "附加输入文件",
"Attribute name": "属性名称",
"Auto accept prompt defaults": "自动接受默认值",
- "CPU architecture": "CPU 架构",
+ "AWS Deadline Cloud API is not accessible. Check your authentication status.": "无法访问 AWS Deadline Cloud API。请检查您的身份验证状态。",
+ "AWS Deadline Cloud submission": "AWS Deadline Cloud 提交",
+ "AWS Deadline Cloud workstation configuration": "AWS Deadline Cloud 工作站配置",
+ "AWS profile": "AWS 配置文件",
"Cancel": "取消",
"Canceling submission...": "正在取消提交...",
"Cannot submit job:\n\n\u2022 {issues}": "无法提交作业:\n\n\\u2022 {issues}",
"Choose job bundle directory": "选择作业捆绑包目录",
"Close": "关闭",
+ "Config File": "配置文件",
"Conflict resolution option": "冲突解决选项",
"Copy": "复制",
+ "CPU architecture": "CPU 架构",
"Current: {current_version} -> New: {latest_version}": "当前: {current_version} -> 新版本: {latest_version}",
"Current logging level": "当前日志记录级别",
"Custom host requirements": "自定义主机要求",
@@ -50,28 +51,29 @@
"Failed to log in to AWS Deadline Cloud:
{error}": "登录 AWS Deadline Cloud 失败:
{error}",
"Farm": "服务器农场",
"Farm settings": "服务器农场设置",
- "GPU memory (GiB)": "GPU 内存 (GiB)",
- "GPUs": "GPU",
"General settings": "常规设置",
"General submission settings": "常规提交设置",
"Global settings": "全局设置",
+ "GPU memory (GiB)": "GPU 内存 (GiB)",
+ "GPUs": "GPU",
"Hardware requirements": "硬件要求",
"Hashing progress": "哈希进度",
"Help": "帮助",
"Host requirements": "主机要求",
"Initial state": "初始状态",
"Issue With Profile Configuration": "配置文件配置问题",
- "Job Properties": "作业属性",
- "Job Submission Confirmation": "作业提交确认",
"Job attachments": "作业附件",
"Job attachments filesystem options": "作业附件文件系统选项",
"Job history directory": "作业历史记录目录",
+ "Job Properties": "作业属性",
+ "Job Submission Confirmation": "作业提交确认",
"Job submission confirmation": "作业提交确认",
"Job-specific settings": "作业特定设置",
"Known asset paths": "已知资产路径",
"Language": "语言",
"Language will change next time the submitter is opened": "语言将在下次打开提交器时更改",
"Load Bundle": "加载捆绑包",
+ "Load from Disk": "从磁盘加载",
"Log in": "登录",
"Log in to AWS Deadline Cloud": "登录 AWS Deadline Cloud",
"Logging you in...": "正在登录...",
@@ -106,6 +108,7 @@
"Require all input paths exist": "要求所有输入路径存在",
"Run on all available worker hosts": "在所有可用的工作主机上运行",
"Run on worker hosts that meet the following requirements": "在满足以下要求的工作主机上运行",
+ "Save to Disk": "保存到磁盘",
"Saved the submission as a job bundle:\n{path}": "已将提交保存为作业捆绑包:\n{path}",
"Scratch space": "临时空间",
"Set max worker count": "设置最大工作线程数",
@@ -142,4 +145,4 @@
"{profile} - You are logged out.": "{profile} - 您已登出。",
"{profile} doesn't have access permissions to submit a job.": "{profile} 没有提交作业的访问权限。",
"{submitter} job submission": "{submitter} 作业提交"
-}
\ No newline at end of file
+}
diff --git a/src/deadline/client/ui/translations/locales/zh_TW.json b/src/deadline/client/ui/translations/locales/zh_TW.json
index a44253123..cd717e03b 100644
--- a/src/deadline/client/ui/translations/locales/zh_TW.json
+++ b/src/deadline/client/ui/translations/locales/zh_TW.json
@@ -1,10 +1,6 @@
{
"...": "...",
"A configuration error was received while accessing credentials for the profile '{profile}'.": "存取設定檔 '{profile}' 的憑證時收到組態錯誤。",
- "AWS Deadline Cloud API is not accessible. Check your authentication status.": "無法存取 AWS Deadline Cloud API。請檢查您的身分驗證狀態。",
- "AWS Deadline Cloud submission": "AWS Deadline Cloud 提交",
- "AWS Deadline Cloud workstation configuration": "AWS Deadline Cloud 工作站組態",
- "AWS profile": "AWS 設定檔",
"About": "關於",
"Application Restart Required": "需要重新啟動應用程式",
"Add": "新增",
@@ -22,14 +18,19 @@
"Attach input files": "附加輸入檔案",
"Attribute name": "屬性名稱",
"Auto accept prompt defaults": "自動接受預設值",
- "CPU architecture": "CPU 架構",
+ "AWS Deadline Cloud API is not accessible. Check your authentication status.": "無法存取 AWS Deadline Cloud API。請檢查您的身分驗證狀態。",
+ "AWS Deadline Cloud submission": "AWS Deadline Cloud 提交",
+ "AWS Deadline Cloud workstation configuration": "AWS Deadline Cloud 工作站組態",
+ "AWS profile": "AWS 設定檔",
"Cancel": "取消",
"Canceling submission...": "正在取消提交...",
"Cannot submit job:\n\n\u2022 {issues}": "無法提交任務:\n\n\\u2022 {issues}",
"Choose job bundle directory": "選擇任務套件目錄",
"Close": "關閉",
+ "Config File": "設定檔",
"Conflict resolution option": "衝突解決選項",
"Copy": "複製",
+ "CPU architecture": "CPU 架構",
"Current: {current_version} -> New: {latest_version}": "目前: {current_version} -> 新版本: {latest_version}",
"Current logging level": "目前的日誌記錄層級",
"Custom host requirements": "自訂主機需求",
@@ -50,28 +51,29 @@
"Failed to log in to AWS Deadline Cloud:
{error}": "登入 AWS Deadline Cloud 失敗:
{error}",
"Farm": "伺服器陣列",
"Farm settings": "伺服器陣列設定",
- "GPU memory (GiB)": "GPU 記憶體 (GiB)",
- "GPUs": "GPU",
"General settings": "一般設定",
"General submission settings": "一般提交設定",
"Global settings": "全域設定",
+ "GPU memory (GiB)": "GPU 記憶體 (GiB)",
+ "GPUs": "GPU",
"Hardware requirements": "硬體需求",
"Hashing progress": "雜湊進度",
"Help": "說明",
"Host requirements": "主機需求",
"Initial state": "初始狀態",
"Issue With Profile Configuration": "設定檔組態問題",
- "Job Properties": "任務屬性",
- "Job Submission Confirmation": "任務提交確認",
"Job attachments": "任務附件",
"Job attachments filesystem options": "任務附件檔案系統選項",
"Job history directory": "任務歷史記錄目錄",
+ "Job Properties": "任務屬性",
+ "Job Submission Confirmation": "任務提交確認",
"Job submission confirmation": "任務提交確認",
"Job-specific settings": "任務特定設定",
"Known asset paths": "已知資產路徑",
"Language": "語言",
"Language will change next time the submitter is opened": "語言將在下次開啟提交器時變更",
"Load Bundle": "載入套件",
+ "Load from Disk": "從磁碟載入",
"Log in": "登入",
"Log in to AWS Deadline Cloud": "登入 AWS Deadline Cloud",
"Logging you in...": "正在登入...",
@@ -106,6 +108,7 @@
"Require all input paths exist": "要求所有輸入路徑存在",
"Run on all available worker hosts": "在所有可用的工作者主機上執行",
"Run on worker hosts that meet the following requirements": "在符合下列需求的工作者主機上執行",
+ "Save to Disk": "儲存到磁碟",
"Saved the submission as a job bundle:\n{path}": "已將提交儲存為任務套件:\n{path}",
"Scratch space": "暫存空間",
"Set max worker count": "設定工作程序數上限",
@@ -142,4 +145,4 @@
"{profile} - You are logged out.": "{profile} - 您已登出。",
"{profile} doesn't have access permissions to submit a job.": "{profile} 沒有提交任務的存取許可。",
"{submitter} job submission": "{submitter} 任務提交"
-}
\ No newline at end of file
+}
diff --git a/src/deadline/client/ui/widgets/shared_job_settings_tab.py b/src/deadline/client/ui/widgets/shared_job_settings_tab.py
index fb873fb47..5ff04151c 100644
--- a/src/deadline/client/ui/widgets/shared_job_settings_tab.py
+++ b/src/deadline/client/ui/widgets/shared_job_settings_tab.py
@@ -7,6 +7,7 @@
from __future__ import annotations
import sys
+from configparser import ConfigParser
from typing import Any, Dict, List, Optional
from qtpy.QtCore import Qt, Signal # type: ignore
@@ -58,11 +59,14 @@ def __init__(
*,
initial_settings: Any,
initial_shared_parameter_values: dict[str, Any],
+ config: Optional[ConfigParser] = None,
parent: Optional[QWidget] = None,
):
super().__init__(parent=parent)
layout = QVBoxLayout(self)
+ self._config = config
+
# This is a dictionary {: } containing values to
# override the queue parameter defaults.
self.initial_shared_parameter_values = initial_shared_parameter_values
@@ -72,7 +76,9 @@ def __init__(
)
layout.addWidget(self.shared_job_properties_box)
- self.deadline_cloud_settings_box = DeadlineCloudSettingsWidget(parent=self)
+ self.deadline_cloud_settings_box = DeadlineCloudSettingsWidget(
+ config=self._config, parent=self
+ )
layout.addWidget(self.deadline_cloud_settings_box)
self.queue_parameters_box = OpenJDParametersWidget(
@@ -84,12 +90,14 @@ def __init__(
)
# Track current farm/queue IDs for change detection
- self.farm_id = get_setting("defaults.farm_id")
- self.queue_id = get_setting("defaults.queue_id")
+ self.farm_id = get_setting("defaults.farm_id", config=self._config)
+ self.queue_id = get_setting("defaults.queue_id", config=self._config)
self.__valid_queue = False
# Connect to the controller for queue parameters
self._controller = DeadlineUIController.getInstance()
+ if self._config is not None:
+ self._controller.set_config(self._config)
self._controller.queue_parameters_updated.connect(
self._handle_queue_parameters_update, Qt.QueuedConnection
)
@@ -109,6 +117,14 @@ def __init__(
if name.startswith("deadline:"):
self.set_parameter_value({"name": name, "value": value})
+ def set_session_config(self, session_config: ConfigParser) -> None:
+ """Update the session config used by this widget and its children."""
+ self._config = session_config
+ self._controller.set_config(session_config)
+ self.deadline_cloud_settings_box._config = session_config
+ self.deadline_cloud_settings_box.farm_box._config = session_config
+ self.deadline_cloud_settings_box.queue_box._config = session_config
+
def refresh_ui(self, job_settings: Any, load_new_bundle: bool = False):
# Refresh the job settings in the UI
self.shared_job_properties_box.refresh_ui(job_settings)
@@ -127,8 +143,8 @@ def refresh_queue_parameters(self, load_new_bundle: bool = False):
"""
If the default queue id or job bundle has changed, refresh the queue parameters.
"""
- farm_id = get_setting("defaults.farm_id")
- queue_id = get_setting("defaults.queue_id")
+ farm_id = get_setting("defaults.farm_id", config=self._config)
+ queue_id = get_setting("defaults.queue_id", config=self._config)
if not farm_id or not queue_id:
self.queue_parameters_box.rebuild_ui(async_loading_state="")
return # If the user has not selected a farm or queue ID, don't try to load
@@ -164,8 +180,8 @@ def _start_load_queue_parameters(self):
"""
Triggers the controller to load queue parameters.
"""
- self.farm_id = farm_id = get_setting("defaults.farm_id")
- self.queue_id = queue_id = get_setting("defaults.queue_id")
+ self.farm_id = farm_id = get_setting("defaults.farm_id", config=self._config)
+ self.queue_id = queue_id = get_setting("defaults.queue_id", config=self._config)
if not self.farm_id or not self.queue_id:
# If the user has not selected a farm or queue ID, don't bother loading
return
@@ -442,8 +458,14 @@ class DeadlineCloudSettingsWidget(QGroupBox):
UI component for the Deadline Cloud settings.
"""
- def __init__(self, *, parent: Optional[QWidget] = None):
+ def __init__(
+ self,
+ *,
+ config: Optional[ConfigParser] = None,
+ parent: Optional[QWidget] = None,
+ ):
super().__init__(tr("Deadline Cloud settings"), parent=parent)
+ self._config = config
self.deadline_settings: Dict[str, Any] = {"counter": -1}
self.layout = QFormLayout(self)
self.layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
@@ -460,11 +482,11 @@ def _build_ui(self):
Build the UI for the Deadline settings
"""
self.farm_box_label = QLabel(tr("Farm"))
- self.farm_box = DeadlineFarmDisplay()
+ self.farm_box = DeadlineFarmDisplay(config=self._config)
self.layout.addRow(self.farm_box_label, self.farm_box)
self.queue_box_label = QLabel(tr("Queue"))
- self.queue_box = DeadlineQueueDisplay()
+ self.queue_box = DeadlineQueueDisplay(config=self._config)
self.layout.addRow(self.queue_box_label, self.queue_box)
def refresh_setting_controls(self, deadline_authorized):
@@ -499,12 +521,20 @@ class _DeadlineNamedResourceDisplay(QWidget):
# provides (operation_name, BaseException)
background_exception = Signal(str, BaseException)
- def __init__(self, *, resource_name, setting_name, parent: Optional[QWidget] = None):
+ def __init__(
+ self,
+ *,
+ resource_name,
+ setting_name,
+ config: Optional[ConfigParser] = None,
+ parent: Optional[QWidget] = None,
+ ):
super().__init__(parent=parent)
self.resource_name = resource_name
self.setting_name = setting_name
- self.item_id = get_setting(self.setting_name)
+ self._config = config
+ self.item_id = get_setting(self.setting_name, config=self._config)
self.item_name = ""
self.item_description = ""
@@ -544,7 +574,7 @@ def refresh(self, deadline_authorized):
api.check_deadline_available, for example from
an AWS Deadline Cloud Status Widget.
"""
- resource_id = get_setting(self.setting_name)
+ resource_id = get_setting(self.setting_name, config=self._config)
if resource_id != self.item_id or not self.item_name:
self.item_id = resource_id
self.item_name = ""
@@ -577,11 +607,13 @@ def _handle_item_update(self, item: tuple) -> None:
class DeadlineFarmDisplay(_DeadlineNamedResourceDisplay):
- def __init__(self, *, parent: Optional[QWidget] = None):
- super().__init__(resource_name="Farm", setting_name="defaults.farm_id", parent=parent)
+ def __init__(self, *, config: Optional[ConfigParser] = None, parent: Optional[QWidget] = None):
+ super().__init__(
+ resource_name="Farm", setting_name="defaults.farm_id", config=config, parent=parent
+ )
def get_item(self):
- farm_id = get_setting(self.setting_name)
+ farm_id = get_setting(self.setting_name, config=self._config)
if farm_id:
deadline = api.get_boto3_client("deadline")
response = deadline.get_farm(farmId=farm_id)
@@ -591,12 +623,14 @@ def get_item(self):
class DeadlineQueueDisplay(_DeadlineNamedResourceDisplay):
- def __init__(self, *, parent: Optional[QWidget] = None):
- super().__init__(resource_name="Queue", setting_name="defaults.queue_id", parent=parent)
+ def __init__(self, *, config: Optional[ConfigParser] = None, parent: Optional[QWidget] = None):
+ super().__init__(
+ resource_name="Queue", setting_name="defaults.queue_id", config=config, parent=parent
+ )
def get_item(self):
- farm_id = get_setting("defaults.farm_id")
- queue_id = get_setting(self.setting_name)
+ farm_id = get_setting("defaults.farm_id", config=self._config)
+ queue_id = get_setting(self.setting_name, config=self._config)
if farm_id and queue_id:
deadline = api.get_boto3_client("deadline")
response = deadline.get_queue(farmId=farm_id, queueId=queue_id)
@@ -610,17 +644,18 @@ class DeadlineStorageProfileNameDisplay(_DeadlineNamedResourceDisplay):
MAC_OS = "Macos"
LINUX_OS = "Linux"
- def __init__(self, *, parent: Optional[QWidget] = None):
+ def __init__(self, *, config: Optional[ConfigParser] = None, parent: Optional[QWidget] = None):
super().__init__(
resource_name="Storage profile name",
setting_name="settings.storage_profile_id",
+ config=config,
parent=parent,
)
def get_item(self):
- farm_id = get_setting("defaults.farm_id")
- queue_id = get_setting("defaults.queue_id")
- storage_profile_id = get_setting(self.setting_name)
+ farm_id = get_setting("defaults.farm_id", config=self._config)
+ queue_id = get_setting("defaults.queue_id", config=self._config)
+ storage_profile_id = get_setting(self.setting_name, config=self._config)
if farm_id and queue_id and storage_profile_id:
deadline = api.get_boto3_client("deadline")
diff --git a/test/squish/suite_deadline_gui/shared/scripts/workstation_config_helpers.py b/test/squish/suite_deadline_gui/shared/scripts/workstation_config_helpers.py
index 324d039e0..a09a60bf5 100644
--- a/test/squish/suite_deadline_gui/shared/scripts/workstation_config_helpers.py
+++ b/test/squish/suite_deadline_gui/shared/scripts/workstation_config_helpers.py
@@ -78,15 +78,6 @@ def open_settings_dialogue():
)
-def hit_apply_button():
- test.log("Hitting `Apply` button to apply selected settings.")
- # hit 'Apply' button
- squish.clickButton(
- squish.waitForObject(workstation_config_locators.deadlinedialog_apply_button)
- )
- test.log("Settings have been applied.")
-
-
def set_farm_name(farm_name: str):
# open Default farm drop down menu
squish.mouseClick(
diff --git a/test/squish/suite_deadline_gui/shared/scripts/workstation_config_locators.py b/test/squish/suite_deadline_gui/shared/scripts/workstation_config_locators.py
index fedea6846..eaf72159d 100644
--- a/test/squish/suite_deadline_gui/shared/scripts/workstation_config_locators.py
+++ b/test/squish/suite_deadline_gui/shared/scripts/workstation_config_locators.py
@@ -15,14 +15,6 @@
"visible": 1,
"window": deadline_config_dialog,
}
-# Apply button
-deadlinedialog_apply_button = {
- "text": "Apply",
- "type": "QPushButton",
- "unnamed": 1,
- "visible": 1,
- "window": deadline_config_dialog,
-}
# global settings box
deadlinedialog_globalsettings_box = {
diff --git a/test/unit/deadline_client/cli/test_cli_bundle_submit.py b/test/unit/deadline_client/cli/test_cli_bundle_submit.py
index fd3fb0b03..abb0e00a7 100644
--- a/test/unit/deadline_client/cli/test_cli_bundle_submit.py
+++ b/test/unit/deadline_client/cli/test_cli_bundle_submit.py
@@ -119,7 +119,7 @@ def test_cli_bundle_explicit_parameters(fresh_deadline_config, temp_job_bundle_d
],
)
- session_mock.assert_called_with(profile_name="NonDefaultProfileName", botocore_session=ANY)
+ session_mock.assert_any_call(profile_name="NonDefaultProfileName", botocore_session=ANY)
session_mock().client().create_job.assert_called_once_with(
farmId=MOCK_FARM_ID,
queueId=MOCK_QUEUE_ID,
diff --git a/test/unit/deadline_client/cli/test_cli_farm.py b/test/unit/deadline_client/cli/test_cli_farm.py
index 682a3d18d..9d783af47 100644
--- a/test/unit/deadline_client/cli/test_cli_farm.py
+++ b/test/unit/deadline_client/cli/test_cli_farm.py
@@ -71,7 +71,7 @@ def test_cli_farm_list_override_profile(fresh_deadline_config):
result = runner.invoke(main, ["farm", "list", "--profile", "NonDefaultProfileName"])
assert result.exit_code == 0
- session_mock.assert_called_with(profile_name="NonDefaultProfileName", botocore_session=ANY)
+ session_mock.assert_any_call(profile_name="NonDefaultProfileName", botocore_session=ANY)
session_mock().client().list_farms.assert_called_once_with()
diff --git a/test/unit/deadline_client/cli/test_cli_queue.py b/test/unit/deadline_client/cli/test_cli_queue.py
index 31f10bcfb..75d10cf54 100644
--- a/test/unit/deadline_client/cli/test_cli_queue.py
+++ b/test/unit/deadline_client/cli/test_cli_queue.py
@@ -81,7 +81,7 @@ def test_cli_queue_list_override_profile(fresh_deadline_config):
result = runner.invoke(main, ["queue", "list", "--profile", "NonDefaultProfileName"])
assert result.exit_code == 0
- session_mock.assert_called_with(profile_name="NonDefaultProfileName", botocore_session=ANY)
+ session_mock.assert_any_call(profile_name="NonDefaultProfileName", botocore_session=ANY)
session_mock().client().list_queues.assert_called_once_with(farmId="farm-overriddenid")
diff --git a/test/unit/deadline_client/config/test_config_file.py b/test/unit/deadline_client/config/test_config_file.py
index 5f3f7f14d..591fa53de 100644
--- a/test/unit/deadline_client/config/test_config_file.py
+++ b/test/unit/deadline_client/config/test_config_file.py
@@ -371,3 +371,72 @@ def test_posix_config_file_permissions(fresh_deadline_config) -> None:
config_file.set_setting("defaults.aws_profile_name", "goodguyprofile")
assert config_file_path.stat().st_mode & 0o777 == 0o600
+
+
+def test_persist_job_id_with_default_config(fresh_deadline_config) -> None:
+ """persist_job_id writes the job ID under the specified farm/queue section."""
+ config.set_setting("defaults.farm_id", "farm-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
+ config.set_setting("defaults.queue_id", "queue-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
+
+ config_file.persist_job_id(
+ "job-ccccccccccccccccccccccccccccccc",
+ profile="(default)",
+ farm_id="farm-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ queue_id="queue-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+ )
+
+ assert config.get_setting("defaults.job_id") == "job-ccccccccccccccccccccccccccccccc"
+
+
+def test_persist_job_id_with_overridden_farm_queue(fresh_deadline_config) -> None:
+ """persist_job_id writes the job ID under the overridden farm/queue section
+ without changing the on-disk farm/queue defaults."""
+ # Set up on-disk defaults
+ config.set_setting("defaults.farm_id", "farm-disk1111111111111111111111111")
+ config.set_setting("defaults.queue_id", "queue-disk111111111111111111111111")
+
+ # Persist job ID under a different farm/queue
+ config_file.persist_job_id(
+ "job-submitted111111111111111111111",
+ profile="(default)",
+ farm_id="farm-cli11111111111111111111111111",
+ queue_id="queue-cli1111111111111111111111111",
+ )
+
+ # On-disk farm/queue defaults are unchanged
+ assert config.get_setting("defaults.farm_id") == "farm-disk1111111111111111111111111"
+ assert config.get_setting("defaults.queue_id") == "queue-disk111111111111111111111111"
+
+ # Job ID under the default farm/queue section is NOT set
+ assert config.get_setting("defaults.job_id") == ""
+
+ # Job ID IS set when we read using the overridden farm/queue
+ from configparser import ConfigParser
+
+ session = ConfigParser()
+ session.read_dict(config_file.read_config())
+ config_file.set_setting(
+ "defaults.farm_id", "farm-cli11111111111111111111111111", config=session
+ )
+ config_file.set_setting(
+ "defaults.queue_id", "queue-cli1111111111111111111111111", config=session
+ )
+ assert (
+ config_file.get_setting("defaults.job_id", config=session)
+ == "job-submitted111111111111111111111"
+ )
+
+
+def test_persist_job_id_creates_section_if_missing(fresh_deadline_config) -> None:
+ """persist_job_id creates the section if it doesn't exist yet."""
+ config_file.persist_job_id(
+ "job-newwwwwwwwwwwwwwwwwwwwwwwwwwwwww",
+ profile="(default)",
+ farm_id="farm-newwwwwwwwwwwwwwwwwwwwwwwwwwww",
+ queue_id="queue-newwwwwwwwwwwwwwwwwwwwwwwwwww",
+ )
+
+ # Re-read from disk to confirm it was persisted
+ fresh = config_file.read_config()
+ section = "profile-(default) farm-newwwwwwwwwwwwwwwwwwwwwwwwwwww queue-newwwwwwwwwwwwwwwwwwwwwwwwwww defaults"
+ assert fresh.get(section, "job_id") == "job-newwwwwwwwwwwwwwwwwwwwwwwwwwwwww"
diff --git a/test/unit/deadline_client/ui/test_session_config.py b/test/unit/deadline_client/ui/test_session_config.py
new file mode 100644
index 000000000..c34b032ab
--- /dev/null
+++ b/test/unit/deadline_client/ui/test_session_config.py
@@ -0,0 +1,527 @@
+# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+"""
+Tests for in-memory session config behavior.
+
+Validates that CLI options (--farm-id, --queue-id, etc.) create a session config
+that is used by the submission dialog without affecting the on-disk config, and
+that the settings dialog correctly separates session vs workstation config.
+"""
+
+from configparser import ConfigParser
+from typing import Callable, Generator, Optional
+from unittest.mock import MagicMock, PropertyMock, patch
+
+import pytest
+
+try:
+ from qtpy.QtWidgets import QWidget
+
+ from deadline.client.config import config_file
+ from deadline.client.cli._common import _apply_cli_options_to_config
+ from deadline.client.ui.dataclasses import JobBundleSettings
+ from deadline.client.job_bundle.submission import AssetReferences
+ from deadline.client.ui.dialogs.submit_job_to_deadline_dialog import (
+ SubmitJobToDeadlineDialog,
+ )
+ from deadline.client.ui.dialogs.deadline_config_dialog import (
+ DeadlineConfigDialog,
+ DeadlineWorkstationConfigWidget,
+ )
+except ImportError:
+ pytest.importorskip("deadline.client.ui.dialogs.submit_job_to_deadline_dialog")
+
+DISK_FARM_ID = "farm-disk11111111111111111111111111"
+DISK_QUEUE_ID = "queue-disk1111111111111111111111111"
+CLI_FARM_ID = "farm-cli111111111111111111111111111"
+CLI_QUEUE_ID = "queue-cli11111111111111111111111111"
+
+
+class MockJobSettingsWidget(QWidget):
+ def __init__(
+ self, initial_settings: Optional[JobBundleSettings] = None, parent: Optional[QWidget] = None
+ ):
+ super().__init__(parent)
+ self.initial_settings = initial_settings
+ self.parameter_changed = MagicMock()
+ self.parameter_changed.connect = MagicMock()
+
+ def update_settings(self, settings: JobBundleSettings) -> None:
+ pass
+
+
+def _mock_auth_instance() -> MagicMock:
+ """Create a mock DeadlineAuthenticationStatus that won't trigger real API calls."""
+ inst = MagicMock()
+ inst.api_availability = False
+ inst.api_availability_changed = MagicMock()
+ inst.api_availability_changed.connect = MagicMock()
+ inst.deadline_config_changed = MagicMock()
+ inst.deadline_config_changed.connect = MagicMock()
+ return inst
+
+
+@pytest.fixture
+def mock_auth_status() -> MagicMock:
+ mock_instance = MagicMock()
+ type(mock_instance).api_availability = PropertyMock(return_value=None)
+ type(mock_instance).creds_source = PropertyMock(return_value=None)
+ type(mock_instance).auth_status = PropertyMock(return_value=None)
+ mock_instance.config = ConfigParser()
+ mock_instance.api_availability_changed = MagicMock()
+ mock_instance.api_availability_changed.connect = MagicMock()
+ mock_instance.creds_source_changed = MagicMock()
+ mock_instance.creds_source_changed.connect = MagicMock()
+ mock_instance.auth_status_changed = MagicMock()
+ mock_instance.auth_status_changed.connect = MagicMock()
+ return mock_instance
+
+
+@pytest.fixture
+def disk_config() -> ConfigParser:
+ """A ConfigParser representing the on-disk config with known farm/queue."""
+ config = ConfigParser()
+ config_file.set_setting("defaults.farm_id", DISK_FARM_ID, config=config)
+ config_file.set_setting("defaults.queue_id", DISK_QUEUE_ID, config=config)
+ return config
+
+
+@pytest.fixture
+def submit_dialog_factory(qtbot, mock_auth_status) -> Callable:
+ """Factory that creates a SubmitJobToDeadlineDialog with mocked auth."""
+
+ def _create(session_config: Optional[ConfigParser] = None) -> "SubmitJobToDeadlineDialog":
+ import deadline.client.ui.deadline_authentication_status as auth_module
+
+ auth_module._deadline_authentication_status = mock_auth_status
+
+ with patch(
+ "deadline.client.ui.widgets.deadline_authentication_status_widget.DeadlineAuthenticationStatus.getInstance",
+ return_value=mock_auth_status,
+ ), patch(
+ "deadline.client.ui.dialogs.submit_job_to_deadline_dialog.DeadlineAuthenticationStatus.getInstance",
+ return_value=mock_auth_status,
+ ):
+ dialog = SubmitJobToDeadlineDialog(
+ job_setup_widget_type=MockJobSettingsWidget,
+ initial_job_settings=JobBundleSettings(),
+ initial_shared_parameter_values={},
+ auto_detected_attachments=AssetReferences(),
+ attachments=AssetReferences(),
+ on_create_job_bundle_callback=MagicMock(),
+ session_config=session_config,
+ )
+ qtbot.addWidget(dialog)
+ return dialog
+
+ return _create
+
+
+@pytest.fixture
+def config_dialog_mocks() -> Generator[MagicMock, None, None]:
+ """Patches config_file, boto3.Session, and auth for DeadlineConfigDialog tests.
+
+ Yields mock_config_file after setting up sane defaults.
+ Callers can override mock_config_file.read_config.return_value etc. as needed.
+ """
+ with patch(
+ "deadline.client.ui.dialogs.deadline_config_dialog.DeadlineAuthenticationStatus.getInstance"
+ ) as mock_auth, patch(
+ "deadline.client.ui.dialogs.deadline_config_dialog.boto3.Session"
+ ) as mock_boto3_session, patch(
+ "deadline.client.ui.dialogs.deadline_config_dialog.config_file"
+ ) as mock_config_file:
+ mock_auth.return_value = _mock_auth_instance()
+ mock_boto3_session.return_value._session.full_config = {"profiles": {}}
+ mock_config_file.read_config.return_value = ConfigParser()
+ mock_config_file.get_setting = config_file.get_setting
+ mock_config_file.set_setting = config_file.set_setting
+ mock_config_file.write_config = MagicMock()
+ yield mock_config_file
+
+
+class TestApplyCliOptionsToConfig:
+ """Tests for _apply_cli_options_to_config override and copy behavior."""
+
+ def test_returns_config_when_no_options(self) -> None:
+ """When no CLI options are provided, returns a copy of the disk config."""
+ result = _apply_cli_options_to_config()
+ assert isinstance(result, ConfigParser)
+
+ def test_overrides_farm_id(self, disk_config) -> None:
+ session = ConfigParser()
+ session.read_dict(disk_config)
+ result = _apply_cli_options_to_config(config=session, farm_id=CLI_FARM_ID)
+ assert config_file.get_setting("defaults.farm_id", config=result) == CLI_FARM_ID
+ # queue_id is scoped to the farm, so changing farm clears the queue
+ assert config_file.get_setting("defaults.queue_id", config=result) == ""
+
+ def test_overrides_queue_id(self, disk_config) -> None:
+ session = ConfigParser()
+ session.read_dict(disk_config)
+ result = _apply_cli_options_to_config(config=session, queue_id=CLI_QUEUE_ID)
+ assert config_file.get_setting("defaults.queue_id", config=result) == CLI_QUEUE_ID
+ assert config_file.get_setting("defaults.farm_id", config=result) == DISK_FARM_ID
+
+ def test_overrides_both(self, disk_config) -> None:
+ session = ConfigParser()
+ session.read_dict(disk_config)
+ result = _apply_cli_options_to_config(
+ config=session, farm_id=CLI_FARM_ID, queue_id=CLI_QUEUE_ID
+ )
+ assert config_file.get_setting("defaults.farm_id", config=result) == CLI_FARM_ID
+ assert config_file.get_setting("defaults.queue_id", config=result) == CLI_QUEUE_ID
+
+ def test_does_not_mutate_disk_cache(self) -> None:
+ """When no config is passed, reads from disk and returns a fresh copy."""
+ with patch.object(config_file, "read_config") as mock_read:
+ base = ConfigParser()
+ config_file.set_setting("defaults.farm_id", DISK_FARM_ID, config=base)
+ mock_read.return_value = base
+
+ result = _apply_cli_options_to_config(farm_id=CLI_FARM_ID)
+
+ assert config_file.get_setting("defaults.farm_id", config=result) == CLI_FARM_ID
+ assert result is not base
+ assert config_file.get_setting("defaults.farm_id", config=base) == DISK_FARM_ID
+
+ def test_does_not_mutate_provided_config(self) -> None:
+ """When a config is explicitly passed, it is copied — the original is not mutated."""
+ original = ConfigParser()
+ config_file.set_setting("defaults.queue_id", DISK_QUEUE_ID, config=original)
+
+ result = _apply_cli_options_to_config(config=original, queue_id=CLI_QUEUE_ID)
+
+ assert result is not original
+ assert config_file.get_setting("defaults.queue_id", config=result) == CLI_QUEUE_ID
+ assert config_file.get_setting("defaults.queue_id", config=original) == DISK_QUEUE_ID
+
+
+class TestSubmitDialogSessionConfig:
+ """Tests for session config propagation through SubmitJobToDeadlineDialog."""
+
+ def test_no_session_config_when_no_cli_args(self, submit_dialog_factory) -> None:
+ dialog = submit_dialog_factory(session_config=None)
+ assert dialog._session_config is None
+
+ def test_session_config_stored_when_provided(self, submit_dialog_factory, disk_config) -> None:
+ session = _apply_cli_options_to_config(config=disk_config, queue_id=CLI_QUEUE_ID)
+ dialog = submit_dialog_factory(session_config=session)
+
+ assert dialog._session_config is not None
+ assert (
+ config_file.get_setting("defaults.queue_id", config=dialog._session_config)
+ == CLI_QUEUE_ID
+ )
+
+ def test_session_config_passed_to_shared_job_settings(
+ self, submit_dialog_factory, disk_config
+ ) -> None:
+ session = _apply_cli_options_to_config(config=disk_config, farm_id=CLI_FARM_ID)
+ dialog = submit_dialog_factory(session_config=session)
+
+ assert dialog.shared_job_settings._config is session
+
+ def test_session_config_passed_to_display_widgets(
+ self, submit_dialog_factory, disk_config
+ ) -> None:
+ """Session config should reach farm_box and queue_box through the widget hierarchy."""
+ session = _apply_cli_options_to_config(config=disk_config, farm_id=CLI_FARM_ID)
+ dialog = submit_dialog_factory(session_config=session)
+
+ settings_box = dialog.shared_job_settings.deadline_cloud_settings_box
+ assert settings_box._config is session
+ assert settings_box.farm_box._config is session
+ assert settings_box.queue_box._config is session
+
+
+@pytest.mark.usefixtures("fresh_deadline_config")
+class TestWorkstationConfigWidget:
+ """Tests for DeadlineWorkstationConfigWidget session vs disk config behavior."""
+
+ def test_no_session_config_by_default(self, config_dialog_mocks, qtbot) -> None:
+ widget = DeadlineWorkstationConfigWidget()
+ qtbot.addWidget(widget)
+ assert widget._session_config is None
+
+ def test_has_session_config_when_provided(self, config_dialog_mocks, qtbot) -> None:
+ session = ConfigParser()
+ config_file.set_setting("defaults.queue_id", CLI_QUEUE_ID, config=session)
+
+ widget = DeadlineWorkstationConfigWidget(session_config=session)
+ qtbot.addWidget(widget)
+ assert widget._session_config is not None
+
+ def test_refresh_uses_session_config(self, config_dialog_mocks, qtbot) -> None:
+ """In session mode, refresh() bases the config on the session config, not disk."""
+ mock_cf = config_dialog_mocks
+ disk = ConfigParser()
+ config_file.set_setting("defaults.farm_id", DISK_FARM_ID, config=disk)
+ mock_cf.read_config.return_value = disk
+
+ session = ConfigParser()
+ config_file.set_setting("defaults.farm_id", CLI_FARM_ID, config=session)
+
+ widget = DeadlineWorkstationConfigWidget(session_config=session)
+ qtbot.addWidget(widget)
+ assert config_file.get_setting("defaults.farm_id", config=widget.config) == CLI_FARM_ID
+
+ def test_refresh_uses_disk_config_without_session(self, config_dialog_mocks, qtbot) -> None:
+ """Without session config, refresh() bases the config on disk."""
+ mock_cf = config_dialog_mocks
+ disk = ConfigParser()
+ config_file.set_setting("defaults.farm_id", DISK_FARM_ID, config=disk)
+ mock_cf.read_config.return_value = disk
+
+ widget = DeadlineWorkstationConfigWidget()
+ qtbot.addWidget(widget)
+ assert config_file.get_setting("defaults.farm_id", config=widget.config) == DISK_FARM_ID
+
+ def test_apply_with_session_does_not_write_to_disk(self, config_dialog_mocks, qtbot) -> None:
+ """With session config, apply() mutates in-memory without writing to disk."""
+ mock_cf = config_dialog_mocks
+ disk = ConfigParser()
+ config_file.set_setting("defaults.farm_id", DISK_FARM_ID, config=disk)
+ mock_cf.read_config.return_value = disk
+
+ session = ConfigParser()
+ config_file.set_setting("defaults.farm_id", CLI_FARM_ID, config=session)
+
+ widget = DeadlineWorkstationConfigWidget(session_config=session)
+ qtbot.addWidget(widget)
+
+ widget.changes["defaults.farm_id"] = "farm-newvalue1111111111111111111111"
+ assert widget.apply() is True
+ mock_cf.write_config.assert_not_called()
+ assert (
+ config_file.get_setting("defaults.farm_id", config=session)
+ == "farm-newvalue1111111111111111111111"
+ )
+
+ def test_apply_without_session_writes_to_disk(self, config_dialog_mocks, qtbot) -> None:
+ """Without session config, apply() writes to disk."""
+ mock_cf = config_dialog_mocks
+ disk = ConfigParser()
+ config_file.set_setting("defaults.farm_id", DISK_FARM_ID, config=disk)
+ mock_cf.read_config.return_value = disk
+
+ widget = DeadlineWorkstationConfigWidget()
+ qtbot.addWidget(widget)
+
+ widget.changes["defaults.farm_id"] = "farm-newvalue1111111111111111111111"
+ assert widget.apply() is True
+ mock_cf.write_config.assert_called_once()
+
+
+@pytest.mark.usefixtures("fresh_deadline_config")
+class TestConfigDialog:
+ """Tests for DeadlineConfigDialog button bar and disk I/O actions."""
+
+ def test_disk_actions_disabled_without_session(self, config_dialog_mocks, qtbot) -> None:
+ """Without session_config, disk actions start disabled."""
+ dialog = DeadlineConfigDialog()
+ qtbot.addWidget(dialog)
+ assert not dialog._load_from_disk_action.isEnabled()
+
+ def test_config_file_button_exists_with_session(self, config_dialog_mocks, qtbot) -> None:
+ """With session_config, Config File button is present."""
+ session = ConfigParser()
+ dialog = DeadlineConfigDialog(session_config=session)
+ qtbot.addWidget(dialog)
+ assert dialog._config_file_button is not None
+
+ def test_session_mode_does_not_write_to_disk(self, config_dialog_mocks, qtbot) -> None:
+ """With session_config, the widget is in session mode and doesn't write to disk."""
+ mock_cf = config_dialog_mocks
+ disk = ConfigParser()
+ config_file.set_setting("defaults.farm_id", DISK_FARM_ID, config=disk)
+ mock_cf.read_config.return_value = disk
+
+ session = ConfigParser()
+ session.read_dict(disk)
+
+ dialog = DeadlineConfigDialog(session_config=session)
+ qtbot.addWidget(dialog)
+
+ assert dialog.config_box._session_config is not None
+ mock_cf.write_config.assert_not_called()
+
+ def test_session_config_shows_cli_override(self, config_dialog_mocks, qtbot) -> None:
+ """CLI --queue-id override is visible in the dialog's config."""
+ mock_cf = config_dialog_mocks
+ disk = ConfigParser()
+ config_file.set_setting("defaults.farm_id", DISK_FARM_ID, config=disk)
+ config_file.set_setting("defaults.queue_id", DISK_QUEUE_ID, config=disk)
+ mock_cf.read_config.return_value = disk
+
+ session = ConfigParser()
+ session.read_dict(disk)
+ config_file.set_setting("defaults.queue_id", CLI_QUEUE_ID, config=session)
+
+ dialog = DeadlineConfigDialog(session_config=session)
+ qtbot.addWidget(dialog)
+
+ assert (
+ config_file.get_setting("defaults.queue_id", config=dialog.config_box.config)
+ == CLI_QUEUE_ID
+ )
+
+ def test_save_to_disk_with_session_writes_config(self, config_dialog_mocks, qtbot) -> None:
+ """Save to Disk applies pending changes to session config and writes to disk."""
+ mock_cf = config_dialog_mocks
+ disk = ConfigParser()
+ config_file.set_setting("defaults.farm_id", DISK_FARM_ID, config=disk)
+ mock_cf.read_config.return_value = disk
+
+ session = ConfigParser()
+ session.read_dict(disk)
+ dialog = DeadlineConfigDialog(session_config=session)
+ qtbot.addWidget(dialog)
+
+ dialog.config_box.changes["defaults.farm_id"] = CLI_FARM_ID
+ dialog._on_save_to_disk()
+
+ mock_cf.write_config.assert_called_once()
+ assert config_file.get_setting("defaults.farm_id", config=session) == CLI_FARM_ID
+ assert not dialog.config_box.changes
+
+ def test_save_to_disk_without_session_writes_config(self, config_dialog_mocks, qtbot) -> None:
+ """Save to Disk reads from disk, applies changes, and writes back."""
+ mock_cf = config_dialog_mocks
+ disk = ConfigParser()
+ config_file.set_setting("defaults.farm_id", DISK_FARM_ID, config=disk)
+ mock_cf.read_config.return_value = disk
+
+ dialog = DeadlineConfigDialog()
+ qtbot.addWidget(dialog)
+
+ dialog.config_box.changes["defaults.farm_id"] = CLI_FARM_ID
+ dialog._on_save_to_disk()
+
+ mock_cf.write_config.assert_called_once()
+ assert not dialog.config_box.changes
+
+ def test_save_to_disk_blocks_on_not_valid(self, config_dialog_mocks, qtbot) -> None:
+ """Save to Disk should not write when changes contain NOT_VALID_MARKER."""
+ mock_cf = config_dialog_mocks
+
+ dialog = DeadlineConfigDialog()
+ qtbot.addWidget(dialog)
+
+ dialog.config_box.changes["defaults.aws_profile_name"] = "[NOT VALID] bad"
+ with patch("deadline.client.ui.dialogs.deadline_config_dialog.QMessageBox.warning"):
+ dialog._on_save_to_disk()
+
+ mock_cf.write_config.assert_not_called()
+
+ def test_load_from_disk_with_session_replaces_session(self, config_dialog_mocks, qtbot) -> None:
+ """Load from Disk replaces the session config with disk contents."""
+ mock_cf = config_dialog_mocks
+ disk = ConfigParser()
+ config_file.set_setting("defaults.farm_id", DISK_FARM_ID, config=disk)
+ mock_cf.read_config.return_value = disk
+
+ session = ConfigParser()
+ session.read_dict(disk)
+ config_file.set_setting("defaults.farm_id", CLI_FARM_ID, config=session)
+
+ dialog = DeadlineConfigDialog(session_config=session)
+ qtbot.addWidget(dialog)
+
+ dialog._on_load_from_disk()
+
+ assert (
+ config_file.get_setting("defaults.farm_id", config=dialog._session_config)
+ == DISK_FARM_ID
+ )
+
+ def test_load_from_disk_without_session_clears_changes(
+ self, config_dialog_mocks, qtbot
+ ) -> None:
+ """Load from Disk discards pending changes."""
+ mock_cf = config_dialog_mocks
+ disk = ConfigParser()
+ config_file.set_setting("defaults.farm_id", DISK_FARM_ID, config=disk)
+ mock_cf.read_config.return_value = disk
+
+ dialog = DeadlineConfigDialog()
+ qtbot.addWidget(dialog)
+
+ dialog.config_box.changes["defaults.farm_id"] = CLI_FARM_ID
+ dialog._on_load_from_disk()
+
+ assert not dialog.config_box.changes
+
+ def test_accept_blocks_on_not_valid_with_session(self, config_dialog_mocks, qtbot) -> None:
+ """Ok should not close the dialog when session-mode changes are not valid."""
+ session = ConfigParser()
+ dialog = DeadlineConfigDialog(session_config=session)
+ qtbot.addWidget(dialog)
+
+ dialog.config_box.changes["defaults.aws_profile_name"] = "[NOT VALID] bad"
+ with patch("deadline.client.ui.dialogs.deadline_config_dialog.QMessageBox.warning"):
+ with patch.object(type(dialog).mro()[1], "accept") as mock_super_accept:
+ dialog.accept()
+ mock_super_accept.assert_not_called()
+
+ def test_accept_blocks_on_not_valid_without_session(self, config_dialog_mocks, qtbot) -> None:
+ """Ok should not close the dialog when workstation-mode changes are not valid."""
+ mock_cf = config_dialog_mocks
+
+ dialog = DeadlineConfigDialog()
+ qtbot.addWidget(dialog)
+
+ dialog.config_box.changes["defaults.aws_profile_name"] = "[NOT VALID] bad"
+ with patch("deadline.client.ui.dialogs.deadline_config_dialog.QMessageBox.warning"):
+ with patch.object(type(dialog).mro()[1], "accept") as mock_super_accept:
+ dialog.accept()
+ mock_super_accept.assert_not_called()
+ mock_cf.write_config.assert_not_called()
+
+
+class TestSubmitDialogPersistJobId:
+ """Tests for job ID persistence after successful GUI submission."""
+
+ def test_persist_job_id_uses_session_config_values(
+ self, submit_dialog_factory, disk_config
+ ) -> None:
+ """After submission, persist_job_id is called with profile/farm/queue from session config."""
+ session = _apply_cli_options_to_config(
+ config=disk_config, farm_id=CLI_FARM_ID, queue_id=CLI_QUEUE_ID
+ )
+ dialog = submit_dialog_factory(session_config=session)
+
+ with patch(
+ "deadline.client.ui.dialogs.submit_job_to_deadline_dialog.persist_job_id"
+ ) as mock_persist:
+ dialog._submission_succeeded_signal_receiver("job-test1111111111111111111111111")
+
+ mock_persist.assert_called_once_with(
+ "job-test1111111111111111111111111",
+ profile=config_file.get_setting("defaults.aws_profile_name", config=session),
+ farm_id=CLI_FARM_ID,
+ queue_id=CLI_QUEUE_ID,
+ )
+
+
+class TestSetSessionConfigPropagation:
+ """Tests that set_session_config propagates to child display widgets."""
+
+ def test_set_session_config_propagates_to_child_widgets(
+ self, submit_dialog_factory, disk_config
+ ) -> None:
+ """set_session_config should update config on farm_box and queue_box."""
+ session = _apply_cli_options_to_config(config=disk_config, farm_id=CLI_FARM_ID)
+ dialog = submit_dialog_factory(session_config=session)
+
+ new_session = ConfigParser()
+ new_session.read_dict(disk_config)
+ config_file.set_setting(
+ "defaults.farm_id", "farm-new111111111111111111111111111", config=new_session
+ )
+
+ dialog.shared_job_settings.set_session_config(new_session)
+
+ settings_box = dialog.shared_job_settings.deadline_cloud_settings_box
+ assert settings_box._config is new_session
+ assert settings_box.farm_box._config is new_session
+ assert settings_box.queue_box._config is new_session