1414import os
1515import re
1616import fnmatch
17- from collections import OrderedDict
1817
1918from git .compat import (
2019 defenc ,
2120 force_text ,
22- with_metaclass ,
2321 is_win ,
2422)
2523
3129
3230# typing-------------------------------------------------------
3331
34- from typing import (Any , Callable , IO , List , Dict , Sequence ,
35- TYPE_CHECKING , Tuple , Union , cast , overload )
32+ from typing import (Any , Callable , Generic , IO , List , Dict , Sequence ,
33+ TYPE_CHECKING , Tuple , TypeVar , Union , cast , overload )
3634
37- from git .types import Lit_config_levels , ConfigLevels_Tup , PathLike , TBD , assert_never
35+ from git .types import Lit_config_levels , ConfigLevels_Tup , PathLike , TBD , assert_never , _T
3836
3937if TYPE_CHECKING :
4038 from git .repo .base import Repo
4139 from io import BytesIO
4240
41+ T_ConfigParser = TypeVar ('T_ConfigParser' , bound = 'GitConfigParser' )
4342# -------------------------------------------------------------
4443
4544__all__ = ('GitConfigParser' , 'SectionConstraint' )
6160
6261
6362class MetaParserBuilder (abc .ABCMeta ):
64-
6563 """Utlity class wrapping base-class methods into decorators that assure read-only properties"""
6664 def __new__ (cls , name : str , bases : TBD , clsdict : Dict [str , Any ]) -> TBD :
6765 """
@@ -115,7 +113,7 @@ def flush_changes(self, *args: Any, **kwargs: Any) -> Any:
115113 return flush_changes
116114
117115
118- class SectionConstraint (object ):
116+ class SectionConstraint (Generic [ T_ConfigParser ] ):
119117
120118 """Constrains a ConfigParser to only option commands which are constrained to
121119 always use the section we have been initialized with.
@@ -128,7 +126,7 @@ class SectionConstraint(object):
128126 _valid_attrs_ = ("get_value" , "set_value" , "get" , "set" , "getint" , "getfloat" , "getboolean" , "has_option" ,
129127 "remove_section" , "remove_option" , "options" )
130128
131- def __init__ (self , config : 'GitConfigParser' , section : str ) -> None :
129+ def __init__ (self , config : T_ConfigParser , section : str ) -> None :
132130 self ._config = config
133131 self ._section_name = section
134132
@@ -149,26 +147,26 @@ def _call_config(self, method: str, *args: Any, **kwargs: Any) -> Any:
149147 return getattr (self ._config , method )(self ._section_name , * args , ** kwargs )
150148
151149 @property
152- def config (self ) -> 'GitConfigParser' :
150+ def config (self ) -> T_ConfigParser :
153151 """return: Configparser instance we constrain"""
154152 return self ._config
155153
156154 def release (self ) -> None :
157155 """Equivalent to GitConfigParser.release(), which is called on our underlying parser instance"""
158156 return self ._config .release ()
159157
160- def __enter__ (self ) -> 'SectionConstraint' :
158+ def __enter__ (self ) -> 'SectionConstraint[T_ConfigParser] ' :
161159 self ._config .__enter__ ()
162160 return self
163161
164162 def __exit__ (self , exception_type : str , exception_value : str , traceback : str ) -> None :
165163 self ._config .__exit__ (exception_type , exception_value , traceback )
166164
167165
168- class _OMD (OrderedDict ):
166+ class _OMD (Dict [ str , List [ _T ]] ):
169167 """Ordered multi-dict."""
170168
171- def __setitem__ (self , key : str , value : Any ) -> None :
169+ def __setitem__ (self , key : str , value : _T ) -> None : # type: ignore[override]
172170 super (_OMD , self ).__setitem__ (key , [value ])
173171
174172 def add (self , key : str , value : Any ) -> None :
@@ -177,7 +175,7 @@ def add(self, key: str, value: Any) -> None:
177175 return None
178176 super (_OMD , self ).__getitem__ (key ).append (value )
179177
180- def setall (self , key : str , values : Any ) -> None :
178+ def setall (self , key : str , values : List [ _T ] ) -> None :
181179 super (_OMD , self ).__setitem__ (key , values )
182180
183181 def __getitem__ (self , key : str ) -> Any :
@@ -194,25 +192,17 @@ def setlast(self, key: str, value: Any) -> None:
194192 prior = super (_OMD , self ).__getitem__ (key )
195193 prior [- 1 ] = value
196194
197- @overload
198- def get (self , key : str , default : None = ...) -> None :
199- ...
200-
201- @overload
202- def get (self , key : str , default : Any = ...) -> Any :
203- ...
204-
205- def get (self , key : str , default : Union [Any , None ] = None ) -> Union [Any , None ]:
206- return super (_OMD , self ).get (key , [default ])[- 1 ]
195+ def get (self , key : str , default : Union [_T , None ] = None ) -> Union [_T , None ]: # type: ignore
196+ return super (_OMD , self ).get (key , [default ])[- 1 ] # type: ignore
207197
208- def getall (self , key : str ) -> Any :
198+ def getall (self , key : str ) -> List [ _T ] :
209199 return super (_OMD , self ).__getitem__ (key )
210200
211- def items (self ) -> List [Tuple [str , Any ]]: # type: ignore[override]
201+ def items (self ) -> List [Tuple [str , _T ]]: # type: ignore[override]
212202 """List of (key, last value for key)."""
213203 return [(k , self [k ]) for k in self ]
214204
215- def items_all (self ) -> List [Tuple [str , List [Any ]]]:
205+ def items_all (self ) -> List [Tuple [str , List [_T ]]]:
216206 """List of (key, list of values for key)."""
217207 return [(k , self .getall (k )) for k in self ]
218208
@@ -238,7 +228,7 @@ def get_config_path(config_level: Lit_config_levels) -> str:
238228 assert_never (config_level , ValueError (f"Invalid configuration level: { config_level !r} " ))
239229
240230
241- class GitConfigParser (with_metaclass ( MetaParserBuilder , cp .RawConfigParser )): # type: ignore ## mypy does not understand dynamic class creation # noqa: E501
231+ class GitConfigParser (cp .RawConfigParser , metaclass = MetaParserBuilder ):
242232
243233 """Implements specifics required to read git style configuration files.
244234
@@ -298,7 +288,10 @@ def __init__(self, file_or_files: Union[None, PathLike, 'BytesIO', Sequence[Unio
298288 :param repo: Reference to repository to use if [includeIf] sections are found in configuration files.
299289
300290 """
301- cp .RawConfigParser .__init__ (self , dict_type = _OMD )
291+ cp .RawConfigParser .__init__ (self , dict_type = _OMD ) # type: ignore[arg-type]
292+ self ._dict : Callable [..., _OMD ] # type: ignore[assignment] # mypy/typeshed bug
293+ self ._defaults : _OMD # type: ignore[assignment] # mypy/typeshed bug
294+ self ._sections : _OMD # type: ignore[assignment] # mypy/typeshed bug
302295
303296 # Used in python 3, needs to stay in sync with sections for underlying implementation to work
304297 if not hasattr (self , '_proxies' ):
@@ -424,7 +417,7 @@ def string_decode(v: str) -> str:
424417 # is it a section header?
425418 mo = self .SECTCRE .match (line .strip ())
426419 if not is_multi_line and mo :
427- sectname = mo .group ('header' ).strip ()
420+ sectname : str = mo .group ('header' ).strip ()
428421 if sectname in self ._sections :
429422 cursect = self ._sections [sectname ]
430423 elif sectname == cp .DEFAULTSECT :
@@ -535,7 +528,7 @@ def _included_paths(self) -> List[Tuple[str, str]]:
535528
536529 return paths
537530
538- def read (self ) -> None :
531+ def read (self ) -> None : # type: ignore[override]
539532 """Reads the data stored in the files we have been initialized with. It will
540533 ignore files that cannot be read, possibly leaving an empty configuration
541534
@@ -626,7 +619,7 @@ def write_section(name, section_dict):
626619 for name , value in self ._sections .items ():
627620 write_section (name , value )
628621
629- def items (self , section_name : str ) -> List [Tuple [str , str ]]:
622+ def items (self , section_name : str ) -> List [Tuple [str , str ]]: # type: ignore[override]
630623 """:return: list((option, value), ...) pairs of all items in the given section"""
631624 return [(k , v ) for k , v in super (GitConfigParser , self ).items (section_name ) if k != '__name__' ]
632625
0 commit comments