Rev 6358: (gz) Add get_environ_unicode for accessing variables without mbcs mangling in file:///srv/pqm.bazaar-vcs.org/archives/thelove/bzr/%2Btrunk/

Patch Queue Manager pqm at pqm.ubuntu.com
Mon Dec 12 13:31:09 UTC 2011


At file:///srv/pqm.bazaar-vcs.org/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 6358 [merge]
revision-id: pqm at pqm.ubuntu.com-20111212133108-m0wtlolqdsh1u9bj
parent: pqm at pqm.ubuntu.com-20111212130524-l9s3fwxxy1pujx7t
parent: martin.packman at canonical.com-20111212121522-iact2d3w4l2ga5tk
committer: Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Mon 2011-12-12 13:31:08 +0000
message:
  (gz) Add get_environ_unicode for accessing variables without mbcs mangling
   on windows (Martin Packman)
modified:
  bzrlib/tests/test_win32utils.py test_win32utils.py-20070713181630-8xsrjymd3e8mgw23-108
  bzrlib/win32utils.py           win32console.py-20051021033308-123c6c929d04973d
=== modified file 'bzrlib/tests/test_win32utils.py'
--- a/bzrlib/tests/test_win32utils.py	2011-06-14 01:26:41 +0000
+++ b/bzrlib/tests/test_win32utils.py	2011-12-05 12:50:21 +0000
@@ -328,3 +328,56 @@
         self.assertCommandLine([u"rm", u"x*"], "-m pdb rm x*", ["rm", u"x*"])
         self.assertCommandLine([u"add", u"d/f1", u"d/f2"], "-m pdb add d/*",
             ["add", u"d/*"])
+
+
+class TestGetEnvironUnicode(tests.TestCase):
+    """Tests for accessing the environment via the windows wide api"""
+
+    _test_needs_features = [CtypesFeature, features.win32_feature]
+
+    def setUp(self):
+        super(TestGetEnvironUnicode, self).setUp()
+        self.overrideEnv("TEST", "1")
+
+    def test_get(self):
+        """In the normal case behaves the same as os.environ access"""
+        self.assertEqual("1", win32utils.get_environ_unicode("TEST"))
+
+    def test_unset(self):
+        """A variable not present in the environment gives None by default"""
+        del os.environ["TEST"]
+        self.assertIs(None, win32utils.get_environ_unicode("TEST"))
+
+    def test_unset_default(self):
+        """A variable not present in the environment gives passed default"""
+        del os.environ["TEST"]
+        self.assertIs("a", win32utils.get_environ_unicode("TEST", "a"))
+
+    def test_unicode(self):
+        """A non-ascii variable is returned as unicode"""
+        unicode_val = u"\xa7" # non-ascii character present in many encodings
+        try:
+            bytes_val = unicode_val.encode(osutils.get_user_encoding())
+        except UnicodeEncodeError:
+            self.skip("Couldn't encode non-ascii string to place in environ")
+        os.environ["TEST"] = bytes_val
+        self.assertEqual(unicode_val, win32utils.get_environ_unicode("TEST"))
+
+    def test_long(self):
+        """A variable bigger than heuristic buffer size is still accessible"""
+        big_val = "x" * (2<<10)
+        os.environ["TEST"] = big_val
+        self.assertEqual(big_val, win32utils.get_environ_unicode("TEST"))
+
+    def test_unexpected_error(self):
+        """An error from the underlying platform function is propogated"""
+        ERROR_INVALID_PARAMETER = 87
+        SetLastError = win32utils.ctypes.windll.kernel32.SetLastError
+        def failer(*args, **kwargs):
+            SetLastError(ERROR_INVALID_PARAMETER)
+            return 0
+        self.overrideAttr(win32utils.get_environ_unicode, "_c_function",
+            failer)
+        e = self.assertRaises(WindowsError,
+            win32utils.get_environ_unicode, "TEST")
+        self.assertEqual(e.winerror, ERROR_INVALID_PARAMETER)

=== modified file 'bzrlib/win32utils.py'
--- a/bzrlib/win32utils.py	2011-12-05 11:06:16 +0000
+++ b/bzrlib/win32utils.py	2011-12-12 13:31:08 +0000
@@ -569,7 +569,7 @@
     return args
 
 
-if has_ctypes and winver != 'Windows 98':
+if has_ctypes and winver == 'Windows NT':
     def get_unicode_argv():
         prototype = ctypes.WINFUNCTYPE(ctypes.c_wchar_p)
         GetCommandLineW = prototype(("GetCommandLineW",
@@ -580,8 +580,36 @@
         # Skip the first argument, since we only care about parameters
         argv = _command_line_to_argv(command_line, sys.argv)[1:]
         return argv
+    
+
+    def get_environ_unicode(key, default=None):
+        """Get `key` from environment as unicode or `default` if unset
+
+        A large enough buffer will be allocated to retrieve the value, though
+        it may take two calls to the underlying library function.
+
+        This needs ctypes because pywin32 does not expose the wide version.
+        """
+        cfunc = getattr(get_environ_unicode, "_c_function", None)
+        if cfunc is None:
+            from ctypes.wintypes import DWORD, LPCWSTR, LPWSTR
+            cfunc = ctypes.WINFUNCTYPE(DWORD, LPCWSTR, LPWSTR, DWORD)(
+                ("GetEnvironmentVariableW", ctypes.windll.kernel32))
+            get_environ_unicode._c_function = cfunc
+        buffer_size = 256 # heuristic, 256 characters often enough
+        while True:
+            buffer = ctypes.create_unicode_buffer(buffer_size)
+            length = cfunc(key, buffer, buffer_size)
+            if not length:
+                code = ctypes.GetLastError()
+                if code == 203: # ERROR_ENVVAR_NOT_FOUND
+                    return default
+                raise ctypes.WinError(code)
+            if buffer_size > length:
+                return buffer[:length]
+            buffer_size = length
 else:
-    get_unicode_argv = None
+    get_unicode_argv = get_environ_unicode = None
 
 
 if has_win32api:




More information about the bazaar-commits mailing list