Rev 5758: (jameinel) Add osutils.lstat/fstat so that even on Windows lstat(fname) == in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Tue Apr 5 14:47:32 UTC 2011


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 5758 [merge]
revision-id: pqm at pqm.ubuntu.com-20110405144726-zi3lj2kwvjml4kx5
parent: pqm at pqm.ubuntu.com-20110405134936-0ys2p9oyxpsjqvh2
parent: john at arbash-meinel.com-20110405122513-jw1boj6ui42zu8l6
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Tue 2011-04-05 14:47:26 +0000
message:
  (jameinel) Add osutils.lstat/fstat so that even on Windows lstat(fname) ==
   fstat(open(fname).fileno()) (John A Meinel)
modified:
  bzrlib/_walkdirs_win32.pyx     _walkdirs_win32.pyx-20080716220454-kweh3tgxez5dvw2l-2
  bzrlib/dirstate.py             dirstate.py-20060728012006-d6mvoihjb3je9peu-1
  bzrlib/osutils.py              osutils.py-20050309040759-eeaff12fbf77ac86
  bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
  bzrlib/tests/test_osutils.py   test_osutils.py-20051201224856-e48ee24c12182989
  bzrlib/workingtree.py          workingtree.py-20050511021032-29b6ec0a681e02e3
  bzrlib/workingtree_4.py        workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
  doc/en/release-notes/bzr-2.3.txt NEWS-20050323055033-4e00b5db738777ff
  doc/en/release-notes/bzr-2.4.txt bzr2.4.txt-20110114053217-k7ym9jfz243fddjm-1
=== modified file 'bzrlib/_walkdirs_win32.pyx'
--- a/bzrlib/_walkdirs_win32.pyx	2010-02-17 17:11:16 +0000
+++ b/bzrlib/_walkdirs_win32.pyx	2011-04-05 12:15:34 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2008, 2009, 2010 Canonical Ltd
+# Copyright (C) 2008-2011 Canonical Ltd
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -69,9 +69,13 @@
 
 
 import operator
+import os
 import stat
 
-from bzrlib import osutils, _readdir_py
+from bzrlib import _readdir_py
+
+cdef object osutils
+osutils = None
 
 
 cdef class _Win32Stat:
@@ -170,6 +174,9 @@
 
     def top_prefix_to_starting_dir(self, top, prefix=""):
         """See DirReader.top_prefix_to_starting_dir."""
+        global osutils
+        if osutils is None:
+            from bzrlib import osutils
         return (osutils.safe_utf8(prefix), None, None, None,
                 osutils.safe_unicode(top))
 
@@ -250,3 +257,37 @@
                 #       earlier Exception, so for now, I'm ignoring this
         dirblock.sort(key=operator.itemgetter(1))
         return dirblock
+
+
+def lstat(path):
+    """Equivalent to os.lstat, except match Win32ReadDir._get_stat_value.
+    """
+    return wrap_stat(os.lstat(path))
+
+
+def fstat(fd):
+    """Like os.fstat, except match Win32ReadDir._get_stat_value
+
+    :seealso: wrap_stat
+    """
+    return wrap_stat(os.fstat(fd))
+
+
+def wrap_stat(st):
+    """Return a _Win32Stat object, based on the given stat result.
+
+    On Windows, os.fstat(open(fname).fileno()) != os.lstat(fname). This is
+    generally because os.lstat and os.fstat differ in what they put into st_ino
+    and st_dev. What gets set where seems to also be dependent on the python
+    version. So we always set it to 0 to avoid worrying about it.
+    """
+    cdef _Win32Stat statvalue
+    statvalue = _Win32Stat()
+    statvalue.st_mode = st.st_mode
+    statvalue.st_ctime = st.st_ctime
+    statvalue.st_mtime = st.st_mtime
+    statvalue.st_atime = st.st_atime
+    statvalue._st_size = st.st_size
+    statvalue.st_ino = 0
+    statvalue.st_dev = 0
+    return statvalue

=== modified file 'bzrlib/dirstate.py'
--- a/bzrlib/dirstate.py	2011-01-25 22:54:08 +0000
+++ b/bzrlib/dirstate.py	2011-04-05 11:10:25 +0000
@@ -265,6 +265,17 @@
         # return '%X.%X' % (int(st.st_mtime), st.st_mode)
 
 
+def _unpack_stat(packed_stat):
+    """Turn a packed_stat back into the stat fields.
+
+    This is meant as a debugging tool, should not be used in real code.
+    """
+    (st_size, st_mtime, st_ctime, st_dev, st_ino,
+     st_mode) = struct.unpack('>LLLLLL', binascii.a2b_base64(packed_stat))
+    return dict(st_size=st_size, st_mtime=st_mtime, st_ctime=st_ctime,
+                st_dev=st_dev, st_ino=st_ino, st_mode=st_mode)
+
+
 class SHA1Provider(object):
     """An interface for getting sha1s of a file."""
 

=== modified file 'bzrlib/osutils.py'
--- a/bzrlib/osutils.py	2011-02-24 16:13:39 +0000
+++ b/bzrlib/osutils.py	2011-04-04 13:37:25 +0000
@@ -392,6 +392,12 @@
 # These were already lazily imported into local scope
 # mkdtemp = tempfile.mkdtemp
 # rmtree = shutil.rmtree
+lstat = os.lstat
+fstat = os.fstat
+
+def wrap_stat(st):
+    return st
+
 
 MIN_ABS_PATHLENGTH = 1
 
@@ -407,6 +413,14 @@
     getcwd = _win32_getcwd
     mkdtemp = _win32_mkdtemp
     rename = _win32_rename
+    try:
+        from bzrlib import _walkdirs_win32
+    except ImportError:
+        pass
+    else:
+        lstat = _walkdirs_win32.lstat
+        fstat = _walkdirs_win32.fstat
+        wrap_stat = _walkdirs_win32.wrap_stat
 
     MIN_ABS_PATHLENGTH = 3
 

=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py	2011-04-05 10:41:36 +0000
+++ b/bzrlib/tests/__init__.py	2011-04-05 12:11:26 +0000
@@ -1267,11 +1267,15 @@
                          'st_mtime did not match')
         self.assertEqual(expected.st_ctime, actual.st_ctime,
                          'st_ctime did not match')
-        if sys.platform != 'win32':
+        if sys.platform == 'win32':
             # On Win32 both 'dev' and 'ino' cannot be trusted. In python2.4 it
             # is 'dev' that varies, in python 2.5 (6?) it is st_ino that is
-            # odd. Regardless we shouldn't actually try to assert anything
-            # about their values
+            # odd. We just force it to always be 0 to avoid any problems.
+            self.assertEqual(0, expected.st_dev)
+            self.assertEqual(0, actual.st_dev)
+            self.assertEqual(0, expected.st_ino)
+            self.assertEqual(0, actual.st_ino)
+        else:
             self.assertEqual(expected.st_dev, actual.st_dev,
                              'st_dev did not match')
             self.assertEqual(expected.st_ino, actual.st_ino,

=== modified file 'bzrlib/tests/test_osutils.py'
--- a/bzrlib/tests/test_osutils.py	2011-01-16 01:12:01 +0000
+++ b/bzrlib/tests/test_osutils.py	2011-04-05 12:23:17 +0000
@@ -231,6 +231,25 @@
             self.assertFalse(osutils.is_inside_or_parent_of_any(dirs, fn))
 
 
+class TestLstat(tests.TestCaseInTempDir):
+
+    def test_lstat_matches_fstat(self):
+        # On Windows, lstat and fstat don't always agree, primarily in the
+        # 'st_ino' and 'st_dev' fields. So we force them to be '0' in our
+        # custom implementation.
+        if sys.platform == 'win32':
+            # We only have special lstat/fstat if we have the extension.
+            # Without it, we may end up re-reading content when we don't have
+            # to, but otherwise it doesn't effect correctness.
+            self.requireFeature(test__walkdirs_win32.win32_readdir_feature)
+        f = open('test-file.txt', 'wb')
+        self.addCleanup(f.close)
+        f.write('some content\n')
+        f.flush()
+        self.assertEqualStat(osutils.fstat(f.fileno()),
+                             osutils.lstat('test-file.txt'))
+
+
 class TestRmTree(tests.TestCaseInTempDir):
 
     def test_rmtree(self):

=== modified file 'bzrlib/workingtree.py'
--- a/bzrlib/workingtree.py	2011-04-05 01:12:15 +0000
+++ b/bzrlib/workingtree.py	2011-04-05 14:47:26 +0000
@@ -523,7 +523,7 @@
         return self.get_file_with_stat(file_id, path, filtered=filtered)[0]
 
     def get_file_with_stat(self, file_id, path=None, filtered=True,
-        _fstat=os.fstat):
+                           _fstat=osutils.fstat):
         """See Tree.get_file_with_stat."""
         if path is None:
             path = self.id2path(file_id)

=== modified file 'bzrlib/workingtree_4.py'
--- a/bzrlib/workingtree_4.py	2011-04-05 11:31:58 +0000
+++ b/bzrlib/workingtree_4.py	2011-04-05 14:47:26 +0000
@@ -365,7 +365,7 @@
         state = self.current_dirstate()
         if stat_value is None:
             try:
-                stat_value = os.lstat(file_abspath)
+                stat_value = osutils.lstat(file_abspath)
             except OSError, e:
                 if e.errno == errno.ENOENT:
                     return None
@@ -474,7 +474,7 @@
             self._must_be_locked()
             if not path:
                 path = self.id2path(file_id)
-            mode = os.lstat(self.abspath(path)).st_mode
+            mode = osutils.lstat(self.abspath(path)).st_mode
             return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
 
     def all_file_ids(self):

=== modified file 'doc/en/release-notes/bzr-2.3.txt'
--- a/doc/en/release-notes/bzr-2.3.txt	2011-04-05 10:41:36 +0000
+++ b/doc/en/release-notes/bzr-2.3.txt	2011-04-05 12:11:26 +0000
@@ -45,10 +45,6 @@
   normally not require running ``bzr whoami`` first.
   (Martin Pool, #616878)
 
-* When reporting a crash without apport, don't print the full list of
-   plugins because it's often too long.
-   (Martin Pool, #716389)
-
 * ``bzr push`` into a repository (that doesn't have a branch), will no
   longer copy all revisions in the repository. Only the ones in the
   ancestry of the source branch, like it does in all other cases.
@@ -62,6 +58,11 @@
   had changed.  Bazaar was attempting to open and lock the master branch
   twice in this case.  (Andrew Bennetts, #733350)
 
+* When reporting a crash without apport, don't print the full list of
+  plugins because it's often too long.
+  (Martin Pool, #716389)
+
+
 Documentation
 *************
 

=== modified file 'doc/en/release-notes/bzr-2.4.txt'
--- a/doc/en/release-notes/bzr-2.4.txt	2011-04-02 00:08:39 +0000
+++ b/doc/en/release-notes/bzr-2.4.txt	2011-04-05 12:25:13 +0000
@@ -73,6 +73,11 @@
 .. Major internal changes, unlikely to be visible to users or plugin 
    developers, but interesting for bzr developers.
 
+* Added ``osutils.lstat`` and ``osutils.fstat``. These are just the ``os``
+  functions on Linux, but they are wrapped on Windows so that fstat
+  matches lstat results across all python versions.
+  (John Arbash Meinel)
+
 Testing
 *******
 




More information about the bazaar-commits mailing list