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