From 1ecd49a7d735cc73ac9e1d312da3796676c7a9cf Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Mon, 22 May 2023 17:06:41 -0700 Subject: [PATCH] gh-104783: Remove locale.getdefaultlocale() locale.getencoding() now uses sys.getfilesystemencoding() if _locale.getencoding() is missing. --- Doc/library/locale.rst | 35 ------ Doc/whatsnew/3.13.rst | 8 ++ Lib/locale.py | 116 +----------------- Lib/test/test_locale.py | 36 +----- ...-05-23-04-01-27.gh-issue-104783.QyhIoq.rst | 2 + 5 files changed, 17 insertions(+), 180 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-05-23-04-01-27.gh-issue-104783.QyhIoq.rst diff --git a/Doc/library/locale.rst b/Doc/library/locale.rst index f2abb3638a141f1..14627013cd7ebce 100644 --- a/Doc/library/locale.rst +++ b/Doc/library/locale.rst @@ -281,31 +281,6 @@ The :mod:`locale` module defines the following exception and functions: 0 to 99. -.. function:: getdefaultlocale([envvars]) - - Tries to determine the default locale settings and returns them as a tuple of - the form ``(language code, encoding)``. - - According to POSIX, a program which has not called ``setlocale(LC_ALL, '')`` - runs using the portable ``'C'`` locale. Calling ``setlocale(LC_ALL, '')`` lets - it use the default locale as defined by the :envvar:`LANG` variable. Since we - do not want to interfere with the current locale setting we thus emulate the - behavior in the way described above. - - To maintain compatibility with other platforms, not only the :envvar:`LANG` - variable is tested, but a list of variables given as envvars parameter. The - first found to be defined will be used. *envvars* defaults to the search - path used in GNU gettext; it must always contain the variable name - ``'LANG'``. The GNU gettext search path contains ``'LC_ALL'``, - ``'LC_CTYPE'``, ``'LANG'`` and ``'LANGUAGE'``, in that order. - - Except for the code ``'C'``, the language code corresponds to :rfc:`1766`. - *language code* and *encoding* may be ``None`` if their values cannot be - determined. - - .. deprecated-removed:: 3.11 3.13 - - .. function:: getlocale(category=LC_CTYPE) Returns the current setting for the given locale category as sequence containing @@ -370,16 +345,6 @@ The :mod:`locale` module defines the following exception and functions: encoding for the locale code just like :func:`setlocale`. -.. function:: resetlocale(category=LC_ALL) - - Sets the locale for *category* to the default setting. - - The default setting is determined by calling :func:`getdefaultlocale`. - *category* defaults to :const:`LC_ALL`. - - .. deprecated-removed:: 3.11 3.13 - - .. function:: strcoll(string1, string2) Compares two strings according to the current :const:`LC_COLLATE` setting. As diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index af331c46f1c0e43..4ad85378375417f 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -102,6 +102,14 @@ Deprecated Removed ======= +* Remove two :mod:`locale` functions deprecated in Python 3.11: + + * ``getdefaultlocale()``: use :func:`locale.setlocale`, + :func:`locale.getpreferredencoding(False) ` + and :func:`locale.getlocale` functions instead. + * ``resetlocale()``: use ``locale.setlocale(locale.LC_ALL, "")`` instead. + + (Contributed by Victor Stinner in :gh:`104783`.) Porting to Python 3.13 diff --git a/Lib/locale.py b/Lib/locale.py index e94f0d1acbaa7d2..a18f008a14999b5 100644 --- a/Lib/locale.py +++ b/Lib/locale.py @@ -24,8 +24,8 @@ # Yuck: LC_MESSAGES is non-standard: can't tell whether it exists before # trying the import. So __all__ is also fiddled at the end of the file. -__all__ = ["getlocale", "getdefaultlocale", "getpreferredencoding", "Error", - "setlocale", "resetlocale", "localeconv", "strcoll", "strxfrm", +__all__ = ["getlocale", "getpreferredencoding", "Error", + "setlocale", "localeconv", "strcoll", "strxfrm", "str", "atof", "atoi", "format_string", "currency", "normalize", "LC_CTYPE", "LC_COLLATE", "LC_TIME", "LC_MONETARY", "LC_NUMERIC", "LC_ALL", "CHAR_MAX", "getencoding"] @@ -516,67 +516,6 @@ def _build_localename(localetuple): raise TypeError('Locale must be None, a string, or an iterable of ' 'two strings -- language code, encoding.') from None -def getdefaultlocale(envvars=('LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE')): - - """ Tries to determine the default locale settings and returns - them as tuple (language code, encoding). - - According to POSIX, a program which has not called - setlocale(LC_ALL, "") runs using the portable 'C' locale. - Calling setlocale(LC_ALL, "") lets it use the default locale as - defined by the LANG variable. Since we don't want to interfere - with the current locale setting we thus emulate the behavior - in the way described above. - - To maintain compatibility with other platforms, not only the - LANG variable is tested, but a list of variables given as - envvars parameter. The first found to be defined will be - used. envvars defaults to the search path used in GNU gettext; - it must always contain the variable name 'LANG'. - - Except for the code 'C', the language code corresponds to RFC - 1766. code and encoding can be None in case the values cannot - be determined. - - """ - - import warnings - warnings.warn( - "Use setlocale(), getencoding() and getlocale() instead", - DeprecationWarning, stacklevel=2 - ) - return _getdefaultlocale(envvars) - -def _getdefaultlocale(envvars=('LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE')): - try: - # check if it's supported by the _locale module - import _locale - code, encoding = _locale._getdefaultlocale() - except (ImportError, AttributeError): - pass - else: - # make sure the code/encoding values are valid - if sys.platform == "win32" and code and code[:2] == "0x": - # map windows language identifier to language name - code = windows_locale.get(int(code, 0)) - # ...add other platform-specific processing here, if - # necessary... - return code, encoding - - # fall back on POSIX behaviour - import os - lookup = os.environ.get - for variable in envvars: - localename = lookup(variable,None) - if localename: - if variable == 'LANGUAGE': - localename = localename.split(':')[0] - break - else: - localename = 'C' - return _parse_localename(localename) - - def getlocale(category=LC_CTYPE): """ Returns the current setting for the given locale category as @@ -612,40 +551,15 @@ def setlocale(category, locale=None): locale = normalize(_build_localename(locale)) return _setlocale(category, locale) -def resetlocale(category=LC_ALL): - - """ Sets the locale for category to the default setting. - - The default setting is determined by calling - getdefaultlocale(). category defaults to LC_ALL. - - """ - import warnings - warnings.warn( - 'Use locale.setlocale(locale.LC_ALL, "") instead', - DeprecationWarning, stacklevel=2 - ) - - with warnings.catch_warnings(): - warnings.simplefilter('ignore', category=DeprecationWarning) - loc = getdefaultlocale() - - _setlocale(category, _build_localename(loc)) - try: from _locale import getencoding except ImportError: + # When _locale.getencoding() is missing, use the Python filesystem + # encoding. + _encoding = sys.getfilesystemencoding() def getencoding(): - if hasattr(sys, 'getandroidapilevel'): - # On Android langinfo.h and CODESET are missing, and UTF-8 is - # always used in mbstowcs() and wcstombs(). - return 'utf-8' - encoding = _getdefaultlocale()[1] - if encoding is None: - # LANG not set, default to UTF-8 - encoding = 'utf-8' - return encoding + return _encoding try: CODESET @@ -1713,13 +1627,6 @@ def _init_categories(categories=categories): _init_categories() del categories['LC_ALL'] - print('Locale defaults as determined by getdefaultlocale():') - print('-'*72) - lang, enc = getdefaultlocale() - print('Language: ', lang or '(undefined)') - print('Encoding: ', enc or '(undefined)') - print() - print('Locale settings on startup:') print('-'*72) for name,category in categories.items(): @@ -1729,17 +1636,6 @@ def _init_categories(categories=categories): print(' Encoding: ', enc or '(undefined)') print() - print() - print('Locale settings after calling resetlocale():') - print('-'*72) - resetlocale() - for name,category in categories.items(): - print(name, '...') - lang, enc = getlocale(category) - print(' Language: ', lang or '(undefined)') - print(' Encoding: ', enc or '(undefined)') - print() - try: setlocale(LC_ALL, "") except: diff --git a/Lib/test/test_locale.py b/Lib/test/test_locale.py index b0d7998559759ef..cd738474214a4e6 100644 --- a/Lib/test/test_locale.py +++ b/Lib/test/test_locale.py @@ -478,43 +478,9 @@ def test_japanese(self): class TestMiscellaneous(unittest.TestCase): - def test_defaults_UTF8(self): - # Issue #18378: on (at least) macOS setting LC_CTYPE to "UTF-8" is - # valid. Furthermore LC_CTYPE=UTF is used by the UTF-8 locale coercing - # during interpreter startup (on macOS). - import _locale - import os - + def test_parse_localename(self): self.assertEqual(locale._parse_localename('UTF-8'), (None, 'UTF-8')) - if hasattr(_locale, '_getdefaultlocale'): - orig_getlocale = _locale._getdefaultlocale - del _locale._getdefaultlocale - else: - orig_getlocale = None - - orig_env = {} - try: - for key in ('LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE'): - if key in os.environ: - orig_env[key] = os.environ[key] - del os.environ[key] - - os.environ['LC_CTYPE'] = 'UTF-8' - - with check_warnings(('', DeprecationWarning)): - self.assertEqual(locale.getdefaultlocale(), (None, 'UTF-8')) - - finally: - for k in orig_env: - os.environ[k] = orig_env[k] - - if 'LC_CTYPE' not in orig_env: - del os.environ['LC_CTYPE'] - - if orig_getlocale is not None: - _locale._getdefaultlocale = orig_getlocale - def test_getencoding(self): # Invoke getencoding to make sure it does not cause exceptions. enc = locale.getencoding() diff --git a/Misc/NEWS.d/next/Library/2023-05-23-04-01-27.gh-issue-104783.QyhIoq.rst b/Misc/NEWS.d/next/Library/2023-05-23-04-01-27.gh-issue-104783.QyhIoq.rst new file mode 100644 index 000000000000000..4af78be6045a85a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-05-23-04-01-27.gh-issue-104783.QyhIoq.rst @@ -0,0 +1,2 @@ +Remove two :mod:`locale` functions deprecated in Python 3.11: +``getdefaultlocale()`` and ``resetlocale()``. Patch by Victor Stinner.