[PATCH 15/22] Avoid setting $LANG and $LANGUAGE, except where we can't

David Shea dshea at redhat.com
Wed Jun 3 16:03:39 UTC 2015


Remove the language argument from AnacondaBaseWindow.retranslate.
Setting the locale and language environment is handled by the caller, so
retranslate should only refresh translations to the current locale.

Setting $LANG is needed by Python's gettext module, so we can't get rid
of all of it. Handle the UEFI firmware language by doing it earlier, and
handle GUI changes by setting the envrionment variable in a way that
kinda maybe sort of looks thread-safe.

(cherry picked from commit 043fd56af3e6c65fbb6eea33c5bbe6b11ef4dde8)

Resolves: rhbz#1188287
---
 anaconda                            |  9 ++++++---
 pyanaconda/localization.py          | 11 +++++++++--
 pyanaconda/ui/gui/spokes/welcome.py | 36 ++++++++++++++++++++++++++++++++----
 widgets/configure.ac                |  2 +-
 widgets/src/BaseWindow.c            |  6 +-----
 widgets/src/BaseWindow.h            |  2 +-
 widgets/src/StandaloneWindow.c      |  5 ++---
 widgets/src/StandaloneWindow.h      |  2 +-
 8 files changed, 53 insertions(+), 20 deletions(-)

diff --git a/anaconda b/anaconda
index e8f1776..de2d77a 100755
--- a/anaconda
+++ b/anaconda
@@ -880,6 +880,7 @@ if __name__ == "__main__":
     signal.signal(signal.SIGTERM, lambda num, frame: sys.exit(1))
 
     setupEnvironment()
+
     # make sure we have /var/log soon, some programs fail to start without it
     iutil.mkdirChain("/var/log")
 
@@ -1108,6 +1109,11 @@ if __name__ == "__main__":
     from pyanaconda import localization
     # Set the language before loading an interface, when it may be too late.
 
+    # first, try to load firmware language if nothing is already set in
+    # the environment
+    if "LANG" not in os.environ:
+        localization.load_firmware_language(ksdata.lang)
+
     # check if the LANG environmental variable is set
     env_lang = os.environ.get("LANG")
     if env_lang is not None:
@@ -1241,9 +1247,6 @@ if __name__ == "__main__":
         if not anaconda.ksdata.timezone.nontp:
             iutil.start_service("chronyd")
 
-    # try to load firmware language
-    localization.load_firmware_language(ksdata.lang)
-
     # FIXME:  This will need to be made cleaner once this file starts to take
     # shape with the new UI code.
     anaconda._intf.setup(ksdata)
diff --git a/pyanaconda/localization.py b/pyanaconda/localization.py
index a7cc908..30112f9 100644
--- a/pyanaconda/localization.py
+++ b/pyanaconda/localization.py
@@ -29,7 +29,7 @@ import glob
 from collections import namedtuple
 
 from pyanaconda import constants
-from pyanaconda.iutil import upcase_first_letter
+from pyanaconda.iutil import upcase_first_letter, setenv
 
 import logging
 log = logging.getLogger("anaconda")
@@ -187,6 +187,9 @@ def setup_locale(locale, lang=None):
     ksdata.lang object (if given). DOES NOT PERFORM ANY CHECKS OF THE GIVEN
     LOCALE.
 
+    $LANG must be set by the caller in order to set the language used by gettext.
+    Doing this in a thread-safe way is up to the caller.
+
     :param locale: locale to setup
     :type locale: str
     :param lang: ksdata.lang object or None
@@ -198,7 +201,7 @@ def setup_locale(locale, lang=None):
     if lang:
         lang.lang = locale
 
-    os.environ["LANG"] = locale
+    setenv("LANG", locale)
     locale_mod.setlocale(locale_mod.LC_ALL, locale)
 
 def get_english_name(locale):
@@ -420,6 +423,8 @@ def load_firmware_language(lang):
     information in the given ksdata.lang object and sets the $LANG environment
     variable.
 
+    This method must be run before any other threads are started.
+
     :param lang: ksdata.lang object
     :return: None
     :rtype: None
@@ -471,6 +476,8 @@ def load_firmware_language(lang):
     log.debug("Using UEFI PlatformLang '%s' ('%s') as our language.", d, locales[0])
     setup_locale(locales[0], lang)
 
+    os.environ["LANG"] = locales[0] # pylint: disable=environment-modify
+
 _DateFieldSpec = namedtuple("DateFieldSpec", ["format", "suffix"])
 
 def resolve_date_format(year, month, day, fail_safe=True):
diff --git a/pyanaconda/ui/gui/spokes/welcome.py b/pyanaconda/ui/gui/spokes/welcome.py
index bf4fbbd..1dbe527 100644
--- a/pyanaconda/ui/gui/spokes/welcome.py
+++ b/pyanaconda/ui/gui/spokes/welcome.py
@@ -23,10 +23,11 @@
 import sys
 import re
 import langtable
+import os
 
 from pyanaconda.ui.gui.hubs.summary import SummaryHub
 from pyanaconda.ui.gui.spokes import StandaloneSpoke
-from pyanaconda.ui.gui.utils import setup_gtk_direction, escape_markup
+from pyanaconda.ui.gui.utils import setup_gtk_direction, escape_markup, gtk_action_wait
 from pyanaconda.ui.gui.xkl_wrapper import XklWrapper
 from pyanaconda.ui.gui.spokes.lib.lang_locale_handler import LangLocaleHandler
 
@@ -65,6 +66,7 @@ class WelcomeLanguageSpoke(LangLocaleHandler, StandaloneSpoke):
         (store, itr) = self._localeSelection.get_selected()
 
         locale = store[itr][1]
+        self._set_lang(locale)
         localization.setup_locale(locale, self.data.lang)
 
         # Skip timezone and keyboard default setting for kickstart installs.
@@ -194,6 +196,7 @@ class WelcomeLanguageSpoke(LangLocaleHandler, StandaloneSpoke):
         # if there are no translations for the given locales,
         # use default
         if not langs_with_translations:
+            self._set_lang(DEFAULT_LANG)
             localization.setup_locale(DEFAULT_LANG, self.data.lang)
             lang_itr, _locale_itr = self._select_locale(self.data.lang.lang)
             langs_with_translations[DEFAULT_LANG] = lang_itr
@@ -218,6 +221,7 @@ class WelcomeLanguageSpoke(LangLocaleHandler, StandaloneSpoke):
         store.set(newItr, 0, "", 1, "", 2, "", 3, True)
 
         # setup the "best" locale
+        self._set_lang(locales[0])
         localization.setup_locale(locales[0], self.data.lang)
         self._select_locale(self.data.lang.lang)
 
@@ -235,7 +239,7 @@ class WelcomeLanguageSpoke(LangLocaleHandler, StandaloneSpoke):
         else:
             widget.set_label(_(before))
 
-    def retranslate(self, lang):
+    def retranslate(self):
         # Change the translations on labels and buttons that do not have
         # substitution text.
         for name in ["pickLanguageLabel", "betaWarnTitle", "betaWarnDesc"]:
@@ -263,7 +267,7 @@ class WelcomeLanguageSpoke(LangLocaleHandler, StandaloneSpoke):
 
         # And of course, don't forget the underlying window.
         self.window.set_property("distribution", distributionText().upper())
-        self.window.retranslate(lang)
+        self.window.retranslate()
 
     def refresh(self):
         self._select_locale(self.data.lang.lang)
@@ -297,8 +301,9 @@ class WelcomeLanguageSpoke(LangLocaleHandler, StandaloneSpoke):
 
         if selected:
             lang = store[selected[0]][1]
+            self._set_lang(lang)
             localization.setup_locale(lang)
-            self.retranslate(lang)
+            self.retranslate()
 
             # Reset the text direction
             setup_gtk_direction()
@@ -330,3 +335,26 @@ class WelcomeLanguageSpoke(LangLocaleHandler, StandaloneSpoke):
                 sys.exit(0)
 
         StandaloneSpoke._on_continue_clicked(self, window, user_data)
+
+    @gtk_action_wait
+    def _set_lang(self, lang):
+        # This is *hopefully* safe. The only threads that might be running
+        # outside of the GIL are those doing file operations, the Gio dbus
+        # proxy thread, and calls from the Gtk main loop. The file operations
+        # won't be doing things that may access the environment, fingers
+        # crossed, the GDbus thread shouldn't be doing anything weird since all
+        # of our dbus calls are from python and synchronous. Using
+        # gtk_action_wait ensures that this is Gtk main loop thread, and it's
+        # holding the GIL.
+        #
+        # There is a lot of uncertainty and weasliness in those statements.
+        # This is not good code.
+        #
+        # We cannot get around setting $LANG. Python's gettext implementation
+        # differs from C in that consults only the environment for the current
+        # language and not the data set via setlocale. If we want translations
+        # from python modules to work, something needs to be set in the
+        # environment when the language changes.
+
+        # pylint: disable=environment-modify
+        os.environ["LANG"] = lang
diff --git a/widgets/configure.ac b/widgets/configure.ac
index 83c197d..cf6e5f9 100644
--- a/widgets/configure.ac
+++ b/widgets/configure.ac
@@ -62,7 +62,7 @@ AC_CHECK_HEADERS([libintl.h stdlib.h string.h unistd.h])
 
 AC_STRUCT_TIMEZONE
 
-AC_CHECK_FUNCS([pow setenv setlocale strchr])
+AC_CHECK_FUNCS([pow setlocale strchr])
 AC_CONFIG_FILES([Makefile
                  doc/Makefile
                  glade/Makefile
diff --git a/widgets/src/BaseWindow.c b/widgets/src/BaseWindow.c
index b739701..4009ddc 100644
--- a/widgets/src/BaseWindow.c
+++ b/widgets/src/BaseWindow.c
@@ -670,7 +670,6 @@ void anaconda_base_window_clear_info(AnacondaBaseWindow *win) {
 /**
  * anaconda_base_window_retranslate:
  * @win: a #AnacondaBaseWindow
- * @lang: target language
  *
  * Reload translations for this widget as needed.  Generally, this is not
  * needed.  However when changing the language during installation, we need
@@ -679,12 +678,9 @@ void anaconda_base_window_clear_info(AnacondaBaseWindow *win) {
  *
  * Since: 1.0
  */
-void anaconda_base_window_retranslate(AnacondaBaseWindow *win, const char *lang) {
+void anaconda_base_window_retranslate(AnacondaBaseWindow *win) {
     GValue distro = G_VALUE_INIT;
 
-    setenv("LANGUAGE", lang, 1);
-    setlocale(LC_ALL, "");
-
     /* This bit is internal gettext magic. */
     {
         extern int _nl_msg_cat_cntr;
diff --git a/widgets/src/BaseWindow.h b/widgets/src/BaseWindow.h
index 0e29df9..193bbb1 100644
--- a/widgets/src/BaseWindow.h
+++ b/widgets/src/BaseWindow.h
@@ -69,7 +69,7 @@ struct _AnacondaBaseWindowClass {
 GType       anaconda_base_window_get_type (void);
 GtkWidget  *anaconda_base_window_new      ();
 
-void        anaconda_base_window_retranslate (AnacondaBaseWindow *win, const char *lang);
+void        anaconda_base_window_retranslate (AnacondaBaseWindow *win);
 
 gboolean    anaconda_base_window_get_beta (AnacondaBaseWindow *win);
 void        anaconda_base_window_set_beta (AnacondaBaseWindow *win, gboolean is_beta);
diff --git a/widgets/src/StandaloneWindow.c b/widgets/src/StandaloneWindow.c
index 8281184..4dfd2cd 100644
--- a/widgets/src/StandaloneWindow.c
+++ b/widgets/src/StandaloneWindow.c
@@ -173,7 +173,6 @@ static void anaconda_standalone_window_init(AnacondaStandaloneWindow *win) {
 /**
  * anaconda_standalone_window_retranslate:
  * @win: a #AnacondaStandaloneWindow
- * @lang: target language
  *
  * Reload translations for this widget as needed.  Generally, this is not
  * needed.  However when changing the language during installation, we need
@@ -182,8 +181,8 @@ static void anaconda_standalone_window_init(AnacondaStandaloneWindow *win) {
  *
  * Since: 1.0
  */
-void anaconda_standalone_window_retranslate(AnacondaStandaloneWindow *win, const char *lang) {
-    anaconda_base_window_retranslate(ANACONDA_BASE_WINDOW(win), lang);
+void anaconda_standalone_window_retranslate(AnacondaStandaloneWindow *win) {
+    anaconda_base_window_retranslate(ANACONDA_BASE_WINDOW(win));
 
     gtk_button_set_label(GTK_BUTTON(win->priv->quit_button), _(QUIT_TEXT));
     gtk_button_set_label(GTK_BUTTON(win->priv->continue_button), _(CONTINUE_TEXT));
diff --git a/widgets/src/StandaloneWindow.h b/widgets/src/StandaloneWindow.h
index 63a9e9e..85671f2 100644
--- a/widgets/src/StandaloneWindow.h
+++ b/widgets/src/StandaloneWindow.h
@@ -63,7 +63,7 @@ struct _AnacondaStandaloneWindowClass {
 
 GType       anaconda_standalone_window_get_type (void);
 GtkWidget  *anaconda_standalone_window_new      ();
-void        anaconda_standalone_window_retranslate       (AnacondaStandaloneWindow *win, const char *lang);
+void        anaconda_standalone_window_retranslate       (AnacondaStandaloneWindow *win);
 
 G_END_DECLS
 
-- 
2.1.0



More information about the anaconda-patches mailing list