Merge lp:~gz/bzr/unversioned_paths_unprintable_898408 into lp:bzr

Proposed by Martin Packman
Status: Merged
Approved by: Vincent Ladeuil
Approved revision: no longer in the source branch.
Merged at revision: 6346
Proposed branch: lp:~gz/bzr/unversioned_paths_unprintable_898408
Merge into: lp:bzr
Diff against target: 147 lines (+57/-5)
5 files modified
bzrlib/tests/blackbox/test_commit.py (+20/-0)
bzrlib/tests/per_workingtree/test_paths2ids.py (+11/-0)
bzrlib/tests/test_workingtree_4.py (+16/-1)
bzrlib/workingtree_4.py (+6/-4)
doc/en/release-notes/bzr-2.5.txt (+4/-0)
To merge this branch: bzr merge lp:~gz/bzr/unversioned_paths_unprintable_898408
Reviewer Review Type Date Requested Status
Vincent Ladeuil Approve
Review via email: mp+84514@code.launchpad.net

Commit message

Ensure PathsNotVersionedError is only ever given unicode paths

Description of the change

Make notifying the user that they're trying to operate on unversioned files work without 'Unprintable exception' on utf-8 terminals again.

The problem stems from the fact bzrlib.workingtree_4 in a couple of methods encodes the paths it's passed to utf-8 in place as a hack to align with dirstate internals. All paths that bubble back up to the ui level need to be unicode,

There are two reasonable places to re-decode the paths, either in the methods that raise PathsNotVersionedError, or in the exception constructor. As bzrlib.tree has a method that raises the exception without squelching the paths first, doing it in the dirstate-interfacing workingtree_4 methods seems most reasonable.

Reporting on non-utf-8 terminals still mangles paths, but that's a long standing issue and this fix moves in the correct direction on that front as well.

To post a comment you must log in.
Revision history for this message
Vincent Ladeuil (vila) wrote :

Very good explanation in the cover letter really made reviewing this proposal easy. Thanks.

I agree with your analysis and it seems better indeed to leave the exception always deal with unicode paths.

review: Approve
Revision history for this message
Martin Packman (gz) wrote :

sent to pqm by email

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bzrlib/tests/blackbox/test_commit.py'
2--- bzrlib/tests/blackbox/test_commit.py 2011-11-29 17:23:13 +0000
3+++ bzrlib/tests/blackbox/test_commit.py 2011-12-06 11:20:29 +0000
4@@ -167,6 +167,26 @@
5 finally:
6 osutils.get_terminal_encoding = default_get_terminal_enc
7
8+ def test_non_ascii_file_unversioned_utf8(self):
9+ self.requireFeature(features.UnicodeFilenameFeature)
10+ tree = self.make_branch_and_tree(".")
11+ self.build_tree(["f"])
12+ tree.add(["f"])
13+ out, err = self.run_bzr(["commit", "-m", "Wrong filename", u"\xa7"],
14+ encoding="utf-8", retcode=3)
15+ self.assertContainsRe(err, "(?m)not versioned: \"\xc2\xa7\"$")
16+
17+ def test_non_ascii_file_unversioned_iso_8859_5(self):
18+ self.requireFeature(features.UnicodeFilenameFeature)
19+ tree = self.make_branch_and_tree(".")
20+ self.build_tree(["f"])
21+ tree.add(["f"])
22+ out, err = self.run_bzr(["commit", "-m", "Wrong filename", u"\xa7"],
23+ encoding="iso-8859-5", retcode=3)
24+ self.expectFailure("Error messages are always written as UTF-8",
25+ self.assertNotContainsString, err, "\xc2\xa7")
26+ self.assertContainsRe(err, "(?m)not versioned: \"\xfd\"$")
27+
28 def test_warn_about_forgotten_commit_message(self):
29 """Test that the lack of -m parameter is caught"""
30 wt = self.make_branch_and_tree('.')
31
32=== modified file 'bzrlib/tests/per_workingtree/test_paths2ids.py'
33--- bzrlib/tests/per_workingtree/test_paths2ids.py 2009-07-10 07:14:02 +0000
34+++ bzrlib/tests/per_workingtree/test_paths2ids.py 2011-12-06 11:20:29 +0000
35@@ -24,6 +24,7 @@
36 from operator import attrgetter
37
38 from bzrlib import errors
39+from bzrlib.tests import features
40 from bzrlib.tests.per_workingtree import TestCaseWithWorkingTree
41
42
43@@ -188,3 +189,13 @@
44 ['unversioned'], [tree])
45 basis.unlock()
46 tree.unlock()
47+
48+ def test_unversioned_non_ascii_one_tree(self):
49+ self.requireFeature(features.UnicodeFilenameFeature)
50+ tree = self.make_branch_and_tree('.')
51+ self.build_tree([u"\xa7"])
52+ self.assertExpectedIds([], tree, [u"\xa7"], require_versioned=False)
53+ self.addCleanup(tree.lock_read().unlock)
54+ e = self.assertRaises(errors.PathsNotVersionedError,
55+ tree.paths2ids, [u"\xa7"])
56+ self.assertEqual([u"\xa7"], e.paths)
57
58=== modified file 'bzrlib/tests/test_workingtree_4.py'
59--- bzrlib/tests/test_workingtree_4.py 2011-05-26 21:02:47 +0000
60+++ bzrlib/tests/test_workingtree_4.py 2011-12-06 11:20:29 +0000
61@@ -29,7 +29,7 @@
62 workingtree_4,
63 )
64 from bzrlib.lockdir import LockDir
65-from bzrlib.tests import TestCaseWithTransport, TestSkipped
66+from bzrlib.tests import TestCaseWithTransport, TestSkipped, features
67 from bzrlib.tree import InterTree
68
69
70@@ -673,6 +673,21 @@
71 tree_iter_changes, ['bar', 'foo'])
72 self.assertEqual(e.paths, ['foo'])
73
74+ def test_iter_changes_unversioned_non_ascii(self):
75+ """Unversioned non-ascii paths should be reported as unicode"""
76+ self.requireFeature(features.UnicodeFilenameFeature)
77+ tree = self.make_branch_and_tree('.')
78+ self.build_tree_contents([('f', '')])
79+ tree.add(['f'], ['f-id'])
80+ def tree_iter_changes(tree, files):
81+ return list(tree.iter_changes(tree.basis_tree(),
82+ specific_files=files, require_versioned=True))
83+ tree.lock_read()
84+ self.addCleanup(tree.unlock)
85+ e = self.assertRaises(errors.PathsNotVersionedError,
86+ tree_iter_changes, tree, [u'\xa7', u'\u03c0'])
87+ self.assertEqual(e.paths, [u'\xa7', u'\u03c0'])
88+
89 def get_tree_with_cachable_file_foo(self):
90 tree = self.make_branch_and_tree('.')
91 tree.lock_write()
92
93=== modified file 'bzrlib/workingtree_4.py'
94--- bzrlib/workingtree_4.py 2011-10-11 12:01:51 +0000
95+++ bzrlib/workingtree_4.py 2011-12-06 11:20:29 +0000
96@@ -969,7 +969,8 @@
97 all_versioned = False
98 break
99 if not all_versioned:
100- raise errors.PathsNotVersionedError(paths)
101+ raise errors.PathsNotVersionedError(
102+ [p.decode('utf-8') for p in paths])
103 # -- remove redundancy in supplied paths to prevent over-scanning --
104 search_paths = osutils.minimum_path_selection(paths)
105 # sketch:
106@@ -1024,7 +1025,8 @@
107 found_dir_names = set(dir_name_id[:2] for dir_name_id in found)
108 for dir_name in split_paths:
109 if dir_name not in found_dir_names:
110- raise errors.PathsNotVersionedError(paths)
111+ raise errors.PathsNotVersionedError(
112+ [p.decode('utf-8') for p in paths])
113
114 for dir_name_id, trees_info in found.iteritems():
115 for index in search_indexes:
116@@ -2174,7 +2176,7 @@
117 path_entries = state._entries_for_path(path)
118 if not path_entries:
119 # this specified path is not present at all: error
120- not_versioned.append(path)
121+ not_versioned.append(path.decode('utf-8'))
122 continue
123 found_versioned = False
124 # for each id at this path
125@@ -2188,7 +2190,7 @@
126 if not found_versioned:
127 # none of the indexes was not 'absent' at all ids for this
128 # path.
129- not_versioned.append(path)
130+ not_versioned.append(path.decode('utf-8'))
131 if len(not_versioned) > 0:
132 raise errors.PathsNotVersionedError(not_versioned)
133 # -- remove redundancy in supplied specific_files to prevent over-scanning --
134
135=== modified file 'doc/en/release-notes/bzr-2.5.txt'
136--- doc/en/release-notes/bzr-2.5.txt 2011-12-03 03:30:10 +0000
137+++ doc/en/release-notes/bzr-2.5.txt 2011-12-06 11:20:29 +0000
138@@ -96,6 +96,10 @@
139 for _CompatabilityThunkFeature based test features.
140 (Vincent Ladeuil, #897718)
141
142+* Make reporting of mistakes involving unversioned files with non-ascii
143+ filenames work again without 'Unprintable exception' being shown.
144+ (Martin Packman, #898408)
145+
146 * Provide names for lazily registered hooks.
147 (Neil Martinsen-Burrell, #894609)
148