##// END OF EJS Templates
update
Matthias Bussonnier -
Show More
@@ -1,45 +1,34 b''
1 name: Run MyPy
1 name: Run MyPy
2
2
3 on:
3 on:
4 push:
4 push:
5 branches: [ master, 7.x]
5 branches: [ master, 7.x]
6 pull_request:
6 pull_request:
7 branches: [ master, 7.x]
7 branches: [ master, 7.x]
8
8
9 jobs:
9 jobs:
10 build:
10 build:
11
11
12 runs-on: ubuntu-latest
12 runs-on: ubuntu-latest
13 strategy:
13 strategy:
14 matrix:
14 matrix:
15 python-version: [3.8]
15 python-version: [3.8]
16
16
17 steps:
17 steps:
18 - uses: actions/checkout@v2
18 - uses: actions/checkout@v2
19 - name: Set up Python ${{ matrix.python-version }}
19 - name: Set up Python ${{ matrix.python-version }}
20 uses: actions/setup-python@v2
20 uses: actions/setup-python@v2
21 with:
21 with:
22 python-version: ${{ matrix.python-version }}
22 python-version: ${{ matrix.python-version }}
23 - name: Install dependencies
23 - name: Install dependencies
24 run: |
24 run: |
25 python -m pip install --upgrade pip
25 python -m pip install --upgrade pip
26 pip install mypy pyflakes flake8
26 pip install mypy pyflakes flake8
27 - name: Lint with mypy
27 - name: Lint with mypy
28 run: |
28 run: |
29 mypy IPython/terminal/ptutils.py
29 mypy -p IPython.terminal
30 mypy IPython/core/magics/*.py
30 mypy -p IPython.core.magics
31 mypy IPython/terminal/__init__.py
32 mypy IPython/terminal/console.py
33 #mypy IPython/terminal/debugger.py
34 #mypy IPython/terminal/embed.py
35 mypy IPython/terminal/interactiveshell.py
36 mypy IPython/terminal/ipapp.py
37 mypy IPython/terminal/magics.py
38 mypy IPython/terminal/prompts.py
39 mypy IPython/terminal/ptshell.py
40 mypy IPython/terminal/ptutils.py
41 mypy IPython/terminal/shortcuts.py
42 mypy IPython/core/c*.py
43 - name: Lint with pyflakes
31 - name: Lint with pyflakes
44 run: |
32 run: |
45 flake8 IPython/core/magics/script.py
33 flake8 IPython/core/magics/script.py
34 flake8 IPython/core/magics/packaging.py
@@ -1,166 +1,166 b''
1 import asyncio
1 import asyncio
2 import sys
2 import sys
3 import threading
3 import threading
4
4
5 from IPython.core.debugger import Pdb
5 from IPython.core.debugger import Pdb
6
6
7 from IPython.core.completer import IPCompleter
7 from IPython.core.completer import IPCompleter
8 from .ptutils import IPythonPTCompleter
8 from .ptutils import IPythonPTCompleter
9 from .shortcuts import create_ipython_shortcuts
9 from .shortcuts import create_ipython_shortcuts
10 from . import embed
10 from . import embed
11
11
12 from pygments.token import Token
12 from pygments.token import Token
13 from prompt_toolkit.shortcuts.prompt import PromptSession
13 from prompt_toolkit.shortcuts.prompt import PromptSession
14 from prompt_toolkit.enums import EditingMode
14 from prompt_toolkit.enums import EditingMode
15 from prompt_toolkit.formatted_text import PygmentsTokens
15 from prompt_toolkit.formatted_text import PygmentsTokens
16
16
17 from prompt_toolkit import __version__ as ptk_version
17 from prompt_toolkit import __version__ as ptk_version
18 PTK3 = ptk_version.startswith('3.')
18 PTK3 = ptk_version.startswith('3.')
19
19
20
20
21 class TerminalPdb(Pdb):
21 class TerminalPdb(Pdb):
22 """Standalone IPython debugger."""
22 """Standalone IPython debugger."""
23
23
24 def __init__(self, *args, pt_session_options=None, **kwargs):
24 def __init__(self, *args, pt_session_options=None, **kwargs):
25 Pdb.__init__(self, *args, **kwargs)
25 Pdb.__init__(self, *args, **kwargs)
26 self._ptcomp = None
26 self._ptcomp = None
27 self.pt_init(pt_session_options)
27 self.pt_init(pt_session_options)
28
28
29 def pt_init(self, pt_session_options=None):
29 def pt_init(self, pt_session_options=None):
30 """Initialize the prompt session and the prompt loop
30 """Initialize the prompt session and the prompt loop
31 and store them in self.pt_app and self.pt_loop.
31 and store them in self.pt_app and self.pt_loop.
32
32
33 Additional keyword arguments for the PromptSession class
33 Additional keyword arguments for the PromptSession class
34 can be specified in pt_session_options.
34 can be specified in pt_session_options.
35 """
35 """
36 if pt_session_options is None:
36 if pt_session_options is None:
37 pt_session_options = {}
37 pt_session_options = {}
38
38
39 def get_prompt_tokens():
39 def get_prompt_tokens():
40 return [(Token.Prompt, self.prompt)]
40 return [(Token.Prompt, self.prompt)]
41
41
42 if self._ptcomp is None:
42 if self._ptcomp is None:
43 compl = IPCompleter(
43 compl = IPCompleter(
44 shell=self.shell, namespace={}, global_namespace={}, parent=self.shell
44 shell=self.shell, namespace={}, global_namespace={}, parent=self.shell
45 )
45 )
46 # add a completer for all the do_ methods
46 # add a completer for all the do_ methods
47 methods_names = [m[3:] for m in dir(self) if m.startswith("do_")]
47 methods_names = [m[3:] for m in dir(self) if m.startswith("do_")]
48
48
49 def gen_comp(self, text):
49 def gen_comp(self, text):
50 return [m for m in methods_names if m.startswith(text)]
50 return [m for m in methods_names if m.startswith(text)]
51 import types
51 import types
52 newcomp = types.MethodType(gen_comp, compl)
52 newcomp = types.MethodType(gen_comp, compl)
53 compl.custom_matchers.insert(0, newcomp)
53 compl.custom_matchers.insert(0, newcomp)
54 # end add completer.
54 # end add completer.
55
55
56 self._ptcomp = IPythonPTCompleter(compl)
56 self._ptcomp = IPythonPTCompleter(compl)
57
57
58 options = dict(
58 options = dict(
59 message=(lambda: PygmentsTokens(get_prompt_tokens())),
59 message=(lambda: PygmentsTokens(get_prompt_tokens())),
60 editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()),
60 editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()),
61 key_bindings=create_ipython_shortcuts(self.shell),
61 key_bindings=create_ipython_shortcuts(self.shell),
62 history=self.shell.debugger_history,
62 history=self.shell.debugger_history,
63 completer=self._ptcomp,
63 completer=self._ptcomp,
64 enable_history_search=True,
64 enable_history_search=True,
65 mouse_support=self.shell.mouse_support,
65 mouse_support=self.shell.mouse_support,
66 complete_style=self.shell.pt_complete_style,
66 complete_style=self.shell.pt_complete_style,
67 style=self.shell.style,
67 style=self.shell.style,
68 color_depth=self.shell.color_depth,
68 color_depth=self.shell.color_depth,
69 )
69 )
70
70
71 if not PTK3:
71 if not PTK3:
72 options['inputhook'] = self.shell.inputhook
72 options['inputhook'] = self.shell.inputhook
73 options.update(pt_session_options)
73 options.update(pt_session_options)
74 self.pt_loop = asyncio.new_event_loop()
74 self.pt_loop = asyncio.new_event_loop()
75 self.pt_app = PromptSession(**options)
75 self.pt_app = PromptSession(**options)
76
76
77 def cmdloop(self, intro=None):
77 def cmdloop(self, intro=None):
78 """Repeatedly issue a prompt, accept input, parse an initial prefix
78 """Repeatedly issue a prompt, accept input, parse an initial prefix
79 off the received input, and dispatch to action methods, passing them
79 off the received input, and dispatch to action methods, passing them
80 the remainder of the line as argument.
80 the remainder of the line as argument.
81
81
82 override the same methods from cmd.Cmd to provide prompt toolkit replacement.
82 override the same methods from cmd.Cmd to provide prompt toolkit replacement.
83 """
83 """
84 if not self.use_rawinput:
84 if not self.use_rawinput:
85 raise ValueError('Sorry ipdb does not support use_rawinput=False')
85 raise ValueError('Sorry ipdb does not support use_rawinput=False')
86
86
87 # In order to make sure that prompt, which uses asyncio doesn't
87 # In order to make sure that prompt, which uses asyncio doesn't
88 # interfere with applications in which it's used, we always run the
88 # interfere with applications in which it's used, we always run the
89 # prompt itself in a different thread (we can't start an event loop
89 # prompt itself in a different thread (we can't start an event loop
90 # within an event loop). This new thread won't have any event loop
90 # within an event loop). This new thread won't have any event loop
91 # running, and here we run our prompt-loop.
91 # running, and here we run our prompt-loop.
92
92
93 self.preloop()
93 self.preloop()
94
94
95 try:
95 try:
96 if intro is not None:
96 if intro is not None:
97 self.intro = intro
97 self.intro = intro
98 if self.intro:
98 if self.intro:
99 self.stdout.write(str(self.intro)+"\n")
99 self.stdout.write(str(self.intro)+"\n")
100 stop = None
100 stop = None
101 while not stop:
101 while not stop:
102 if self.cmdqueue:
102 if self.cmdqueue:
103 line = self.cmdqueue.pop(0)
103 line = self.cmdqueue.pop(0)
104 else:
104 else:
105 self._ptcomp.ipy_completer.namespace = self.curframe_locals
105 self._ptcomp.ipy_completer.namespace = self.curframe_locals
106 self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals
106 self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals
107
107
108 # Run the prompt in a different thread.
108 # Run the prompt in a different thread.
109 line = ''
109 line = ''
110 keyboard_interrupt = False
110 keyboard_interrupt = False
111
111
112 def in_thread():
112 def in_thread():
113 nonlocal line, keyboard_interrupt
113 nonlocal line, keyboard_interrupt
114 try:
114 try:
115 line = self.pt_app.prompt()
115 line = self.pt_app.prompt()
116 except EOFError:
116 except EOFError:
117 line = 'EOF'
117 line = 'EOF'
118 except KeyboardInterrupt:
118 except KeyboardInterrupt:
119 keyboard_interrupt = True
119 keyboard_interrupt = True
120
120
121 th = threading.Thread(target=in_thread)
121 th = threading.Thread(target=in_thread)
122 th.start()
122 th.start()
123 th.join()
123 th.join()
124
124
125 if keyboard_interrupt:
125 if keyboard_interrupt:
126 raise KeyboardInterrupt
126 raise KeyboardInterrupt
127
127
128 line = self.precmd(line)
128 line = self.precmd(line)
129 stop = self.onecmd(line)
129 stop = self.onecmd(line)
130 stop = self.postcmd(stop, line)
130 stop = self.postcmd(stop, line)
131 self.postloop()
131 self.postloop()
132 except Exception:
132 except Exception:
133 raise
133 raise
134
134
135 def do_interact(self, arg):
135 def do_interact(self, arg):
136 ipshell = embed.InteractiveShellEmbed(
136 ipshell = embed.InteractiveShellEmbed(
137 config=self.shell.config,
137 config=self.shell.config,
138 banner1="*interactive*",
138 banner1="*interactive*",
139 exit_msg="*exiting interactive console...*",
139 exit_msg="*exiting interactive console...*",
140 )
140 )
141 global_ns = self.curframe.f_globals
141 global_ns = self.curframe.f_globals
142 ipshell(
142 ipshell(
143 module=sys.modules.get(global_ns["__name__"], None),
143 module=sys.modules.get(global_ns["__name__"], None),
144 local_ns=self.curframe_locals,
144 local_ns=self.curframe_locals,
145 )
145 )
146
146
147
147
148 def set_trace(frame=None):
148 def set_trace(frame=None):
149 """
149 """
150 Start debugging from `frame`.
150 Start debugging from `frame`.
151
151
152 If frame is not specified, debugging starts from caller's frame.
152 If frame is not specified, debugging starts from caller's frame.
153 """
153 """
154 TerminalPdb().set_trace(frame or sys._getframe().f_back)
154 TerminalPdb().set_trace(frame or sys._getframe().f_back)
155
155
156
156
157 if __name__ == '__main__':
157 if __name__ == '__main__':
158 import pdb
158 import pdb
159 # IPython.core.debugger.Pdb.trace_dispatch shall not catch
159 # IPython.core.debugger.Pdb.trace_dispatch shall not catch
160 # bdb.BdbQuit. When started through __main__ and an exception
160 # bdb.BdbQuit. When started through __main__ and an exception
161 # happened after hitting "c", this is needed in order to
161 # happened after hitting "c", this is needed in order to
162 # be able to quit the debugging session (see #9950).
162 # be able to quit the debugging session (see #9950).
163 old_trace_dispatch = pdb.Pdb.trace_dispatch
163 old_trace_dispatch = pdb.Pdb.trace_dispatch
164 pdb.Pdb = TerminalPdb
164 pdb.Pdb = TerminalPdb # type: ignore
165 pdb.Pdb.trace_dispatch = old_trace_dispatch
165 pdb.Pdb.trace_dispatch = old_trace_dispatch # type: ignore
166 pdb.main()
166 pdb.main()
@@ -1,399 +1,401 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 An embedded IPython shell.
3 An embedded IPython shell.
4 """
4 """
5 # Copyright (c) IPython Development Team.
5 # Copyright (c) IPython Development Team.
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7
7
8
8
9 import sys
9 import sys
10 import warnings
10 import warnings
11
11
12 from IPython.core import ultratb, compilerop
12 from IPython.core import ultratb, compilerop
13 from IPython.core import magic_arguments
13 from IPython.core import magic_arguments
14 from IPython.core.magic import Magics, magics_class, line_magic
14 from IPython.core.magic import Magics, magics_class, line_magic
15 from IPython.core.interactiveshell import DummyMod, InteractiveShell
15 from IPython.core.interactiveshell import DummyMod, InteractiveShell
16 from IPython.terminal.interactiveshell import TerminalInteractiveShell
16 from IPython.terminal.interactiveshell import TerminalInteractiveShell
17 from IPython.terminal.ipapp import load_default_config
17 from IPython.terminal.ipapp import load_default_config
18
18
19 from traitlets import Bool, CBool, Unicode
19 from traitlets import Bool, CBool, Unicode
20 from IPython.utils.io import ask_yes_no
20 from IPython.utils.io import ask_yes_no
21
21
22 from typing import Set
23
22 class KillEmbedded(Exception):pass
24 class KillEmbedded(Exception):pass
23
25
24 # kept for backward compatibility as IPython 6 was released with
26 # kept for backward compatibility as IPython 6 was released with
25 # the typo. See https://github.com/ipython/ipython/pull/10706
27 # the typo. See https://github.com/ipython/ipython/pull/10706
26 KillEmbeded = KillEmbedded
28 KillEmbeded = KillEmbedded
27
29
28 # This is an additional magic that is exposed in embedded shells.
30 # This is an additional magic that is exposed in embedded shells.
29 @magics_class
31 @magics_class
30 class EmbeddedMagics(Magics):
32 class EmbeddedMagics(Magics):
31
33
32 @line_magic
34 @line_magic
33 @magic_arguments.magic_arguments()
35 @magic_arguments.magic_arguments()
34 @magic_arguments.argument('-i', '--instance', action='store_true',
36 @magic_arguments.argument('-i', '--instance', action='store_true',
35 help='Kill instance instead of call location')
37 help='Kill instance instead of call location')
36 @magic_arguments.argument('-x', '--exit', action='store_true',
38 @magic_arguments.argument('-x', '--exit', action='store_true',
37 help='Also exit the current session')
39 help='Also exit the current session')
38 @magic_arguments.argument('-y', '--yes', action='store_true',
40 @magic_arguments.argument('-y', '--yes', action='store_true',
39 help='Do not ask confirmation')
41 help='Do not ask confirmation')
40 def kill_embedded(self, parameter_s=''):
42 def kill_embedded(self, parameter_s=''):
41 """%kill_embedded : deactivate for good the current embedded IPython
43 """%kill_embedded : deactivate for good the current embedded IPython
42
44
43 This function (after asking for confirmation) sets an internal flag so
45 This function (after asking for confirmation) sets an internal flag so
44 that an embedded IPython will never activate again for the given call
46 that an embedded IPython will never activate again for the given call
45 location. This is useful to permanently disable a shell that is being
47 location. This is useful to permanently disable a shell that is being
46 called inside a loop: once you've figured out what you needed from it,
48 called inside a loop: once you've figured out what you needed from it,
47 you may then kill it and the program will then continue to run without
49 you may then kill it and the program will then continue to run without
48 the interactive shell interfering again.
50 the interactive shell interfering again.
49
51
50
52
51 Kill Instance Option:
53 Kill Instance Option:
52
54
53 If for some reasons you need to kill the location where the instance
55 If for some reasons you need to kill the location where the instance
54 is created and not called, for example if you create a single
56 is created and not called, for example if you create a single
55 instance in one place and debug in many locations, you can use the
57 instance in one place and debug in many locations, you can use the
56 ``--instance`` option to kill this specific instance. Like for the
58 ``--instance`` option to kill this specific instance. Like for the
57 ``call location`` killing an "instance" should work even if it is
59 ``call location`` killing an "instance" should work even if it is
58 recreated within a loop.
60 recreated within a loop.
59
61
60 .. note::
62 .. note::
61
63
62 This was the default behavior before IPython 5.2
64 This was the default behavior before IPython 5.2
63
65
64 """
66 """
65
67
66 args = magic_arguments.parse_argstring(self.kill_embedded, parameter_s)
68 args = magic_arguments.parse_argstring(self.kill_embedded, parameter_s)
67 print(args)
69 print(args)
68 if args.instance:
70 if args.instance:
69 # let no ask
71 # let no ask
70 if not args.yes:
72 if not args.yes:
71 kill = ask_yes_no(
73 kill = ask_yes_no(
72 "Are you sure you want to kill this embedded instance? [y/N] ", 'n')
74 "Are you sure you want to kill this embedded instance? [y/N] ", 'n')
73 else:
75 else:
74 kill = True
76 kill = True
75 if kill:
77 if kill:
76 self.shell._disable_init_location()
78 self.shell._disable_init_location()
77 print("This embedded IPython instance will not reactivate anymore "
79 print("This embedded IPython instance will not reactivate anymore "
78 "once you exit.")
80 "once you exit.")
79 else:
81 else:
80 if not args.yes:
82 if not args.yes:
81 kill = ask_yes_no(
83 kill = ask_yes_no(
82 "Are you sure you want to kill this embedded call_location? [y/N] ", 'n')
84 "Are you sure you want to kill this embedded call_location? [y/N] ", 'n')
83 else:
85 else:
84 kill = True
86 kill = True
85 if kill:
87 if kill:
86 self.shell.embedded_active = False
88 self.shell.embedded_active = False
87 print("This embedded IPython call location will not reactivate anymore "
89 print("This embedded IPython call location will not reactivate anymore "
88 "once you exit.")
90 "once you exit.")
89
91
90 if args.exit:
92 if args.exit:
91 # Ask-exit does not really ask, it just set internals flags to exit
93 # Ask-exit does not really ask, it just set internals flags to exit
92 # on next loop.
94 # on next loop.
93 self.shell.ask_exit()
95 self.shell.ask_exit()
94
96
95
97
96 @line_magic
98 @line_magic
97 def exit_raise(self, parameter_s=''):
99 def exit_raise(self, parameter_s=''):
98 """%exit_raise Make the current embedded kernel exit and raise and exception.
100 """%exit_raise Make the current embedded kernel exit and raise and exception.
99
101
100 This function sets an internal flag so that an embedded IPython will
102 This function sets an internal flag so that an embedded IPython will
101 raise a `IPython.terminal.embed.KillEmbedded` Exception on exit, and then exit the current I. This is
103 raise a `IPython.terminal.embed.KillEmbedded` Exception on exit, and then exit the current I. This is
102 useful to permanently exit a loop that create IPython embed instance.
104 useful to permanently exit a loop that create IPython embed instance.
103 """
105 """
104
106
105 self.shell.should_raise = True
107 self.shell.should_raise = True
106 self.shell.ask_exit()
108 self.shell.ask_exit()
107
109
108
110
109
111
110 class InteractiveShellEmbed(TerminalInteractiveShell):
112 class InteractiveShellEmbed(TerminalInteractiveShell):
111
113
112 dummy_mode = Bool(False)
114 dummy_mode = Bool(False)
113 exit_msg = Unicode('')
115 exit_msg = Unicode('')
114 embedded = CBool(True)
116 embedded = CBool(True)
115 should_raise = CBool(False)
117 should_raise = CBool(False)
116 # Like the base class display_banner is not configurable, but here it
118 # Like the base class display_banner is not configurable, but here it
117 # is True by default.
119 # is True by default.
118 display_banner = CBool(True)
120 display_banner = CBool(True)
119 exit_msg = Unicode()
121 exit_msg = Unicode()
120
122
121 # When embedding, by default we don't change the terminal title
123 # When embedding, by default we don't change the terminal title
122 term_title = Bool(False,
124 term_title = Bool(False,
123 help="Automatically set the terminal title"
125 help="Automatically set the terminal title"
124 ).tag(config=True)
126 ).tag(config=True)
125
127
126 _inactive_locations = set()
128 _inactive_locations: Set[str] = set()
129
130 def _disable_init_location(self):
131 """Disable the current Instance creation location"""
132 InteractiveShellEmbed._inactive_locations.add(self._init_location_id)
127
133
128 @property
134 @property
129 def embedded_active(self):
135 def embedded_active(self):
130 return (self._call_location_id not in InteractiveShellEmbed._inactive_locations)\
136 return (self._call_location_id not in InteractiveShellEmbed._inactive_locations)\
131 and (self._init_location_id not in InteractiveShellEmbed._inactive_locations)
137 and (self._init_location_id not in InteractiveShellEmbed._inactive_locations)
132
138
133 def _disable_init_location(self):
134 """Disable the current Instance creation location"""
135 InteractiveShellEmbed._inactive_locations.add(self._init_location_id)
136
137 @embedded_active.setter
139 @embedded_active.setter
138 def embedded_active(self, value):
140 def embedded_active(self, value):
139 if value:
141 if value:
140 InteractiveShellEmbed._inactive_locations.discard(
142 InteractiveShellEmbed._inactive_locations.discard(
141 self._call_location_id)
143 self._call_location_id)
142 InteractiveShellEmbed._inactive_locations.discard(
144 InteractiveShellEmbed._inactive_locations.discard(
143 self._init_location_id)
145 self._init_location_id)
144 else:
146 else:
145 InteractiveShellEmbed._inactive_locations.add(
147 InteractiveShellEmbed._inactive_locations.add(
146 self._call_location_id)
148 self._call_location_id)
147
149
148 def __init__(self, **kw):
150 def __init__(self, **kw):
149 if kw.get('user_global_ns', None) is not None:
151 if kw.get('user_global_ns', None) is not None:
150 raise DeprecationWarning(
152 raise DeprecationWarning(
151 "Key word argument `user_global_ns` has been replaced by `user_module` since IPython 4.0.")
153 "Key word argument `user_global_ns` has been replaced by `user_module` since IPython 4.0.")
152
154
153 clid = kw.pop('_init_location_id', None)
155 clid = kw.pop('_init_location_id', None)
154 if not clid:
156 if not clid:
155 frame = sys._getframe(1)
157 frame = sys._getframe(1)
156 clid = '%s:%s' % (frame.f_code.co_filename, frame.f_lineno)
158 clid = '%s:%s' % (frame.f_code.co_filename, frame.f_lineno)
157 self._init_location_id = clid
159 self._init_location_id = clid
158
160
159 super(InteractiveShellEmbed,self).__init__(**kw)
161 super(InteractiveShellEmbed,self).__init__(**kw)
160
162
161 # don't use the ipython crash handler so that user exceptions aren't
163 # don't use the ipython crash handler so that user exceptions aren't
162 # trapped
164 # trapped
163 sys.excepthook = ultratb.FormattedTB(color_scheme=self.colors,
165 sys.excepthook = ultratb.FormattedTB(color_scheme=self.colors,
164 mode=self.xmode,
166 mode=self.xmode,
165 call_pdb=self.pdb)
167 call_pdb=self.pdb)
166
168
167 def init_sys_modules(self):
169 def init_sys_modules(self):
168 """
170 """
169 Explicitly overwrite :mod:`IPython.core.interactiveshell` to do nothing.
171 Explicitly overwrite :mod:`IPython.core.interactiveshell` to do nothing.
170 """
172 """
171 pass
173 pass
172
174
173 def init_magics(self):
175 def init_magics(self):
174 super(InteractiveShellEmbed, self).init_magics()
176 super(InteractiveShellEmbed, self).init_magics()
175 self.register_magics(EmbeddedMagics)
177 self.register_magics(EmbeddedMagics)
176
178
177 def __call__(self, header='', local_ns=None, module=None, dummy=None,
179 def __call__(self, header='', local_ns=None, module=None, dummy=None,
178 stack_depth=1, global_ns=None, compile_flags=None, **kw):
180 stack_depth=1, global_ns=None, compile_flags=None, **kw):
179 """Activate the interactive interpreter.
181 """Activate the interactive interpreter.
180
182
181 __call__(self,header='',local_ns=None,module=None,dummy=None) -> Start
183 __call__(self,header='',local_ns=None,module=None,dummy=None) -> Start
182 the interpreter shell with the given local and global namespaces, and
184 the interpreter shell with the given local and global namespaces, and
183 optionally print a header string at startup.
185 optionally print a header string at startup.
184
186
185 The shell can be globally activated/deactivated using the
187 The shell can be globally activated/deactivated using the
186 dummy_mode attribute. This allows you to turn off a shell used
188 dummy_mode attribute. This allows you to turn off a shell used
187 for debugging globally.
189 for debugging globally.
188
190
189 However, *each* time you call the shell you can override the current
191 However, *each* time you call the shell you can override the current
190 state of dummy_mode with the optional keyword parameter 'dummy'. For
192 state of dummy_mode with the optional keyword parameter 'dummy'. For
191 example, if you set dummy mode on with IPShell.dummy_mode = True, you
193 example, if you set dummy mode on with IPShell.dummy_mode = True, you
192 can still have a specific call work by making it as IPShell(dummy=False).
194 can still have a specific call work by making it as IPShell(dummy=False).
193 """
195 """
194
196
195 # we are called, set the underlying interactiveshell not to exit.
197 # we are called, set the underlying interactiveshell not to exit.
196 self.keep_running = True
198 self.keep_running = True
197
199
198 # If the user has turned it off, go away
200 # If the user has turned it off, go away
199 clid = kw.pop('_call_location_id', None)
201 clid = kw.pop('_call_location_id', None)
200 if not clid:
202 if not clid:
201 frame = sys._getframe(1)
203 frame = sys._getframe(1)
202 clid = '%s:%s' % (frame.f_code.co_filename, frame.f_lineno)
204 clid = '%s:%s' % (frame.f_code.co_filename, frame.f_lineno)
203 self._call_location_id = clid
205 self._call_location_id = clid
204
206
205 if not self.embedded_active:
207 if not self.embedded_active:
206 return
208 return
207
209
208 # Normal exits from interactive mode set this flag, so the shell can't
210 # Normal exits from interactive mode set this flag, so the shell can't
209 # re-enter (it checks this variable at the start of interactive mode).
211 # re-enter (it checks this variable at the start of interactive mode).
210 self.exit_now = False
212 self.exit_now = False
211
213
212 # Allow the dummy parameter to override the global __dummy_mode
214 # Allow the dummy parameter to override the global __dummy_mode
213 if dummy or (dummy != 0 and self.dummy_mode):
215 if dummy or (dummy != 0 and self.dummy_mode):
214 return
216 return
215
217
216 # self.banner is auto computed
218 # self.banner is auto computed
217 if header:
219 if header:
218 self.old_banner2 = self.banner2
220 self.old_banner2 = self.banner2
219 self.banner2 = self.banner2 + '\n' + header + '\n'
221 self.banner2 = self.banner2 + '\n' + header + '\n'
220 else:
222 else:
221 self.old_banner2 = ''
223 self.old_banner2 = ''
222
224
223 if self.display_banner:
225 if self.display_banner:
224 self.show_banner()
226 self.show_banner()
225
227
226 # Call the embedding code with a stack depth of 1 so it can skip over
228 # Call the embedding code with a stack depth of 1 so it can skip over
227 # our call and get the original caller's namespaces.
229 # our call and get the original caller's namespaces.
228 self.mainloop(local_ns, module, stack_depth=stack_depth,
230 self.mainloop(local_ns, module, stack_depth=stack_depth,
229 global_ns=global_ns, compile_flags=compile_flags)
231 global_ns=global_ns, compile_flags=compile_flags)
230
232
231 self.banner2 = self.old_banner2
233 self.banner2 = self.old_banner2
232
234
233 if self.exit_msg is not None:
235 if self.exit_msg is not None:
234 print(self.exit_msg)
236 print(self.exit_msg)
235
237
236 if self.should_raise:
238 if self.should_raise:
237 raise KillEmbedded('Embedded IPython raising error, as user requested.')
239 raise KillEmbedded('Embedded IPython raising error, as user requested.')
238
240
239
241
240 def mainloop(self, local_ns=None, module=None, stack_depth=0,
242 def mainloop(self, local_ns=None, module=None, stack_depth=0,
241 display_banner=None, global_ns=None, compile_flags=None):
243 display_banner=None, global_ns=None, compile_flags=None):
242 """Embeds IPython into a running python program.
244 """Embeds IPython into a running python program.
243
245
244 Parameters
246 Parameters
245 ----------
247 ----------
246
248
247 local_ns, module
249 local_ns, module
248 Working local namespace (a dict) and module (a module or similar
250 Working local namespace (a dict) and module (a module or similar
249 object). If given as None, they are automatically taken from the scope
251 object). If given as None, they are automatically taken from the scope
250 where the shell was called, so that program variables become visible.
252 where the shell was called, so that program variables become visible.
251
253
252 stack_depth : int
254 stack_depth : int
253 How many levels in the stack to go to looking for namespaces (when
255 How many levels in the stack to go to looking for namespaces (when
254 local_ns or module is None). This allows an intermediate caller to
256 local_ns or module is None). This allows an intermediate caller to
255 make sure that this function gets the namespace from the intended
257 make sure that this function gets the namespace from the intended
256 level in the stack. By default (0) it will get its locals and globals
258 level in the stack. By default (0) it will get its locals and globals
257 from the immediate caller.
259 from the immediate caller.
258
260
259 compile_flags
261 compile_flags
260 A bit field identifying the __future__ features
262 A bit field identifying the __future__ features
261 that are enabled, as passed to the builtin :func:`compile` function.
263 that are enabled, as passed to the builtin :func:`compile` function.
262 If given as None, they are automatically taken from the scope where
264 If given as None, they are automatically taken from the scope where
263 the shell was called.
265 the shell was called.
264
266
265 """
267 """
266
268
267 if (global_ns is not None) and (module is None):
269 if (global_ns is not None) and (module is None):
268 raise DeprecationWarning("'global_ns' keyword argument is deprecated, and has been removed in IPython 5.0 use `module` keyword argument instead.")
270 raise DeprecationWarning("'global_ns' keyword argument is deprecated, and has been removed in IPython 5.0 use `module` keyword argument instead.")
269
271
270 if (display_banner is not None):
272 if (display_banner is not None):
271 warnings.warn("The display_banner parameter is deprecated since IPython 4.0", DeprecationWarning)
273 warnings.warn("The display_banner parameter is deprecated since IPython 4.0", DeprecationWarning)
272
274
273 # Get locals and globals from caller
275 # Get locals and globals from caller
274 if ((local_ns is None or module is None or compile_flags is None)
276 if ((local_ns is None or module is None or compile_flags is None)
275 and self.default_user_namespaces):
277 and self.default_user_namespaces):
276 call_frame = sys._getframe(stack_depth).f_back
278 call_frame = sys._getframe(stack_depth).f_back
277
279
278 if local_ns is None:
280 if local_ns is None:
279 local_ns = call_frame.f_locals
281 local_ns = call_frame.f_locals
280 if module is None:
282 if module is None:
281 global_ns = call_frame.f_globals
283 global_ns = call_frame.f_globals
282 try:
284 try:
283 module = sys.modules[global_ns['__name__']]
285 module = sys.modules[global_ns['__name__']]
284 except KeyError:
286 except KeyError:
285 warnings.warn("Failed to get module %s" % \
287 warnings.warn("Failed to get module %s" % \
286 global_ns.get('__name__', 'unknown module')
288 global_ns.get('__name__', 'unknown module')
287 )
289 )
288 module = DummyMod()
290 module = DummyMod()
289 module.__dict__ = global_ns
291 module.__dict__ = global_ns
290 if compile_flags is None:
292 if compile_flags is None:
291 compile_flags = (call_frame.f_code.co_flags &
293 compile_flags = (call_frame.f_code.co_flags &
292 compilerop.PyCF_MASK)
294 compilerop.PyCF_MASK)
293
295
294 # Save original namespace and module so we can restore them after
296 # Save original namespace and module so we can restore them after
295 # embedding; otherwise the shell doesn't shut down correctly.
297 # embedding; otherwise the shell doesn't shut down correctly.
296 orig_user_module = self.user_module
298 orig_user_module = self.user_module
297 orig_user_ns = self.user_ns
299 orig_user_ns = self.user_ns
298 orig_compile_flags = self.compile.flags
300 orig_compile_flags = self.compile.flags
299
301
300 # Update namespaces and fire up interpreter
302 # Update namespaces and fire up interpreter
301
303
302 # The global one is easy, we can just throw it in
304 # The global one is easy, we can just throw it in
303 if module is not None:
305 if module is not None:
304 self.user_module = module
306 self.user_module = module
305
307
306 # But the user/local one is tricky: ipython needs it to store internal
308 # But the user/local one is tricky: ipython needs it to store internal
307 # data, but we also need the locals. We'll throw our hidden variables
309 # data, but we also need the locals. We'll throw our hidden variables
308 # like _ih and get_ipython() into the local namespace, but delete them
310 # like _ih and get_ipython() into the local namespace, but delete them
309 # later.
311 # later.
310 if local_ns is not None:
312 if local_ns is not None:
311 reentrant_local_ns = {k: v for (k, v) in local_ns.items() if k not in self.user_ns_hidden.keys()}
313 reentrant_local_ns = {k: v for (k, v) in local_ns.items() if k not in self.user_ns_hidden.keys()}
312 self.user_ns = reentrant_local_ns
314 self.user_ns = reentrant_local_ns
313 self.init_user_ns()
315 self.init_user_ns()
314
316
315 # Compiler flags
317 # Compiler flags
316 if compile_flags is not None:
318 if compile_flags is not None:
317 self.compile.flags = compile_flags
319 self.compile.flags = compile_flags
318
320
319 # make sure the tab-completer has the correct frame information, so it
321 # make sure the tab-completer has the correct frame information, so it
320 # actually completes using the frame's locals/globals
322 # actually completes using the frame's locals/globals
321 self.set_completer_frame()
323 self.set_completer_frame()
322
324
323 with self.builtin_trap, self.display_trap:
325 with self.builtin_trap, self.display_trap:
324 self.interact()
326 self.interact()
325
327
326 # now, purge out the local namespace of IPython's hidden variables.
328 # now, purge out the local namespace of IPython's hidden variables.
327 if local_ns is not None:
329 if local_ns is not None:
328 local_ns.update({k: v for (k, v) in self.user_ns.items() if k not in self.user_ns_hidden.keys()})
330 local_ns.update({k: v for (k, v) in self.user_ns.items() if k not in self.user_ns_hidden.keys()})
329
331
330
332
331 # Restore original namespace so shell can shut down when we exit.
333 # Restore original namespace so shell can shut down when we exit.
332 self.user_module = orig_user_module
334 self.user_module = orig_user_module
333 self.user_ns = orig_user_ns
335 self.user_ns = orig_user_ns
334 self.compile.flags = orig_compile_flags
336 self.compile.flags = orig_compile_flags
335
337
336
338
337 def embed(**kwargs):
339 def embed(**kwargs):
338 """Call this to embed IPython at the current point in your program.
340 """Call this to embed IPython at the current point in your program.
339
341
340 The first invocation of this will create an :class:`InteractiveShellEmbed`
342 The first invocation of this will create an :class:`InteractiveShellEmbed`
341 instance and then call it. Consecutive calls just call the already
343 instance and then call it. Consecutive calls just call the already
342 created instance.
344 created instance.
343
345
344 If you don't want the kernel to initialize the namespace
346 If you don't want the kernel to initialize the namespace
345 from the scope of the surrounding function,
347 from the scope of the surrounding function,
346 and/or you want to load full IPython configuration,
348 and/or you want to load full IPython configuration,
347 you probably want `IPython.start_ipython()` instead.
349 you probably want `IPython.start_ipython()` instead.
348
350
349 Here is a simple example::
351 Here is a simple example::
350
352
351 from IPython import embed
353 from IPython import embed
352 a = 10
354 a = 10
353 b = 20
355 b = 20
354 embed(header='First time')
356 embed(header='First time')
355 c = 30
357 c = 30
356 d = 40
358 d = 40
357 embed()
359 embed()
358
360
359 Full customization can be done by passing a :class:`Config` in as the
361 Full customization can be done by passing a :class:`Config` in as the
360 config argument.
362 config argument.
361 """
363 """
362 config = kwargs.get('config')
364 config = kwargs.get('config')
363 header = kwargs.pop('header', u'')
365 header = kwargs.pop('header', u'')
364 compile_flags = kwargs.pop('compile_flags', None)
366 compile_flags = kwargs.pop('compile_flags', None)
365 if config is None:
367 if config is None:
366 config = load_default_config()
368 config = load_default_config()
367 config.InteractiveShellEmbed = config.TerminalInteractiveShell
369 config.InteractiveShellEmbed = config.TerminalInteractiveShell
368 kwargs['config'] = config
370 kwargs['config'] = config
369 using = kwargs.get('using', 'sync')
371 using = kwargs.get('using', 'sync')
370 if using :
372 if using :
371 kwargs['config'].update({'TerminalInteractiveShell':{'loop_runner':using, 'colors':'NoColor', 'autoawait': using!='sync'}})
373 kwargs['config'].update({'TerminalInteractiveShell':{'loop_runner':using, 'colors':'NoColor', 'autoawait': using!='sync'}})
372 #save ps1/ps2 if defined
374 #save ps1/ps2 if defined
373 ps1 = None
375 ps1 = None
374 ps2 = None
376 ps2 = None
375 try:
377 try:
376 ps1 = sys.ps1
378 ps1 = sys.ps1
377 ps2 = sys.ps2
379 ps2 = sys.ps2
378 except AttributeError:
380 except AttributeError:
379 pass
381 pass
380 #save previous instance
382 #save previous instance
381 saved_shell_instance = InteractiveShell._instance
383 saved_shell_instance = InteractiveShell._instance
382 if saved_shell_instance is not None:
384 if saved_shell_instance is not None:
383 cls = type(saved_shell_instance)
385 cls = type(saved_shell_instance)
384 cls.clear_instance()
386 cls.clear_instance()
385 frame = sys._getframe(1)
387 frame = sys._getframe(1)
386 shell = InteractiveShellEmbed.instance(_init_location_id='%s:%s' % (
388 shell = InteractiveShellEmbed.instance(_init_location_id='%s:%s' % (
387 frame.f_code.co_filename, frame.f_lineno), **kwargs)
389 frame.f_code.co_filename, frame.f_lineno), **kwargs)
388 shell(header=header, stack_depth=2, compile_flags=compile_flags,
390 shell(header=header, stack_depth=2, compile_flags=compile_flags,
389 _call_location_id='%s:%s' % (frame.f_code.co_filename, frame.f_lineno))
391 _call_location_id='%s:%s' % (frame.f_code.co_filename, frame.f_lineno))
390 InteractiveShellEmbed.clear_instance()
392 InteractiveShellEmbed.clear_instance()
391 #restore previous instance
393 #restore previous instance
392 if saved_shell_instance is not None:
394 if saved_shell_instance is not None:
393 cls = type(saved_shell_instance)
395 cls = type(saved_shell_instance)
394 cls.clear_instance()
396 cls.clear_instance()
395 for subclass in cls._walk_mro():
397 for subclass in cls._walk_mro():
396 subclass._instance = saved_shell_instance
398 subclass._instance = saved_shell_instance
397 if ps1 is not None:
399 if ps1 is not None:
398 sys.ps1 = ps1
400 sys.ps1 = ps1
399 sys.ps2 = ps2
401 sys.ps2 = ps2
@@ -1,157 +1,157 b''
1 """Inputhook for OS X
1 """Inputhook for OS X
2
2
3 Calls NSApp / CoreFoundation APIs via ctypes.
3 Calls NSApp / CoreFoundation APIs via ctypes.
4 """
4 """
5
5
6 # obj-c boilerplate from appnope, used under BSD 2-clause
6 # obj-c boilerplate from appnope, used under BSD 2-clause
7
7
8 import ctypes
8 import ctypes
9 import ctypes.util
9 import ctypes.util
10 from threading import Event
10 from threading import Event
11
11
12 objc = ctypes.cdll.LoadLibrary(ctypes.util.find_library('objc'))
12 objc = ctypes.cdll.LoadLibrary(ctypes.util.find_library("objc")) # type: ignore
13
13
14 void_p = ctypes.c_void_p
14 void_p = ctypes.c_void_p
15
15
16 objc.objc_getClass.restype = void_p
16 objc.objc_getClass.restype = void_p
17 objc.sel_registerName.restype = void_p
17 objc.sel_registerName.restype = void_p
18 objc.objc_msgSend.restype = void_p
18 objc.objc_msgSend.restype = void_p
19 objc.objc_msgSend.argtypes = [void_p, void_p]
19 objc.objc_msgSend.argtypes = [void_p, void_p]
20
20
21 msg = objc.objc_msgSend
21 msg = objc.objc_msgSend
22
22
23 def _utf8(s):
23 def _utf8(s):
24 """ensure utf8 bytes"""
24 """ensure utf8 bytes"""
25 if not isinstance(s, bytes):
25 if not isinstance(s, bytes):
26 s = s.encode('utf8')
26 s = s.encode('utf8')
27 return s
27 return s
28
28
29 def n(name):
29 def n(name):
30 """create a selector name (for ObjC methods)"""
30 """create a selector name (for ObjC methods)"""
31 return objc.sel_registerName(_utf8(name))
31 return objc.sel_registerName(_utf8(name))
32
32
33 def C(classname):
33 def C(classname):
34 """get an ObjC Class by name"""
34 """get an ObjC Class by name"""
35 return objc.objc_getClass(_utf8(classname))
35 return objc.objc_getClass(_utf8(classname))
36
36
37 # end obj-c boilerplate from appnope
37 # end obj-c boilerplate from appnope
38
38
39 # CoreFoundation C-API calls we will use:
39 # CoreFoundation C-API calls we will use:
40 CoreFoundation = ctypes.cdll.LoadLibrary(ctypes.util.find_library('CoreFoundation'))
40 CoreFoundation = ctypes.cdll.LoadLibrary(ctypes.util.find_library("CoreFoundation")) # type: ignore
41
41
42 CFFileDescriptorCreate = CoreFoundation.CFFileDescriptorCreate
42 CFFileDescriptorCreate = CoreFoundation.CFFileDescriptorCreate
43 CFFileDescriptorCreate.restype = void_p
43 CFFileDescriptorCreate.restype = void_p
44 CFFileDescriptorCreate.argtypes = [void_p, ctypes.c_int, ctypes.c_bool, void_p, void_p]
44 CFFileDescriptorCreate.argtypes = [void_p, ctypes.c_int, ctypes.c_bool, void_p, void_p]
45
45
46 CFFileDescriptorGetNativeDescriptor = CoreFoundation.CFFileDescriptorGetNativeDescriptor
46 CFFileDescriptorGetNativeDescriptor = CoreFoundation.CFFileDescriptorGetNativeDescriptor
47 CFFileDescriptorGetNativeDescriptor.restype = ctypes.c_int
47 CFFileDescriptorGetNativeDescriptor.restype = ctypes.c_int
48 CFFileDescriptorGetNativeDescriptor.argtypes = [void_p]
48 CFFileDescriptorGetNativeDescriptor.argtypes = [void_p]
49
49
50 CFFileDescriptorEnableCallBacks = CoreFoundation.CFFileDescriptorEnableCallBacks
50 CFFileDescriptorEnableCallBacks = CoreFoundation.CFFileDescriptorEnableCallBacks
51 CFFileDescriptorEnableCallBacks.restype = None
51 CFFileDescriptorEnableCallBacks.restype = None
52 CFFileDescriptorEnableCallBacks.argtypes = [void_p, ctypes.c_ulong]
52 CFFileDescriptorEnableCallBacks.argtypes = [void_p, ctypes.c_ulong]
53
53
54 CFFileDescriptorCreateRunLoopSource = CoreFoundation.CFFileDescriptorCreateRunLoopSource
54 CFFileDescriptorCreateRunLoopSource = CoreFoundation.CFFileDescriptorCreateRunLoopSource
55 CFFileDescriptorCreateRunLoopSource.restype = void_p
55 CFFileDescriptorCreateRunLoopSource.restype = void_p
56 CFFileDescriptorCreateRunLoopSource.argtypes = [void_p, void_p, void_p]
56 CFFileDescriptorCreateRunLoopSource.argtypes = [void_p, void_p, void_p]
57
57
58 CFRunLoopGetCurrent = CoreFoundation.CFRunLoopGetCurrent
58 CFRunLoopGetCurrent = CoreFoundation.CFRunLoopGetCurrent
59 CFRunLoopGetCurrent.restype = void_p
59 CFRunLoopGetCurrent.restype = void_p
60
60
61 CFRunLoopAddSource = CoreFoundation.CFRunLoopAddSource
61 CFRunLoopAddSource = CoreFoundation.CFRunLoopAddSource
62 CFRunLoopAddSource.restype = None
62 CFRunLoopAddSource.restype = None
63 CFRunLoopAddSource.argtypes = [void_p, void_p, void_p]
63 CFRunLoopAddSource.argtypes = [void_p, void_p, void_p]
64
64
65 CFRelease = CoreFoundation.CFRelease
65 CFRelease = CoreFoundation.CFRelease
66 CFRelease.restype = None
66 CFRelease.restype = None
67 CFRelease.argtypes = [void_p]
67 CFRelease.argtypes = [void_p]
68
68
69 CFFileDescriptorInvalidate = CoreFoundation.CFFileDescriptorInvalidate
69 CFFileDescriptorInvalidate = CoreFoundation.CFFileDescriptorInvalidate
70 CFFileDescriptorInvalidate.restype = None
70 CFFileDescriptorInvalidate.restype = None
71 CFFileDescriptorInvalidate.argtypes = [void_p]
71 CFFileDescriptorInvalidate.argtypes = [void_p]
72
72
73 # From CFFileDescriptor.h
73 # From CFFileDescriptor.h
74 kCFFileDescriptorReadCallBack = 1
74 kCFFileDescriptorReadCallBack = 1
75 kCFRunLoopCommonModes = void_p.in_dll(CoreFoundation, 'kCFRunLoopCommonModes')
75 kCFRunLoopCommonModes = void_p.in_dll(CoreFoundation, 'kCFRunLoopCommonModes')
76
76
77
77
78 def _NSApp():
78 def _NSApp():
79 """Return the global NSApplication instance (NSApp)"""
79 """Return the global NSApplication instance (NSApp)"""
80 objc.objc_msgSend.argtypes = [void_p, void_p]
80 objc.objc_msgSend.argtypes = [void_p, void_p]
81 return msg(C('NSApplication'), n('sharedApplication'))
81 return msg(C('NSApplication'), n('sharedApplication'))
82
82
83
83
84 def _wake(NSApp):
84 def _wake(NSApp):
85 """Wake the Application"""
85 """Wake the Application"""
86 objc.objc_msgSend.argtypes = [
86 objc.objc_msgSend.argtypes = [
87 void_p,
87 void_p,
88 void_p,
88 void_p,
89 void_p,
89 void_p,
90 void_p,
90 void_p,
91 void_p,
91 void_p,
92 void_p,
92 void_p,
93 void_p,
93 void_p,
94 void_p,
94 void_p,
95 void_p,
95 void_p,
96 void_p,
96 void_p,
97 void_p,
97 void_p,
98 ]
98 ]
99 event = msg(
99 event = msg(
100 C("NSEvent"),
100 C("NSEvent"),
101 n(
101 n(
102 "otherEventWithType:location:modifierFlags:"
102 "otherEventWithType:location:modifierFlags:"
103 "timestamp:windowNumber:context:subtype:data1:data2:"
103 "timestamp:windowNumber:context:subtype:data1:data2:"
104 ),
104 ),
105 15, # Type
105 15, # Type
106 0, # location
106 0, # location
107 0, # flags
107 0, # flags
108 0, # timestamp
108 0, # timestamp
109 0, # window
109 0, # window
110 None, # context
110 None, # context
111 0, # subtype
111 0, # subtype
112 0, # data1
112 0, # data1
113 0, # data2
113 0, # data2
114 )
114 )
115 objc.objc_msgSend.argtypes = [void_p, void_p, void_p, void_p]
115 objc.objc_msgSend.argtypes = [void_p, void_p, void_p, void_p]
116 msg(NSApp, n('postEvent:atStart:'), void_p(event), True)
116 msg(NSApp, n('postEvent:atStart:'), void_p(event), True)
117
117
118
118
119 _triggered = Event()
119 _triggered = Event()
120
120
121 def _input_callback(fdref, flags, info):
121 def _input_callback(fdref, flags, info):
122 """Callback to fire when there's input to be read"""
122 """Callback to fire when there's input to be read"""
123 _triggered.set()
123 _triggered.set()
124 CFFileDescriptorInvalidate(fdref)
124 CFFileDescriptorInvalidate(fdref)
125 CFRelease(fdref)
125 CFRelease(fdref)
126 NSApp = _NSApp()
126 NSApp = _NSApp()
127 objc.objc_msgSend.argtypes = [void_p, void_p, void_p]
127 objc.objc_msgSend.argtypes = [void_p, void_p, void_p]
128 msg(NSApp, n('stop:'), NSApp)
128 msg(NSApp, n('stop:'), NSApp)
129 _wake(NSApp)
129 _wake(NSApp)
130
130
131 _c_callback_func_type = ctypes.CFUNCTYPE(None, void_p, void_p, void_p)
131 _c_callback_func_type = ctypes.CFUNCTYPE(None, void_p, void_p, void_p)
132 _c_input_callback = _c_callback_func_type(_input_callback)
132 _c_input_callback = _c_callback_func_type(_input_callback)
133
133
134
134
135 def _stop_on_read(fd):
135 def _stop_on_read(fd):
136 """Register callback to stop eventloop when there's data on fd"""
136 """Register callback to stop eventloop when there's data on fd"""
137 _triggered.clear()
137 _triggered.clear()
138 fdref = CFFileDescriptorCreate(None, fd, False, _c_input_callback, None)
138 fdref = CFFileDescriptorCreate(None, fd, False, _c_input_callback, None)
139 CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack)
139 CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack)
140 source = CFFileDescriptorCreateRunLoopSource(None, fdref, 0)
140 source = CFFileDescriptorCreateRunLoopSource(None, fdref, 0)
141 loop = CFRunLoopGetCurrent()
141 loop = CFRunLoopGetCurrent()
142 CFRunLoopAddSource(loop, source, kCFRunLoopCommonModes)
142 CFRunLoopAddSource(loop, source, kCFRunLoopCommonModes)
143 CFRelease(source)
143 CFRelease(source)
144
144
145
145
146 def inputhook(context):
146 def inputhook(context):
147 """Inputhook for Cocoa (NSApp)"""
147 """Inputhook for Cocoa (NSApp)"""
148 NSApp = _NSApp()
148 NSApp = _NSApp()
149 _stop_on_read(context.fileno())
149 _stop_on_read(context.fileno())
150 objc.objc_msgSend.argtypes = [void_p, void_p]
150 objc.objc_msgSend.argtypes = [void_p, void_p]
151 msg(NSApp, n('run'))
151 msg(NSApp, n('run'))
152 if not _triggered.is_set():
152 if not _triggered.is_set():
153 # app closed without firing callback,
153 # app closed without firing callback,
154 # probably due to last window being closed.
154 # probably due to last window being closed.
155 # Run the loop manually in this case,
155 # Run the loop manually in this case,
156 # since there may be events still to process (#9734)
156 # since there may be events still to process (#9734)
157 CoreFoundation.CFRunLoopRun()
157 CoreFoundation.CFRunLoopRun()
General Comments 0
You need to be logged in to leave comments. Login now