Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handling getdefaultlocale() deprecation warnings #754

Merged
merged 4 commits into from
Aug 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 29 additions & 9 deletions apprise/AppriseLocale.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
import ctypes
import locale
import contextlib
import os
import re
from os.path import join
from os.path import dirname
from os.path import abspath
Expand Down Expand Up @@ -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):
"""
Initializes our object, if a language is specified, then we
Expand Down Expand Up @@ -181,7 +194,7 @@ def lang_at(self, lang):
@staticmethod
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
# hence en_CA becomes en, en_US becomes en.
Expand All @@ -190,6 +203,17 @@ def detect_language(lang=None, detect_fallback=True):
# no detection enabled; we're done
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'):
windll = ctypes.windll.kernel32
try:
Expand All @@ -203,11 +227,12 @@ def detect_language(lang=None, detect_fallback=True):
# Fallback to posix detection
pass

# Linux Handling
try:
# Detect language
lang = locale.getdefaultlocale()[0]
# Acquire our locale
lang = locale.getlocale()[0]

except ValueError as e:
except TypeError as e:
# This occurs when an invalid locale was parsed from the
# environment variable. While we still return None in this
# case, we want to better notify the end user of this. Users
Expand All @@ -217,11 +242,6 @@ def detect_language(lang=None, detect_fallback=True):
'Language detection failure / {}'.format(str(e)))
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()

def __getstate__(self):
Expand Down
10 changes: 9 additions & 1 deletion apprise/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import contextlib
import os
import hashlib
import locale
from itertools import chain
from os.path import expanduser
from functools import reduce
Expand Down Expand Up @@ -1488,7 +1489,7 @@ def environ(*remove, **update):

# Create a backup of our environment for restoration purposes
env_orig = os.environ.copy()

loc_orig = locale.getlocale()
try:
os.environ.update(update)
[os.environ.pop(k, None) for k in remove]
Expand All @@ -1497,6 +1498,13 @@ def environ(*remove, **update):
finally:
# Restore our snapshot
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):
Expand Down
29 changes: 17 additions & 12 deletions test/test_locale.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ def test_detect_language_windows_users():

if hasattr(ctypes, 'windll'):
from ctypes import windll

else:
windll = mock.Mock()
# 4105 = en_CA
Expand All @@ -164,18 +165,17 @@ def test_detect_language_windows_users():
assert AppriseLocale.AppriseLocale.detect_language() == 'en'


@pytest.mark.skipif(sys.platform == "win32", reason="Does not work on Windows")
def test_detect_language_windows_users_croaks_please_review():
def test_detect_language_using_env():
"""
When enabling CI testing on Windows, those tests did not produce the
correct results. They may want to be reviewed.
Test the reading of information from an environment variable
"""

# The below accesses the windows fallback code and fail
# then it will resort to the environment variables.
with environ('LANG', 'LANGUAGE', 'LC_ALL', 'LC_CTYPE'):
# Language can't be detected
assert AppriseLocale.AppriseLocale.detect_language() is None
# Language can now be detected in this case
assert isinstance(
AppriseLocale.AppriseLocale.detect_language(), str)

# Detect French language.
with environ('LANGUAGE', 'LC_ALL', 'LC_CTYPE', LANG="fr_CA"):
Expand All @@ -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
# this test:
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
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")
@mock.patch('locale.getdefaultlocale')
def test_detect_language_defaultlocale(mock_getlocale):
@mock.patch('locale.getlocale')
def test_detect_language_locale(mock_getlocale):
"""
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
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
# set up a default language on first load
Expand Down