Merge lp:~doxxx/bzr/bug939605 into lp:bzr

Proposed by Gordon Tyler
Status: Rejected
Rejected by: Martin Packman
Proposed branch: lp:~doxxx/bzr/bug939605
Merge into: lp:bzr
Diff against target: 155 lines (+53/-21)
5 files modified
bzrlib/mergetools.py (+16/-7)
bzrlib/osutils.py (+12/-9)
bzrlib/tests/test_mergetools.py (+14/-5)
bzrlib/tests/test_osutils.py (+8/-0)
doc/en/release-notes/bzr-2.6.txt (+3/-0)
To merge this branch: bzr merge lp:~doxxx/bzr/bug939605
Reviewer Review Type Date Requested Status
Martin Pool Approve
Martin Packman (community) Needs Information
Alexander Belchenko Approve
Review via email: mp+94488@code.launchpad.net

Description of the change

Fixes bug 939605 by updating osutils.find_executable_on_path to additionally search the Windows App Path registry.

The check_availability and invoke functions in mergetools use a small utility function, _get_executable_path, to get the full path to the executable in the mergetool commandline, which skips the PATH search if the path is already absolute.

To post a comment you must log in.
Revision history for this message
Alexander Belchenko (bialix) wrote :

Looks OK for me, though I can't comment on your change to existing test. Maybe second voice from core dev is needed. I can say that I've tested it and it works. Thank you for fixing it so fast.

Also, I'd like to have this patch backported to bzr 2.5.1, because it clearly fixed bug and don't change anything public.

And you need NEWS message, I guess ;-)

review: Approve
Revision history for this message
Gordon Tyler (doxxx) wrote :

Darn NEWS...

Revision history for this message
Gordon Tyler (doxxx) wrote :

Release notes updated.

Revision history for this message
Martin Packman (gz) wrote :

Wouldn't it be better to fold the logic you've added in _get_executable_path into osutils.find_executable_on_path? If we need a look-before-you-leap executable finder, we may as well have a single one that does the right thing rather than needing extra special cases on top. I wouldn't worry about that making the '_on_path' naming a little misleading, it's hardly the biggest problem is osutils.

For the test you could override the PATH envvar for the duration of the test. Just %SystemRoot% should do.

review: Needs Information
Revision history for this message
Gordon Tyler (doxxx) wrote :

Since Win32's ShellExecute function searches the App Path, it makes sense to include it in osutils.find_executable_on_path.

That's a good point about the SystemRoot env var. Since '%SystemRoot\system32' is guaranteed to be in the PATH, I can use something like cmd.exe to test that and use the env var to construct the path to test against.

Thanks.

Revision history for this message
Gordon Tyler (doxxx) wrote :

Updated according to Martin's suggestions.

Revision history for this message
Martin Pool (mbp) wrote :

looks reasonable to me too, and +1 more to putting this in 2.5

review: Approve
Revision history for this message
Martin Packman (gz) wrote :

Unmerged revisions

6481. By Gordon Tyler

Re-removed unnecessary import.

6480. By Gordon Tyler

Improved readablity of check_availability.

6479. By Gordon Tyler

Removed unnecessary import.

6478. By Gordon Tyler

Updated mergetools._get_executable_path and improved test_invoke_expands_exe_path.

6477. By Gordon Tyler

Changed osutils.find_executable_on_path to search using win32utils.get_app_path if the executable is not found using the PATH env var.

6476. By Gordon Tyler

Updated release notes.

6475. By Gordon Tyler

Added _get_executable_path function to mergetools module, which searches the PATH and App Path registry on win32.
Changed check_availability and invoke to use _get_executable_path.
Updated tests.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'bzrlib/mergetools.py'
--- bzrlib/mergetools.py 2011-12-19 13:23:58 +0000
+++ bzrlib/mergetools.py 2012-02-27 17:50:24 +0000
@@ -48,13 +48,13 @@
48 cmd_list = cmdline.split(command_line)48 cmd_list = cmdline.split(command_line)
49 exe = cmd_list[0]49 exe = cmd_list[0]
50 if sys.platform == 'win32':50 if sys.platform == 'win32':
51 if os.path.isabs(exe):51 exe = _get_executable_path(exe)
52 base, ext = os.path.splitext(exe)52 if exe is None:
53 path_ext = [unicode(s.lower())53 return False
54 for s in os.getenv('PATHEXT', '').split(os.pathsep)]54 base, ext = os.path.splitext(exe)
55 return os.path.exists(exe) and ext in path_ext55 path_ext = [unicode(s.lower())
56 else:56 for s in os.getenv('PATHEXT', '').split(os.pathsep)]
57 return osutils.find_executable_on_path(exe) is not None57 return os.path.exists(exe) and ext in path_ext
58 else:58 else:
59 return (os.access(exe, os.X_OK)59 return (os.access(exe, os.X_OK)
60 or osutils.find_executable_on_path(exe) is not None)60 or osutils.find_executable_on_path(exe) is not None)
@@ -69,6 +69,9 @@
69 if invoker is None:69 if invoker is None:
70 invoker = subprocess_invoker70 invoker = subprocess_invoker
71 cmd_list = cmdline.split(command_line)71 cmd_list = cmdline.split(command_line)
72 exe = _get_executable_path(cmd_list[0])
73 if exe is not None:
74 cmd_list[0] = exe
72 args, tmp_file = _subst_filename(cmd_list, filename)75 args, tmp_file = _subst_filename(cmd_list, filename)
73 def cleanup(retcode):76 def cleanup(retcode):
74 if tmp_file is not None:77 if tmp_file is not None:
@@ -79,6 +82,12 @@
79 return invoker(args[0], args[1:], cleanup)82 return invoker(args[0], args[1:], cleanup)
8083
8184
85def _get_executable_path(exe):
86 if os.path.isabs(exe):
87 return exe
88 return osutils.find_executable_on_path(exe)
89
90
82def _subst_filename(args, filename):91def _subst_filename(args, filename):
83 subst_names = {92 subst_names = {
84 'base': filename + u'.BASE',93 'base': filename + u'.BASE',
8594
=== modified file 'bzrlib/osutils.py'
--- bzrlib/osutils.py 2012-02-01 12:31:53 +0000
+++ bzrlib/osutils.py 2012-02-27 17:50:24 +0000
@@ -2473,10 +2473,6 @@
2473 :param name: The base name of the executable.2473 :param name: The base name of the executable.
2474 :return: The path to the executable found or None.2474 :return: The path to the executable found or None.
2475 """2475 """
2476 path = os.environ.get('PATH')
2477 if path is None:
2478 return None
2479 path = path.split(os.pathsep)
2480 if sys.platform == 'win32':2476 if sys.platform == 'win32':
2481 exts = os.environ.get('PATHEXT', '').split(os.pathsep)2477 exts = os.environ.get('PATHEXT', '').split(os.pathsep)
2482 exts = [ext.lower() for ext in exts]2478 exts = [ext.lower() for ext in exts]
@@ -2488,11 +2484,18 @@
2488 exts = [ext]2484 exts = [ext]
2489 else:2485 else:
2490 exts = ['']2486 exts = ['']
2491 for ext in exts:2487 path = os.environ.get('PATH')
2492 for d in path:2488 if path is not None:
2493 f = os.path.join(d, name) + ext2489 path = path.split(os.pathsep)
2494 if os.access(f, os.X_OK):2490 for ext in exts:
2495 return f2491 for d in path:
2492 f = os.path.join(d, name) + ext
2493 if os.access(f, os.X_OK):
2494 return f
2495 if sys.platform == 'win32':
2496 app_path = win32utils.get_app_path(name)
2497 if app_path != name:
2498 return app_path
2496 return None2499 return None
24972500
24982501
24992502
=== modified file 'bzrlib/tests/test_mergetools.py'
--- bzrlib/tests/test_mergetools.py 2011-06-30 21:00:38 +0000
+++ bzrlib/tests/test_mergetools.py 2012-02-27 17:50:24 +0000
@@ -81,11 +81,7 @@
81 self.assertTrue(mergetools.check_availability(sys.executable))81 self.assertTrue(mergetools.check_availability(sys.executable))
8282
83 def test_exe_on_path(self):83 def test_exe_on_path(self):
84 if sys.platform == 'win32':84 self.assertTrue(mergetools.check_availability('python'))
85 exe = 'cmd.exe'
86 else:
87 exe = 'sh'
88 self.assertTrue(mergetools.check_availability(exe))
8985
90 def test_nonexistent(self):86 def test_nonexistent(self):
91 self.assertFalse(mergetools.check_availability('DOES NOT EXIST'))87 self.assertFalse(mergetools.check_availability('DOES NOT EXIST'))
@@ -112,6 +108,19 @@
112 ('test.txt.OTHER', 'other stuff'),108 ('test.txt.OTHER', 'other stuff'),
113 ))109 ))
114 110
111 def test_invoke_expands_exe_path(self):
112 self.overrideEnv('PATH', os.path.dirname(sys.executable))
113 def dummy_invoker(exe, args, cleanup):
114 self._exe = exe
115 self._args = args
116 cleanup(0)
117 return 0
118 command = '%s {result}' % os.path.basename(sys.executable)
119 retcode = mergetools.invoke(command, 'test.txt', dummy_invoker)
120 self.assertEqual(0, retcode)
121 self.assertEqual(sys.executable, self._exe)
122 self.assertEqual(['test.txt'], self._args)
123
115 def test_success(self):124 def test_success(self):
116 def dummy_invoker(exe, args, cleanup):125 def dummy_invoker(exe, args, cleanup):
117 self._exe = exe126 self._exe = exe
118127
=== modified file 'bzrlib/tests/test_osutils.py'
--- bzrlib/tests/test_osutils.py 2012-02-01 12:32:26 +0000
+++ bzrlib/tests/test_osutils.py 2012-02-27 17:50:24 +0000
@@ -2225,6 +2225,14 @@
2225 self.assertTrue(2225 self.assertTrue(
2226 osutils.find_executable_on_path('THIS SHOULD NOT EXIST') is None)2226 osutils.find_executable_on_path('THIS SHOULD NOT EXIST') is None)
2227 self.assertTrue(osutils.find_executable_on_path('file.txt') is None)2227 self.assertTrue(osutils.find_executable_on_path('file.txt') is None)
2228
2229 def test_windows_app_path(self):
2230 if sys.platform != 'win32':
2231 raise tests.TestSkipped('test requires win32')
2232 # Override PATH env var so that exe can only be found on App Path
2233 self.overrideEnv('PATH', '')
2234 # Internt Explorer is always registered in the App Path
2235 self.assertTrue(osutils.find_executable_on_path('iexplore') is not None)
22282236
2229 def test_other(self):2237 def test_other(self):
2230 if sys.platform == 'win32':2238 if sys.platform == 'win32':
22312239
=== modified file 'doc/en/release-notes/bzr-2.6.txt'
--- doc/en/release-notes/bzr-2.6.txt 2012-02-25 14:13:19 +0000
+++ doc/en/release-notes/bzr-2.6.txt 2012-02-27 17:50:24 +0000
@@ -47,6 +47,9 @@
47* Fix ``bzr config`` display for ``RegistryOption`` values.47* Fix ``bzr config`` display for ``RegistryOption`` values.
48 (Vincent Ladeuil, #930182)48 (Vincent Ladeuil, #930182)
4949
50* Fixed merge tool availability checking and invocation to search the
51 Windows App Path registry in addition to the PATH. (Gordon Tyler, #939605)
52
50Documentation53Documentation
51*************54*************
5255