Ticket #1072: rename-stringutils-drop-listdir_unicode-and-open_unicode.dpatch

File rename-stringutils-drop-listdir_unicode-and-open_unicode.dpatch, 32.1 KB (added by davidsarah, at 2010-07-12T00:51:52Z)

Rename stringutils to encodingutil, and drop listdir_unicode and open_unicode (since the Python stdlib functions work fine with Unicode paths). Also move some utility functions to fileutil.

Line 
1Sun Jul 11 20:55:25 GMT Daylight Time 2010  david-sarah@jacaranda.org
2  * Allow URIs passed in the initial JSON for t=mkdir-with-children, t=mkdir-immutable to be Unicode. Also pass the name of each child into nodemaker.create_from_cap for error reporting.
3
4Mon Jul 12 01:30:15 GMT Daylight Time 2010  david-sarah@jacaranda.org
5  * Rename stringutils to encodingutil, and drop listdir_unicode and open_unicode (since the Python stdlib functions work fine with Unicode paths). Also move some utility functions to fileutil.
6
7New patches:
8
9[Allow URIs passed in the initial JSON for t=mkdir-with-children, t=mkdir-immutable to be Unicode. Also pass the name of each child into nodemaker.create_from_cap for error reporting.
10david-sarah@jacaranda.org**20100711195525
11 Ignore-this: deac32d8b91ba26ede18905d3f7d2b93
12] {
13hunk ./src/allmydata/web/common.py 14
14      EmptyPathnameComponentError, MustBeDeepImmutableError, \
15      MustBeReadonlyError, MustNotBeUnknownRWError
16 from allmydata.mutable.common import UnrecoverableFileError
17-from allmydata.util import abbreviate # TODO: consolidate
18+from allmydata.util import abbreviate
19+from allmydata.util.stringutils import to_str
20 
21 class IOpHandleTable(Interface):
22     pass
23hunk ./src/allmydata/web/common.py 68
24     children = {}
25     if children_json:
26         data = simplejson.loads(children_json)
27-        for (name, (ctype, propdict)) in data.iteritems():
28-            name = unicode(name)
29-            writecap = propdict.get("rw_uri")
30-            if writecap is not None:
31-                writecap = str(writecap)
32-            readcap = propdict.get("ro_uri")
33-            if readcap is not None:
34-                readcap = str(readcap)
35+        for (namex, (ctype, propdict)) in data.iteritems():
36+            namex = unicode(namex)
37+            writecap = to_str(propdict.get("rw_uri"))
38+            readcap = to_str(propdict.get("ro_uri"))
39             metadata = propdict.get("metadata", {})
40hunk ./src/allmydata/web/common.py 73
41-            childnode = nodemaker.create_from_cap(writecap, readcap)
42-            children[name] = (childnode, metadata)
43+            # name= argument is just for error reporting
44+            childnode = nodemaker.create_from_cap(writecap, readcap, name=namex)
45+            children[namex] = (childnode, metadata)
46     return children
47 
48 def abbreviate_time(data):
49}
50[Rename stringutils to encodingutil, and drop listdir_unicode and open_unicode (since the Python stdlib functions work fine with Unicode paths). Also move some utility functions to fileutil.
51david-sarah@jacaranda.org**20100712003015
52 Ignore-this: 103b809d180df17a7283077c3104c7be
53] {
54move ./src/allmydata/test/test_stringutils.py ./src/allmydata/test/test_encodingutil.py
55move ./src/allmydata/util/stringutils.py ./src/allmydata/util/encodingutil.py
56hunk ./src/allmydata/dirnode.py 19
57      DeepCheckAndRepairResults
58 from allmydata.monitor import Monitor
59 from allmydata.util import hashutil, mathutil, base32, log
60-from allmydata.util.stringutils import quote_output
61+from allmydata.util.encodingutil import quote_output
62 from allmydata.util.assertutil import precondition
63 from allmydata.util.netstring import netstring, split_netstring
64 from allmydata.util.consumer import download_to_data
65hunk ./src/allmydata/scripts/cli.py 4
66 import os.path, re, sys, fnmatch
67 from twisted.python import usage
68 from allmydata.scripts.common import BaseOptions, get_aliases
69-from allmydata.util.stringutils import argv_to_unicode
70+from allmydata.util.encodingutil import argv_to_unicode
71 
72 NODEURL_RE=re.compile("http(s?)://([^:]*)(:([1-9][0-9]*))?")
73 
74hunk ./src/allmydata/scripts/common.py 5
75 import os, sys, urllib
76 import codecs
77 from twisted.python import usage
78-from allmydata.util.stringutils import unicode_to_url, quote_output
79+from allmydata.util.encodingutil import unicode_to_url, quote_output
80 from allmydata.util.assertutil import precondition
81 
82 class BaseOptions:
83hunk ./src/allmydata/scripts/common_http.py 6
84 import urlparse, httplib
85 import allmydata # for __full_version__
86 
87-from allmydata.util.stringutils import quote_output
88+from allmydata.util.encodingutil import quote_output
89 from allmydata.scripts.common import TahoeError
90 
91 
92hunk ./src/allmydata/scripts/slow_operation.py 7
93                                      UnknownAliasError
94 from allmydata.scripts.common_http import do_http, format_http_error
95 from allmydata.util import base32
96-from allmydata.util.stringutils import quote_output, is_printable_ascii
97+from allmydata.util.encodingutil import quote_output, is_printable_ascii
98 import urllib
99 import simplejson
100 
101hunk ./src/allmydata/scripts/tahoe_add_alias.py 8
102 from allmydata.scripts.common_http import do_http, check_http_error
103 from allmydata.scripts.common import get_aliases
104 from allmydata.util.fileutil import move_into_place
105-from allmydata.util.stringutils import unicode_to_output, quote_output
106+from allmydata.util.encodingutil import unicode_to_output, quote_output
107 
108 
109 def add_line_to_aliasfile(aliasfile, alias, cap):
110hunk ./src/allmydata/scripts/tahoe_backup.py 12
111 from allmydata.scripts.common_http import do_http, HTTPError, format_http_error
112 from allmydata.util import time_format
113 from allmydata.scripts import backupdb
114-from allmydata.util.stringutils import listdir_unicode, open_unicode, quote_output, to_str
115+from allmydata.util.encodingutil import quote_output, to_str
116+from allmydata.util.fileutil import open_expanduser
117 from allmydata.util.assertutil import precondition
118 
119 
120hunk ./src/allmydata/scripts/tahoe_backup.py 170
121         compare_contents = {} # childname -> rocap
122 
123         try:
124-            children = listdir_unicode(localpath)
125+            children = os.listdir(localpath)
126         except EnvironmentError:
127             self.directories_skipped += 1
128             self.warn("WARNING: permission denied on directory %s" % quote_output(localpath))
129hunk ./src/allmydata/scripts/tahoe_backup.py 175
130             children = []
131+        except (UnicodeEncodeError, UnicodeDecodeError):
132+            self.directories_skipped += 1
133+            self.warn("WARNING: could not list directory %s due to an encoding error" % quote_output(localpath))
134+            children = []
135 
136         for child in self.options.filter_listdir(children):
137             assert isinstance(child, unicode), child
138hunk ./src/allmydata/scripts/tahoe_backup.py 300
139 
140         if must_upload:
141             self.verboseprint("uploading %s.." % quote_output(childpath))
142-            infileobj = open_unicode(childpath, "rb")
143+            infileobj = open_expanduser(childpath, "rb")
144             url = self.options['node-url'] + "uri"
145             resp = do_http("PUT", url, infileobj)
146             if resp.status not in (200, 201):
147hunk ./src/allmydata/scripts/tahoe_check.py 8
148 from allmydata.scripts.common import get_alias, DEFAULT_ALIAS, escape_path, \
149                                      UnknownAliasError
150 from allmydata.scripts.common_http import do_http, format_http_error
151-from allmydata.util.stringutils import quote_output, quote_path
152+from allmydata.util.encodingutil import quote_output, quote_path
153 
154 class Checker:
155     pass
156hunk ./src/allmydata/scripts/tahoe_cp.py 11
157                                      DefaultAliasMarker, TahoeError
158 from allmydata.scripts.common_http import do_http, HTTPError
159 from allmydata import uri
160-from allmydata.util.stringutils import unicode_to_url, listdir_unicode, open_unicode, \
161-    abspath_expanduser_unicode, quote_output, to_str
162+from allmydata.util.encodingutil import unicode_to_url, quote_output, to_str
163+from allmydata.util import fileutil
164+from allmydata.util.fileutil import open_expanduser, abspath_expanduser
165 from allmydata.util.assertutil import precondition
166 
167 
168hunk ./src/allmydata/scripts/tahoe_cp.py 17
169-def _put_local_file(pathname, inf):
170-    # TODO: create temporary file and move into place?
171-    # TODO: move this to fileutil.
172-    outf = open_unicode(pathname, "wb")
173-    try:
174-        while True:
175-            data = inf.read(32768)
176-            if not data:
177-                break
178-            outf.write(data)
179-    finally:
180-        outf.close()
181-
182-
183 class MissingSourceError(TahoeError):
184     def __init__(self, name):
185         TahoeError.__init__(self, "No such file or directory %s" % quote_output(name))
186hunk ./src/allmydata/scripts/tahoe_cp.py 71
187         return True
188 
189     def open(self, caps_only):
190-        return open_unicode(self.pathname, "rb")
191+        return open_expanduser(self.pathname, "rb")
192 
193 
194 class LocalFileTarget:
195hunk ./src/allmydata/scripts/tahoe_cp.py 80
196         self.pathname = pathname
197 
198     def put_file(self, inf):
199-        _put_local_file(self.pathname, inf)
200+        fileutil.put_file(self.pathname, inf)
201 
202 
203 class LocalMissingTarget:
204hunk ./src/allmydata/scripts/tahoe_cp.py 89
205         self.pathname = pathname
206 
207     def put_file(self, inf):
208-        _put_local_file(self.pathname, inf)
209+        fileutil.put_file(self.pathname, inf)
210 
211 
212 class LocalDirectorySource:
213hunk ./src/allmydata/scripts/tahoe_cp.py 104
214         if self.children is not None:
215             return
216         self.children = {}
217-        children = listdir_unicode(self.pathname)
218+        children = os.listdir(self.pathname)
219         for i,n in enumerate(children):
220             self.progressfunc("examining %d of %d" % (i, len(children)))
221             pn = os.path.join(self.pathname, n)
222hunk ./src/allmydata/scripts/tahoe_cp.py 132
223         if self.children is not None:
224             return
225         self.children = {}
226-        children = listdir_unicode(self.pathname)
227+        children = os.listdir(self.pathname)
228         for i,n in enumerate(children):
229             self.progressfunc("examining %d of %d" % (i, len(children)))
230             n = unicode(n)
231hunk ./src/allmydata/scripts/tahoe_cp.py 158
232     def put_file(self, name, inf):
233         precondition(isinstance(name, unicode), name)
234         pathname = os.path.join(self.pathname, name)
235-        _put_local_file(pathname, inf)
236+        fileutil.put_file(pathname, inf)
237 
238     def set_children(self):
239         pass
240hunk ./src/allmydata/scripts/tahoe_cp.py 515
241         rootcap, path = get_alias(self.aliases, destination_spec, None)
242         if rootcap == DefaultAliasMarker:
243             # no alias, so this is a local file
244-            pathname = abspath_expanduser_unicode(path.decode('utf-8'))
245+            pathname = abspath_expanduser(path.decode('utf-8'))
246             if not os.path.exists(pathname):
247                 t = LocalMissingTarget(pathname)
248             elif os.path.isdir(pathname):
249hunk ./src/allmydata/scripts/tahoe_cp.py 555
250         rootcap, path = get_alias(self.aliases, source_spec, None)
251         if rootcap == DefaultAliasMarker:
252             # no alias, so this is a local file
253-            pathname = abspath_expanduser_unicode(path.decode('utf-8'))
254+            pathname = abspath_expanduser(path.decode('utf-8'))
255             name = os.path.basename(pathname)
256             if not os.path.exists(pathname):
257                 raise MissingSourceError(source_spec)
258hunk ./src/allmydata/scripts/tahoe_get.py 6
259 from allmydata.scripts.common import get_alias, DEFAULT_ALIAS, escape_path, \
260                                      UnknownAliasError
261 from allmydata.scripts.common_http import do_http, format_http_error
262-from allmydata.util.stringutils import open_unicode
263+from allmydata.util.fileutil import open_expanduser
264 
265 def get(options):
266     nodeurl = options['node-url']
267hunk ./src/allmydata/scripts/tahoe_get.py 30
268     resp = do_http("GET", url)
269     if resp.status in (200, 201,):
270         if to_file:
271-            outf = open_unicode(to_file, "wb")
272+            outf = open_expanduser(to_file, "wb")
273         else:
274             outf = stdout
275         while True:
276hunk ./src/allmydata/scripts/tahoe_ls.py 7
277 from allmydata.scripts.common import get_alias, DEFAULT_ALIAS, escape_path, \
278                                      UnknownAliasError
279 from allmydata.scripts.common_http import do_http, format_http_error
280-from allmydata.util.stringutils import unicode_to_output, quote_output, is_printable_ascii, to_str
281+from allmydata.util.encodingutil import unicode_to_output, quote_output, is_printable_ascii, to_str
282 
283 def list(options):
284     nodeurl = options['node-url']
285hunk ./src/allmydata/scripts/tahoe_manifest.py 9
286 from allmydata.scripts.common import get_alias, DEFAULT_ALIAS, escape_path, \
287                                      UnknownAliasError
288 from allmydata.scripts.common_http import do_http, format_http_error
289-from allmydata.util.stringutils import quote_output, quote_path
290+from allmydata.util.encodingutil import quote_output, quote_path
291 
292 class FakeTransport:
293     disconnecting = False
294hunk ./src/allmydata/scripts/tahoe_mkdir.py 5
295 import urllib
296 from allmydata.scripts.common_http import do_http, check_http_error
297 from allmydata.scripts.common import get_alias, DEFAULT_ALIAS, UnknownAliasError
298-from allmydata.util.stringutils import quote_output
299+from allmydata.util.encodingutil import quote_output
300 
301 def mkdir(options):
302     nodeurl = options['node-url']
303hunk ./src/allmydata/scripts/tahoe_mv.py 8
304 from allmydata.scripts.common import get_alias, DEFAULT_ALIAS, escape_path, \
305                                      UnknownAliasError
306 from allmydata.scripts.common_http import do_http, format_http_error
307-from allmydata.util.stringutils import to_str
308+from allmydata.util.encodingutil import to_str
309 
310 # this script is used for both 'mv' and 'ln'
311 
312hunk ./src/allmydata/scripts/tahoe_put.py 7
313 from allmydata.scripts.common_http import do_http, format_http_success, format_http_error
314 from allmydata.scripts.common import get_alias, DEFAULT_ALIAS, escape_path, \
315                                      UnknownAliasError
316-from allmydata.util.stringutils import quote_output, open_unicode
317+from allmydata.util.encodingutil import quote_output
318+from allmydata.util.fileutil import open_expanduser
319 
320 def put(options):
321     """
322hunk ./src/allmydata/scripts/tahoe_put.py 68
323     if mutable:
324         url += "?mutable=true"
325     if from_file:
326-        infileobj = open_unicode(from_file, "rb")
327+        infileobj = open_expanduser(from_file, "rb")
328     else:
329         # do_http() can't use stdin directly: for one thing, we need a
330         # Content-Length field. So we currently must copy it.
331hunk ./src/allmydata/test/test_backupdb.py 7
332 from twisted.trial import unittest
333 
334 from allmydata.util import fileutil
335-from allmydata.util.stringutils import listdir_unicode, get_filesystem_encoding, unicode_platform
336+from allmydata.util.encodingutil import get_filesystem_encoding, unicode_platform
337 from allmydata.util.assertutil import precondition
338 from allmydata.scripts import backupdb
339 
340hunk ./src/allmydata/test/test_backupdb.py 252
341         self.failUnless(bdb)
342 
343         self.writeto(u"f\u00f6\u00f6.txt", "foo.txt")
344-        files = [fn for fn in listdir_unicode(unicode(basedir)) if fn.endswith(".txt")]
345+        files = [fn for fn in os.listdir(unicode(basedir)) if fn.endswith(".txt")]
346         self.failUnlessEqual(len(files), 1)
347         foo_fn = os.path.join(basedir, files[0])
348         #print foo_fn, type(foo_fn)
349hunk ./src/allmydata/test/test_cli.py 34
350 from twisted.python import usage
351 
352 from allmydata.util.assertutil import precondition
353-from allmydata.util.stringutils import listdir_unicode, open_unicode, unicode_platform, \
354-    quote_output, get_output_encoding, get_argv_encoding, get_filesystem_encoding, \
355+from allmydata.util.encodingutil import unicode_platform, quote_output, \
356+    get_output_encoding, get_argv_encoding, get_filesystem_encoding, \
357     unicode_to_output, to_str
358 
359 timeout = 480 # deep_check takes 360s on Zandr's linksys box, others take > 240s
360hunk ./src/allmydata/test/test_cli.py 442
361         fileutil.make_dirs(basedir)
362 
363         for name in filenames:
364-            open_unicode(os.path.join(unicode(basedir), name), "wb").close()
365+            open(os.path.join(unicode(basedir), name), "wb").close()
366 
367hunk ./src/allmydata/test/test_cli.py 444
368-        for file in listdir_unicode(unicode(basedir)):
369+        for file in os.listdir(unicode(basedir)):
370             self.failUnlessIn(normalize(file), filenames)
371 
372 
373hunk ./src/allmydata/test/test_cli.py 977
374         rel_fn = os.path.join(unicode(self.basedir), u"à trier.txt")
375         # we make the file small enough to fit in a LIT file, for speed
376         DATA = "short file"
377-        f = open_unicode(rel_fn, "wb")
378-        try:
379-            f.write(DATA)
380-        finally:
381-            f.close()
382+        fileutil.write(rel_fn, DATA)
383 
384         d = self.do_cli("create-alias", "tahoe")
385 
386hunk ./src/allmydata/test/test_cli.py 1348
387         self.set_up_grid()
388 
389         DATA1 = "unicode file content"
390-        f = open_unicode(fn1, "wb")
391-        try:
392-            f.write(DATA1)
393-        finally:
394-            f.close()
395+        fileutil.write(fn1, DATA1)
396 
397         fn2 = os.path.join(self.basedir, "Metallica")
398         DATA2 = "non-unicode file content"
399hunk ./src/allmydata/test/test_encodingutil.py 16
400 # systems.
401 
402 if __name__ == "__main__":
403-    import sys, os
404-    import tempfile
405-    import shutil
406+    import sys
407     import platform
408 
409     if len(sys.argv) != 2:
410hunk ./src/allmydata/test/test_encodingutil.py 32
411     print "    filesystem_encoding = '%s'" % sys.getfilesystemencoding()
412     print "    output_encoding = '%s'" % sys.stdout.encoding
413     print "    argv_encoding = '%s'" % (sys.platform == "win32" and 'ascii' or sys.stdout.encoding)
414-
415-    try:
416-        tmpdir = tempfile.mkdtemp()
417-        for fname in TEST_FILENAMES:
418-            open(os.path.join(tmpdir, fname), 'w').close()
419-
420-        # Use Unicode API under Windows or MacOS X
421-        if sys.platform in ('win32', 'darwin'):
422-            dirlist = os.listdir(unicode(tmpdir))
423-        else:
424-            dirlist = os.listdir(tmpdir)
425-
426-        print "    dirlist = %s" % repr(dirlist)
427-    except:
428-        print "    # Oops, I cannot write filenames containing non-ascii characters"
429     print
430 
431hunk ./src/allmydata/test/test_encodingutil.py 34
432-    shutil.rmtree(tmpdir)
433     sys.exit(0)
434 
435 from twisted.trial import unittest
436hunk ./src/allmydata/test/test_encodingutil.py 41
437 import sys
438 
439 from allmydata.test.common_util import ReallyEqualMixin
440-from allmydata.util.stringutils import argv_to_unicode, unicode_to_url, \
441-    unicode_to_output, unicode_platform, listdir_unicode, open_unicode, \
442-    FilenameEncodingError, get_output_encoding, _reload
443-from allmydata.dirnode import normalize
444+from allmydata.util.encodingutil import argv_to_unicode, unicode_to_url, \
445+    unicode_to_output, unicode_platform, get_output_encoding, _reload
446 
447 from twisted.python import usage
448 
449hunk ./src/allmydata/test/test_encodingutil.py 85
450         _reload()
451         self.failUnlessRaises(UnicodeEncodeError, unicode_to_output, lumiere_nfc)
452 
453-    @patch('os.listdir')
454-    def test_no_unicode_normalization(self, mock):
455-        # Pretend to run on a Unicode platform.
456-        # We normalized to NFC in 1.7beta, but we now don't.
457-        orig_platform = sys.platform
458-        try:
459-            sys.platform = 'darwin'
460-            mock.return_value = [Artonwall_nfd]
461-            _reload()
462-            self.failUnlessReallyEqual(listdir_unicode(u'/dummy'), [Artonwall_nfd])
463-        finally:
464-            sys.platform = orig_platform
465-
466-# The following tests applies only to platforms which don't store filenames as
467-# Unicode entities on the filesystem.
468-class StringUtilsNonUnicodePlatform(unittest.TestCase):
469-    def setUp(self):
470-        # Mock sys.platform because unicode_platform() uses it
471-        self.original_platform = sys.platform
472-        sys.platform = 'linux'
473-
474-    def tearDown(self):
475-        sys.platform = self.original_platform
476-        _reload()
477-
478-    @patch('sys.getfilesystemencoding')
479-    @patch('os.listdir')
480-    def test_listdir_unicode(self, mock_listdir, mock_getfilesystemencoding):
481-        # What happens if latin1-encoded filenames are encountered on an UTF-8
482-        # filesystem?
483-        mock_listdir.return_value = [
484-            lumiere_nfc.encode('utf-8'),
485-            lumiere_nfc.encode('latin1')]
486-
487-        mock_getfilesystemencoding.return_value = 'utf-8'
488-        _reload()
489-        self.failUnlessRaises(FilenameEncodingError,
490-                              listdir_unicode,
491-                              u'/dummy')
492-       
493-        # We're trying to list a directory whose name cannot be represented in
494-        # the filesystem encoding.  This should fail.
495-        mock_getfilesystemencoding.return_value = 'ascii'
496-        _reload()
497-        self.failUnlessRaises(FilenameEncodingError,
498-                              listdir_unicode,
499-                              u'/' + lumiere_nfc)
500-
501-    @patch('sys.getfilesystemencoding')
502-    def test_open_unicode(self, mock):
503-        mock.return_value = 'ascii'
504-        _reload()
505-        self.failUnlessRaises(FilenameEncodingError,
506-                              open_unicode,
507-                              lumiere_nfc, 'rb')
508 
509 class StringUtils(ReallyEqualMixin):
510     def setUp(self):
511hunk ./src/allmydata/test/test_encodingutil.py 130
512         _reload()
513         self.failUnlessReallyEqual(unicode_platform(), matrix[self.platform])
514 
515-    @patch('sys.getfilesystemencoding')
516-    @patch('os.listdir')
517-    def test_listdir_unicode(self, mock_listdir, mock_getfilesystemencoding):
518-        if 'dirlist' not in dir(self):
519-            return
520-
521-        try:
522-            u"test".encode(self.filesystem_encoding)
523-        except (LookupError, AttributeError):
524-            raise unittest.SkipTest("This platform does not support the '%s' filesystem encoding "
525-                                    "that we are testing for the benefit of a different platform."
526-                                    % (self.filesystem_encoding,))
527-
528-        mock_listdir.return_value = self.dirlist
529-        mock_getfilesystemencoding.return_value = self.filesystem_encoding
530-       
531-        _reload()
532-        filenames = listdir_unicode(u'/dummy')
533-
534-        self.failUnlessEqual(set([normalize(fname) for fname in filenames]),
535-                             set(TEST_FILENAMES))
536-
537-    @patch('sys.getfilesystemencoding')
538-    @patch('__builtin__.open')
539-    def test_open_unicode(self, mock_open, mock_getfilesystemencoding):
540-        mock_getfilesystemencoding.return_value = self.filesystem_encoding
541-        fn = u'/dummy_directory/" + lumiere_nfc + ".txt'
542-
543-        try:
544-            u"test".encode(self.filesystem_encoding)
545-        except (LookupError, AttributeError):
546-            raise unittest.SkipTest("This platform does not support the '%s' filesystem encoding "
547-                                    "that we are testing for the benefit of a different platform."
548-                                    % (self.filesystem_encoding,))
549-
550-        _reload()
551-        try:
552-            open_unicode(fn, 'rb')
553-        except FilenameEncodingError:
554-            return
555-
556-        # Pass Unicode string to open() on Unicode platforms
557-        if unicode_platform():
558-            mock_open.assert_called_with(fn, 'rb')
559-
560-        # Pass correctly encoded bytestrings to open() on non-Unicode platforms
561-        else:
562-            fn_bytestring = fn.encode(self.filesystem_encoding)
563-            mock_open.assert_called_with(fn_bytestring, 'rb')
564-
565 
566 class UbuntuKarmicUTF8(StringUtils, unittest.TestCase):
567     uname = 'Linux korn 2.6.31-14-generic #48-Ubuntu SMP Fri Oct 16 14:05:01 UTC 2009 x86_64'
568hunk ./src/allmydata/test/test_encodingutil.py 139
569     filesystem_encoding = 'UTF-8'
570     output_encoding = 'UTF-8'
571     argv_encoding = 'UTF-8'
572-    dirlist = ['test_file', '\xc3\x84rtonwall.mp3', 'Blah blah.txt']
573 
574 class UbuntuKarmicLatin1(StringUtils, unittest.TestCase):
575     uname = 'Linux korn 2.6.31-14-generic #48-Ubuntu SMP Fri Oct 16 14:05:01 UTC 2009 x86_64'
576hunk ./src/allmydata/test/test_encodingutil.py 148
577     filesystem_encoding = 'ISO-8859-1'
578     output_encoding = 'ISO-8859-1'
579     argv_encoding = 'ISO-8859-1'
580-    dirlist = ['test_file', 'Blah blah.txt', '\xc4rtonwall.mp3']
581 
582 class WindowsXP(StringUtils, unittest.TestCase):
583     uname = 'Windows XP 5.1.2600 x86 x86 Family 15 Model 75 Step ping 2, AuthenticAMD'
584hunk ./src/allmydata/test/test_encodingutil.py 156
585     filesystem_encoding = 'mbcs'
586     output_encoding = 'cp850'
587     argv_encoding = 'ascii'
588-    dirlist = [u'Blah blah.txt', u'test_file', u'\xc4rtonwall.mp3']
589 
590 class WindowsXP_UTF8(StringUtils, unittest.TestCase):
591     uname = 'Windows XP 5.1.2600 x86 x86 Family 15 Model 75 Step ping 2, AuthenticAMD'
592hunk ./src/allmydata/test/test_encodingutil.py 164
593     filesystem_encoding = 'mbcs'
594     output_encoding = 'cp65001'
595     argv_encoding = 'ascii'
596-    dirlist = [u'Blah blah.txt', u'test_file', u'\xc4rtonwall.mp3']
597 
598 class WindowsVista(StringUtils, unittest.TestCase):
599     uname = 'Windows Vista 6.0.6000 x86 x86 Family 6 Model 15 Stepping 11, GenuineIntel'
600hunk ./src/allmydata/test/test_encodingutil.py 172
601     filesystem_encoding = 'mbcs'
602     output_encoding = 'cp850'
603     argv_encoding = 'ascii'
604-    dirlist = [u'Blah blah.txt', u'test_file', u'\xc4rtonwall.mp3']
605 
606 class MacOSXLeopard(StringUtils, unittest.TestCase):
607     uname = 'Darwin g5.local 9.8.0 Darwin Kernel Version 9.8.0: Wed Jul 15 16:57:01 PDT 2009; root:xnu-1228.15.4~1/RELEASE_PPC Power Macintosh powerpc'
608hunk ./src/allmydata/test/test_encodingutil.py 181
609     filesystem_encoding = 'utf-8'
610     output_encoding = 'UTF-8'
611     argv_encoding = 'UTF-8'
612-    dirlist = [u'A\u0308rtonwall.mp3', u'Blah blah.txt', u'test_file']
613 
614 class MacOSXLeopard7bit(StringUtils, unittest.TestCase):
615     uname = 'Darwin g5.local 9.8.0 Darwin Kernel Version 9.8.0: Wed Jul 15 16:57:01 PDT 2009; root:xnu-1228.15.4~1/RELEASE_PPC Power Macintosh powerpc'
616hunk ./src/allmydata/test/test_encodingutil.py 188
617     filesystem_encoding = 'utf-8'
618     output_encoding = 'US-ASCII'
619     argv_encoding = 'US-ASCII'
620-    dirlist = [u'A\u0308rtonwall.mp3', u'Blah blah.txt', u'test_file']
621 
622 class OpenBSD(StringUtils, unittest.TestCase):
623     uname = 'OpenBSD 4.1 GENERIC#187 i386 Intel(R) Celeron(R) CPU 2.80GHz ("GenuineIntel" 686-class)'
624hunk ./src/allmydata/util/encodingutil.py 7
625 """
626 
627 import sys
628-import os
629 import re
630 from allmydata.util.assertutil import precondition
631 from twisted.python import usage
632hunk ./src/allmydata/util/encodingutil.py 176
633     Does the current platform handle Unicode filenames natively?
634     """
635     return is_unicode_platform
636-
637-class FilenameEncodingError(Exception):
638-    """
639-    Filename cannot be encoded using the current encoding of your filesystem
640-    (%s). Please configure your locale correctly or rename this file.
641-    """
642-    pass
643-
644-def listdir_unicode_fallback(path):
645-    """
646-    This function emulates a fallback Unicode API similar to one available
647-    under Windows or MacOS X.
648-
649-    If badly encoded filenames are encountered, an exception is raised.
650-    """
651-    precondition(isinstance(path, unicode), path)
652-
653-    try:
654-        byte_path = path.encode(filesystem_encoding)
655-    except (UnicodeEncodeError, UnicodeDecodeError):
656-        raise FilenameEncodingError(path)
657-
658-    try:
659-        return [unicode(fn, filesystem_encoding) for fn in os.listdir(byte_path)]
660-    except UnicodeDecodeError:
661-        raise FilenameEncodingError(fn)
662-
663-def listdir_unicode(path):
664-    """
665-    Wrapper around listdir() which provides safe access to the convenient
666-    Unicode API even under platforms that don't provide one natively.
667-    """
668-    precondition(isinstance(path, unicode), path)
669-
670-    # On Windows and MacOS X, the Unicode API is used
671-    # On other platforms (ie. Unix systems), the byte-level API is used
672-
673-    if is_unicode_platform:
674-        return os.listdir(path)
675-    else:
676-        return listdir_unicode_fallback(path)
677-
678-def open_unicode(path, mode):
679-    """
680-    Wrapper around open() which provides safe access to the convenient Unicode
681-    API even under Unix.
682-    """
683-    precondition(isinstance(path, unicode), path)
684-
685-    if is_unicode_platform:
686-        return open(os.path.expanduser(path), mode)
687-    else:
688-        try:
689-            return open(os.path.expanduser(path.encode(filesystem_encoding)), mode)
690-        except UnicodeEncodeError:
691-            raise FilenameEncodingError(path)
692-
693-def abspath_expanduser_unicode(path):
694-    precondition(isinstance(path, unicode), path)
695-
696-    if is_unicode_platform:
697-        return os.path.abspath(os.path.expanduser(path))
698-    else:
699-        try:
700-            pathstr = path.encode(filesystem_encoding)
701-            return os.path.abspath(os.path.expanduser(pathstr)).decode(filesystem_encoding)
702-        except (UnicodeEncodeError, UnicodeDecodeError):
703-            raise FilenameEncodingError(path)
704hunk ./src/allmydata/util/fileutil.py 212
705     finally:
706         rf.close()
707 
708+def put_file(pathname, inf):
709+    # TODO: create temporary file and move into place?
710+    outf = open_expanduser(pathname, "wb")
711+    try:
712+        while True:
713+            data = inf.read(32768)
714+            if not data:
715+                break
716+            outf.write(data)
717+    finally:
718+        outf.close()
719+
720+def open_expanduser(path, mode):
721+    assert isinstance(path, unicode), path
722+    return open(os.path.expanduser(path), mode)
723+
724+def abspath_expanduser(path):
725+    assert isinstance(path, unicode), path
726+    return os.path.abspath(os.path.expanduser(path))
727+
728hunk ./src/allmydata/web/common.py 15
729      MustBeReadonlyError, MustNotBeUnknownRWError
730 from allmydata.mutable.common import UnrecoverableFileError
731 from allmydata.util import abbreviate
732-from allmydata.util.stringutils import to_str
733+from allmydata.util.encodingutil import to_str
734 
735 class IOpHandleTable(Interface):
736     pass
737}
738
739Context:
740
741[upcase_since_on_welcome
742terrellrussell@gmail.com**20100708193903] 
743[server_version_on_welcome_page.dpatch.txt
744freestorm77@gmail.com**20100605191721
745 Ignore-this: b450c76dc875f5ac8cca229a666cbd0a
746 
747 
748 - The storage server version is 0 for all storage nodes in the Welcome Page
749 
750 
751] 
752[NEWS: add NEWS snippets about two recent patches
753zooko@zooko.com**20100708162058
754 Ignore-this: 6c9da6a0ad7351a960bdd60f81532899
755] 
756[directory_html_top_banner.dpatch
757freestorm77@gmail.com**20100622205301
758 Ignore-this: 1d770d975e0c414c996564774f049bca
759 
760 The div tag with the link "Return to Welcome page" on the directory.xhtml page is not correct
761 
762] 
763[tahoe_css_toolbar.dpatch
764freestorm77@gmail.com**20100622210046
765 Ignore-this: 5b3ebb2e0f52bbba718a932f80c246c0
766 
767 CSS modification to be correctly diplayed with Internet Explorer 8
768 
769 The links on the top of page directory.xhtml are not diplayed in the same line as display with Firefox.
770 
771] 
772[runnin_test_tahoe_css.dpatch
773freestorm77@gmail.com**20100622214714
774 Ignore-this: e0db73d68740aad09a7b9ae60a08c05c
775 
776 Runnin test for changes in tahoe.css file
777 
778] 
779[runnin_test_directory_xhtml.dpatch
780freestorm77@gmail.com**20100622201403
781 Ignore-this: f8962463fce50b9466405cb59fe11d43
782 
783 Runnin test for diretory.xhtml top banner
784 
785] 
786[stringutils.py: tolerate sys.stdout having no 'encoding' attribute.
787david-sarah@jacaranda.org**20100626040817
788 Ignore-this: f42cad81cef645ee38ac1df4660cc850
789] 
790[quickstart.html: python 2.5 -> 2.6 as recommended version
791david-sarah@jacaranda.org**20100705175858
792 Ignore-this: bc3a14645ea1d5435002966ae903199f
793] 
794[SFTP: don't call .stopProducing on the producer registered with OverwriteableFileConsumer (which breaks with warner's new downloader).
795david-sarah@jacaranda.org**20100628231926
796 Ignore-this: 131b7a5787bc85a9a356b5740d9d996f
797] 
798[docs/how_to_make_a_tahoe-lafs_release.txt: trivial correction, install.html should now be quickstart.html.
799david-sarah@jacaranda.org**20100625223929
800 Ignore-this: 99a5459cac51bd867cc11ad06927ff30
801] 
802[setup: in the Makefile, refuse to upload tarballs unless someone has passed the environment variable "BB_BRANCH" with value "trunk"
803zooko@zooko.com**20100619034928
804 Ignore-this: 276ddf9b6ad7ec79e27474862e0f7d6
805] 
806[trivial: tiny update to in-line comment
807zooko@zooko.com**20100614045715
808 Ignore-this: 10851b0ed2abfed542c97749e5d280bc
809 (I'm actually committing this patch as a test of the new eager-annotation-computation of trac-darcs.)
810] 
811[docs: about.html link to home page early on, and be decentralized storage instead of cloud storage this time around
812zooko@zooko.com**20100619065318
813 Ignore-this: dc6db03f696e5b6d2848699e754d8053
814] 
815[docs: update about.html, especially to have a non-broken link to quickstart.html, and also to comment out the broken links to "for Paranoids" and "for Corporates"
816zooko@zooko.com**20100619065124
817 Ignore-this: e292c7f51c337a84ebfeb366fbd24d6c
818] 
819[TAG allmydata-tahoe-1.7.0
820zooko@zooko.com**20100619052631
821 Ignore-this: d21e27afe6d85e2e3ba6a3292ba2be1
822] 
823Patch bundle hash:
8245dd0b81c02ed648d4eb4ab565a237c53ccbf4ff3