Use TypeAdapter for Json serialization (#4183)

This commit is contained in:
Daniel Girtler 2026-02-03 22:27:51 +11:00 committed by GitHub
parent 0dddc7308d
commit 1d8352b466
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 15 additions and 63 deletions

View File

@ -77,7 +77,7 @@ class ArchConfig:
services: list[str] = field(default_factory=list) services: list[str] = field(default_factory=list)
custom_commands: list[str] = field(default_factory=list) custom_commands: list[str] = field(default_factory=list)
def unsafe_json(self) -> dict[str, Any]: def unsafe_config(self) -> dict[str, Any]:
config: dict[str, list[UserSerialization] | str | None] = {} config: dict[str, list[UserSerialization] | str | None] = {}
if self.auth_config: if self.auth_config:
@ -94,7 +94,7 @@ class ArchConfig:
return config return config
def safe_json(self) -> dict[str, Any]: def safe_config(self) -> dict[str, Any]:
config: Any = { config: Any = {
'version': self.version, 'version': self.version,
'script': self.script, 'script': self.script,

View File

@ -2,6 +2,9 @@ import json
import readline import readline
import stat import stat
from pathlib import Path from pathlib import Path
from typing import Any
from pydantic import TypeAdapter
from archinstall.lib.menu.helpers import Confirmation, Selection from archinstall.lib.menu.helpers import Confirmation, Selection
from archinstall.lib.translationhandler import tr from archinstall.lib.translationhandler import tr
@ -10,7 +13,6 @@ from archinstall.tui.ui.result import ResultType
from .args import ArchConfig from .args import ArchConfig
from .crypt import encrypt from .crypt import encrypt
from .general import JSON, UNSAFE_JSON
from .output import debug, logger, warn from .output import debug, logger, warn
from .utils.util import get_password, prompt_dir from .utils.util import get_password, prompt_dir
@ -40,12 +42,18 @@ class ConfigurationOutput:
return self._user_creds_file return self._user_creds_file
def user_config_to_json(self) -> str: def user_config_to_json(self) -> str:
out = self._config.safe_json() config = self._config.safe_config()
return json.dumps(out, indent=4, sort_keys=True, cls=JSON)
adapter = TypeAdapter(dict[str, Any])
python_dict = adapter.dump_python(config)
return json.dumps(python_dict, indent=4, sort_keys=True)
def user_credentials_to_json(self) -> str: def user_credentials_to_json(self) -> str:
out = self._config.unsafe_json() config = self._config.unsafe_config()
return json.dumps(out, indent=4, sort_keys=True, cls=UNSAFE_JSON)
adapter = TypeAdapter(dict[str, Any])
python_dict = adapter.dump_python(config)
return json.dumps(python_dict, indent=4, sort_keys=True)
def write_debug(self) -> None: def write_debug(self) -> None:
debug(' -- Chosen configuration --') debug(' -- Chosen configuration --')

View File

@ -1,9 +1,5 @@
import json
from datetime import date, datetime
from enum import Enum
from functools import lru_cache from functools import lru_cache
from pathlib import Path from pathlib import Path
from typing import Any, override
from archinstall.lib.packages.packages import check_package_upgrade from archinstall.lib.packages.packages import check_package_upgrade
@ -35,55 +31,3 @@ def running_from_host() -> bool:
""" """
is_host = not Path('/run/archiso').exists() is_host = not Path('/run/archiso').exists()
return is_host return is_host
def jsonify(obj: Any, safe: bool = True) -> Any:
"""
Converts objects into json.dumps() compatible nested dictionaries.
Setting safe to True skips dictionary keys starting with a bang (!)
"""
compatible_types = str, int, float, bool
if isinstance(obj, dict):
return {
key: jsonify(value, safe)
for key, value in obj.items()
if isinstance(key, compatible_types) and not (isinstance(key, str) and key.startswith('!') and safe)
}
if isinstance(obj, Enum):
return obj.value
if hasattr(obj, 'json'):
# json() is a friendly name for json-helper, it should return
# a dictionary representation of the object so that it can be
# processed by the json library.
return jsonify(obj.json(), safe)
if isinstance(obj, datetime | date):
return obj.isoformat()
if isinstance(obj, list | set | tuple):
return [jsonify(item, safe) for item in obj]
if isinstance(obj, Path):
return str(obj)
if hasattr(obj, '__dict__'):
return vars(obj)
return obj
class JSON(json.JSONEncoder, json.JSONDecoder):
"""
A safe JSON encoder that will omit private information in dicts (starting with !)
"""
@override
def encode(self, o: Any) -> str:
return super().encode(jsonify(o))
class UNSAFE_JSON(json.JSONEncoder, json.JSONDecoder):
"""
UNSAFE_JSON will call/encode and keep private information in dicts (starting with !)
"""
@override
def encode(self, o: Any) -> str:
return super().encode(jsonify(o, safe=False))