Ticket #1076: nfc-normalization-3.dpatch

File nfc-normalization-3.dpatch, 96.9 KB (added by davidsarah, at 2010-06-18T02:17:53Z)

Patch bundle for normalization changes including tests (and a new test for normalization of names coming out of a directory). Also work around a bug in locale.getpreferredencoding. Fixes a hold in the previous patch where childnames in directories created by modemaker.create_mutable/immutable_directory would not be normalized. Does not include the 'tahoe backup' change.

Line 
1Wed Jun 16 04:14:50 GMT Daylight Time 2010  david-sarah@jacaranda.org
2  * Provisional patch to NFC-normalize filenames going in and out of Tahoe directories.
3
4Wed Jun 16 05:20:12 GMT Daylight Time 2010  david-sarah@jacaranda.org
5  * stringutils.py: Add encoding argument to quote_output. Also work around a bug in locale.getpreferredencoding on older Pythons.
6
7Thu Jun 17 02:55:37 GMT Daylight Time 2010  david-sarah@jacaranda.org
8  * stringutils.py: don't NFC-normalize the output of listdir_unicode.
9
10Thu Jun 17 04:40:25 GMT Daylight Time 2010  david-sarah@jacaranda.org
11  * test_dirnode.py: partial tests for normalization changes.
12
13Thu Jun 17 04:44:09 GMT Daylight Time 2010  david-sarah@jacaranda.org
14  * test_stringutils.py: take account of the output of listdir_unicode no longer being normalized. Also use Unicode escapes, not UTF-8.
15
16Thu Jun 17 04:44:40 GMT Daylight Time 2010  david-sarah@jacaranda.org
17  * stringutils.py: remove unused import.
18
19Thu Jun 17 05:14:11 GMT Daylight Time 2010  david-sarah@jacaranda.org
20  * dirnode.py: comments about normalization changes.
21
22Fri Jun 18 01:02:49 GMT Daylight Time 2010  david-sarah@jacaranda.org
23  * dirnodes: fix normalization hole where childnames in directories created by nodemaker.create_mutable/immutable_directory would not be normalized. Add a test that we normalize names coming out of a directory.
24
25New patches:
26
27[Provisional patch to NFC-normalize filenames going in and out of Tahoe directories.
28david-sarah@jacaranda.org**20100616031450
29 Ignore-this: ed08c9d8df37ef0b7cca42bb562c996b
30] {
31hunk ./src/allmydata/dirnode.py 2
32 
33-import time, math
34+import time, math, unicodedata
35 
36 from zope.interface import implements
37 from twisted.internet import defer
38hunk ./src/allmydata/dirnode.py 19
39      DeepCheckAndRepairResults
40 from allmydata.monitor import Monitor
41 from allmydata.util import hashutil, mathutil, base32, log
42+from allmydata.util.stringutils import quote_output
43 from allmydata.util.assertutil import precondition
44 from allmydata.util.netstring import netstring, split_netstring
45 from allmydata.util.consumer import download_to_data
46hunk ./src/allmydata/dirnode.py 79
47 
48     return metadata
49 
50+def normalize(namex):
51+    return unicodedata.normalize('NFC', namex)
52 
53 # TODO: {Deleter,MetadataSetter,Adder}.modify all start by unpacking the
54 # contents and end by repacking them. It might be better to apply them to
55hunk ./src/allmydata/dirnode.py 87
56 # the unpacked contents.
57 
58 class Deleter:
59-    def __init__(self, node, name, must_exist=True, must_be_directory=False, must_be_file=False):
60+    def __init__(self, node, namex, must_exist=True, must_be_directory=False, must_be_file=False):
61         self.node = node
62hunk ./src/allmydata/dirnode.py 89
63-        self.name = name
64+        self.name = normalize(namex)
65         self.must_exist = must_exist
66         self.must_be_directory = must_be_directory
67         self.must_be_file = must_be_file
68hunk ./src/allmydata/dirnode.py 115
69 
70 
71 class MetadataSetter:
72-    def __init__(self, node, name, metadata, create_readonly_node=None):
73+    def __init__(self, node, namex, metadata, create_readonly_node=None):
74         self.node = node
75hunk ./src/allmydata/dirnode.py 117
76-        self.name = name
77+        self.name = normalize(namex)
78         self.metadata = metadata
79         self.create_readonly_node = create_readonly_node
80 
81hunk ./src/allmydata/dirnode.py 145
82         if entries is None:
83             entries = {}
84         precondition(isinstance(entries, dict), entries)
85+        # keys of 'entries' may not be normalized.
86         self.entries = entries
87         self.overwrite = overwrite
88         self.create_readonly_node = create_readonly_node
89hunk ./src/allmydata/dirnode.py 150
90 
91-    def set_node(self, name, node, metadata):
92-        precondition(isinstance(name, unicode), name)
93+    def set_node(self, namex, node, metadata):
94         precondition(IFilesystemNode.providedBy(node), node)
95hunk ./src/allmydata/dirnode.py 152
96-        self.entries[name] = (node, metadata)
97+        self.entries[namex] = (node, metadata)
98 
99     def modify(self, old_contents, servermap, first_time):
100         children = self.node._unpack_contents(old_contents)
101hunk ./src/allmydata/dirnode.py 157
102         now = time.time()
103-        for (name, (child, new_metadata)) in self.entries.iteritems():
104-            precondition(isinstance(name, unicode), name)
105+        for (namex, (child, new_metadata)) in self.entries.iteritems():
106+            name = normalize(namex)
107             precondition(IFilesystemNode.providedBy(child), child)
108 
109             # Strictly speaking this is redundant because we would raise the
110hunk ./src/allmydata/dirnode.py 168
111             metadata = None
112             if name in children:
113                 if not self.overwrite:
114-                    raise ExistingChildError("child '%s' already exists" % name)
115+                    raise ExistingChildError("child %s already exists" % quote_output(name, encoding='utf-8'))
116 
117                 if self.overwrite == "only-files" and IDirectoryNode.providedBy(children[name][0]):
118hunk ./src/allmydata/dirnode.py 171
119-                    raise ExistingChildError("child '%s' already exists" % name)
120+                    raise ExistingChildError("child %s already exists" % quote_output(name, encoding='utf-8'))
121                 metadata = children[name][1].copy()
122 
123             metadata = update_metadata(metadata, new_metadata, now)
124hunk ./src/allmydata/dirnode.py 182
125         new_contents = self.node._pack_contents(children)
126         return new_contents
127 
128+
129 def _encrypt_rw_uri(filenode, rw_uri):
130     assert isinstance(rw_uri, str)
131     writekey = filenode.get_writekey()
132hunk ./src/allmydata/dirnode.py 219
133         (child, metadata) = children[name]
134         child.raise_error()
135         if deep_immutable and not child.is_allowed_in_immutable_directory():
136-            raise MustBeDeepImmutableError("child '%s' is not allowed in an immutable directory" % (name,), name)
137+            raise MustBeDeepImmutableError("child %s is not allowed in an immutable directory" %
138+                                           quote_output(name, encoding='utf-8'), name)
139         if has_aux:
140             entry = children.get_aux(name)
141         if not entry:
142hunk ./src/allmydata/dirnode.py 292
143         return plaintext
144 
145     def _create_and_validate_node(self, rw_uri, ro_uri, name):
146+        # name is just for error reporting
147         node = self._nodemaker.create_from_cap(rw_uri, ro_uri,
148                                                deep_immutable=not self.is_mutable(),
149                                                name=name)
150hunk ./src/allmydata/dirnode.py 300
151         return node
152 
153     def _create_readonly_node(self, node, name):
154+        # name is just for error reporting
155         if not node.is_unknown() and node.is_readonly():
156             return node
157         return self._create_and_validate_node(None, node.get_readonly_uri(), name=name)
158hunk ./src/allmydata/dirnode.py 309
159         # the directory is serialized as a list of netstrings, one per child.
160         # Each child is serialized as a list of four netstrings: (name, ro_uri,
161         # rwcapdata, metadata), in which the name, ro_uri, metadata are in
162-        # cleartext. The 'name' is UTF-8 encoded. The rwcapdata is formatted as:
163+        # cleartext. The 'name' is UTF-8 encoded, and should be normalized to NFC.
164+        # The rwcapdata is formatted as:
165         # pack("16ss32s", iv, AES(H(writekey+iv), plaintext_rw_uri), mac)
166         assert isinstance(data, str), (repr(data), type(data))
167         # an empty directory is serialized as an empty string
168hunk ./src/allmydata/dirnode.py 323
169         while position < len(data):
170             entries, position = split_netstring(data, 1, position)
171             entry = entries[0]
172-            (name_utf8, ro_uri, rwcapdata, metadata_s), subpos = split_netstring(entry, 4)
173+            (namex_utf8, ro_uri, rwcapdata, metadata_s), subpos = split_netstring(entry, 4)
174             if not mutable and len(rwcapdata) > 0:
175                 raise ValueError("the rwcapdata field of a dirnode in an immutable directory was not empty")
176hunk ./src/allmydata/dirnode.py 326
177-            name = name_utf8.decode("utf-8")
178+
179+            # A name containing characters that are unassigned in one version of Unicode might
180+            # not be normalized wrt a later version. Therefore we normalize names going both in
181+            # and out of directories.
182+            name = normalize(namex_utf8.decode("utf-8"))
183+
184             rw_uri = ""
185             if writeable:
186                 rw_uri = self._decrypt_rwcapdata(rwcapdata)
187hunk ./src/allmydata/dirnode.py 354
188                     children[name] = (child, metadata)
189                     children.set_with_aux(name, (child, metadata), auxilliary=entry)
190                 else:
191-                    log.msg(format="mutable cap for child '%(name)s' unpacked from an immutable directory",
192-                                   name=name_utf8,
193+                    log.msg(format="mutable cap for child %(name)s unpacked from an immutable directory",
194+                                   name=quote_output(name, encoding='utf-8'),
195                                    facility="tahoe.webish", level=log.UNUSUAL)
196             except CapConstraintError, e:
197hunk ./src/allmydata/dirnode.py 358
198-                log.msg(format="unmet constraint on cap for child '%(name)s' unpacked from a directory:\n"
199-                               "%(message)s", message=e.args[0], name=name_utf8,
200+                log.msg(format="unmet constraint on cap for child %(name)s unpacked from a directory:\n"
201+                               "%(message)s", message=e.args[0], name=quote_output(name, encoding='utf-8'),
202                                facility="tahoe.webish", level=log.UNUSUAL)
203 
204         return children
205hunk ./src/allmydata/dirnode.py 422
206         name to a tuple of (IFilesystemNode, metadata)."""
207         return self._read()
208 
209-    def has_child(self, name):
210+    def has_child(self, namex):
211         """I return a Deferred that fires with a boolean, True if there
212         exists a child of the given name, False if not."""
213hunk ./src/allmydata/dirnode.py 425
214-        assert isinstance(name, unicode)
215+        name = normalize(namex)
216         d = self._read()
217         d.addCallback(lambda children: children.has_key(name))
218         return d
219hunk ./src/allmydata/dirnode.py 442
220             raise NoSuchChildError(name)
221         return child
222 
223-    def get(self, name):
224+    def get(self, namex):
225         """I return a Deferred that fires with the named child node,
226         which is an IFilesystemNode."""
227hunk ./src/allmydata/dirnode.py 445
228-        assert isinstance(name, unicode)
229+        name = normalize(namex)
230         d = self._read()
231         d.addCallback(self._get, name)
232         return d
233hunk ./src/allmydata/dirnode.py 450
234 
235-    def get_child_and_metadata(self, name):
236+    def get_child_and_metadata(self, namex):
237         """I return a Deferred that fires with the (node, metadata) pair for
238         the named child. The node is an IFilesystemNode, and the metadata
239         is a dictionary."""
240hunk ./src/allmydata/dirnode.py 454
241-        assert isinstance(name, unicode)
242+        name = normalize(namex)
243         d = self._read()
244         d.addCallback(self._get_with_metadata, name)
245         return d
246hunk ./src/allmydata/dirnode.py 459
247 
248-    def get_metadata_for(self, name):
249-        assert isinstance(name, unicode)
250+    def get_metadata_for(self, namex):
251+        name = normalize(namex)
252         d = self._read()
253         d.addCallback(lambda children: children[name][1])
254         return d
255hunk ./src/allmydata/dirnode.py 465
256 
257-    def set_metadata_for(self, name, metadata):
258-        assert isinstance(name, unicode)
259+    def set_metadata_for(self, namex, metadata):
260+        name = normalize(namex)
261         if self.is_readonly():
262             return defer.fail(NotWriteableError())
263         assert isinstance(metadata, dict)
264hunk ./src/allmydata/dirnode.py 476
265         d.addCallback(lambda res: self)
266         return d
267 
268-    def get_child_at_path(self, path):
269+    def get_child_at_path(self, pathx):
270         """Transform a child path into an IFilesystemNode.
271 
272         I perform a recursive series of 'get' operations to find the named
273hunk ./src/allmydata/dirnode.py 486
274         The path can be either a single string (slash-separated) or a list of
275         path-name elements.
276         """
277-        d = self.get_child_and_metadata_at_path(path)
278+        d = self.get_child_and_metadata_at_path(pathx)
279         d.addCallback(lambda (node, metadata): node)
280         return d
281 
282hunk ./src/allmydata/dirnode.py 490
283-    def get_child_and_metadata_at_path(self, path):
284+    def get_child_and_metadata_at_path(self, pathx):
285         """Transform a child path into an IFilesystemNode and
286         a metadata dictionary from the last edge that was traversed.
287         """
288hunk ./src/allmydata/dirnode.py 495
289 
290-        if not path:
291+        if not pathx:
292             return defer.succeed((self, {}))
293hunk ./src/allmydata/dirnode.py 497
294-        if isinstance(path, (list, tuple)):
295+        if isinstance(pathx, (list, tuple)):
296             pass
297         else:
298hunk ./src/allmydata/dirnode.py 500
299-            path = path.split("/")
300-        for p in path:
301-            assert isinstance(p, unicode)
302-        childname = path[0]
303-        remaining_path = path[1:]
304-        if remaining_path:
305-            d = self.get(childname)
306+            pathx = pathx.split("/")
307+        for p in pathx:
308+            assert isinstance(p, unicode), p
309+        childnamex = pathx[0]
310+        remaining_pathx = pathx[1:]
311+        if remaining_pathx:
312+            d = self.get(childnamex)
313             d.addCallback(lambda node:
314hunk ./src/allmydata/dirnode.py 508
315-                          node.get_child_and_metadata_at_path(remaining_path))
316+                          node.get_child_and_metadata_at_path(remaining_pathx))
317             return d
318hunk ./src/allmydata/dirnode.py 510
319-        d = self.get_child_and_metadata(childname)
320+        d = self.get_child_and_metadata(childnamex)
321         return d
322 
323hunk ./src/allmydata/dirnode.py 513
324-    def set_uri(self, name, writecap, readcap, metadata=None, overwrite=True):
325-        precondition(isinstance(name, unicode), name)
326+    def set_uri(self, namex, writecap, readcap, metadata=None, overwrite=True):
327         precondition(isinstance(writecap, (str,type(None))), writecap)
328         precondition(isinstance(readcap, (str,type(None))), readcap)
329 
330hunk ./src/allmydata/dirnode.py 519
331         # We now allow packing unknown nodes, provided they are valid
332         # for this type of directory.
333-        child_node = self._create_and_validate_node(writecap, readcap, name)
334-        d = self.set_node(name, child_node, metadata, overwrite)
335+        child_node = self._create_and_validate_node(writecap, readcap, namex)
336+        d = self.set_node(namex, child_node, metadata, overwrite)
337         d.addCallback(lambda res: child_node)
338         return d
339 
340hunk ./src/allmydata/dirnode.py 528
341         # this takes URIs
342         a = Adder(self, overwrite=overwrite,
343                   create_readonly_node=self._create_readonly_node)
344-        for (name, e) in entries.iteritems():
345-            assert isinstance(name, unicode)
346+        for (namex, e) in entries.iteritems():
347+            assert isinstance(namex, unicode), namex
348             if len(e) == 2:
349                 writecap, readcap = e
350                 metadata = None
351hunk ./src/allmydata/dirnode.py 541
352             
353             # We now allow packing unknown nodes, provided they are valid
354             # for this type of directory.
355-            child_node = self._create_and_validate_node(writecap, readcap, name)
356-            a.set_node(name, child_node, metadata)
357+            child_node = self._create_and_validate_node(writecap, readcap, namex)
358+            a.set_node(namex, child_node, metadata)
359         d = self._node.modify(a.modify)
360         d.addCallback(lambda ign: self)
361         return d
362hunk ./src/allmydata/dirnode.py 547
363 
364-    def set_node(self, name, child, metadata=None, overwrite=True):
365+    def set_node(self, namex, child, metadata=None, overwrite=True):
366         """I add a child at the specific name. I return a Deferred that fires
367         when the operation finishes. This Deferred will fire with the child
368         node that was just added. I will replace any existing child of the
369hunk ./src/allmydata/dirnode.py 560
370 
371         if self.is_readonly():
372             return defer.fail(NotWriteableError())
373-        assert isinstance(name, unicode)
374         assert IFilesystemNode.providedBy(child), child
375         a = Adder(self, overwrite=overwrite,
376                   create_readonly_node=self._create_readonly_node)
377hunk ./src/allmydata/dirnode.py 563
378-        a.set_node(name, child, metadata)
379+        a.set_node(namex, child, metadata)
380         d = self._node.modify(a.modify)
381         d.addCallback(lambda res: child)
382         return d
383hunk ./src/allmydata/dirnode.py 579
384         return d
385 
386 
387-    def add_file(self, name, uploadable, metadata=None, overwrite=True):
388+    def add_file(self, namex, uploadable, metadata=None, overwrite=True):
389         """I upload a file (using the given IUploadable), then attach the
390         resulting FileNode to the directory at the given name. I return a
391         Deferred that fires (with the IFileNode of the uploaded file) when
392hunk ./src/allmydata/dirnode.py 584
393         the operation completes."""
394-        assert isinstance(name, unicode)
395+        name = normalize(namex)
396         if self.is_readonly():
397             return defer.fail(NotWriteableError())
398         d = self._uploader.upload(uploadable)
399hunk ./src/allmydata/dirnode.py 594
400                       self.set_node(name, node, metadata, overwrite))
401         return d
402 
403-    def delete(self, name, must_exist=True, must_be_directory=False, must_be_file=False):
404+    def delete(self, namex, must_exist=True, must_be_directory=False, must_be_file=False):
405         """I remove the child at the specific name. I return a Deferred that
406         fires (with the node just removed) when the operation finishes."""
407hunk ./src/allmydata/dirnode.py 597
408-        assert isinstance(name, unicode)
409         if self.is_readonly():
410             return defer.fail(NotWriteableError())
411hunk ./src/allmydata/dirnode.py 599
412-        deleter = Deleter(self, name, must_exist=must_exist,
413+        deleter = Deleter(self, namex, must_exist=must_exist,
414                           must_be_directory=must_be_directory, must_be_file=must_be_file)
415         d = self._node.modify(deleter.modify)
416         d.addCallback(lambda res: deleter.old_child)
417hunk ./src/allmydata/dirnode.py 605
418         return d
419 
420-    def create_subdirectory(self, name, initial_children={}, overwrite=True,
421+    def create_subdirectory(self, namex, initial_children={}, overwrite=True,
422                             mutable=True, metadata=None):
423hunk ./src/allmydata/dirnode.py 607
424-        assert isinstance(name, unicode)
425+        name = normalize(namex)
426         if self.is_readonly():
427             return defer.fail(NotWriteableError())
428         if mutable:
429hunk ./src/allmydata/dirnode.py 624
430         d.addCallback(_created)
431         return d
432 
433-    def move_child_to(self, current_child_name, new_parent,
434-                      new_child_name=None, overwrite=True):
435+    def move_child_to(self, current_child_namex, new_parent,
436+                      new_child_namex=None, overwrite=True):
437         """I take one of my children and move them to a new parent. The child
438         is referenced by name. On the new parent, the child will live under
439         'new_child_name', which defaults to 'current_child_name'. I return a
440hunk ./src/allmydata/dirnode.py 630
441         Deferred that fires when the operation finishes."""
442-        assert isinstance(current_child_name, unicode)
443+
444         if self.is_readonly() or new_parent.is_readonly():
445             return defer.fail(NotWriteableError())
446hunk ./src/allmydata/dirnode.py 633
447-        if new_child_name is None:
448-            new_child_name = current_child_name
449-        assert isinstance(new_child_name, unicode)
450+
451+        current_child_name = normalize(current_child_namex)
452+        if new_child_namex is None:
453+            new_child_namex = current_child_name
454         d = self.get(current_child_name)
455         def sn(child):
456hunk ./src/allmydata/dirnode.py 639
457-            return new_parent.set_node(new_child_name, child,
458+            return new_parent.set_node(new_child_namex, child,
459                                        overwrite=overwrite)
460         d.addCallback(sn)
461         d.addCallback(lambda child: self.delete(current_child_name))
462}
463[stringutils.py: Add encoding argument to quote_output. Also work around a bug in locale.getpreferredencoding on older Pythons.
464david-sarah@jacaranda.org**20100616042012
465 Ignore-this: 48174c37ad95205997e4d3cdd81f1e28
466] {
467hunk ./src/allmydata/util/stringutils.py 13
468 from allmydata.util.assertutil import precondition
469 from twisted.python import usage
470 import locale
471+from allmydata.util import log
472 
473 
474 def _canonical_encoding(encoding):
475hunk ./src/allmydata/util/stringutils.py 18
476     if encoding is None:
477+        log.msg("Warning: falling back to UTF-8 encoding.", level=log.WEIRD)
478         encoding = 'utf-8'
479     encoding = encoding.lower()
480     if encoding == "cp65001":
481hunk ./src/allmydata/util/stringutils.py 23
482         encoding = 'utf-8'
483-    elif encoding == "us-ascii" or encoding == "646":
484+    elif encoding == "us-ascii" or encoding == "646" or encoding == "ansi_x3.4-1968":
485         encoding = 'ascii'
486 
487     # sometimes Python returns an encoding name that it doesn't support for conversion
488hunk ./src/allmydata/util/stringutils.py 44
489     global filesystem_encoding, output_encoding, argv_encoding, is_unicode_platform
490 
491     filesystem_encoding = _canonical_encoding(sys.getfilesystemencoding())
492-    output_encoding = _canonical_encoding(sys.stdout.encoding or locale.getpreferredencoding())
493+
494+    outenc = sys.stdout.encoding
495+    if outenc is None:
496+        try:
497+            outenc = locale.getpreferredencoding()
498+        except Exception:
499+            pass  # work around <http://bugs.python.org/issue1443504>
500+    output_encoding = _canonical_encoding(outenc)
501+
502     if sys.platform == 'win32':
503         # Unicode arguments are not supported on Windows yet; see #565 and #1074.
504         argv_encoding = 'ascii'
505hunk ./src/allmydata/util/stringutils.py 139
506                                  (output_encoding, repr(s)))
507     return out
508 
509-def quote_output(s, quotemarks=True):
510+def quote_output(s, quotemarks=True, encoding=None):
511     """
512     Encode either a Unicode string or a UTF-8-encoded bytestring for representation
513     on stdout or stderr, tolerating errors. If 'quotemarks' is True, the string is
514hunk ./src/allmydata/util/stringutils.py 155
515             return 'b' + repr(s)
516 
517     try:
518-        out = s.encode(output_encoding)
519+        out = s.encode(encoding or output_encoding)
520     except (UnicodeEncodeError, UnicodeDecodeError):
521         return repr(s)
522 
523}
524[stringutils.py: don't NFC-normalize the output of listdir_unicode.
525david-sarah@jacaranda.org**20100617015537
526 Ignore-this: 93c9b6f3d7c6812a0afa8d9e1b0b4faa
527] {
528hunk ./src/allmydata/util/stringutils.py 214
529     # On other platforms (ie. Unix systems), the byte-level API is used
530 
531     if is_unicode_platform:
532-        dirlist = os.listdir(path)
533+        return os.listdir(path)
534     else:
535hunk ./src/allmydata/util/stringutils.py 216
536-        dirlist = listdir_unicode_fallback(path)
537-
538-    # Normalize the resulting unicode filenames
539-    #
540-    # This prevents different OSes from generating non-equal unicode strings for
541-    # the same filename representation
542-    return [unicodedata.normalize('NFC', fname) for fname in dirlist]
543+        return listdir_unicode_fallback(path)
544 
545 def open_unicode(path, mode):
546     """
547}
548[test_dirnode.py: partial tests for normalization changes.
549david-sarah@jacaranda.org**20100617034025
550 Ignore-this: 2e3169dd8b120d42dff35bd267dcb417
551] {
552hunk ./src/allmydata/test/test_dirnode.py 49
553 future_write_uri = "x-tahoe-crazy://I_am_from_the_future."
554 future_read_uri = "x-tahoe-crazy-readonly://I_am_from_the_future."
555 
556+# 'o' 'n' 'e-macron'
557+one_nfc = u"on\u0113"
558+one_nfd = u"one\u0304"
559+
560 class Dirnode(GridTestMixin, unittest.TestCase,
561               testutil.ShouldFailMixin, testutil.StallMixin, ErrorMixin):
562     timeout = 240 # It takes longer than 120 seconds on Francois's arm box.
563hunk ./src/allmydata/test/test_dirnode.py 80
564         c = self.g.clients[0]
565         nm = c.nodemaker
566 
567-        kids = {u"one": (nm.create_from_cap(one_uri), {}),
568+        kids = {one_nfd: (nm.create_from_cap(one_uri), {}),
569                 u"two": (nm.create_from_cap(setup_py_uri),
570                          {"metakey": "metavalue"}),
571                 u"mut": (nm.create_from_cap(mut_write_uri, mut_read_uri), {}),
572hunk ./src/allmydata/test/test_dirnode.py 105
573         
574         def _check_kids(children):
575             self.failUnlessEqual(set(children.keys()),
576-                                 set([u"one", u"two", u"mut", u"fut", u"fro", u"empty_litdir", u"tiny_litdir"]))
577-            one_node, one_metadata = children[u"one"]
578+                                 set([one_nfc, u"two", u"mut", u"fut", u"fro", u"empty_litdir", u"tiny_litdir"]))
579+            one_node, one_metadata = children[one_nfc]
580             two_node, two_metadata = children[u"two"]
581             mut_node, mut_metadata = children[u"mut"]
582             fut_node, fut_metadata = children[u"fut"]
583hunk ./src/allmydata/test/test_dirnode.py 161
584         d.addCallback(_check_kids)
585 
586         bad_future_node = UnknownNode(future_write_uri, None)
587-        bad_kids1 = {u"one": (bad_future_node, {})}
588+        bad_kids1 = {one_nfd: (bad_future_node, {})}
589         # This should fail because we don't know how to diminish the future_write_uri
590         # cap (given in a write slot and not prefixed with "ro." or "imm.") to a readcap.
591         d.addCallback(lambda ign:
592hunk ./src/allmydata/test/test_dirnode.py 169
593                                       "cannot attach unknown",
594                                       nm.create_new_mutable_directory,
595                                       bad_kids1))
596-        bad_kids2 = {u"one": (nm.create_from_cap(one_uri), None)}
597+        bad_kids2 = {one_nfd: (nm.create_from_cap(one_uri), None)}
598         d.addCallback(lambda ign:
599                       self.shouldFail(AssertionError, "bad_kids2",
600                                       "requires metadata to be a dict",
601hunk ./src/allmydata/test/test_dirnode.py 183
602         c = self.g.clients[0]
603         nm = c.nodemaker
604 
605-        kids = {u"one": (nm.create_from_cap(one_uri), {}),
606+        kids = {one_nfd: (nm.create_from_cap(one_uri), {}),
607                 u"two": (nm.create_from_cap(setup_py_uri),
608                          {"metakey": "metavalue"}),
609                 u"fut": (nm.create_from_cap(None, future_read_uri), {}),
610hunk ./src/allmydata/test/test_dirnode.py 209
611         
612         def _check_kids(children):
613             self.failUnlessEqual(set(children.keys()),
614-                                 set([u"one", u"two", u"fut", u"empty_litdir", u"tiny_litdir"]))
615-            one_node, one_metadata = children[u"one"]
616+                                 set([one_nfc, u"two", u"fut", u"empty_litdir", u"tiny_litdir"]))
617+            one_node, one_metadata = children[one_nfc]
618             two_node, two_metadata = children[u"two"]
619             fut_node, fut_metadata = children[u"fut"]
620             emptylit_node, emptylit_metadata = children[u"empty_litdir"]
621hunk ./src/allmydata/test/test_dirnode.py 254
622         d.addCallback(_check_kids)
623 
624         bad_future_node1 = UnknownNode(future_write_uri, None)
625-        bad_kids1 = {u"one": (bad_future_node1, {})}
626+        bad_kids1 = {one_nfd: (bad_future_node1, {})}
627         d.addCallback(lambda ign:
628                       self.shouldFail(MustNotBeUnknownRWError, "bad_kids1",
629                                       "cannot attach unknown",
630hunk ./src/allmydata/test/test_dirnode.py 261
631                                       c.create_immutable_dirnode,
632                                       bad_kids1))
633         bad_future_node2 = UnknownNode(future_write_uri, future_read_uri)
634-        bad_kids2 = {u"one": (bad_future_node2, {})}
635+        bad_kids2 = {one_nfd: (bad_future_node2, {})}
636         d.addCallback(lambda ign:
637                       self.shouldFail(MustBeDeepImmutableError, "bad_kids2",
638                                       "is not immutable",
639hunk ./src/allmydata/test/test_dirnode.py 267
640                                       c.create_immutable_dirnode,
641                                       bad_kids2))
642-        bad_kids3 = {u"one": (nm.create_from_cap(one_uri), None)}
643+        bad_kids3 = {one_nfd: (nm.create_from_cap(one_uri), None)}
644         d.addCallback(lambda ign:
645                       self.shouldFail(AssertionError, "bad_kids3",
646                                       "requires metadata to be a dict",
647hunk ./src/allmydata/test/test_dirnode.py 273
648                                       c.create_immutable_dirnode,
649                                       bad_kids3))
650-        bad_kids4 = {u"one": (nm.create_from_cap(mut_write_uri), {})}
651+        bad_kids4 = {one_nfd: (nm.create_from_cap(mut_write_uri), {})}
652         d.addCallback(lambda ign:
653                       self.shouldFail(MustBeDeepImmutableError, "bad_kids4",
654                                       "is not immutable",
655hunk ./src/allmydata/test/test_dirnode.py 279
656                                       c.create_immutable_dirnode,
657                                       bad_kids4))
658-        bad_kids5 = {u"one": (nm.create_from_cap(mut_read_uri), {})}
659+        bad_kids5 = {one_nfd: (nm.create_from_cap(mut_read_uri), {})}
660         d.addCallback(lambda ign:
661                       self.shouldFail(MustBeDeepImmutableError, "bad_kids5",
662                                       "is not immutable",
663hunk ./src/allmydata/test/test_dirnode.py 336
664             d.addCallback(_check_kids)
665             d.addCallback(lambda ign: n.get(u"subdir"))
666             d.addCallback(lambda sd: self.failIf(sd.is_mutable()))
667-            bad_kids = {u"one": (nm.create_from_cap(mut_write_uri), {})}
668+            bad_kids = {one_nfd: (nm.create_from_cap(mut_write_uri), {})}
669             d.addCallback(lambda ign:
670                           self.shouldFail(MustBeDeepImmutableError, "YZ",
671                                           "is not immutable",
672}
673[test_stringutils.py: take account of the output of listdir_unicode no longer being normalized. Also use Unicode escapes, not UTF-8.
674david-sarah@jacaranda.org**20100617034409
675 Ignore-this: 47f3f072f0e2efea0abeac130f84c56f
676] {
677hunk ./src/allmydata/test/test_stringutils.py 1
678-# coding=utf-8
679+
680+lumiere_nfc = u"lumi\u00E8re"
681+Artonwall_nfc = u"\u00C4rtonwall.mp3"
682+Artonwall_nfd = u"A\u0308rtonwall.mp3"
683 
684 TEST_FILENAMES = (
685hunk ./src/allmydata/test/test_stringutils.py 7
686-  u'Ärtonwall.mp3',
687+  Artonwall_nfc,
688   u'test_file',
689   u'Blah blah.txt',
690 )
691hunk ./src/allmydata/test/test_stringutils.py 22
692     import platform
693 
694     if len(sys.argv) != 2:
695-        print "Usage: %s lumière" % sys.argv[0]
696+        print "Usage: %s lumi<e-grave>re" % sys.argv[0]
697         sys.exit(1)
698     
699     print
700hunk ./src/allmydata/test/test_stringutils.py 62
701 from allmydata.util.stringutils import argv_to_unicode, unicode_to_url, \
702     unicode_to_output, unicode_platform, listdir_unicode, open_unicode, \
703     FilenameEncodingError, get_output_encoding, _reload
704+from allmydata.dirnode import normalize
705 
706 from twisted.python import usage
707 
708hunk ./src/allmydata/test/test_stringutils.py 96
709 
710         self.failUnlessRaises(usage.UsageError,
711                               argv_to_unicode,
712-                              u'lumière'.encode('latin1'))
713+                              lumiere_nfc.encode('latin1'))
714 
715     @patch('sys.stdout')
716     def test_unicode_to_output(self, mock):
717hunk ./src/allmydata/test/test_stringutils.py 100
718-        # Encoding koi8-r cannot represent 'è'
719+        # Encoding koi8-r cannot represent e-grave
720         mock.encoding = 'koi8-r'
721         _reload()
722hunk ./src/allmydata/test/test_stringutils.py 103
723-        self.failUnlessRaises(UnicodeEncodeError, unicode_to_output, u'lumière')
724+        self.failUnlessRaises(UnicodeEncodeError, unicode_to_output, lumiere_nfc)
725 
726     @patch('os.listdir')
727hunk ./src/allmydata/test/test_stringutils.py 106
728-    def test_unicode_normalization(self, mock):
729-        # Pretend to run on an Unicode platform
730+    def test_no_unicode_normalization(self, mock):
731+        # Pretend to run on a Unicode platform.
732+        # We normalized to NFC in 1.7beta, but we now don't.
733         orig_platform = sys.platform
734         try:
735             sys.platform = 'darwin'
736hunk ./src/allmydata/test/test_stringutils.py 112
737-            mock.return_value = [u'A\u0308rtonwall.mp3']
738+            mock.return_value = [Artonwall_nfd]
739             _reload()
740hunk ./src/allmydata/test/test_stringutils.py 114
741-            self.failUnlessReallyEqual(listdir_unicode(u'/dummy'), [u'\xc4rtonwall.mp3'])
742+            self.failUnlessReallyEqual(listdir_unicode(u'/dummy'), [Artonwall_nfd])
743         finally:
744             sys.platform = orig_platform
745 
746hunk ./src/allmydata/test/test_stringutils.py 136
747         # What happens if latin1-encoded filenames are encountered on an UTF-8
748         # filesystem?
749         mock_listdir.return_value = [
750-            u'lumière'.encode('utf-8'),
751-            u'lumière'.encode('latin1')]
752+            lumiere_nfc.encode('utf-8'),
753+            lumiere_nfc.encode('latin1')]
754 
755         mock_getfilesystemencoding.return_value = 'utf-8'
756         _reload()
757hunk ./src/allmydata/test/test_stringutils.py 151
758         _reload()
759         self.failUnlessRaises(FilenameEncodingError,
760                               listdir_unicode,
761-                              u'/lumière')
762+                              u'/' + lumiere_nfc)
763 
764     @patch('sys.getfilesystemencoding')
765     def test_open_unicode(self, mock):
766hunk ./src/allmydata/test/test_stringutils.py 159
767         _reload()
768         self.failUnlessRaises(FilenameEncodingError,
769                               open_unicode,
770-                              u'lumière', 'rb')
771+                              lumiere_nfc, 'rb')
772 
773 class StringUtils(ReallyEqualMixin):
774     def setUp(self):
775hunk ./src/allmydata/test/test_stringutils.py 177
776             return
777 
778         mock.encoding = self.output_encoding
779-        argu = u'lumière'
780+        argu = lumiere_nfc
781         argv = self.argv
782         _reload()
783         self.failUnlessReallyEqual(argv_to_unicode(argv), argu)
784hunk ./src/allmydata/test/test_stringutils.py 183
785 
786     def test_unicode_to_url(self):
787-        self.failUnless(unicode_to_url(u'lumière'), "lumi\xc3\xa8re")
788+        self.failUnless(unicode_to_url(lumiere_nfc), "lumi\xc3\xa8re")
789 
790     @patch('sys.stdout')
791     def test_unicode_to_output(self, mock):
792hunk ./src/allmydata/test/test_stringutils.py 192
793 
794         mock.encoding = self.output_encoding
795         _reload()
796-        self.failUnlessReallyEqual(unicode_to_output(u'lumière'), self.output)
797+        self.failUnlessReallyEqual(unicode_to_output(lumiere_nfc), self.output)
798 
799     def test_unicode_platform(self):
800         matrix = {
801hunk ./src/allmydata/test/test_stringutils.py 224
802         _reload()
803         filenames = listdir_unicode(u'/dummy')
804 
805-        for fname in TEST_FILENAMES:
806-            self.failUnless(isinstance(fname, unicode))
807-            self.failUnlessIn(fname, filenames)
808+        self.failUnlessEqual(set([normalize(fname) for fname in filenames]),
809+                             set(TEST_FILENAMES))
810 
811     @patch('sys.getfilesystemencoding')
812     @patch('__builtin__.open')
813hunk ./src/allmydata/test/test_stringutils.py 231
814     def test_open_unicode(self, mock_open, mock_getfilesystemencoding):
815         mock_getfilesystemencoding.return_value = self.filesystem_encoding
816-        fn = u'/dummy_directory/lumière.txt'
817+        fn = u'/dummy_directory/" + lumiere_nfc + ".txt'
818 
819         try:
820             u"test".encode(self.filesystem_encoding)
821}
822[stringutils.py: remove unused import.
823david-sarah@jacaranda.org**20100617034440
824 Ignore-this: 16ec7d737c34665156c2ac486acd545a
825] hunk ./src/allmydata/util/stringutils.py 9
826 import sys
827 import os
828 import re
829-import unicodedata
830 from allmydata.util.assertutil import precondition
831 from twisted.python import usage
832 import locale
833[dirnode.py: comments about normalization changes.
834david-sarah@jacaranda.org**20100617041411
835 Ignore-this: 9040c4854e73a71dbbb55b50ea3b41b2
836] {
837hunk ./src/allmydata/dirnode.py 79
838 
839     return metadata
840 
841+
842+# 'x' at the end of a variable name indicates that it holds a Unicode string that may not
843+# be NFC-normalized.
844+
845 def normalize(namex):
846     return unicodedata.normalize('NFC', namex)
847 
848hunk ./src/allmydata/dirnode.py 332
849                 raise ValueError("the rwcapdata field of a dirnode in an immutable directory was not empty")
850 
851             # A name containing characters that are unassigned in one version of Unicode might
852-            # not be normalized wrt a later version. Therefore we normalize names going both in
853-            # and out of directories.
854+            # not be normalized wrt a later version. See the note in section 'Normalization Stability'
855+            # at <http://unicode.org/policies/stability_policy.html>.
856+            # Therefore we normalize names going both in and out of directories.
857             name = normalize(namex_utf8.decode("utf-8"))
858 
859             rw_uri = ""
860}
861[dirnodes: fix normalization hole where childnames in directories created by nodemaker.create_mutable/immutable_directory would not be normalized. Add a test that we normalize names coming out of a directory.
862david-sarah@jacaranda.org**20100618000249
863 Ignore-this: 46a9226eff1003013b067edbdbd4c25b
864] {
865hunk ./src/allmydata/dirnode.py 166
866             precondition(IFilesystemNode.providedBy(child), child)
867 
868             # Strictly speaking this is redundant because we would raise the
869-            # error again in pack_children.
870+            # error again in _pack_normalized_children.
871             child.raise_error()
872 
873             metadata = None
874hunk ./src/allmydata/dirnode.py 202
875     # The MAC is not checked by readers in Tahoe >= 1.3.0, but we still
876     # produce it for the sake of older readers.
877 
878-def pack_children(filenode, children, deep_immutable=False):
879+
880+def pack_children(filenode, childrenx, deep_immutable=False):
881+    # initial_children must have metadata (i.e. {} instead of None)
882+    children = {}
883+    for (namex, (node, metadata)) in childrenx.iteritems():
884+        precondition(isinstance(metadata, dict),
885+                     "directory creation requires metadata to be a dict, not None", metadata)
886+        children[normalize(namex)] = (node, metadata)
887+
888+    return _pack_normalized_children(filenode, children, deep_immutable=deep_immutable)
889+
890+
891+def _pack_normalized_children(filenode, children, deep_immutable=False):
892     """Take a dict that maps:
893hunk ./src/allmydata/dirnode.py 216
894-         children[unicode_name] = (IFileSystemNode, metadata_dict)
895+         children[unicode_nfc_name] = (IFileSystemNode, metadata_dict)
896     and pack it into a single string, for use as the contents of the backing
897     file. This is the same format as is returned by _unpack_contents. I also
898     accept an AuxValueDict, in which case I'll use the auxilliary cached data
899hunk ./src/allmydata/dirnode.py 383
900 
901     def _pack_contents(self, children):
902         # expects children in the same format as _unpack_contents
903-        return pack_children(self._node, children)
904+        return _pack_normalized_children(self._node, children)
905 
906     def is_readonly(self):
907         return self._node.is_readonly()
908hunk ./src/allmydata/nodemaker.py 3
909 import weakref
910 from zope.interface import implements
911-from allmydata.util.assertutil import precondition
912-from allmydata.interfaces import INodeMaker, MustBeDeepImmutableError
913+from allmydata.interfaces import INodeMaker
914 from allmydata.immutable.filenode import ImmutableFileNode, LiteralFileNode
915 from allmydata.immutable.upload import Data
916 from allmydata.mutable.filenode import MutableFileNode
917hunk ./src/allmydata/nodemaker.py 99
918         return d
919 
920     def create_new_mutable_directory(self, initial_children={}):
921-        # initial_children must have metadata (i.e. {} instead of None)
922-        for (name, (node, metadata)) in initial_children.iteritems():
923-            precondition(isinstance(metadata, dict),
924-                         "create_new_mutable_directory requires metadata to be a dict, not None", metadata)
925-            node.raise_error()
926         d = self.create_mutable_file(lambda n:
927                                      pack_children(n, initial_children))
928         d.addCallback(self._create_dirnode)
929hunk ./src/allmydata/nodemaker.py 107
930     def create_immutable_directory(self, children, convergence=None):
931         if convergence is None:
932             convergence = self.secret_holder.get_convergence_secret()
933-        for (name, (node, metadata)) in children.iteritems():
934-            precondition(isinstance(metadata, dict),
935-                         "create_immutable_directory requires metadata to be a dict, not None", metadata)
936-            node.raise_error()
937-            if not node.is_allowed_in_immutable_directory():
938-                raise MustBeDeepImmutableError("%s is not immutable" % (node,), name)
939         n = DummyImmutableFileNode() # writekey=None
940hunk ./src/allmydata/nodemaker.py 108
941-        packed = pack_children(n, children)
942+        packed = pack_children(n, children, deep_immutable=True)
943         uploadable = Data(packed, convergence)
944         d = self.uploader.upload(uploadable, history=self.history)
945         d.addCallback(lambda results: self.create_from_cap(None, results.uri))
946hunk ./src/allmydata/test/test_dirnode.py 3
947 
948 import time
949+import unicodedata
950 from zope.interface import implements
951 from twisted.trial import unittest
952 from twisted.internet import defer
953hunk ./src/allmydata/test/test_dirnode.py 265
954         bad_kids2 = {one_nfd: (bad_future_node2, {})}
955         d.addCallback(lambda ign:
956                       self.shouldFail(MustBeDeepImmutableError, "bad_kids2",
957-                                      "is not immutable",
958+                                      "is not allowed in an immutable directory",
959                                       c.create_immutable_dirnode,
960                                       bad_kids2))
961         bad_kids3 = {one_nfd: (nm.create_from_cap(one_uri), None)}
962hunk ./src/allmydata/test/test_dirnode.py 277
963         bad_kids4 = {one_nfd: (nm.create_from_cap(mut_write_uri), {})}
964         d.addCallback(lambda ign:
965                       self.shouldFail(MustBeDeepImmutableError, "bad_kids4",
966-                                      "is not immutable",
967+                                      "is not allowed in an immutable directory",
968                                       c.create_immutable_dirnode,
969                                       bad_kids4))
970         bad_kids5 = {one_nfd: (nm.create_from_cap(mut_read_uri), {})}
971hunk ./src/allmydata/test/test_dirnode.py 283
972         d.addCallback(lambda ign:
973                       self.shouldFail(MustBeDeepImmutableError, "bad_kids5",
974-                                      "is not immutable",
975+                                      "is not allowed in an immutable directory",
976                                       c.create_immutable_dirnode,
977                                       bad_kids5))
978         d.addCallback(lambda ign: c.create_immutable_dirnode({}))
979hunk ./src/allmydata/test/test_dirnode.py 340
980             bad_kids = {one_nfd: (nm.create_from_cap(mut_write_uri), {})}
981             d.addCallback(lambda ign:
982                           self.shouldFail(MustBeDeepImmutableError, "YZ",
983-                                          "is not immutable",
984+                                          "is not allowed in an immutable directory",
985                                           n.create_subdirectory,
986                                           u"sub2", bad_kids, mutable=False))
987             return d
988hunk ./src/allmydata/test/test_dirnode.py 347
989         d.addCallback(_made_parent)
990         return d
991 
992-    def test_spaces_are_stripped_on_the_way_out(self):
993-        self.basedir = "dirnode/Dirnode/test_spaces_are_stripped_on_the_way_out"
994+    def test_directory_representation(self):
995+        self.basedir = "dirnode/Dirnode/test_directory_representation"
996         self.set_up_grid()
997         c = self.g.clients[0]
998         nm = c.nodemaker
999hunk ./src/allmydata/test/test_dirnode.py 356
1000         # This test checks that any trailing spaces in URIs are retained in the
1001         # encoded directory, but stripped when we get them out of the directory.
1002         # See ticket #925 for why we want that.
1003+        # It also tests that we store child names as UTF-8 NFC, and normalize
1004+        # them again when retrieving them.
1005 
1006         stripped_write_uri = "lafs://from_the_future\t"
1007         stripped_read_uri = "lafs://readonly_from_the_future\t"
1008hunk ./src/allmydata/test/test_dirnode.py 368
1009         self.failUnlessEqual(child.get_write_uri(), spacedout_write_uri)
1010         self.failUnlessEqual(child.get_readonly_uri(), "ro." + spacedout_read_uri)
1011 
1012-        kids = {u"child": (child, {})}
1013-        d = c.create_dirnode(kids)
1014-       
1015+        child_dottedi = u"ch\u0131\u0307ld"
1016+
1017+        kids_in   = {child_dottedi: (child, {}), one_nfd: (child, {})}
1018+        kids_out  = {child_dottedi: (child, {}), one_nfc: (child, {})}
1019+        kids_norm = {u"child":      (child, {}), one_nfc: (child, {})}
1020+        d = c.create_dirnode(kids_in)
1021+
1022         def _created(dn):
1023             self.failUnless(isinstance(dn, dirnode.DirectoryNode))
1024             self.failUnless(dn.is_mutable())
1025hunk ./src/allmydata/test/test_dirnode.py 387
1026 
1027         def _check_data(data):
1028             # Decode the netstring representation of the directory to check that the
1029-            # spaces are retained when the URIs are stored.
1030+            # spaces are retained when the URIs are stored, and that the names are stored
1031+            # as NFC.
1032             position = 0
1033             numkids = 0
1034             while position < len(data):
1035hunk ./src/allmydata/test/test_dirnode.py 397
1036                 (name_utf8, ro_uri, rwcapdata, metadata_s), subpos = split_netstring(entry, 4)
1037                 name = name_utf8.decode("utf-8")
1038                 rw_uri = self.rootnode._decrypt_rwcapdata(rwcapdata)
1039-                self.failUnless(name in kids)
1040-                (expected_child, ign) = kids[name]
1041+                self.failUnlessIn(name, kids_out)
1042+                (expected_child, ign) = kids_out[name]
1043                 self.failUnlessEqual(rw_uri, expected_child.get_write_uri())
1044                 self.failUnlessEqual("ro." + ro_uri, expected_child.get_readonly_uri())
1045                 numkids += 1
1046hunk ./src/allmydata/test/test_dirnode.py 403
1047 
1048-            self.failUnlessEqual(numkids, 1)
1049-            return self.rootnode.list()
1050+            self.failUnlessEqual(numkids, len(kids_out))
1051+            return self.rootnode
1052         d.addCallback(_check_data)
1053hunk ./src/allmydata/test/test_dirnode.py 406
1054-       
1055-        # Now when we use the real directory listing code, the trailing spaces
1056-        # should have been stripped (and "ro." should have been prepended to the
1057-        # ro_uri, since it's unknown).
1058+
1059+        # Mock up a hypothetical future version of Unicode that adds a canonical equivalence
1060+        # between dotless-i + dot-above, and 'i'. That would actually be prohibited by the
1061+        # stability rules, but similar additions involving currently-unassigned characters
1062+        # would not be.
1063+        old_normalize = unicodedata.normalize
1064+        def future_normalize(form, s):
1065+            assert form == 'NFC', form
1066+            return old_normalize(form, s).replace(u"\u0131\u0307", u"i")
1067+
1068+        def _list(node):
1069+            unicodedata.normalize = future_normalize
1070+            d2 = node.list()
1071+            def _undo_mock(res):
1072+                unicodedata.normalize = old_normalize
1073+                return res
1074+            d2.addBoth(_undo_mock)
1075+            return d2
1076+        d.addCallback(_list)
1077+
1078         def _check_kids(children):
1079hunk ./src/allmydata/test/test_dirnode.py 427
1080-            self.failUnlessEqual(set(children.keys()), set([u"child"]))
1081+            # Now when we use the real directory listing code, the trailing spaces
1082+            # should have been stripped (and "ro." should have been prepended to the
1083+            # ro_uri, since it's unknown). Also the dotless-i + dot-above should have been
1084+            # normalized to 'i'.
1085+
1086+            self.failUnlessEqual(set(children.keys()), set(kids_norm.keys()))
1087             child_node, child_metadata = children[u"child"]
1088 
1089             self.failUnlessEqual(child_node.get_write_uri(), stripped_write_uri)
1090hunk ./src/allmydata/test/test_dirnode.py 440
1091         d.addCallback(_check_kids)
1092 
1093         d.addCallback(lambda ign: nm.create_from_cap(self.cap.to_string()))
1094-        d.addCallback(lambda n: n.list())
1095+        d.addCallback(_list)
1096         d.addCallback(_check_kids)  # again with dirnode recreated from cap
1097         return d
1098 
1099}
1100
1101Context:
1102
1103[running.html: fix overeager replacement of 'tahoe' with 'Tahoe-LAFS', and some simplifications.
1104david-sarah@jacaranda.org**20100617000952
1105 Ignore-this: 472b4b531c866574ed79f076b58495b5
1106] 
1107[Add a specification for servers of happiness.
1108Kevan Carstensen <kevan@isnotajoke.com>**20100524003508
1109 Ignore-this: 982e2be8a411be5beaf3582bdfde6151
1110] 
1111[Note that servers of happiness only applies to immutable files for the moment
1112Kevan Carstensen <kevan@isnotajoke.com>**20100524042836
1113 Ignore-this: cf83cac7a2b3ed347ae278c1a7d9a176
1114] 
1115[Add a note about running Tahoe-LAFS on a small grid to running.html
1116zooko@zooko.com**20100616140227
1117 Ignore-this: 14dfbff0d47144f7c2375108c6055dc2
1118 also Change "tahoe" and "Tahoe" to "Tahoe-LAFS" in running.html
1119 author: Kevan Carstensen
1120] 
1121[CLI.txt: introduce 'create-alias' before 'add-alias', document Unicode argument support, and other minor updates.
1122david-sarah@jacaranda.org**20100610225547
1123 Ignore-this: de7326e98d79291cdc15aed86ae61fe8
1124] 
1125[test_system.py: investigate failure in allmydata.test.test_system.SystemTest.test_upload_and_download_random_key due to bytes_sent not being an int
1126david-sarah@jacaranda.org**20100616001648
1127 Ignore-this: 9c78092ab7bfdc909acae3a144ddd1f8
1128] 
1129[SFTP: remove a dubious use of 'pragma: no cover'.
1130david-sarah@jacaranda.org**20100613164356
1131 Ignore-this: 8f96a81b1196017ed6cfa1d914e56fa5
1132] 
1133[SFTP: test that renaming onto a just-opened file fails.
1134david-sarah@jacaranda.org**20100612033709
1135 Ignore-this: 9b14147ad78b16a5ab0e0e4813491414
1136] 
1137[SFTP: further small improvements to test coverage. Also ensure that after a test failure, later tests don't fail spuriously due to the checks for heisenfile leaks.
1138david-sarah@jacaranda.org**20100612030737
1139 Ignore-this: 4ec1dd3d7542be42007987a2f51508e7
1140] 
1141[SFTP: further improve test coverage (paths containing '.', bad data for posix-rename extension, and error in test of openShell).
1142david-sarah@jacaranda.org**20100611213142
1143 Ignore-this: 956f9df7f9e8a66b506ca58dd9a5dbe7
1144] 
1145[SFTP: improve test coverage for no-write on mutable files, and check for heisenfile table leaks in all relevant tests. Delete test_memory_leak since it is now redundant.
1146david-sarah@jacaranda.org**20100611205752
1147 Ignore-this: 88be1cf323c10dd534a4b8fdac121e31
1148] 
1149[SFTP: add test for extension of file opened with FXF_APPEND.
1150david-sarah@jacaranda.org**20100610182647
1151 Ignore-this: c0216d26453ce3cb4b92eef37d218fb4
1152] 
1153[NEWS: add UTF-8 coding declaration.
1154david-sarah@jacaranda.org**20100609234851
1155 Ignore-this: 3e6ef125b278e0a982c88d23180a78ae
1156] 
1157[tests: bump up the timeout on this iputil test from 2s to 4s
1158zooko@zooko.com**20100609143017
1159 Ignore-this: 786b7f7bbc85d45cdf727a6293750798
1160] 
1161[docs: a few tweaks to NEWS and CREDITS and make quickstart.html point to 1.7.0β!
1162zooko@zooko.com**20100609142927
1163 Ignore-this: f8097d3062f41f06c4420a7c84a56481
1164] 
1165[docs: Update NEWS file with new features and bugfixes in 1.7.0
1166francois@ctrlaltdel.ch**20100609091120
1167 Ignore-this: 8c1014e4469ef530e5ff48d7d6ae71c5
1168] 
1169[docs: wording fix, thanks to Jeremy Visser, fix #987
1170francois@ctrlaltdel.ch**20100609081103
1171 Ignore-this: 6d2e627e0f1cd58c0e1394e193287a4b
1172] 
1173[SFTP: fix most significant memory leak described in #1045 (due to a file being added to all_heisenfiles under more than one direntry when renamed).
1174david-sarah@jacaranda.org**20100609080003
1175 Ignore-this: 490b4c14207f6725d0dd32c395fbcefa
1176] 
1177[test_stringutils.py: Fix test failure on CentOS builder, possibly Python 2.4.3-related.
1178david-sarah@jacaranda.org**20100609065056
1179 Ignore-this: 503b561b213baf1b92ae641f2fdf080a
1180] 
1181[docs: update relnote.txt for Tahoe-LAFS v1.7.0β
1182zooko@zooko.com**20100609054602
1183 Ignore-this: 52e1bf86a91d45315960fb8806b7a479
1184] 
1185[setup: move the mock library from install_requires to tests_require (re: #1016)
1186zooko@zooko.com**20100609050542
1187 Ignore-this: c51a4ff3e19ed630755be752d2233db4
1188] 
1189[setup: show-tool-versions.py: print out the output from the unix command "locale" and re-arrange encoding data a little bit
1190zooko@zooko.com**20100609040714
1191 Ignore-this: 69382719b462d13ff940fcd980776004
1192] 
1193[setup: add zope.interface to the packages described by show-tool-versions.py
1194zooko@zooko.com**20100609034915
1195 Ignore-this: b5262b2af5c953a5f68a60bd48dcaa75
1196] 
1197[Fix for Unicode-related test failures on Zooko's OS X 10.6 machine.
1198david-sarah@jacaranda.org**20100609055448
1199 Ignore-this: 395ad16429e56623edfa74457a121190
1200] 
1201[stringutils.py, sftpd.py: Portability fixes for Python <= 2.5.
1202david-sarah@jacaranda.org**20100609013302
1203 Ignore-this: 9d9ce476ee1b96796e0f48cc5338f852
1204] 
1205[_auto_deps.py: allow Python 2.4.3 on Redhat-based distributions.
1206david-sarah@jacaranda.org**20100609003646
1207 Ignore-this: ad3cafdff200caf963024873d0ebff3c
1208] 
1209[CREDITS: update François's Description
1210zooko@zooko.com**20100608155513
1211 Ignore-this: a266b438d25ca2cb28eafff75aa4b2a
1212] 
1213[CREDITS: jsgf
1214zooko@zooko.com**20100608143052
1215 Ignore-this: 10abe06d40b88e22a9107d30f1b84810
1216] 
1217[setup: rename the setuptools_trial .egg that comes bundled in the base dir to not have "-py2.6" in its name, since it works with other versions of python as well
1218zooko@zooko.com**20100608041607
1219 Ignore-this: 64fe386d2e5fba0ab441116e74dad5a3
1220] 
1221[setup: rename the darcsver .egg that comes bundled in the base dir to not have "-py2.6" in its name, since it works with other versions of python as well
1222zooko@zooko.com**20100608041534
1223 Ignore-this: 53f925f160256409cf01b76d2583f83f
1224] 
1225[Back out Windows-specific Unicode argument support for v1.7.
1226david-sarah@jacaranda.org**20100609000803
1227 Ignore-this: b230ffe6fdaf9a0d85dfe745b37b42fb
1228] 
1229[SFTP: suppress NoSuchChildError if heisenfile attributes have been updated in setAttrs, in the case where the parent is available.
1230david-sarah@jacaranda.org**20100608063753
1231 Ignore-this: 8c72a5a9c15934f8fe4594ba3ee50ddd
1232] 
1233[SFTP: ignore permissions when opening a file (needed for sshfs interoperability).
1234david-sarah@jacaranda.org**20100608055700
1235 Ignore-this: f87f6a430f629326a324ddd94426c797
1236] 
1237[tests: bump up the timeout on these tests; MM's buildslave is sometimes extremely slow on tests, but it will complete them if given enough time. MM is working on making that buildslave more predictable in how long it takes to run tests.
1238zooko@zooko.com**20100608033754
1239 Ignore-this: 98dc27692c5ace1e4b0650b6680629d7
1240] 
1241[test_web.py: fix pyflakes warnings introduced by byterange patch.
1242david-sarah@jacaranda.org**20100608042012
1243 Ignore-this: a7612724893b51d1154dec4372e0508
1244] 
1245[Improve HTTP/1.1 byterange handling
1246Jeremy Fitzhardinge <jeremy@goop.org>**20100310025913
1247 Ignore-this: 6d69e694973d618f0dc65983735cd9be
1248 
1249 Fix parsing of a Range: header to support:
1250  - multiple ranges (parsed, but not returned)
1251  - suffix byte ranges ("-2139")
1252  - correct handling of incorrectly formatted range headers
1253    (correct behaviour is to ignore the header and return the full
1254     file)
1255  - return appropriate error for ranges outside the file
1256 
1257 Multiple ranges are parsed, but only the first range is returned.
1258 Returning multiple ranges requires using the multipart/byterange
1259 content type.
1260 
1261] 
1262[test_cli.py: remove invalid 'test_listdir_unicode_bad' test.
1263david-sarah@jacaranda.org**20100607183730
1264 Ignore-this: fadfe87980dc1862f349bfcc21b2145f
1265] 
1266[check_memory.py: adapt to servers-of-happiness changes.
1267david-sarah@jacaranda.org**20100608013528
1268 Ignore-this: c6b28411c543d1aea2f148a955f7998
1269] 
1270[show-tool-versions.py: platform.linux_distribution() is not always available
1271david-sarah@jacaranda.org**20100608004523
1272 Ignore-this: 793fb4050086723af05d06bed8b1b92a
1273] 
1274[show-tool-versions.py: show platform.linux_distribution()
1275david-sarah@jacaranda.org**20100608003829
1276 Ignore-this: 81cb5e5fc6324044f0fc6d82903c8223
1277] 
1278[Remove the 'tahoe debug consolidate' subcommand.
1279david-sarah@jacaranda.org**20100607183757
1280 Ignore-this: 4b14daa3ae557cea07d6e119d25dafe9
1281] 
1282[tests: drastically increase timeout of this very time-consuming test in honor of François's ARM box
1283zooko@zooko.com**20100607115929
1284 Ignore-this: bf1bb52ffb6b5ccae71d4dde14621bc8
1285] 
1286[setup: update authorship, datestamp, licensing, and add special exceptions to allow combination with Eclipse- and QPL- licensed code
1287zooko@zooko.com**20100607062329
1288 Ignore-this: 5a1d7b12dfafd61283ea65a245416381
1289] 
1290[common_http.py, tahoe_cp.py: Fix an error in calling the superclass constructor in HTTPError and MissingSourceError (introduced by the Unicode fixes).
1291david-sarah@jacaranda.org**20100607174714
1292 Ignore-this: 1a118d593d81c918a4717c887f033aec
1293] 
1294[FTP-and-SFTP.txt: minor technical correction to doc for 'no-write' flag.
1295david-sarah@jacaranda.org**20100607061600
1296 Ignore-this: 66aee0c1b6c00538602d08631225e114
1297] 
1298[test_stringutils.py: trivial error in exception message for skipped test.
1299david-sarah@jacaranda.org**20100607061455
1300 Ignore-this: f261a5d4e2b8fe3bcc37e02539ba1ae2
1301] 
1302[setup: organize misc/ scripts and tools and remove obsolete ones
1303zooko@zooko.com**20100607051618
1304 Ignore-this: 161db1158c6b7be8365b0b3dee2e0b28
1305 This is for ticket #1068.
1306] 
1307[More Unicode test fixes.
1308david-sarah@jacaranda.org**20100607053358
1309 Ignore-this: 6a271fb77c31f28cb7bdba63b26a2dd2
1310] 
1311[Unicode fixes for platforms with non-native-Unicode filesystems.
1312david-sarah@jacaranda.org**20100607043238
1313 Ignore-this: 2134dc1793c4f8e50350bd749c4c98c2
1314] 
1315[Unicode fixes.
1316david-sarah@jacaranda.org**20100607010215
1317 Ignore-this: d58727b5cd2ce00e6b6dae3166030138
1318] 
1319[quickstart.html: link to snapshots page, sorted with most recent first.
1320david-sarah@jacaranda.org**20100606221127
1321 Ignore-this: 93ea7e6ee47acc66f6daac9cabffed2d
1322] 
1323[setup: loosen the Desert Island test to allow it to check the network for new packages as long as it doesn't actually download any
1324zooko@zooko.com**20100606175717
1325 Ignore-this: e438a8eb3c1b0e68080711ec6ff93ffa
1326 (You can look but don't touch.)
1327] 
1328[setup: have the buildbots print out locale.getpreferredencoding(), locale.getdefaultlocale(), locale.getlocale(), and os.path.supports_unicode_filenames
1329zooko@zooko.com**20100605162932
1330 Ignore-this: 85e31e0e0e1364e9215420e272d58116
1331 Even though that latter one is completely useless, I'm curious.
1332] 
1333[quickstart.html: We haven't released 1.7beta yet.
1334david-sarah@jacaranda.org**20100606220301
1335 Ignore-this: 4e18898cfdb08cc3ddd1ff94d43fdda7
1336] 
1337[Raise Python version requirement to 2.4.4 for non-UCS-2 builds, to avoid a critical Python security bug.
1338david-sarah@jacaranda.org**20100605031713
1339 Ignore-this: 2df2b6d620c5d8191c79eefe655059e2
1340] 
1341[unicode tests: fix missing import
1342zooko@zooko.com**20100604142630
1343 Ignore-this: db437fe8009971882aaea9de05e2bc3
1344] 
1345[unicode: make test_cli test a non-ascii argument, and make the fallback term encoding be locale.getpreferredencoding()
1346zooko@zooko.com**20100604141251
1347 Ignore-this: b2bfc07942f69141811e59891842bd8c
1348] 
1349[unicode: always decode json manifest as utf-8 then encode for stdout
1350zooko@zooko.com**20100604084840
1351 Ignore-this: ac481692315fae870a0f3562bd7db48e
1352 pyflakes pointed out that the exception handler fallback called an un-imported function, showing that the fallback wasn't being exercised.
1353 I'm not 100% sure that this patch is right and would appreciate François or someone reviewing it.
1354] 
1355[fix flakes
1356zooko@zooko.com**20100604075845
1357 Ignore-this: 3e6a84b78771b0ad519e771a13605f0
1358] 
1359[fix syntax of assertion handling that isn't portable to older versions of Python
1360zooko@zooko.com**20100604075805
1361 Ignore-this: 3a12b293aad25883fb17230266eb04ec
1362] 
1363[test_stringutils.py: Skip test test_listdir_unicode_good if filesystem supports only ASCII filenames
1364Francois Deppierraz <francois@ctrlaltdel.ch>**20100521160839
1365 Ignore-this: f2ccdbd04c8d9f42f1efb0eb80018257
1366] 
1367[test_stringutils.py: Skip test_listdir_unicode on mocked platform which cannot store non-ASCII filenames
1368Francois Deppierraz <francois@ctrlaltdel.ch>**20100521160559
1369 Ignore-this: b93fde736a8904712b506e799250a600
1370] 
1371[test_stringutils.py: Add a test class for OpenBSD 4.1 with LANG=C
1372Francois Deppierraz <francois@ctrlaltdel.ch>**20100521140053
1373 Ignore-this: 63f568aec259cef0e807752fc8150b73
1374] 
1375[test_stringutils.py: Mock the open() call in test_open_unicode
1376Francois Deppierraz <francois@ctrlaltdel.ch>**20100521135817
1377 Ignore-this: d8be4e56a6eefe7d60f97f01ea20ac67
1378 
1379 This test ensure that open(a_unicode_string) is used on Unicode platforms
1380 (Windows or MacOS X) and that open(a_correctly_encoded_bytestring) on other
1381 platforms such as Unix.
1382 
1383] 
1384[test_stringutils.py: Fix a trivial Python 2.4 syntax incompatibility
1385Francois Deppierraz <francois@ctrlaltdel.ch>**20100521093345
1386 Ignore-this: 9297e3d14a0dd37d0c1a4c6954fd59d3
1387] 
1388[test_cli.py: Fix tests when sys.stdout.encoding=None and refactor this code into functions
1389Francois Deppierraz <francois@ctrlaltdel.ch>**20100520084447
1390 Ignore-this: cf2286e225aaa4d7b1927c78c901477f
1391] 
1392[Fix handling of correctly encoded unicode filenames (#534)
1393Francois Deppierraz <francois@ctrlaltdel.ch>**20100520004356
1394 Ignore-this: 8a3a7df214a855f5a12dc0eeab6f2e39
1395 
1396 Tahoe CLI commands working on local files, for instance 'tahoe cp' or 'tahoe
1397 backup', have been improved to correctly handle filenames containing non-ASCII
1398 characters.
1399   
1400 In the case where Tahoe encounters a filename which cannot be decoded using the
1401 system encoding, an error will be returned and the operation will fail.  Under
1402 Linux, this typically happens when the filesystem contains filenames encoded
1403 with another encoding, for instance latin1, than the system locale, for
1404 instance UTF-8.  In such case, you'll need to fix your system with tools such
1405 as 'convmv' before using Tahoe CLI.
1406   
1407 All CLI commands have been improved to support non-ASCII parameters such as
1408 filenames and aliases on all supported Operating Systems except Windows as of
1409 now.
1410] 
1411[stringutils.py: Unicode helper functions + associated tests
1412Francois Deppierraz <francois@ctrlaltdel.ch>**20100520004105
1413 Ignore-this: 7a73fc31de2fd39d437d6abd278bfa9a
1414 
1415 This file contains a bunch of helper functions which converts
1416 unicode string from and to argv, filenames and stdout.
1417] 
1418[Add dependency on Michael Foord's mock library
1419Francois Deppierraz <francois@ctrlaltdel.ch>**20100519233325
1420 Ignore-this: 9bb01bf1e4780f6b98ed394c3b772a80
1421] 
1422[setup: adjust make clean target to ignore our bundled build tools
1423zooko@zooko.com**20100604051250
1424 Ignore-this: d24d2a3b849000790cfbfab69237454e
1425] 
1426[setup: bundle a copy of setuptools_trial as an unzipped egg in the base dir of the Tahoe-LAFS source tree
1427zooko@zooko.com**20100604044648
1428 Ignore-this: a4736e9812b4dab2d5a2bc4bfc5c3b28
1429 This is to work-around this Distribute issue:
1430 http://bitbucket.org/tarek/distribute/issue/55/revision-control-plugin-automatically-installed-as-a-build-dependency-is-not-present-when-another-build-dependency-is-being
1431] 
1432[setup: bundle a copy of darcsver in unzipped egg form in the root of the Tahoe-LAFS source tree
1433zooko@zooko.com**20100604044146
1434 Ignore-this: a51a52e82dd3a39225657ffa27decae2
1435 This is to work-around this Distribute issue:
1436 http://bitbucket.org/tarek/distribute/issue/55/revision-control-plugin-automatically-installed-as-a-build-dependency-is-not-present-when-another-build-dependency-is-being
1437] 
1438[setup: undo the previous patch to quote the executable in scripts
1439zooko@zooko.com**20100604025204
1440 Ignore-this: beda3b951c49d1111478618b8cabe005
1441 The problem isn't in the script, it is in the cli.exe script that is built by setuptools. This might be related to
1442 http://bugs.python.org/issue6792
1443 and
1444 http://bugs.python.org/setuptools/issue2
1445 Or it might be a separate issue involving the launcher.c code e.g. http://tahoe-lafs.org/trac/zetuptoolz/browser/launcher.c?rev=576#L210 and its handling of the interpreter name.
1446] 
1447[quickstart.html: warn against installing Python at a path containing spaces.
1448david-sarah@jacaranda.org**20100604032413
1449 Ignore-this: c7118332573abd7762d9a897e650bc6a
1450] 
1451[setup: put quotes around the path to executable in case it has spaces in it, when building a tahoe.exe for win32
1452zooko@zooko.com**20100604020836
1453 Ignore-this: 478684843169c94a9c14726fedeeed7d
1454] 
1455[misc/show-tool-versions.py: Display additional Python interpreter encoding informations (stdout, stdin and filesystem)
1456Francois Deppierraz <francois@ctrlaltdel.ch>**20100521094313
1457 Ignore-this: 3ae9b0b07fd1d53fb632ef169f7c5d26
1458] 
1459[Fix test failures in test_web caused by changes to web page titles in #1062. Also, change a 'target' field to '_blank' instead of 'blank' in welcome.xhtml.
1460david-sarah@jacaranda.org**20100603232105
1461 Ignore-this: 6e2cc63f42b07e2a3b2d1a857abc50a6
1462] 
1463[Resolve merge conflict for sftpd.py
1464david-sarah@jacaranda.org**20100603182537
1465 Ignore-this: ba8b543e51312ac949798eb8f5bd9d9c
1466] 
1467[SFTP: possible fix for metadata times being shown as the epoch.
1468david-sarah@jacaranda.org**20100602234514
1469 Ignore-this: bdd7dfccf34eff818ff88aa4f3d28790
1470] 
1471[SFTP: further improvements to test coverage.
1472david-sarah@jacaranda.org**20100602234422
1473 Ignore-this: 87eeee567e8d7562659442ea491e187c
1474] 
1475[SFTP: improve test coverage. Also make creating a directory fail when permissions are read-only (rather than ignoring the permissions).
1476david-sarah@jacaranda.org**20100602041934
1477 Ignore-this: a5e9d9081677bc7f3ddb18ca7a1f531f
1478] 
1479[dirnode.py: fix a bug in the no-write change for Adder, and improve test coverage. Add a 'metadata' argument to create_subdirectory, with documentation. Also update some comments in test_dirnode.py made stale by the ctime/mtime change.
1480david-sarah@jacaranda.org**20100602032641
1481 Ignore-this: 48817b54cd63f5422cb88214c053b03b
1482] 
1483[dirnode.py: Fix bug that caused 'tahoe' fields, 'ctime' and 'mtime' not to be updated when new metadata is present.
1484david-sarah@jacaranda.org**20100602014644
1485 Ignore-this: 5bac95aa897b68f2785d481e49b6a66
1486] 
1487[SFTP: fix a bug that caused the temporary files underlying EncryptedTemporaryFiles not to be closed.
1488david-sarah@jacaranda.org**20100601055310
1489 Ignore-this: 44fee4cfe222b2b1690f4c5e75083a52
1490] 
1491[SFTP: changes for #1063 ('no-write' field) including comment:1 (clearing owner write permission diminishes to a read cap). Includes documentation changes, but not tests for the new behaviour.
1492david-sarah@jacaranda.org**20100601051139
1493 Ignore-this: eff7c08bd47fd52bfe2b844dabf02558
1494] 
1495[dirnode.py: Fix #1034 (MetadataSetter does not enforce restriction on setting 'tahoe' subkeys), and expose the metadata updater for use by SFTP. Also, support diminishing a child cap to read-only if 'no-write' is set in the metadata.
1496david-sarah@jacaranda.org**20100601045428
1497 Ignore-this: 14f26e17e58db97fad0dcfd350b38e95
1498] 
1499[SFTP: the same bug as in _sync_heisenfiles also occurred in two other places.
1500david-sarah@jacaranda.org**20100530060127
1501 Ignore-this: 8d137658fc6e4596fa42697476c39aa3
1502] 
1503[SFTP: another try at fixing the _sync_heisenfiles bug.
1504david-sarah@jacaranda.org**20100530055254
1505 Ignore-this: c15f76f32a60083a6b7de6ca0e917934
1506] 
1507[SFTP: fix silly bug in _sync_heisenfiles ('f is not ignore' vs 'not (f is ignore)').
1508david-sarah@jacaranda.org**20100530053807
1509 Ignore-this: 71c4bc62613bf8fef835886d8eb61c27
1510] 
1511[SFTP: log when a sync completes.
1512david-sarah@jacaranda.org**20100530051840
1513 Ignore-this: d99765663ceb673c8a693dfcf88c25ea
1514] 
1515[SFTP: fix bug in previous logging patch.
1516david-sarah@jacaranda.org**20100530050000
1517 Ignore-this: 613e4c115f03fe2d04c621b510340817
1518] 
1519[SFTP: more logging to track down OpenOffice hang.
1520david-sarah@jacaranda.org**20100530040809
1521 Ignore-this: 6c11f2d1eac9f62e2d0f04f006476a03
1522] 
1523[SFTP: avoid blocking close on a heisenfile that has been abandoned or never changed. Also, improve the logging to help track down a case where OpenOffice hangs on opening a file with FXF_READ|FXF_WRITE.
1524david-sarah@jacaranda.org**20100530025544
1525 Ignore-this: 9919dddd446fff64de4031ad51490d1c
1526] 
1527[Move suppression of DeprecationWarning about BaseException.message from sftpd.py to main __init__.py. Also, remove the global suppression of the 'integer argument expected, got float' warning, which turned out to be a bug.
1528david-sarah@jacaranda.org**20100529050537
1529 Ignore-this: 87648afa0dec0d2e73614007de102a16
1530] 
1531[SFTP: cater to clients that assume a file is created as soon as they have made an open request; also, fix some race conditions associated with closing a file at about the same time as renaming or removing it.
1532david-sarah@jacaranda.org**20100529045253
1533 Ignore-this: 2404076b2154ff2659e2b10e0b9e813c
1534] 
1535[Change doc comments in interfaces.py to take into account unknown nodes.
1536david-sarah@jacaranda.org**20100528171922
1537 Ignore-this: d2fde6890b3bca9c7275775f64fbff56
1538] 
1539[Add must_exist, must_be_directory, and must_be_file arguments to DirectoryNode.delete. This will be used to fixes a minor condition in the SFTP frontend.
1540david-sarah@jacaranda.org**20100527194529
1541 Ignore-this: 6d8114cef4450c52c57639f82852716f
1542] 
1543[Trivial whitespace changes.
1544david-sarah@jacaranda.org**20100527194114
1545 Ignore-this: 98d611bc54ee20b01a5f6b334ff61b2d
1546] 
1547[SFTP: 'sync' any open files at a direntry before opening any new file at that direntry. This works around the sshfs misbehaviour of returning success to clients immediately on close.
1548david-sarah@jacaranda.org**20100525230257
1549 Ignore-this: 63245d6d864f8f591c86170864d7c57f
1550] 
1551[SFTP: handle removing a file while it is open. Also some simplifications of the logout handling.
1552david-sarah@jacaranda.org**20100525184210
1553 Ignore-this: 660ee80be6ecab783c60452a9da896de
1554] 
1555[SFTP: a posix-rename response should actually return an FXP_STATUS reply, not an FXP_EXTENDED_REPLY as Twisted Conch assumes. Work around this by raising an SFTPError with code FX_OK.
1556david-sarah@jacaranda.org**20100525033323
1557 Ignore-this: fe2914d3ef7f5194bbeaf3f2dda2ad7d
1558] 
1559[SFTP: fix problem with posix-rename code returning a Deferred for the renamed filenode, not for the result of the request (an empty string).
1560david-sarah@jacaranda.org**20100525020209
1561 Ignore-this: 69f7491df2a8f7ea92d999a6d9f0581d
1562] 
1563[SFTP: fix time handling to make sure floats are not passed into twisted.conch, and to print times in the future less ambiguously in directory listings.
1564david-sarah@jacaranda.org**20100524230412
1565 Ignore-this: eb1a3fb72492fa2fb19667b6e4300440
1566] 
1567[SFTP: name of the POSIX rename extension should be 'posix-rename@openssh.com', not 'extposix-rename@openssh.com'.
1568david-sarah@jacaranda.org**20100524021156
1569 Ignore-this: f90eb1ff9560176635386ee797a3fdc7
1570] 
1571[SFTP: avoid race condition where .write could be called on an OverwriteableFileConsumer after it had been closed.
1572david-sarah@jacaranda.org**20100523233830
1573 Ignore-this: 55d381064a15bd64381163341df4d09f
1574] 
1575[SFTP: log tracebacks for RAISEd exceptions.
1576david-sarah@jacaranda.org**20100523221535
1577 Ignore-this: c76a7852df099b358642f0631237cc89
1578] 
1579[Suppress 'integer argument expected, got float' DeprecationWarning everywhere
1580david-sarah@jacaranda.org**20100523221157
1581 Ignore-this: 80efd7e27798f5d2ad66c7a53e7048e5
1582] 
1583[SFTP: more logging to investigate behaviour of getAttrs(path).
1584david-sarah@jacaranda.org**20100523204236
1585 Ignore-this: e58fd35dc9015316e16a9f49f19bb469
1586] 
1587[SFTP: fix pyflakes warnings; drop 'noisy' versions of eventually_callback and eventually_errback; robustify conversion of exception messages to UTF-8.
1588david-sarah@jacaranda.org**20100523140905
1589 Ignore-this: 420196fc58646b05bbc9c3732b6eb314
1590] 
1591[SFTP: fixes and test cases for renaming of open files.
1592david-sarah@jacaranda.org**20100523032549
1593 Ignore-this: 32e0726be0fc89335f3035157e202c68
1594] 
1595[SFTP: Increase test_sftp timeout to cater for francois' ARM buildslave.
1596david-sarah@jacaranda.org**20100522191639
1597 Ignore-this: a5acf9660d304677048ab4dd72908ad8
1598] 
1599[SFTP: Fix error in support for getAttrs on an open file, to index open files by directory entry rather than path. Extend that support to renaming open files. Also, implement the extposix-rename@openssh.org extension, and some other minor refactoring.
1600david-sarah@jacaranda.org**20100522035836
1601 Ignore-this: 8ef93a828e927cce2c23b805250b81a4
1602] 
1603[SFTP: relax pyasn1 version dependency to >= 0.0.8a.
1604david-sarah@jacaranda.org**20100520181437
1605 Ignore-this: 2c7b3dee7b7e14ba121d3118193a386a
1606] 
1607[SFTP tests: fix test_openDirectory_and_attrs that was failing in timezones west of UTC.
1608david-sarah@jacaranda.org**20100520181027
1609 Ignore-this: 9beaf602beef437c11c7e97f54ce2599
1610] 
1611[SFTP: allow getAttrs to succeed on a file that has been opened for creation but not yet uploaded or linked (part of #1050).
1612david-sarah@jacaranda.org**20100520035613
1613 Ignore-this: 2f59107d60d5476edac19361ccf6cf94
1614] 
1615[SFTP: improve logging so that results of requests are (usually) logged.
1616david-sarah@jacaranda.org**20100520003652
1617 Ignore-this: 3f59eeee374a3eba71db9be31d5a95
1618] 
1619[SFTP: add tests for more combinations of open flags.
1620david-sarah@jacaranda.org**20100519053933
1621 Ignore-this: b97ee351b1e8ecfecabac70698060665
1622] 
1623[SFTP: allow FXF_WRITE | FXF_TRUNC (#1050).
1624david-sarah@jacaranda.org**20100519043240
1625 Ignore-this: bd70009f11d07ac6e9fd0d1e3fa87a9b
1626] 
1627[SFTP: remove another case where we were logging data.
1628david-sarah@jacaranda.org**20100519012713
1629 Ignore-this: 83115daf3a90278fed0e3fc267607584
1630] 
1631[SFTP: avoid logging all data passed to callbacks.
1632david-sarah@jacaranda.org**20100519000651
1633 Ignore-this: ade6d69a473ada50acef6389fc7fdf69
1634] 
1635[SFTP: fixes related to reporting of permissions (needed for sshfs).
1636david-sarah@jacaranda.org**20100518054521
1637 Ignore-this: c51f8a5d0dc76b80d33ffef9b0541325
1638] 
1639[SFTP: change error code returned for ExistingChildError to FX_FAILURE (fixes gvfs with some picky programs such as gedit).
1640david-sarah@jacaranda.org**20100518004205
1641 Ignore-this: c194c2c9aaf3edba7af84b7413cec375
1642] 
1643[SFTP: fixed bugs that caused hangs during write (#1037).
1644david-sarah@jacaranda.org**20100517044228
1645 Ignore-this: b8b95e82c4057367388a1e6baada993b
1646] 
1647[SFTP: work around a probable bug in twisted.conch.ssh.session:loseConnection(). Also some minor error handling cleanups.
1648david-sarah@jacaranda.org**20100517012606
1649 Ignore-this: 5d3da7c4219cb0c14547e7fd70c74204
1650] 
1651[SFTP: add pyasn1 as dependency, needed if we are using Twisted >= 9.0.0.
1652david-sarah@jacaranda.org**20100516193710
1653 Ignore-this: 76fd92e8a950bb1983a90a09e89c54d3
1654] 
1655[SFTP: Support statvfs extensions, avoid logging actual data, and decline shell sessions politely.
1656david-sarah@jacaranda.org**20100516154347
1657 Ignore-this: 9d05d23ba77693c03a61accd348ccbe5
1658] 
1659[SFTP: fix error in SFTPUserHandler arguments introduced by execCommand patch.
1660david-sarah@jacaranda.org**20100516014045
1661 Ignore-this: f5ee494dc6ad6aa536cc8144bd2e3d19
1662] 
1663[SFTP: implement execCommand to interoperate with clients that issue a 'df -P -k /' command. Also eliminate use of Zope adaptation.
1664david-sarah@jacaranda.org**20100516012754
1665 Ignore-this: 2d0ed28b759f67f83875b1eaf5778992
1666] 
1667[sftpd.py: 'log.OPERATIONAL' should be just 'OPERATIONAL'.
1668david-sarah@jacaranda.org**20100515155533
1669 Ignore-this: f2347cb3301bbccc086356f6edc685
1670] 
1671[Attempt to fix #1040 by making SFTPUser implement ISession.
1672david-sarah@jacaranda.org**20100515005719
1673 Ignore-this: b3baaf088ba567e861e61e347195dfc4
1674] 
1675[Eliminate Windows newlines from sftpd.py.
1676david-sarah@jacaranda.org**20100515005656
1677 Ignore-this: cd54fd25beb957887514ae76e08c277
1678] 
1679[Update SFTP implementation and tests: fix #1038 and switch to foolscap logging; also some code reorganization.
1680david-sarah@jacaranda.org**20100514043113
1681 Ignore-this: 262f76d953dcd4317210789f2b2bf5da
1682] 
1683[Change shouldFail to avoid Unicode errors when converting Failure to str
1684david-sarah@jacaranda.org**20100512060754
1685 Ignore-this: 86ed419d332d9c33090aae2cde1dc5df
1686] 
1687[Tests for new SFTP implementation
1688david-sarah@jacaranda.org**20100512060552
1689 Ignore-this: 20308d4a59b3ebc868aad55ae0a7a981
1690] 
1691[New SFTP implementation: mutable files, read/write support, streaming download, Unicode filenames, and more
1692david-sarah@jacaranda.org**20100512055407
1693 Ignore-this: 906f51c48d974ba9cf360c27845c55eb
1694] 
1695[allmydata.org -> tahoe-lafs.org in __init__.py
1696david-sarah@jacaranda.org**20100603063530
1697 Ignore-this: f7d82331d5b4a3c4c0938023409335af
1698] 
1699[small change to CREDITS
1700david-sarah@jacaranda.org**20100603062421
1701 Ignore-this: 2909cdbedc19da5573dec810fc23243
1702] 
1703[Resolve conflict in patch to change imports to absolute.
1704david-sarah@jacaranda.org**20100603054608
1705 Ignore-this: 15aa1caa88e688ffa6dc53bed7dcca7d
1706] 
1707[Minor documentation tweaks.
1708david-sarah@jacaranda.org**20100603054458
1709 Ignore-this: e30ae407b0039dfa5b341d8f88e7f959
1710] 
1711[title_rename_xhtml.dpatch.txt
1712freestorm77@gmail.com**20100529172542
1713 Ignore-this: d2846afcc9ea72ac443a62ecc23d121b
1714 
1715 - Renamed xhtml Title from "Allmydata - Tahoe" to "Tahoe-LAFS"
1716 - Renamed Tahoe to Tahoe-LAFS in page content
1717 - Changed Tahoe-LAFS home page link to http://tahoe-lafs.org (added target="blank")
1718 - Deleted commented css script in info.xhtml
1719 
1720 
1721] 
1722[tests: refactor test_web.py to have less duplication of literal caps-from-the-future
1723zooko@zooko.com**20100519055146
1724 Ignore-this: 49e5412e6cc4566ca67f069ffd850af6
1725 This is a prelude to a patch which will add tests of caps from the future which have non-ascii chars in them.
1726] 
1727[doc_reformat_stats.txt
1728freestorm77@gmail.com**20100424114615
1729 Ignore-this: af315db5f7e3a17219ff8fb39bcfcd60
1730 
1731 
1732    - Added heading format begining and ending by "=="
1733    - Added Index
1734    - Added Title
1735           
1736    Note: No change are made in paragraphs content
1737 
1738 
1739 **END OF DESCRIPTION***
1740 
1741 Place the long patch description above the ***END OF DESCRIPTION*** marker.
1742 The first line of this file will be the patch name.
1743 
1744 
1745 This patch contains the following changes:
1746 
1747 M ./docs/stats.txt -2 +2
1748] 
1749[doc_reformat_performance.txt
1750freestorm77@gmail.com**20100424114444
1751 Ignore-this: 55295ff5cd8a5b67034eb661a5b0699d
1752 
1753    - Added heading format begining and ending by "=="
1754    - Added Index
1755    - Added Title
1756         
1757    Note: No change are made in paragraphs content
1758 
1759 
1760] 
1761[doc_refomat_logging.txt
1762freestorm77@gmail.com**20100424114316
1763 Ignore-this: 593f0f9914516bf1924dfa6eee74e35f
1764 
1765    - Added heading format begining and ending by "=="
1766    - Added Index
1767    - Added Title
1768         
1769    Note: No change are made in paragraphs content
1770 
1771] 
1772[doc_reformat_known_issues.txt
1773freestorm77@gmail.com**20100424114118
1774 Ignore-this: 9577c3965d77b7ac18698988cfa06049
1775 
1776     - Added heading format begining and ending by "=="
1777     - Added Index
1778     - Added Title
1779           
1780     Note: No change are made in paragraphs content
1781   
1782 
1783] 
1784[doc_reformat_helper.txt
1785freestorm77@gmail.com**20100424120649
1786 Ignore-this: de2080d6152ae813b20514b9908e37fb
1787 
1788 
1789    - Added heading format begining and ending by "=="
1790    - Added Index
1791    - Added Title
1792             
1793    Note: No change are made in paragraphs content
1794 
1795] 
1796[doc_reformat_garbage-collection.txt
1797freestorm77@gmail.com**20100424120830
1798 Ignore-this: aad3e4c99670871b66467062483c977d
1799 
1800 
1801    - Added heading format begining and ending by "=="
1802    - Added Index
1803    - Added Title
1804             
1805    Note: No change are made in paragraphs content
1806 
1807] 
1808[doc_reformat_FTP-and-SFTP.txt
1809freestorm77@gmail.com**20100424121334
1810 Ignore-this: 3736b3d8f9a542a3521fbb566d44c7cf
1811 
1812 
1813    - Added heading format begining and ending by "=="
1814    - Added Index
1815    - Added Title
1816           
1817    Note: No change are made in paragraphs content
1818 
1819] 
1820[doc_reformat_debian.txt
1821freestorm77@gmail.com**20100424120537
1822 Ignore-this: 45fe4355bb869e55e683405070f47eff
1823 
1824 
1825    - Added heading format begining and ending by "=="
1826    - Added Index
1827    - Added Title
1828             
1829    Note: No change are made in paragraphs content
1830 
1831] 
1832[doc_reformat_configuration.txt
1833freestorm77@gmail.com**20100424104903
1834 Ignore-this: 4fbabc51b8122fec69ce5ad1672e79f2
1835 
1836 
1837 - Added heading format begining and ending by "=="
1838 - Added Index
1839 - Added Title
1840 
1841 Note: No change are made in paragraphs content
1842 
1843] 
1844[doc_reformat_CLI.txt
1845freestorm77@gmail.com**20100424121512
1846 Ignore-this: 2d3a59326810adcb20ea232cea405645
1847 
1848      - Added heading format begining and ending by "=="
1849      - Added Index
1850      - Added Title
1851           
1852      Note: No change are made in paragraphs content
1853 
1854] 
1855[doc_reformat_backupdb.txt
1856freestorm77@gmail.com**20100424120416
1857 Ignore-this: fed696530e9d2215b6f5058acbedc3ab
1858 
1859 
1860    - Added heading format begining and ending by "=="
1861    - Added Index
1862    - Added Title
1863             
1864    Note: No change are made in paragraphs content
1865 
1866] 
1867[doc_reformat_architecture.txt
1868freestorm77@gmail.com**20100424120133
1869 Ignore-this: 6e2cab4635080369f2b8cadf7b2f58e
1870 
1871 
1872     - Added heading format begining and ending by "=="
1873     - Added Index
1874     - Added Title
1875             
1876     Note: No change are made in paragraphs content
1877 
1878 
1879] 
1880[Correct harmless indentation errors found by pylint
1881david-sarah@jacaranda.org**20100226052151
1882 Ignore-this: 41335bce830700b18b80b6e00b45aef5
1883] 
1884[Change relative imports to absolute
1885david-sarah@jacaranda.org**20100226071433
1886 Ignore-this: 32e6ce1a86e2ffaaba1a37d9a1a5de0e
1887] 
1888[Document reason for the trialcoverage version requirement being 0.3.3.
1889david-sarah@jacaranda.org**20100525004444
1890 Ignore-this: 2f9f1df6882838b000c063068f258aec
1891] 
1892[Downgrade version requirement for trialcoverage to 0.3.3 (from 0.3.10), to avoid needing to compile coveragepy on Windows.
1893david-sarah@jacaranda.org**20100524233707
1894 Ignore-this: 9c397a374c8b8017e2244b8a686432a8
1895] 
1896[Suppress deprecation warning for twisted.web.error.NoResource when using Twisted >= 9.0.0.
1897david-sarah@jacaranda.org**20100516205625
1898 Ignore-this: 2361a3023cd3db86bde5e1af759ed01
1899] 
1900[docs: CREDITS for Jeremy Visser
1901zooko@zooko.com**20100524081829
1902 Ignore-this: d7c1465fd8d4e25b8d46d38a1793465b
1903] 
1904[test: show stdout and stderr in case of non-zero exit code from "tahoe" command
1905zooko@zooko.com**20100524073348
1906 Ignore-this: 695e81cd6683f4520229d108846cd551
1907] 
1908[setup: upgrade bundled zetuptoolz to zetuptoolz-0.6c15dev and make it unpacked and directly loaded by setup.py
1909zooko@zooko.com**20100523205228
1910 Ignore-this: 24fb32aaee3904115a93d1762f132c7
1911 Also fix the relevant "make clean" target behavior.
1912] 
1913[setup: remove bundled zipfile egg of setuptools
1914zooko@zooko.com**20100523205120
1915 Ignore-this: c68b5f2635bb93d1c1fa7b613a026f9e
1916 We're about to replace it with bundled unpacked source code of setuptools, which is much nicer for debugging and evolving under revision control.
1917] 
1918[setup: remove bundled copy of setuptools_trial-0.5.2.tar
1919zooko@zooko.com**20100522221539
1920 Ignore-this: 140f90eb8fb751a509029c4b24afe647
1921 Hopefully it will get installed automatically as needed and we won't bundle it anymore.
1922] 
1923[setup: remove bundled setuptools_darcs-1.2.8.tar
1924zooko@zooko.com**20100522015333
1925 Ignore-this: 378b1964b513ae7fe22bae2d3478285d
1926 This version of setuptools_darcs had a bug when used on Windows which has been fixed in setuptools_darcs-1.2.9. Hopefully we will not need to bundle a copy of setuptools_darcs-1.2.9 in with Tahoe-LAFS and can instead rely on it to be downloaded from PyPI or bundled in the "tahoe deps" separate tarball.
1927] 
1928[tests: fix pyflakes warnings in bench_dirnode.py
1929zooko@zooko.com**20100521202511
1930 Ignore-this: f23d55b4ed05e52865032c65a15753c4
1931] 
1932[setup: if the string '--reporter=bwverbose-coverage' appears on sys.argv then you need trialcoverage
1933zooko@zooko.com**20100521122226
1934 Ignore-this: e760c45dcfb5a43c1dc1e8a27346bdc2
1935] 
1936[tests: don't let bench_dirnode.py do stuff and have side-effects at import time (unless __name__ == '__main__')
1937zooko@zooko.com**20100521122052
1938 Ignore-this: 96144a412250d9bbb5fccbf83b8753b8
1939] 
1940[tests: increase timeout to give François's ARM buildslave a chance to complete the tests
1941zooko@zooko.com**20100520134526
1942 Ignore-this: 3dd399fdc8b91149c82b52f955b50833
1943] 
1944[run_trial.darcspath
1945freestorm77@gmail.com**20100510232829
1946 Ignore-this: 5ebb4df74e9ea8a4bdb22b65373d1ff2
1947] 
1948[docs: line-wrap README.txt
1949zooko@zooko.com**20100518174240
1950 Ignore-this: 670a02d360df7de51ebdcf4fae752577
1951] 
1952[Hush pyflakes warnings
1953Kevan Carstensen <kevan@isnotajoke.com>**20100515184344
1954 Ignore-this: fd602c3bba115057770715c36a87b400
1955] 
1956[setup: new improved misc/show-tool-versions.py
1957zooko@zooko.com**20100516050122
1958 Ignore-this: ce9b1de1b35b07d733e6cf823b66335a
1959] 
1960[Improve code coverage of the Tahoe2PeerSelector tests.
1961Kevan Carstensen <kevan@isnotajoke.com>**20100515032913
1962 Ignore-this: 793151b63ffa65fdae6915db22d9924a
1963] 
1964[Remove a comment that no longer makes sense.
1965Kevan Carstensen <kevan@isnotajoke.com>**20100514203516
1966 Ignore-this: 956983c7e7c7e4477215494dfce8f058
1967] 
1968[docs: update docs/architecture.txt to more fully and correctly explain the upload procedure
1969zooko@zooko.com**20100514043458
1970 Ignore-this: 538b6ea256a49fed837500342092efa3
1971] 
1972[Fix up the behavior of #778, per reviewers' comments
1973Kevan Carstensen <kevan@isnotajoke.com>**20100514004917
1974 Ignore-this: 9c20b60716125278b5456e8feb396bff
1975 
1976   - Make some important utility functions clearer and more thoroughly
1977     documented.
1978   - Assert in upload.servers_of_happiness that the buckets attributes
1979     of PeerTrackers passed to it are mutually disjoint.
1980   - Get rid of some silly non-Pythonisms that I didn't see when I first
1981     wrote these patches.
1982   - Make sure that should_add_server returns true when queried about a
1983     shnum that it doesn't know about yet.
1984   - Change Tahoe2PeerSelector.preexisting_shares to map a shareid to a set
1985     of peerids, alter dependencies to deal with that.
1986   - Remove upload.should_add_servers, because it is no longer necessary
1987   - Move upload.shares_of_happiness and upload.shares_by_server to a utility
1988     file.
1989   - Change some points in Tahoe2PeerSelector.
1990   - Compute servers_of_happiness using a bipartite matching algorithm that
1991     we know is optimal instead of an ad-hoc greedy algorithm that isn't.
1992   - Change servers_of_happiness to just take a sharemap as an argument,
1993     change its callers to merge existing_shares and used_peers before
1994     calling it.
1995   - Change an error message in the encoder to be more appropriate for
1996     servers of happiness.
1997   - Clarify the wording of an error message in immutable/upload.py
1998   - Refactor a happiness failure message to happinessutil.py, and make
1999     immutable/upload.py and immutable/encode.py use it.
2000   - Move the word "only" as far to the right as possible in failure
2001     messages.
2002   - Use a better definition of progress during peer selection.
2003   - Do read-only peer share detection queries in parallel, not sequentially.
2004   - Clean up logging semantics; print the query statistics whenever an
2005     upload is unsuccessful, not just in one case.
2006 
2007] 
2008[Alter the error message when an upload fails, per some comments in #778.
2009Kevan Carstensen <kevan@isnotajoke.com>**20091230210344
2010 Ignore-this: ba97422b2f9737c46abeb828727beb1
2011 
2012 When I first implemented #778, I just altered the error messages to refer to
2013 servers where they referred to shares. The resulting error messages weren't
2014 very good. These are a bit better.
2015] 
2016[Change "UploadHappinessError" to "UploadUnhappinessError"
2017Kevan Carstensen <kevan@isnotajoke.com>**20091205043037
2018 Ignore-this: 236b64ab19836854af4993bb5c1b221a
2019] 
2020[Alter the error message returned when peer selection fails
2021Kevan Carstensen <kevan@isnotajoke.com>**20091123002405
2022 Ignore-this: b2a7dc163edcab8d9613bfd6907e5166
2023 
2024 The Tahoe2PeerSelector returned either NoSharesError or NotEnoughSharesError
2025 for a variety of error conditions that weren't informatively described by them.
2026 This patch creates a new error, UploadHappinessError, replaces uses of
2027 NoSharesError and NotEnoughSharesError with it, and alters the error message
2028 raised with the errors to be more in line with the new servers_of_happiness
2029 behavior. See ticket #834 for more information.
2030] 
2031[Eliminate overcounting iof servers_of_happiness in Tahoe2PeerSelector; also reorganize some things.
2032Kevan Carstensen <kevan@isnotajoke.com>**20091118014542
2033 Ignore-this: a6cb032cbff74f4f9d4238faebd99868
2034] 
2035[Change stray "shares_of_happiness" to "servers_of_happiness"
2036Kevan Carstensen <kevan@isnotajoke.com>**20091116212459
2037 Ignore-this: 1c971ba8c3c4d2e7ba9f020577b28b73
2038] 
2039[Alter Tahoe2PeerSelector to make sure that it recognizes existing shares on readonly servers, fixing an issue in #778
2040Kevan Carstensen <kevan@isnotajoke.com>**20091116192805
2041 Ignore-this: 15289f4d709e03851ed0587b286fd955
2042] 
2043[Alter 'immutable/encode.py' and 'immutable/upload.py' to use servers_of_happiness instead of shares_of_happiness.
2044Kevan Carstensen <kevan@isnotajoke.com>**20091104111222
2045 Ignore-this: abb3283314820a8bbf9b5d0cbfbb57c8
2046] 
2047[Alter the signature of set_shareholders in IEncoder to add a 'servermap' parameter, which gives IEncoders enough information to perform a sane check for servers_of_happiness.
2048Kevan Carstensen <kevan@isnotajoke.com>**20091104033241
2049 Ignore-this: b3a6649a8ac66431beca1026a31fed94
2050] 
2051[Alter CiphertextDownloader to work with servers_of_happiness
2052Kevan Carstensen <kevan@isnotajoke.com>**20090924041932
2053 Ignore-this: e81edccf0308c2d3bedbc4cf217da197
2054] 
2055[Revisions of the #778 tests, per reviewers' comments
2056Kevan Carstensen <kevan@isnotajoke.com>**20100514012542
2057 Ignore-this: 735bbc7f663dce633caeb3b66a53cf6e
2058 
2059 - Fix comments and confusing naming.
2060 - Add tests for the new error messages suggested by David-Sarah
2061   and Zooko.
2062 - Alter existing tests for new error messages.
2063 - Make sure that the tests continue to work with the trunk.
2064 - Add a test for a mutual disjointedness assertion that I added to
2065   upload.servers_of_happiness.
2066 - Fix the comments to correctly reflect read-onlyness
2067 - Add a test for an edge case in should_add_server
2068 - Add an assertion to make sure that share redistribution works as it
2069   should
2070 - Alter tests to work with revised servers_of_happiness semantics
2071 - Remove tests for should_add_server, since that function no longer exists.
2072 - Alter tests to know about merge_peers, and to use it before calling
2073   servers_of_happiness.
2074 - Add tests for merge_peers.
2075 - Add Zooko's puzzles to the tests.
2076 - Edit encoding tests to expect the new kind of failure message.
2077 - Edit tests to expect error messages with the word "only" moved as far
2078   to the right as possible.
2079 - Extended and cleaned up some helper functions.
2080 - Changed some tests to call more appropriate helper functions.
2081 - Added a test for the failing redistribution algorithm
2082 - Added a test for the progress message
2083 - Added a test for the upper bound on readonly peer share discovery.
2084 
2085] 
2086[Alter various unit tests to work with the new happy behavior
2087Kevan Carstensen <kevan@isnotajoke.com>**20100107181325
2088 Ignore-this: 132032bbf865e63a079f869b663be34a
2089] 
2090[Replace "UploadHappinessError" with "UploadUnhappinessError" in tests.
2091Kevan Carstensen <kevan@isnotajoke.com>**20091205043453
2092 Ignore-this: 83f4bc50c697d21b5f4e2a4cd91862ca
2093] 
2094[Add tests for the behavior described in #834.
2095Kevan Carstensen <kevan@isnotajoke.com>**20091123012008
2096 Ignore-this: d8e0aa0f3f7965ce9b5cea843c6d6f9f
2097] 
2098[Re-work 'test_upload.py' to be more readable; add more tests for #778
2099Kevan Carstensen <kevan@isnotajoke.com>**20091116192334
2100 Ignore-this: 7e8565f92fe51dece5ae28daf442d659
2101] 
2102[Test Tahoe2PeerSelector to make sure that it recognizeses existing shares on readonly servers
2103Kevan Carstensen <kevan@isnotajoke.com>**20091109003735
2104 Ignore-this: 12f9b4cff5752fca7ed32a6ebcff6446
2105] 
2106[Add more tests for comment:53 in ticket #778
2107Kevan Carstensen <kevan@isnotajoke.com>**20091104112849
2108 Ignore-this: 3bb2edd299a944cc9586e14d5d83ec8c
2109] 
2110[Add a test for upload.shares_by_server
2111Kevan Carstensen <kevan@isnotajoke.com>**20091104111324
2112 Ignore-this: f9802e82d6982a93e00f92e0b276f018
2113] 
2114[Minor tweak to an existing test -- make the first server read-write, instead of read-only
2115Kevan Carstensen <kevan@isnotajoke.com>**20091104034232
2116 Ignore-this: a951a46c93f7f58dd44d93d8623b2aee
2117] 
2118[Alter tests to use the new form of set_shareholders
2119Kevan Carstensen <kevan@isnotajoke.com>**20091104033602
2120 Ignore-this: 3deac11fc831618d11441317463ef830
2121] 
2122[Refactor some behavior into a mixin, and add tests for the behavior described in #778
2123"Kevan Carstensen" <kevan@isnotajoke.com>**20091030091908
2124 Ignore-this: a6f9797057ca135579b249af3b2b66ac
2125] 
2126[Alter NoNetworkGrid to allow the creation of readonly servers for testing purposes.
2127Kevan Carstensen <kevan@isnotajoke.com>**20091018013013
2128 Ignore-this: e12cd7c4ddeb65305c5a7e08df57c754
2129] 
2130[Update 'docs/architecture.txt' to reflect readonly share discovery
2131kevan@isnotajoke.com**20100514003852
2132 Ignore-this: 7ead71b34df3b1ecfdcfd3cb2882e4f9
2133] 
2134[Alter the wording in docs/architecture.txt to more accurately describe the servers_of_happiness behavior.
2135Kevan Carstensen <kevan@isnotajoke.com>**20100428002455
2136 Ignore-this: 6eff7fa756858a1c6f73728d989544cc
2137] 
2138[Alter wording in 'interfaces.py' to be correct wrt #778
2139"Kevan Carstensen" <kevan@isnotajoke.com>**20091205034005
2140 Ignore-this: c9913c700ac14e7a63569458b06980e0
2141] 
2142[Update 'docs/configuration.txt' to reflect the servers_of_happiness behavior.
2143Kevan Carstensen <kevan@isnotajoke.com>**20091205033813
2144 Ignore-this: 5e1cb171f8239bfb5b565d73c75ac2b8
2145] 
2146[Clarify quickstart instructions for installing pywin32
2147david-sarah@jacaranda.org**20100511180300
2148 Ignore-this: d4668359673600d2acbc7cd8dd44b93c
2149] 
2150[web: add a simple test that you can load directory.xhtml
2151zooko@zooko.com**20100510063729
2152 Ignore-this: e49b25fa3c67b3c7a56c8b1ae01bb463
2153] 
2154[setup: fix typos in misc/show-tool-versions.py
2155zooko@zooko.com**20100510063615
2156 Ignore-this: 2181b1303a0e288e7a9ebd4c4855628
2157] 
2158[setup: show code-coverage tool versions in show-tools-versions.py
2159zooko@zooko.com**20100510062955
2160 Ignore-this: 4b4c68eb3780b762c8dbbd22b39df7cf
2161] 
2162[docs: update README, mv it to README.txt, update setup.py
2163zooko@zooko.com**20100504094340
2164 Ignore-this: 40e28ca36c299ea1fd12d3b91e5b421c
2165] 
2166[Dependency on Windmill test framework is not needed yet.
2167david-sarah@jacaranda.org**20100504161043
2168 Ignore-this: be088712bec650d4ef24766c0026ebc8
2169] 
2170[tests: pass z to tar so that BSD tar will know to ungzip
2171zooko@zooko.com**20100504090628
2172 Ignore-this: 1339e493f255e8fc0b01b70478f23a09
2173] 
2174[setup: update comments and URLs in setup.cfg
2175zooko@zooko.com**20100504061653
2176 Ignore-this: f97692807c74bcab56d33100c899f829
2177] 
2178[setup: reorder and extend the show-tool-versions script, the better to glean information about our new buildslaves
2179zooko@zooko.com**20100504045643
2180 Ignore-this: 836084b56b8d4ee8f1de1f4efb706d36
2181] 
2182[CLI: Support for https url in option --node-url
2183Francois Deppierraz <francois@ctrlaltdel.ch>**20100430185609
2184 Ignore-this: 1717176b4d27c877e6bc67a944d9bf34
2185 
2186 This patch modifies the regular expression used for verifying of '--node-url'
2187 parameter.  Support for accessing a Tahoe gateway over HTTPS was already
2188 present, thanks to Python's urllib.
2189 
2190] 
2191[backupdb.did_create_directory: use REPLACE INTO, not INSERT INTO + ignore error
2192Brian Warner <warner@lothar.com>**20100428050803
2193 Ignore-this: 1fca7b8f364a21ae413be8767161e32f
2194 
2195 This handles the case where we upload a new tahoe directory for a
2196 previously-processed local directory, possibly creating a new dircap (if the
2197 metadata had changed). Now we replace the old dirhash->dircap record. The
2198 previous behavior left the old record in place (with the old dircap and
2199 timestamps), so we'd never stop creating new directories and never converge
2200 on a null backup.
2201] 
2202["tahoe webopen": add --info flag, to get ?t=info
2203Brian Warner <warner@lothar.com>**20100424233003
2204 Ignore-this: 126b0bb6db340fabacb623d295eb45fa
2205 
2206 Also fix some trailing whitespace.
2207] 
2208[docs: install.html http-equiv refresh to quickstart.html
2209zooko@zooko.com**20100421165708
2210 Ignore-this: 52b4b619f9dde5886ae2cd7f1f3b734b
2211] 
2212[docs: install.html -> quickstart.html
2213zooko@zooko.com**20100421155757
2214 Ignore-this: 6084e203909306bed93efb09d0e6181d
2215 It is not called "installing" because that implies that it is going to change the configuration of your operating system. It is not called "building" because that implies that you need developer tools like a compiler. Also I added a stern warning against looking at the "InstallDetails" wiki page, which I have renamed to "AdvancedInstall".
2216] 
2217[Fix another typo in tahoe_storagespace munin plugin
2218david-sarah@jacaranda.org**20100416220935
2219 Ignore-this: ad1f7aa66b554174f91dfb2b7a3ea5f3
2220] 
2221[Add dependency on windmill >= 1.3
2222david-sarah@jacaranda.org**20100416190404
2223 Ignore-this: 4437a7a464e92d6c9012926b18676211
2224] 
2225[licensing: phrase the OpenSSL-exemption in the vocabulary of copyright instead of computer technology, and replicate the exemption from the GPL to the TGPPL
2226zooko@zooko.com**20100414232521
2227 Ignore-this: a5494b2f582a295544c6cad3f245e91
2228] 
2229[munin-tahoe_storagespace
2230freestorm77@gmail.com**20100221203626
2231 Ignore-this: 14d6d6a587afe1f8883152bf2e46b4aa
2232 
2233 Plugin configuration rename
2234 
2235] 
2236[setup: add licensing declaration for setuptools (noticed by the FSF compliance folks)
2237zooko@zooko.com**20100309184415
2238 Ignore-this: 2dfa7d812d65fec7c72ddbf0de609ccb
2239] 
2240[setup: fix error in licensing declaration from Shawn Willden, as noted by the FSF compliance division
2241zooko@zooko.com**20100309163736
2242 Ignore-this: c0623d27e469799d86cabf67921a13f8
2243] 
2244[CREDITS to Jacob Appelbaum
2245zooko@zooko.com**20100304015616
2246 Ignore-this: 70db493abbc23968fcc8db93f386ea54
2247] 
2248[desert-island-build-with-proper-versions
2249jacob@appelbaum.net**20100304013858] 
2250[docs: a few small edits to try to guide newcomers through the docs
2251zooko@zooko.com**20100303231902
2252 Ignore-this: a6aab44f5bf5ad97ea73e6976bc4042d
2253 These edits were suggested by my watching over Jake Appelbaum's shoulder as he completely ignored/skipped/missed install.html and also as he decided that debian.txt wouldn't help him with basic installation. Then I threw in a few docs edits that have been sitting around in my sandbox asking to be committed for months.
2254] 
2255[TAG allmydata-tahoe-1.6.1
2256david-sarah@jacaranda.org**20100228062314
2257 Ignore-this: eb5f03ada8ea953ee7780e7fe068539
2258] 
2259Patch bundle hash:
2260e0f77c2b62b77e8709eaedbbbe06987c9fccaadc