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 )