Ticket #1108: abspath-and-unicode-basedirs.dpatch

File abspath-and-unicode-basedirs.dpatch, 32.4 KB (added by davidsarah, at 2010-07-22T00:35:25Z)
  • util.fileutil, test.test_util: add abspath_expanduser_unicode function, to work around <http://bugs.python.org/issue3426>. util.encodingutil: add a convenience function argv_to_abspath. * Replace uses of os.path.abspath with abspath_expanduser_unicode where necessary. This makes basedir paths consistently represented as Unicode.
Line 
1Thu Jul 22 00:15:07 GMT Daylight Time 2010  david-sarah@jacaranda.org
2  * util.fileutil, test.test_util: add abspath_expanduser_unicode function, to work around <http://bugs.python.org/issue3426>. util.encodingutil: add a convenience function argv_to_abspath.
3
4Thu Jul 22 01:14:18 GMT Daylight Time 2010  david-sarah@jacaranda.org
5  * Replace uses of os.path.abspath with abspath_expanduser_unicode where necessary. This makes basedir paths consistently represented as Unicode.
6
7New patches:
8
9[util.fileutil, test.test_util: add abspath_expanduser_unicode function, to work around <http://bugs.python.org/issue3426>. util.encodingutil: add a convenience function argv_to_abspath.
10david-sarah@jacaranda.org**20100721231507
11 Ignore-this: eee6904d1f65a733ff35190879844d08
12] {
13hunk ./src/allmydata/test/test_util.py 4
14 
15 def foo(): pass # keep the line number constant
16 
17-import os, time
18+import os, time, sys
19 from StringIO import StringIO
20 from twisted.trial import unittest
21 from twisted.internet import defer, reactor
22hunk ./src/allmydata/test/test_util.py 473
23         used = fileutil.du(basedir)
24         self.failUnlessEqual(10+11+12+13, used)
25 
26+    def test_abspath_expanduser_unicode(self):
27+        self.failUnlessRaises(AssertionError, fileutil.abspath_expanduser_unicode, "bytestring")
28+
29+        saved_cwd = os.path.normpath(os.getcwdu())
30+        abspath_cwd = fileutil.abspath_expanduser_unicode(u".")
31+        self.failUnless(isinstance(saved_cwd, unicode), saved_cwd)
32+        self.failUnless(isinstance(abspath_cwd, unicode), abspath_cwd)
33+        self.failUnlessEqual(abspath_cwd, saved_cwd)
34+
35+        # adapted from <http://svn.python.org/view/python/branches/release26-maint/Lib/test/test_posixpath.py?view=markup&pathrev=78279#test_abspath>
36+
37+        self.failUnlessIn(u"foo", fileutil.abspath_expanduser_unicode(u"foo"))
38+        self.failIfIn(u"~", fileutil.abspath_expanduser_unicode(u"~"))
39+
40+        cwds = ['cwd']
41+        try:
42+            cwds.append(u'\xe7w\xf0'.encode(sys.getfilesystemencoding()
43+                                            or 'ascii'))
44+        except UnicodeEncodeError:
45+            pass # the cwd can't be encoded -- test with ascii cwd only
46+
47+        for cwd in cwds:
48+            try:
49+                os.mkdir(cwd)
50+                os.chdir(cwd)
51+                for upath in (u'', u'fuu', u'f\xf9\xf9', u'/fuu', u'U:\\', u'~'):
52+                    uabspath = fileutil.abspath_expanduser_unicode(upath)
53+                    self.failUnless(isinstance(uabspath, unicode), uabspath)
54+            finally:
55+                os.chdir(saved_cwd)
56+
57 class PollMixinTests(unittest.TestCase):
58     def setUp(self):
59         self.pm = pollmixin.PollMixin()
60hunk ./src/allmydata/util/encodingutil.py 13
61 from twisted.python import usage
62 import locale
63 from allmydata.util import log
64+from allmydata.util.fileutil import abspath_expanduser_unicode
65 
66 
67 def _canonical_encoding(encoding):
68hunk ./src/allmydata/util/encodingutil.py 95
69         raise usage.UsageError("Argument %s cannot be decoded as %s." %
70                                (quote_output(s), argv_encoding))
71 
72+def argv_to_abspath(s):
73+    """
74+    Convenience function to decode an argv element to an absolute path, with ~ expanded.
75+    If this fails, raise a UsageError.
76+    """
77+    return abspath_expanduser_unicode(argv_to_unicode(s))
78+
79 def unicode_to_url(s):
80     """
81     Encode an unicode object used in an URL.
82hunk ./src/allmydata/util/fileutil.py 276
83     finally:
84         outf.close()
85 
86+
87+# Work around <http://bugs.python.org/issue3426>. This code is adapted from
88+# <http://svn.python.org/view/python/trunk/Lib/ntpath.py?revision=78247&view=markup>
89+# with some simplifications.
90+
91+_getfullpathname = None
92+try:
93+    from nt import _getfullpathname
94+except ImportError:
95+    pass
96+
97+def abspath_expanduser_unicode(path):
98+    """Return the absolute version of a path."""
99+    assert isinstance(path, unicode), path
100+
101+    path = os.path.expanduser(path)
102+
103+    if _getfullpathname:
104+        # On Windows, os.path.isabs will return True for paths without a drive letter,
105+        # e.g. "\\". See <http://bugs.python.org/issue1669539>.
106+        try:
107+            path = _getfullpathname(path or u".")
108+        except WindowsError:
109+            pass
110+
111+    if not os.path.isabs(path):
112+        path = os.path.join(os.getcwdu(), path)
113+
114+    # We won't hit <http://bugs.python.org/issue5827> because
115+    # there is always at least one Unicode path component.
116+    return os.path.normpath(path)
117+
118}
119[Replace uses of os.path.abspath with abspath_expanduser_unicode where necessary. This makes basedir paths consistently represented as Unicode.
120david-sarah@jacaranda.org**20100722001418
121 Ignore-this: 9f8cb706540e695550e0dbe303c01f52
122] {
123hunk ./src/allmydata/node.py 14
124 from allmydata.util import log
125 from allmydata.util import fileutil, iputil, observer
126 from allmydata.util.assertutil import precondition, _assert
127+from allmydata.util.fileutil import abspath_expanduser_unicode
128+from allmydata.util.encodingutil import get_filesystem_encoding
129 
130 # Add our application versions to the data that Foolscap's LogPublisher
131 # reports.
132hunk ./src/allmydata/node.py 54
133     PORTNUMFILE = None
134     CERTFILE = "node.pem"
135 
136-    def __init__(self, basedir="."):
137+    def __init__(self, basedir=u"."):
138         service.MultiService.__init__(self)
139hunk ./src/allmydata/node.py 56
140-        self.basedir = os.path.abspath(basedir)
141+        self.basedir = abspath_expanduser_unicode(unicode(basedir))
142         self._portnumfile = os.path.join(self.basedir, self.PORTNUMFILE)
143         self._tub_ready_observerlist = observer.OneShotObserverList()
144         fileutil.make_dirs(os.path.join(self.basedir, "private"), 0700)
145hunk ./src/allmydata/node.py 79
146         iputil.increase_rlimits()
147 
148     def init_tempdir(self):
149-        local_tempdir = "tmp" # default is NODEDIR/tmp/
150-        tempdir = self.get_config("node", "tempdir", local_tempdir)
151+        local_tempdir_utf8 = "tmp" # default is NODEDIR/tmp/
152+        tempdir = self.get_config("node", "tempdir", local_tempdir_utf8).decode('utf-8')
153         tempdir = os.path.join(self.basedir, tempdir)
154         if not os.path.exists(tempdir):
155             fileutil.make_dirs(tempdir)
156hunk ./src/allmydata/node.py 84
157-        tempfile.tempdir = os.path.abspath(tempdir)
158+        tempfile.tempdir = abspath_expanduser_unicode(tempdir)
159         # this should cause twisted.web.http (which uses
160         # tempfile.TemporaryFile) to put large request bodies in the given
161         # directory. Without this, the default temp dir is usually /tmp/,
162hunk ./src/allmydata/node.py 172
163     def setup_ssh(self):
164         ssh_port = self.get_config("node", "ssh.port", "")
165         if ssh_port:
166-            ssh_keyfile = self.get_config("node", "ssh.authorized_keys_file")
167+            ssh_keyfile = self.get_config("node", "ssh.authorized_keys_file").decode('utf-8')
168             from allmydata import manhole
169hunk ./src/allmydata/node.py 174
170-            m = manhole.AuthorizedKeysManhole(ssh_port, ssh_keyfile)
171+            m = manhole.AuthorizedKeysManhole(ssh_port, ssh_keyfile.encode(get_filesystem_encoding()))
172             m.setServiceParent(self)
173             self.log("AuthorizedKeysManhole listening on %s" % ssh_port)
174 
175hunk ./src/allmydata/scripts/backupdb.py 12
176 from allmydata.util.netstring import netstring
177 from allmydata.util.hashutil import backupdb_dirhash
178 from allmydata.util import base32
179+from allmydata.util.fileutil import abspath_expanduser_unicode
180+from allmydata.util.encodingutil import to_str
181 
182 DAY = 24*60*60
183 MONTH = 30*DAY
184hunk ./src/allmydata/scripts/backupdb.py 208
185         current working directory. The database stores absolute pathnames.
186         """
187 
188-        path = os.path.abspath(path)
189+        path = abspath_expanduser_unicode(path)
190         s = os.stat(path)
191         size = s[stat.ST_SIZE]
192         ctime = s[stat.ST_CTIME]
193hunk ./src/allmydata/scripts/backupdb.py 251
194         probability = min(max(probability, 0.0), 1.0)
195         should_check = bool(random.random() < probability)
196 
197-        return FileResult(self, str(filecap), should_check,
198+        return FileResult(self, to_str(filecap), should_check,
199                           path, mtime, ctime, size)
200 
201     def get_or_allocate_fileid_for_cap(self, filecap):
202hunk ./src/allmydata/scripts/backupdb.py 354
203         probability = min(max(probability, 0.0), 1.0)
204         should_check = bool(random.random() < probability)
205 
206-        return DirectoryResult(self, dirhash_s, str(dircap), should_check)
207+        return DirectoryResult(self, dirhash_s, to_str(dircap), should_check)
208 
209     def did_create_directory(self, dircap, dirhash):
210         now = time.time()
211hunk ./src/allmydata/scripts/create_node.py 87
212 
213 
214 def create_node(basedir, config, out=sys.stdout, err=sys.stderr):
215+    # This should always be called with an absolute Unicode basedir.
216+    precondition(isinstance(basedir, unicode), basedir)
217+
218     if os.path.exists(basedir):
219hunk ./src/allmydata/scripts/create_node.py 91
220-        if os.listdir(basedir):
221-            print >>err, "The base directory \"%s\", which is \"%s\" is not empty." % (basedir, os.path.abspath(basedir))
222+        if listdir_unicode(basedir):
223+            print >>err, "The base directory %s is not empty." % quote_output(basedir)
224             print >>err, "To avoid clobbering anything, I am going to quit now."
225             print >>err, "Please use a different directory, or empty this one."
226             return -1
227hunk ./src/allmydata/scripts/create_node.py 135
228 
229     from allmydata.util import fileutil
230     fileutil.make_dirs(os.path.join(basedir, "private"), 0700)
231-    print >>out, "Node created in %s" % basedir
232+    print >>out, "Node created in %s" % quote_output(basedir)
233     if not config.get("introducer", ""):
234         print >>out, " Please set [client]introducer.furl= in tahoe.cfg!"
235         print >>out, " The node cannot connect to a grid without it."
236hunk ./src/allmydata/scripts/create_node.py 149
237 
238 
239 def create_introducer(basedir, config, out=sys.stdout, err=sys.stderr):
240+    # This should always be called with an absolute Unicode basedir.
241+    precondition(isinstance(basedir, unicode), basedir)
242+
243     if os.path.exists(basedir):
244hunk ./src/allmydata/scripts/create_node.py 153
245-        if os.listdir(basedir):
246-            print >>err, "The base directory \"%s\", which is \"%s\" is not empty." % (basedir, os.path.abspath(basedir))
247+        if listdir_unicode(basedir):
248+            print >>err, "The base directory %s is not empty." % quote_output(basedir)
249             print >>err, "To avoid clobbering anything, I am going to quit now."
250             print >>err, "Please use a different directory, or empty this one."
251             return -1
252hunk ./src/allmydata/scripts/create_node.py 169
253     write_node_config(c, config)
254     c.close()
255 
256-    print >>out, "Introducer created in %s" % basedir
257+    print >>out, "Introducer created in %s" % quote_output(basedir)
258 
259 
260 subCommands = [
261hunk ./src/allmydata/scripts/debug.py 8
262 from twisted.python import usage, failure
263 from twisted.internet import defer
264 
265+
266 class DumpOptions(usage.Options):
267     def getSynopsis(self):
268         return "Usage: tahoe debug dump-share SHARE_FILENAME"
269hunk ./src/allmydata/scripts/debug.py 32
270         return t
271 
272     def parseArgs(self, filename):
273-        self['filename'] = filename
274+        from allmydata.util.encodingutil import argv_to_abspath
275+        self['filename'] = argv_to_abspath(filename)
276 
277 def dump_share(options):
278     from allmydata.storage.mutable import MutableShareFile
279hunk ./src/allmydata/scripts/debug.py 37
280+    from allmydata.util.encodingutil import quote_output
281 
282     out = options.stdout
283 
284hunk ./src/allmydata/scripts/debug.py 42
285     # check the version, to see if we have a mutable or immutable share
286-    print >>out, "share filename: %s" % options['filename']
287+    print >>out, "share filename: %s" % quote_output(options['filename'])
288 
289     f = open(options['filename'], "rb")
290     prefix = f.read(32)
291hunk ./src/allmydata/scripts/debug.py 67
292     from allmydata import uri
293     from allmydata.util import base32
294     from allmydata.immutable.layout import ReadBucketProxy
295+    from allmydata.util.encodingutil import quote_output, to_str
296+
297     # use a ReadBucketProxy to parse the bucket and find the uri extension
298     bp = ReadBucketProxy(None, '', '')
299     offsets = bp._parse_offsets(f.read_share_data(0, 0x44))
300hunk ./src/allmydata/scripts/debug.py 112
301     # the storage index isn't stored in the share itself, so we depend upon
302     # knowing the parent directory name to get it
303     pieces = options['filename'].split(os.sep)
304-    if len(pieces) >= 2 and base32.could_be_base32_encoded(pieces[-2]):
305-        storage_index = base32.a2b(pieces[-2])
306-        uri_extension_hash = base32.a2b(unpacked["UEB_hash"])
307-        u = uri.CHKFileVerifierURI(storage_index, uri_extension_hash,
308-                                   unpacked["needed_shares"],
309-                                   unpacked["total_shares"], unpacked["size"])
310-        verify_cap = u.to_string()
311-        print >>out, "%20s: %s" % ("verify-cap", verify_cap)
312+    if len(pieces) >= 2:
313+        piece = to_str(pieces[-2])
314+        if base32.could_be_base32_encoded(piece):
315+            storage_index = base32.a2b(piece)
316+            uri_extension_hash = base32.a2b(unpacked["UEB_hash"])
317+            u = uri.CHKFileVerifierURI(storage_index, uri_extension_hash,
318+                                      unpacked["needed_shares"],
319+                                      unpacked["total_shares"], unpacked["size"])
320+            verify_cap = u.to_string()
321+            print >>out, "%20s: %s" % ("verify-cap", quote_output(verify_cap, quotemarks=False))
322 
323     sizes = {}
324     sizes['data'] = (offsets['plaintext_hash_tree'] -
325hunk ./src/allmydata/scripts/debug.py 220
326     from allmydata.mutable.common import NeedMoreDataError
327     from allmydata.util import base32, hashutil
328     from allmydata.uri import SSKVerifierURI
329+    from allmydata.util.encodingutil import quote_output, to_str
330 
331     offset = m.DATA_OFFSET
332 
333hunk ./src/allmydata/scripts/debug.py 267
334     # the storage index isn't stored in the share itself, so we depend upon
335     # knowing the parent directory name to get it
336     pieces = options['filename'].split(os.sep)
337-    if len(pieces) >= 2 and base32.could_be_base32_encoded(pieces[-2]):
338-        storage_index = base32.a2b(pieces[-2])
339-        fingerprint = hashutil.ssk_pubkey_fingerprint_hash(pubkey)
340-        u = SSKVerifierURI(storage_index, fingerprint)
341-        verify_cap = u.to_string()
342-        print >>out, "  verify-cap:", verify_cap
343+    if len(pieces) >= 2:
344+        piece = to_str(pieces[-2])
345+        if base32.could_be_base32_encoded(piece):
346+            storage_index = base32.a2b(piece)
347+            fingerprint = hashutil.ssk_pubkey_fingerprint_hash(pubkey)
348+            u = SSKVerifierURI(storage_index, fingerprint)
349+            verify_cap = u.to_string()
350+            print >>out, "  verify-cap:", quote_output(verify_cap, quotemarks=False)
351 
352     if options['offsets']:
353         # NOTE: this offset-calculation code is fragile, and needs to be
354hunk ./src/allmydata/scripts/debug.py 393
355     from allmydata import uri
356     from allmydata.storage.server import si_b2a
357     from allmydata.util import base32, hashutil
358+    from allmydata.util.encodingutil import quote_output
359 
360     if isinstance(u, uri.CHKFileURI):
361         if show_header:
362hunk ./src/allmydata/scripts/debug.py 415
363     elif isinstance(u, uri.LiteralFileURI):
364         if show_header:
365             print >>out, "Literal File URI:"
366-        print >>out, " data:", u.data
367+        print >>out, " data:", quote_output(u.data)
368 
369     elif isinstance(u, uri.WriteableSSKFileURI):
370         if show_header:
371hunk ./src/allmydata/scripts/debug.py 461
372 class FindSharesOptions(usage.Options):
373     def getSynopsis(self):
374         return "Usage: tahoe debug find-shares STORAGE_INDEX NODEDIRS.."
375+
376     def parseArgs(self, storage_index_s, *nodedirs):
377hunk ./src/allmydata/scripts/debug.py 463
378+        from allmydata.util.encodingutil import argv_to_abspath
379         self.si_s = storage_index_s
380hunk ./src/allmydata/scripts/debug.py 465
381-        self.nodedirs = nodedirs
382+        self.nodedirs = map(argv_to_abspath, nodedirs)
383+
384     def getUsage(self, width=None):
385         t = usage.Options.getUsage(self, width)
386         t += """
387hunk ./src/allmydata/scripts/debug.py 495
388     /home/warner/testnet/node-2/storage/shares/44k/44kai1tui348689nrw8fjegc8c/2
389     """
390     from allmydata.storage.server import si_a2b, storage_index_to_dir
391+    from allmydata.util.encodingutil import listdir_unicode
392 
393     out = options.stdout
394     sharedir = storage_index_to_dir(si_a2b(options.si_s))
395hunk ./src/allmydata/scripts/debug.py 500
396     for d in options.nodedirs:
397-        d = os.path.join(os.path.expanduser(d), "storage/shares", sharedir)
398+        d = os.path.join(d, "storage/shares", sharedir)
399         if os.path.exists(d):
400hunk ./src/allmydata/scripts/debug.py 502
401-            for shnum in os.listdir(d):
402+            for shnum in listdir_unicode(d):
403                 print >>out, os.path.join(d, shnum)
404 
405     return 0
406hunk ./src/allmydata/scripts/debug.py 513
407 
408     """
409     def parseArgs(self, *nodedirs):
410-        self.nodedirs = nodedirs
411+        from allmydata.util.encodingutil import argv_to_abspath
412+        self.nodedirs = map(argv_to_abspath, nodedirs)
413         if not nodedirs:
414             raise usage.UsageError("must specify at least one node directory")
415 
416hunk ./src/allmydata/scripts/debug.py 562
417     from allmydata.mutable.common import NeedMoreDataError
418     from allmydata.immutable.layout import ReadBucketProxy
419     from allmydata.util import base32
420+    from allmydata.util.encodingutil import quote_output
421     import struct
422 
423     f = open(abs_sharefile, "rb")
424hunk ./src/allmydata/scripts/debug.py 602
425             print >>out, "SDMF %s %d/%d %d #%d:%s %d %s" % \
426                   (si_s, k, N, datalen,
427                    seqnum, base32.b2a(root_hash),
428-                   expiration, abs_sharefile)
429+                   expiration, quote_output(abs_sharefile))
430         else:
431hunk ./src/allmydata/scripts/debug.py 604
432-            print >>out, "UNKNOWN mutable %s" % (abs_sharefile,)
433+            print >>out, "UNKNOWN mutable %s" % quote_output(abs_sharefile)
434 
435     elif struct.unpack(">L", prefix[:4]) == (1,):
436         # immutable
437hunk ./src/allmydata/scripts/debug.py 636
438 
439         print >>out, "CHK %s %d/%d %d %s %d %s" % (si_s, k, N, filesize,
440                                                    ueb_hash, expiration,
441-                                                   abs_sharefile)
442+                                                   quote_output(abs_sharefile))
443 
444     else:
445hunk ./src/allmydata/scripts/debug.py 639
446-        print >>out, "UNKNOWN really-unknown %s" % (abs_sharefile,)
447+        print >>out, "UNKNOWN really-unknown %s" % quote_output(abs_sharefile)
448 
449     f.close()
450 
451hunk ./src/allmydata/scripts/debug.py 644
452 def catalog_shares(options):
453+    from allmydata.util.encodingutil import listdir_unicode, quote_output
454+
455     out = options.stdout
456     err = options.stderr
457     now = time.time()
458hunk ./src/allmydata/scripts/debug.py 650
459     for d in options.nodedirs:
460-        d = os.path.join(os.path.expanduser(d), "storage/shares")
461+        d = os.path.join(d, "storage/shares")
462         try:
463hunk ./src/allmydata/scripts/debug.py 652
464-            abbrevs = os.listdir(d)
465+            abbrevs = listdir_unicode(d)
466         except EnvironmentError:
467             # ignore nodes that have storage turned off altogether
468             pass
469hunk ./src/allmydata/scripts/debug.py 662
470                     continue
471                 abbrevdir = os.path.join(d, abbrevdir)
472                 # this tool may get run against bad disks, so we can't assume
473-                # that os.listdir will always succeed. Try to catalog as much
474+                # that listdir_unicode will always succeed. Try to catalog as much
475                 # as possible.
476                 try:
477hunk ./src/allmydata/scripts/debug.py 665
478-                    sharedirs = os.listdir(abbrevdir)
479+                    sharedirs = listdir_unicode(abbrevdir)
480                     for si_s in sharedirs:
481                         si_dir = os.path.join(abbrevdir, si_s)
482                         catalog_shares_one_abbrevdir(si_s, si_dir, now, out,err)
483hunk ./src/allmydata/scripts/debug.py 670
484                 except:
485-                    print >>err, "Error processing %s" % abbrevdir
486+                    print >>err, "Error processing %s" % quote_output(abbrevdir)
487                     failure.Failure().printTraceback(err)
488 
489     return 0
490hunk ./src/allmydata/scripts/debug.py 676
491 
492 def catalog_shares_one_abbrevdir(si_s, si_dir, now, out, err):
493+    from allmydata.util.encodingutil import listdir_unicode, quote_output
494+
495     try:
496hunk ./src/allmydata/scripts/debug.py 679
497-        for shnum_s in os.listdir(si_dir):
498+        for shnum_s in listdir_unicode(si_dir):
499             abs_sharefile = os.path.join(si_dir, shnum_s)
500hunk ./src/allmydata/scripts/debug.py 681
501-            abs_sharefile = os.path.abspath(abs_sharefile)
502             assert os.path.isfile(abs_sharefile)
503             try:
504                 describe_share(abs_sharefile, si_s, shnum_s, now,
505hunk ./src/allmydata/scripts/debug.py 686
506                                out)
507             except:
508-                print >>err, "Error processing %s" % abs_sharefile
509+                print >>err, "Error processing %s" % quote_output(abs_sharefile)
510                 failure.Failure().printTraceback(err)
511     except:
512hunk ./src/allmydata/scripts/debug.py 689
513-        print >>err, "Error processing %s" % si_dir
514+        print >>err, "Error processing %s" % quote_output(si_dir)
515         failure.Failure().printTraceback(err)
516 
517 class CorruptShareOptions(usage.Options):
518hunk ./src/allmydata/scripts/keygen.py 32
519         print >>err, "a basedir was not provided, please use --basedir or -C"
520         return -1
521     if os.path.exists(basedir):
522-        if os.listdir(basedir):
523-            print >>err, "The base directory \"%s\", which is \"%s\" is not empty." % (basedir, os.path.abspath(basedir))
524+        if listdir_unicode(basedir):
525+            print >>err, "The base directory %s is not empty." % quote_output(basedir)
526             print >>err, "To avoid clobbering anything, I am going to quit now."
527             print >>err, "Please use a different directory, or empty this one."
528             return -1
529hunk ./src/allmydata/scripts/tahoe_backup.py 14
530 from allmydata.scripts import backupdb
531 from allmydata.util.encodingutil import listdir_unicode, quote_output, to_str, FilenameEncodingError
532 from allmydata.util.assertutil import precondition
533+from allmydata.util.fileutil import abspath_expanduser_unicode
534 
535 
536 def get_local_metadata(path):
537hunk ./src/allmydata/scripts/tahoe_backup.py 88
538         self.backupdb = None
539         bdbfile = os.path.join(options["node-directory"],
540                                "private", "backupdb.sqlite")
541-        bdbfile = os.path.abspath(bdbfile)
542+        bdbfile = abspath_expanduser_unicode(bdbfile)
543         self.backupdb = backupdb.get_backupdb(bdbfile, stderr)
544         if not self.backupdb:
545             print >>stderr, "ERROR: Unable to load backup db."
546hunk ./src/allmydata/scripts/tahoe_cp.py 12
547 from allmydata.scripts.common_http import do_http, HTTPError
548 from allmydata import uri
549 from allmydata.util import fileutil
550+from allmydata.util.fileutil import abspath_expanduser_unicode
551 from allmydata.util.encodingutil import unicode_to_url, listdir_unicode, quote_output, to_str
552 from allmydata.util.assertutil import precondition
553 
554hunk ./src/allmydata/scripts/tahoe_cp.py 515
555         rootcap, path = get_alias(self.aliases, destination_spec, None)
556         if rootcap == DefaultAliasMarker:
557             # no alias, so this is a local file
558-            pathname = os.path.abspath(os.path.expanduser(path.decode('utf-8')))
559+            pathname = abspath_expanduser_unicode(path.decode('utf-8'))
560             if not os.path.exists(pathname):
561                 t = LocalMissingTarget(pathname)
562             elif os.path.isdir(pathname):
563hunk ./src/allmydata/scripts/tahoe_cp.py 555
564         rootcap, path = get_alias(self.aliases, source_spec, None)
565         if rootcap == DefaultAliasMarker:
566             # no alias, so this is a local file
567-            pathname = os.path.abspath(os.path.expanduser(path.decode('utf-8')))
568+            pathname = abspath_expanduser_unicode(path.decode('utf-8'))
569             name = os.path.basename(pathname)
570             if not os.path.exists(pathname):
571                 raise MissingSourceError(source_spec)
572hunk ./src/allmydata/test/check_grid.py 68
573         ]
574 
575     def parseArgs(self, nodedir, tahoe):
576-        self.nodedir = nodedir
577-        self.tahoe = os.path.abspath(tahoe)
578+        # Note: does not support Unicode arguments.
579+        self.nodedir = os.path.expanduser(nodedir)
580+        self.tahoe = os.path.abspath(os.path.expanduser(tahoe))
581 
582 class CommandFailed(Exception):
583     pass
584hunk ./src/allmydata/test/check_memory.py 12
585 from allmydata.immutable import upload
586 from allmydata.scripts import create_node
587 from allmydata.util import fileutil, pollmixin
588+from allmydata.util.fileutil import abspath_expanduser_unicode
589+from allmydata.util.encodingutil import get_filesystem_encoding
590 from foolscap.api import Tub, fireEventually, flushEventualQueue
591 from twisted.python import log
592 
593hunk ./src/allmydata/test/check_memory.py 68
594     numnodes = 7
595 
596     def __init__(self, basedir, mode):
597-        self.basedir = basedir = os.path.abspath(basedir)
598-        if not basedir.startswith(os.path.abspath(".")):
599+        self.basedir = basedir = abspath_expanduser_unicode(unicode(basedir))
600+        if not (basedir + os.path.sep).startswith(abspath_expanduser_unicode(u".") + os.path.sep):
601             raise AssertionError("safety issue: basedir must be a subdir")
602         self.testdir = testdir = os.path.join(basedir, "test")
603         if os.path.exists(testdir):
604hunk ./src/allmydata/test/check_memory.py 231
605     def start_client(self):
606         # this returns a Deferred that fires with the client's control.furl
607         log.msg("MAKING CLIENT")
608-        clientdir = self.clientdir = os.path.join(self.testdir, "client")
609+        # self.testdir is an absolute Unicode path
610+        clientdir = self.clientdir = os.path.join(self.testdir, u"client")
611+        clientdir_str = clientdir.encode(get_filesystem_encoding())
612         quiet = StringIO()
613         create_node.create_node(clientdir, {}, out=quiet)
614         log.msg("DONE MAKING CLIENT")
615hunk ./src/allmydata/test/check_memory.py 272
616         logfile = os.path.join(self.basedir, "client.log")
617         cmd = ["twistd", "-n", "-y", "tahoe-client.tac", "-l", logfile]
618         env = os.environ.copy()
619-        self.proc = reactor.spawnProcess(pp, cmd[0], cmd, env, path=clientdir)
620+        self.proc = reactor.spawnProcess(pp, cmd[0], cmd, env, path=clientdir_str)
621         log.msg("CLIENT STARTED")
622 
623         # now we wait for the client to get started. we're looking for the
624hunk ./src/allmydata/test/test_cli.py 36
625 from allmydata.util.assertutil import precondition
626 from allmydata.util.encodingutil import listdir_unicode, unicode_platform, \
627     quote_output, get_output_encoding, get_argv_encoding, get_filesystem_encoding, \
628-    unicode_to_output, to_str
629+    unicode_to_output, to_str, to_argv
630+from allmydata.util.fileutil import abspath_expanduser_unicode
631 
632 timeout = 480 # deep_check takes 360s on Zandr's linksys box, others take > 240s
633 
634hunk ./src/allmydata/test/test_cli.py 185
635         u = uri.LiteralFileURI("this is some data")
636         output = self._dump_cap(u.to_string())
637         self.failUnless("Literal File URI:" in output, output)
638-        self.failUnless("data: this is some data" in output, output)
639+        self.failUnless("data: 'this is some data'" in output, output)
640 
641     def test_dump_cap_ssk(self):
642         writekey = "\x01" * 16
643hunk ./src/allmydata/test/test_cli.py 778
644         self.set_up_grid()
645 
646         rel_fn = os.path.join(self.basedir, "DATAFILE")
647-        abs_fn = os.path.abspath(rel_fn)
648+        abs_fn = to_argv(abspath_expanduser_unicode(unicode(rel_fn)))
649         # we make the file small enough to fit in a LIT file, for speed
650         fileutil.write(rel_fn, "short file")
651         d = self.do_cli("put", rel_fn)
652hunk ./src/allmydata/test/test_encodingutil.py 262
653             raise unittest.SkipTest("%r\nIt is possible that the filesystem on which this test is being run "
654                                     "does not support Unicode, even though the platform does." % (e,))
655 
656-        fn = lumiere_nfc + '/' + lumiere_nfc + '.txt'
657+        fn = lumiere_nfc + u'/' + lumiere_nfc + u'.txt'
658         open(fn, 'wb').close()
659         self.failUnless(os.path.exists(fn))
660hunk ./src/allmydata/test/test_encodingutil.py 265
661-        self.failUnless(os.path.exists(os.path.abspath(fn)))
662+        self.failUnless(os.path.exists(os.path.join(os.getcwdu(), fn)))
663         filenames = listdir_unicode(lumiere_nfc)
664 
665         # We only require that the listing includes a filename that is canonically equivalent
666hunk ./src/allmydata/test/test_runner.py 196
667         rc, out, err = self.run_tahoe(argv)
668         self.failIfEqual(rc, 0, str((out, err, rc)))
669         self.failUnlessEqual(out, "")
670-        self.failUnless("is not empty." in err)
671+        self.failUnlessIn("is not empty.", err)
672 
673         # make sure it rejects too many arguments
674         argv = ["create-key-generator", "basedir", "extraarg"]
675hunk ./src/allmydata/test/test_system.py 15
676 from allmydata.immutable.filenode import ImmutableFileNode, LiteralFileNode
677 from allmydata.util import idlib, mathutil
678 from allmydata.util import log, base32
679+from allmydata.util.encodingutil import quote_output
680+from allmydata.util.fileutil import abspath_expanduser_unicode
681 from allmydata.util.consumer import MemoryConsumer, download_to_data
682 from allmydata.scripts import runner
683 from allmydata.interfaces import IDirectoryNode, IFileNode, \
684hunk ./src/allmydata/test/test_system.py 1290
685                 if magic == '\x00\x00\x00\x01':
686                     break
687         else:
688-            self.fail("unable to find any uri_extension files in %s"
689+            self.fail("unable to find any uri_extension files in %r"
690                       % self.basedir)
691hunk ./src/allmydata/test/test_system.py 1292
692-        log.msg("test_system.SystemTest._test_runner using %s" % filename)
693+        log.msg("test_system.SystemTest._test_runner using %r" % filename)
694 
695         out,err = StringIO(), StringIO()
696         rc = runner.runner(["debug", "dump-share", "--offsets",
697hunk ./src/allmydata/test/test_system.py 1303
698 
699         # we only upload a single file, so we can assert some things about
700         # its size and shares.
701-        self.failUnless(("share filename: %s" % filename) in output)
702-        self.failUnless("size: %d\n" % len(self.data) in output)
703-        self.failUnless("num_segments: 1\n" in output)
704+        self.failUnlessIn("share filename: %s" % quote_output(abspath_expanduser_unicode(filename)), output)
705+        self.failUnlessIn("size: %d\n" % len(self.data), output)
706+        self.failUnlessIn("num_segments: 1\n", output)
707         # segment_size is always a multiple of needed_shares
708hunk ./src/allmydata/test/test_system.py 1307
709-        self.failUnless("segment_size: %d\n" % mathutil.next_multiple(len(self.data), 3) in output)
710-        self.failUnless("total_shares: 10\n" in output)
711+        self.failUnlessIn("segment_size: %d\n" % mathutil.next_multiple(len(self.data), 3), output)
712+        self.failUnlessIn("total_shares: 10\n", output)
713         # keys which are supposed to be present
714         for key in ("size", "num_segments", "segment_size",
715                     "needed_shares", "total_shares",
716hunk ./src/allmydata/test/test_system.py 1316
717                     #"plaintext_hash", "plaintext_root_hash",
718                     "crypttext_hash", "crypttext_root_hash",
719                     "share_root_hash", "UEB_hash"):
720-            self.failUnless("%s: " % key in output, key)
721-        self.failUnless("  verify-cap: URI:CHK-Verifier:" in output)
722+            self.failUnlessIn("%s: " % key, output)
723+        self.failUnlessIn("  verify-cap: URI:CHK-Verifier:", output)
724 
725         # now use its storage index to find the other shares using the
726         # 'find-shares' tool
727}
728
729Context:
730
731[__init__.py: silence DeprecationWarning about BaseException.message globally. fixes #1129
732david-sarah@jacaranda.org**20100720011939
733 Ignore-this: 38808986ba79cb2786b010504a22f89
734] 
735[test_runner: test that 'tahoe --version' outputs no noise (e.g. DeprecationWarnings).
736david-sarah@jacaranda.org**20100720011345
737 Ignore-this: dd358b7b2e5d57282cbe133e8069702e
738] 
739[TAG allmydata-tahoe-1.7.1
740zooko@zooko.com**20100719131352
741 Ignore-this: 6942056548433dc653a746703819ad8c
742] 
743Patch bundle hash:
7447701b52497cb185827602a19acd985d87395e356