Merge lp:~facundo/ubuntuone-client/isolate-accessing-functions into lp:ubuntuone-client

Proposed by Facundo Batista
Status: Merged
Approved by: Facundo Batista
Approved revision: 780
Merged at revision: 787
Proposed branch: lp:~facundo/ubuntuone-client/isolate-accessing-functions
Merge into: lp:ubuntuone-client
Diff against target: 368 lines (+128/-36)
7 files modified
tests/platform/test_os_helper.py (+76/-7)
ubuntuone/platform/linux/__init__.py (+10/-7)
ubuntuone/platform/linux/os_helper.py (+13/-0)
ubuntuone/syncdaemon/filesystem_manager.py (+12/-11)
ubuntuone/syncdaemon/hash_queue.py (+2/-2)
ubuntuone/syncdaemon/local_rescan.py (+11/-8)
ubuntuone/syncdaemon/sync.py (+4/-1)
To merge this branch: bzr merge lp:~facundo/ubuntuone-client/isolate-accessing-functions
Reviewer Review Type Date Requested Status
Guillermo Gonzalez Approve
Manuel de la Peña (community) Approve
Review via email: mp+44222@code.launchpad.net

Commit message

Finish separating all user files access.

Description of the change

Finish separating all user files access.

As part of the plan to get all paths as Unicode internally, this branch isolates *everything* that touches user files into separated functions. This do not include syncdaemon controlled paths (for example, the file of version number for FSM, or the directory where the metadata is stored).

So, please be sure that nothing is missing in this regard while reviewing.

To post a comment you must log in.
Revision history for this message
Guillermo Gonzalez (verterok) wrote :

Hi,

Only a few questions/comments:

1) What do you think about changing the name of the access function, to something more explicit e.g: is_readeable or can_read or...you get the idea ;-)

2) The stat_path is using os.lstat (in linux) but it's used to replace calls to os.stat, e.g: in the before the hashing loop in ubuntuone/syncdaemon/hash_queue.py (and also in local rescan). Is this change ok? should be using lstat or stat?

Beside those two issues, looks really good.

review: Needs Information
Revision history for this message
Facundo Batista (facundo) wrote :

1) I thought about using other name, but "access" is the name it always been used (not only in Python, also in the OS and other languages), and *today* is only to check readability (tomorrow it can grow other params).

2) All places I found we were using lstat/stat, we really should not follow symlinks or we don't have symlinks to follow.

Thanks!

Revision history for this message
Guillermo Gonzalez (verterok) :
review: Approve
Revision history for this message
Manuel de la Peña (mandel) wrote :

Looks good to me, later we will have to add the new decorators in ubuntuone-dev-tools to ignore platform specific tests. I'll take care of that as soon as I start actively working on the code. For the lest everything looks great!

review: Approve
Revision history for this message
Ubuntu One Auto Pilot (otto-pilot) wrote :

Attempt to merge into lp:ubuntuone-client failed due to conflicts:

text conflict in ubuntuone/syncdaemon/filesystem_manager.py
text conflict in ubuntuone/syncdaemon/local_rescan.py

780. By Facundo Batista

Merged trunk in

Revision history for this message
Guillermo Gonzalez (verterok) wrote :

+1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'tests/platform/test_os_helper.py'
2--- tests/platform/test_os_helper.py 2010-12-02 21:27:56 +0000
3+++ tests/platform/test_os_helper.py 2011-01-05 15:16:24 +0000
4@@ -15,22 +15,28 @@
5 #
6 # You should have received a copy of the GNU General Public License along
7 # with this program. If not, see <http://www.gnu.org/licenses/>.
8+
9 """Linux specific tests for the platform module."""
10+
11+import errno
12 import os
13
14 from contrib.testing.testcase import BaseTwistedTestCase
15 from ubuntuone.platform.linux import (
16- set_dir_readonly,
17- set_file_readonly,
18- set_dir_readwrite,
19- set_file_readwrite,
20+ access,
21 allow_writes,
22- remove_file,
23- remove_dir,
24- path_exists,
25+ listdir,
26 make_dir,
27 open_file,
28+ path_exists,
29+ remove_dir,
30+ remove_file,
31 rename,
32+ set_dir_readonly,
33+ set_dir_readwrite,
34+ set_file_readonly,
35+ set_file_readwrite,
36+ stat_path,
37 )
38
39
40@@ -183,3 +189,66 @@
41 self.assertFalse(os.path.exists(testdir1))
42 self.assertTrue(os.path.exists(testdir2))
43
44+ def test_listdir(self):
45+ """Return a list of the files in a dir."""
46+ open(os.path.join(self.basedir, 'foo'), 'w').close()
47+ open(os.path.join(self.basedir, 'bar'), 'w').close()
48+ l = listdir(self.basedir)
49+ self.assertEqual(sorted(l), ['bar', 'foo', 'test_file'])
50+
51+ def test_access_rw(self):
52+ """Test access on a file with full permission."""
53+ self.assertTrue(access(self.testfile))
54+
55+ def test_access_ro(self):
56+ """Test access on a file with read only permission."""
57+ os.chmod(self.testfile, 0o444)
58+ self.assertTrue(access(self.testfile))
59+ os.chmod(self.testfile, 0o664)
60+
61+ def test_access_nothing(self):
62+ """Test access on a file with no permission at all."""
63+ os.chmod(self.testfile, 0o000)
64+ self.assertFalse(access(self.testfile))
65+ os.chmod(self.testfile, 0o664)
66+
67+ def test_stat_normal(self):
68+ """Test on a normal file."""
69+ self.assertEqual(os.stat(self.testfile), stat_path(self.testfile))
70+
71+ def test_stat_symlink(self):
72+ """Test that it doesn't follow symlinks.
73+
74+ We compare the inode only (enough to see if it's returning info
75+ from the link or the linked), as we can not compare the full stat
76+ because the st_mode will be different.
77+ """
78+ link = os.path.join(self.basedir, 'foo')
79+ os.symlink(self.testfile, link)
80+ self.assertNotEqual(os.stat(link).st_ino, stat_path(link).st_ino)
81+ self.assertEqual(os.lstat(link).st_ino, stat_path(link).st_ino)
82+
83+ def test_stat_no_path(self):
84+ """Test that it raises proper error when no file is there."""
85+ try:
86+ return stat_path(os.path.join(self.basedir, 'nofile'))
87+ except OSError, e:
88+ self.assertEqual(e.errno, errno.ENOENT)
89+
90+ def test_path_exists_file_yes(self):
91+ """The file is there."""
92+ self.assertTrue(path_exists(self.testfile))
93+
94+ def test_path_exists_file_no(self):
95+ """The file is not there."""
96+ os.remove(self.testfile)
97+ self.assertFalse(path_exists(self.testfile))
98+
99+ def test_path_exists_dir_yes(self):
100+ """The dir is there."""
101+ self.assertTrue(path_exists(self.basedir))
102+
103+ def test_path_exists_dir_no(self):
104+ """The dir is not there."""
105+ self.rmtree(self.basedir)
106+ self.assertFalse(path_exists(self.basedir))
107
108=== modified file 'ubuntuone/platform/linux/__init__.py'
109--- ubuntuone/platform/linux/__init__.py 2010-12-15 18:36:41 +0000
110+++ ubuntuone/platform/linux/__init__.py 2011-01-05 15:16:24 +0000
111@@ -26,17 +26,20 @@
112 from dbus.mainloop.glib import DBusGMainLoop
113
114 from ubuntuone.platform.linux.os_helper import (
115- set_file_readonly,
116- set_file_readwrite,
117- set_dir_readonly,
118- set_dir_readwrite,
119+ access,
120 allow_writes,
121- remove_file,
122- remove_dir,
123- path_exists,
124+ listdir,
125 make_dir,
126 open_file,
127+ path_exists,
128+ remove_dir,
129+ remove_file,
130 rename,
131+ set_dir_readonly,
132+ set_dir_readwrite,
133+ set_file_readonly,
134+ set_file_readwrite,
135+ stat_path,
136 )
137 from ubuntuone.platform.linux.vm_helper import (create_shares_link,
138 get_udf_path_name, get_udf_path, get_share_path, VMMetadataUpgraderMixIn)
139
140=== modified file 'ubuntuone/platform/linux/os_helper.py'
141--- ubuntuone/platform/linux/os_helper.py 2010-12-02 21:27:56 +0000
142+++ ubuntuone/platform/linux/os_helper.py 2011-01-05 15:16:24 +0000
143@@ -80,3 +80,16 @@
144 def rename(path_from, path_to):
145 """Rename a file or directory."""
146 os.rename(path_from, path_to)
147+
148+def listdir(directory):
149+ """List a directory."""
150+ return os.listdir(directory)
151+
152+def access(path):
153+ """Return if the path is at least readable."""
154+ return os.access(path, os.R_OK)
155+
156+def stat_path(path):
157+ """Return stat info about a path."""
158+ return os.lstat(path)
159+
160
161=== modified file 'ubuntuone/syncdaemon/filesystem_manager.py'
162--- ubuntuone/syncdaemon/filesystem_manager.py 2011-01-04 13:22:34 +0000
163+++ ubuntuone/syncdaemon/filesystem_manager.py 2011-01-05 15:16:24 +0000
164@@ -40,16 +40,17 @@
165 MOVE_LIMBO_ROW_TYPE,
166 )
167 from ubuntuone.platform import (
168+ make_dir,
169+ open_file,
170+ path_exists,
171+ remove_dir,
172+ remove_file,
173+ rename,
174 set_dir_readonly,
175 set_dir_readwrite,
176 set_file_readonly,
177 set_file_readwrite,
178- path_exists,
179- remove_file,
180- remove_dir,
181- make_dir,
182- open_file,
183- rename,
184+ stat_path,
185 )
186
187 METADATA_VERSION = "6"
188@@ -837,7 +838,7 @@
189 # we try to stat, if we fail, so what?
190 #pylint: disable-msg=W0704
191 try:
192- mdobj["stat"] = os.stat(path_to) # needed if not the same FS
193+ mdobj["stat"] = stat_path(path_to) # needed if not the same FS
194 except OSError:
195 log_warning("Got an OSError while getting the stat of %r", path_to)
196 self.fs[mdid] = mdobj
197@@ -1003,7 +1004,7 @@
198 # get the values
199 mdobj = self.fs[mdid]
200 partial_in_md = mdobj["info"]["is_partial"]
201- partial_in_disk = os.path.exists(self._get_partial_path(mdobj))
202+ partial_in_disk = path_exists(self._get_partial_path(mdobj))
203
204 # check and return
205 if partial_in_md != partial_in_disk:
206@@ -1042,7 +1043,7 @@
207 path = self.get_abspath(mdobj['share_id'], mdobj['path'])
208 with self._enable_share_write(share_id, os.path.dirname(path)):
209 # if we don't have the dir yet, create it
210- if is_dir and not os.path.exists(path):
211+ if is_dir and not path_exists(path):
212 self.eq.add_to_mute_filter("FS_DIR_CREATE", path=path)
213 make_dir(path)
214
215@@ -1129,7 +1130,7 @@
216 #pylint: disable-msg=W0704
217 try:
218 # don't alert EQ, partials are in other directory, not watched
219- os.remove(partial_path)
220+ remove_file(partial_path)
221 except OSError, e:
222 # we only remove it if its there.
223 m = "OSError %s when trying to remove partial_path %r"
224@@ -1491,7 +1492,7 @@
225 os.lstat is used as we don't support symlinks
226 """
227 try:
228- return os.lstat(path)
229+ return stat_path(path)
230 except OSError, e:
231 if e.errno == errno.ENOENT:
232 return None
233
234=== modified file 'ubuntuone/syncdaemon/hash_queue.py'
235--- ubuntuone/syncdaemon/hash_queue.py 2010-12-02 21:27:56 +0000
236+++ ubuntuone/syncdaemon/hash_queue.py 2011-01-05 15:16:24 +0000
237@@ -23,7 +23,6 @@
238 import logging
239 import threading
240 import Queue
241-import os
242
243 from twisted.internet import reactor
244
245@@ -32,6 +31,7 @@
246
247 from ubuntuone.platform import (
248 open_file,
249+ stat_path,
250 )
251
252 class StopHashing(Exception):
253@@ -112,7 +112,7 @@
254 crc = 0
255 size = 0
256 try:
257- initial_stat = os.stat(path)
258+ initial_stat = stat_path(path)
259 with open_file(path) as fh:
260 while True:
261 # stop hashing if path_to_cancel == path or _stopped is True
262
263=== modified file 'ubuntuone/syncdaemon/local_rescan.py'
264--- ubuntuone/syncdaemon/local_rescan.py 2010-12-20 19:56:56 +0000
265+++ ubuntuone/syncdaemon/local_rescan.py 2011-01-05 15:16:24 +0000
266@@ -30,9 +30,12 @@
267
268 from ubuntuone.syncdaemon.interfaces import IMarker
269 from ubuntuone.platform import (
270+ access,
271+ listdir,
272 path_exists,
273 remove_file,
274 rename,
275+ stat_path,
276 )
277
278 class ScanTransactionDirty(Exception):
279@@ -104,7 +107,7 @@
280 fix the nodes states, we just remove them to don't let garbage behind.
281 """
282 try:
283- partials = os.listdir(self.fsm.partials_dir)
284+ partials = listdir(self.fsm.partials_dir)
285 except OSError, e:
286 if e.errno != errno.ENOENT:
287 raise
288@@ -114,7 +117,7 @@
289 for partial in partials:
290 partial_path = os.path.join(self.fsm.partials_dir, partial)
291 log_debug("Removing old .partial: %r", partial_path)
292- os.remove(partial_path)
293+ remove_file(partial_path)
294
295 def _process_limbo(self):
296 """Process the FSM limbos and send corresponding AQ orders."""
297@@ -243,7 +246,7 @@
298
299 for ancestor in volume.ancestors:
300 # check that ancestor is still there
301- if not os.access(ancestor, os.R_OK):
302+ if not access(ancestor):
303 log_info("Tree broken at path: %r", volume.path)
304 revert_watches()
305 return False
306@@ -255,7 +258,7 @@
307 self.eq.add_watch(ancestor)
308
309 # finally, check that UDF is ok in disk
310- if not os.access(volume.path, os.R_OK):
311+ if not access(volume.path):
312 revert_watches()
313 return False
314
315@@ -365,7 +368,7 @@
316 """
317 if oldstat is None:
318 return True
319- newstat = os.stat(fullname)
320+ newstat = stat_path(fullname)
321 different = (newstat.st_ino != oldstat.st_ino or
322 newstat.st_size != oldstat.st_size or
323 newstat.st_mtime != oldstat.st_mtime)
324@@ -614,18 +617,18 @@
325 """The scan, really."""
326
327 log_debug("scanning the dir %r", dirpath)
328- listdir = os.listdir(dirpath)
329+ dircontent = listdir(dirpath)
330
331 # get the info from disk
332 dnames = []
333 fnames = []
334- for something in listdir:
335+ for something in dircontent:
336 fullname = os.path.join(dirpath, something)
337 stat_result = get_stat(fullname)
338 if stat_result is None or \
339 stat.S_ISLNK(stat_result.st_mode):
340 continue
341- elif not os.access(fullname, os.R_OK):
342+ elif not access(fullname):
343 log_warning("Ignoring path as we don't have enough "
344 "permissions to track it: %r", fullname)
345 continue
346
347=== modified file 'ubuntuone/syncdaemon/sync.py'
348--- ubuntuone/syncdaemon/sync.py 2010-12-20 19:56:56 +0000
349+++ ubuntuone/syncdaemon/sync.py 2011-01-05 15:16:24 +0000
350@@ -32,6 +32,9 @@
351 from ubuntuone.syncdaemon.logger import DebugCapture
352 from ubuntuone.syncdaemon.filesystem_manager import InconsistencyError
353 from ubuntuone.syncdaemon.volume_manager import VolumeDoesNotExist
354+from ubuntuone.platform import (
355+ stat_path,
356+)
357
358 empty_hash = ""
359
360@@ -307,7 +310,7 @@
361
362 def validate_actual_data(self, path, oldstat):
363 """Validates that the received info is not obsolete."""
364- newstat = os.stat(path)
365+ newstat = stat_path(path)
366 if newstat.st_ino != oldstat.st_ino or \
367 newstat.st_size != oldstat.st_size or \
368 newstat.st_mtime != oldstat.st_mtime:

Subscribers

People subscribed via source and target branches