2828 "setlocale" , "resetlocale" , "localeconv" , "strcoll" , "strxfrm" ,
2929 "str" , "atof" , "atoi" , "format" , "format_string" , "currency" ,
3030 "normalize" , "LC_CTYPE" , "LC_COLLATE" , "LC_TIME" , "LC_MONETARY" ,
31- "LC_NUMERIC" , "LC_ALL" , "CHAR_MAX" ]
31+ "LC_NUMERIC" , "LC_ALL" , "CHAR_MAX" , "getencoding" ]
3232
3333def _strcoll (a ,b ):
3434 """ strcoll(string,string) -> int.
@@ -185,8 +185,14 @@ def _format(percent, value, grouping=False, monetary=False, *additional):
185185 formatted = percent % ((value ,) + additional )
186186 else :
187187 formatted = percent % value
188+ if percent [- 1 ] in 'eEfFgGdiu' :
189+ formatted = _localize (formatted , grouping , monetary )
190+ return formatted
191+
192+ # Transform formatted as locale number according to the locale settings
193+ def _localize (formatted , grouping = False , monetary = False ):
188194 # floats and decimal ints need special action!
189- if percent [ - 1 ] in 'eEfFgG' :
195+ if '.' in formatted :
190196 seps = 0
191197 parts = formatted .split ('.' )
192198 if grouping :
@@ -196,7 +202,7 @@ def _format(percent, value, grouping=False, monetary=False, *additional):
196202 formatted = decimal_point .join (parts )
197203 if seps :
198204 formatted = _strip_padding (formatted , seps )
199- elif percent [ - 1 ] in 'diu' :
205+ else :
200206 seps = 0
201207 if grouping :
202208 formatted , seps = _group (formatted , monetary = monetary )
@@ -267,7 +273,7 @@ def currency(val, symbol=True, grouping=False, international=False):
267273 raise ValueError ("Currency formatting is not possible using "
268274 "the 'C' locale." )
269275
270- s = _format ( '%%.%if' % digits , abs (val ), grouping , monetary = True )
276+ s = _localize ( f' { abs (val ):.{ digits }f } ' , grouping , monetary = True )
271277 # '<' and '>' are markers if the sign must be inserted between symbol and value
272278 s = '<' + s + '>'
273279
@@ -279,6 +285,8 @@ def currency(val, symbol=True, grouping=False, international=False):
279285 if precedes :
280286 s = smb + (separated and ' ' or '' ) + s
281287 else :
288+ if international and smb [- 1 ] == ' ' :
289+ smb = smb [:- 1 ]
282290 s = s + (separated and ' ' or '' ) + smb
283291
284292 sign_pos = conv [val < 0 and 'n_sign_posn' or 'p_sign_posn' ]
@@ -321,6 +329,10 @@ def delocalize(string):
321329 string = string .replace (dd , '.' )
322330 return string
323331
332+ def localize (string , grouping = False , monetary = False ):
333+ """Parses a string as locale number according to the locale settings."""
334+ return _localize (string , grouping , monetary )
335+
324336def atof (string , func = float ):
325337 "Parses a string as a float according to the locale settings."
326338 return func (delocalize (string ))
@@ -492,6 +504,10 @@ def _parse_localename(localename):
492504 return tuple (code .split ('.' )[:2 ])
493505 elif code == 'C' :
494506 return None , None
507+ elif code == 'UTF-8' :
508+ # On macOS "LC_CTYPE=UTF-8" is a valid locale setting
509+ # for getting UTF-8 handling for text.
510+ return None , 'UTF-8'
495511 raise ValueError ('unknown locale: %s' % localename )
496512
497513def _build_localename (localetuple ):
@@ -539,6 +555,12 @@ def getdefaultlocale(envvars=('LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE')):
539555
540556 """
541557
558+ import warnings
559+ warnings .warn (
560+ "Use setlocale(), getencoding() and getlocale() instead" ,
561+ DeprecationWarning , stacklevel = 2
562+ )
563+
542564 try :
543565 # check if it's supported by the _locale module
544566 import _locale
@@ -611,55 +633,72 @@ def resetlocale(category=LC_ALL):
611633 getdefaultlocale(). category defaults to LC_ALL.
612634
613635 """
614- _setlocale (category , _build_localename (getdefaultlocale ()))
636+ import warnings
637+ warnings .warn (
638+ 'Use locale.setlocale(locale.LC_ALL, "") instead' ,
639+ DeprecationWarning , stacklevel = 2
640+ )
641+
642+ with warnings .catch_warnings ():
643+ warnings .simplefilter ('ignore' , category = DeprecationWarning )
644+ loc = getdefaultlocale ()
645+
646+ _setlocale (category , _build_localename (loc ))
647+
648+
649+ try :
650+ from _locale import getencoding
651+ except ImportError :
652+ def getencoding ():
653+ if hasattr (sys , 'getandroidapilevel' ):
654+ # On Android langinfo.h and CODESET are missing, and UTF-8 is
655+ # always used in mbstowcs() and wcstombs().
656+ return 'utf-8'
657+ encoding = getdefaultlocale ()[1 ]
658+ if encoding is None :
659+ # LANG not set, default to UTF-8
660+ encoding = 'utf-8'
661+ return encoding
615662
616- if sys .platform .startswith ("win" ):
617- # On Win32, this will return the ANSI code page
618- def getpreferredencoding (do_setlocale = True ):
663+ try :
664+ CODESET
665+ except NameError :
666+ def getpreferredencoding (do_setlocale = True ):
619667 """Return the charset that the user is likely using."""
668+ if sys .flags .warn_default_encoding :
669+ import warnings
670+ warnings .warn (
671+ "UTF-8 Mode affects locale.getpreferredencoding(). Consider locale.getencoding() instead." ,
672+ EncodingWarning , 2 )
620673 if sys .flags .utf8_mode :
621- return 'UTF-8'
622- import _bootlocale
623- return _bootlocale .getpreferredencoding (False )
674+ return 'utf-8'
675+ return getencoding ()
624676else :
625677 # On Unix, if CODESET is available, use that.
626- try :
627- CODESET
628- except NameError :
629- if hasattr (sys , 'getandroidapilevel' ):
630- # On Android langinfo.h and CODESET are missing, and UTF-8 is
631- # always used in mbstowcs() and wcstombs().
632- def getpreferredencoding (do_setlocale = True ):
633- return 'UTF-8'
634- else :
635- # Fall back to parsing environment variables :-(
636- def getpreferredencoding (do_setlocale = True ):
637- """Return the charset that the user is likely using,
638- by looking at environment variables."""
639- if sys .flags .utf8_mode :
640- return 'UTF-8'
641- res = getdefaultlocale ()[1 ]
642- if res is None :
643- # LANG not set, default conservatively to ASCII
644- res = 'ascii'
645- return res
646- else :
647- def getpreferredencoding (do_setlocale = True ):
648- """Return the charset that the user is likely using,
649- according to the system configuration."""
650- if sys .flags .utf8_mode :
651- return 'UTF-8'
652- import _bootlocale
653- if do_setlocale :
654- oldloc = setlocale (LC_CTYPE )
655- try :
656- setlocale (LC_CTYPE , "" )
657- except Error :
658- pass
659- result = _bootlocale .getpreferredencoding (False )
660- if do_setlocale :
661- setlocale (LC_CTYPE , oldloc )
662- return result
678+ def getpreferredencoding (do_setlocale = True ):
679+ """Return the charset that the user is likely using,
680+ according to the system configuration."""
681+
682+ if sys .flags .warn_default_encoding :
683+ import warnings
684+ warnings .warn (
685+ "UTF-8 Mode affects locale.getpreferredencoding(). Consider locale.getencoding() instead." ,
686+ EncodingWarning , 2 )
687+ if sys .flags .utf8_mode :
688+ return 'utf-8'
689+
690+ if not do_setlocale :
691+ return getencoding ()
692+
693+ old_loc = setlocale (LC_CTYPE )
694+ try :
695+ try :
696+ setlocale (LC_CTYPE , "" )
697+ except Error :
698+ pass
699+ return getencoding ()
700+ finally :
701+ setlocale (LC_CTYPE , old_loc )
663702
664703
665704### Database
@@ -734,6 +773,7 @@ def getpreferredencoding(do_setlocale = True):
734773for k , v in sorted (locale_encoding_alias .items ()):
735774 k = k .replace ('_' , '' )
736775 locale_encoding_alias .setdefault (k , v )
776+ del k , v
737777
738778#
739779# The locale_alias table maps lowercase alias names to C locale names
0 commit comments