Ticket #534: unicode.dpatch

File unicode.dpatch, 37.1 KB (added by francois, at 2009-02-24T11:50:41Z)
Line 
1Tue Feb 24 01:06:57 CET 2009  francois@ctrlaltdel.ch
2  * CLI.txt: Document behaviour in presence of unicode filename
3
4Tue Feb 24 01:12:51 CET 2009  francois@ctrlaltdel.ch
5  * cli.py: Convert filenames and filepaths argument into unicode objects
6
7Tue Feb 24 01:15:54 CET 2009  francois@ctrlaltdel.ch
8  * common.py: support unicode alias names
9
10Tue Feb 24 01:16:59 CET 2009  francois@ctrlaltdel.ch
11  * tahoe_add_alias.py: support unicode alias names
12
13Tue Feb 24 01:21:18 CET 2009  francois@ctrlaltdel.ch
14  * tahoe_ls.py: convert filenames using unicode_to_stdout()
15
16Tue Feb 24 01:22:48 CET 2009  francois@ctrlaltdel.ch
17  * tahoe_manifest.py: use unicode_to_stdout() instead of .encode("utf-8") to avoid an exception if terminal doesn't support utf-8
18
19Tue Feb 24 01:23:45 CET 2009  francois@ctrlaltdel.ch
20  * tahoe_mkdir.py: path is now unicode and needs conversion before ending up in an url
21
22Tue Feb 24 01:25:13 CET 2009  francois@ctrlaltdel.ch
23  * test_cli.py: add a bunch of unicode tests
24
25Tue Feb 24 02:00:21 CET 2009  francois@ctrlaltdel.ch
26  * stringutils.py: common functions taking care of encoding conversions
27
28Tue Feb 24 02:02:47 CET 2009  francois@ctrlaltdel.ch
29  * tahoe_cp.py: handle unicode filename if the system locale supports it, print an error message otherwise
30
31Tue Feb 24 02:03:08 CET 2009  francois@ctrlaltdel.ch
32  * tahoe_backup.py: handle unicode filenames if locale settings supports it, print an error message otherwise
33
34Tue Feb 24 12:45:49 CET 2009  francois@ctrlaltdel.ch
35  * tahoe_backup.py: fix conflict
36
37New patches:
38
39[CLI.txt: Document behaviour in presence of unicode filename
40francois@ctrlaltdel.ch**20090224000657] hunk ./docs/frontends/CLI.txt 94
41-In Tahoe v1.3.0, passing non-ascii characters to the cli is not guaranteed to
42-work, although it might work on your platform, especially if your platform
43-uses utf-8 encoding.
44+Filenames containing non-ascii characters are supported on the commande
45+line if your terminal is correctly configured for UTF-8 support. This is
46+usually the case on moderns GNU/Linux distributions.
47+
48+If your terminal doesn't support UTF-8, you will still be able to list
49+directories but non-ascii characters will be replaced by a question mark
50+(?) on display.
51+
52+Reading from and writing to files whose name contain accentuated
53+characters is also supported when your system correctly understand them.
54+Under Unix, this is usually handled by locale settings. If Tahoe cannot
55+correctly decode a filename, it will raise an error. In such case,
56+you'll need to correct the name of your file, perhaps with help from
57+tools such as convmv.
58
59[cli.py: Convert filenames and filepaths argument into unicode objects
60francois@ctrlaltdel.ch**20090224001251] {
61hunk ./src/allmydata/scripts/cli.py 4
62+from allmydata.util.stringutils import argv_to_unicode
63hunk ./src/allmydata/scripts/cli.py 53
64-        self.where = where
65+        self.where = argv_to_unicode(where)
66hunk ./src/allmydata/scripts/cli.py 58
67-        self.alias = alias
68+        self.alias = argv_to_unicode(alias)
69hunk ./src/allmydata/scripts/cli.py 68
70-        self.alias = alias
71+        self.alias = argv_to_unicode(alias)
72hunk ./src/allmydata/scripts/cli.py 87
73-        self.where = where
74+        self.where = argv_to_unicode(where)
75hunk ./src/allmydata/scripts/cli.py 98
76-        self.from_file = arg1
77-        self.to_file = arg2
78+        self.from_file = argv_to_unicode(arg1)
79+
80+        if arg2:
81+            self.to_file = argv_to_unicode(arg2)
82+        else:
83+            self.to_file = None
84+
85hunk ./src/allmydata/scripts/cli.py 140
86-            self.from_file = arg1
87-            self.to_file = arg2
88+            self.from_file = argv_to_unicode(arg1)
89+            self.to_file =  argv_to_unicode(arg2)
90hunk ./src/allmydata/scripts/cli.py 143
91-            self.from_file = arg1 # might be "-"
92+            self.from_file = argv_to_unicode(arg1) # might be "-"
93hunk ./src/allmydata/scripts/cli.py 148
94-        if self.from_file == "-":
95+        if self.from_file == u"-":
96hunk ./src/allmydata/scripts/cli.py 182
97-        self.sources = args[:-1]
98-        self.destination = args[-1]
99+        self.sources = map(argv_to_unicode, args[:-1])
100+        self.destination = argv_to_unicode(args[-1])
101hunk ./src/allmydata/scripts/cli.py 187
102-        self.where = where
103+        self.where = argv_to_unicode(where)
104hunk ./src/allmydata/scripts/cli.py 194
105-        self.from_file = frompath
106-        self.to_file = topath
107+        self.from_file = argv_to_unicode(frompath)
108+        self.to_file = argv_to_unicode(topath)
109hunk ./src/allmydata/scripts/cli.py 202
110-        self.from_file = frompath
111-        self.to_file = topath
112+        self.from_file = argv_to_unicode(frompath)
113+        self.to_file = argv_to_unicode(topath)
114hunk ./src/allmydata/scripts/cli.py 227
115-        self.from_dir = localdir
116-        self.to_dir = topath
117+        self.from_dir = argv_to_unicode(localdir)
118+        self.to_dir = argv_to_unicode(topath)
119hunk ./src/allmydata/scripts/cli.py 276
120-        self.where = where
121+        self.where = argv_to_unicode(where)
122hunk ./src/allmydata/scripts/cli.py 291
123-        self.where = where
124+        self.where = argv_to_unicode(where)
125hunk ./src/allmydata/scripts/cli.py 303
126-        self.where = where
127+        self.where = argv_to_unicode(where)
128hunk ./src/allmydata/scripts/cli.py 318
129-        self.where = where
130+        self.where = argv_to_unicode(where)
131hunk ./src/allmydata/scripts/cli.py 334
132-        self.where = where
133+        self.where = argv_to_unicode(where)
134hunk ./src/allmydata/scripts/common.py 3
135+import codecs
136}
137
138[common.py: support unicode alias names
139francois@ctrlaltdel.ch**20090224001554] {
140hunk ./src/allmydata/scripts/common.py 5
141-
142+from allmydata.util.stringutils import unicode_to_url
143hunk ./src/allmydata/scripts/common.py 104
144-        f = open(aliasfile, "r")
145+        f = codecs.open(aliasfile, "r", "utf-8")
146hunk ./src/allmydata/scripts/common.py 111
147-            cap = cap.strip()
148+            cap = cap.strip().encode('ascii')
149hunk ./src/allmydata/scripts/common.py 150
150-    return "/".join([urllib.quote(s) for s in segments])
151+    return "/".join([urllib.quote(unicode_to_url(s)) for s in segments])
152}
153
154[tahoe_add_alias.py: support unicode alias names
155francois@ctrlaltdel.ch**20090224001659] {
156hunk ./src/allmydata/scripts/tahoe_add_alias.py 3
157+import codecs
158+import sys
159hunk ./src/allmydata/scripts/tahoe_add_alias.py 8
160+from allmydata.util.stringutils import unicode_to_stdout
161hunk ./src/allmydata/scripts/tahoe_add_alias.py 58
162-    f = open(aliasfile, "a")
163+    f = codecs.open(aliasfile, "a", "utf-8")
164hunk ./src/allmydata/scripts/tahoe_add_alias.py 61
165-    print >>stdout, "Alias '%s' created" % (alias,)
166+    print >>stdout, "Alias '%s' created" % (unicode_to_stdout(alias),)
167}
168
169[tahoe_ls.py: convert filenames using unicode_to_stdout()
170francois@ctrlaltdel.ch**20090224002118] {
171hunk ./src/allmydata/scripts/tahoe_ls.py 6
172+from allmydata.util.stringutils import unicode_to_stdout
173hunk ./src/allmydata/scripts/tahoe_ls.py 116
174-        line.append(name + classify)
175+        line.append(unicode_to_stdout(name) + classify)
176}
177
178[tahoe_manifest.py: use unicode_to_stdout() instead of .encode("utf-8") to avoid an exception if terminal doesn't support utf-8
179francois@ctrlaltdel.ch**20090224002248] hunk ./src/allmydata/scripts/tahoe_manifest.py 71
180-                    print >>stdout, d["cap"], "/".join([p.encode("utf-8")
181+                    print >>stdout, d["cap"], "/".join([unicode_to_stdout(p)
182
183[tahoe_mkdir.py: path is now unicode and needs conversion before ending up in an url
184francois@ctrlaltdel.ch**20090224002345] {
185hunk ./src/allmydata/scripts/tahoe_mkdir.py 5
186+from allmydata.util.stringutils import unicode_to_url
187hunk ./src/allmydata/scripts/tahoe_mkdir.py 35
188-                                           urllib.quote(path))
189+                                           urllib.quote(unicode_to_url(path)))
190}
191
192[test_cli.py: add a bunch of unicode tests
193francois@ctrlaltdel.ch**20090224002513] {
194hunk ./src/allmydata/test/test_cli.py 3
195+import sys
196hunk ./src/allmydata/test/test_cli.py 426
197+        d.addCallback(lambda res: self.do_cli("create-alias", "études"))
198+        def _check_create_unicode((rc,stdout,stderr)):
199+            self.failUnlessEqual(rc, 0)
200+            self.failIf(stderr)
201+
202+            # If stdout only supports ascii, accentuated characters are
203+            # being replaced by '?'
204+            if sys.stdout.encoding == "ANSI_X3.4-1968":
205+                self.failUnless("Alias '?tudes' created" in stdout)
206+            else:
207+                self.failUnless("Alias 'études' created" in stdout)
208+
209+            aliases = get_aliases(self.get_clientdir())
210+            self.failUnless(aliases[u"études"].startswith("URI:DIR2:"))
211+        d.addCallback(_check_create_unicode)
212+
213+        d.addCallback(lambda res: self.do_cli("ls", "études:"))
214+        def _check_ls1((rc, stdout, stderr)):
215+            self.failUnlessEqual(rc, 0)
216+            self.failIf(stderr)
217+
218+            self.failUnlessEqual(stdout, "")
219+        d.addCallback(_check_ls1)
220+
221+        d.addCallback(lambda res: self.do_cli("put", "-", "études:uploaded.txt",
222+          stdin="Blah blah blah"))
223+
224+        d.addCallback(lambda res: self.do_cli("ls", "études:"))
225+        def _check_ls2((rc, stdout, stderr)):
226+            self.failUnlessEqual(rc, 0)
227+            self.failIf(stderr)
228+
229+            self.failUnlessEqual(stdout, "uploaded.txt\n")
230+        d.addCallback(_check_ls2)
231+
232hunk ./src/allmydata/test/test_cli.py 685
233+    def test_immutable_from_file_unicode(self):
234+        # tahoe put file.txt "à trier.txt"
235+        self.basedir = os.path.dirname(self.mktemp())
236+        self.set_up_grid()
237+
238+        rel_fn = os.path.join(self.basedir, "DATAFILE")
239+        abs_fn = os.path.abspath(rel_fn)
240+        # we make the file small enough to fit in a LIT file, for speed
241+        DATA = "short file"
242+        f = open(rel_fn, "w")
243+        f.write(DATA)
244+        f.close()
245+
246+        d = self.do_cli("create-alias", "tahoe")
247+
248+        d.addCallback(lambda res:
249+                      self.do_cli("put", rel_fn, "à trier.txt"))
250+        def _uploaded((rc,stdout,stderr)):
251+            readcap = stdout.strip()
252+            self.failUnless(readcap.startswith("URI:LIT:"))
253+            self.failUnless("201 Created" in stderr, stderr)
254+            self.readcap = readcap
255+        d.addCallback(_uploaded)
256+
257+        d.addCallback(lambda res:
258+                      self.do_cli("get", "tahoe:à trier.txt"))
259+        d.addCallback(lambda (rc,stdout,stderr):
260+                      self.failUnlessEqual(stdout, DATA))
261+
262+        return d
263+
264hunk ./src/allmydata/test/test_cli.py 726
265+        d = self.do_cli("create-alias", "tahoe")
266+
267+        # Use unicode strings when calling os functions
268+        if sys.getfilesystemencoding() == "ANSI_X3.4-1968":
269+            fn1 = os.path.join(self.basedir, u"Artonwall")
270+        else:
271+            fn1 = os.path.join(self.basedir, u"Ärtonwall")
272hunk ./src/allmydata/test/test_cli.py 734
273-        fn1 = os.path.join(self.basedir, "Ärtonwall")
274hunk ./src/allmydata/test/test_cli.py 736
275+        d.addCallback(lambda res: self.do_cli("cp", fn1.encode('utf-8'), "tahoe:Ärtonwall"))
276+
277+        d.addCallback(lambda res: self.do_cli("get", "tahoe:Ärtonwall"))
278+        d.addCallback(lambda (rc,out,err): self.failUnlessEqual(out, DATA1))
279hunk ./src/allmydata/test/test_cli.py 741
280-        fn2 = os.path.join(self.basedir, "Metallica")
281+
282+        fn2 = os.path.join(self.basedir, u"Metallica")
283hunk ./src/allmydata/test/test_cli.py 749
284-        d = self.do_cli("create-alias", "tahoe")
285-        d.addCallback(lambda res: self.do_cli("cp", fn1, "tahoe:"))
286-        d.addCallback(lambda res: self.do_cli("cp", fn2, "tahoe:"))
287-
288-        d.addCallback(lambda res: self.do_cli("get", "tahoe:Ärtonwall"))
289-        d.addCallback(lambda (rc,out,err): self.failUnlessEqual(out, DATA1))
290+        d.addCallback(lambda res: self.do_cli("cp", fn2.encode('utf-8'), "tahoe:"))
291hunk ./src/allmydata/test/test_cli.py 754
292+        d.addCallback(lambda res: self.do_cli("ls", "tahoe:"))
293+
294hunk ./src/allmydata/test/test_cli.py 757
295-    test_unicode_filename.todo = "This behavior is not yet supported, although it does happen to work (for reasons that are ill-understood) on many platforms.  See issue ticket #534."
296hunk ./src/allmydata/test/test_cli.py 775
297+class Mkdir(GridTestMixin, CLITestMixin, unittest.TestCase):
298+    def test_unicode_mkdir(self):
299+        self.basedir = os.path.dirname(self.mktemp())
300+        self.set_up_grid()
301+
302+        d = self.do_cli("create-alias", "tahoe")
303+        d.addCallback(lambda res: self.do_cli("mkdir", "tahoe:Motörhead"))
304+
305+        return d
306+
307+
308hunk ./src/allmydata/test/test_cli.py 820
309+        if sys.getfilesystemencoding() == "ANSI_X3.4-1968":
310+            self.writeto(u"parent/artonwall.txt", "Marmelade Jacuzzi")
311+        else:
312+            self.writeto(u"parent/ärtonwall.txt", "Marmelade Jacuzzi")
313+
314hunk ./src/allmydata/test/test_cli.py 849
315-            # foo.txt, bar.txt, blah.txt
316-            self.failUnlessEqual(fu, 3)
317+            # foo.txt, bar.txt, blah.txt, ärtonwall.txt
318+            self.failUnlessEqual(fu, 4)
319hunk ./src/allmydata/test/test_cli.py 899
320-                # foo.txt, bar.txt, blah.txt
321+                # foo.txt, bar.txt, blah.txt, ärtonwall.txt
322hunk ./src/allmydata/test/test_cli.py 901
323-                self.failUnlessEqual(fr, 3)
324+                self.failUnlessEqual(fr, 4)
325hunk ./src/allmydata/test/test_cli.py 929
326-                self.failUnlessEqual(fchecked, 3)
327+                self.failUnlessEqual(fchecked, 4)
328hunk ./src/allmydata/test/test_cli.py 931
329-                self.failUnlessEqual(fr, 3)
330+                self.failUnlessEqual(fr, 4)
331hunk ./src/allmydata/test/test_cli.py 977
332-                # old bar.txt
333-                self.failUnlessEqual(fr, 1)
334+                # old bar.txt, ärtonwall.txt
335+                self.failUnlessEqual(fr, 2)
336hunk ./src/allmydata/test/test_cli.py 1017
337-            self.failUnlessEqual(fu, 5)
338+            self.failUnlessEqual(fu, 6)
339}
340
341[stringutils.py: common functions taking care of encoding conversions
342francois@ctrlaltdel.ch**20090224010021] {
343addfile ./src/allmydata/util/stringutils.py
344hunk ./src/allmydata/util/stringutils.py 1
345+"""
346+Functions used to convert inputs from whatever encoding used in the system to
347+unicode and back.
348+
349+TODO:
350+  * Accept two cli arguments --argv-encoding and --filesystem-encoding
351+"""
352+
353+import sys
354+from allmydata.util.assertutil import precondition
355+from twisted.python import usage
356+
357+def argv_to_unicode(s):
358+    """
359+    Decode given argv element to unicode.
360+    """
361+    # sys.argv encoding detection in Python is not trivial so utf-8 is
362+    # currently used by default and an informative error message is given if
363+    # the argument cannot be correctly decoded.
364+
365+    precondition(isinstance(s, str), s)
366+    try:
367+        return unicode(s, 'utf-8')
368+    except UnicodeEncodeError:
369+        raise usageError("Argument '%s' cannot be decoded as UTF-8." % s)
370+
371+def fs_to_unicode(s):
372+    """
373+    Decode a filename (or a directory name) to unicode using the same encoding
374+    as the filesystem.
375+    """
376+    # Filename encoding detection is a little bit better thanks to
377+    # getfilesystemencoding() in the sys module. However, filenames can be
378+    # encoded using another encoding than the one used on the filesystem.
379+   
380+    precondition(isinstance(s, str), s)
381+    encoding = sys.getfilesystemencoding()
382+    try:
383+        return unicode(s, encoding)
384+    except UnicodeDecodeError:
385+        raise usage.UsageError("Filename '%s' cannot be decoded using the current encoding of your filesystem (%s). Please rename this file." % (s, encoding))
386+
387+def unicode_to_fs(s):
388+    """
389+    Encode an unicode object used in file or directoy name.
390+    """
391+
392+    precondition(isinstance(s, unicode), s)
393+    encoding = sys.getfilesystemencoding()
394+    try:
395+        return s.encode(encoding)
396+    except UnicodeEncodeError:
397+        raise usage.UsageError("Filename '%s' cannot be encoded using the current encoding of your filesystem (%s). Please configure your locale correctly or rename this file." % (s, encoding))
398+
399+def unicode_to_url(s):
400+    """
401+    Encode an unicode object used in an URL.
402+    """
403+    # According to RFC 2718, non-ascii characters in url's must be UTF-8 encoded.
404+
405+    precondition(isinstance(s, unicode), s)
406+    return s.encode('utf-8')
407+
408+def unicode_to_stdout(s):
409+    """
410+    Encode an unicode object for representation on stdout.
411+    """
412+
413+    precondition(isinstance(s, unicode), s)
414+    return s.encode(sys.stdout.encoding, 'replace')
415}
416
417[tahoe_cp.py: handle unicode filename if the system locale supports it, print an error message otherwise
418francois@ctrlaltdel.ch**20090224010247] {
419hunk ./src/allmydata/scripts/tahoe_cp.py 5
420+import sys
421hunk ./src/allmydata/scripts/tahoe_cp.py 9
422+from twisted.python import usage
423+from allmydata.util.stringutils import fs_to_unicode, unicode_to_fs, unicode_to_url
424+from allmydata.util.assertutil import precondition
425hunk ./src/allmydata/scripts/tahoe_cp.py 72
426+        precondition(isinstance(pathname, unicode), pathname)
427hunk ./src/allmydata/scripts/tahoe_cp.py 83
428+        precondition(isinstance(pathname, unicode), pathname)
429hunk ./src/allmydata/scripts/tahoe_cp.py 96
430+        precondition(isinstance(pathname, unicode), pathname)
431hunk ./src/allmydata/scripts/tahoe_cp.py 110
432+        precondition(isinstance(pathname, unicode), pathname)
433+
434hunk ./src/allmydata/scripts/tahoe_cp.py 120
435-        children = os.listdir(self.pathname)
436+        children = os.listdir(unicode_to_fs(self.pathname))
437hunk ./src/allmydata/scripts/tahoe_cp.py 122
438+            n = fs_to_unicode(n)
439hunk ./src/allmydata/scripts/tahoe_cp.py 138
440+        precondition(isinstance(pathname, unicode), pathname)
441+
442hunk ./src/allmydata/scripts/tahoe_cp.py 148
443-        children = os.listdir(self.pathname)
444+        children = os.listdir(unicode_to_fs(self.pathname))
445hunk ./src/allmydata/scripts/tahoe_cp.py 150
446+            n = fs_to_unicode(n)
447hunk ./src/allmydata/scripts/tahoe_cp.py 172
448+        precondition(isinstance(name, unicode), name)
449hunk ./src/allmydata/scripts/tahoe_cp.py 174
450-        outf = open(pathname, "wb")
451+        outf = open(unicode_to_fs(pathname), "wb")
452hunk ./src/allmydata/scripts/tahoe_cp.py 359
453-                                                   urllib.quote(name.encode('utf-8'))])
454+                                                   urllib.quote(unicode_to_url(name))])
455}
456
457[tahoe_backup.py: handle unicode filenames if locale settings supports it, print an error message otherwise
458francois@ctrlaltdel.ch**20090224010308] {
459hunk ./src/allmydata/scripts/tahoe_backup.py 6
460+import sys
461hunk ./src/allmydata/scripts/tahoe_backup.py 12
462+from allmydata.util.stringutils import fs_to_unicode, unicode_to_fs, unicode_to_stdout
463+from allmydata.util.assertutil import precondition
464+from twisted.python import usage
465hunk ./src/allmydata/scripts/tahoe_backup.py 236
466-            print >>self.options.stdout, msg
467+            print >>self.options.stdout, unicode_to_stdout(msg)
468hunk ./src/allmydata/scripts/tahoe_backup.py 239
469+        precondition(isinstance(localpath, unicode), localpath)
470merger 0.0 (
471hunk ./src/allmydata/scripts/tahoe_backup.py 248
472-        for child in os.listdir(localpath):
473+        for child in self.options.filter_listdir(os.listdir(localpath)):
474hunk ./src/allmydata/scripts/tahoe_backup.py 248
475-        for child in os.listdir(localpath):
476-            childpath = os.path.join(localpath, child)
477+        for child in os.listdir(unicode_to_fs(localpath)):
478+            child = fs_to_unicode(child)
479+            childpath = os.path.join(localpath, child)
480)
481hunk ./src/allmydata/scripts/tahoe_backup.py 334
482+        precondition(isinstance(childpath, unicode), childpath)
483+
484hunk ./src/allmydata/scripts/tahoe_backup.py 344
485-            infileobj = open(os.path.expanduser(childpath), "rb")
486+            infileobj = open(unicode_to_fs(os.path.expanduser(childpath)), "rb")
487}
488
489[tahoe_backup.py: fix conflict
490francois@ctrlaltdel.ch**20090224114549] hunk ./src/allmydata/scripts/tahoe_backup.py 248
491-        for child in os.listdir(localpath):
492+        for child in self.options.filter_listdir(os.listdir(unicode_to_fs(localpath))):
493+            child = fs_to_unicode(child)
494
495Context:
496
497[test_repairer: change to use faster no_network.GridTestMixin, split Verifier tests into separate cases, refactor judgement funcs into shared methods
498warner@lothar.com**20090224041506
499 Ignore-this: 584ce72d6276da5edc00562793d4ee53
500] 
501[immutable/checker.py: trap ShareVersionIncompatible too. Also, use f.check
502warner@lothar.com**20090224041405
503 Ignore-this: b667e8d3192116293babcacdeed42898
504 instead of examining the value returned by f.trap, because the latter appears
505 to squash exception types down into their base classes (i.e. since
506 ShareVersionIncompatible is a subclass of LayoutInvalid,
507 f.trap(Failure(ShareVersionIncompatible)) == LayoutInvalid).
508 
509 All this resulted in 'incompatible' shares being misclassified as 'corrupt'.
510] 
511[immutable/layout.py: wrap to 80 cols, no functional changes
512warner@lothar.com**20090224005837
513 Ignore-this: 40019480180ec34141506a28d7711608
514] 
515[test_repairer: change Repairer to use much-faster no_network.GridTestMixin. As a side-effect, fix what I think was a bug: some of the assert-minimal-effort-expended checks were mixing write counts and allocate counts
516warner@lothar.com**20090223234227
517 Ignore-this: d58bd0a909f9939775730cda4a858cae
518] 
519[test/no_network.py: add a basic stats provider
520warner@lothar.com**20090223233937
521 Ignore-this: c9f3cc4eed99cfc36f68938ceff4162c
522] 
523[tests: stop using setUpClass/tearDownClass, since they've been deprecated in Twisted-8.2.0
524warner@lothar.com**20090223204312
525 Ignore-this: 24c6592141cf64103530c024f93a5b88
526] 
527[test_checker: improve test coverage for checker results
528warner@lothar.com**20090223201943
529 Ignore-this: 83e173602f0f4c811a7a9893d85385df
530] 
531[Fixed tests again so they will pass on windows.
532Alberto Berti <alberto@metapensiero.it>**20090223003502
533 Ignore-this: 80d5074e7153642a2fa2a77958bfb50d
534] 
535[misc/*: remove RuntimeError too
536warner@lothar.com**20090222233401
537 Ignore-this: b76f8a184f75bb28eb9d8002f957936a
538] 
539[scripts: stop using RuntimeError, for #639
540warner@lothar.com**20090222233106
541 Ignore-this: 686a424442670fffbd4d1816c284a601
542] 
543[mutable/publish: stop using RuntimeError, for #639
544warner@lothar.com**20090222233056
545 Ignore-this: 2a80a661c7850d97357caddad48c6e9d
546] 
547[remove more RuntimeError from unit tests, for #639
548warner@lothar.com**20090222232855
549 Ignore-this: 1a1c3e1457f3f29ba7101fe406ee5f43
550] 
551[stop using RuntimeError in unit tests, for #639
552warner@lothar.com**20090222232722
553 Ignore-this: 475ce0c0dcd7a1f5ed83ef460312efea
554] 
555[ftpd/sftpd: stop using RuntimeError, for #639
556warner@lothar.com**20090222232426
557 Ignore-this: 97001362c4ba9e94b2e254e229b79987
558] 
559[Added tests for the cse when listdir is an iterator
560Alberto Berti <alberto@metapensiero.it>**20090222224356
561 Ignore-this: 218fb2aba02c28b4b1e5324bdb5adeaa
562] 
563[Fixed tests so that they pass also on buildbots.
564Alberto Berti <alberto@metapensiero.it>**20090222224311
565 Ignore-this: fcb91cd6acf028382411d23d380a4576
566] 
567[Use failUnlessEqual instead of failUnless(a == b)
568Alberto Berti <alberto@metapensiero.it>**20090222224214
569 Ignore-this: 8f9144632e3ac9acb4726fb48a083bf4
570] 
571[Better implementation of filtering algorithm.
572Alberto Berti <alberto@metapensiero.it>**20090222224049
573 Ignore-this: 67a8bd2f99bcc87ca2443bef13370a87
574] 
575[Removed '.hgrags' from vcs excludes
576Alberto Berti <alberto@metapensiero.it>**20090222223946
577 Ignore-this: 3e94c22fc9d85f380ee11fb8bdb4d1e9
578] 
579[docs: CREDITS to Alberto Berti
580zooko@zooko.com**20090222193314
581 Ignore-this: 74d370ada3234cce9e58aec15d739f71
582] 
583[Added tests for the --exclude* options of backup command.
584Alberto Berti <alberto@metapensiero.it>**20090222165106
585 Ignore-this: f1b931cf2e7929ce47b737c022bca707
586] 
587[Added --exclude, --exclude-from and --exclude-vcs options to backup command.
588Alberto Berti <alberto@metapensiero.it>**20090222170829
589 Ignore-this: 4912890229cd54a2f61f14f06bc4afcc
590 
591 It is still impossible to specify absolute exclusion path, only
592 relative. I must check with tar or rsync how they allow them to be
593 specified.
594] 
595[Raise a more explanatory exception for errors encountered during backup processing.
596Alberto Berti <alberto@metapensiero.it>**20090222170252
597 Ignore-this: f6b8ffe2a903ba07a2c1c59130dac1e4
598] 
599[Added tests for the fixed alias related command's synopsis
600Alberto Berti <alberto@metapensiero.it>**20090222163732
601 Ignore-this: 4432b4e88e990ba53a5b3fe0f12db2ac
602] 
603[Add missing synopsis and descriptions for alias commands.
604Alberto Berti <alberto@metapensiero.it>**20090221003106
605 Ignore-this: 8aedd03d36d92d912102c7f29e4ca697
606] 
607[docs: move many specification-like documents into specifications/
608warner@lothar.com**20090222054054
609 Ignore-this: a4110cc478198c0611205aba1ccf54f4
610] 
611[test_web.py: increase test coverage of web.status.plural()
612warner@lothar.com**20090222000116
613 Ignore-this: 3138c9d5d2410d8e1121e9b2ed694169
614] 
615[crawler: fix performance problems: only save state once per timeslice (not after every bucket), don't start the crawler until 5 minutes after node startup
616warner@lothar.com**20090221205649
617 Ignore-this: e6551569982bd31d19779ff15c2d6f58
618] 
619[test_system: oops, don't assume that all files in storage/ are in a deep storage/shares/prefix/si/shnum path, since now the crawler pickle has a short path
620warner@lothar.com**20090221061710
621 Ignore-this: fde76d0e5cae853014d1bb18b5f17dae
622] 
623[crawler: tolerate low-resolution system clocks (i.e. windows)
624warner@lothar.com**20090221061533
625 Ignore-this: 57286a3abcaf44f6d1a78c3c1ad547a5
626] 
627[BucketCountingCrawler: store just the count, not cycle+count, since it's too easy to make usage mistakes otherwise
628warner@lothar.com**20090221035831
629 Ignore-this: 573b6f651af74380cdd64059fbbdda4b
630] 
631[test_storage: startService the server, as is now the standard practice
632warner@lothar.com**20090221035755
633 Ignore-this: 3999889bd628fe4039bbcf1b29160453
634] 
635[crawler: load state from the pickle in init, rather than waiting until startService, so get_state() can be called early
636warner@lothar.com**20090221035720
637 Ignore-this: ecd128a5f4364c0daf4b72d791340b66
638] 
639[BucketCountingCrawler: rename status and state keys to use 'bucket' instead of 'share', because the former is more accurate
640warner@lothar.com**20090221034606
641 Ignore-this: cf819f63fac9506c878d6c9715ce35b7
642] 
643[storage: also report space-free-for-root and space-free-for-nonroot, since that helps users understand the space-left-for-tahoe number better
644warner@lothar.com**20090221032856
645 Ignore-this: 9fdf0475f758acd98b73026677170b45
646] 
647[storage: add bucket-counting share crawler, add its output (number of files+directories maintained by a storage server) and status to the webapi /storage page
648warner@lothar.com**20090221030408
649 Ignore-this: 28761c5e076648026bc5f518506db65c
650] 
651[storage: move si_b2a/si_a2b/storage_index_to_dir out of server.py and into common.py
652warner@lothar.com**20090221030309
653 Ignore-this: 645056428ab797f0b542831c82bf192a
654] 
655[crawler: add get_progress, clean up get_state
656warner@lothar.com**20090221002743
657 Ignore-this: 9bea69f154c75b31a53425a8ea67789b
658] 
659[web/storage: make sure we can handle platforms without os.statvfs too
660warner@lothar.com**20090220220353
661 Ignore-this: 79d4cb8482a8543b9759dc949c86c587
662] 
663[crawler: provide for one-shot crawlers, which stop after their first full cycle, for share-upgraders and database-populaters
664warner@lothar.com**20090220211911
665 Ignore-this: fcdf72c5ffcafa374d376388be6fa5c5
666] 
667[web: add Storage status page, improve tests
668warner@lothar.com**20090220202926
669 Ignore-this: e34d5270dcf0237fe72f573f717c7a4
670] 
671[storage: include reserved_space in stats
672warner@lothar.com**20090220202920
673 Ignore-this: b5b480fe0abad0148ecad0c1fb47ecae
674] 
675[web/check_results: sort share identifiers in the sharemap display
676warner@lothar.com**20090220182922
677 Ignore-this: 5c7bfcee3e15c7082c3653eb8a460960
678] 
679[webapi: pass client through constructor arguments, remove IClient, should make it easier to test web renderers in isolation
680warner@lothar.com**20090220181554
681 Ignore-this: e7848cd1bee8faf2ce7aaf040b9bf8e3
682] 
683[test/no_network: do startService on the storage servers, make it easier to customize the storage servers
684warner@lothar.com**20090220022254
685 Ignore-this: e62f328721c007e4c5ee023a6efdf66d
686] 
687[crawler: modify API to support upcoming bucket-counting crawler
688warner@lothar.com**20090220013142
689 Ignore-this: 808f8382837b13082f8b245db2ebee06
690] 
691[test_backupdb: make the not-a-database file larger, since the older sqlite-2.3.2 on OS-X is easily fooled
692warner@lothar.com**20090220000409
693 Ignore-this: 694d2ca5053bb96e91670765d0cedf2e
694] 
695[web/reliability: add parameter descriptions, adapted from a patch from Terrell Russell.
696warner@lothar.com**20090219222918
697 Ignore-this: 835f5ab01e1aff31b2ff9febb9a51f3
698] 
699[test_crawler: hush pyflakes
700warner@lothar.com**20090219202340
701 Ignore-this: 765d22c9c9682cc86c5205dc130500af
702] 
703[test_crawler: disable the percentage-of-cpu-used test, since it is too unreliable on our slow buildslaves. But leave the code in place for developers to run by hand.
704warner@lothar.com**20090219201654
705 Ignore-this: ff7cf5cfa79c6f2ef0cf959495dd989a
706] 
707[reliability.py: fix the numpy conversion, it was completely broken. Thanks to Terrell Russell for the help.
708warner@lothar.com**20090219195515
709 Ignore-this: f2b1eb65855111b338e1487feee1bbcf
710] 
711[reliability: switch to NumPy, since Numeric is deprecated
712warner@lothar.com**20090219074435
713 Ignore-this: f588a68e9bcd3b0bc3653570882b6fd5
714] 
715[setup.py: fix pyflakes complaints
716warner@lothar.com**20090219073643
717 Ignore-this: a314e5456b0a796bc9f70232a119ec68
718] 
719[move show-tool-versions out of setup.py and into a separate script in misc/ , since setuptools is trying to build and install a bunch of stuff first
720warner@lothar.com**20090219073558
721 Ignore-this: 9e56bc43026379212e6b6671ed6a1fd4
722] 
723[test_crawler: don't require >=1 cycle on cygwin
724warner@lothar.com**20090219065818
725 Ignore-this: b8d2d40f26aeb30a7622479840a04635
726] 
727[setup.py: add show_tool_versions command, for the benefit of a new buildbot step
728warner@lothar.com**20090219062436
729 Ignore-this: 21d761c76a033e481831584bedc60c86
730] 
731[setup.py: wrap to 80 cols, no functional changes
732warner@lothar.com**20090219055751
733 Ignore-this: d29e57c6ee555f2ee435667b7e13e60b
734] 
735[crawler: use fileutil.move_info_place in preference to our own version
736warner@lothar.com**20090219051342
737 Ignore-this: ee4e46f3de965610503ba36b28184db9
738] 
739[fileutil: add move_into_place(), to perform the standard unix trick of atomically replacing a file, with a fallback for windows
740warner@lothar.com**20090219051310
741 Ignore-this: c1d35e8ca88fcb223ea194513611c511
742] 
743[crawler: fix problems on windows and our slow cygwin slave
744warner@lothar.com**20090219042431
745 Ignore-this: 8019cb0da79ba00c536183a6f57b4cab
746] 
747[#633: first version of a rate-limited interruptable share-crawler
748warner@lothar.com**20090219034633
749 Ignore-this: 5d2d30c743e3b096a8e775d5a9b33601
750] 
751[change StorageServer to take nodeid in the constructor, instead of assigning it later, since it's cleaner and because the original problem (Tubs not being ready until later) went away
752warner@lothar.com**20090218222301
753 Ignore-this: 740d582f20c93bebf60e21d9a446d3d2
754] 
755[test_system: split off checker tests to test_deepcheck.py, this file is too big
756warner@lothar.com**20090218214234
757 Ignore-this: 82bf8db81dfbc98224bbf694054a8761
758] 
759[break storage.py into smaller pieces in storage/*.py . No behavioral changes.
760warner@lothar.com**20090218204655
761 Ignore-this: 312d408d1cacc5a764d791b53ebf8f91
762] 
763[immutable/layout: minor change to repr name
764warner@lothar.com**20090218204648
765 Ignore-this: c8781ef15b7dea63b39236a1899b86ce
766] 
767[docs: add lease-tradeoffs diagram
768warner@lothar.com**20090218204137
769 Ignore-this: c22a589ad465dac846da834c30dc4083
770] 
771[interfaces.py: allow add/renew/cancel-lease to return Any, so that 1.3.1 clients (the first to use these calls) can tolerate future storage servers which might return something other than None
772warner@lothar.com**20090218192903
773 Ignore-this: dcbb704a05416ecc66d90fb486c3d75b
774] 
775[docs/debian.txt: minor edit
776warner@lothar.com**20090218032212
777 Ignore-this: 64ff1fb163ffca4bcfd920254f1cf866
778] 
779[add --add-lease to 'tahoe check', 'tahoe deep-check', and webapi.
780warner@lothar.com**20090218013243
781 Ignore-this: 176b2006cef5041adcb592ee83e084dd
782] 
783[change RIStorageServer.remote_add_lease to exit silently in case of no-such-bucket, instead of raising IndexError, because that makes the upcoming --add-lease feature faster and less noisy
784warner@lothar.com**20090218013053
785 Ignore-this: 6fdfcea2c832178f1ce72ab0ff510f3a
786] 
787[CLI #590: convert 'tahoe deep-check' to streaming form, improve display, add tests
788warner@lothar.com**20090217231511
789 Ignore-this: 6d88eb94b1c877eacc8c5ca7d0aac776
790] 
791[interfaces.py: document behavior of add_lease/renew_lease/cancel_lease, before I change it
792warner@lothar.com**20090217194809
793 Ignore-this: 703c6712926b8edb19d55d790b65a400
794] 
795[test_backupdb: improve error messages if the test fails
796warner@lothar.com**20090217170838
797 Ignore-this: ef657e87c66e4304d3e0aca9831b84c
798] 
799[webapi #590: add streaming deep-check. Still need a CLI tool to use it.
800warner@lothar.com**20090217053553
801 Ignore-this: a0edd3d2a531c48a64d8397f7e4b208c
802] 
803[test_web.Grid: change the CHECK() function to make it easier to test t= values with hyphens in them
804warner@lothar.com**20090217050034
805 Ignore-this: 410c08735347c2057df52f6716520228
806] 
807[test_web: improve checker-results coverage with a no-network -based test, enhance no-network harness to assist, fix some bugs in web/check_results.py that were exposed
808warner@lothar.com**20090217041242
809 Ignore-this: fe54bb66a9ae073c002a7af51cd1e18
810] 
811[web: fix handling of reliability page when Numeric is not available
812warner@lothar.com**20090217015658
813 Ignore-this: 9d329182f1b2e5f812e5e7eb5f4cf2ed
814] 
815[test/no_network: update comments with setup timing: no_network takes 50ms, SystemTestMixin takes 2s (on my laptop)
816warner@lothar.com**20090217000643
817 Ignore-this: cc778fa3219775b25057bfc9491f8f34
818] 
819[test_upload: rewrite in terms of no-network GridTestMixin, improve no_network.py as necessary
820warner@lothar.com**20090216234457
821 Ignore-this: 80a341d5aa3036d24de98e267499d70d
822] 
823[test_download: rewrite in terms of no-network GridTestMixin, improve no_network.py as necessary
824warner@lothar.com**20090216233658
825 Ignore-this: ec2febafd2403830519120fb3f3ca04e
826] 
827[test_dirnode.py: convert Deleter to new no-network gridtest
828warner@lothar.com**20090216232348
829 Ignore-this: 8041739442ec4db726675e48f9775ae9
830] 
831[test_cli.py: modify to use the new 'no-network' gridtest instead of SystemTestMixin, which speeds it up from 73s to 43s on my system
832warner@lothar.com**20090216232005
833 Ignore-this: ec6d010c9182aa72049d1fb894cf890e
834] 
835[tests: fix no_network framework to work with upload/download and checker
836warner@lothar.com**20090216231947
837 Ignore-this: 74b4dbd66b8384ae7c7544969fe4f744
838] 
839[client.py: improve docstring
840warner@lothar.com**20090216231532
841 Ignore-this: bbaa9e3f63fdb0048e3125c4681b2d1f
842] 
843[test_cli: add test coverage for help strings
844warner@lothar.com**20090216210833
845 Ignore-this: d2020849107f687448e159a19d0e5dab
846] 
847[test/no_network: new test harness, like system-test but doesn't use the network so it's faster
848warner@lothar.com**20090216205844
849 Ignore-this: 31678f7bdef30b0216fd657fc6145534
850] 
851[interfaces.py: minor docstring edit
852warner@lothar.com**20090216205816
853 Ignore-this: cec3855070197f7920b370f95e8b07bd
854] 
855[setup: if you sdist_dsc (to produce the input files for dpkg-buildpackage) then run darcsver first
856zooko@zooko.com**20090216201558
857 Ignore-this: b85be51b3d4a9a19a3366e690f1063e2
858] 
859[doc: a few edits to docs made after the 1.3.0 release
860zooko@zooko.com**20090216201539
861 Ignore-this: dbff3b929d88134d862f1dffd1ef068a
862] 
863[test_cli: improve test coverage slightly
864warner@lothar.com**20090216030451
865 Ignore-this: e01ccc6a6fb44aaa4fb14fe8669e2065
866] 
867[test_util: get almost full test coverage of dictutil, starting with the original pyutil tests as a base. The remaining three uncovered lines involve funny cases of ValueOrderedDict that I can't figure out how to get at
868warner@lothar.com**20090216023210
869 Ignore-this: dc1f0c6d8c003c0ade38bc8f8516b04d
870] 
871[provisioning/reliability: add tests, hush pyflakes, remove dead code, fix web links
872warner@lothar.com**20090215222451
873 Ignore-this: 7854df3e0130d9388f06efd4c797262f
874] 
875[util/statistics: add tests, fix mean_repair_cost
876warner@lothar.com**20090215222326
877 Ignore-this: c576eabc74c23b170702018fc3c122d9
878] 
879[test_repairer: hush pyflakes
880warner@lothar.com**20090215222310
881 Ignore-this: 875eb52e86077cda77efd02da77f8cfa
882] 
883[lossmodel.lyx: move draft paper into docs/proposed/, since it's unfinished
884warner@lothar.com**20090215221905
885 Ignore-this: 7f7ee204e47fd66932759c94deefe68
886] 
887[build a 'reliability' web page, with a simulation of file decay and repair over time
888warner@lothar.com**20090213234234
889 Ignore-this: 9e9623eaac7b0637bbd0071f082bd345
890] 
891[More lossmodel work, on repair.
892Shawn Willden <shawn-tahoe@willden.org>**20090116025648] 
893[Loss model work (temp1)
894Shawn Willden <shawn@willden.org>**20090115030058] 
895[Statistics module
896Shawn Willden <shawn-tahoe@willden.org>**20090114021235
897 
898 Added a statistics module for calculating various facets of
899 share survival statistics.
900] 
901[docs: relnotes-short.txt
902zooko@zooko.com**20090215163510
903 Ignore-this: 683649bb13499bbe0e5cea2e1716ff59
904 linkedin.com imposed a strict limit on the number of characters I could post.  This forced me to prune and prune and edit and edit until relnotes.txt was a quarter of its former size.  Here's the short version.
905] 
906[TAG allmydata-tahoe-1.3.0
907zooko@zooko.com**20090214000556
908 Ignore-this: aa6c9a31a14a58ad2298cb7b08d3ea70
909] 
910Patch bundle hash:
911f887deca1f988b992a6399f6b38d155b5ce00bfd