Handled Py11 getdefaultlocale() deprecation warnings (#754)

pull/928/head
Chris Caron 2023-08-20 00:20:46 -04:00 committed by GitHub
parent e46f8fec1d
commit 5fd568fa35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 55 additions and 22 deletions

View File

@ -33,6 +33,8 @@
import ctypes import ctypes
import locale import locale
import contextlib import contextlib
import os
import re
from os.path import join from os.path import join
from os.path import dirname from os.path import dirname
from os.path import abspath from os.path import abspath
@ -95,6 +97,17 @@ class AppriseLocale:
""" """
# Locale regular expression
_local_re = re.compile(
r'^\s*(?P<lang>[a-z]{2})([_:]((?P<country>[a-z]{2}))?'
r'(\.(?P<enc>[a-z0-9]+))?|.+)?', re.IGNORECASE)
# Define our default encoding
_default_encoding = 'utf-8'
# Define our default language
_default_language = 'en'
def __init__(self, language=None): def __init__(self, language=None):
""" """
Initializes our object, if a language is specified, then we Initializes our object, if a language is specified, then we
@ -181,7 +194,7 @@ class AppriseLocale:
@staticmethod @staticmethod
def detect_language(lang=None, detect_fallback=True): def detect_language(lang=None, detect_fallback=True):
""" """
returns the language (if it's retrievable) Returns the language (if it's retrievable)
""" """
# We want to only use the 2 character version of this language # We want to only use the 2 character version of this language
# hence en_CA becomes en, en_US becomes en. # hence en_CA becomes en, en_US becomes en.
@ -190,6 +203,17 @@ class AppriseLocale:
# no detection enabled; we're done # no detection enabled; we're done
return None return None
# Posix lookup
lookup = os.environ.get
localename = None
for variable in ('LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE'):
localename = lookup(variable, None)
if localename:
result = AppriseLocale._local_re.match(localename)
if result and result.group('lang'):
return result.group('lang').lower()
# Windows handling
if hasattr(ctypes, 'windll'): if hasattr(ctypes, 'windll'):
windll = ctypes.windll.kernel32 windll = ctypes.windll.kernel32
try: try:
@ -203,11 +227,12 @@ class AppriseLocale:
# Fallback to posix detection # Fallback to posix detection
pass pass
# Linux Handling
try: try:
# Detect language # Acquire our locale
lang = locale.getdefaultlocale()[0] lang = locale.getlocale()[0]
except ValueError as e: except TypeError as e:
# This occurs when an invalid locale was parsed from the # This occurs when an invalid locale was parsed from the
# environment variable. While we still return None in this # environment variable. While we still return None in this
# case, we want to better notify the end user of this. Users # case, we want to better notify the end user of this. Users
@ -217,11 +242,6 @@ class AppriseLocale:
'Language detection failure / {}'.format(str(e))) 'Language detection failure / {}'.format(str(e)))
return None return None
except TypeError:
# None is returned if the default can't be determined
# we're done in this case
return None
return None if not lang else lang[0:2].lower() return None if not lang else lang[0:2].lower()
def __getstate__(self): def __getstate__(self):

View File

@ -36,6 +36,7 @@ import json
import contextlib import contextlib
import os import os
import hashlib import hashlib
import locale
from itertools import chain from itertools import chain
from os.path import expanduser from os.path import expanduser
from functools import reduce from functools import reduce
@ -1488,7 +1489,7 @@ def environ(*remove, **update):
# Create a backup of our environment for restoration purposes # Create a backup of our environment for restoration purposes
env_orig = os.environ.copy() env_orig = os.environ.copy()
loc_orig = locale.getlocale()
try: try:
os.environ.update(update) os.environ.update(update)
[os.environ.pop(k, None) for k in remove] [os.environ.pop(k, None) for k in remove]
@ -1497,6 +1498,13 @@ def environ(*remove, **update):
finally: finally:
# Restore our snapshot # Restore our snapshot
os.environ = env_orig.copy() os.environ = env_orig.copy()
try:
# Restore locale
locale.setlocale(locale.LC_ALL, loc_orig)
except locale.Error:
# Thrown in py3.6
pass
def apply_template(template, app_mode=TemplateType.RAW, **kwargs): def apply_template(template, app_mode=TemplateType.RAW, **kwargs):

View File

@ -144,6 +144,7 @@ def test_detect_language_windows_users():
if hasattr(ctypes, 'windll'): if hasattr(ctypes, 'windll'):
from ctypes import windll from ctypes import windll
else: else:
windll = mock.Mock() windll = mock.Mock()
# 4105 = en_CA # 4105 = en_CA
@ -164,18 +165,17 @@ def test_detect_language_windows_users():
assert AppriseLocale.AppriseLocale.detect_language() == 'en' assert AppriseLocale.AppriseLocale.detect_language() == 'en'
@pytest.mark.skipif(sys.platform == "win32", reason="Does not work on Windows") def test_detect_language_using_env():
def test_detect_language_windows_users_croaks_please_review():
""" """
When enabling CI testing on Windows, those tests did not produce the Test the reading of information from an environment variable
correct results. They may want to be reviewed.
""" """
# The below accesses the windows fallback code and fail # The below accesses the windows fallback code and fail
# then it will resort to the environment variables. # then it will resort to the environment variables.
with environ('LANG', 'LANGUAGE', 'LC_ALL', 'LC_CTYPE'): with environ('LANG', 'LANGUAGE', 'LC_ALL', 'LC_CTYPE'):
# Language can't be detected # Language can now be detected in this case
assert AppriseLocale.AppriseLocale.detect_language() is None assert isinstance(
AppriseLocale.AppriseLocale.detect_language(), str)
# Detect French language. # Detect French language.
with environ('LANGUAGE', 'LC_ALL', 'LC_CTYPE', LANG="fr_CA"): with environ('LANGUAGE', 'LC_ALL', 'LC_CTYPE', LANG="fr_CA"):
@ -187,23 +187,28 @@ def test_detect_language_windows_users_croaks_please_review():
# dropped, but just to ensure this issue does not come back, we keep # dropped, but just to ensure this issue does not come back, we keep
# this test: # this test:
with environ(*list(os.environ.keys()), LC_CTYPE="UTF-8"): with environ(*list(os.environ.keys()), LC_CTYPE="UTF-8"):
assert AppriseLocale.AppriseLocale.detect_language() is None assert isinstance(AppriseLocale.AppriseLocale.detect_language(), str)
# Test with absolutely no environment variables what-so-ever # Test with absolutely no environment variables what-so-ever
with environ(*list(os.environ.keys())): with environ(*list(os.environ.keys())):
assert AppriseLocale.AppriseLocale.detect_language() is None assert isinstance(AppriseLocale.AppriseLocale.detect_language(), str)
@pytest.mark.skipif(sys.platform == "win32", reason="Does not work on Windows") @pytest.mark.skipif(sys.platform == "win32", reason="Does not work on Windows")
@mock.patch('locale.getdefaultlocale') @mock.patch('locale.getlocale')
def test_detect_language_defaultlocale(mock_getlocale): def test_detect_language_locale(mock_getlocale):
""" """
API: Apprise() Default locale detection API: Apprise() Default locale detection
""" """
# Handle case where getdefaultlocale() can't be detected # Handle case where getlocale() can't be detected
mock_getlocale.return_value = None mock_getlocale.return_value = None
assert AppriseLocale.AppriseLocale.detect_language() is None with environ('LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE'):
assert AppriseLocale.AppriseLocale.detect_language() is None
mock_getlocale.return_value = (None, None)
with environ('LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE'):
assert AppriseLocale.AppriseLocale.detect_language() is None
# if detect_language and windows env fail us, then we don't # if detect_language and windows env fail us, then we don't
# set up a default language on first load # set up a default language on first load