99from configparser import ConfigParser , MissingSectionHeaderError
1010from dataclasses import dataclass
1111from fnmatch import fnmatch
12+ from pathlib import Path
1213from types import TracebackType
1314from typing import (
1415 Any ,
1819 Iterator ,
1920 KeysView ,
2021 Literal ,
21- Optional ,
2222 TextIO ,
23- Union ,
2423)
2524
2625from defusedxml import ElementTree
@@ -107,7 +106,7 @@ class ConfigurationParser:
107106
108107 def __init__ (
109108 self ,
110- collapse : Union [ bool , Iterable [str ] ] = False ,
109+ collapse : bool | Iterable [str ] = False ,
111110 collapse_inverse : bool = False ,
112111 separator : tuple [str ] = ("=" ,),
113112 comment_prefixes : tuple [str ] = (";" , "#" ),
@@ -120,7 +119,7 @@ def __init__(
120119 self .comment_prefixes = comment_prefixes
121120 self .parsed_data = {}
122121
123- def __getitem__ (self , item : Any ) -> Union [ dict , str ] :
122+ def __getitem__ (self , item : Any ) -> dict | str :
124123 return self .parsed_data [item ]
125124
126125 def __contains__ (self , item : str ) -> bool :
@@ -157,7 +156,7 @@ def parse_file(self, fh: TextIO) -> None:
157156 """
158157 raise NotImplementedError ()
159158
160- def get (self , item : str , default : Optional [ Any ] = None ) -> Any :
159+ def get (self , item : str , default : Any | None = None ) -> Any :
161160 return self .parsed_data .get (item , default )
162161
163162 def read_file (self , fh : TextIO | io .BytesIO ) -> None :
@@ -388,7 +387,7 @@ class ListUnwrapper:
388387 """Provides utility functions to unwrap dictionary objects out of lists."""
389388
390389 @staticmethod
391- def unwrap (data : Union [ dict , list ] ) -> Union [ dict , list ] :
390+ def unwrap (data : dict | list ) -> dict | list :
392391 """Transforms a list with dictionaries to a dictionary.
393392
394393 The order of the list is preserved. If no dictionary is found, the list remains untouched:
@@ -409,7 +408,7 @@ def unwrap(data: Union[dict, list]) -> Union[dict, list]:
409408 return ListUnwrapper ._unwrap_dict (orig )
410409
411410 @staticmethod
412- def _unwrap_dict (data : Union [ dict , list ] ) -> Union [ dict , list ] :
411+ def _unwrap_dict (data : dict | list ) -> dict | list :
413412 """Looks for dictionaries and unwraps its values."""
414413
415414 if not isinstance (data , dict ):
@@ -425,7 +424,7 @@ def _unwrap_dict(data: Union[dict, list]) -> Union[dict, list]:
425424 return root
426425
427426 @staticmethod
428- def _unwrap_dict_list (data : Union [ dict , list ] ) -> Union [ dict , list ] :
427+ def _unwrap_dict_list (data : dict | list ) -> dict | list :
429428 """Unwraps a list containing dictionaries."""
430429 if not isinstance (data , list ) or not any (isinstance (obj , dict ) for obj in data ):
431430 return data
@@ -559,9 +558,9 @@ def __enter__(self) -> ScopeManager:
559558
560559 def __exit__ (
561560 self ,
562- type : Optional [ type [BaseException ]] ,
563- value : Optional [ BaseException ] ,
564- traceback : Optional [ TracebackType ] ,
561+ type : type [BaseException ] | None ,
562+ value : BaseException | None ,
563+ traceback : TracebackType | None ,
565564 ) -> None :
566565 self .clean ()
567566
@@ -636,7 +635,7 @@ def _change_scope(
636635 manager : ScopeManager ,
637636 line : str ,
638637 key : str ,
639- next_line : Optional [ str ] = None ,
638+ next_line : str | None = None ,
640639 ) -> bool :
641640 """A function to check whether to create a new scope, or go back to a previous one.
642641
@@ -722,7 +721,7 @@ def _change_scope(
722721 manager : ScopeManager ,
723722 line : str ,
724723 key : str ,
725- next_line : Optional [ str ] = None ,
724+ next_line : str | None = None ,
726725 ) -> bool :
727726 scope_char = ("[" , "]" )
728727 changed = False
@@ -785,22 +784,22 @@ def _update_continued_values(self, func: Callable, key, values: list[str]) -> tu
785784
786785@dataclass (frozen = True )
787786class ParserOptions :
788- collapse : Optional [ Union [ bool , set ]] = None
789- collapse_inverse : Optional [ bool ] = None
790- separator : Optional [ tuple [str ]] = None
791- comment_prefixes : Optional [ tuple [str ]] = None
787+ collapse : bool | set | None = None
788+ collapse_inverse : bool | None = None
789+ separator : tuple [str ] | None = None
790+ comment_prefixes : tuple [str ] | None = None
792791
793792
794793@dataclass (frozen = True )
795794class ParserConfig :
796795 parser : type [ConfigurationParser ] = Default
797- collapse : Optional [ Union [ bool , set ]] = None
798- collapse_inverse : Optional [ bool ] = None
799- separator : Optional [ tuple [str ]] = None
800- comment_prefixes : Optional [ tuple [str ]] = None
801- fields : Optional [ tuple [str ]] = None
796+ collapse : bool | set | None = None
797+ collapse_inverse : bool | None = None
798+ separator : tuple [str ] | None = None
799+ comment_prefixes : tuple [str ] | None = None
800+ fields : tuple [str ] | None = None
802801
803- def create_parser (self , options : Optional [ ParserOptions ] = None ) -> ConfigurationParser :
802+ def create_parser (self , options : ParserOptions | None = None ) -> ConfigurationParser :
804803 kwargs = {}
805804
806805 for field_name in ["collapse" , "collapse_inverse" , "separator" , "comment_prefixes" , "fields" ]:
@@ -890,7 +889,7 @@ def create_parser(self, options: Optional[ParserOptions] = None) -> Configuratio
890889}
891890
892891
893- def parse (path : Union [ FilesystemEntry , TargetPath ] , hint : Optional [ str ] = None , * args , ** kwargs ) -> ConfigurationParser :
892+ def parse (path : FilesystemEntry | TargetPath | Path , hint : str | None = None , * args , ** kwargs ) -> ConfigurationParser :
894893 """Parses the content of an ``path`` or ``entry`` to a dictionary.
895894
896895 Args:
@@ -909,7 +908,7 @@ def parse(path: Union[FilesystemEntry, TargetPath], hint: Optional[str] = None,
909908 if isinstance (path , TargetPath ):
910909 entry = path .get ()
911910
912- if not entry .is_file (follow_symlinks = True ):
911+ if not isinstance ( entry , Path ) and not entry .is_file (follow_symlinks = True ):
913912 raise FileNotFoundError (f"Could not parse { path } as a dictionary." )
914913
915914 options = ParserOptions (* args , ** kwargs )
@@ -918,14 +917,14 @@ def parse(path: Union[FilesystemEntry, TargetPath], hint: Optional[str] = None,
918917
919918
920919def parse_config (
921- entry : FilesystemEntry ,
922- hint : Optional [ str ] = None ,
923- options : Optional [ ParserOptions ] = None ,
920+ entry : FilesystemEntry | Path ,
921+ hint : str | None = None ,
922+ options : ParserOptions | None = None ,
924923) -> ConfigurationParser :
925924 parser_type = _select_parser (entry , hint )
926925
927926 parser = parser_type .create_parser (options )
928- with entry .open () as fh :
927+ with entry .open ("rb" ) if isinstance ( entry , Path ) else entry . open ( ) as fh :
929928 if not isinstance (parser , Bin ):
930929 open_file = io .TextIOWrapper (fh , encoding = "utf-8" )
931930 else :
@@ -935,7 +934,7 @@ def parse_config(
935934 return parser
936935
937936
938- def _select_parser (entry : FilesystemEntry , hint : Optional [ str ] = None ) -> ParserConfig :
937+ def _select_parser (entry : FilesystemEntry , hint : str | None = None ) -> ParserConfig :
939938 if hint and (parser_type := CONFIG_MAP .get (hint )):
940939 return parser_type
941940
0 commit comments