Mercurial > hg
comparison mercurial/transaction.py @ 43076:2372284d9457
formatting: blacken the codebase
This is using my patch to black
(https://github.com/psf/black/pull/826) so we don't un-wrap collection
literals.
Done with:
hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S
# skip-blame mass-reformatting only
# no-check-commit reformats foo_bar functions
Differential Revision: https://phab.mercurial-scm.org/D6971
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:45:02 -0400 |
parents | a614f26d4897 |
children | 687b865b95ad |
comparison
equal
deleted
inserted
replaced
43075:57875cf423c9 | 43076:2372284d9457 |
---|---|
19 from . import ( | 19 from . import ( |
20 error, | 20 error, |
21 pycompat, | 21 pycompat, |
22 util, | 22 util, |
23 ) | 23 ) |
24 from .utils import ( | 24 from .utils import stringutil |
25 stringutil, | |
26 ) | |
27 | 25 |
28 version = 2 | 26 version = 2 |
29 | 27 |
30 # These are the file generators that should only be executed after the | 28 # These are the file generators that should only be executed after the |
31 # finalizers are done, since they rely on the output of the finalizers (like | 29 # finalizers are done, since they rely on the output of the finalizers (like |
32 # the changelog having been written). | 30 # the changelog having been written). |
33 postfinalizegenerators = { | 31 postfinalizegenerators = {'bookmarks', 'dirstate'} |
34 'bookmarks', | 32 |
35 'dirstate' | 33 gengroupall = 'all' |
36 } | 34 gengroupprefinalize = 'prefinalize' |
37 | 35 gengrouppostfinalize = 'postfinalize' |
38 gengroupall='all' | 36 |
39 gengroupprefinalize='prefinalize' | |
40 gengrouppostfinalize='postfinalize' | |
41 | 37 |
42 def active(func): | 38 def active(func): |
43 def _active(self, *args, **kwds): | 39 def _active(self, *args, **kwds): |
44 if self._count == 0: | 40 if self._count == 0: |
45 raise error.Abort(_( | 41 raise error.Abort( |
46 'cannot use transaction when it is already committed/aborted')) | 42 _('cannot use transaction when it is already committed/aborted') |
43 ) | |
47 return func(self, *args, **kwds) | 44 return func(self, *args, **kwds) |
45 | |
48 return _active | 46 return _active |
49 | 47 |
50 def _playback(journal, report, opener, vfsmap, entries, backupentries, | 48 |
51 unlink=True, checkambigfiles=None): | 49 def _playback( |
50 journal, | |
51 report, | |
52 opener, | |
53 vfsmap, | |
54 entries, | |
55 backupentries, | |
56 unlink=True, | |
57 checkambigfiles=None, | |
58 ): | |
52 for f, o, _ignore in entries: | 59 for f, o, _ignore in entries: |
53 if o or not unlink: | 60 if o or not unlink: |
54 checkambig = checkambigfiles and (f, '') in checkambigfiles | 61 checkambig = checkambigfiles and (f, '') in checkambigfiles |
55 try: | 62 try: |
56 fp = opener(f, 'a', checkambig=checkambig) | 63 fp = opener(f, 'a', checkambig=checkambig) |
57 if fp.tell() < o: | 64 if fp.tell() < o: |
58 raise error.Abort(_( | 65 raise error.Abort( |
66 _( | |
59 "attempted to truncate %s to %d bytes, but it was " | 67 "attempted to truncate %s to %d bytes, but it was " |
60 "already %d bytes\n") % (f, o, fp.tell())) | 68 "already %d bytes\n" |
69 ) | |
70 % (f, o, fp.tell()) | |
71 ) | |
61 fp.truncate(o) | 72 fp.truncate(o) |
62 fp.close() | 73 fp.close() |
63 except IOError: | 74 except IOError: |
64 report(_("failed to truncate %s\n") % f) | 75 report(_("failed to truncate %s\n") % f) |
65 raise | 76 raise |
71 raise | 82 raise |
72 | 83 |
73 backupfiles = [] | 84 backupfiles = [] |
74 for l, f, b, c in backupentries: | 85 for l, f, b, c in backupentries: |
75 if l not in vfsmap and c: | 86 if l not in vfsmap and c: |
76 report("couldn't handle %s: unknown cache location %s\n" | 87 report("couldn't handle %s: unknown cache location %s\n" % (b, l)) |
77 % (b, l)) | |
78 vfs = vfsmap[l] | 88 vfs = vfsmap[l] |
79 try: | 89 try: |
80 if f and b: | 90 if f and b: |
81 filepath = vfs.join(f) | 91 filepath = vfs.join(f) |
82 backuppath = vfs.join(b) | 92 backuppath = vfs.join(b) |
107 opener.unlink(f) | 117 opener.unlink(f) |
108 except (IOError, OSError, error.Abort): | 118 except (IOError, OSError, error.Abort): |
109 # only pure backup file remains, it is sage to ignore any error | 119 # only pure backup file remains, it is sage to ignore any error |
110 pass | 120 pass |
111 | 121 |
122 | |
112 class transaction(util.transactional): | 123 class transaction(util.transactional): |
113 def __init__(self, report, opener, vfsmap, journalname, undoname=None, | 124 def __init__( |
114 after=None, createmode=None, validator=None, releasefn=None, | 125 self, |
115 checkambigfiles=None, name=r'<unnamed>'): | 126 report, |
127 opener, | |
128 vfsmap, | |
129 journalname, | |
130 undoname=None, | |
131 after=None, | |
132 createmode=None, | |
133 validator=None, | |
134 releasefn=None, | |
135 checkambigfiles=None, | |
136 name=r'<unnamed>', | |
137 ): | |
116 """Begin a new transaction | 138 """Begin a new transaction |
117 | 139 |
118 Begins a new transaction that allows rolling back writes in the event of | 140 Begins a new transaction that allows rolling back writes in the event of |
119 an exception. | 141 an exception. |
120 | 142 |
195 # holds callbacks to call during abort | 217 # holds callbacks to call during abort |
196 self._abortcallback = {} | 218 self._abortcallback = {} |
197 | 219 |
198 def __repr__(self): | 220 def __repr__(self): |
199 name = r'/'.join(self._names) | 221 name = r'/'.join(self._names) |
200 return (r'<transaction name=%s, count=%d, usages=%d>' % | 222 return r'<transaction name=%s, count=%d, usages=%d>' % ( |
201 (name, self._count, self._usages)) | 223 name, |
224 self._count, | |
225 self._usages, | |
226 ) | |
202 | 227 |
203 def __del__(self): | 228 def __del__(self): |
204 if self._journal: | 229 if self._journal: |
205 self._abort() | 230 self._abort() |
206 | 231 |
288 failure and success). | 313 failure and success). |
289 """ | 314 """ |
290 self._addbackupentry((location, '', tmpfile, False)) | 315 self._addbackupentry((location, '', tmpfile, False)) |
291 | 316 |
292 @active | 317 @active |
293 def addfilegenerator(self, genid, filenames, genfunc, order=0, | 318 def addfilegenerator(self, genid, filenames, genfunc, order=0, location=''): |
294 location=''): | |
295 """add a function to generates some files at transaction commit | 319 """add a function to generates some files at transaction commit |
296 | 320 |
297 The `genfunc` argument is a function capable of generating proper | 321 The `genfunc` argument is a function capable of generating proper |
298 content of each entry in the `filename` tuple. | 322 content of each entry in the `filename` tuple. |
299 | 323 |
332 order, filenames, genfunc, location = entry | 356 order, filenames, genfunc, location = entry |
333 | 357 |
334 # for generation at closing, check if it's before or after finalize | 358 # for generation at closing, check if it's before or after finalize |
335 postfinalize = group == gengrouppostfinalize | 359 postfinalize = group == gengrouppostfinalize |
336 if ( | 360 if ( |
337 group != gengroupall | 361 group != gengroupall |
338 and (id in postfinalizegenerators) != postfinalize | 362 and (id in postfinalizegenerators) != postfinalize |
339 ): | 363 ): |
340 continue | 364 continue |
341 | 365 |
342 vfs = self._vfsmap[location] | 366 vfs = self._vfsmap[location] |
343 files = [] | 367 files = [] |
348 self.registertmp(name, location=location) | 372 self.registertmp(name, location=location) |
349 checkambig = False | 373 checkambig = False |
350 else: | 374 else: |
351 self.addbackup(name, location=location) | 375 self.addbackup(name, location=location) |
352 checkambig = (name, location) in self._checkambigfiles | 376 checkambig = (name, location) in self._checkambigfiles |
353 files.append(vfs(name, 'w', atomictemp=True, | 377 files.append( |
354 checkambig=checkambig)) | 378 vfs(name, 'w', atomictemp=True, checkambig=checkambig) |
379 ) | |
355 genfunc(*files) | 380 genfunc(*files) |
356 for f in files: | 381 for f in files: |
357 f.close() | 382 f.close() |
358 # skip discard() loop since we're sure no open file remains | 383 # skip discard() loop since we're sure no open file remains |
359 del files[:] | 384 del files[:] |
467 @active | 492 @active |
468 def close(self): | 493 def close(self): |
469 '''commit the transaction''' | 494 '''commit the transaction''' |
470 if self._count == 1: | 495 if self._count == 1: |
471 self._validator(self) # will raise exception if needed | 496 self._validator(self) # will raise exception if needed |
472 self._validator = None # Help prevent cycles. | 497 self._validator = None # Help prevent cycles. |
473 self._generatefiles(group=gengroupprefinalize) | 498 self._generatefiles(group=gengroupprefinalize) |
474 categories = sorted(self._finalizecallback) | 499 categories = sorted(self._finalizecallback) |
475 for cat in categories: | 500 for cat in categories: |
476 self._finalizecallback[cat](self) | 501 self._finalizecallback[cat](self) |
477 # Prevent double usage and help clear cycles. | 502 # Prevent double usage and help clear cycles. |
484 self._file.close() | 509 self._file.close() |
485 self._backupsfile.close() | 510 self._backupsfile.close() |
486 # cleanup temporary files | 511 # cleanup temporary files |
487 for l, f, b, c in self._backupentries: | 512 for l, f, b, c in self._backupentries: |
488 if l not in self._vfsmap and c: | 513 if l not in self._vfsmap and c: |
489 self._report("couldn't remove %s: unknown cache location %s\n" | 514 self._report( |
490 % (b, l)) | 515 "couldn't remove %s: unknown cache location %s\n" % (b, l) |
516 ) | |
491 continue | 517 continue |
492 vfs = self._vfsmap[l] | 518 vfs = self._vfsmap[l] |
493 if not f and b and vfs.exists(b): | 519 if not f and b and vfs.exists(b): |
494 try: | 520 try: |
495 vfs.unlink(b) | 521 vfs.unlink(b) |
496 except (IOError, OSError, error.Abort) as inst: | 522 except (IOError, OSError, error.Abort) as inst: |
497 if not c: | 523 if not c: |
498 raise | 524 raise |
499 # Abort may be raise by read only opener | 525 # Abort may be raise by read only opener |
500 self._report("couldn't remove %s: %s\n" | 526 self._report( |
501 % (vfs.join(b), inst)) | 527 "couldn't remove %s: %s\n" % (vfs.join(b), inst) |
528 ) | |
502 self._entries = [] | 529 self._entries = [] |
503 self._writeundo() | 530 self._writeundo() |
504 if self._after: | 531 if self._after: |
505 self._after() | 532 self._after() |
506 self._after = None # Help prevent cycles. | 533 self._after = None # Help prevent cycles. |
507 if self._opener.isfile(self._backupjournal): | 534 if self._opener.isfile(self._backupjournal): |
508 self._opener.unlink(self._backupjournal) | 535 self._opener.unlink(self._backupjournal) |
509 if self._opener.isfile(self._journal): | 536 if self._opener.isfile(self._journal): |
510 self._opener.unlink(self._journal) | 537 self._opener.unlink(self._journal) |
511 for l, _f, b, c in self._backupentries: | 538 for l, _f, b, c in self._backupentries: |
512 if l not in self._vfsmap and c: | 539 if l not in self._vfsmap and c: |
513 self._report("couldn't remove %s: unknown cache location" | 540 self._report( |
514 "%s\n" % (b, l)) | 541 "couldn't remove %s: unknown cache location" "%s\n" % (b, l) |
542 ) | |
515 continue | 543 continue |
516 vfs = self._vfsmap[l] | 544 vfs = self._vfsmap[l] |
517 if b and vfs.exists(b): | 545 if b and vfs.exists(b): |
518 try: | 546 try: |
519 vfs.unlink(b) | 547 vfs.unlink(b) |
520 except (IOError, OSError, error.Abort) as inst: | 548 except (IOError, OSError, error.Abort) as inst: |
521 if not c: | 549 if not c: |
522 raise | 550 raise |
523 # Abort may be raise by read only opener | 551 # Abort may be raise by read only opener |
524 self._report("couldn't remove %s: %s\n" | 552 self._report( |
525 % (vfs.join(b), inst)) | 553 "couldn't remove %s: %s\n" % (vfs.join(b), inst) |
554 ) | |
526 self._backupentries = [] | 555 self._backupentries = [] |
527 self._journal = None | 556 self._journal = None |
528 | 557 |
529 self._releasefn(self, True) # notify success of closing transaction | 558 self._releasefn(self, True) # notify success of closing transaction |
530 self._releasefn = None # Help prevent cycles. | 559 self._releasefn = None # Help prevent cycles. |
531 | 560 |
532 # run post close action | 561 # run post close action |
533 categories = sorted(self._postclosecallback) | 562 categories = sorted(self._postclosecallback) |
534 for cat in categories: | 563 for cat in categories: |
535 self._postclosecallback[cat](self) | 564 self._postclosecallback[cat](self) |
545 | 574 |
546 def _writeundo(self): | 575 def _writeundo(self): |
547 """write transaction data for possible future undo call""" | 576 """write transaction data for possible future undo call""" |
548 if self._undoname is None: | 577 if self._undoname is None: |
549 return | 578 return |
550 undobackupfile = self._opener.open("%s.backupfiles" % self._undoname, | 579 undobackupfile = self._opener.open( |
551 'w') | 580 "%s.backupfiles" % self._undoname, 'w' |
581 ) | |
552 undobackupfile.write('%d\n' % version) | 582 undobackupfile.write('%d\n' % version) |
553 for l, f, b, c in self._backupentries: | 583 for l, f, b, c in self._backupentries: |
554 if not f: # temporary file | 584 if not f: # temporary file |
555 continue | 585 continue |
556 if not b: | 586 if not b: |
557 u = '' | 587 u = '' |
558 else: | 588 else: |
559 if l not in self._vfsmap and c: | 589 if l not in self._vfsmap and c: |
560 self._report("couldn't remove %s: unknown cache location" | 590 self._report( |
561 "%s\n" % (b, l)) | 591 "couldn't remove %s: unknown cache location" |
592 "%s\n" % (b, l) | |
593 ) | |
562 continue | 594 continue |
563 vfs = self._vfsmap[l] | 595 vfs = self._vfsmap[l] |
564 base, name = vfs.split(b) | 596 base, name = vfs.split(b) |
565 assert name.startswith(self._journal), name | 597 assert name.startswith(self._journal), name |
566 uname = name.replace(self._journal, self._undoname, 1) | 598 uname = name.replace(self._journal, self._undoname, 1) |
567 u = vfs.reljoin(base, uname) | 599 u = vfs.reljoin(base, uname) |
568 util.copyfile(vfs.join(b), vfs.join(u), hardlink=True) | 600 util.copyfile(vfs.join(b), vfs.join(u), hardlink=True) |
569 undobackupfile.write("%s\0%s\0%s\0%d\n" % (l, f, u, c)) | 601 undobackupfile.write("%s\0%s\0%s\0%d\n" % (l, f, u, c)) |
570 undobackupfile.close() | 602 undobackupfile.close() |
571 | 603 |
572 | |
573 def _abort(self): | 604 def _abort(self): |
574 self._count = 0 | 605 self._count = 0 |
575 self._usages = 0 | 606 self._usages = 0 |
576 self._file.close() | 607 self._file.close() |
577 self._backupsfile.close() | 608 self._backupsfile.close() |
589 try: | 620 try: |
590 for cat in sorted(self._abortcallback): | 621 for cat in sorted(self._abortcallback): |
591 self._abortcallback[cat](self) | 622 self._abortcallback[cat](self) |
592 # Prevent double usage and help clear cycles. | 623 # Prevent double usage and help clear cycles. |
593 self._abortcallback = None | 624 self._abortcallback = None |
594 _playback(self._journal, self._report, self._opener, | 625 _playback( |
595 self._vfsmap, self._entries, self._backupentries, | 626 self._journal, |
596 False, checkambigfiles=self._checkambigfiles) | 627 self._report, |
628 self._opener, | |
629 self._vfsmap, | |
630 self._entries, | |
631 self._backupentries, | |
632 False, | |
633 checkambigfiles=self._checkambigfiles, | |
634 ) | |
597 self._report(_("rollback completed\n")) | 635 self._report(_("rollback completed\n")) |
598 except BaseException as exc: | 636 except BaseException as exc: |
599 self._report(_("rollback failed - please run hg recover\n")) | 637 self._report(_("rollback failed - please run hg recover\n")) |
600 self._report(_("(failure reason: %s)\n") | 638 self._report( |
601 % stringutil.forcebytestr(exc)) | 639 _("(failure reason: %s)\n") % stringutil.forcebytestr(exc) |
640 ) | |
602 finally: | 641 finally: |
603 self._journal = None | 642 self._journal = None |
604 self._releasefn(self, False) # notify failure of transaction | 643 self._releasefn(self, False) # notify failure of transaction |
605 self._releasefn = None # Help prevent cycles. | 644 self._releasefn = None # Help prevent cycles. |
645 | |
606 | 646 |
607 def rollback(opener, vfsmap, file, report, checkambigfiles=None): | 647 def rollback(opener, vfsmap, file, report, checkambigfiles=None): |
608 """Rolls back the transaction contained in the given file | 648 """Rolls back the transaction contained in the given file |
609 | 649 |
610 Reads the entries in the specified file, and the corresponding | 650 Reads the entries in the specified file, and the corresponding |
629 for l in lines: | 669 for l in lines: |
630 try: | 670 try: |
631 f, o = l.split('\0') | 671 f, o = l.split('\0') |
632 entries.append((f, int(o), None)) | 672 entries.append((f, int(o), None)) |
633 except ValueError: | 673 except ValueError: |
634 report( | 674 report(_("couldn't read journal entry %r!\n") % pycompat.bytestr(l)) |
635 _("couldn't read journal entry %r!\n") % pycompat.bytestr(l)) | |
636 | 675 |
637 backupjournal = "%s.backupfiles" % file | 676 backupjournal = "%s.backupfiles" % file |
638 if opener.exists(backupjournal): | 677 if opener.exists(backupjournal): |
639 fp = opener.open(backupjournal) | 678 fp = opener.open(backupjournal) |
640 lines = fp.readlines() | 679 lines = fp.readlines() |
646 # Shave off the trailing newline | 685 # Shave off the trailing newline |
647 line = line[:-1] | 686 line = line[:-1] |
648 l, f, b, c = line.split('\0') | 687 l, f, b, c = line.split('\0') |
649 backupentries.append((l, f, b, bool(c))) | 688 backupentries.append((l, f, b, bool(c))) |
650 else: | 689 else: |
651 report(_("journal was created by a different version of " | 690 report( |
652 "Mercurial\n")) | 691 _( |
653 | 692 "journal was created by a different version of " |
654 _playback(file, report, opener, vfsmap, entries, backupentries, | 693 "Mercurial\n" |
655 checkambigfiles=checkambigfiles) | 694 ) |
695 ) | |
696 | |
697 _playback( | |
698 file, | |
699 report, | |
700 opener, | |
701 vfsmap, | |
702 entries, | |
703 backupentries, | |
704 checkambigfiles=checkambigfiles, | |
705 ) |