Merge lp:~jelmer/qbrz/context-managers into lp:qbrz

Proposed by Jelmer Vernooij
Status: Merged
Approved by: Jelmer Vernooij
Approved revision: 1653
Merged at revision: 1650
Proposed branch: lp:~jelmer/qbrz/context-managers
Merge into: lp:qbrz
Diff against target: 1586 lines (+198/-535)
25 files modified
extras/build_pot.py (+2/-8)
extras/import_po.py (+1/-4)
lib/annotate.py (+3/-12)
lib/browse.py (+29/-33)
lib/cat.py (+3/-14)
lib/cleanup.py (+0/-184)
lib/commands.py (+4/-14)
lib/commit.py (+2/-5)
lib/commit_data.py (+2/-8)
lib/diff.py (+20/-39)
lib/diff_arg.py (+8/-11)
lib/diffview.py (+1/-1)
lib/diffwindow.py (+7/-11)
lib/extra/isignored.py (+1/-1)
lib/extra/isversioned.py (+1/-1)
lib/lazycachedrevloader.py (+14/-17)
lib/log.py (+4/-13)
lib/loggraphviz.py (+4/-17)
lib/spellcheck_enchant.py (+2/-2)
lib/subprocess.py (+2/-8)
lib/tests/test_extdiff.py (+4/-4)
lib/tests/test_guidebar.py (+1/-1)
lib/treewidget.py (+7/-32)
lib/widgets/shelve.py (+69/-81)
lib/widgets/shelvelist.py (+7/-14)
To merge this branch: bzr merge lp:~jelmer/qbrz/context-managers
Reviewer Review Type Date Requested Status
Jelmer Vernooij Approve
Review via email: mp+426396@code.launchpad.net

Commit message

Use more context managers.

Description of the change

Use more context managers.

To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) :
review: Approve
Revision history for this message
Aleksandr Smyshliaev (a1s) :
Revision history for this message
Aleksandr Smyshliaev (a1s) :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'extras/build_pot.py'
2--- extras/build_pot.py 2020-07-15 09:34:25 +0000
3+++ extras/build_pot.py 2022-07-06 15:37:54 +0000
4@@ -63,18 +63,12 @@
5 raise DistutilsOptionError("You can't use options --lang=XXX and --no-lang in the same time.")
6
7 def _force_LF(self, src, dst=None):
8- f = open(src, 'rU')
9- try:
10+ with open(src, 'rU') as f:
11 content = f.read()
12- finally:
13- f.close()
14 if dst is None:
15 dst = src
16- f = open(dst, 'wb')
17- try:
18+ with open(dst, 'wb') as f:
19 f.write(content)
20- finally:
21- f.close()
22
23 def run(self):
24 """Run xgettext for project sources"""
25
26=== modified file 'extras/import_po.py'
27--- extras/import_po.py 2019-10-27 14:48:19 +0000
28+++ extras/import_po.py 2022-07-06 15:37:54 +0000
29@@ -80,11 +80,8 @@
30 for n, fn in entries:
31 log.info(' %s -> %s' % (n, fn))
32 ft = t.extractfile(n)
33- fd = open(fn, 'wb')
34- try:
35+ with open(fn, 'wb') as fd:
36 fd.write(ft.read())
37- finally:
38- fd.close()
39 if pot_file:
40 if find_executable('msginit') is None:
41 log.warn("GNU gettext msginit utility not found!")
42
43=== modified file 'lib/annotate.py'
44--- lib/annotate.py 2022-01-07 14:04:10 +0000
45+++ lib/annotate.py 2022-07-06 15:37:54 +0000
46@@ -410,12 +410,9 @@
47 QtCore.QCoreApplication.processEvents()
48 self.encoding = get_set_encoding(self.encoding, self.branch)
49 self.encoding_selector.encoding = self.encoding
50- self.branch.lock_read()
51- try:
52+ with self.branch.lock_read():
53 self.set_annotate_title()
54 self.annotate(self.annotate_tree, self.fileId, self.path)
55- finally:
56- self.branch.unlock()
57 finally:
58 self.throbber.hide()
59 QtCore.QTimer.singleShot(1, self._activate_line)
60@@ -634,8 +631,7 @@
61 def set_annotate_revision(self):
62 self.throbber.show()
63 try:
64- self.branch.lock_read()
65- try:
66+ with self.branch.lock_read():
67 revid = self.log_list.currentIndex().data(logmodel.RevIdRole)
68 if revid.startswith(CURRENT_REVISION):
69 rev = cached_revisions[revid]
70@@ -646,8 +642,6 @@
71 self.set_annotate_title()
72 self.processEvents()
73 self.annotate(self.annotate_tree, self.fileId, self.path)
74- finally:
75- self.branch.unlock()
76 finally:
77 self.throbber.hide()
78
79@@ -657,11 +651,8 @@
80 get_set_encoding(encoding, self.branch)
81 self.throbber.show()
82 try:
83- self.branch.lock_read()
84- try:
85+ with self.branch.lock_read():
86 self.annotate(self.annotate_tree, self.fileId, self.path)
87- finally:
88- self.branch.unlock()
89 finally:
90 self.throbber.hide()
91
92
93=== modified file 'lib/browse.py'
94--- lib/browse.py 2021-01-08 13:56:34 +0000
95+++ lib/browse.py 2022-07-06 15:37:54 +0000
96@@ -158,49 +158,45 @@
97 self.diffbuttons,
98 self.refresh_button)
99 state = self.file_tree.get_state()
100- if text=="wt:":
101+ if text == "wt:":
102 self.tree = self.workingtree
103- self.tree.lock_read()
104+ with self.tree.lock_read():
105 try:
106 self.file_tree.set_tree(self.workingtree, self.branch)
107 self.file_tree.restore_state(state)
108- finally:
109- self.tree.unlock()
110 for button in buttons:
111 button.setEnabled(True)
112 else:
113 branch = self.branch
114- branch.lock_read()
115- self.processEvents()
116-
117- for button in buttons:
118- button.setEnabled(False)
119- fmodel = self.file_tree.tree_filter_model
120- fmodel.setFilter(fmodel.UNCHANGED, True)
121- self.filter_menu.set_filters(fmodel.filters)
122-
123- try:
124- if revision_id is None:
125- text = revspec.spec or ''
126- if revspec.in_branch == revspec.in_history:
127- args = [branch]
128- else:
129- args = [branch, False]
130-
131- revision_id = revspec.in_branch(*args).rev_id
132-
133- self.revision_id = revision_id
134- self.tree = branch.repository.revision_tree(revision_id)
135+ with branch.lock_read():
136 self.processEvents()
137- self.file_tree.set_tree(self.tree, self.branch)
138- self.file_tree.restore_state(state)
139- if self.revno_map is None:
140+
141+ for button in buttons:
142+ button.setEnabled(False)
143+ fmodel = self.file_tree.tree_filter_model
144+ fmodel.setFilter(fmodel.UNCHANGED, True)
145+ self.filter_menu.set_filters(fmodel.filters)
146+
147+ try:
148+ if revision_id is None:
149+ text = revspec.spec or ''
150+ if revspec.in_branch == revspec.in_history:
151+ args = [branch]
152+ else:
153+ args = [branch, False]
154+
155+ revision_id = revspec.in_branch(*args).rev_id
156+
157+ self.revision_id = revision_id
158+ self.tree = branch.repository.revision_tree(revision_id)
159 self.processEvents()
160- # XXX make this operation lazy? how?
161- self.revno_map = self.branch.get_revision_id_to_revno_map()
162- self.file_tree.tree_model.set_revno_map(self.revno_map)
163- finally:
164- branch.unlock()
165+ self.file_tree.set_tree(self.tree, self.branch)
166+ self.file_tree.restore_state(state)
167+ if self.revno_map is None:
168+ self.processEvents()
169+ # XXX make this operation lazy? how?
170+ self.revno_map = self.branch.get_revision_id_to_revno_map()
171+ self.file_tree.tree_model.set_revno_map(self.revno_map)
172 self.revision_edit.setText(text)
173 finally:
174 self.throbber.hide()
175
176=== modified file 'lib/cat.py'
177--- lib/cat.py 2021-01-08 08:16:13 +0000
178+++ lib/cat.py 2022-07-06 15:37:54 +0000
179@@ -142,8 +142,7 @@
180 "%r is not present in revision %s" % (
181 self.filename, self.tree.get_revision_id()))
182
183- self.tree.lock_read()
184- try:
185+ with self.tree.lock_read():
186 kind = self.tree.kind(self.filename)
187 if kind == 'file':
188 text = self.tree.get_file_text(self.filename)
189@@ -151,8 +150,6 @@
190 text = self.tree.get_symlink_target(self.filename)
191 else:
192 text = ''
193- finally:
194- self.tree.unlock()
195 self.processEvents()
196
197 self.text = text
198@@ -304,11 +301,8 @@
199 kind = osutils.file_kind(self.filename)
200 text = ''
201 if kind == 'file':
202- f = open(self.filename, 'rb')
203- try:
204+ with open(self.filename, 'rb') as f:
205 text = f.read()
206- finally:
207- f.close()
208 elif kind == 'symlink':
209 text = os.readlink(self.filename)
210 self.text = text
211@@ -337,13 +331,8 @@
212 os.makedirs(qdir)
213 basename = os.path.basename(relpath)
214 fname = os.path.join(qdir, basename)
215- f = open(fname, 'wb')
216- tree.lock_read()
217- try:
218+ with open(fname, 'wb') as f, tree.lock_read():
219 f.write(tree.get_file_text(relpath))
220- finally:
221- tree.unlock()
222- f.close()
223 # open it
224 url = QtCore.QUrl.fromLocalFile(fname)
225 result = QtGui.QDesktopServices.openUrl(url)
226
227=== removed file 'lib/cleanup.py'
228--- lib/cleanup.py 2020-07-14 15:58:13 +0000
229+++ lib/cleanup.py 1970-01-01 00:00:00 +0000
230@@ -1,184 +0,0 @@
231-# Copyright (C) 2009, 2010 Canonical Ltd
232-#
233-# This program is free software; you can redistribute it and/or modify
234-# it under the terms of the GNU General Public License as published by
235-# the Free Software Foundation; either version 2 of the License, or
236-# (at your option) any later version.
237-#
238-# This program is distributed in the hope that it will be useful,
239-# but WITHOUT ANY WARRANTY; without even the implied warranty of
240-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
241-# GNU General Public License for more details.
242-#
243-# You should have received a copy of the GNU General Public License
244-# along with this program; if not, write to the Free Software
245-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
246-
247-"""Helpers for managing cleanup functions and the errors they might raise.
248-
249-The usual way to run cleanup code in Python is::
250-
251- try:
252- do_something()
253- finally:
254- cleanup_something()
255-
256-However if both `do_something` and `cleanup_something` raise an exception
257-Python will forget the original exception and propagate the one from
258-cleanup_something. Unfortunately, this is almost always much less useful than
259-the original exception.
260-
261-If you want to be certain that the first, and only the first, error is raised,
262-then use::
263-
264- operation = OperationWithCleanups(do_something)
265- operation.add_cleanup(cleanup_something)
266- operation.run_simple()
267-
268-This is more inconvenient (because you need to make every try block a
269-function), but will ensure that the first error encountered is the one raised,
270-while also ensuring all cleanups are run. See OperationWithCleanups for more
271-details.
272-"""
273-
274-from __future__ import absolute_import
275-
276-from collections import deque
277-# import sys
278-# from . import (
279-# debug,
280-# trace,
281-# )
282-
283-from breezy import (
284- debug,
285- trace,
286- )
287-
288-
289-def _log_cleanup_error(exc):
290- trace.mutter('Cleanup failed:')
291- trace.log_exception_quietly()
292- if 'cleanup' in debug.debug_flags:
293- trace.warning('brz: warning: Cleanup failed: %s', exc)
294-
295-
296-def _run_cleanup(func, *args, **kwargs):
297- """Run func(*args, **kwargs), logging but not propagating any error it
298- raises.
299-
300- :returns: True if func raised no errors, else False.
301- """
302- try:
303- func(*args, **kwargs)
304- except KeyboardInterrupt:
305- raise
306- except Exception as exc:
307- _log_cleanup_error(exc)
308- return False
309- return True
310-
311-
312-def _run_cleanups(funcs):
313- """Run a series of cleanup functions."""
314- for func, args, kwargs in funcs:
315- _run_cleanup(func, *args, **kwargs)
316-
317-
318-class ObjectWithCleanups(object):
319- """A mixin for objects that hold a cleanup list.
320-
321- Subclass or client code can call add_cleanup and then later `cleanup_now`.
322- """
323- def __init__(self):
324- self.cleanups = deque()
325-
326- def add_cleanup(self, cleanup_func, *args, **kwargs):
327- """Add a cleanup to run.
328-
329- Cleanups may be added at any time.
330- Cleanups will be executed in LIFO order.
331- """
332- self.cleanups.appendleft((cleanup_func, args, kwargs))
333-
334- def cleanup_now(self):
335- _run_cleanups(self.cleanups)
336- self.cleanups.clear()
337-
338-
339-class OperationWithCleanups(ObjectWithCleanups):
340- """A way to run some code with a dynamic cleanup list.
341-
342- This provides a way to add cleanups while the function-with-cleanups is
343- running.
344-
345- Typical use::
346-
347- operation = OperationWithCleanups(some_func)
348- operation.run(args...)
349-
350- where `some_func` is::
351-
352- def some_func(operation, args, ...):
353- do_something()
354- operation.add_cleanup(something)
355- # etc
356-
357- Note that the first argument passed to `some_func` will be the
358- OperationWithCleanups object. To invoke `some_func` without that, use
359- `run_simple` instead of `run`.
360- """
361-
362- def __init__(self, func):
363- super(OperationWithCleanups, self).__init__()
364- self.func = func
365-
366- def run(self, *args, **kwargs):
367- return _do_with_cleanups(self.cleanups, self.func, self, *args, **kwargs)
368-
369- def run_simple(self, *args, **kwargs):
370- return _do_with_cleanups(self.cleanups, self.func, *args, **kwargs)
371-
372-
373-def _do_with_cleanups(cleanup_funcs, func, *args, **kwargs):
374- """Run `func`, then call all the cleanup_funcs.
375-
376- All the cleanup_funcs are guaranteed to be run. The first exception raised
377- by func or any of the cleanup_funcs is the one that will be propagted by
378- this function (subsequent errors are caught and logged).
379-
380- Conceptually similar to::
381-
382- try:
383- return func(*args, **kwargs)
384- finally:
385- for cleanup, cargs, ckwargs in cleanup_funcs:
386- cleanup(*cargs, **ckwargs)
387-
388- It avoids several problems with using try/finally directly:
389- * an exception from func will not be obscured by a subsequent exception
390- from a cleanup.
391- * an exception from a cleanup will not prevent other cleanups from
392- running (but the first exception encountered is still the one
393- propagated).
394-
395- Unike `_run_cleanup`, `_do_with_cleanups` can propagate an exception from a
396- cleanup, but only if there is no exception from func.
397- """
398- try:
399- result = func(*args, **kwargs)
400- except:
401- # We have an exception from func already, so suppress cleanup errors.
402- _run_cleanups(cleanup_funcs)
403- raise
404- # No exception from func, so allow first cleanup error to propgate.
405- pending_cleanups = iter(cleanup_funcs)
406- try:
407- for cleanup, c_args, c_kwargs in pending_cleanups:
408- cleanup(*c_args, **c_kwargs)
409- except:
410- # Still run the remaining cleanups but suppress any further errors.
411- _run_cleanups(pending_cleanups)
412- raise
413- # No error, so we can return the result
414- return result
415
416=== modified file 'lib/commands.py'
417--- lib/commands.py 2021-08-05 11:12:43 +0000
418+++ lib/commands.py 2022-07-06 15:37:54 +0000
419@@ -367,11 +367,8 @@
420 if message is not None and file:
421 raise errors.BzrCommandError("please specify either --message or --file")
422 if file:
423- f = open(file)
424- try:
425+ with open(file) as f:
426 message = f.read().decode(file_encoding or osutils.get_user_encoding())
427- finally:
428- f.close()
429 tree, selected_list = WorkingTree.open_containing_paths(selected_list)
430 if selected_list == ['']:
431 selected_list = None
432@@ -403,18 +400,11 @@
433 takes_options.append('change')
434 aliases = ['qdi']
435
436- def get_diff_window_args(self, processEvents, add_cleanup):
437- # RJL if you get a ``AttributeError: 'function' object has no attribute 'enter_context'``
438- # error, or something similar, use something like:
439- #
440- # exit_stack = contextlib.ExitStack()
441- #
442- # and pass that as add_cleanup
443- #
444+ def get_diff_window_args(self, processEvents, es):
445 args = {}
446 (args["old_tree"], args["new_tree"],
447 args["old_branch"], args["new_branch"],
448- args["specific_files"], _) = get_trees_and_branches_to_diff_locked(self.file_list, self.revision, self.old, self.new, add_cleanup)
449+ args["specific_files"], _) = get_trees_and_branches_to_diff_locked(self.file_list, self.revision, self.old, self.new, es)
450 args["ignore_whitespace"] = self.ignore_whitespace
451 return args
452
453@@ -716,7 +706,7 @@
454 del kw['encoding']
455 return breezy.builtins.cmd_merge.run(self, *args, **kw)
456
457- def get_diff_window_args(self, processEvents, add_cleanup):
458+ def get_diff_window_args(self, processEvents, es):
459 tree_merger = self.merger.make_merger()
460 self.tt = tree_merger.make_preview_transform()
461 result_tree = self.tt.get_preview_tree()
462
463=== modified file 'lib/commit.py'
464--- lib/commit.py 2022-01-07 15:10:55 +0000
465+++ lib/commit.py 2022-07-06 15:37:54 +0000
466@@ -465,8 +465,7 @@
467 self.throbber.show()
468 self.refresh_button.setDisabled(True)
469 try:
470- self.tree.lock_read()
471- try:
472+ with self.tree.lock_read():
473 if self.pending_merges_list:
474 self.pending_merges_list.load_tree(self.tree)
475 # Force the loading of the revisions, before we start
476@@ -488,7 +487,7 @@
477 # if there are any paths from the command line that
478 # are not versioned, we want_unversioned.
479 for path in self.initial_selected_list:
480- if not self.tree.path2id(path):
481+ if not self.tree.is_versioned(path):
482 want_unversioned = True
483 break
484
485@@ -504,8 +503,6 @@
486 self.is_loading = False
487 self.processEvents()
488 self.update_compleater_words()
489- finally:
490- self.tree.unlock()
491 finally:
492 self.throbber.hide()
493 self.refresh_button.setDisabled(False)
494
495=== modified file 'lib/commit_data.py'
496--- lib/commit_data.py 2019-10-27 14:48:19 +0000
497+++ lib/commit_data.py 2022-07-06 15:37:54 +0000
498@@ -164,14 +164,11 @@
499 def save(self):
500 """Save data to the branch/tree."""
501 br = self._get_branch()
502- br.lock_write()
503- try:
504+ with br.lock_write():
505 # XXX save should wipe if self._data is empty
506 self._set_new_commit_data(self._filtered_data())
507 # clear old data
508 self._wipe_old_data()
509- finally:
510- br.unlock()
511
512 def _wipe_old_data(self):
513 """Wipe saved data in old format."""
514@@ -180,13 +177,10 @@
515 def wipe(self):
516 """Delete saved data from branch/tree config."""
517 br = self._get_branch()
518- br.lock_write()
519- try:
520+ with br.lock_write():
521 self._set_new_commit_data({})
522 # clear old data
523 self._wipe_old_data()
524- finally:
525- br.unlock()
526
527 def _get_branch(self):
528 """Return branch object if either branch or tree was specified on init.
529
530=== modified file 'lib/diff.py'
531--- lib/diff.py 2022-01-08 11:48:59 +0000
532+++ lib/diff.py 2022-07-06 15:37:54 +0000
533@@ -17,8 +17,18 @@
534 # along with this program; if not, write to the Free Software
535 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
536
537+from contextlib import ExitStack
538+import errno
539+import re
540+import time
541+import sys
542+import os
543+import glob
544+
545 from PyQt5 import QtCore, QtGui, QtWidgets
546
547+from breezy import trace, osutils, cmdline
548+
549 from breezy.errors import NoSuchId, ExecutableMissing
550 from breezy.tree import FileTimestampUnavailable
551 from breezy.plugins.qbrz.lib.diff_arg import * # import DiffArgProvider classes
552@@ -30,22 +40,9 @@
553 )
554
555 from breezy.lazy_import import lazy_import
556-try:
557- QString = unicode
558-except NameError:
559- # Python 3
560- QString = str
561-
562 lazy_import(globals(), '''
563-import errno
564-import re
565-import time
566-import sys
567-import os
568-import glob
569 from patiencediff import PatienceSequenceMatcher as SequenceMatcher
570 from breezy.plugins.qbrz.lib.i18n import gettext, ngettext, N_
571-from breezy import trace, osutils, cmdline
572 from breezy.workingtree import WorkingTree
573 from breezy.trace import mutter
574 ''')
575@@ -78,10 +75,9 @@
576 parent_window.windows.append(window)
577 elif context:
578 ext_diff = str(ext_diff) # convert QString to str
579- cleanup = []
580- try:
581+ with ExitStack() as es:
582 args = arg_provider.get_diff_window_args(
583- QtWidgets.QApplication.processEvents, cleanup.append
584+ QtWidgets.QApplication.processEvents, es
585 )
586 old_tree = args["old_tree"]
587 new_tree = args["new_tree"]
588@@ -91,9 +87,6 @@
589 context.diff_paths(specific_files)
590 else:
591 context.diff_tree()
592- finally:
593- while cleanup:
594- cleanup.pop()()
595
596 else:
597 args=["diff", "--using", ext_diff] # NEVER USE --using=xxx, ALWAYS --using xxx
598@@ -188,11 +181,10 @@
599 """
600 RJLRJL: updated to call .iter_changes directly
601 """
602- try:
603- cleanup = []
604+ with ExitStack() as es:
605 if lock_trees:
606 for t in trees:
607- cleanup.append(t.lock_read().unlock)
608+ es.enter_context(t.lock_read())
609
610 # changes = trees[1].iter_changes(trees[0], specific_files=specific_files, require_versioned=True)
611
612@@ -215,9 +207,6 @@
613 if not di:
614 continue
615 yield di
616- finally:
617- while cleanup:
618- cleanup.pop()()
619
620 @classmethod
621 def create(cls, trees, file_id, paths, changed_content, versioned,
622@@ -588,17 +577,13 @@
623 NOTE: Directories cannot be specified.
624 Use diff_tree or diff_paths instead when specifing directory.
625 """
626- cleanup = []
627- try:
628+ with ExitStack() as es:
629 if lock_trees:
630- cleanup.append(self._differ.new_tree.lock_read().unlock)
631- cleanup.append(self._differ.old_tree.lock_read().unlock)
632+ es.enter_context(self._differ.new_tree.lock_read())
633+ es.enter_context(self._differ.old_tree.lock_read())
634 for file_id in file_ids:
635 self._differ.diff(file_id)
636 time.sleep(interval * 0.001)
637- finally:
638- while cleanup:
639- cleanup.pop()()
640
641 def diff_paths(self, paths, interval=50, lock_trees=True):
642 """
643@@ -610,12 +595,11 @@
644 valid_paths = []
645 ids = []
646 dir_included = False
647- cleanup = []
648- try:
649+ with ExitStack() as es:
650 # Sometimes, we must lock tree before calling tree.kind()
651 if lock_trees:
652- cleanup.append(new_tree.lock_read().unlock)
653- cleanup.append(old_tree.lock_read().unlock)
654+ es.enter_context(new_tree.lock_read())
655+ es.enter_context(old_tree.lock_read())
656 for p in paths:
657 id = new_tree.path2id(p)
658 if id:
659@@ -630,9 +614,6 @@
660 self.diff_tree(valid_paths, interval, False)
661 else:
662 self.diff_ids(ids, interval, False)
663- finally:
664- while cleanup:
665- cleanup.pop()()
666
667 def diff_tree(self, specific_files=None, interval=50, lock_trees=True):
668 """
669
670=== modified file 'lib/diff_arg.py'
671--- lib/diff_arg.py 2017-09-11 06:53:22 +0000
672+++ lib/diff_arg.py 2022-07-06 15:37:54 +0000
673@@ -30,7 +30,7 @@
674 """Contract class to pass arguments to either builtin diff window, or
675 external diffs"""
676
677- def get_diff_window_args(self, processEvents, add_cleanup):
678+ def get_diff_window_args(self, processEvents, es):
679 """Returns the arguments for the builtin diff window.
680
681 :return: {"old_tree": old_tree,
682@@ -52,7 +52,7 @@
683
684 class InternalDiffArgProvider(DiffArgProvider):
685 """Use for passing arguments from internal source."""
686-
687+
688 def __init__(self,
689 old_revid, new_revid,
690 old_branch, new_branch,
691@@ -67,7 +67,7 @@
692
693 self.old_tree = old_tree
694 self.new_tree = new_tree
695-
696+
697 def need_to_load_paths(self):
698 return self.specific_file_ids is not None \
699 and self.specific_files is None
700@@ -83,14 +83,11 @@
701 self.new_branch.repository.revision_tree(self.new_revid)
702
703 if self.need_to_load_paths():
704- self.new_tree.lock_read()
705- try:
706+ with self.new_tree.lock_read():
707 self.specific_files = [self.new_tree.id2path(id) \
708 for id in self.specific_file_ids]
709- finally:
710- self.new_tree.unlock()
711
712- def get_diff_window_args(self, processEvents, add_cleanup):
713+ def get_diff_window_args(self, processEvents, es):
714 self.load_old_tree()
715 processEvents()
716 self.load_new_tree_and_paths()
717@@ -107,11 +104,11 @@
718 if revid.startswith(CURRENT_REVISION):
719 return ''
720 return 'revid:%s' % revid
721-
722+
723 return "-r%s..%s" % (
724 get_revspec_part(self.old_revid),
725 get_revspec_part(self.new_revid))
726-
727+
728 def get_ext_diff_args(self, processEvents):
729 from breezy import urlutils
730 from breezy import errors
731@@ -177,7 +174,7 @@
732 else:
733 InternalDiffArgProvider.load_old_tree(self)
734
735- def get_diff_window_args(self, processEvents, add_cleanup):
736+ def get_diff_window_args(self, processEvents, es):
737 self.load_old_tree()
738 processEvents()
739
740
741=== modified file 'lib/diffview.py'
742--- lib/diffview.py 2022-01-08 10:52:49 +0000
743+++ lib/diffview.py 2022-07-06 15:37:54 +0000
744@@ -295,7 +295,7 @@
745 def adjust_range(self):
746 page_step = self.browsers[0].verticalScrollBar().pageStep()
747 self.setPageStep(page_step)
748- self.setRange(0, self.total_length - page_step + 4)
749+ self.setRange(0, int(self.total_length - page_step + 4))
750 self.setVisible(self.total_length > page_step)
751
752 def get_position_info(self, target):
753
754=== modified file 'lib/diffwindow.py'
755--- lib/diffwindow.py 2022-01-08 11:48:59 +0000
756+++ lib/diffwindow.py 2022-07-06 15:37:54 +0000
757@@ -20,6 +20,7 @@
758 # along with this program; if not, write to the Free Software
759 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
760
761+from contextlib import ExitStack
762 import errno
763 import re
764 import time
765@@ -35,8 +36,6 @@
766 from breezy.workingtree import WorkingTree
767 from breezy.bzr.workingtree_4 import DirStateRevisionTree
768 from breezy import trace
769-# from breezy import cleanup
770-from breezy.plugins.qbrz.lib import cleanup
771
772 from breezy.plugins.qbrz.lib.diffview import (
773 SidebySideDiffView,
774@@ -345,16 +344,13 @@
775 throbber window, then loads the branches etc if they weren't specified
776 in our constructor.
777 """
778- op = cleanup.OperationWithCleanups(self._initial_load)
779- self.throbber.show()
780- op.add_cleanup(self.throbber.hide)
781- op.run()
782+ with ExitStack() as es:
783+ es.callback(self.throbber.hide)
784+ self.throbber.show()
785+ self._initial_load(es)
786
787- def _initial_load(self, op):
788- # RJL Breezy expects a context handler of some type
789- exit_stack = contextlib.ExitStack()
790- args = self.arg_provider.get_diff_window_args(self.processEvents, exit_stack)
791- # args = self.arg_provider.get_diff_window_args(self.processEvents, op.add_cleanup)
792+ def _initial_load(self, es):
793+ args = self.arg_provider.get_diff_window_args(self.processEvents, es)
794
795 self.trees = (args["old_tree"], args["new_tree"])
796 self.branches = (args.get("old_branch", None), args.get("new_branch",None))
797
798=== modified file 'lib/extra/isignored.py'
799--- lib/extra/isignored.py 2019-10-27 14:48:19 +0000
800+++ lib/extra/isignored.py 2022-07-06 15:37:54 +0000
801@@ -33,7 +33,7 @@
802
803 def run(self, filename):
804 tree, relpath = workingtree.WorkingTree.open_containing(filename)
805- if tree.is_ignored(relpath) and not tree.path2id(relpath):
806+ if tree.is_ignored(relpath) and not tree.is_versioned(relpath):
807 if not trace.is_quiet():
808 print('ignored', file=self.outf)
809 return 1
810
811=== modified file 'lib/extra/isversioned.py'
812--- lib/extra/isversioned.py 2019-10-27 14:48:19 +0000
813+++ lib/extra/isversioned.py 2022-07-06 15:37:54 +0000
814@@ -33,7 +33,7 @@
815
816 def run(self, filename):
817 tree, relpath = workingtree.WorkingTree.open_containing(filename)
818- if tree.path2id(relpath):
819+ if tree.is_versioned(relpath):
820 if not trace.is_quiet():
821 print('versioned', file=self.outf)
822 return 1
823
824=== modified file 'lib/lazycachedrevloader.py'
825--- lib/lazycachedrevloader.py 2021-01-05 13:03:34 +0000
826+++ lib/lazycachedrevloader.py 2022-07-06 15:37:54 +0000
827@@ -37,13 +37,13 @@
828 before_batch_load = None,
829 revisions_loaded = None,
830 pass_prev_loaded_rev = False):
831-
832+
833 start_time = time.process_time()
834 showed_throbber = False
835 revids = [revid for revid in revids if not revid == "root:"]
836 return_revisions = {}
837 throbber = current_throbber()
838-
839+
840 try:
841 for revid in [revid for revid in revids
842 if revid in cached_revisions]:
843@@ -51,7 +51,7 @@
844 if pass_prev_loaded_rev:
845 if revisions_loaded is not None:
846 revisions_loaded(return_revisions, False)
847-
848+
849 revs_loaded = {}
850 revids = [revid for revid in revids if revid not in cached_revisions]
851 if revids:
852@@ -59,24 +59,23 @@
853 repo_revids=((repo, revids),)
854 else:
855 repo_revids = repo(revids)
856-
857+
858 for repo, revids in repo_revids:
859 repo_is_local = isinstance(repo.controldir.transport, LocalTransport)
860 if repo_is_local:
861 batch_size = local_batch_size
862 else:
863 batch_size = remote_batch_size
864-
865+
866 if revids:
867- repo.lock_read()
868- try:
869+ with repo.lock_read():
870 if not repo_is_local:
871 update_ui()
872-
873+
874 for offset in range(0, len(revids), batch_size):
875-
876+
877 running_time = time.process_time() - start_time
878-
879+
880 if time_before_first_ui_update < running_time:
881 if revisions_loaded is not None:
882 revisions_loaded(revs_loaded, False)
883@@ -86,28 +85,26 @@
884 throbber.show()
885 showed_throbber = True
886 update_ui()
887-
888+
889 batch_revids = revids[offset:offset+batch_size]
890-
891+
892 if before_batch_load is not None:
893 stop = before_batch_load(repo, batch_revids)
894 if stop:
895 break
896-
897+
898 for rev in repo.get_revisions(batch_revids):
899 cached_revisions[rev.revision_id] = rev
900 return_revisions[rev.revision_id] = rev
901 revs_loaded[rev.revision_id] = rev
902 rev.repository = repo
903- finally:
904- repo.unlock()
905-
906+
907 if revisions_loaded is not None:
908 revisions_loaded(revs_loaded, True)
909 finally:
910 if showed_throbber:
911 throbber.hide()
912-
913+
914 return return_revisions
915
916 def update_ui():
917
918=== modified file 'lib/log.py'
919--- lib/log.py 2022-01-08 11:48:59 +0000
920+++ lib/log.py 2022-07-06 15:37:54 +0000
921@@ -612,9 +612,8 @@
922 for repo, repo_revids in repos_revids:
923 repo_revids = [revid for revid in repo_revids if revid not in self.tree_cache]
924 if repo_revids:
925- repo.lock_read()
926- self.processEvents()
927- try:
928+ with repo.lock_read():
929+ self.processEvents()
930 for revid in repo_revids:
931 if (revid.startswith(CURRENT_REVISION) and gv_is_wtgv):
932 tree = gv.working_trees[revid]
933@@ -622,8 +621,6 @@
934 tree = repo.revision_tree(revid)
935 self.tree_cache[revid] = tree
936 self.processEvents()
937- finally:
938- repo.unlock()
939 self.processEvents()
940
941 delta = self.tree_cache[revids[0]].changes_from(self.tree_cache[revids[1]])
942@@ -830,13 +827,10 @@
943 tree = branch.repository.revision_tree(top_revid)
944 file_id = file_ids[0]
945 path = paths[0]
946- tree.lock_read()
947- try:
948+ with tree.lock_read():
949 kind = tree.kind(path)
950 if kind == 'file':
951 file_content_bytes = tree.get_file_text(path)
952- finally:
953- tree.unlock()
954 if kind != 'file':
955 QtWidgets.QMessageBox.information(self, gettext("Not a file"),
956 gettext("Operation is supported for a single file only,\n"
957@@ -844,11 +838,8 @@
958 return
959 filename = QtWidgets.QFileDialog.getSaveFileName(self, gettext("Save file in this revision as..."))[0]
960 if filename:
961- f = open(str(filename), 'wb')
962- try:
963+ with open(str(filename), 'wb') as f:
964 f.write(file_content_bytes)
965- finally:
966- f.close()
967
968 @ui_current_widget
969 def revert_file(self):
970
971=== modified file 'lib/loggraphviz.py'
972--- lib/loggraphviz.py 2021-08-05 11:27:16 +0000
973+++ lib/loggraphviz.py 2022-07-06 15:37:54 +0000
974@@ -219,7 +219,6 @@
975 finally:
976 self.unlock_branches()
977
978-
979 def load_current_dir_repo(self):
980 # There are no local repositories. Try open the repository
981 # of the current directory, and try load revisions data from
982@@ -1050,13 +1049,10 @@
983 return_revisions = {}
984 for repo, revids in self.get_repo_revids(revids):
985 if revids:
986- repo.lock_read()
987- try:
988+ with repo.lock_read():
989 self.update_ui()
990 for rev in repo.get_revisions(revids):
991 return_revisions[rev.revision_id] = rev
992- finally:
993- repo.unlock()
994 return return_revisions
995
996
997@@ -1443,8 +1439,7 @@
998 if tree is None:
999 tree = bi.branch.basis_tree()
1000
1001- tree.lock_read()
1002- try:
1003+ with tree.lock_read():
1004 for file_id in self.file_ids:
1005 if tree.kind(tree.id2path(file_id)) in ('directory',
1006 'tree-reference'):
1007@@ -1452,8 +1447,6 @@
1008 break
1009 if self.has_dir:
1010 break
1011- finally:
1012- tree.unlock()
1013
1014 if revids is None:
1015 revids = [rev.revid for rev in self.graph_viz.revisions]
1016@@ -1485,8 +1478,7 @@
1017 self.filter_changed_callback(changed_revs, False)
1018 self.graph_viz.update_ui()
1019
1020- repo.lock_read()
1021- try:
1022+ with repo.lock_read():
1023 if not self.uses_inventory():
1024 text_keys = [(file_id, revid)
1025 for revid in revids
1026@@ -1508,8 +1500,6 @@
1027 self.graph_viz.update_ui()
1028
1029 check_text_keys(text_keys)
1030- finally:
1031- repo.unlock()
1032
1033 def load_filter_file_id_chunk_finished(self):
1034 self.filter_changed_callback([], True)
1035@@ -1555,8 +1545,7 @@
1036
1037 :return: True if a change is found. False otherwise
1038 """
1039- tree.lock_read()
1040- try:
1041+ with tree.lock_read():
1042 # Copied from mutabletree, cause we need file_ids too.
1043 # Check pending merges
1044 if len(tree.get_parent_ids()) > 1:
1045@@ -1579,8 +1568,6 @@
1046 except StopIteration:
1047 # No changes
1048 return False
1049- finally:
1050- tree.unlock()
1051
1052 def get_revision_visible(self, rev):
1053 if rev.revid.startswith(CURRENT_REVISION):
1054
1055=== modified file 'lib/spellcheck_enchant.py'
1056--- lib/spellcheck_enchant.py 2019-10-27 14:48:19 +0000
1057+++ lib/spellcheck_enchant.py 2022-07-06 15:37:54 +0000
1058@@ -30,7 +30,7 @@
1059 text = self._text[offset:]
1060 if not text:
1061 raise StopIteration()
1062- match = re.match('(\w+?)[A-Z]', text)
1063+ match = re.match(br'(\w+?)[A-Z]', text)
1064 if match is None:
1065 word = text
1066 else:
1067@@ -60,7 +60,7 @@
1068 self.checker.set_text(text)
1069 for err in self.checker:
1070 yield err.wordpos, len(err.word)
1071-
1072+
1073 def suggest(self, text):
1074 return self.dict.suggest(text)
1075
1076
1077=== modified file 'lib/subprocess.py'
1078--- lib/subprocess.py 2022-04-14 11:45:16 +0000
1079+++ lib/subprocess.py 2022-07-06 15:37:54 +0000
1080@@ -847,12 +847,9 @@
1081 if not os.path.isdir(qdir):
1082 os.makedirs(qdir)
1083 fd, fname = tempfile.mkstemp(dir=qdir)
1084- f = os.fdopen(fd, "wb")
1085- try:
1086+ with os.fdopen(fd, "wb") as f:
1087 # f.write(text.decode('utf8'))
1088 f.write(text)
1089- finally:
1090- f.close() # it closes fd as well
1091 self._args_file = fname
1092 return fname
1093
1094@@ -1074,11 +1071,8 @@
1095 if s_cmd[0][0] == '@':
1096 fname = s_cmd[1:]
1097 # Changed this: it's written in 'b' so must be read that way too
1098- f = open(fname, 'rb')
1099- try:
1100+ with open(fname, 'rb') as f:
1101 s_cmd = f.read()
1102- finally:
1103- f.close()
1104 # We stored a bencoded string like b'l6:ignore18:qbrz-setup-iss.loge', so...:
1105 s_cmd = bencode.bdecode(s_cmd)
1106 # ...and again, sometimes we get a list like [b'ignore', b'qbrz-setup-iss.log'], so...
1107
1108=== modified file 'lib/tests/test_extdiff.py'
1109--- lib/tests/test_extdiff.py 2020-07-05 15:21:39 +0000
1110+++ lib/tests/test_extdiff.py 2022-07-06 15:37:54 +0000
1111@@ -25,11 +25,11 @@
1112
1113 def test_no_arguments(self):
1114 self.differ.set_command_string("test")
1115- self.assertEqual(self.differ.command_template, ["test", "@old_path", "@new_path"])
1116+ self.assertEqual(self.differ.command_template, ["test", "{old_path}", "{new_path}"])
1117
1118 def test_has_arguments(self):
1119- self.differ.set_command_string("test --old @old_path --new @new_path")
1120- self.assertEqual(self.differ.command_template, ["test", "--old", "@old_path", "--new", "@new_path"])
1121+ self.differ.set_command_string("test --old {old_path} --new {new_path}")
1122+ self.assertEqual(self.differ.command_template, ["test", "--old", "{old_path}", "--new", "{new_path}"])
1123
1124 class TestPrefix(QTestCase):
1125 def setUp(self):
1126@@ -221,7 +221,7 @@
1127 def assertPopen(self, paths, old_contents):
1128 for args, path, old_content in zip(self.popen_mock.args, paths, old_contents):
1129 # tool, old_path, new_path = args[0][0]
1130- tool, _, _, old_path, new_path = args[0][0]
1131+ tool, old_path, new_path = args[0][0]
1132 self.assertEqual(tool, "diff.exe")
1133 self.assertFileContent(old_path, old_content)
1134 self.assertEqual(new_path, self.tree.abspath(path))
1135
1136=== modified file 'lib/tests/test_guidebar.py'
1137--- lib/tests/test_guidebar.py 2021-01-05 16:26:04 +0000
1138+++ lib/tests/test_guidebar.py 2022-07-06 15:37:54 +0000
1139@@ -24,7 +24,7 @@
1140 def __init__(self, tree):
1141 self.tree = tree
1142
1143- def get_diff_window_args(self, processEvents, add_cleanup):
1144+ def get_diff_window_args(self, processEvents, es):
1145 # This will be used by DiffWindow::_initial_load
1146 return dict(
1147 old_tree=self.tree.basis_tree(),
1148
1149=== modified file 'lib/treewidget.py'
1150--- lib/treewidget.py 2022-01-08 11:48:59 +0000
1151+++ lib/treewidget.py 2022-07-06 15:37:54 +0000
1152@@ -485,16 +485,10 @@
1153
1154 if isinstance(self.tree, WorkingTree):
1155 # print(':::-> tree.lock_read (try) about to execute')
1156- tree.lock_read()
1157- try:
1158- # RJL Breezy release notes state that:
1159- #
1160- # The ``Tree.get_root_id`` method has been removed. Use``Tree.path2id('')`` instead. (Jelmer Vernooij)
1161- #
1162+ with tree.lock_read():
1163 root_id = self.tree.path2id('')
1164 basis_tree = self.tree.basis_tree()
1165- basis_tree.lock_read()
1166- try:
1167+ with basis_tree.lock_read():
1168 # print(':::->>second try block')
1169 for the_change in self.tree.iter_changes(basis_tree, want_unversioned=want_unversioned):
1170 # iter_changes now seems to appear to return a TreeChange type See Jelmer's 7390.
1171@@ -641,12 +635,8 @@
1172 dir_fileid = self.tree.path2id(dir_path)
1173 # print('\tname setting gave ', item_data, item_data.path, dir_path, name, dir_fileid)
1174 item_data.item.name = get_name(dir_fileid, dir_path, item_data.path, item_data.change)
1175- finally:
1176- basis_tree.unlock()
1177 # print('\n\t\t FIRST process_tree...', initial_checked_paths, load_dirs)
1178 self.process_tree(self.working_tree_get_children, initial_checked_paths, load_dirs)
1179- finally:
1180- tree.unlock()
1181 else:
1182 # print('\n\t\t SECOND process_tree...', initial_checked_paths, load_dirs)
1183 self.process_tree(self.revision_tree_get_children, initial_checked_paths, load_dirs)
1184@@ -1579,11 +1569,8 @@
1185 self.change_load_filter = change_load_filter
1186
1187 if branch:
1188- branch.lock_read()
1189- try:
1190+ with branch.lock_read():
1191 last_revno = branch.last_revision_info()[0]
1192- finally:
1193- branch.unlock()
1194 self.revno_item_delegate.set_max_revno(last_revno)
1195 # update width uncoditionally because we may change the revno column
1196 self.set_header_width_settings()
1197@@ -1656,8 +1643,7 @@
1198
1199 def restore_state(self, state):
1200 (checked, expanded, selected, v_scroll) = state
1201- self.tree.lock_read()
1202- try:
1203+ with self.tree.lock_read():
1204 if self.tree_model.checkable and checked is not None:
1205 for (ref, state) in checked:
1206 if not state == QtCore.Qt.PartiallyChecked:
1207@@ -1679,12 +1665,9 @@
1208 QtCore.QItemSelectionModel.SelectCurrent |
1209 QtCore.QItemSelectionModel.Rows)
1210 self.verticalScrollBar().setValue(v_scroll)
1211- finally:
1212- self.tree.unlock()
1213
1214 def refresh(self):
1215- self.tree.lock_read()
1216- try:
1217+ with self.tree.lock_read():
1218 state = self.get_state()
1219 self.tree_model.set_tree(self.tree, self.branch,
1220 self.changes_mode, self.want_unversioned,
1221@@ -1699,8 +1682,6 @@
1222 # after every time we do a layout changed. The issue is similar to
1223 # http://www.qtsoftware.com/developer/task-tracker/index_html?method=entry&id=236755
1224 self.set_header_width_settings()
1225- finally:
1226- self.tree.unlock()
1227
1228 def mousePressEvent(self, event):
1229 index = self.indexAt(event.pos())
1230@@ -1917,11 +1898,8 @@
1231 item = items[0]
1232
1233 if isinstance(self.tree, WorkingTree):
1234- self.tree.lock_read()
1235- try:
1236+ with self.tree.lock_read():
1237 abspath = self.tree.abspath(item.path)
1238- finally:
1239- self.tree.unlock()
1240 url = QtCore.QUrl.fromLocalFile(abspath)
1241 QtGui.QDesktopServices.openUrl(url)
1242 else:
1243@@ -2020,11 +1998,8 @@
1244 QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
1245 if res == QtWidgets.QMessageBox.Yes:
1246 try:
1247- self.tree.lock_write()
1248- try:
1249+ with self.tree.lock_write():
1250 self.tree.revert(paths, self.tree.basis_tree())
1251- finally:
1252- self.tree.unlock()
1253 except Exception:
1254 report_exception(type=SUB_LOAD_METHOD, window=self.window())
1255 # XXX - it would be good it we could just refresh the selected items
1256
1257=== modified file 'lib/widgets/shelve.py'
1258--- lib/widgets/shelve.py 2021-01-06 09:29:35 +0000
1259+++ lib/widgets/shelve.py 2022-07-06 15:37:54 +0000
1260@@ -17,6 +17,8 @@
1261 # along with this program; if not, write to the Free Software
1262 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1263
1264+from contextlib import ExitStack
1265+import os
1266 import sys, time
1267 from PyQt5 import QtCore, QtGui, QtWidgets
1268
1269@@ -56,8 +58,6 @@
1270 from breezy.shelf import ShelfCreator
1271 from breezy.shelf_ui import Shelver
1272 from breezy.osutils import split_lines
1273-from cStringIO import StringIO
1274-import os
1275 ''')
1276
1277
1278@@ -453,15 +453,14 @@
1279 self.hunk_view.set_tab_width(pixels)
1280
1281 def refresh(self):
1282- cleanup = []
1283- try:
1284+ with ExitStack() as es:
1285 old_rev = self.revision
1286 old_changes = self._get_change_dictionary()
1287 self.clear(clear_message = False)
1288
1289 shelver, creator = self._create_shelver_and_creator()
1290- cleanup.append(shelver.finalize)
1291- cleanup.append(creator.finalize)
1292+ es.callback(shelver.finalize)
1293+ es.callback(creator.finalize)
1294
1295 file_list = shelver.file_list
1296 if file_list:
1297@@ -489,10 +488,6 @@
1298 item = self._create_item(change, shelver, self.trees, old_changes)
1299 self.file_view.addTopLevelItem(item)
1300
1301- finally:
1302- for func in cleanup:
1303- func()
1304-
1305 if self.select_all:
1306 self.check_all(True)
1307 self.select_all = False
1308@@ -625,16 +620,15 @@
1309 self.loaded = False
1310
1311 def use_editor(self):
1312- cleanup = []
1313 items = self.file_view.selectedItems()
1314 if len(items) != 1 or items[0].change.status != 'modify text':
1315 return
1316 else:
1317 change = items[0].change
1318- try:
1319+ with ExitStack() as es:
1320 target_tree, work_tree = self.trees
1321- cleanup.append(work_tree.lock_read().unlock)
1322- cleanup.append(target_tree.lock_read().unlock)
1323+ es.enter_context(work_tree.lock_read())
1324+ es.enter_context(target_tree.lock_read())
1325 config = work_tree.branch.get_config()
1326 change_editor = config.get_change_editor(target_tree, work_tree)
1327 if change_editor is None:
1328@@ -643,16 +637,13 @@
1329 self.editor_button.setEnabled(False)
1330 return
1331
1332- cleanup.append(change_editor.finish)
1333+ es.callback(change_editor.finish)
1334 lines = split_lines(change_editor.edit_file(change.file_id))
1335 change_count = Shelver._count_changed_regions(change.work_lines, lines)
1336 if change_count > 0:
1337 change.edited_lines = lines
1338 self.update_item(items[0])
1339 self.selected_file_changed()
1340- finally:
1341- while cleanup:
1342- cleanup.pop()()
1343
1344 def _get_change_dictionary(self):
1345 change_dict = {}
1346@@ -683,59 +674,56 @@
1347 QtWidgets.QMessageBox.information(self, gettext('Shelve'), gettext('No changes selected.'), gettext('&OK'))
1348 return
1349
1350- cleanup = []
1351- try:
1352- shelver, creator = self._create_shelver_and_creator(destroy=destroy)
1353- cleanup.append(shelver.finalize)
1354- cleanup.append(creator.finalize)
1355- trees = (shelver.target_tree, shelver.work_tree)
1356- if len(trees[1].get_parent_ids()) > 1:
1357- raise WorkingTreeHasPendingMarge
1358- if self.revision != trees[0].get_revision_id():
1359- raise WorkingTreeHasChanged
1360-
1361- changes = []
1362- for ch in creator.iter_shelvable():
1363- change = Change(ch, shelver, trees)
1364- key = (change.file_id, change.status)
1365- org_change = change_dict.get(key)
1366- if org_change is None:
1367- continue
1368- if not change.is_same_change(org_change):
1369- raise WorkingTreeHasChanged
1370- del(change_dict[key])
1371- changes.append(org_change)
1372-
1373- if change_dict:
1374- raise WorkingTreeHasChanged
1375-
1376- for change in changes:
1377- if change.status == 'modify text':
1378- self.handle_modify_text(creator, change)
1379- elif change.status == 'modify binary':
1380- creator.shelve_content_change(change.data[1])
1381+ with ExitStack() as es:
1382+ try:
1383+ shelver, creator = self._create_shelver_and_creator(destroy=destroy)
1384+ es.callback(shelver.finalize)
1385+ es.callback(creator.finalize)
1386+ trees = (shelver.target_tree, shelver.work_tree)
1387+ if len(trees[1].get_parent_ids()) > 1:
1388+ raise WorkingTreeHasPendingMarge
1389+ if self.revision != trees[0].get_revision_id():
1390+ raise WorkingTreeHasChanged
1391+
1392+ changes = []
1393+ for ch in creator.iter_shelvable():
1394+ change = Change(ch, shelver, trees)
1395+ key = (change.file_id, change.status)
1396+ org_change = change_dict.get(key)
1397+ if org_change is None:
1398+ continue
1399+ if not change.is_same_change(org_change):
1400+ raise WorkingTreeHasChanged
1401+ del(change_dict[key])
1402+ changes.append(org_change)
1403+
1404+ if change_dict:
1405+ raise WorkingTreeHasChanged
1406+
1407+ for change in changes:
1408+ if change.status == 'modify text':
1409+ self.handle_modify_text(creator, change)
1410+ elif change.status == 'modify binary':
1411+ creator.shelve_content_change(change.data[1])
1412+ else:
1413+ creator.shelve_change(change.data)
1414+ manager = shelver.work_tree.get_shelf_manager()
1415+ message = str(self.message.toPlainText()).strip() or gettext('<no message>')
1416+ if destroy:
1417+ creator.transform()
1418+ shelf_id = -1
1419 else:
1420- creator.shelve_change(change.data)
1421- manager = shelver.work_tree.get_shelf_manager()
1422- message = str(self.message.toPlainText()).strip() or gettext('<no message>')
1423- if destroy:
1424- creator.transform()
1425- shelf_id = -1
1426- else:
1427- shelf_id = manager.shelve_changes(creator, message)
1428-
1429- except WorkingTreeHasPendingMarge:
1430- QtWidgets.QMessageBox.warning(self, gettext('Shelve'),
1431- gettext('Operation aborted because working tree has pending merges.'), gettext('&OK'))
1432- return
1433- except WorkingTreeHasChanged:
1434- QtWidgets.QMessageBox.warning(self, gettext('Shelve'),
1435- gettext('Operation aborted because target files has been changed.'), gettext('&OK'))
1436- return
1437-
1438- finally:
1439- while cleanup:
1440- cleanup.pop()()
1441+ shelf_id = manager.shelve_changes(creator, message)
1442+
1443+ except WorkingTreeHasPendingMarge:
1444+ QtWidgets.QMessageBox.warning(self, gettext('Shelve'),
1445+ gettext('Operation aborted because working tree has pending merges.'), gettext('&OK'))
1446+ return
1447+ except WorkingTreeHasChanged:
1448+ QtWidgets.QMessageBox.warning(self, gettext('Shelve'),
1449+ gettext('Operation aborted because target files has been changed.'), gettext('&OK'))
1450+ return
1451+
1452 self.shelfCreated.emit(shelf_id)
1453 self.clear()
1454
1455@@ -888,12 +876,12 @@
1456 continue
1457 if bottom < y1:
1458 break
1459- painter.fillRect(6, y1, 13, 13, QtCore.Qt.white)
1460+ painter.fillRect(6, int(y1), 13, 13, QtCore.Qt.white)
1461
1462- painter.drawRect(6, y1, 13, 13)
1463+ painter.drawRect(6, int(y1), 13, 13)
1464 if hunk.selected:
1465- painter.drawLine(9, y1 + 7, 12, y1 + 10)
1466- painter.drawLine(16, y1 + 3, 12, y1 + 10)
1467+ painter.drawLine(9, int(y1) + 7, 12, int(y1) + 10)
1468+ painter.drawLine(16, int(y1) + 3, 12, int(y1) + 10)
1469
1470 del painter
1471
1472@@ -942,7 +930,7 @@
1473 self.monospacedInactiveFormat.setForeground(QtGui.QColor(128, 128, 128))
1474
1475 titleFont = QtGui.QFont(monospacedFont)
1476- titleFont.setPointSize(titleFont.pointSize() * 140 / 100)
1477+ titleFont.setPointSize(titleFont.pointSize() * 140 // 100)
1478 titleFont.setBold(True)
1479 titleFont.setItalic(True)
1480
1481@@ -1062,17 +1050,17 @@
1482 continue
1483 if not self.complete:
1484 # Fill header rect.
1485- painter.fillRect(left, y1, width, 20, self.header_color)
1486+ painter.fillRect(left, int(y1), width, 20, self.header_color)
1487 # Overlay focus rect.
1488 if i == self._focused_index:
1489 if self.hasFocus():
1490 color = self.focus_color
1491 else:
1492 color = self.focus_color_inactive
1493- painter.fillRect(left, y1, width, y2 - y1, color)
1494+ painter.fillRect(left, int(y1), width, int(y2 - y1), color)
1495 # Draw border.
1496- painter.drawLine(left, y1, right, y1)
1497- painter.drawLine(left, y2, right, y2)
1498+ painter.drawLine(left, int(y1), right, int(y1))
1499+ painter.drawLine(left, int(y2), right, int(y2))
1500
1501 def move_next(self):
1502 index = int(self._focused_index + 1)
1503@@ -1127,8 +1115,8 @@
1504 MARGIN = 24
1505 height = self.viewport().height()
1506 cur_pos = sbar.value()
1507- max_pos = self.hunk_list[index][1] - MARGIN
1508- min_pos = self.hunk_list[index][2] - height + MARGIN
1509+ max_pos = int(self.hunk_list[index][1] - MARGIN)
1510+ min_pos = int(self.hunk_list[index][2] - height + MARGIN)
1511 if max_pos <= min_pos or max_pos < cur_pos:
1512 sbar.setValue(max_pos)
1513 elif cur_pos < min_pos:
1514
1515=== modified file 'lib/widgets/shelvelist.py'
1516--- lib/widgets/shelvelist.py 2021-01-06 09:29:35 +0000
1517+++ lib/widgets/shelvelist.py 2022-07-06 15:37:54 +0000
1518@@ -17,6 +17,8 @@
1519 # along with this program; if not, write to the Free Software
1520 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1521
1522+from contextlib import ExitStack
1523+
1524 import sys, time
1525 from PyQt5 import QtCore, QtGui, QtWidgets
1526 from PyQt5.QtGui import QKeySequence
1527@@ -55,7 +57,6 @@
1528 from breezy.plugins.qbrz.lib.widgets.texteditaccessory import setup_guidebar_for_find
1529 from breezy.lazy_import import lazy_import
1530 lazy_import(globals(), '''
1531-from breezy import transform
1532 from breezy.workingtree import WorkingTree
1533 from breezy.plugins.qbrz.lib.encoding_selector import EncodingMenuSelector
1534 from breezy.plugins.qbrz.lib.diff import DiffItem
1535@@ -274,8 +275,7 @@
1536 self.loaded = False
1537 self.clear()
1538 tree = WorkingTree.open_containing(self.directory)[0]
1539- tree.lock_read()
1540- try:
1541+ with tree.lock_read():
1542 manager = tree.get_shelf_manager()
1543 shelves = manager.active_shelves()
1544 for shelf_id in reversed(shelves):
1545@@ -298,8 +298,6 @@
1546 self.tabwidth_selector.setTabWidth(tabwidth)
1547 self._on_tabwidth_changed(tabwidth)
1548
1549- finally:
1550- tree.unlock()
1551 self.update()
1552 self.loaded = True
1553
1554@@ -312,18 +310,17 @@
1555 self.manager = None
1556
1557 def show_changes(self, shelf_id):
1558- cleanup = []
1559 shelf_file = self.manager.read_shelf(shelf_id)
1560- cleanup.append(shelf_file.close)
1561- try:
1562+ with ExitStack() as es:
1563+ es.callback(shelf_file.close)
1564 records = Unshelver.iter_records(shelf_file)
1565 revid = Unshelver.parse_metadata(records)[b'revision_id']
1566 try:
1567 base_tree = self.tree.revision_tree(revid)
1568 except NoSuchRevisionInTree:
1569 base_tree = self.tree.branch.repository.revision_tree(revid)
1570- preview = transform.TransformPreview(base_tree)
1571- cleanup.append(preview.finalize)
1572+ preview = base_tree.preview_transform()
1573+ es.callback(preview.finalize)
1574 preview.deserialize(records)
1575
1576 tabwidth = get_tab_width_pixels(self.tree.branch)
1577@@ -332,10 +329,6 @@
1578
1579 self.load_diff(preview.get_preview_tree(), base_tree)
1580
1581- finally:
1582- for func in cleanup:
1583- func()
1584-
1585 def load_diff(self, tree, base_tree):
1586 self.file_view.clear()
1587

Subscribers

People subscribed via source and target branches