Repository: keras-team/keras (python)

Issue:
UnicodeEncodeError when running keras.model.predict
The following problem occurs in keras version 3.1.1 (tensorflow version 2.16.1)
UnicodeEncodeError: 'charmap' codec can't encode characters in position 19-38: character maps to <undefined>

prediction = trained_nn_model.predict(last_sequence)
A bit more details:
  File "...\lib\site-packages\keras\src\utils\traceback_utils.py", line 122, in error_handler
    raise e.with_traceback(filtered_tb) from None
  File "...\.pyenv\pyenv-win\versions\3.10.5\lib\encodings\cp1252.py", line 20, in encode
    return codecs.charmap_encode(input,self.errors,encoding_table)[0]
UnicodeEncodeError: 'charmap' codec can't encode characters in position 19-38: character maps to <undefined>

(The positions varies when tried with different test data).
The code works ok with keras version 2.15.0 (tensorflow 2.15.0)

Pull request description:
Fix encoding issue
Fixes #19386 

The root cause is that `━` is not supported by CP125* encoding. We can replace it with `=` which aligns with Keras2 behavior.
Additionally, `print_msg` has been modified to encode and decode to the target encoding, providing a more robust approach.


Patch:
diff --git a/keras/src/utils/io_utils.py b/keras/src/utils/io_utils.py
index 32322f405..f087ab6dd 100644
--- a/keras/src/utils/io_utils.py
+++ b/keras/src/utils/io_utils.py
@@ -91,10 +91,18 @@ def set_logging_verbosity(level):
 
 def print_msg(message, line_break=True):
     """Print the message to absl logging or stdout."""
+    message = str(message)
     if is_interactive_logging_enabled():
-        if line_break:
-            sys.stdout.write(message + "\n")
-        else:
+        message = message + "\n" if line_break else message
+        try:
+            sys.stdout.write(message)
+        except UnicodeEncodeError:
+            # If the encoding differs from UTF-8, `sys.stdout.write` may fail.
+            # To address this, replace special unicode characters in the
+            # message, and then encode and decode using the target encoding.
+            message = _replace_special_unicode_character(message)
+            message_bytes = message.encode(sys.stdout.encoding, errors="ignore")
+            message = message_bytes.decode(sys.stdout.encoding)
             sys.stdout.write(message)
         sys.stdout.flush()
     else:
@@ -123,3 +131,8 @@ def ask_to_proceed_with_overwrite(filepath):
         return False
     print_msg("[TIP] Next time specify overwrite=True!")
     return True
+
+
+def _replace_special_unicode_character(message):
+    message = str(message).replace("━", "=")  # Fall back to Keras2 behavior.
+    return message


Test patch:
diff --git a/keras/src/utils/io_utils_test.py b/keras/src/utils/io_utils_test.py
index 235314de3..2fe1fbbea 100644
--- a/keras/src/utils/io_utils_test.py
+++ b/keras/src/utils/io_utils_test.py
@@ -1,3 +1,5 @@
+import sys
+import tempfile
 from unittest.mock import patch
 
 from keras.src.testing import test_case
@@ -55,3 +57,13 @@ class TestIoUtils(test_case.TestCase):
     @patch("builtins.input", side_effect=["invalid", "n"])
     def test_ask_to_proceed_with_overwrite_invalid_then_no(self, _):
         self.assertFalse(io_utils.ask_to_proceed_with_overwrite("test_path"))
+
+    def test_print_msg_with_different_encoding(self):
+        # https://github.com/keras-team/keras/issues/19386
+        io_utils.enable_interactive_logging()
+        self.assertTrue(io_utils.is_interactive_logging_enabled())
+        ori_stdout = sys.stdout
+        with tempfile.TemporaryFile(mode="w", encoding="cp1251") as tmp:
+            sys.stdout = tmp
+            io_utils.print_msg("━")
+        sys.stdout = ori_stdout