This is an automated email from the ASF dual-hosted git repository.

JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git


The following commit(s) were added to refs/heads/master by this push:
     new 05b0656211 [python] Handle platforms without getuid in ticket-cache 
lookup (#10827)
05b0656211 is described below

commit 05b0656211fefa3465c85239da04abb1e0db6875
Author: skyyyy955 <[email protected]>
AuthorDate: Tue Oct 27 14:10:25 2026 +0800

    [python] Handle platforms without getuid in ticket-cache lookup (#10827)
---
 paimon-python/pypaimon/filesystem/_kerberos.py |  2 +
 paimon-python/pypaimon/tests/kerberos_test.py  | 70 +++++++++++++++++++++++++-
 2 files changed, 71 insertions(+), 1 deletion(-)

diff --git a/paimon-python/pypaimon/filesystem/_kerberos.py 
b/paimon-python/pypaimon/filesystem/_kerberos.py
index 00a31ba975..7098f7e9da 100644
--- a/paimon-python/pypaimon/filesystem/_kerberos.py
+++ b/paimon-python/pypaimon/filesystem/_kerberos.py
@@ -39,6 +39,8 @@ def get_ticket_cache_path() -> Optional[str]:
         if cc.startswith('FILE:'):
             return cc[5:]
         return cc
+    if not hasattr(os, 'getuid'):
+        return None
     default_path = f'/tmp/krb5cc_{os.getuid()}'
     if os.path.exists(default_path):
         return default_path
diff --git a/paimon-python/pypaimon/tests/kerberos_test.py 
b/paimon-python/pypaimon/tests/kerberos_test.py
index 722043d778..1a95a15082 100644
--- a/paimon-python/pypaimon/tests/kerberos_test.py
+++ b/paimon-python/pypaimon/tests/kerberos_test.py
@@ -18,6 +18,7 @@
 import os
 import tempfile
 import unittest
+from types import SimpleNamespace
 from unittest.mock import MagicMock, patch
 
 from pypaimon.common.options import Options
@@ -218,7 +219,7 @@ class KerberosHdfsTest(unittest.TestCase):
         env.pop("KRB5CCNAME", None)
         with patch.dict(os.environ, env, clear=True):
             with patch("os.path.exists", return_value=True):
-                with patch("os.getuid", return_value=1000):
+                with patch("os.getuid", return_value=1000, create=True):
                     path = PyArrowFileIO._get_ticket_cache_path()
                     self.assertEqual(path, "/tmp/krb5cc_1000")
 
@@ -310,5 +311,72 @@ class KerberosHdfsTest(unittest.TestCase):
                                       str(ctx.exception))
 
 
+class KerberosCachePortabilityTest(unittest.TestCase):
+
+    def test_no_uid_or_explicit_cache_returns_none(self):
+        from pypaimon.filesystem import _kerberos
+
+        system = SimpleNamespace(environ={}, path=MagicMock())
+        with patch.object(_kerberos, 'os', system):
+            self.assertIsNone(_kerberos.get_ticket_cache_path())
+        system.path.exists.assert_not_called()
+
+    def test_explicit_cache_does_not_require_uid(self):
+        from pypaimon.filesystem import _kerberos
+
+        for value, expected in [
+            ('FILE:C:/cache/ticket', 'C:/cache/ticket'),
+            ('C:/cache/ticket', 'C:/cache/ticket'),
+            ('MEMORY:ticket', 'MEMORY:ticket'),
+        ]:
+            with self.subTest(value=value):
+                system = SimpleNamespace(
+                    environ={'KRB5CCNAME': value}, path=MagicMock())
+                with patch.object(_kerberos, 'os', system):
+                    self.assertEqual(_kerberos.get_ticket_cache_path(), 
expected)
+                system.path.exists.assert_not_called()
+                self.assertEqual(system.environ, {'KRB5CCNAME': value})
+
+    def test_unix_default_cache_requires_existing_file(self):
+        from pypaimon.filesystem import _kerberos
+
+        for exists in [True, False]:
+            with self.subTest(exists=exists):
+                system = SimpleNamespace(
+                    environ={}, path=MagicMock(), 
getuid=MagicMock(return_value=1000))
+                system.path.exists.return_value = exists
+                with patch.object(_kerberos, 'os', system):
+                    self.assertEqual(_kerberos.get_ticket_cache_path(),
+                                     '/tmp/krb5cc_1000' if exists else None)
+                system.getuid.assert_called_once_with()
+                system.path.exists.assert_called_once_with('/tmp/krb5cc_1000')
+
+    def test_uid_errors_are_not_suppressed(self):
+        from pypaimon.filesystem import _kerberos
+
+        system = SimpleNamespace(
+            environ={}, path=MagicMock(), 
getuid=MagicMock(side_effect=OSError('uid failed')))
+        with patch.object(_kerberos, 'os', system):
+            with self.assertRaisesRegex(OSError, 'uid failed'):
+                _kerberos.get_ticket_cache_path()
+        system.path.exists.assert_not_called()
+
+    def test_native_keytab_without_cache_still_raises(self):
+        from pypaimon.filesystem import _kerberos
+        from pypaimon.filesystem.hdfs_native_file_io import HdfsNativeFileIO
+
+        file_io = HdfsNativeFileIO.__new__(HdfsNativeFileIO)
+        file_io.properties = Options({
+            'security.kerberos.login.principal': 'user@REALM',
+            'security.kerberos.login.keytab': '/test/user.keytab',
+        })
+        system = SimpleNamespace(environ={}, path=MagicMock())
+        with patch.object(_kerberos, 'os', system):
+            with patch.object(_kerberos, 'kerberos_login_from_keytab') as 
login:
+                with self.assertRaisesRegex(RuntimeError, 'no ticket cache 
path'):
+                    file_io._setup_kerberos()
+        login.assert_called_once_with('user@REALM', '/test/user.keytab')
+
+
 if __name__ == '__main__':
     unittest.main()

Reply via email to