##// END OF EJS Templates
Apply autosuggestion only at EOL....
Matthias Bussonnier -
Show More
@@ -0,0 +1,40 b''
1 import pytest
2 from IPython.terminal.shortcuts import _apply_autosuggest
3
4 from unittest.mock import Mock
5
6
7 def make_event(text, cursor, suggestion):
8 event = Mock()
9 event.current_buffer = Mock()
10 event.current_buffer.suggestion = Mock()
11 event.current_buffer.cursor_position = cursor
12 event.current_buffer.suggestion.text = suggestion
13 event.current_buffer.document = Mock()
14 event.current_buffer.document.get_end_of_line_position = Mock(return_value=0)
15 event.current_buffer.document.text = text
16 event.current_buffer.document.cursor_position = cursor
17 return event
18
19
20 @pytest.mark.parametrize(
21 "text, cursor, suggestion, called",
22 [
23 ("123456", 6, "123456789", True),
24 ("123456", 3, "123456789", False),
25 ("123456 \n789", 6, "123456789", True),
26 ],
27 )
28 def test_autosuggest_at_EOL(text, cursor, suggestion, called):
29 """
30 test that autosuggest is only applied at end of line.
31 """
32
33 event = make_event(text, cursor, suggestion)
34 event.current_buffer.insert_text = Mock()
35 _apply_autosuggest(event)
36 if called:
37 event.current_buffer.insert_text.assert_called()
38 else:
39 event.current_buffer.insert_text.assert_not_called()
40 # event.current_buffer.document.get_end_of_line_position.assert_called()
@@ -1,544 +1,551 b''
1 1 """
2 2 Module to define and register Terminal IPython shortcuts with
3 3 :mod:`prompt_toolkit`
4 4 """
5 5
6 6 # Copyright (c) IPython Development Team.
7 7 # Distributed under the terms of the Modified BSD License.
8 8
9 9 import warnings
10 10 import signal
11 11 import sys
12 12 import re
13 13 import os
14 14 from typing import Callable
15 15
16 16
17 17 from prompt_toolkit.application.current import get_app
18 18 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER
19 19 from prompt_toolkit.filters import (has_focus, has_selection, Condition,
20 20 vi_insert_mode, emacs_insert_mode, has_completions, vi_mode)
21 21 from prompt_toolkit.key_binding.bindings.completion import display_completions_like_readline
22 22 from prompt_toolkit.key_binding import KeyBindings
23 23 from prompt_toolkit.key_binding.bindings import named_commands as nc
24 24 from prompt_toolkit.key_binding.vi_state import InputMode, ViState
25 25
26 26 from IPython.utils.decorators import undoc
27 27
28 28 @undoc
29 29 @Condition
30 30 def cursor_in_leading_ws():
31 31 before = get_app().current_buffer.document.current_line_before_cursor
32 32 return (not before) or before.isspace()
33 33
34 34
35 # Needed for to accept autosuggestions in vi insert mode
36 def _apply_autosuggest(event):
37 """
38 Apply autosuggestion if at end of line.
39 """
40 b = event.current_buffer
41 d = b.document
42 after_cursor = d.text[d.cursor_position :]
43 lines = after_cursor.split("\n")
44 end_of_current_line = lines[0].strip()
45 suggestion = b.suggestion
46 if (suggestion is not None) and (suggestion.text) and (end_of_current_line == ""):
47 b.insert_text(suggestion.text)
48 else:
49 nc.end_of_line(event)
50
35 51 def create_ipython_shortcuts(shell):
36 52 """Set up the prompt_toolkit keyboard shortcuts for IPython"""
37 53
38 54 kb = KeyBindings()
39 55 insert_mode = vi_insert_mode | emacs_insert_mode
40 56
41 57 if getattr(shell, 'handle_return', None):
42 58 return_handler = shell.handle_return(shell)
43 59 else:
44 60 return_handler = newline_or_execute_outer(shell)
45 61
46 62 kb.add('enter', filter=(has_focus(DEFAULT_BUFFER)
47 63 & ~has_selection
48 64 & insert_mode
49 65 ))(return_handler)
50 66
51 67 def reformat_and_execute(event):
52 68 reformat_text_before_cursor(event.current_buffer, event.current_buffer.document, shell)
53 69 event.current_buffer.validate_and_handle()
54 70
55 71 kb.add('escape', 'enter', filter=(has_focus(DEFAULT_BUFFER)
56 72 & ~has_selection
57 73 & insert_mode
58 74 ))(reformat_and_execute)
59 75
60 76 kb.add("c-\\")(quit)
61 77
62 78 kb.add('c-p', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER))
63 79 )(previous_history_or_previous_completion)
64 80
65 81 kb.add('c-n', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER))
66 82 )(next_history_or_next_completion)
67 83
68 84 kb.add('c-g', filter=(has_focus(DEFAULT_BUFFER) & has_completions)
69 85 )(dismiss_completion)
70 86
71 87 kb.add('c-c', filter=has_focus(DEFAULT_BUFFER))(reset_buffer)
72 88
73 89 kb.add('c-c', filter=has_focus(SEARCH_BUFFER))(reset_search_buffer)
74 90
75 91 supports_suspend = Condition(lambda: hasattr(signal, 'SIGTSTP'))
76 92 kb.add('c-z', filter=supports_suspend)(suspend_to_bg)
77 93
78 94 # Ctrl+I == Tab
79 95 kb.add('tab', filter=(has_focus(DEFAULT_BUFFER)
80 96 & ~has_selection
81 97 & insert_mode
82 98 & cursor_in_leading_ws
83 99 ))(indent_buffer)
84 100 kb.add('c-o', filter=(has_focus(DEFAULT_BUFFER) & emacs_insert_mode)
85 101 )(newline_autoindent_outer(shell.input_transformer_manager))
86 102
87 103 kb.add('f2', filter=has_focus(DEFAULT_BUFFER))(open_input_in_editor)
88 104
89 105 @Condition
90 106 def auto_match():
91 107 return shell.auto_match
92 108
93 109 focused_insert = (vi_insert_mode | emacs_insert_mode) & has_focus(DEFAULT_BUFFER)
94 110 _preceding_text_cache = {}
95 111 _following_text_cache = {}
96 112
97 113 def preceding_text(pattern):
98 114 try:
99 115 return _preceding_text_cache[pattern]
100 116 except KeyError:
101 117 pass
102 118 m = re.compile(pattern)
103 119
104 120 def _preceding_text():
105 121 app = get_app()
106 122 return bool(m.match(app.current_buffer.document.current_line_before_cursor))
107 123
108 124 condition = Condition(_preceding_text)
109 125 _preceding_text_cache[pattern] = condition
110 126 return condition
111 127
112 128 def following_text(pattern):
113 129 try:
114 130 return _following_text_cache[pattern]
115 131 except KeyError:
116 132 pass
117 133 m = re.compile(pattern)
118 134
119 135 def _following_text():
120 136 app = get_app()
121 137 return bool(m.match(app.current_buffer.document.current_line_after_cursor))
122 138
123 139 condition = Condition(_following_text)
124 140 _following_text_cache[pattern] = condition
125 141 return condition
126 142
127 143 # auto match
128 144 @kb.add("(", filter=focused_insert & auto_match & following_text(r"[,)}\]]|$"))
129 145 def _(event):
130 146 event.current_buffer.insert_text("()")
131 147 event.current_buffer.cursor_left()
132 148
133 149 @kb.add("[", filter=focused_insert & auto_match & following_text(r"[,)}\]]|$"))
134 150 def _(event):
135 151 event.current_buffer.insert_text("[]")
136 152 event.current_buffer.cursor_left()
137 153
138 154 @kb.add("{", filter=focused_insert & auto_match & following_text(r"[,)}\]]|$"))
139 155 def _(event):
140 156 event.current_buffer.insert_text("{}")
141 157 event.current_buffer.cursor_left()
142 158
143 159 @kb.add(
144 160 '"',
145 161 filter=focused_insert
146 162 & auto_match
147 163 & preceding_text(r'^([^"]+|"[^"]*")*$')
148 164 & following_text(r"[,)}\]]|$"),
149 165 )
150 166 def _(event):
151 167 event.current_buffer.insert_text('""')
152 168 event.current_buffer.cursor_left()
153 169
154 170 @kb.add(
155 171 "'",
156 172 filter=focused_insert
157 173 & auto_match
158 174 & preceding_text(r"^([^']+|'[^']*')*$")
159 175 & following_text(r"[,)}\]]|$"),
160 176 )
161 177 def _(event):
162 178 event.current_buffer.insert_text("''")
163 179 event.current_buffer.cursor_left()
164 180
165 181 # raw string
166 182 @kb.add(
167 183 "(", filter=focused_insert & auto_match & preceding_text(r".*(r|R)[\"'](-*)$")
168 184 )
169 185 def _(event):
170 186 matches = re.match(
171 187 r".*(r|R)[\"'](-*)",
172 188 event.current_buffer.document.current_line_before_cursor,
173 189 )
174 190 dashes = matches.group(2) or ""
175 191 event.current_buffer.insert_text("()" + dashes)
176 192 event.current_buffer.cursor_left(len(dashes) + 1)
177 193
178 194 @kb.add(
179 195 "[", filter=focused_insert & auto_match & preceding_text(r".*(r|R)[\"'](-*)$")
180 196 )
181 197 def _(event):
182 198 matches = re.match(
183 199 r".*(r|R)[\"'](-*)",
184 200 event.current_buffer.document.current_line_before_cursor,
185 201 )
186 202 dashes = matches.group(2) or ""
187 203 event.current_buffer.insert_text("[]" + dashes)
188 204 event.current_buffer.cursor_left(len(dashes) + 1)
189 205
190 206 @kb.add(
191 207 "{", filter=focused_insert & auto_match & preceding_text(r".*(r|R)[\"'](-*)$")
192 208 )
193 209 def _(event):
194 210 matches = re.match(
195 211 r".*(r|R)[\"'](-*)",
196 212 event.current_buffer.document.current_line_before_cursor,
197 213 )
198 214 dashes = matches.group(2) or ""
199 215 event.current_buffer.insert_text("{}" + dashes)
200 216 event.current_buffer.cursor_left(len(dashes) + 1)
201 217
202 218 # just move cursor
203 219 @kb.add(")", filter=focused_insert & auto_match & following_text(r"^\)"))
204 220 @kb.add("]", filter=focused_insert & auto_match & following_text(r"^\]"))
205 221 @kb.add("}", filter=focused_insert & auto_match & following_text(r"^\}"))
206 222 @kb.add('"', filter=focused_insert & auto_match & following_text('^"'))
207 223 @kb.add("'", filter=focused_insert & auto_match & following_text("^'"))
208 224 def _(event):
209 225 event.current_buffer.cursor_right()
210 226
211 227 @kb.add(
212 228 "backspace",
213 229 filter=focused_insert
214 230 & preceding_text(r".*\($")
215 231 & auto_match
216 232 & following_text(r"^\)"),
217 233 )
218 234 @kb.add(
219 235 "backspace",
220 236 filter=focused_insert
221 237 & preceding_text(r".*\[$")
222 238 & auto_match
223 239 & following_text(r"^\]"),
224 240 )
225 241 @kb.add(
226 242 "backspace",
227 243 filter=focused_insert
228 244 & preceding_text(r".*\{$")
229 245 & auto_match
230 246 & following_text(r"^\}"),
231 247 )
232 248 @kb.add(
233 249 "backspace",
234 250 filter=focused_insert
235 251 & preceding_text('.*"$')
236 252 & auto_match
237 253 & following_text('^"'),
238 254 )
239 255 @kb.add(
240 256 "backspace",
241 257 filter=focused_insert
242 258 & preceding_text(r".*'$")
243 259 & auto_match
244 260 & following_text(r"^'"),
245 261 )
246 262 def _(event):
247 263 event.current_buffer.delete()
248 264 event.current_buffer.delete_before_cursor()
249 265
250 266 if shell.display_completions == "readlinelike":
251 267 kb.add(
252 268 "c-i",
253 269 filter=(
254 270 has_focus(DEFAULT_BUFFER)
255 271 & ~has_selection
256 272 & insert_mode
257 273 & ~cursor_in_leading_ws
258 274 ),
259 275 )(display_completions_like_readline)
260 276
261 277 if sys.platform == "win32":
262 278 kb.add("c-v", filter=(has_focus(DEFAULT_BUFFER) & ~vi_mode))(win_paste)
263 279
264 280 @Condition
265 281 def ebivim():
266 282 return shell.emacs_bindings_in_vi_insert_mode
267 283
268 284 focused_insert_vi = has_focus(DEFAULT_BUFFER) & vi_insert_mode
269 285
270 # Needed for to accept autosuggestions in vi insert mode
271 def _apply_autosuggest(event):
272 b = event.current_buffer
273 suggestion = b.suggestion
274 if suggestion is not None and suggestion.text:
275 b.insert_text(suggestion.text)
276 else:
277 nc.end_of_line(event)
278
279 286 @kb.add("end", filter=has_focus(DEFAULT_BUFFER) & (ebivim | ~vi_insert_mode))
280 287 def _(event):
281 288 _apply_autosuggest(event)
282 289
283 290 @kb.add("c-e", filter=focused_insert_vi & ebivim)
284 291 def _(event):
285 292 _apply_autosuggest(event)
286 293
287 294 @kb.add("c-f", filter=focused_insert_vi)
288 295 def _(event):
289 296 b = event.current_buffer
290 297 suggestion = b.suggestion
291 298 if suggestion:
292 299 b.insert_text(suggestion.text)
293 300 else:
294 301 nc.forward_char(event)
295 302
296 303 @kb.add("escape", "f", filter=focused_insert_vi & ebivim)
297 304 def _(event):
298 305 b = event.current_buffer
299 306 suggestion = b.suggestion
300 307 if suggestion:
301 308 t = re.split(r"(\S+\s+)", suggestion.text)
302 309 b.insert_text(next((x for x in t if x), ""))
303 310 else:
304 311 nc.forward_word(event)
305 312
306 313 # Simple Control keybindings
307 314 key_cmd_dict = {
308 315 "c-a": nc.beginning_of_line,
309 316 "c-b": nc.backward_char,
310 317 "c-k": nc.kill_line,
311 318 "c-w": nc.backward_kill_word,
312 319 "c-y": nc.yank,
313 320 "c-_": nc.undo,
314 321 }
315 322
316 323 for key, cmd in key_cmd_dict.items():
317 324 kb.add(key, filter=focused_insert_vi & ebivim)(cmd)
318 325
319 326 # Alt and Combo Control keybindings
320 327 keys_cmd_dict = {
321 328 # Control Combos
322 329 ("c-x", "c-e"): nc.edit_and_execute,
323 330 ("c-x", "e"): nc.edit_and_execute,
324 331 # Alt
325 332 ("escape", "b"): nc.backward_word,
326 333 ("escape", "c"): nc.capitalize_word,
327 334 ("escape", "d"): nc.kill_word,
328 335 ("escape", "h"): nc.backward_kill_word,
329 336 ("escape", "l"): nc.downcase_word,
330 337 ("escape", "u"): nc.uppercase_word,
331 338 ("escape", "y"): nc.yank_pop,
332 339 ("escape", "."): nc.yank_last_arg,
333 340 }
334 341
335 342 for keys, cmd in keys_cmd_dict.items():
336 343 kb.add(*keys, filter=focused_insert_vi & ebivim)(cmd)
337 344
338 345 def get_input_mode(self):
339 346 app = get_app()
340 347 app.ttimeoutlen = shell.ttimeoutlen
341 348 app.timeoutlen = shell.timeoutlen
342 349
343 350 return self._input_mode
344 351
345 352 def set_input_mode(self, mode):
346 353 shape = {InputMode.NAVIGATION: 2, InputMode.REPLACE: 4}.get(mode, 6)
347 354 cursor = "\x1b[{} q".format(shape)
348 355
349 356 sys.stdout.write(cursor)
350 357 sys.stdout.flush()
351 358
352 359 self._input_mode = mode
353 360
354 361 if shell.editing_mode == "vi" and shell.modal_cursor:
355 362 ViState._input_mode = InputMode.INSERT
356 363 ViState.input_mode = property(get_input_mode, set_input_mode)
357 364
358 365 return kb
359 366
360 367
361 368 def reformat_text_before_cursor(buffer, document, shell):
362 369 text = buffer.delete_before_cursor(len(document.text[:document.cursor_position]))
363 370 try:
364 371 formatted_text = shell.reformat_handler(text)
365 372 buffer.insert_text(formatted_text)
366 373 except Exception as e:
367 374 buffer.insert_text(text)
368 375
369 376
370 377 def newline_or_execute_outer(shell):
371 378
372 379 def newline_or_execute(event):
373 380 """When the user presses return, insert a newline or execute the code."""
374 381 b = event.current_buffer
375 382 d = b.document
376 383
377 384 if b.complete_state:
378 385 cc = b.complete_state.current_completion
379 386 if cc:
380 387 b.apply_completion(cc)
381 388 else:
382 389 b.cancel_completion()
383 390 return
384 391
385 392 # If there's only one line, treat it as if the cursor is at the end.
386 393 # See https://github.com/ipython/ipython/issues/10425
387 394 if d.line_count == 1:
388 395 check_text = d.text
389 396 else:
390 397 check_text = d.text[:d.cursor_position]
391 398 status, indent = shell.check_complete(check_text)
392 399
393 400 # if all we have after the cursor is whitespace: reformat current text
394 401 # before cursor
395 402 after_cursor = d.text[d.cursor_position:]
396 403 reformatted = False
397 404 if not after_cursor.strip():
398 405 reformat_text_before_cursor(b, d, shell)
399 406 reformatted = True
400 407 if not (d.on_last_line or
401 408 d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()
402 409 ):
403 410 if shell.autoindent:
404 411 b.insert_text('\n' + indent)
405 412 else:
406 413 b.insert_text('\n')
407 414 return
408 415
409 416 if (status != 'incomplete') and b.accept_handler:
410 417 if not reformatted:
411 418 reformat_text_before_cursor(b, d, shell)
412 419 b.validate_and_handle()
413 420 else:
414 421 if shell.autoindent:
415 422 b.insert_text('\n' + indent)
416 423 else:
417 424 b.insert_text('\n')
418 425 return newline_or_execute
419 426
420 427
421 428 def previous_history_or_previous_completion(event):
422 429 """
423 430 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
424 431
425 432 If completer is open this still select previous completion.
426 433 """
427 434 event.current_buffer.auto_up()
428 435
429 436
430 437 def next_history_or_next_completion(event):
431 438 """
432 439 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
433 440
434 441 If completer is open this still select next completion.
435 442 """
436 443 event.current_buffer.auto_down()
437 444
438 445
439 446 def dismiss_completion(event):
440 447 b = event.current_buffer
441 448 if b.complete_state:
442 449 b.cancel_completion()
443 450
444 451
445 452 def reset_buffer(event):
446 453 b = event.current_buffer
447 454 if b.complete_state:
448 455 b.cancel_completion()
449 456 else:
450 457 b.reset()
451 458
452 459
453 460 def reset_search_buffer(event):
454 461 if event.current_buffer.document.text:
455 462 event.current_buffer.reset()
456 463 else:
457 464 event.app.layout.focus(DEFAULT_BUFFER)
458 465
459 466 def suspend_to_bg(event):
460 467 event.app.suspend_to_background()
461 468
462 469 def quit(event):
463 470 """
464 471 On platforms that support SIGQUIT, send SIGQUIT to the current process.
465 472 On other platforms, just exit the process with a message.
466 473 """
467 474 sigquit = getattr(signal, "SIGQUIT", None)
468 475 if sigquit is not None:
469 476 os.kill(0, signal.SIGQUIT)
470 477 else:
471 478 sys.exit("Quit")
472 479
473 480 def indent_buffer(event):
474 481 event.current_buffer.insert_text(' ' * 4)
475 482
476 483 @undoc
477 484 def newline_with_copy_margin(event):
478 485 """
479 486 DEPRECATED since IPython 6.0
480 487
481 488 See :any:`newline_autoindent_outer` for a replacement.
482 489
483 490 Preserve margin and cursor position when using
484 491 Control-O to insert a newline in EMACS mode
485 492 """
486 493 warnings.warn("`newline_with_copy_margin(event)` is deprecated since IPython 6.0. "
487 494 "see `newline_autoindent_outer(shell)(event)` for a replacement.",
488 495 DeprecationWarning, stacklevel=2)
489 496
490 497 b = event.current_buffer
491 498 cursor_start_pos = b.document.cursor_position_col
492 499 b.newline(copy_margin=True)
493 500 b.cursor_up(count=1)
494 501 cursor_end_pos = b.document.cursor_position_col
495 502 if cursor_start_pos != cursor_end_pos:
496 503 pos_diff = cursor_start_pos - cursor_end_pos
497 504 b.cursor_right(count=pos_diff)
498 505
499 506 def newline_autoindent_outer(inputsplitter) -> Callable[..., None]:
500 507 """
501 508 Return a function suitable for inserting a indented newline after the cursor.
502 509
503 510 Fancier version of deprecated ``newline_with_copy_margin`` which should
504 511 compute the correct indentation of the inserted line. That is to say, indent
505 512 by 4 extra space after a function definition, class definition, context
506 513 manager... And dedent by 4 space after ``pass``, ``return``, ``raise ...``.
507 514 """
508 515
509 516 def newline_autoindent(event):
510 517 """insert a newline after the cursor indented appropriately."""
511 518 b = event.current_buffer
512 519 d = b.document
513 520
514 521 if b.complete_state:
515 522 b.cancel_completion()
516 523 text = d.text[:d.cursor_position] + '\n'
517 524 _, indent = inputsplitter.check_complete(text)
518 525 b.insert_text('\n' + (' ' * (indent or 0)), move_cursor=False)
519 526
520 527 return newline_autoindent
521 528
522 529
523 530 def open_input_in_editor(event):
524 531 event.app.current_buffer.open_in_editor()
525 532
526 533
527 534 if sys.platform == 'win32':
528 535 from IPython.core.error import TryNext
529 536 from IPython.lib.clipboard import (ClipboardEmpty,
530 537 win32_clipboard_get,
531 538 tkinter_clipboard_get)
532 539
533 540 @undoc
534 541 def win_paste(event):
535 542 try:
536 543 text = win32_clipboard_get()
537 544 except TryNext:
538 545 try:
539 546 text = tkinter_clipboard_get()
540 547 except (TryNext, ClipboardEmpty):
541 548 return
542 549 except ClipboardEmpty:
543 550 return
544 551 event.current_buffer.insert_text(text.replace("\t", " " * 4))
@@ -1,85 +1,87 b''
1 1 """
2 2 Un-targz and retargz a targz file to ensure reproducible build.
3 3
4 4 usage:
5 5
6 6 $ export SOURCE_DATE_EPOCH=$(date +%s)
7 # or
8 $ export SOURCE_DATE_EPOCH=$(git show -s --format=%ct HEAD)
7 9 ...
8 10 $ python retar.py <tarfile.gz>
9 11
10 12 The process of creating an sdist can be non-reproducible:
11 13 - directory created during the process get a mtime of the creation date;
12 14 - gziping files embed the timestamp of zip creation.
13 15
14 16 This will untar-retar; ensuring that all mtime > SOURCE_DATE_EPOCH will be set
15 17 equal to SOURCE_DATE_EPOCH.
16 18
17 19 """
18 20
19 21 import tarfile
20 22 import sys
21 23 import os
22 24 import gzip
23 25 import io
24 26
25 27 from pathlib import Path
26 28
27 29 if len(sys.argv) > 2:
28 30 raise ValueError("Too many arguments")
29 31
30 32
31 33 timestamp = int(os.environ["SOURCE_DATE_EPOCH"])
32 34
33 35 path = Path(sys.argv[1])
34 36 old_buf = io.BytesIO()
35 37 with open(path, "rb") as f:
36 38 old_buf.write(f.read())
37 39 old_buf.seek(0)
38 40 if path.name.endswith("gz"):
39 41 r_mode = "r:gz"
40 42 if path.name.endswith("bz2"):
41 43 r_mode = "r:bz2"
42 44 if path.name.endswith("xz"):
43 45 raise ValueError("XZ is deprecated but it's written nowhere")
44 46 old = tarfile.open(fileobj=old_buf, mode=r_mode)
45 47
46 48 buf = io.BytesIO()
47 49 new = tarfile.open(fileobj=buf, mode="w", format=tarfile.GNU_FORMAT)
48 50 for i, m in enumerate(old):
49 51 data = None
50 52 # mutation does not work, copy
51 53 if m.name.endswith('.DS_Store'):
52 54 continue
53 55 m2 = tarfile.TarInfo(m.name)
54 56 m2.mtime = min(timestamp, m.mtime)
55 57 m2.pax_headers["mtime"] = m2.mtime
56 58 m2.size = m.size
57 59 m2.type = m.type
58 60 m2.linkname = m.linkname
59 61 m2.mode = m.mode
60 62 if m.isdir():
61 63 new.addfile(m2)
62 64 else:
63 65 data = old.extractfile(m)
64 66 new.addfile(m2, data)
65 67 new.close()
66 68 old.close()
67 69
68 70 buf.seek(0)
69 71
70 72 if r_mode == "r:gz":
71 73 with open(path, "wb") as f:
72 74 with gzip.GzipFile("", "wb", fileobj=f, mtime=timestamp) as gzf:
73 75 gzf.write(buf.read())
74 76 elif r_mode == "r:bz2":
75 77 import bz2
76 78
77 79 with bz2.open(path, "wb") as f:
78 80 f.write(buf.read())
79 81
80 82 else:
81 83 assert False
82 84
83 85 # checks the archive is valid.
84 86 archive = tarfile.open(path)
85 87 names = archive.getnames()
General Comments 0
You need to be logged in to leave comments. Login now