Coverage for configzen/formats/std_plist.py: 45%
35 statements
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-15 02:42 +0000
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-15 02:42 +0000
1"""`configzen.formats.std_plist`: The Plist data format."""
3from __future__ import annotations 1abcdefgh
5from plistlib import PlistFormat, dump, load 1abcdefgh
6from typing import IO, TYPE_CHECKING, Any, ClassVar 1abcdefgh
8from runtime_generics import runtime_generic 1abcdefgh
10from configzen.data import BinaryDataFormat, DataFormatOptions 1abcdefgh
12if TYPE_CHECKING: 1abcdefgh
13 from collections.abc import MutableMapping
15 from typing_extensions import Unpack
17 from configzen.data import Data
20__all__ = ( 1abcdefgh
21 "PlistDataFormat",
22 "PlistOptions",
23)
26class PlistOptions(DataFormatOptions, total=False): 1abcdefgh
27 """Prototype of the allowed options for the Plist data format."""
29 fmt: PlistFormat 1abcdefgh
30 dict_type: type[MutableMapping[str, Any]] 1abcdefgh
31 sort_keys: bool 1abcdefgh
32 skipkeys: bool 1abcdefgh
35@runtime_generic 1abcdefgh
36class PlistDataFormat(BinaryDataFormat[PlistOptions]): 1abcdefgh
37 """The Plist data format."""
39 option_name: ClassVar[str] = "plist" 1abcdefgh
41 # Subclass and override for global effect.
42 plist_options: PlistOptions = PlistOptions() 1abcdefgh
44 default_extension: ClassVar[str] = "plist" 1abcdefgh
46 def configure(self, **options: Unpack[PlistOptions]) -> None: 1abcdefgh
47 """For the documentation of the options, see the PlistOptions class."""
48 if "fmt" not in options:
49 options["fmt"] = self.plist_options.get("fmt", PlistFormat.FMT_XML)
50 if "dict_type" not in options:
51 options["dict_type"] = self.plist_options.get(
52 "dict_type",
53 dict,
54 ) # type: ignore[typeddict-item]
55 if "sort_keys" not in options:
56 # configzen focuses on preserving the original structure,
57 # so we don't sort by default.
58 options["sort_keys"] = self.plist_options.get("sort_keys", False)
59 if "skipkeys" not in options:
60 options["skipkeys"] = self.plist_options.get("skipkeys", False)
61 self.plist_options = options
63 def load(self, stream: IO[bytes]) -> Data: 1abcdefgh
64 """Load the data from the given stream."""
65 dict_class: type[MutableMapping[str, Any]] = self.plist_options["dict_type"]
66 document = (
67 load(
68 stream,
69 fmt=self.plist_options["fmt"],
70 dict_type=dict_class,
71 )
72 or dict_class()
73 )
74 if not isinstance(document, dict_class):
75 msg = (
76 f"Expected a {dict_class.__name__} mapping, "
77 f"but got {type(document).__name__} instead."
78 )
79 raise TypeError(msg)
80 return document
82 def dump(self, data: Data, stream: IO[bytes]) -> None: 1abcdefgh
83 """Dump the given data to the stream."""
84 dump(
85 data,
86 stream,
87 fmt=self.plist_options["fmt"],
88 sort_keys=self.plist_options["sort_keys"],
89 skipkeys=self.plist_options["skipkeys"],
90 )