Merge lp:~jelmer/brz/just-read-mtab into lp:brz

Proposed by Jelmer Vernooij
Status: Merged
Approved by: Jelmer Vernooij
Approved revision: no longer in the source branch.
Merge reported by: The Breezy Bot
Merged at revision: not available
Proposed branch: lp:~jelmer/brz/just-read-mtab
Merge into: lp:brz
Diff against target: 170 lines (+107/-17)
3 files modified
breezy/osutils.py (+62/-12)
breezy/tests/test_osutils.py (+40/-5)
doc/en/release-notes/brz-3.1.txt (+5/-0)
To merge this branch: bzr merge lp:~jelmer/brz/just-read-mtab
Reviewer Review Type Date Requested Status
Martin Packman Approve
Review via email: mp+368878@code.launchpad.net

Commit message

Read filesystem metadata from mtab directly rather than going via psutil.

Description of the change

Read filesystem metadata from mtab directly rather than going via psutil.

To post a comment you must log in.
Revision history for this message
Martin Packman (gz) wrote :

Looks good, see a few inline comments. May be worth filtering out `path.startswith(('/sys/', '/run/'))` which I have quite a few of and are never(?) going to be interesting for our purposes.

review: Approve
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote :
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'breezy/osutils.py'
2--- breezy/osutils.py 2019-06-16 01:03:51 +0000
3+++ breezy/osutils.py 2019-06-16 15:36:24 +0000
4@@ -2634,27 +2634,77 @@
5 return False
6
7
8+def read_mtab(path):
9+ """Read an fstab-style file and extract mountpoint+filesystem information.
10+
11+ :param path: Path to read from
12+ :yield: Tuples with mountpoints (as bytestrings) and filesystem names
13+ """
14+ with open(path, 'rb') as f:
15+ for line in f:
16+ if line.startswith(b'#'):
17+ continue
18+ cols = line.split()
19+ if len(cols) < 3:
20+ continue
21+ yield cols[1], cols[2].decode('ascii', 'replace')
22+
23+
24+MTAB_PATH = '/etc/mtab'
25+
26+class FilesystemFinder(object):
27+ """Find the filesystem for a particular path."""
28+
29+ def __init__(self, mountpoints):
30+ def key(x):
31+ return len(x[0])
32+ self._mountpoints = sorted(mountpoints, key=key, reverse=True)
33+
34+ @classmethod
35+ def from_mtab(cls):
36+ """Create a FilesystemFinder from an mtab-style file.
37+
38+ Note that this will silenty ignore mtab if it doesn't exist or can not
39+ be opened.
40+ """
41+ # TODO(jelmer): Use inotify to be notified when /etc/mtab changes and
42+ # we need to re-read it.
43+ try:
44+ return cls(read_mtab(MTAB_PATH))
45+ except EnvironmentError as e:
46+ trace.mutter('Unable to read mtab: %s', e)
47+ return cls([])
48+
49+ def find(self, path):
50+ """Find the filesystem used by a particular path.
51+
52+ :param path: Path to find (bytestring or text type)
53+ :return: Filesystem name (as text type) or None, if the filesystem is
54+ unknown.
55+ """
56+ for mountpoint, filesystem in self._mountpoints:
57+ if is_inside(mountpoint, path):
58+ return filesystem
59+ return None
60+
61+
62+_FILESYSTEM_FINDER = None
63+
64+
65 def get_fs_type(path):
66 """Return the filesystem type for the partition a path is in.
67
68 :param path: Path to search filesystem type for
69 :return: A FS type, as string. E.g. "ext2"
70 """
71- # TODO(jelmer): It would be nice to avoid an extra dependency here, but the only
72- # alternative is reading platform-specific files under /proc :(
73- try:
74- import psutil
75- except ImportError as e:
76- raise errors.DependencyNotPresent('psutil', e)
77+ global _FILESYSTEM_FINDER
78+ if _FILESYSTEM_FINDER is None:
79+ _FILESYSTEM_FINDER = FilesystemFinder.from_mtab()
80
81- if not PY3 and not isinstance(path, str):
82+ if not isinstance(path, bytes):
83 path = path.encode(_fs_enc)
84
85- for part in sorted(psutil.disk_partitions(), key=lambda x: len(x.mountpoint), reverse=True):
86- if is_inside(part.mountpoint, path):
87- return part.fstype
88- # Unable to parse the file? Since otherwise at least the entry for / should match..
89- return None
90+ return _FILESYSTEM_FINDER.find(path)
91
92
93 if PY3:
94
95=== modified file 'breezy/tests/test_osutils.py'
96--- breezy/tests/test_osutils.py 2019-06-16 01:03:51 +0000
97+++ breezy/tests/test_osutils.py 2019-06-16 15:36:24 +0000
98@@ -62,8 +62,6 @@
99
100 term_ios_feature = features.ModuleAvailableFeature('termios')
101
102-psutil_feature = features.ModuleAvailableFeature('psutil')
103-
104
105 def _already_unicode(s):
106 return s
107@@ -2356,8 +2354,45 @@
108 self.assertIsInstance(osutils.supports_symlinks(self.test_dir), bool)
109
110
111+class MtabReader(tests.TestCaseInTempDir):
112+
113+ def test_read_mtab(self):
114+ self.build_tree_contents([('mtab', """\
115+/dev/mapper/blah--vg-root / ext4 rw,relatime,errors=remount-ro 0 0
116+/dev/mapper/blah--vg-home /home vfat rw,relatime 0 0
117+# comment
118+
119+iminvalid
120+""")])
121+ self.assertEqual(
122+ list(osutils.read_mtab('mtab')),
123+ [(b'/', 'ext4'),
124+ (b'/home', 'vfat')])
125+
126+
127 class GetFsTypeTests(tests.TestCaseInTempDir):
128
129- def test_returns_string(self):
130- self.requireFeature(psutil_feature)
131- self.assertIsInstance(osutils.get_fs_type(self.test_dir), str)
132+ def test_returns_string_or_none(self):
133+ ret = osutils.get_fs_type(self.test_dir)
134+ self.assertTrue(isinstance(ret, text_type) or ret is None)
135+
136+ def test_returns_most_specific(self):
137+ self.overrideAttr(
138+ osutils, '_FILESYSTEM_FINDER',
139+ osutils.FilesystemFinder(
140+ [(b'/', 'ext4'), (b'/home', 'vfat'),
141+ (b'/home/jelmer', 'ext2')]))
142+ self.assertEqual(osutils.get_fs_type(b'/home/jelmer/blah'), 'ext2')
143+ self.assertEqual(osutils.get_fs_type('/home/jelmer/blah'), 'ext2')
144+ self.assertEqual(osutils.get_fs_type(b'/home/jelmer'), 'ext2')
145+ self.assertEqual(osutils.get_fs_type(b'/home/martin'), 'vfat')
146+ self.assertEqual(osutils.get_fs_type(b'/home'), 'vfat')
147+ self.assertEqual(osutils.get_fs_type(b'/other'), 'ext4')
148+
149+ def test_returns_none(self):
150+ self.overrideAttr(
151+ osutils, '_FILESYSTEM_FINDER',
152+ osutils.FilesystemFinder([]))
153+ self.assertIs(osutils.get_fs_type('/home/jelmer/blah'), None)
154+ self.assertIs(osutils.get_fs_type(b'/home/jelmer/blah'), None)
155+ self.assertIs(osutils.get_fs_type('/home/jelmer'), None)
156
157=== modified file 'doc/en/release-notes/brz-3.1.txt'
158--- doc/en/release-notes/brz-3.1.txt 2019-06-15 19:09:08 +0000
159+++ doc/en/release-notes/brz-3.1.txt 2019-06-16 15:36:24 +0000
160@@ -30,6 +30,11 @@
161 * The 'quilt' plugin, extracted from brz-debian, is now
162 bundled. (Jelmer Vernooij)
163
164+* Directly read mtab rather than using psutil when trying to figure out
165+ filesystem types. This removes a dependency that not all users may
166+ have installed and speeds up import time since psutil brings in
167+ various other modules. (Jelmer Vernooij)
168+
169 Improvements
170 ************
171

Subscribers

People subscribed via source and target branches