We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
SettingsManager._load not working with Field(default_factory=...)
## 🐛 Bug
Ran into a [test failure](https://github.com/napari/napari/pull/2695/checks?check_run_id=2925027700#step:7:250) on #2695 that looks to be a bug in the Settings Manager.

The preferred way to add a default mutable container to a pydantic model is to use `default_factor`, but currently, if you do this, you'll get an error:

```python
class PluginsSettings(BaseNapariSettings):
    # this works, and is used in a couple places in the settings
    # but is not how an empty mutable object should be declared
    some_setting: Dict[str, str] = Field(dict())

    # this is the preferred way to create an empty mutable
    # but it doesn't work:
    some_other_setting: Dict[str, str] = Field(default_factory=dict)
```

traceback:

```pytb
napari/utils/settings/_manager.py:262: in get_settings
    SETTINGS = SettingsManager(config_path=config_path)
napari/utils/settings/_manager.py:90: in __init__
    self._load()
napari/utils/settings/_manager.py:186: in _load
    self._defaults[section] = setting(**_section_defaults)
pydantic/env_settings.py:36: in pydantic.env_settings.BaseSettings.__init__
    ???
napari/utils/events/evented_model.py:129: in __init__
    super().__init__(**kwargs)
pydantic/main.py:406: in pydantic.main.BaseModel.__init__
    ???
E   pydantic.error_wrappers.ValidationError: 2 validation errors for PluginsSettings
E   extension2reader
E     none is not an allowed value (type=type_error.none.not_allowed)
E   extension2writer
E     none is not an allowed value (type=type_error.none.not_allowed)

```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `napari/utils/settings/_manager.py`
Content:
```
1 """Settings management.
2 """
3 
4 import json
5 import os
6 import warnings
7 from pathlib import Path
8 from typing import Any, Dict, List, Optional, Union
9 
10 from appdirs import user_config_dir
11 from yaml import safe_dump, safe_load
12 
13 from ...utils.translations import trans
14 from .._base import _APPAUTHOR, _APPNAME, _FILENAME
15 from ._defaults import CORE_SETTINGS as CORE_SETTINGS
16 from ._defaults import (
17     AppearanceSettings,
18     ApplicationSettings,
19     BaseNapariSettings,
20     ExperimentalSettings,
21     PluginsSettings,
22     ShortcutsSettings,
23 )
24 
25 
26 class _SettingsMixin:
27     appearance: AppearanceSettings
28     application: ApplicationSettings
29     plugins: PluginsSettings
30     shortcuts: ShortcutsSettings
31     experimental: ExperimentalSettings
32 
33 
34 class SettingsManager(_SettingsMixin):
35     """
36     Napari settings manager using evented SettingsModels.
37 
38     This provides the presistence layer for the application settings.
39 
40     Parameters
41     ----------
42     config_path : str, optional
43         Provide the base folder to store napari configuration. Default is None,
44         which will point to user config provided by `appdirs`.
45     save_to_disk : bool, optional
46         Persist settings on disk. Default is True.
47 
48     Notes
49     -----
50     The settings manager will create a new user configuration folder which is
51     provided by `appdirs` in a cross platform manner. On the first startup a
52     new configuration file will be created using the default values defined by
53     the `CORE_SETTINGS` models.
54 
55     If a configuration file is found in the specified location, it will be
56     loaded by the `_load` method. On configuration load the following checks
57     are performed:
58 
59     - If invalid sections are found, these will be removed from the file.
60     - If invalid keys are found within a valid section, these will be removed
61       from the file.
62     - If invalid values are found within valid sections and valid keys, these
63       will be replaced by the default value provided by `CORE_SETTINGS`
64       models.
65     """
66 
67     _FILENAME = _FILENAME
68     _APPNAME = _APPNAME
69     _APPAUTHOR = _APPAUTHOR
70 
71     def __init__(
72         self,
73         config_path: Optional[Path] = None,
74         save_to_disk: bool = True,
75     ):
76         self._config_path = (
77             Path(user_config_dir(self._APPNAME, self._APPAUTHOR))
78             if config_path is None
79             else Path(config_path)
80         )
81         self._save_to_disk = save_to_disk
82         self._settings: Dict[str, BaseNapariSettings] = {}
83         self._defaults: Dict[str, BaseNapariSettings] = {}
84         self._plugins: List[str] = []
85         self._env_settings: Dict[str, Any] = {}
86 
87         if not self._config_path.is_dir():
88             os.makedirs(self._config_path)
89 
90         self._load()
91 
92     def __getattr__(self, attr):
93         if attr in self._settings:
94             return self._settings[attr]
95 
96     def __dir__(self):
97         """Add setting keys to make tab completion works."""
98         return list(super().__dir__()) + list(self._settings)
99 
100     def __str__(self):
101         return safe_dump(self._to_dict(safe=True))
102 
103     def _remove_default(self, settings_data):
104         """
105         Attempt to convert self to dict and to remove any default values from the configuration
106         """
107         for section, values in settings_data.items():
108             if section not in self._defaults:
109                 continue
110 
111             default_values = self._defaults[section].dict()
112             for k, v in list(values.items()):
113                 if default_values.get(k, None) == v:
114                     del values[k]
115 
116         return settings_data
117 
118     def _to_dict(self, safe: bool = False) -> dict:
119         """Convert the settings to a dictionary."""
120         data = {}
121         for section, model in self._settings.items():
122             if safe:
123                 # We roundtrip to keep string objects (like SchemaVersion)
124                 # yaml representable
125                 data[section] = json.loads(model.json())
126             else:
127                 data[section] = model.dict()
128 
129         return data
130 
131     def _save(self):
132         """Save configuration to disk."""
133         if self._save_to_disk:
134             path = self.path / self._FILENAME
135 
136             if self._env_settings:
137                 # If using environment variables do not save them in the
138                 # `settings.yaml` file. We just delete any keys loaded
139                 # as environment variables
140                 data = self._to_dict(safe=True)
141                 for section, env_data in self._env_settings.items():
142                     for k, v in env_data.items():
143                         del data[section][k]
144             else:
145                 data = self._to_dict(safe=True)
146 
147             with open(path, "w") as fh:
148                 fh.write(safe_dump(self._remove_default(data)))
149 
150     def _load(self):
151         """Read configuration from disk."""
152         path = self.path / self._FILENAME
153 
154         if path.is_file():
155             try:
156                 with open(path) as fh:
157                     data = safe_load(fh.read()) or {}
158             except Exception as err:
159                 warnings.warn(
160                     trans._(
161                         "The content of the napari settings file could not be read\n\nThe default settings will be used and the content of the file will be replaced the next time settings are changed.\n\nError:\n{err}",
162                         deferred=True,
163                         err=err,
164                     )
165                 )
166                 data = {}
167 
168             # Load data once and save it in the base class
169             BaseNapariSettings._LOADED_DATA = data
170 
171         for setting in CORE_SETTINGS:
172             section = setting.schema().get("section", None)
173             if section is None:
174                 raise ValueError(
175                     trans._(
176                         "Settings model {setting!r} must provide a `section` in the `schemas_extra`",
177                         deferred=True,
178                         setting=setting,
179                     )
180                 )
181 
182             _section_defaults = {}
183             for option, option_data in setting.schema()["properties"].items():
184                 _section_defaults[option] = option_data.get("default", None)
185 
186             self._defaults[section] = setting(**_section_defaults)
187             model = setting()
188             model.events.connect(lambda x: self._save())
189             self._settings[section] = model
190             self._env_settings[section] = getattr(
191                 model.__config__, "_env_settings"
192             )(model)
193 
194         self._save()
195 
196     @property
197     def path(self):
198         return self._config_path
199 
200     def reset(self):
201         """Reset settings to default values."""
202         for section in self._settings:
203             for key, default_value in self._defaults[section].dict().items():
204                 setattr(self._settings[section], key, default_value)
205 
206         self._save()
207 
208     def schemas(self) -> dict:
209         """Return the json schema for each of the settings model."""
210         schemas = {}
211         for section, settings in self._settings.items():
212             schemas[section] = {
213                 "json_schema": settings.schema_json(),
214                 "model": settings,
215             }
216 
217         return schemas
218 
219     def register_plugin(self, plugin):
220         """Register plugin settings with the settings manager.
221 
222         Parameters
223         ----------
224         plugin
225             The napari plugin that may or may not provide settings.
226         """
227         self._plugins.append(plugin)
228 
229 
230 class _SettingsProxy(_SettingsMixin):
231     """Backwards compatibility layer."""
232 
233     def __getattribute__(self, name) -> Any:
234         return getattr(get_settings(), name)
235 
236 
237 SETTINGS: Union[SettingsManager, _SettingsProxy] = _SettingsProxy()
238 
239 
240 def get_settings(path: Optional[Union[Path, str]] = None) -> SettingsManager:
241     """
242     Get settings for a given path.
243 
244     Parameters
245     ----------
246     path: Path, optional
247         The path to read/write the settings from.
248 
249     Returns
250     -------
251     SettingsManager
252         The settings manager.
253 
254     Notes
255     -----
256     The path can only be set once per session.
257     """
258     global SETTINGS
259 
260     if isinstance(SETTINGS, _SettingsProxy):
261         config_path = Path(path).resolve() if path else None
262         SETTINGS = SettingsManager(config_path=config_path)
263     elif path is not None:
264         import inspect
265 
266         curframe = inspect.currentframe()
267         calframe = inspect.getouterframes(curframe, 2)
268         raise Exception(
269             trans._(
270                 "The path can only be set once per session. Settings called from {calframe[1][3]}",
271                 deferred=True,
272                 calframe=calframe,
273             )
274         )
275 
276     return SETTINGS
277
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/napari/utils/settings/_manager.py b/napari/utils/settings/_manager.py
--- a/napari/utils/settings/_manager.py
+++ b/napari/utils/settings/_manager.py
@@ -179,11 +179,9 @@
                     )
                 )
 
-            _section_defaults = {}
-            for option, option_data in setting.schema()["properties"].items():
-                _section_defaults[option] = option_data.get("default", None)
-
-            self._defaults[section] = setting(**_section_defaults)
+            self._defaults[section] = setting(
+                **{k: v.get_default() for k, v in setting.__fields__.items()}
+            )
             model = setting()
             model.events.connect(lambda x: self._save())
             self._settings[section] = model

{"golden_diff": "diff --git a/napari/utils/settings/_manager.py b/napari/utils/settings/_manager.py\n--- a/napari/utils/settings/_manager.py\n+++ b/napari/utils/settings/_manager.py\n@@ -179,11 +179,9 @@\n                     )\n                 )\n \n-            _section_defaults = {}\n-            for option, option_data in setting.schema()[\"properties\"].items():\n-                _section_defaults[option] = option_data.get(\"default\", None)\n-\n-            self._defaults[section] = setting(**_section_defaults)\n+            self._defaults[section] = setting(\n+                **{k: v.get_default() for k, v in setting.__fields__.items()}\n+            )\n             model = setting()\n             model.events.connect(lambda x: self._save())\n             self._settings[section] = model\n", "issue": "SettingsManager._load not working with Field(default_factory=...)\n## \ud83d\udc1b Bug\r\nRan into a [test failure](https://github.com/napari/napari/pull/2695/checks?check_run_id=2925027700#step:7:250) on #2695 that looks to be a bug in the Settings Manager.\r\n\r\nThe preferred way to add a default mutable container to a pydantic model is to use `default_factor`, but currently, if you do this, you'll get an error:\r\n\r\n```python\r\nclass PluginsSettings(BaseNapariSettings):\r\n    # this works, and is used in a couple places in the settings\r\n    # but is not how an empty mutable object should be declared\r\n    some_setting: Dict[str, str] = Field(dict())\r\n\r\n    # this is the preferred way to create an empty mutable\r\n    # but it doesn't work:\r\n    some_other_setting: Dict[str, str] = Field(default_factory=dict)\r\n```\r\n\r\ntraceback:\r\n\r\n```pytb\r\nnapari/utils/settings/_manager.py:262: in get_settings\r\n    SETTINGS = SettingsManager(config_path=config_path)\r\nnapari/utils/settings/_manager.py:90: in __init__\r\n    self._load()\r\nnapari/utils/settings/_manager.py:186: in _load\r\n    self._defaults[section] = setting(**_section_defaults)\r\npydantic/env_settings.py:36: in pydantic.env_settings.BaseSettings.__init__\r\n    ???\r\nnapari/utils/events/evented_model.py:129: in __init__\r\n    super().__init__(**kwargs)\r\npydantic/main.py:406: in pydantic.main.BaseModel.__init__\r\n    ???\r\nE   pydantic.error_wrappers.ValidationError: 2 validation errors for PluginsSettings\r\nE   extension2reader\r\nE     none is not an allowed value (type=type_error.none.not_allowed)\r\nE   extension2writer\r\nE     none is not an allowed value (type=type_error.none.not_allowed)\r\n\r\n```\n", "before_files": [{"content": "\"\"\"Settings management.\n\"\"\"\n\nimport json\nimport os\nimport warnings\nfrom pathlib import Path\nfrom typing import Any, Dict, List, Optional, Union\n\nfrom appdirs import user_config_dir\nfrom yaml import safe_dump, safe_load\n\nfrom ...utils.translations import trans\nfrom .._base import _APPAUTHOR, _APPNAME, _FILENAME\nfrom ._defaults import CORE_SETTINGS as CORE_SETTINGS\nfrom ._defaults import (\n    AppearanceSettings,\n    ApplicationSettings,\n    BaseNapariSettings,\n    ExperimentalSettings,\n    PluginsSettings,\n    ShortcutsSettings,\n)\n\n\nclass _SettingsMixin:\n    appearance: AppearanceSettings\n    application: ApplicationSettings\n    plugins: PluginsSettings\n    shortcuts: ShortcutsSettings\n    experimental: ExperimentalSettings\n\n\nclass SettingsManager(_SettingsMixin):\n    \"\"\"\n    Napari settings manager using evented SettingsModels.\n\n    This provides the presistence layer for the application settings.\n\n    Parameters\n    ----------\n    config_path : str, optional\n        Provide the base folder to store napari configuration. Default is None,\n        which will point to user config provided by `appdirs`.\n    save_to_disk : bool, optional\n        Persist settings on disk. Default is True.\n\n    Notes\n    -----\n    The settings manager will create a new user configuration folder which is\n    provided by `appdirs` in a cross platform manner. On the first startup a\n    new configuration file will be created using the default values defined by\n    the `CORE_SETTINGS` models.\n\n    If a configuration file is found in the specified location, it will be\n    loaded by the `_load` method. On configuration load the following checks\n    are performed:\n\n    - If invalid sections are found, these will be removed from the file.\n    - If invalid keys are found within a valid section, these will be removed\n      from the file.\n    - If invalid values are found within valid sections and valid keys, these\n      will be replaced by the default value provided by `CORE_SETTINGS`\n      models.\n    \"\"\"\n\n    _FILENAME = _FILENAME\n    _APPNAME = _APPNAME\n    _APPAUTHOR = _APPAUTHOR\n\n    def __init__(\n        self,\n        config_path: Optional[Path] = None,\n        save_to_disk: bool = True,\n    ):\n        self._config_path = (\n            Path(user_config_dir(self._APPNAME, self._APPAUTHOR))\n            if config_path is None\n            else Path(config_path)\n        )\n        self._save_to_disk = save_to_disk\n        self._settings: Dict[str, BaseNapariSettings] = {}\n        self._defaults: Dict[str, BaseNapariSettings] = {}\n        self._plugins: List[str] = []\n        self._env_settings: Dict[str, Any] = {}\n\n        if not self._config_path.is_dir():\n            os.makedirs(self._config_path)\n\n        self._load()\n\n    def __getattr__(self, attr):\n        if attr in self._settings:\n            return self._settings[attr]\n\n    def __dir__(self):\n        \"\"\"Add setting keys to make tab completion works.\"\"\"\n        return list(super().__dir__()) + list(self._settings)\n\n    def __str__(self):\n        return safe_dump(self._to_dict(safe=True))\n\n    def _remove_default(self, settings_data):\n        \"\"\"\n        Attempt to convert self to dict and to remove any default values from the configuration\n        \"\"\"\n        for section, values in settings_data.items():\n            if section not in self._defaults:\n                continue\n\n            default_values = self._defaults[section].dict()\n            for k, v in list(values.items()):\n                if default_values.get(k, None) == v:\n                    del values[k]\n\n        return settings_data\n\n    def _to_dict(self, safe: bool = False) -> dict:\n        \"\"\"Convert the settings to a dictionary.\"\"\"\n        data = {}\n        for section, model in self._settings.items():\n            if safe:\n                # We roundtrip to keep string objects (like SchemaVersion)\n                # yaml representable\n                data[section] = json.loads(model.json())\n            else:\n                data[section] = model.dict()\n\n        return data\n\n    def _save(self):\n        \"\"\"Save configuration to disk.\"\"\"\n        if self._save_to_disk:\n            path = self.path / self._FILENAME\n\n            if self._env_settings:\n                # If using environment variables do not save them in the\n                # `settings.yaml` file. We just delete any keys loaded\n                # as environment variables\n                data = self._to_dict(safe=True)\n                for section, env_data in self._env_settings.items():\n                    for k, v in env_data.items():\n                        del data[section][k]\n            else:\n                data = self._to_dict(safe=True)\n\n            with open(path, \"w\") as fh:\n                fh.write(safe_dump(self._remove_default(data)))\n\n    def _load(self):\n        \"\"\"Read configuration from disk.\"\"\"\n        path = self.path / self._FILENAME\n\n        if path.is_file():\n            try:\n                with open(path) as fh:\n                    data = safe_load(fh.read()) or {}\n            except Exception as err:\n                warnings.warn(\n                    trans._(\n                        \"The content of the napari settings file could not be read\\n\\nThe default settings will be used and the content of the file will be replaced the next time settings are changed.\\n\\nError:\\n{err}\",\n                        deferred=True,\n                        err=err,\n                    )\n                )\n                data = {}\n\n            # Load data once and save it in the base class\n            BaseNapariSettings._LOADED_DATA = data\n\n        for setting in CORE_SETTINGS:\n            section = setting.schema().get(\"section\", None)\n            if section is None:\n                raise ValueError(\n                    trans._(\n                        \"Settings model {setting!r} must provide a `section` in the `schemas_extra`\",\n                        deferred=True,\n                        setting=setting,\n                    )\n                )\n\n            _section_defaults = {}\n            for option, option_data in setting.schema()[\"properties\"].items():\n                _section_defaults[option] = option_data.get(\"default\", None)\n\n            self._defaults[section] = setting(**_section_defaults)\n            model = setting()\n            model.events.connect(lambda x: self._save())\n            self._settings[section] = model\n            self._env_settings[section] = getattr(\n                model.__config__, \"_env_settings\"\n            )(model)\n\n        self._save()\n\n    @property\n    def path(self):\n        return self._config_path\n\n    def reset(self):\n        \"\"\"Reset settings to default values.\"\"\"\n        for section in self._settings:\n            for key, default_value in self._defaults[section].dict().items():\n                setattr(self._settings[section], key, default_value)\n\n        self._save()\n\n    def schemas(self) -> dict:\n        \"\"\"Return the json schema for each of the settings model.\"\"\"\n        schemas = {}\n        for section, settings in self._settings.items():\n            schemas[section] = {\n                \"json_schema\": settings.schema_json(),\n                \"model\": settings,\n            }\n\n        return schemas\n\n    def register_plugin(self, plugin):\n        \"\"\"Register plugin settings with the settings manager.\n\n        Parameters\n        ----------\n        plugin\n            The napari plugin that may or may not provide settings.\n        \"\"\"\n        self._plugins.append(plugin)\n\n\nclass _SettingsProxy(_SettingsMixin):\n    \"\"\"Backwards compatibility layer.\"\"\"\n\n    def __getattribute__(self, name) -> Any:\n        return getattr(get_settings(), name)\n\n\nSETTINGS: Union[SettingsManager, _SettingsProxy] = _SettingsProxy()\n\n\ndef get_settings(path: Optional[Union[Path, str]] = None) -> SettingsManager:\n    \"\"\"\n    Get settings for a given path.\n\n    Parameters\n    ----------\n    path: Path, optional\n        The path to read/write the settings from.\n\n    Returns\n    -------\n    SettingsManager\n        The settings manager.\n\n    Notes\n    -----\n    The path can only be set once per session.\n    \"\"\"\n    global SETTINGS\n\n    if isinstance(SETTINGS, _SettingsProxy):\n        config_path = Path(path).resolve() if path else None\n        SETTINGS = SettingsManager(config_path=config_path)\n    elif path is not None:\n        import inspect\n\n        curframe = inspect.currentframe()\n        calframe = inspect.getouterframes(curframe, 2)\n        raise Exception(\n            trans._(\n                \"The path can only be set once per session. Settings called from {calframe[1][3]}\",\n                deferred=True,\n                calframe=calframe,\n            )\n        )\n\n    return SETTINGS\n", "path": "napari/utils/settings/_manager.py"}], "after_files": [{"content": "\"\"\"Settings management.\n\"\"\"\n\nimport json\nimport os\nimport warnings\nfrom pathlib import Path\nfrom typing import Any, Dict, List, Optional, Union\n\nfrom appdirs import user_config_dir\nfrom yaml import safe_dump, safe_load\n\nfrom ...utils.translations import trans\nfrom .._base import _APPAUTHOR, _APPNAME, _FILENAME\nfrom ._defaults import CORE_SETTINGS as CORE_SETTINGS\nfrom ._defaults import (\n    AppearanceSettings,\n    ApplicationSettings,\n    BaseNapariSettings,\n    ExperimentalSettings,\n    PluginsSettings,\n    ShortcutsSettings,\n)\n\n\nclass _SettingsMixin:\n    appearance: AppearanceSettings\n    application: ApplicationSettings\n    plugins: PluginsSettings\n    shortcuts: ShortcutsSettings\n    experimental: ExperimentalSettings\n\n\nclass SettingsManager(_SettingsMixin):\n    \"\"\"\n    Napari settings manager using evented SettingsModels.\n\n    This provides the presistence layer for the application settings.\n\n    Parameters\n    ----------\n    config_path : str, optional\n        Provide the base folder to store napari configuration. Default is None,\n        which will point to user config provided by `appdirs`.\n    save_to_disk : bool, optional\n        Persist settings on disk. Default is True.\n\n    Notes\n    -----\n    The settings manager will create a new user configuration folder which is\n    provided by `appdirs` in a cross platform manner. On the first startup a\n    new configuration file will be created using the default values defined by\n    the `CORE_SETTINGS` models.\n\n    If a configuration file is found in the specified location, it will be\n    loaded by the `_load` method. On configuration load the following checks\n    are performed:\n\n    - If invalid sections are found, these will be removed from the file.\n    - If invalid keys are found within a valid section, these will be removed\n      from the file.\n    - If invalid values are found within valid sections and valid keys, these\n      will be replaced by the default value provided by `CORE_SETTINGS`\n      models.\n    \"\"\"\n\n    _FILENAME = _FILENAME\n    _APPNAME = _APPNAME\n    _APPAUTHOR = _APPAUTHOR\n\n    def __init__(\n        self,\n        config_path: Optional[Path] = None,\n        save_to_disk: bool = True,\n    ):\n        self._config_path = (\n            Path(user_config_dir(self._APPNAME, self._APPAUTHOR))\n            if config_path is None\n            else Path(config_path)\n        )\n        self._save_to_disk = save_to_disk\n        self._settings: Dict[str, BaseNapariSettings] = {}\n        self._defaults: Dict[str, BaseNapariSettings] = {}\n        self._plugins: List[str] = []\n        self._env_settings: Dict[str, Any] = {}\n\n        if not self._config_path.is_dir():\n            os.makedirs(self._config_path)\n\n        self._load()\n\n    def __getattr__(self, attr):\n        if attr in self._settings:\n            return self._settings[attr]\n\n    def __dir__(self):\n        \"\"\"Add setting keys to make tab completion works.\"\"\"\n        return list(super().__dir__()) + list(self._settings)\n\n    def __str__(self):\n        return safe_dump(self._to_dict(safe=True))\n\n    def _remove_default(self, settings_data):\n        \"\"\"\n        Attempt to convert self to dict and to remove any default values from the configuration\n        \"\"\"\n        for section, values in settings_data.items():\n            if section not in self._defaults:\n                continue\n\n            default_values = self._defaults[section].dict()\n            for k, v in list(values.items()):\n                if default_values.get(k, None) == v:\n                    del values[k]\n\n        return settings_data\n\n    def _to_dict(self, safe: bool = False) -> dict:\n        \"\"\"Convert the settings to a dictionary.\"\"\"\n        data = {}\n        for section, model in self._settings.items():\n            if safe:\n                # We roundtrip to keep string objects (like SchemaVersion)\n                # yaml representable\n                data[section] = json.loads(model.json())\n            else:\n                data[section] = model.dict()\n\n        return data\n\n    def _save(self):\n        \"\"\"Save configuration to disk.\"\"\"\n        if self._save_to_disk:\n            path = self.path / self._FILENAME\n\n            if self._env_settings:\n                # If using environment variables do not save them in the\n                # `settings.yaml` file. We just delete any keys loaded\n                # as environment variables\n                data = self._to_dict(safe=True)\n                for section, env_data in self._env_settings.items():\n                    for k, v in env_data.items():\n                        del data[section][k]\n            else:\n                data = self._to_dict(safe=True)\n\n            with open(path, \"w\") as fh:\n                fh.write(safe_dump(self._remove_default(data)))\n\n    def _load(self):\n        \"\"\"Read configuration from disk.\"\"\"\n        path = self.path / self._FILENAME\n\n        if path.is_file():\n            try:\n                with open(path) as fh:\n                    data = safe_load(fh.read()) or {}\n            except Exception as err:\n                warnings.warn(\n                    trans._(\n                        \"The content of the napari settings file could not be read\\n\\nThe default settings will be used and the content of the file will be replaced the next time settings are changed.\\n\\nError:\\n{err}\",\n                        deferred=True,\n                        err=err,\n                    )\n                )\n                data = {}\n\n            # Load data once and save it in the base class\n            BaseNapariSettings._LOADED_DATA = data\n\n        for setting in CORE_SETTINGS:\n            section = setting.schema().get(\"section\", None)\n            if section is None:\n                raise ValueError(\n                    trans._(\n                        \"Settings model {setting!r} must provide a `section` in the `schemas_extra`\",\n                        deferred=True,\n                        setting=setting,\n                    )\n                )\n\n            self._defaults[section] = setting(\n                **{k: v.get_default() for k, v in setting.__fields__.items()}\n            )\n            model = setting()\n            model.events.connect(lambda x: self._save())\n            self._settings[section] = model\n            self._env_settings[section] = getattr(\n                model.__config__, \"_env_settings\"\n            )(model)\n\n        self._save()\n\n    @property\n    def path(self):\n        return self._config_path\n\n    def reset(self):\n        \"\"\"Reset settings to default values.\"\"\"\n        for section in self._settings:\n            for key, default_value in self._defaults[section].dict().items():\n                setattr(self._settings[section], key, default_value)\n\n        self._save()\n\n    def schemas(self) -> dict:\n        \"\"\"Return the json schema for each of the settings model.\"\"\"\n        schemas = {}\n        for section, settings in self._settings.items():\n            schemas[section] = {\n                \"json_schema\": settings.schema_json(),\n                \"model\": settings,\n            }\n\n        return schemas\n\n    def register_plugin(self, plugin):\n        \"\"\"Register plugin settings with the settings manager.\n\n        Parameters\n        ----------\n        plugin\n            The napari plugin that may or may not provide settings.\n        \"\"\"\n        self._plugins.append(plugin)\n\n\nclass _SettingsProxy(_SettingsMixin):\n    \"\"\"Backwards compatibility layer.\"\"\"\n\n    def __getattribute__(self, name) -> Any:\n        return getattr(get_settings(), name)\n\n\nSETTINGS: Union[SettingsManager, _SettingsProxy] = _SettingsProxy()\n\n\ndef get_settings(path: Optional[Union[Path, str]] = None) -> SettingsManager:\n    \"\"\"\n    Get settings for a given path.\n\n    Parameters\n    ----------\n    path: Path, optional\n        The path to read/write the settings from.\n\n    Returns\n    -------\n    SettingsManager\n        The settings manager.\n\n    Notes\n    -----\n    The path can only be set once per session.\n    \"\"\"\n    global SETTINGS\n\n    if isinstance(SETTINGS, _SettingsProxy):\n        config_path = Path(path).resolve() if path else None\n        SETTINGS = SettingsManager(config_path=config_path)\n    elif path is not None:\n        import inspect\n\n        curframe = inspect.currentframe()\n        calframe = inspect.getouterframes(curframe, 2)\n        raise Exception(\n            trans._(\n                \"The path can only be set once per session. Settings called from {calframe[1][3]}\",\n                deferred=True,\n                calframe=calframe,\n            )\n        )\n\n    return SETTINGS\n", "path": "napari/utils/settings/_manager.py"}]}