Read and write the user configuration
| 439 | |
| 440 | |
| 441 | class UserConfigManager(MarimoConfigReader): |
| 442 | """Read and write the user configuration""" |
| 443 | |
| 444 | def save_config( |
| 445 | self, config: MarimoConfig | PartialMarimoConfig |
| 446 | ) -> MarimoConfig: |
| 447 | import tomlkit |
| 448 | |
| 449 | config_path = self.get_config_path() |
| 450 | LOGGER.info("Saving user configuration to %s", config_path) |
| 451 | # Remove the secret placeholders from the incoming config |
| 452 | config = remove_secret_placeholders(config) |
| 453 | # Merge the current config with the new config |
| 454 | current_config = self._load_config() |
| 455 | merged = merge_config(current_config, config) |
| 456 | # None-as-delete: any key whose merged value is None (typically because |
| 457 | # the incoming config explicitly sent null) is removed from disk. Lets |
| 458 | # the UI clear optional scalars (e.g. ai.max_tokens) without a separate |
| 459 | # delete primitive. |
| 460 | _drop_none_values(cast(dict[str, Any], merged)) |
| 461 | |
| 462 | with open(config_path, "w", encoding="utf-8") as f: |
| 463 | tomlkit.dump(merged, f, sort_keys=True) |
| 464 | |
| 465 | return merge_default_config(merged) |
| 466 | |
| 467 | def save_config_if_missing(self) -> None: |
| 468 | try: |
| 469 | config_path = self.get_config_path() |
| 470 | if not os.path.exists(config_path): |
| 471 | self.save_config(DEFAULT_CONFIG) |
| 472 | except Exception as e: |
| 473 | LOGGER.warning("Failed to save config: %s", e) |
| 474 | |
| 475 | def get_config(self, *, hide_secrets: bool = True) -> MarimoConfig: |
| 476 | current_config = self._load_config() |
| 477 | if hide_secrets: |
| 478 | return mask_secrets(current_config) |
| 479 | return current_config |
| 480 | |
| 481 | def get_config_path(self) -> str: |
| 482 | return get_or_create_user_config_path() |
| 483 | |
| 484 | def _load_config(self) -> MarimoConfig: |
| 485 | """ |
| 486 | Load configuration, taking into account user config file, if any. |
| 487 | """ |
| 488 | try: |
| 489 | path = self.get_config_path() |
| 490 | except OSError as e: |
| 491 | path = None |
| 492 | LOGGER.warning( |
| 493 | "Encountered error when searching for config: %s", str(e) |
| 494 | ) |
| 495 | |
| 496 | if path is not None: |
| 497 | LOGGER.debug("Using config at %s", path) |
| 498 | try: |
no outgoing calls
searching dependent graphs…