143143 between keys and values are surrounded by spaces.
144144"""
145145
146- # Do not import dataclasses; overhead is unacceptable (gh-117703)
146+ # Defer annotations so that typing need not be imported at runtime.
147+ from __future__ import annotations
147148
148- from collections .abc import Iterable , MutableMapping
149- from collections import ChainMap as _ChainMap
149+ # Do not import dataclasses; overhead is unacceptable (gh-117703)
150150import contextlib
151151import functools
152152import io
153153import itertools
154154import os
155155import re
156156import sys
157+ from collections import ChainMap as _ChainMap
158+ from collections .abc import Iterable , MutableMapping
157159
158- __all__ = ("NoSectionError" , "DuplicateOptionError" , "DuplicateSectionError" ,
159- "NoOptionError" , "InterpolationError" , "InterpolationDepthError" ,
160- "InterpolationMissingOptionError" , "InterpolationSyntaxError" ,
161- "ParsingError" , "MissingSectionHeaderError" ,
162- "MultilineContinuationError" , "UnnamedSectionDisabledError" ,
163- "InvalidWriteError" , "ConfigParser" , "RawConfigParser" ,
164- "Interpolation" , "BasicInterpolation" , "ExtendedInterpolation" ,
165- "SectionProxy" , "ConverterMapping" ,
166- "DEFAULTSECT" , "MAX_INTERPOLATION_DEPTH" , "UNNAMED_SECTION" )
160+ # Do not import typing either; it is only needed for annotations.
161+ TYPE_CHECKING = False
162+ if TYPE_CHECKING :
163+ from typing import ClassVar
164+
165+ __all__ = (
166+ "DEFAULTSECT" ,
167+ "MAX_INTERPOLATION_DEPTH" ,
168+ "UNNAMED_SECTION" ,
169+ "BasicInterpolation" ,
170+ "ConfigParser" ,
171+ "ConverterMapping" ,
172+ "DuplicateOptionError" ,
173+ "DuplicateSectionError" ,
174+ "ExtendedInterpolation" ,
175+ "Interpolation" ,
176+ "InterpolationDepthError" ,
177+ "InterpolationError" ,
178+ "InterpolationMissingOptionError" ,
179+ "InterpolationSyntaxError" ,
180+ "InvalidWriteError" ,
181+ "MissingSectionHeaderError" ,
182+ "MultilineContinuationError" ,
183+ "NoOptionError" ,
184+ "NoSectionError" ,
185+ "ParsingError" ,
186+ "RawConfigParser" ,
187+ "SectionProxy" ,
188+ "UnnamedSectionDisabledError" ,
189+ )
167190
168191_default_dict = dict
169192DEFAULTSECT = "DEFAULT"
@@ -190,7 +213,7 @@ class NoSectionError(Error):
190213 """Raised when no section matches a requested option."""
191214
192215 def __init__ (self , section ):
193- Error .__init__ (self , 'No section: %r' % ( section ,) )
216+ Error .__init__ (self , f 'No section: { section !r } ' )
194217 self .section = section
195218 self .args = (section , )
196219
@@ -208,7 +231,7 @@ def __init__(self, section, source=None, lineno=None):
208231 if source is not None :
209232 message = ["While reading from " , repr (source )]
210233 if lineno is not None :
211- message .append (" [line {0 :2d}]" . format ( lineno ) )
234+ message .append (f " [line { lineno :2d} ]" )
212235 message .append (": section " )
213236 message .extend (msg )
214237 msg = message
@@ -234,7 +257,7 @@ def __init__(self, section, option, source=None, lineno=None):
234257 if source is not None :
235258 message = ["While reading from " , repr (source )]
236259 if lineno is not None :
237- message .append (" [line {0 :2d}]" . format ( lineno ) )
260+ message .append (f " [line { lineno :2d} ]" )
238261 message .append (": option " )
239262 message .extend (msg )
240263 msg = message
@@ -252,8 +275,7 @@ class NoOptionError(Error):
252275 """A requested option was not found."""
253276
254277 def __init__ (self , option , section ):
255- Error .__init__ (self , "No option %r in section: %r" %
256- (option , section ))
278+ Error .__init__ (self , f"No option { option !r} in section: { section !r} " )
257279 self .option = option
258280 self .section = section
259281 self .args = (option , section )
@@ -273,9 +295,9 @@ class InterpolationMissingOptionError(InterpolationError):
273295 """A string substitution required a setting which was not available."""
274296
275297 def __init__ (self , option , section , rawval , reference ):
276- msg = ("Bad value substitution: option {!r} in section {!r} contains "
277- " an interpolation key {!r} which is not a valid option name. "
278- " Raw value: {!r}". format ( option , section , reference , rawval ) )
298+ msg = (f "Bad value substitution: option { option !r} in section "
299+ f" { section !r } contains an interpolation key { reference !r} "
300+ f"which is not a valid option name. Raw value: { rawval !r} " )
279301 InterpolationError .__init__ (self , option , section , msg )
280302 self .reference = reference
281303 self .args = (option , section , rawval , reference )
@@ -293,11 +315,10 @@ class InterpolationDepthError(InterpolationError):
293315 """Raised when substitutions are nested too deeply."""
294316
295317 def __init__ (self , option , section , rawval ):
296- msg = ("Recursion limit exceeded in value substitution: option {!r} "
297- "in section {!r} contains an interpolation key which "
298- "cannot be substituted in {} steps. Raw value: {!r}"
299- "" .format (option , section , MAX_INTERPOLATION_DEPTH ,
300- rawval ))
318+ msg = (f"Recursion limit exceeded in value substitution: option "
319+ f"{ option !r} in section { section !r} contains an interpolation "
320+ f"key which cannot be substituted in "
321+ f"{ MAX_INTERPOLATION_DEPTH } steps. Raw value: { rawval !r} " )
301322 InterpolationError .__init__ (self , option , section , msg )
302323 self .args = (option , section , rawval )
303324
@@ -327,7 +348,7 @@ def combine(self, others):
327348 return self
328349
329350 @staticmethod
330- def _raise_all (exceptions : Iterable [' ParsingError' ]):
351+ def _raise_all (exceptions : Iterable [ParsingError ]):
331352 """
332353 Combine any number of ParsingErrors into one and raise it.
333354 """
@@ -343,8 +364,8 @@ class MissingSectionHeaderError(ParsingError):
343364 def __init__ (self , filename , lineno , line ):
344365 Error .__init__ (
345366 self ,
346- 'File contains no section headers.\n file: %r, line: %d \n %r' %
347- ( filename , lineno , line ) )
367+ 'File contains no section headers.\n '
368+ f'file: { filename !r } , line: { lineno :d } \n { line !r } ' )
348369 self .source = filename
349370 self .lineno = lineno
350371 self .line = line
@@ -357,8 +378,7 @@ def __init__(self, filename, lineno, line):
357378 Error .__init__ (
358379 self ,
359380 "Key without value continued with an indented line.\n "
360- "file: %r, line: %d\n %r"
361- % (filename , lineno , line ))
381+ f"file: { filename !r} , line: { lineno :d} \n { line !r} " )
362382 self .source = filename
363383 self .lineno = lineno
364384 self .line = line
@@ -437,8 +457,8 @@ def before_set(self, parser, section, option, value):
437457 tmp_value = value .replace ('%%' , '' ) # escaped percent signs
438458 tmp_value = self ._KEYCRE .sub ('' , tmp_value ) # valid syntax
439459 if '%' in tmp_value :
440- raise ValueError ("invalid interpolation syntax in %r at "
441- " position %d" % ( value , tmp_value .find ('%' )) )
460+ raise ValueError (f "invalid interpolation syntax in { value !r } "
461+ f"at position { tmp_value .find ('%' ):d } " )
442462 return value
443463
444464 def _interpolate_some (self , parser , option , accum , rest , section , map ,
@@ -463,7 +483,7 @@ def _interpolate_some(self, parser, option, accum, rest, section, map,
463483 m = self ._KEYCRE .match (rest )
464484 if m is None :
465485 raise InterpolationSyntaxError (option , section ,
466- "bad interpolation variable reference %r" % rest )
486+ f "bad interpolation variable reference { rest !r } " )
467487 var = parser .optionxform (m .group (1 ))
468488 rest = rest [m .end ():]
469489 try :
@@ -479,8 +499,8 @@ def _interpolate_some(self, parser, option, accum, rest, section, map,
479499 else :
480500 raise InterpolationSyntaxError (
481501 option , section ,
482- "'%% ' must be followed by '% %' or '(', "
483- "found: %r" % ( rest ,) )
502+ "'%' must be followed by '%' or '(', "
503+ f "found: { rest !r } " )
484504
485505
486506class ExtendedInterpolation (Interpolation ):
@@ -498,12 +518,12 @@ def before_set(self, parser, section, option, value):
498518 tmp_value = value .replace ('$$' , '' ) # escaped dollar signs
499519 tmp_value = self ._KEYCRE .sub ('' , tmp_value ) # valid syntax
500520 if '$' in tmp_value :
501- raise ValueError ("invalid interpolation syntax in %r at "
502- " position %d" % ( value , tmp_value .find ('$' )) )
521+ raise ValueError (f "invalid interpolation syntax in { value !r } "
522+ f"at position { tmp_value .find ('$' ):d } " )
503523 return value
504524
505- def _interpolate_some (self , parser , option , accum , rest , section , map ,
506- depth ):
525+ def _interpolate_some (self , parser , option , # noqa: C901 # gh-159686
526+ accum , rest , section , map , depth ):
507527 rawval = parser .get (section , option , raw = True , fallback = rest )
508528 if depth > MAX_INTERPOLATION_DEPTH :
509529 raise InterpolationDepthError (option , section , rawval )
@@ -524,7 +544,7 @@ def _interpolate_some(self, parser, option, accum, rest, section, map,
524544 m = self ._KEYCRE .match (rest )
525545 if m is None :
526546 raise InterpolationSyntaxError (option , section ,
527- "bad interpolation variable reference %r" % rest )
547+ f "bad interpolation variable reference { rest !r } " )
528548 path = m .group (1 ).split (':' )
529549 rest = rest [m .end ():]
530550 sect = section
@@ -540,7 +560,7 @@ def _interpolate_some(self, parser, option, accum, rest, section, map,
540560 else :
541561 raise InterpolationSyntaxError (
542562 option , section ,
543- "More than one ':' found: %r" % ( rest ,) )
563+ f "More than one ':' found: { rest !r } " )
544564 except (KeyError , NoSectionError , NoOptionError ):
545565 raise InterpolationMissingOptionError (
546566 option , section , rawval , ":" .join (path )) from None
@@ -556,7 +576,7 @@ def _interpolate_some(self, parser, option, accum, rest, section, map,
556576 raise InterpolationSyntaxError (
557577 option , section ,
558578 "'$' must be followed by '$' or '{', "
559- "found: %r" % ( rest ,) )
579+ f "found: { rest !r } " )
560580
561581
562582class _ReadState :
@@ -570,7 +590,7 @@ class _ReadState:
570590
571591 def __init__ (self ):
572592 self .elements_added = set ()
573- self .errors = list ()
593+ self .errors = []
574594
575595
576596class _Line (str ):
@@ -650,8 +670,10 @@ class RawConfigParser(MutableMapping):
650670 # Compiled regular expression for matching leading whitespace in a line
651671 NONSPACECRE = re .compile (r"\S" )
652672 # Possible boolean values in the configuration.
653- BOOLEAN_STATES = {'1' : True , 'yes' : True , 'true' : True , 'on' : True ,
654- '0' : False , 'no' : False , 'false' : False , 'off' : False }
673+ BOOLEAN_STATES : ClassVar [dict [str , bool ]] = {
674+ '1' : True , 'yes' : True , 'true' : True , 'on' : True ,
675+ '0' : False , 'no' : False , 'false' : False , 'off' : False ,
676+ }
655677
656678 def __init__ (self , defaults = None , dict_type = _default_dict ,
657679 allow_no_value = False , * , delimiters = ('=' , ':' ),
@@ -714,11 +736,10 @@ def add_section(self, section):
714736 already exists. Raise ValueError if name is DEFAULT.
715737 """
716738 if section == self .default_section :
717- raise ValueError ('Invalid section name: %r' % section )
739+ raise ValueError (f 'Invalid section name: { section !r } ' )
718740
719- if section is UNNAMED_SECTION :
720- if not self ._allow_unnamed_section :
721- raise UnnamedSectionDisabledError
741+ if section is UNNAMED_SECTION and not self ._allow_unnamed_section :
742+ raise UnnamedSectionDisabledError
722743
723744 if section in self ._sections :
724745 raise DuplicateSectionError (section )
@@ -970,13 +991,13 @@ def write(self, fp, space_around_delimiters=True):
970991 preserved when writing the configuration back.
971992 """
972993 if space_around_delimiters :
973- d = " {} " . format ( self ._delimiters [0 ])
994+ d = f " { self ._delimiters [0 ]} "
974995 else :
975996 d = self ._delimiters [0 ]
976997 if self ._defaults :
977998 self ._write_section (fp , self .default_section ,
978999 self ._defaults .items (), d )
979- if UNNAMED_SECTION in self ._sections and self . _sections [ UNNAMED_SECTION ] :
1000+ if self ._sections . get ( UNNAMED_SECTION ) :
9801001 self ._write_section (fp , UNNAMED_SECTION , self ._sections [UNNAMED_SECTION ].items (), d , unnamed = True )
9811002
9821003 for section in self ._sections :
@@ -988,7 +1009,7 @@ def write(self, fp, space_around_delimiters=True):
9881009 def _write_section (self , fp , section_name , section_items , delimiter , unnamed = False ):
9891010 """Write a single section to the specified 'fp'."""
9901011 if not unnamed :
991- fp .write ("[{}]\n " . format ( section_name ) )
1012+ fp .write (f "[{ section_name } ]\n " )
9921013 for key , value in section_items :
9931014 self ._validate_key_contents (key )
9941015 value = self ._interpolation .before_write (self , section_name , key ,
@@ -999,7 +1020,7 @@ def _write_section(self, fp, section_name, section_items, delimiter, unnamed=Fal
9991020 .replace ('\r ' , '\n ' ).replace ('\n ' , '\n \t ' ))
10001021 else :
10011022 value = ""
1002- fp .write ("{}{ }\n ". format ( key , value ) )
1023+ fp .write (f" { key } { value } \n " )
10031024 fp .write ("\n " )
10041025
10051026 def remove_option (self , section , option ):
@@ -1165,7 +1186,7 @@ def _handle_option(self, st, line, fpname):
11651186 st .errors .append (ParsingError (fpname , st .lineno , line ))
11661187 return
11671188
1168- st .optname , vi , optval = mo .group ('option' , 'vi ' , 'value' )
1189+ st .optname , optval = mo .group ('option' , 'value' )
11691190 if not st .optname :
11701191 st .errors .append (ParsingError (fpname , st .lineno , line ))
11711192 st .optname = self .optionxform (st .optname .rstrip ())
@@ -1225,7 +1246,7 @@ def _convert_to_boolean(self, value):
12251246 """Return a boolean value translating from other types if necessary.
12261247 """
12271248 if value .lower () not in self .BOOLEAN_STATES :
1228- raise ValueError ('Not a boolean: %s' % value )
1249+ raise ValueError (f 'Not a boolean: { value } ' )
12291250 return self .BOOLEAN_STATES [value .lower ()]
12301251
12311252 def _validate_key_contents (self , key ):
@@ -1256,9 +1277,8 @@ def _validate_value_types(self, *, section="", option="", value=""):
12561277 raise TypeError ("section names must be strings or UNNAMED_SECTION" )
12571278 if not isinstance (option , str ):
12581279 raise TypeError ("option keys must be strings" )
1259- if not self ._allow_no_value or value :
1260- if not isinstance (value , str ):
1261- raise TypeError ("option values must be strings" )
1280+ if (not self ._allow_no_value or value ) and not isinstance (value , str ):
1281+ raise TypeError ("option values must be strings" )
12621282
12631283 @property
12641284 def converters (self ):
@@ -1310,7 +1330,7 @@ def __init__(self, parser, name):
13101330 setattr (self , key , getter )
13111331
13121332 def __repr__ (self ):
1313- return '<Section: {}>' . format ( self ._name )
1333+ return f '<Section: { self ._name } >'
13141334
13151335 def __getitem__ (self , key ):
13161336 if not self ._parser .has_option (self ._name , key ):
@@ -1393,8 +1413,7 @@ def __setitem__(self, key, value):
13931413 try :
13941414 k = 'get' + key
13951415 except TypeError :
1396- raise ValueError ('Incompatible key: {} (type: {})'
1397- '' .format (key , type (key )))
1416+ raise ValueError (f'Incompatible key: { key } (type: { type (key )} )' )
13981417 if k == 'get' :
13991418 raise ValueError ('Incompatible key: cannot use "" as a name' )
14001419 self ._data [key ] = value
0 commit comments