##// END OF EJS Templates
Backport PR #10239: IPython/lib/editorhooks.py: wait for process even if wait=False...
Thomas Kluyver -
Show More
@@ -1,129 +1,129 b''
1 """ 'editor' hooks for common editors that work well with ipython
1 """ 'editor' hooks for common editors that work well with ipython
2
2
3 They should honor the line number argument, at least.
3 They should honor the line number argument, at least.
4
4
5 Contributions are *very* welcome.
5 Contributions are *very* welcome.
6 """
6 """
7 from __future__ import print_function
7 from __future__ import print_function
8
8
9 import os
9 import os
10 import pipes
10 import pipes
11 import shlex
11 import shlex
12 import subprocess
12 import subprocess
13 import sys
13 import sys
14
14
15 from IPython import get_ipython
15 from IPython import get_ipython
16 from IPython.core.error import TryNext
16 from IPython.core.error import TryNext
17 from IPython.utils import py3compat
17 from IPython.utils import py3compat
18
18
19
19
20 def install_editor(template, wait=False):
20 def install_editor(template, wait=False):
21 """Installs the editor that is called by IPython for the %edit magic.
21 """Installs the editor that is called by IPython for the %edit magic.
22
22
23 This overrides the default editor, which is generally set by your EDITOR
23 This overrides the default editor, which is generally set by your EDITOR
24 environment variable or is notepad (windows) or vi (linux). By supplying a
24 environment variable or is notepad (windows) or vi (linux). By supplying a
25 template string `run_template`, you can control how the editor is invoked
25 template string `run_template`, you can control how the editor is invoked
26 by IPython -- (e.g. the format in which it accepts command line options)
26 by IPython -- (e.g. the format in which it accepts command line options)
27
27
28 Parameters
28 Parameters
29 ----------
29 ----------
30 template : basestring
30 template : basestring
31 run_template acts as a template for how your editor is invoked by
31 run_template acts as a template for how your editor is invoked by
32 the shell. It should contain '{filename}', which will be replaced on
32 the shell. It should contain '{filename}', which will be replaced on
33 invokation with the file name, and '{line}', $line by line number
33 invokation with the file name, and '{line}', $line by line number
34 (or 0) to invoke the file with.
34 (or 0) to invoke the file with.
35 wait : bool
35 wait : bool
36 If `wait` is true, wait until the user presses enter before returning,
36 If `wait` is true, wait until the user presses enter before returning,
37 to facilitate non-blocking editors that exit immediately after
37 to facilitate non-blocking editors that exit immediately after
38 the call.
38 the call.
39 """
39 """
40
40
41 # not all editors support $line, so we'll leave out this check
41 # not all editors support $line, so we'll leave out this check
42 # for substitution in ['$file', '$line']:
42 # for substitution in ['$file', '$line']:
43 # if not substitution in run_template:
43 # if not substitution in run_template:
44 # raise ValueError(('run_template should contain %s'
44 # raise ValueError(('run_template should contain %s'
45 # ' for string substitution. You supplied "%s"' % (substitution,
45 # ' for string substitution. You supplied "%s"' % (substitution,
46 # run_template)))
46 # run_template)))
47
47
48 def call_editor(self, filename, line=0):
48 def call_editor(self, filename, line=0):
49 if line is None:
49 if line is None:
50 line = 0
50 line = 0
51 cmd = template.format(filename=pipes.quote(filename), line=line)
51 cmd = template.format(filename=pipes.quote(filename), line=line)
52 print(">", cmd)
52 print(">", cmd)
53 # pipes.quote doesn't work right on Windows, but it does after splitting
53 # pipes.quote doesn't work right on Windows, but it does after splitting
54 if sys.platform.startswith('win'):
54 if sys.platform.startswith('win'):
55 cmd = shlex.split(cmd)
55 cmd = shlex.split(cmd)
56 proc = subprocess.Popen(cmd, shell=True)
56 proc = subprocess.Popen(cmd, shell=True)
57 if wait and proc.wait() != 0:
57 if proc.wait() != 0:
58 raise TryNext()
58 raise TryNext()
59 if wait:
59 if wait:
60 py3compat.input("Press Enter when done editing:")
60 py3compat.input("Press Enter when done editing:")
61
61
62 get_ipython().set_hook('editor', call_editor)
62 get_ipython().set_hook('editor', call_editor)
63 get_ipython().editor = template
63 get_ipython().editor = template
64
64
65
65
66 # in these, exe is always the path/name of the executable. Useful
66 # in these, exe is always the path/name of the executable. Useful
67 # if you don't have the editor directory in your path
67 # if you don't have the editor directory in your path
68 def komodo(exe=u'komodo'):
68 def komodo(exe=u'komodo'):
69 """ Activestate Komodo [Edit] """
69 """ Activestate Komodo [Edit] """
70 install_editor(exe + u' -l {line} {filename}', wait=True)
70 install_editor(exe + u' -l {line} {filename}', wait=True)
71
71
72
72
73 def scite(exe=u"scite"):
73 def scite(exe=u"scite"):
74 """ SciTE or Sc1 """
74 """ SciTE or Sc1 """
75 install_editor(exe + u' {filename} -goto:{line}')
75 install_editor(exe + u' {filename} -goto:{line}')
76
76
77
77
78 def notepadplusplus(exe=u'notepad++'):
78 def notepadplusplus(exe=u'notepad++'):
79 """ Notepad++ http://notepad-plus.sourceforge.net """
79 """ Notepad++ http://notepad-plus.sourceforge.net """
80 install_editor(exe + u' -n{line} {filename}')
80 install_editor(exe + u' -n{line} {filename}')
81
81
82
82
83 def jed(exe=u'jed'):
83 def jed(exe=u'jed'):
84 """ JED, the lightweight emacsish editor """
84 """ JED, the lightweight emacsish editor """
85 install_editor(exe + u' +{line} {filename}')
85 install_editor(exe + u' +{line} {filename}')
86
86
87
87
88 def idle(exe=u'idle'):
88 def idle(exe=u'idle'):
89 """ Idle, the editor bundled with python
89 """ Idle, the editor bundled with python
90
90
91 Parameters
91 Parameters
92 ----------
92 ----------
93 exe : str, None
93 exe : str, None
94 If none, should be pretty smart about finding the executable.
94 If none, should be pretty smart about finding the executable.
95 """
95 """
96 if exe is None:
96 if exe is None:
97 import idlelib
97 import idlelib
98 p = os.path.dirname(idlelib.__filename__)
98 p = os.path.dirname(idlelib.__filename__)
99 # i'm not sure if this actually works. Is this idle.py script
99 # i'm not sure if this actually works. Is this idle.py script
100 # guarenteed to be executable?
100 # guarenteed to be executable?
101 exe = os.path.join(p, 'idle.py')
101 exe = os.path.join(p, 'idle.py')
102 install_editor(exe + u' {filename}')
102 install_editor(exe + u' {filename}')
103
103
104
104
105 def mate(exe=u'mate'):
105 def mate(exe=u'mate'):
106 """ TextMate, the missing editor"""
106 """ TextMate, the missing editor"""
107 # wait=True is not required since we're using the -w flag to mate
107 # wait=True is not required since we're using the -w flag to mate
108 install_editor(exe + u' -w -l {line} {filename}')
108 install_editor(exe + u' -w -l {line} {filename}')
109
109
110
110
111 # ##########################################
111 # ##########################################
112 # these are untested, report any problems
112 # these are untested, report any problems
113 # ##########################################
113 # ##########################################
114
114
115
115
116 def emacs(exe=u'emacs'):
116 def emacs(exe=u'emacs'):
117 install_editor(exe + u' +{line} {filename}')
117 install_editor(exe + u' +{line} {filename}')
118
118
119
119
120 def gnuclient(exe=u'gnuclient'):
120 def gnuclient(exe=u'gnuclient'):
121 install_editor(exe + u' -nw +{line} {filename}')
121 install_editor(exe + u' -nw +{line} {filename}')
122
122
123
123
124 def crimson_editor(exe=u'cedt.exe'):
124 def crimson_editor(exe=u'cedt.exe'):
125 install_editor(exe + u' /L:{line} {filename}')
125 install_editor(exe + u' /L:{line} {filename}')
126
126
127
127
128 def kate(exe=u'kate'):
128 def kate(exe=u'kate'):
129 install_editor(exe + u' -u -l {line} {filename}')
129 install_editor(exe + u' -u -l {line} {filename}')
@@ -1,37 +1,38 b''
1 """Test installing editor hooks"""
1 """Test installing editor hooks"""
2 import sys
2 import sys
3
3
4 try:
4 try:
5 import mock
5 import mock
6 except ImportError:
6 except ImportError:
7 from unittest import mock
7 from unittest import mock
8
8
9 import nose.tools as nt
9 import nose.tools as nt
10
10
11 from IPython import get_ipython
11 from IPython import get_ipython
12 from IPython.lib import editorhooks
12 from IPython.lib import editorhooks
13
13
14 def test_install_editor():
14 def test_install_editor():
15 called = []
15 called = []
16 def fake_popen(*args, **kwargs):
16 def fake_popen(*args, **kwargs):
17 called.append({
17 called.append({
18 'args': args,
18 'args': args,
19 'kwargs': kwargs,
19 'kwargs': kwargs,
20 })
20 })
21 return mock.MagicMock(**{'wait.return_value': 0})
21 editorhooks.install_editor('foo -l {line} -f {filename}', wait=False)
22 editorhooks.install_editor('foo -l {line} -f {filename}', wait=False)
22
23
23 with mock.patch('subprocess.Popen', fake_popen):
24 with mock.patch('subprocess.Popen', fake_popen):
24 get_ipython().hooks.editor('the file', 64)
25 get_ipython().hooks.editor('the file', 64)
25
26
26 nt.assert_equal(len(called), 1)
27 nt.assert_equal(len(called), 1)
27 args = called[0]['args']
28 args = called[0]['args']
28 kwargs = called[0]['kwargs']
29 kwargs = called[0]['kwargs']
29
30
30 nt.assert_equal(kwargs, {'shell': True})
31 nt.assert_equal(kwargs, {'shell': True})
31
32
32 if sys.platform.startswith('win'):
33 if sys.platform.startswith('win'):
33 expected = ['foo', '-l', '64', '-f', 'the file']
34 expected = ['foo', '-l', '64', '-f', 'the file']
34 else:
35 else:
35 expected = "foo -l 64 -f 'the file'"
36 expected = "foo -l 64 -f 'the file'"
36 cmd = args[0]
37 cmd = args[0]
37 nt.assert_equal(cmd, expected)
38 nt.assert_equal(cmd, expected)
General Comments 0
You need to be logged in to leave comments. Login now