Merge lp:~vila/bzr/856261-unshelve-line-based into lp:bzr
- 856261-unshelve-line-based
- Merge into bzr.dev
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Vincent Ladeuil | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | 6175 | ||||
Proposed branch: | lp:~vila/bzr/856261-unshelve-line-based | ||||
Merge into: | lp:bzr | ||||
Diff against target: |
182 lines (+97/-7) 4 files modified
bzrlib/shelf_ui.py (+26/-4) bzrlib/tests/script.py (+8/-0) bzrlib/tests/test_script.py (+56/-3) doc/en/release-notes/bzr-2.5.txt (+7/-0) |
||||
To merge this branch: | bzr merge lp:~vila/bzr/856261-unshelve-line-based | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Martin Packman (community) | Needs Fixing | ||
Vincent Ladeuil | Needs Information | ||
Review via email: mp+77195@code.launchpad.net |
Commit message
Allows bzr shelve to be used in test scripts and under emacs shells
Description of the change
I've been deprived from using shelve under emacs for years.
But when I ran into bug #856261 trying to reproduce bug #850594, I thought
it may be worth to have another go at it.
The fix is simple: since osutils.getchar() requires a true term (which
neither emacs not the test scripts provide), I handle the input as a
line-based one which is exactly what emacs does and also exactly what the
test script interface provides :)
I'd like feedback on how we want to configure this feature, knowing that an
environment variable may be more appropriate here (not everybody use
terminals under emacs only and the test script need a way to force the
behavior). Or should I just consider that the fix for #491196 is around the
corner and name the config variable... shelve.
shelve.line_ui ? both boolean defaulting to False or shelve.input
(defaulting to 'char' accepting 'line' ?)
Benoit Pierre (benoit.pierre) wrote : | # |
1 | # Bazaar merge directive format 2 (Bazaar 0.90) | |||
2 | # revision_id: benoit.pierre@gmail.com-20110927192241-jfqqjzm2e6ojjfq0 | |||
3 | # target_branch: lp:bzr | |||
4 | # testament_sha1: b3590b8caa798f8691f0e18ee303b9dbd983fd09 | |||
5 | # timestamp: 2011-09-27 21:23:43 +0200 | |||
6 | # base_revision_id: pqm@pqm.ubuntu.com-20110610161957-hh5ni839m7r3wsan | |||
7 | # | |||
8 | # Begin patch | |||
9 | === modified file 'bzrlib/shelf_ui.py' | |||
10 | --- bzrlib/shelf_ui.py 2010-11-04 17:48:47 +0000 | |||
11 | +++ bzrlib/shelf_ui.py 2011-09-27 19:22:41 +0000 | |||
12 | @@ -251,23 +251,8 @@ | |||
13 | 251 | diff_file.seek(0) | 251 | diff_file.seek(0) |
14 | 252 | return patches.parse_patch(diff_file) | 252 | return patches.parse_patch(diff_file) |
15 | 253 | 253 | ||
33 | 254 | def prompt(self, message): | 254 | def prompt(self, message, choices, default): |
34 | 255 | """Prompt the user for a character. | 255 | return ui.ui_factory.confirm(message, choices, default=default) |
18 | 256 | |||
19 | 257 | :param message: The message to prompt a user with. | ||
20 | 258 | :return: A character. | ||
21 | 259 | """ | ||
22 | 260 | if not sys.stdin.isatty(): | ||
23 | 261 | # Since there is no controlling terminal we will hang when trying | ||
24 | 262 | # to prompt the user, better abort now. See | ||
25 | 263 | # https://code.launchpad.net/~bialix/bzr/shelve-no-tty/+merge/14905 | ||
26 | 264 | # for more context. | ||
27 | 265 | raise errors.BzrError("You need a controlling terminal.") | ||
28 | 266 | sys.stdout.write(message) | ||
29 | 267 | char = osutils.getchar() | ||
30 | 268 | sys.stdout.write("\r" + ' ' * len(message) + '\r') | ||
31 | 269 | sys.stdout.flush() | ||
32 | 270 | return char | ||
35 | 271 | 256 | ||
36 | 272 | def prompt_bool(self, question, long=False, allow_editor=False): | 257 | def prompt_bool(self, question, long=False, allow_editor=False): |
37 | 273 | """Prompt the user with a yes/no question. | 258 | """Prompt the user with a yes/no question. |
38 | @@ -279,16 +264,11 @@ | |||
39 | 279 | """ | 264 | """ |
40 | 280 | if self.auto: | 265 | if self.auto: |
41 | 281 | return True | 266 | return True |
52 | 282 | editor_string = '' | 267 | choices = '&yes\n&No' |
53 | 283 | if long: | 268 | if allow_editor: |
54 | 284 | if allow_editor: | 269 | choices += '\n&edit manually' |
55 | 285 | editor_string = '(E)dit manually, ' | 270 | choices += '\n&finish\n&quit' |
56 | 286 | prompt = ' [(y)es, (N)o, %s(f)inish, or (q)uit]' % editor_string | 271 | char = self.prompt(question, choices, 'n') |
47 | 287 | else: | ||
48 | 288 | if allow_editor: | ||
49 | 289 | editor_string = 'e' | ||
50 | 290 | prompt = ' [yN%sfq?]' % editor_string | ||
51 | 291 | char = self.prompt(question + prompt) | ||
57 | 292 | if char == 'y': | 272 | if char == 'y': |
58 | 293 | return True | 273 | return True |
59 | 294 | elif char == 'e' and allow_editor: | 274 | elif char == 'e' and allow_editor: |
60 | 295 | 275 | ||
61 | === modified file 'bzrlib/tests/blackbox/test_break_lock.py' | |||
62 | --- bzrlib/tests/blackbox/test_break_lock.py 2011-03-10 13:29:54 +0000 | |||
63 | +++ bzrlib/tests/blackbox/test_break_lock.py 2011-09-27 19:22:41 +0000 | |||
64 | @@ -91,7 +91,7 @@ | |||
65 | 91 | self.master_branch.lock_write() | 91 | self.master_branch.lock_write() |
66 | 92 | # run the break-lock | 92 | # run the break-lock |
67 | 93 | # we need 5 yes's - wt, branch, repo, bound branch, bound repo. | 93 | # we need 5 yes's - wt, branch, repo, bound branch, bound repo. |
69 | 94 | self.run_bzr('break-lock checkout', stdin="y\ny\ny\ny\n") | 94 | self.run_bzr('break-lock checkout', stdin="yyyy") |
70 | 95 | # a new tree instance should be lockable | 95 | # a new tree instance should be lockable |
71 | 96 | br = branch.Branch.open('checkout') | 96 | br = branch.Branch.open('checkout') |
72 | 97 | br.lock_write() | 97 | br.lock_write() |
73 | 98 | 98 | ||
74 | === modified file 'bzrlib/tests/blackbox/test_uncommit.py' | |||
75 | --- bzrlib/tests/blackbox/test_uncommit.py 2010-09-15 09:35:42 +0000 | |||
76 | +++ bzrlib/tests/blackbox/test_uncommit.py 2011-09-27 19:22:41 +0000 | |||
77 | @@ -72,7 +72,7 @@ | |||
78 | 72 | $ bzr uncommit | 72 | $ bzr uncommit |
79 | 73 | ... | 73 | ... |
80 | 74 | The above revision(s) will be removed. | 74 | The above revision(s) will be removed. |
82 | 75 | 2>Uncommit these revisions? [y/n]: | 75 | 2>Uncommit these revisions? ([y]es, [n]o): n |
83 | 76 | <n | 76 | <n |
84 | 77 | Canceled | 77 | Canceled |
85 | 78 | """) | 78 | """) |
86 | 79 | 79 | ||
87 | === modified file 'bzrlib/tests/test_script.py' | |||
88 | --- bzrlib/tests/test_script.py 2011-05-16 13:39:39 +0000 | |||
89 | +++ bzrlib/tests/test_script.py 2011-09-27 19:22:41 +0000 | |||
90 | @@ -580,12 +580,12 @@ | |||
91 | 580 | self.addCleanup(commands.builtin_command_registry.remove, 'test-confirm') | 580 | self.addCleanup(commands.builtin_command_registry.remove, 'test-confirm') |
92 | 581 | self.run_script(""" | 581 | self.run_script(""" |
93 | 582 | $ bzr test-confirm | 582 | $ bzr test-confirm |
96 | 583 | 2>Really do it? [y/n]: | 583 | 2>Really do it? ([y]es, [n]o): y |
97 | 584 | <yes | 584 | <y |
98 | 585 | Do it! | 585 | Do it! |
99 | 586 | $ bzr test-confirm | 586 | $ bzr test-confirm |
102 | 587 | 2>Really do it? [y/n]: | 587 | 2>Really do it? ([y]es, [n]o): n |
103 | 588 | <no | 588 | <n |
104 | 589 | ok, no | 589 | ok, no |
105 | 590 | """) | 590 | """) |
106 | 591 | 591 | ||
107 | 592 | 592 | ||
108 | === modified file 'bzrlib/tests/test_shelf_ui.py' | |||
109 | --- bzrlib/tests/test_shelf_ui.py 2011-06-09 14:40:22 +0000 | |||
110 | +++ bzrlib/tests/test_shelf_ui.py 2011-09-27 19:22:41 +0000 | |||
111 | @@ -41,16 +41,18 @@ | |||
112 | 41 | self.expected = [] | 41 | self.expected = [] |
113 | 42 | self.diff_writer = StringIO() | 42 | self.diff_writer = StringIO() |
114 | 43 | 43 | ||
117 | 44 | def expect(self, prompt, response): | 44 | def expect(self, message, choices, response): |
118 | 45 | self.expected.append((prompt, response)) | 45 | self.expected.append((message, choices, response)) |
119 | 46 | 46 | ||
121 | 47 | def prompt(self, message): | 47 | def prompt(self, message, choices, default): |
122 | 48 | try: | 48 | try: |
124 | 49 | prompt, response = self.expected.pop(0) | 49 | expected_message, expected_choices, response = self.expected.pop(0) |
125 | 50 | except IndexError: | 50 | except IndexError: |
126 | 51 | raise AssertionError('Unexpected prompt: %s' % message) | 51 | raise AssertionError('Unexpected prompt: %s' % message) |
128 | 52 | if prompt != message: | 52 | if message != expected_message: |
129 | 53 | raise AssertionError('Wrong prompt: %s' % message) | 53 | raise AssertionError('Wrong prompt: %s' % message) |
130 | 54 | if choices != expected_choices: | ||
131 | 55 | raise AssertionError('Wrong choices: %s' % choices) | ||
132 | 54 | return response | 56 | return response |
133 | 55 | 57 | ||
134 | 56 | 58 | ||
135 | @@ -83,7 +85,7 @@ | |||
136 | 83 | shelver = ExpectShelver(tree, tree.basis_tree()) | 85 | shelver = ExpectShelver(tree, tree.basis_tree()) |
137 | 84 | self.addCleanup(shelver.finalize) | 86 | self.addCleanup(shelver.finalize) |
138 | 85 | e = self.assertRaises(AssertionError, shelver.run) | 87 | e = self.assertRaises(AssertionError, shelver.run) |
140 | 86 | self.assertEqual('Unexpected prompt: Shelve? [yNfq?]', str(e)) | 88 | self.assertEqual('Unexpected prompt: Shelve?', str(e)) |
141 | 87 | 89 | ||
142 | 88 | def test_wrong_prompt_failure(self): | 90 | def test_wrong_prompt_failure(self): |
143 | 89 | tree = self.create_shelvable_tree() | 91 | tree = self.create_shelvable_tree() |
144 | @@ -91,9 +93,9 @@ | |||
145 | 91 | self.addCleanup(tree.unlock) | 93 | self.addCleanup(tree.unlock) |
146 | 92 | shelver = ExpectShelver(tree, tree.basis_tree()) | 94 | shelver = ExpectShelver(tree, tree.basis_tree()) |
147 | 93 | self.addCleanup(shelver.finalize) | 95 | self.addCleanup(shelver.finalize) |
149 | 94 | shelver.expect('foo', 'y') | 96 | shelver.expect('foo', 'bar', 'y') |
150 | 95 | e = self.assertRaises(AssertionError, shelver.run) | 97 | e = self.assertRaises(AssertionError, shelver.run) |
152 | 96 | self.assertEqual('Wrong prompt: Shelve? [yNfq?]', str(e)) | 98 | self.assertEqual('Wrong prompt: Shelve?', str(e)) |
153 | 97 | 99 | ||
154 | 98 | def test_shelve_not_diff(self): | 100 | def test_shelve_not_diff(self): |
155 | 99 | tree = self.create_shelvable_tree() | 101 | tree = self.create_shelvable_tree() |
156 | @@ -101,8 +103,8 @@ | |||
157 | 101 | self.addCleanup(tree.unlock) | 103 | self.addCleanup(tree.unlock) |
158 | 102 | shelver = ExpectShelver(tree, tree.basis_tree()) | 104 | shelver = ExpectShelver(tree, tree.basis_tree()) |
159 | 103 | self.addCleanup(shelver.finalize) | 105 | self.addCleanup(shelver.finalize) |
162 | 104 | shelver.expect('Shelve? [yNfq?]', 'n') | 106 | shelver.expect('Shelve?', '&yes\n&No\n&finish\n&quit', 'n') |
163 | 105 | shelver.expect('Shelve? [yNfq?]', 'n') | 107 | shelver.expect('Shelve?', '&yes\n&No\n&finish\n&quit', 'n') |
164 | 106 | # No final shelving prompt because no changes were selected | 108 | # No final shelving prompt because no changes were selected |
165 | 107 | shelver.run() | 109 | shelver.run() |
166 | 108 | self.assertFileEqual(LINES_ZY, 'tree/foo') | 110 | self.assertFileEqual(LINES_ZY, 'tree/foo') |
167 | @@ -113,9 +115,9 @@ | |||
168 | 113 | self.addCleanup(tree.unlock) | 115 | self.addCleanup(tree.unlock) |
169 | 114 | shelver = ExpectShelver(tree, tree.basis_tree()) | 116 | shelver = ExpectShelver(tree, tree.basis_tree()) |
170 | 115 | self.addCleanup(shelver.finalize) | 117 | self.addCleanup(shelver.finalize) |
174 | 116 | shelver.expect('Shelve? [yNfq?]', 'y') | 118 | shelver.expect('Shelve?', '&yes\n&No\n&finish\n&quit', 'y') |
175 | 117 | shelver.expect('Shelve? [yNfq?]', 'y') | 119 | shelver.expect('Shelve?', '&yes\n&No\n&finish\n&quit', 'y') |
176 | 118 | shelver.expect('Shelve 2 change(s)? [yNfq?]', 'n') | 120 | shelver.expect('Shelve 2 change(s)?', '&yes\n&No\n&finish\n&quit', 'n') |
177 | 119 | shelver.run() | 121 | shelver.run() |
178 | 120 | self.assertFileEqual(LINES_ZY, 'tree/foo') | 122 | self.assertFileEqual(LINES_ZY, 'tree/foo') |
179 | 121 | 123 | ||
180 | @@ -125,9 +127,9 @@ | |||
181 | 125 | self.addCleanup(tree.unlock) | 127 | self.addCleanup(tree.unlock) |
182 | 126 | shelver = ExpectShelver(tree, tree.basis_tree()) | 128 | shelver = ExpectShelver(tree, tree.basis_tree()) |
183 | 127 | self.addCleanup(shelver.finalize) | 129 | self.addCleanup(shelver.finalize) |
187 | 128 | shelver.expect('Shelve? [yNfq?]', 'y') | 130 | shelver.expect('Shelve?', '&yes\n&No\n&finish\n&quit', 'y') |
188 | 129 | shelver.expect('Shelve? [yNfq?]', 'y') | 131 | shelver.expect('Shelve?', '&yes\n&No\n&finish\n&quit', 'y') |
189 | 130 | shelver.expect('Shelve 2 change(s)? [yNfq?]', 'y') | 132 | shelver.expect('Shelve 2 change(s)?', '&yes\n&No\n&finish\n&quit', 'y') |
190 | 131 | shelver.run() | 133 | shelver.run() |
191 | 132 | self.assertFileEqual(LINES_AJ, 'tree/foo') | 134 | self.assertFileEqual(LINES_AJ, 'tree/foo') |
192 | 133 | 135 | ||
193 | @@ -137,9 +139,9 @@ | |||
194 | 137 | self.addCleanup(tree.unlock) | 139 | self.addCleanup(tree.unlock) |
195 | 138 | shelver = ExpectShelver(tree, tree.basis_tree()) | 140 | shelver = ExpectShelver(tree, tree.basis_tree()) |
196 | 139 | self.addCleanup(shelver.finalize) | 141 | self.addCleanup(shelver.finalize) |
200 | 140 | shelver.expect('Shelve? [yNfq?]', 'y') | 142 | shelver.expect('Shelve?', '&yes\n&No\n&finish\n&quit', 'y') |
201 | 141 | shelver.expect('Shelve? [yNfq?]', 'n') | 143 | shelver.expect('Shelve?', '&yes\n&No\n&finish\n&quit', 'n') |
202 | 142 | shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y') | 144 | shelver.expect('Shelve 1 change(s)?', '&yes\n&No\n&finish\n&quit', 'y') |
203 | 143 | shelver.run() | 145 | shelver.run() |
204 | 144 | self.assertFileEqual(LINES_AY, 'tree/foo') | 146 | self.assertFileEqual(LINES_AY, 'tree/foo') |
205 | 145 | 147 | ||
206 | @@ -150,8 +152,8 @@ | |||
207 | 150 | self.addCleanup(tree.unlock) | 152 | self.addCleanup(tree.unlock) |
208 | 151 | shelver = ExpectShelver(tree, tree.basis_tree()) | 153 | shelver = ExpectShelver(tree, tree.basis_tree()) |
209 | 152 | self.addCleanup(shelver.finalize) | 154 | self.addCleanup(shelver.finalize) |
212 | 153 | shelver.expect('Shelve binary changes? [yNfq?]', 'y') | 155 | shelver.expect('Shelve binary changes?', '&yes\n&No\n&finish\n&quit', 'y') |
213 | 154 | shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y') | 156 | shelver.expect('Shelve 1 change(s)?', '&yes\n&No\n&finish\n&quit', 'y') |
214 | 155 | shelver.run() | 157 | shelver.run() |
215 | 156 | self.assertFileEqual(LINES_AJ, 'tree/foo') | 158 | self.assertFileEqual(LINES_AJ, 'tree/foo') |
216 | 157 | 159 | ||
217 | @@ -162,10 +164,10 @@ | |||
218 | 162 | self.addCleanup(tree.unlock) | 164 | self.addCleanup(tree.unlock) |
219 | 163 | shelver = ExpectShelver(tree, tree.basis_tree()) | 165 | shelver = ExpectShelver(tree, tree.basis_tree()) |
220 | 164 | self.addCleanup(shelver.finalize) | 166 | self.addCleanup(shelver.finalize) |
225 | 165 | shelver.expect('Shelve renaming "foo" => "bar"? [yNfq?]', 'y') | 167 | shelver.expect('Shelve renaming "foo" => "bar"?', '&yes\n&No\n&finish\n&quit', 'y') |
226 | 166 | shelver.expect('Shelve? [yNfq?]', 'y') | 168 | shelver.expect('Shelve?', '&yes\n&No\n&finish\n&quit', 'y') |
227 | 167 | shelver.expect('Shelve? [yNfq?]', 'y') | 169 | shelver.expect('Shelve?', '&yes\n&No\n&finish\n&quit', 'y') |
228 | 168 | shelver.expect('Shelve 3 change(s)? [yNfq?]', 'y') | 170 | shelver.expect('Shelve 3 change(s)?', '&yes\n&No\n&finish\n&quit', 'y') |
229 | 169 | shelver.run() | 171 | shelver.run() |
230 | 170 | self.assertFileEqual(LINES_AJ, 'tree/foo') | 172 | self.assertFileEqual(LINES_AJ, 'tree/foo') |
231 | 171 | 173 | ||
232 | @@ -176,8 +178,8 @@ | |||
233 | 176 | self.addCleanup(tree.unlock) | 178 | self.addCleanup(tree.unlock) |
234 | 177 | shelver = ExpectShelver(tree, tree.basis_tree()) | 179 | shelver = ExpectShelver(tree, tree.basis_tree()) |
235 | 178 | self.addCleanup(shelver.finalize) | 180 | self.addCleanup(shelver.finalize) |
238 | 179 | shelver.expect('Shelve removing file "foo"? [yNfq?]', 'y') | 181 | shelver.expect('Shelve removing file "foo"?', '&yes\n&No\n&finish\n&quit', 'y') |
239 | 180 | shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y') | 182 | shelver.expect('Shelve 1 change(s)?', '&yes\n&No\n&finish\n&quit', 'y') |
240 | 181 | shelver.run() | 183 | shelver.run() |
241 | 182 | self.assertFileEqual(LINES_AJ, 'tree/foo') | 184 | self.assertFileEqual(LINES_AJ, 'tree/foo') |
242 | 183 | 185 | ||
243 | @@ -190,8 +192,8 @@ | |||
244 | 190 | self.addCleanup(tree.unlock) | 192 | self.addCleanup(tree.unlock) |
245 | 191 | shelver = ExpectShelver(tree, tree.basis_tree()) | 193 | shelver = ExpectShelver(tree, tree.basis_tree()) |
246 | 192 | self.addCleanup(shelver.finalize) | 194 | self.addCleanup(shelver.finalize) |
249 | 193 | shelver.expect('Shelve adding file "foo"? [yNfq?]', 'y') | 195 | shelver.expect('Shelve adding file "foo"?', '&yes\n&No\n&finish\n&quit', 'y') |
250 | 194 | shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y') | 196 | shelver.expect('Shelve 1 change(s)?', '&yes\n&No\n&finish\n&quit', 'y') |
251 | 195 | shelver.run() | 197 | shelver.run() |
252 | 196 | self.assertPathDoesNotExist('tree/foo') | 198 | self.assertPathDoesNotExist('tree/foo') |
253 | 197 | 199 | ||
254 | @@ -203,9 +205,9 @@ | |||
255 | 203 | self.addCleanup(tree.unlock) | 205 | self.addCleanup(tree.unlock) |
256 | 204 | shelver = ExpectShelver(tree, tree.basis_tree()) | 206 | shelver = ExpectShelver(tree, tree.basis_tree()) |
257 | 205 | self.addCleanup(shelver.finalize) | 207 | self.addCleanup(shelver.finalize) |
259 | 206 | shelver.expect('Shelve changing "foo" from file to directory? [yNfq?]', | 208 | shelver.expect('Shelve changing "foo" from file to directory?', '&yes\n&No\n&finish\n&quit', |
260 | 207 | 'y') | 209 | 'y') |
262 | 208 | shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y') | 210 | shelver.expect('Shelve 1 change(s)?', '&yes\n&No\n&finish\n&quit', 'y') |
263 | 209 | 211 | ||
264 | 210 | def test_shelve_modify_target(self): | 212 | def test_shelve_modify_target(self): |
265 | 211 | self.requireFeature(tests.SymlinkFeature) | 213 | self.requireFeature(tests.SymlinkFeature) |
266 | @@ -220,8 +222,8 @@ | |||
267 | 220 | shelver = ExpectShelver(tree, tree.basis_tree()) | 222 | shelver = ExpectShelver(tree, tree.basis_tree()) |
268 | 221 | self.addCleanup(shelver.finalize) | 223 | self.addCleanup(shelver.finalize) |
269 | 222 | shelver.expect('Shelve changing target of "baz" from "bar" to ' | 224 | shelver.expect('Shelve changing target of "baz" from "bar" to ' |
272 | 223 | '"vax"? [yNfq?]', 'y') | 225 | '"vax"?', '&yes\n&No\n&finish\n&quit', 'y') |
273 | 224 | shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y') | 226 | shelver.expect('Shelve 1 change(s)?', '&yes\n&No\n&finish\n&quit', 'y') |
274 | 225 | shelver.run() | 227 | shelver.run() |
275 | 226 | self.assertEqual('bar', os.readlink('tree/baz')) | 228 | self.assertEqual('bar', os.readlink('tree/baz')) |
276 | 227 | 229 | ||
277 | @@ -231,8 +233,8 @@ | |||
278 | 231 | self.addCleanup(tree.unlock) | 233 | self.addCleanup(tree.unlock) |
279 | 232 | shelver = ExpectShelver(tree, tree.basis_tree()) | 234 | shelver = ExpectShelver(tree, tree.basis_tree()) |
280 | 233 | self.addCleanup(shelver.finalize) | 235 | self.addCleanup(shelver.finalize) |
283 | 234 | shelver.expect('Shelve? [yNfq?]', 'f') | 236 | shelver.expect('Shelve?', '&yes\n&No\n&finish\n&quit', 'f') |
284 | 235 | shelver.expect('Shelve 2 change(s)? [yNfq?]', 'y') | 237 | shelver.expect('Shelve 2 change(s)?', '&yes\n&No\n&finish\n&quit', 'y') |
285 | 236 | shelver.run() | 238 | shelver.run() |
286 | 237 | self.assertFileEqual(LINES_AJ, 'tree/foo') | 239 | self.assertFileEqual(LINES_AJ, 'tree/foo') |
287 | 238 | 240 | ||
288 | @@ -242,7 +244,7 @@ | |||
289 | 242 | self.addCleanup(tree.unlock) | 244 | self.addCleanup(tree.unlock) |
290 | 243 | shelver = ExpectShelver(tree, tree.basis_tree()) | 245 | shelver = ExpectShelver(tree, tree.basis_tree()) |
291 | 244 | self.addCleanup(shelver.finalize) | 246 | self.addCleanup(shelver.finalize) |
293 | 245 | shelver.expect('Shelve? [yNfq?]', 'q') | 247 | shelver.expect('Shelve?', '&yes\n&No\n&finish\n&quit', 'q') |
294 | 246 | self.assertRaises(errors.UserAbort, shelver.run) | 248 | self.assertRaises(errors.UserAbort, shelver.run) |
295 | 247 | self.assertFileEqual(LINES_ZY, 'tree/foo') | 249 | self.assertFileEqual(LINES_ZY, 'tree/foo') |
296 | 248 | 250 | ||
297 | @@ -264,19 +266,8 @@ | |||
298 | 264 | self.addCleanup(tree.unlock) | 266 | self.addCleanup(tree.unlock) |
299 | 265 | shelver = ExpectShelver(tree, tree.basis_tree(), file_list=['bar']) | 267 | shelver = ExpectShelver(tree, tree.basis_tree(), file_list=['bar']) |
300 | 266 | self.addCleanup(shelver.finalize) | 268 | self.addCleanup(shelver.finalize) |
314 | 267 | shelver.expect('Shelve adding file "bar"? [yNfq?]', 'y') | 269 | shelver.expect('Shelve adding file "bar"?', '&yes\n&No\n&finish\n&quit', 'y') |
315 | 268 | shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y') | 270 | shelver.expect('Shelve 1 change(s)?', '&yes\n&No\n&finish\n&quit', 'y') |
303 | 269 | shelver.run() | ||
304 | 270 | |||
305 | 271 | def test_shelve_help(self): | ||
306 | 272 | tree = self.create_shelvable_tree() | ||
307 | 273 | tree.lock_tree_write() | ||
308 | 274 | self.addCleanup(tree.unlock) | ||
309 | 275 | shelver = ExpectShelver(tree, tree.basis_tree()) | ||
310 | 276 | self.addCleanup(shelver.finalize) | ||
311 | 277 | shelver.expect('Shelve? [yNfq?]', '?') | ||
312 | 278 | shelver.expect('Shelve? [(y)es, (N)o, (f)inish, or (q)uit]', 'f') | ||
313 | 279 | shelver.expect('Shelve 2 change(s)? [yNfq?]', 'y') | ||
316 | 280 | shelver.run() | 271 | shelver.run() |
317 | 281 | 272 | ||
318 | 282 | def test_shelve_destroy(self): | 273 | def test_shelve_destroy(self): |
319 | @@ -339,8 +330,8 @@ | |||
320 | 339 | shelver = ExpectShelver(tree, tree.basis_tree(), | 330 | shelver = ExpectShelver(tree, tree.basis_tree(), |
321 | 340 | reporter=shelf_ui.ApplyReporter()) | 331 | reporter=shelf_ui.ApplyReporter()) |
322 | 341 | self.addCleanup(shelver.finalize) | 332 | self.addCleanup(shelver.finalize) |
325 | 342 | shelver.expect('Apply change? [yNfq?]', 'n') | 333 | shelver.expect('Apply change?', '&yes\n&No\n&finish\n&quit', 'n') |
326 | 343 | shelver.expect('Apply change? [yNfq?]', 'n') | 334 | shelver.expect('Apply change?', '&yes\n&No\n&finish\n&quit', 'n') |
327 | 344 | # No final shelving prompt because no changes were selected | 335 | # No final shelving prompt because no changes were selected |
328 | 345 | shelver.run() | 336 | shelver.run() |
329 | 346 | self.assertFileEqual(LINES_ZY, 'tree/foo') | 337 | self.assertFileEqual(LINES_ZY, 'tree/foo') |
330 | @@ -352,9 +343,9 @@ | |||
331 | 352 | shelver = ExpectShelver(tree, tree.basis_tree(), | 343 | shelver = ExpectShelver(tree, tree.basis_tree(), |
332 | 353 | reporter=shelf_ui.ApplyReporter()) | 344 | reporter=shelf_ui.ApplyReporter()) |
333 | 354 | self.addCleanup(shelver.finalize) | 345 | self.addCleanup(shelver.finalize) |
337 | 355 | shelver.expect('Apply change? [yNfq?]', 'y') | 346 | shelver.expect('Apply change?', '&yes\n&No\n&finish\n&quit', 'y') |
338 | 356 | shelver.expect('Apply change? [yNfq?]', 'y') | 347 | shelver.expect('Apply change?', '&yes\n&No\n&finish\n&quit', 'y') |
339 | 357 | shelver.expect('Apply 2 change(s)? [yNfq?]', 'n') | 348 | shelver.expect('Apply 2 change(s)?', '&yes\n&No\n&finish\n&quit', 'n') |
340 | 358 | shelver.run() | 349 | shelver.run() |
341 | 359 | self.assertFileEqual(LINES_ZY, 'tree/foo') | 350 | self.assertFileEqual(LINES_ZY, 'tree/foo') |
342 | 360 | 351 | ||
343 | @@ -365,9 +356,9 @@ | |||
344 | 365 | shelver = ExpectShelver(tree, tree.basis_tree(), | 356 | shelver = ExpectShelver(tree, tree.basis_tree(), |
345 | 366 | reporter=shelf_ui.ApplyReporter()) | 357 | reporter=shelf_ui.ApplyReporter()) |
346 | 367 | self.addCleanup(shelver.finalize) | 358 | self.addCleanup(shelver.finalize) |
350 | 368 | shelver.expect('Apply change? [yNfq?]', 'y') | 359 | shelver.expect('Apply change?', '&yes\n&No\n&finish\n&quit', 'y') |
351 | 369 | shelver.expect('Apply change? [yNfq?]', 'y') | 360 | shelver.expect('Apply change?', '&yes\n&No\n&finish\n&quit', 'y') |
352 | 370 | shelver.expect('Apply 2 change(s)? [yNfq?]', 'y') | 361 | shelver.expect('Apply 2 change(s)?', '&yes\n&No\n&finish\n&quit', 'y') |
353 | 371 | shelver.run() | 362 | shelver.run() |
354 | 372 | self.assertFileEqual(LINES_AJ, 'tree/foo') | 363 | self.assertFileEqual(LINES_AJ, 'tree/foo') |
355 | 373 | 364 | ||
356 | @@ -379,8 +370,8 @@ | |||
357 | 379 | shelver = ExpectShelver(tree, tree.basis_tree(), | 370 | shelver = ExpectShelver(tree, tree.basis_tree(), |
358 | 380 | reporter=shelf_ui.ApplyReporter()) | 371 | reporter=shelf_ui.ApplyReporter()) |
359 | 381 | self.addCleanup(shelver.finalize) | 372 | self.addCleanup(shelver.finalize) |
362 | 382 | shelver.expect('Apply binary changes? [yNfq?]', 'y') | 373 | shelver.expect('Apply binary changes?', '&yes\n&No\n&finish\n&quit', 'y') |
363 | 383 | shelver.expect('Apply 1 change(s)? [yNfq?]', 'y') | 374 | shelver.expect('Apply 1 change(s)?', '&yes\n&No\n&finish\n&quit', 'y') |
364 | 384 | shelver.run() | 375 | shelver.run() |
365 | 385 | self.assertFileEqual(LINES_AJ, 'tree/foo') | 376 | self.assertFileEqual(LINES_AJ, 'tree/foo') |
366 | 386 | 377 | ||
367 | @@ -392,10 +383,10 @@ | |||
368 | 392 | shelver = ExpectShelver(tree, tree.basis_tree(), | 383 | shelver = ExpectShelver(tree, tree.basis_tree(), |
369 | 393 | reporter=shelf_ui.ApplyReporter()) | 384 | reporter=shelf_ui.ApplyReporter()) |
370 | 394 | self.addCleanup(shelver.finalize) | 385 | self.addCleanup(shelver.finalize) |
375 | 395 | shelver.expect('Rename "bar" => "foo"? [yNfq?]', 'y') | 386 | shelver.expect('Rename "bar" => "foo"?', '&yes\n&No\n&finish\n&quit', 'y') |
376 | 396 | shelver.expect('Apply change? [yNfq?]', 'y') | 387 | shelver.expect('Apply change?', '&yes\n&No\n&finish\n&quit', 'y') |
377 | 397 | shelver.expect('Apply change? [yNfq?]', 'y') | 388 | shelver.expect('Apply change?', '&yes\n&No\n&finish\n&quit', 'y') |
378 | 398 | shelver.expect('Apply 3 change(s)? [yNfq?]', 'y') | 389 | shelver.expect('Apply 3 change(s)?', '&yes\n&No\n&finish\n&quit', 'y') |
379 | 399 | shelver.run() | 390 | shelver.run() |
380 | 400 | self.assertFileEqual(LINES_AJ, 'tree/foo') | 391 | self.assertFileEqual(LINES_AJ, 'tree/foo') |
381 | 401 | 392 | ||
382 | @@ -407,8 +398,8 @@ | |||
383 | 407 | shelver = ExpectShelver(tree, tree.basis_tree(), | 398 | shelver = ExpectShelver(tree, tree.basis_tree(), |
384 | 408 | reporter=shelf_ui.ApplyReporter()) | 399 | reporter=shelf_ui.ApplyReporter()) |
385 | 409 | self.addCleanup(shelver.finalize) | 400 | self.addCleanup(shelver.finalize) |
388 | 410 | shelver.expect('Add file "foo"? [yNfq?]', 'y') | 401 | shelver.expect('Add file "foo"?', '&yes\n&No\n&finish\n&quit', 'y') |
389 | 411 | shelver.expect('Apply 1 change(s)? [yNfq?]', 'y') | 402 | shelver.expect('Apply 1 change(s)?', '&yes\n&No\n&finish\n&quit', 'y') |
390 | 412 | shelver.run() | 403 | shelver.run() |
391 | 413 | self.assertFileEqual(LINES_AJ, 'tree/foo') | 404 | self.assertFileEqual(LINES_AJ, 'tree/foo') |
392 | 414 | 405 | ||
393 | @@ -422,8 +413,8 @@ | |||
394 | 422 | shelver = ExpectShelver(tree, tree.basis_tree(), | 413 | shelver = ExpectShelver(tree, tree.basis_tree(), |
395 | 423 | reporter=shelf_ui.ApplyReporter()) | 414 | reporter=shelf_ui.ApplyReporter()) |
396 | 424 | self.addCleanup(shelver.finalize) | 415 | self.addCleanup(shelver.finalize) |
399 | 425 | shelver.expect('Delete file "foo"? [yNfq?]', 'y') | 416 | shelver.expect('Delete file "foo"?', '&yes\n&No\n&finish\n&quit', 'y') |
400 | 426 | shelver.expect('Apply 1 change(s)? [yNfq?]', 'y') | 417 | shelver.expect('Apply 1 change(s)?', '&yes\n&No\n&finish\n&quit', 'y') |
401 | 427 | shelver.run() | 418 | shelver.run() |
402 | 428 | self.assertPathDoesNotExist('tree/foo') | 419 | self.assertPathDoesNotExist('tree/foo') |
403 | 429 | 420 | ||
404 | @@ -436,8 +427,8 @@ | |||
405 | 436 | shelver = ExpectShelver(tree, tree.basis_tree(), | 427 | shelver = ExpectShelver(tree, tree.basis_tree(), |
406 | 437 | reporter=shelf_ui.ApplyReporter()) | 428 | reporter=shelf_ui.ApplyReporter()) |
407 | 438 | self.addCleanup(shelver.finalize) | 429 | self.addCleanup(shelver.finalize) |
410 | 439 | shelver.expect('Change "foo" from directory to a file? [yNfq?]', 'y') | 430 | shelver.expect('Change "foo" from directory to a file?', '&yes\n&No\n&finish\n&quit', 'y') |
411 | 440 | shelver.expect('Apply 1 change(s)? [yNfq?]', 'y') | 431 | shelver.expect('Apply 1 change(s)?', '&yes\n&No\n&finish\n&quit', 'y') |
412 | 441 | 432 | ||
413 | 442 | def test_shelve_modify_target(self): | 433 | def test_shelve_modify_target(self): |
414 | 443 | self.requireFeature(tests.SymlinkFeature) | 434 | self.requireFeature(tests.SymlinkFeature) |
415 | @@ -452,9 +443,9 @@ | |||
416 | 452 | shelver = ExpectShelver(tree, tree.basis_tree(), | 443 | shelver = ExpectShelver(tree, tree.basis_tree(), |
417 | 453 | reporter=shelf_ui.ApplyReporter()) | 444 | reporter=shelf_ui.ApplyReporter()) |
418 | 454 | self.addCleanup(shelver.finalize) | 445 | self.addCleanup(shelver.finalize) |
420 | 455 | shelver.expect('Change target of "baz" from "vax" to "bar"? [yNfq?]', | 446 | shelver.expect('Change target of "baz" from "vax" to "bar"?', '&yes\n&No\n&finish\n&quit', |
421 | 456 | 'y') | 447 | 'y') |
423 | 457 | shelver.expect('Apply 1 change(s)? [yNfq?]', 'y') | 448 | shelver.expect('Apply 1 change(s)?', '&yes\n&No\n&finish\n&quit', 'y') |
424 | 458 | shelver.run() | 449 | shelver.run() |
425 | 459 | self.assertEqual('bar', os.readlink('tree/baz')) | 450 | self.assertEqual('bar', os.readlink('tree/baz')) |
426 | 460 | 451 | ||
427 | 461 | 452 | ||
428 | === modified file 'bzrlib/tests/test_ui.py' | |||
429 | --- bzrlib/tests/test_ui.py 2011-05-16 13:39:39 +0000 | |||
430 | +++ bzrlib/tests/test_ui.py 2011-09-27 19:22:41 +0000 | |||
431 | @@ -109,13 +109,14 @@ | |||
432 | 109 | pb.finished() | 109 | pb.finished() |
433 | 110 | 110 | ||
434 | 111 | def test_text_ui_get_boolean(self): | 111 | def test_text_ui_get_boolean(self): |
442 | 112 | stdin = tests.StringIOWrapper("y\n" # True | 112 | stdin = tests.StringIOWrapper("y" # True |
443 | 113 | "n\n" # False | 113 | "n" # False |
444 | 114 | "yes with garbage\nY\n" # True | 114 | "Y" # True |
445 | 115 | "not an answer\nno\n" # False | 115 | "N" # False |
446 | 116 | "I'm sure!\nyes\n" # True | 116 | "\r\ngarbagey" # True |
447 | 117 | "NO\n" # False | 117 | "\r\ngarbagen" # False |
448 | 118 | "foo\n") | 118 | "foo\n" |
449 | 119 | ) | ||
450 | 119 | stdout = tests.StringIOWrapper() | 120 | stdout = tests.StringIOWrapper() |
451 | 120 | stderr = tests.StringIOWrapper() | 121 | stderr = tests.StringIOWrapper() |
452 | 121 | factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr) | 122 | factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr) |
453 | @@ -154,7 +155,7 @@ | |||
454 | 154 | out = test_progress._TTYStringIO() | 155 | out = test_progress._TTYStringIO() |
455 | 155 | self.overrideEnv('TERM', 'xterm') | 156 | self.overrideEnv('TERM', 'xterm') |
456 | 156 | factory = _mod_ui_text.TextUIFactory( | 157 | factory = _mod_ui_text.TextUIFactory( |
458 | 157 | stdin=tests.StringIOWrapper("yada\ny\n"), | 158 | stdin=tests.StringIOWrapper("bar\ny"), |
459 | 158 | stdout=out, stderr=out) | 159 | stdout=out, stderr=out) |
460 | 159 | factory._avail_width = lambda: 79 | 160 | factory._avail_width = lambda: 79 |
461 | 160 | pb = factory.nested_progress_bar() | 161 | pb = factory.nested_progress_bar() |
462 | @@ -171,7 +172,7 @@ | |||
463 | 171 | self.assertContainsRe(output, | 172 | self.assertContainsRe(output, |
464 | 172 | "| foo *\r\r *\r*") | 173 | "| foo *\r\r *\r*") |
465 | 173 | self.assertContainsRe(output, | 174 | self.assertContainsRe(output, |
467 | 174 | r"what do you want\? \[y/n\]: what do you want\? \[y/n\]: ") | 175 | r"what do you want\? \(\[y\]es, \[n\]o\): y") |
468 | 175 | # stdin should have been totally consumed | 176 | # stdin should have been totally consumed |
469 | 176 | self.assertEqual('', factory.stdin.readline()) | 177 | self.assertEqual('', factory.stdin.readline()) |
470 | 177 | 178 | ||
471 | 178 | 179 | ||
472 | === modified file 'bzrlib/ui/__init__.py' | |||
473 | --- bzrlib/ui/__init__.py 2011-05-27 05:16:48 +0000 | |||
474 | +++ bzrlib/ui/__init__.py 2011-09-27 19:22:41 +0000 | |||
475 | @@ -315,6 +315,9 @@ | |||
476 | 315 | warnings.warn(fail) # so tests will fail etc | 315 | warnings.warn(fail) # so tests will fail etc |
477 | 316 | return fail | 316 | return fail |
478 | 317 | 317 | ||
479 | 318 | def confirm(self, msg, choices, default=None): | ||
480 | 319 | raise NotImplementedError(self.confirm) | ||
481 | 320 | |||
482 | 318 | def get_boolean(self, prompt): | 321 | def get_boolean(self, prompt): |
483 | 319 | """Get a boolean question answered from the user. | 322 | """Get a boolean question answered from the user. |
484 | 320 | 323 | ||
485 | @@ -322,7 +325,8 @@ | |||
486 | 322 | line without terminating \\n. | 325 | line without terminating \\n. |
487 | 323 | :return: True or False for y/yes or n/no. | 326 | :return: True or False for y/yes or n/no. |
488 | 324 | """ | 327 | """ |
490 | 325 | raise NotImplementedError(self.get_boolean) | 328 | char = self.confirm(prompt + '?', '&yes\n&no', default=None) |
491 | 329 | return 'y' == char | ||
492 | 326 | 330 | ||
493 | 327 | def get_integer(self, prompt): | 331 | def get_integer(self, prompt): |
494 | 328 | """Get an integer from the user. | 332 | """Get an integer from the user. |
495 | @@ -477,6 +481,9 @@ | |||
496 | 477 | def confirm_action(self, prompt, confirmation_id, args): | 481 | def confirm_action(self, prompt, confirmation_id, args): |
497 | 478 | return self.get_boolean(prompt % args) | 482 | return self.get_boolean(prompt % args) |
498 | 479 | 483 | ||
499 | 484 | def confirm(self, msg, choices, default=None): | ||
500 | 485 | return self.responses.pop(0) | ||
501 | 486 | |||
502 | 480 | def get_boolean(self, prompt): | 487 | def get_boolean(self, prompt): |
503 | 481 | return self.responses.pop(0) | 488 | return self.responses.pop(0) |
504 | 482 | 489 | ||
505 | 483 | 490 | ||
506 | === modified file 'bzrlib/ui/text.py' | |||
507 | --- bzrlib/ui/text.py 2011-05-16 13:39:39 +0000 | |||
508 | +++ bzrlib/ui/text.py 2011-09-27 19:22:41 +0000 | |||
509 | @@ -30,6 +30,7 @@ | |||
510 | 30 | 30 | ||
511 | 31 | from bzrlib import ( | 31 | from bzrlib import ( |
512 | 32 | debug, | 32 | debug, |
513 | 33 | errors, | ||
514 | 33 | progress, | 34 | progress, |
515 | 34 | osutils, | 35 | osutils, |
516 | 35 | trace, | 36 | trace, |
517 | @@ -61,6 +62,40 @@ | |||
518 | 61 | # paints progress, network activity, etc | 62 | # paints progress, network activity, etc |
519 | 62 | self._progress_view = self.make_progress_view() | 63 | self._progress_view = self.make_progress_view() |
520 | 63 | 64 | ||
521 | 65 | def confirm(self, msg, choices, default=None): | ||
522 | 66 | if self.stdin == sys.stdin and self.stdin.isatty(): | ||
523 | 67 | def getchar(): | ||
524 | 68 | return osutils.getchar() | ||
525 | 69 | else: | ||
526 | 70 | def getchar(): | ||
527 | 71 | return self.stdin.read(1) | ||
528 | 72 | |||
529 | 73 | shortcuts = '' | ||
530 | 74 | alternatives = [] | ||
531 | 75 | for c in choices.split('\n'): | ||
532 | 76 | shortcut = c.find('&') | ||
533 | 77 | if -1 != shortcut and (shortcut + 1) < len(c): | ||
534 | 78 | help = c[:shortcut] | ||
535 | 79 | help += '[' + c[shortcut + 1] + ']' | ||
536 | 80 | help += c[(shortcut + 2):] | ||
537 | 81 | shortcut = c[shortcut + 1] | ||
538 | 82 | else: | ||
539 | 83 | help = c.strip('&') | ||
540 | 84 | shortcut = c[0] | ||
541 | 85 | shortcuts += shortcut.lower() | ||
542 | 86 | alternatives.append(help) | ||
543 | 87 | |||
544 | 88 | prompt = u'%s (%s): ' % (msg, ', '.join(alternatives)) | ||
545 | 89 | self.prompt(prompt) | ||
546 | 90 | |||
547 | 91 | while True: | ||
548 | 92 | char = getchar().lower() | ||
549 | 93 | if '\r' == char and default is not None: | ||
550 | 94 | char = default | ||
551 | 95 | if -1 != shortcuts.find(char): | ||
552 | 96 | self.stderr.write(char + '\n') | ||
553 | 97 | return char | ||
554 | 98 | |||
555 | 64 | def be_quiet(self, state): | 99 | def be_quiet(self, state): |
556 | 65 | if state and not self._quiet: | 100 | if state and not self._quiet: |
557 | 66 | self.clear_term() | 101 | self.clear_term() |
558 | @@ -78,18 +113,6 @@ | |||
559 | 78 | # to clear it. We might need to separately check for the case of | 113 | # to clear it. We might need to separately check for the case of |
560 | 79 | self._progress_view.clear() | 114 | self._progress_view.clear() |
561 | 80 | 115 | ||
562 | 81 | def get_boolean(self, prompt): | ||
563 | 82 | while True: | ||
564 | 83 | self.prompt(prompt + "? [y/n]: ") | ||
565 | 84 | line = self.stdin.readline().lower() | ||
566 | 85 | if line in ('y\n', 'yes\n'): | ||
567 | 86 | return True | ||
568 | 87 | elif line in ('n\n', 'no\n'): | ||
569 | 88 | return False | ||
570 | 89 | elif line in ('', None): | ||
571 | 90 | # end-of-file; possibly should raise an error here instead | ||
572 | 91 | return None | ||
573 | 92 | |||
574 | 93 | def get_integer(self, prompt): | 116 | def get_integer(self, prompt): |
575 | 94 | while True: | 117 | while True: |
576 | 95 | self.prompt(prompt) | 118 | self.prompt(prompt) |
577 | 96 | 119 | ||
578 | # Begin bundle | |||
579 | 97 | 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 |
Vincent Ladeuil (vila) wrote : | # |
> I also ran into problems using shelve with my VIM plugin.
WOW ! (And that's coming from die-hard emacs user ;-D)
> Other commands
> use ui.get_boolean, but there is no corresponding method for handling
> more complex choices. So even after providing my own UI class
> implementation, I cannot use shelve.
>
> In VIM there is a confirm function:
>
> confirm({msg} [, {choices} [, {default} [, {type}]]])
>
> Which can be used like this:
>
> confirm("Save changes?", "&Yes\n&
>
> Where the letter after the '&' is the shortcut key for that choice.
>
> So I have been working on a branch to implement something just like
> this (see attached patch).
This sounds *seriously* interesting and I urge you to pusblish your changes
in a branch and propose it for merge so we can properly review it (I'm
pleased tht launchpad displays your patch inline ! Wow too there) but I
really want your patch to be discussed as it seems we have perfect case of
triangulation here ;)
>
> N.B.: your patch removes a call to gettext.
Ouch, yeah, I initially wrote the patch on a 2.4-based branch where the
gettext call wasn't there >-/ I'll push a revision fixing that asap. Thanks
catching it !
Martin Packman (gz) wrote : | # |
The problem with the shelf_ui code is it does something totally different to all the other bazaar user interactions, hence the issues with qbzr and test scripts and editors apparently. Making it use bzrlib.ui should be the goal, but doesn't need to happen in this branch.
Benoit, that bundle is really exciting and apart from some nitpicking I think should basically be merged. Please do propose it as Vincent said.
Notes just on the changes in this branch:
+ char_based = not(os.
+ if char_based and not sys.stdin.isatty():
Space after 'not' otherwise it looks like a function. :)
Having modules one by one need to learn about magic emacs variables is a good argument for this logic living elsewhere.
+ # XXX: Warn if more than one char is typed ?
To get similar logic to the current and get_boolean behaviour, you'd take the first character that matched the set, which you can't do at this level without remembering the last line and only prompting for a new one when there are no more characters. That's pretty unimportant though.
Vincent Ladeuil (vila) wrote : | # |
> The problem with the shelf_ui code is it does something totally different to
> all the other bazaar user interactions, hence the issues with qbzr and test
> scripts and editors apparently. Making it use bzrlib.ui should be the goal,
> but doesn't need to happen in this branch.
/me nods
>
> Benoit, that bundle is really exciting and apart from some nitpicking I think
> should basically be merged. Please do propose it as Vincent said.
/me .. err, sorry :)
>
> Notes just on the changes in this branch:
>
> + char_based = not(os.
> + if char_based and not sys.stdin.isatty():
>
> Space after 'not' otherwise it looks like a function. :)
Yeah, thanks. But the plan is not to mention emacs at all there but instead
let user configure that themselves (INSIDE_EMACS is a far too big hammer and
was just a quick way to test but it's not reliable nor appropriate for all
cases), any feedback on the proposed config variables ?
>
> Having modules one by one need to learn about magic emacs variables is a good
> argument for this logic living elsewhere.
>
> + # XXX: Warn if more than one char is typed ?
>
> To get similar logic to the current and get_boolean behaviour, you'd take the
> first character that matched the set, which you can't do at this level without
> remembering the last line and only prompting for a new one when there are no
> more characters. That's pretty unimportant though.
And would keep the same UI which can drive any user nuts, it's really
intended to be line-based with one asnwer by *line* ending with CR, no
buffering of answers there.
What made shelve unsuable for me is that I needed to prepare the answers for
all the questions *before* seeing the questions, keeping this logic just
don't work ;)
So my question may be clearer as:
- given that we expect only a single char (followed by a \n), what should we
do if we get more ? Warn ? Swallow silently ? Fail ?
Also, I need to add tests and that will probably remove the weird
char = '\n'
just-in-case hack.
Martin Packman (gz) wrote : | # |
So, to be clear, I think this branch may as well land as an improvement to the current isatty() hack, with the thought that Benoit's change will be the basis for a longer term solution.
> > + char_based = not(os.
...or just `is None` for that matter.
> Yeah, thanks. But the plan is not to mention emacs at all there but instead
> let user configure that themselves (INSIDE_EMACS is a far too big hammer and
> was just a quick way to test but it's not reliable nor appropriate for all
> cases), any feedback on the proposed config variables ?
For now it seems an appropriate hammer, just hang it elsewhere in a little private function. After this is all moved to bzrlib.ui then a config value to enable/disable character based input seems reasonable, but I wouldn't bother for now.
> - given that we expect only a single char (followed by a \n), what should we
> do if we get more ? Warn ? Swallow silently ? Fail ?
Swallow (and maybe warn) or fail and reprompt would all be fine.
> Also, I need to add tests and that will probably remove the weird
>
> char = '\n'
>
> just-in-case hack.
Right, you probably want a test for the EOF case, it needs to make sure it gets out of the shelf loop somehow.
Vincent Ladeuil (vila) wrote : | # |
sent to pqm by email
Preview Diff
1 | === modified file 'bzrlib/shelf_ui.py' | |||
2 | --- bzrlib/shelf_ui.py 2011-09-16 15:39:47 +0000 | |||
3 | +++ bzrlib/shelf_ui.py 2011-09-28 15:10:33 +0000 | |||
4 | @@ -16,6 +16,7 @@ | |||
5 | 16 | 16 | ||
6 | 17 | 17 | ||
7 | 18 | from cStringIO import StringIO | 18 | from cStringIO import StringIO |
8 | 19 | import os | ||
9 | 19 | import shutil | 20 | import shutil |
10 | 20 | import sys | 21 | import sys |
11 | 21 | import tempfile | 22 | import tempfile |
12 | @@ -251,20 +252,41 @@ | |||
13 | 251 | diff_file.seek(0) | 252 | diff_file.seek(0) |
14 | 252 | return patches.parse_patch(diff_file) | 253 | return patches.parse_patch(diff_file) |
15 | 253 | 254 | ||
16 | 255 | def _char_based(self): | ||
17 | 256 | # FIXME: A bit hackish to use INSIDE_EMACS here, but there is another | ||
18 | 257 | # work in progress moving this method (and more importantly prompt() | ||
19 | 258 | # below) into the ui area and address the issue in better ways. | ||
20 | 259 | # -- vila 2011-09-28 | ||
21 | 260 | return os.environ.get('INSIDE_EMACS', None) is None | ||
22 | 261 | |||
23 | 254 | def prompt(self, message): | 262 | def prompt(self, message): |
24 | 255 | """Prompt the user for a character. | 263 | """Prompt the user for a character. |
25 | 256 | 264 | ||
26 | 257 | :param message: The message to prompt a user with. | 265 | :param message: The message to prompt a user with. |
27 | 258 | :return: A character. | 266 | :return: A character. |
28 | 259 | """ | 267 | """ |
32 | 260 | if not sys.stdin.isatty(): | 268 | char_based = self._char_based() |
33 | 261 | # Since there is no controlling terminal we will hang when trying | 269 | if char_based and not sys.stdin.isatty(): |
34 | 262 | # to prompt the user, better abort now. See | 270 | # Since there is no controlling terminal we will hang when |
35 | 271 | # trying to prompt the user, better abort now. See | ||
36 | 263 | # https://code.launchpad.net/~bialix/bzr/shelve-no-tty/+merge/14905 | 272 | # https://code.launchpad.net/~bialix/bzr/shelve-no-tty/+merge/14905 |
37 | 264 | # for more context. | 273 | # for more context. |
38 | 265 | raise errors.BzrError(gettext("You need a controlling terminal.")) | 274 | raise errors.BzrError(gettext("You need a controlling terminal.")) |
39 | 266 | sys.stdout.write(message) | 275 | sys.stdout.write(message) |
41 | 267 | char = osutils.getchar() | 276 | if char_based: |
42 | 277 | # We peek one char at a time which requires a real term here | ||
43 | 278 | char = osutils.getchar() | ||
44 | 279 | else: | ||
45 | 280 | # While running tests (or under emacs) the input is line buffered | ||
46 | 281 | # so we must not use osutils.getchar(). Instead we switch to a mode | ||
47 | 282 | # where each line is terminated by a new line | ||
48 | 283 | line = sys.stdin.readline() | ||
49 | 284 | if line: | ||
50 | 285 | # XXX: Warn if more than one char is typed ? | ||
51 | 286 | char = line[0] | ||
52 | 287 | else: | ||
53 | 288 | # Empty input, callers handle it as enter | ||
54 | 289 | char = '' | ||
55 | 268 | sys.stdout.write("\r" + ' ' * len(message) + '\r') | 290 | sys.stdout.write("\r" + ' ' * len(message) + '\r') |
56 | 269 | sys.stdout.flush() | 291 | sys.stdout.flush() |
57 | 270 | return char | 292 | return char |
58 | 271 | 293 | ||
59 | === modified file 'bzrlib/tests/script.py' | |||
60 | --- bzrlib/tests/script.py 2011-01-12 01:01:53 +0000 | |||
61 | +++ bzrlib/tests/script.py 2011-09-28 15:10:33 +0000 | |||
62 | @@ -481,6 +481,10 @@ | |||
63 | 481 | def setUp(self): | 481 | def setUp(self): |
64 | 482 | super(TestCaseWithMemoryTransportAndScript, self).setUp() | 482 | super(TestCaseWithMemoryTransportAndScript, self).setUp() |
65 | 483 | self.script_runner = ScriptRunner() | 483 | self.script_runner = ScriptRunner() |
66 | 484 | # FIXME: See shelf_ui.Shelver._char_based. This allow using shelve in | ||
67 | 485 | # scripts while providing a line-based input (better solution in | ||
68 | 486 | # progress). -- vila 2011-09-28 | ||
69 | 487 | self.overrideEnv('INSIDE_EMACS', '1') | ||
70 | 484 | 488 | ||
71 | 485 | def run_script(self, script, null_output_matches_anything=False): | 489 | def run_script(self, script, null_output_matches_anything=False): |
72 | 486 | return self.script_runner.run_script(self, script, | 490 | return self.script_runner.run_script(self, script, |
73 | @@ -511,6 +515,10 @@ | |||
74 | 511 | def setUp(self): | 515 | def setUp(self): |
75 | 512 | super(TestCaseWithTransportAndScript, self).setUp() | 516 | super(TestCaseWithTransportAndScript, self).setUp() |
76 | 513 | self.script_runner = ScriptRunner() | 517 | self.script_runner = ScriptRunner() |
77 | 518 | # FIXME: See shelf_ui.Shelver._char_based. This allow using shelve in | ||
78 | 519 | # scripts while providing a line-based input (better solution in | ||
79 | 520 | # progress). -- vila 2011-09-28 | ||
80 | 521 | self.overrideEnv('INSIDE_EMACS', '1') | ||
81 | 514 | 522 | ||
82 | 515 | def run_script(self, script, null_output_matches_anything=False): | 523 | def run_script(self, script, null_output_matches_anything=False): |
83 | 516 | return self.script_runner.run_script(self, script, | 524 | return self.script_runner.run_script(self, script, |
84 | 517 | 525 | ||
85 | === modified file 'bzrlib/tests/test_script.py' | |||
86 | --- bzrlib/tests/test_script.py 2011-05-16 13:39:39 +0000 | |||
87 | +++ bzrlib/tests/test_script.py 2011-09-28 15:10:33 +0000 | |||
88 | @@ -572,9 +572,9 @@ | |||
89 | 572 | def test_confirm_action(self): | 572 | def test_confirm_action(self): |
90 | 573 | """You can write tests that demonstrate user confirmation. | 573 | """You can write tests that demonstrate user confirmation. |
91 | 574 | 574 | ||
95 | 575 | Specifically, ScriptRunner does't care if the output line for the prompt | 575 | Specifically, ScriptRunner does't care if the output line for the |
96 | 576 | isn't terminated by a newline from the program; it's implicitly terminated | 576 | prompt isn't terminated by a newline from the program; it's implicitly |
97 | 577 | by the input. | 577 | terminated by the input. |
98 | 578 | """ | 578 | """ |
99 | 579 | commands.builtin_command_registry.register(cmd_test_confirm) | 579 | commands.builtin_command_registry.register(cmd_test_confirm) |
100 | 580 | self.addCleanup(commands.builtin_command_registry.remove, 'test-confirm') | 580 | self.addCleanup(commands.builtin_command_registry.remove, 'test-confirm') |
101 | @@ -589,3 +589,56 @@ | |||
102 | 589 | ok, no | 589 | ok, no |
103 | 590 | """) | 590 | """) |
104 | 591 | 591 | ||
105 | 592 | class TestShelve(script.TestCaseWithTransportAndScript): | ||
106 | 593 | |||
107 | 594 | def setUp(self): | ||
108 | 595 | super(TestShelve, self).setUp() | ||
109 | 596 | self.run_script(""" | ||
110 | 597 | $ bzr init test | ||
111 | 598 | Created a standalone tree (format: 2a) | ||
112 | 599 | $ cd test | ||
113 | 600 | $ echo foo > file | ||
114 | 601 | $ bzr add | ||
115 | 602 | adding file | ||
116 | 603 | $ bzr commit -m 'file added' | ||
117 | 604 | 2>Committing to:...test/ | ||
118 | 605 | 2>added file | ||
119 | 606 | 2>Committed revision 1. | ||
120 | 607 | $ echo bar > file | ||
121 | 608 | """) | ||
122 | 609 | |||
123 | 610 | def test_shelve(self): | ||
124 | 611 | self.run_script(""" | ||
125 | 612 | $ bzr shelve -m 'shelve bar' | ||
126 | 613 | # Shelve? [yNfq?] | ||
127 | 614 | <y | ||
128 | 615 | # Shelve 1 change(s)? [yNfq?] | ||
129 | 616 | <y | ||
130 | 617 | 2>Selected changes: | ||
131 | 618 | 2> M file | ||
132 | 619 | 2>Changes shelved with id "1". | ||
133 | 620 | """, | ||
134 | 621 | # shelve uses \r that can't be represented in the | ||
135 | 622 | # script ? | ||
136 | 623 | null_output_matches_anything=True) | ||
137 | 624 | self.run_script(""" | ||
138 | 625 | $ bzr shelve --list | ||
139 | 626 | 1: shelve bar | ||
140 | 627 | """) | ||
141 | 628 | |||
142 | 629 | def test_dont_shelve(self): | ||
143 | 630 | # We intentionally provide no input here to test EOF | ||
144 | 631 | self.run_script(""" | ||
145 | 632 | $ bzr shelve -m 'shelve bar' | ||
146 | 633 | # Shelve? [yNfq?] | ||
147 | 634 | # Shelve 1 change(s)? [yNfq?] | ||
148 | 635 | 2>No changes to shelve. | ||
149 | 636 | """, | ||
150 | 637 | # shelve uses \r that can't be represented in the | ||
151 | 638 | # script ? | ||
152 | 639 | null_output_matches_anything=True) | ||
153 | 640 | self.run_script(""" | ||
154 | 641 | $ bzr st | ||
155 | 642 | modified: | ||
156 | 643 | file | ||
157 | 644 | """) | ||
158 | 592 | 645 | ||
159 | === modified file 'doc/en/release-notes/bzr-2.5.txt' | |||
160 | --- doc/en/release-notes/bzr-2.5.txt 2011-09-27 10:17:36 +0000 | |||
161 | +++ doc/en/release-notes/bzr-2.5.txt 2011-09-28 15:10:33 +0000 | |||
162 | @@ -45,6 +45,10 @@ | |||
163 | 45 | .. Fixes for situations where bzr would previously crash or give incorrect | 45 | .. Fixes for situations where bzr would previously crash or give incorrect |
164 | 46 | or undesirable results. | 46 | or undesirable results. |
165 | 47 | 47 | ||
166 | 48 | * ``bzr shelve`` can now be used in emacs shells as the input handling is | ||
167 | 49 | turned into a line-basde one when ``INSIDE_EMACS`` is set (which is the | ||
168 | 50 | case for all recent emacs versions). (Vincent Ladeuil, #856261) | ||
169 | 51 | |||
170 | 48 | * Redirects between http and https no longer discard path information | 52 | * Redirects between http and https no longer discard path information |
171 | 49 | in some cases. (Jelmer Vernooij, #853765) | 53 | in some cases. (Jelmer Vernooij, #853765) |
172 | 50 | 54 | ||
173 | @@ -87,6 +91,9 @@ | |||
174 | 87 | Testing | 91 | Testing |
175 | 88 | ******* | 92 | ******* |
176 | 89 | 93 | ||
177 | 94 | * Test scripts can now use ``bzr shelve`` and provide their input as | ||
178 | 95 | complete lines. (Vincent Ladeuil, #856261) | ||
179 | 96 | |||
180 | 90 | .. Fixes and changes that are only relevant to bzr's test framework and | 97 | .. Fixes and changes that are only relevant to bzr's test framework and |
181 | 91 | suite. This can include new facilities for writing tests, fixes to | 98 | suite. This can include new facilities for writing tests, fixes to |
182 | 92 | spurious test failures and changes to the way things should be tested. | 99 | spurious test failures and changes to the way things should be tested. |
Excerpts from Vincent Ladeuil's message of Tue Sep 27 16:37:19 UTC 2011: /code.launchpad .net/~vila/ bzr/856261- unshelve- line-based/ +merge/ 77195 line_based_ input ?
> Vincent Ladeuil has proposed merging lp:~vila/bzr/856261-unshelve-line-based into lp:bzr.
>
> Requested reviews:
> bzr-core (bzr-core)
>
> For more details, see:
> https:/
>
> I've been deprived from using shelve under emacs for years.
>
> But when I ran into bug #856261 trying to reproduce bug #850594, I thought
> it may be worth to have another go at it.
>
> The fix is simple: since osutils.getchar() requires a true term (which
> neither emacs not the test scripts provide), I handle the input as a
> line-based one which is exactly what emacs does and also exactly what the
> test script interface provides :)
>
> I'd like feedback on how we want to configure this feature, knowing that an
> environment variable may be more appropriate here (not everybody use
> terminals under emacs only and the test script need a way to force the
> behavior). Or should I just consider that the fix for #491196 is around the
> corner and name the config variable... shelve.
> shelve.line_ui ? both boolean defaulting to False or shelve.input
> (defaulting to 'char' accepting 'line' ?)
I also ran into problems using shelve with my VIM plugin. Other commands
use ui.get_boolean, but there is no corresponding method for handling
more complex choices. So even after providing my own UI class
implementation, I cannot use shelve.
In VIM there is a confirm function:
confirm({msg} [, {choices} [, {default} [, {type}]]])
Which can be used like this:
confirm("Save changes?", "&Yes\n& No\n&Cancel" )
Where the letter after the '&' is the shortcut key for that choice.
So I have been working on a branch to implement something just like
this (see attached patch).
N.B.: your patch removes a call to gettext.
--
A: Because it destroys the flow of conversation.
Q: Why is top posting dumb?