##// END OF EJS Templates
Merge pull request #1 from ipython/master...
Kousik Mitra -
r25329:875db0e2 merge
parent child Browse files
Show More
@@ -0,0 +1,64 b''
1 """
2 Inputhook for running the original asyncio event loop while we're waiting for
3 input.
4
5 By default, in IPython, we run the prompt with a different asyncio event loop,
6 because otherwise we risk that people are freezing the prompt by scheduling bad
7 coroutines. E.g., a coroutine that does a while/true and never yield back
8 control to the loop. We can't cancel that.
9
10 However, sometimes we want the asyncio loop to keep running while waiting for
11 a prompt.
12
13 The following example will print the numbers from 1 to 10 above the prompt,
14 while we are waiting for input. (This works also because we use
15 prompt_toolkit`s `patch_stdout`)::
16
17 In [1]: import asyncio
18
19 In [2]: %gui asyncio
20
21 In [3]: async def f():
22 ...: for i in range(10):
23 ...: await asyncio.sleep(1)
24 ...: print(i)
25
26
27 In [4]: asyncio.ensure_future(f())
28
29 """
30 import asyncio
31 from prompt_toolkit import __version__ as ptk_version
32
33 PTK3 = ptk_version.startswith('3.')
34
35
36 # Keep reference to the original asyncio loop, because getting the event loop
37 # within the input hook would return the other loop.
38 loop = asyncio.get_event_loop()
39
40
41 def inputhook(context):
42 """
43 Inputhook for asyncio event loop integration.
44 """
45 # For prompt_toolkit 3.0, this input hook literally doesn't do anything.
46 # The event loop integration here is implemented in `interactiveshell.py`
47 # by running the prompt itself in the current asyncio loop. The main reason
48 # for this is that nesting asyncio event loops is unreliable.
49 if PTK3:
50 return
51
52 # For prompt_toolkit 2.0, we can run the current asyncio event loop,
53 # because prompt_toolkit 2.0 uses a different event loop internally.
54
55 def stop():
56 loop.stop()
57
58 fileno = context.fileno()
59 loop.add_reader(fileno, stop)
60 try:
61 loop.run_forever()
62 finally:
63 loop.remove_reader(fileno)
64
@@ -0,0 +1,60 b''
1
2 .. _shell_mimerenderer:
3
4
5 Mime Renderer Extensions
6 ========================
7
8 Like it's cousins, Jupyter Notebooks and JupyterLab, Terminal IPython can be
9 thought to render a number of mimetypes in the shell. This can be used to either
10 display inline images if your terminal emulator supports it; or open some
11 display results with external file viewers.
12
13 Registering new mimetype handlers can so far only be done my extensions and
14 requires 4 steps:
15
16 - Define a callable that takes 2 parameters:``data`` and ``metadata``; return
17 value of the callable is so far ignored. This callable is responsible for
18 "displaying" the given mimetype. Which can be sending the right escape
19 sequences and bytes to the current terminal; or open an external program. -
20 - Appending the right mimetype to ``ipython.display_formatter.active_types``
21 for IPython to know it should not ignore those mimetypes.
22 - Enabling the given mimetype: ``ipython.display_formatter.formatters[mime].enabled = True``
23 - Registering above callable with mimetype handler:
24 ``ipython.mime_renderers[mime] = handler``
25
26
27 Here is a complete IPython extension to display images inline and convert math
28 to png, before displaying it inline for iterm2 on macOS ::
29
30
31 from base64 import encodebytes
32 from IPython.lib.latextools import latex_to_png
33
34
35 def mathcat(data, meta):
36 png = latex_to_png(f'$${data}$$'.replace('\displaystyle', '').replace('$$$', '$$'))
37 imcat(png, meta)
38
39 IMAGE_CODE = '\033]1337;File=name=name;inline=true;:{}\a'
40
41 def imcat(image_data, metadata):
42 try:
43 print(IMAGE_CODE.format(encodebytes(image_data).decode()))
44 # bug workaround
45 except:
46 print(IMAGE_CODE.format(image_data))
47
48 def register_mimerenderer(ipython, mime, handler):
49 ipython.display_formatter.active_types.append(mime)
50 ipython.display_formatter.formatters[mime].enabled = True
51 ipython.mime_renderers[mime] = handler
52
53 def load_ipython_extension(ipython):
54 register_mimerenderer(ipython, 'image/png', imcat)
55 register_mimerenderer(ipython, 'image/jpeg', imcat)
56 register_mimerenderer(ipython, 'text/latex', mathcat)
57
58 This example only work for iterm2 on macOS and skip error handling for brevity.
59 One could also invoke an external viewer with ``subprocess.run()`` and a
60 temporary file, which is left as an exercise.
@@ -29,6 +29,8 b' from IPython.core.release import author_email'
29 29 from IPython.utils.sysinfo import sys_info
30 30 from IPython.utils.py3compat import input
31 31
32 from IPython.core.release import __version__ as version
33
32 34 #-----------------------------------------------------------------------------
33 35 # Code
34 36 #-----------------------------------------------------------------------------
@@ -68,7 +70,7 b' To ensure accurate tracking of this issue, please file a report about it at:'
68 70 """
69 71
70 72 _lite_message_template = """
71 If you suspect this is an IPython bug, please report it at:
73 If you suspect this is an IPython {version} bug, please report it at:
72 74 https://github.com/ipython/ipython/issues
73 75 or send an email to the mailing list at {email}
74 76
@@ -222,5 +224,5 b' def crash_handler_lite(etype, evalue, tb):'
222 224 else:
223 225 # we are not in a shell, show generic config
224 226 config = "c."
225 print(_lite_message_template.format(email=author_email, config=config), file=sys.stderr)
227 print(_lite_message_template.format(email=author_email, config=config, version=version), file=sys.stderr)
226 228
@@ -153,7 +153,7 b' class DisplayHook(Configurable):'
153 153 # This can be set to True by the write_output_prompt method in a subclass
154 154 prompt_end_newline = False
155 155
156 def write_format_data(self, format_dict, md_dict=None):
156 def write_format_data(self, format_dict, md_dict=None) -> None:
157 157 """Write the format data dict to the frontend.
158 158
159 159 This default version of this method simply writes the plain text
@@ -19,7 +19,7 b' spec.'
19 19 import sys
20 20
21 21 from traitlets.config.configurable import Configurable
22 from traitlets import List
22 from traitlets import List, Dict
23 23
24 24 # This used to be defined here - it is imported for backwards compatibility
25 25 from .display import publish_display_data
@@ -28,6 +28,7 b' from .display import publish_display_data'
28 28 # Main payload class
29 29 #-----------------------------------------------------------------------------
30 30
31
31 32 class DisplayPublisher(Configurable):
32 33 """A traited class that publishes display data to frontends.
33 34
@@ -35,6 +36,10 b' class DisplayPublisher(Configurable):'
35 36 be accessed there.
36 37 """
37 38
39 def __init__(self, shell=None, *args, **kwargs):
40 self.shell = shell
41 super().__init__(*args, **kwargs)
42
38 43 def _validate_data(self, data, metadata=None):
39 44 """Validate the display data.
40 45
@@ -53,7 +58,7 b' class DisplayPublisher(Configurable):'
53 58 raise TypeError('metadata must be a dict, got: %r' % data)
54 59
55 60 # use * to indicate transient, update are keyword-only
56 def publish(self, data, metadata=None, source=None, *, transient=None, update=False, **kwargs):
61 def publish(self, data, metadata=None, source=None, *, transient=None, update=False, **kwargs) -> None:
57 62 """Publish data and metadata to all frontends.
58 63
59 64 See the ``display_data`` message in the messaging documentation for
@@ -98,7 +103,15 b' class DisplayPublisher(Configurable):'
98 103 rather than creating a new output.
99 104 """
100 105
101 # The default is to simply write the plain text data using sys.stdout.
106 handlers = {}
107 if self.shell is not None:
108 handlers = getattr(self.shell, 'mime_renderers', {})
109
110 for mime, handler in handlers.items():
111 if mime in data:
112 handler(data[mime], metadata.get(mime, None))
113 return
114
102 115 if 'text/plain' in data:
103 116 print(data['text/plain'])
104 117
@@ -857,7 +857,7 b' class InteractiveShell(SingletonConfigurable):'
857 857 self.configurables.append(self.display_formatter)
858 858
859 859 def init_display_pub(self):
860 self.display_pub = self.display_pub_class(parent=self)
860 self.display_pub = self.display_pub_class(parent=self, shell=self)
861 861 self.configurables.append(self.display_pub)
862 862
863 863 def init_data_pub(self):
@@ -853,6 +853,8 b' python-profiler package from non-free.""")'
853 853 sys.argv = save_argv
854 854 if restore_main:
855 855 sys.modules['__main__'] = restore_main
856 if '__mp_main__' in sys.modules:
857 sys.modules['__mp_main__'] = restore_main
856 858 else:
857 859 # Remove from sys.modules the reference to main_mod we'd
858 860 # added. Otherwise it will trap references to objects
@@ -76,7 +76,7 b" info_fields = ['type_name', 'base_class', 'string_form', 'namespace',"
76 76 'call_def', 'call_docstring',
77 77 # These won't be printed but will be used to determine how to
78 78 # format the object
79 'ismagic', 'isalias', 'isclass', 'argspec', 'found', 'name'
79 'ismagic', 'isalias', 'isclass', 'found', 'name'
80 80 ]
81 81
82 82
@@ -200,26 +200,39 b' def is_simple_callable(obj):'
200 200 return (inspect.isfunction(obj) or inspect.ismethod(obj) or \
201 201 isinstance(obj, _builtin_func_type) or isinstance(obj, _builtin_meth_type))
202 202
203
203 @undoc
204 204 def getargspec(obj):
205 205 """Wrapper around :func:`inspect.getfullargspec` on Python 3, and
206 206 :func:inspect.getargspec` on Python 2.
207 207
208 208 In addition to functions and methods, this can also handle objects with a
209 209 ``__call__`` attribute.
210
211 DEPRECATED: Deprecated since 7.10. Do not use, will be removed.
210 212 """
213
214 warnings.warn('`getargspec` function is deprecated as of IPython 7.10'
215 'and will be removed in future versions.', DeprecationWarning, stacklevel=2)
216
211 217 if safe_hasattr(obj, '__call__') and not is_simple_callable(obj):
212 218 obj = obj.__call__
213 219
214 220 return inspect.getfullargspec(obj)
215 221
216
222 @undoc
217 223 def format_argspec(argspec):
218 224 """Format argspect, convenience wrapper around inspect's.
219 225
220 226 This takes a dict instead of ordered arguments and calls
221 227 inspect.format_argspec with the arguments in the necessary order.
228
229 DEPRECATED: Do not use; will be removed in future versions.
222 230 """
231
232 warnings.warn('`format_argspec` function is deprecated as of IPython 7.10'
233 'and will be removed in future versions.', DeprecationWarning, stacklevel=2)
234
235
223 236 return inspect.formatargspec(argspec['args'], argspec['varargs'],
224 237 argspec['varkw'], argspec['defaults'])
225 238
@@ -916,33 +929,6 b' class Inspector(Colorable):'
916 929 if call_ds:
917 930 out['call_docstring'] = call_ds
918 931
919 # Compute the object's argspec as a callable. The key is to decide
920 # whether to pull it from the object itself, from its __init__ or
921 # from its __call__ method.
922
923 if inspect.isclass(obj):
924 # Old-style classes need not have an __init__
925 callable_obj = getattr(obj, "__init__", None)
926 elif callable(obj):
927 callable_obj = obj
928 else:
929 callable_obj = None
930
931 if callable_obj is not None:
932 try:
933 argspec = getargspec(callable_obj)
934 except Exception:
935 # For extensions/builtins we can't retrieve the argspec
936 pass
937 else:
938 # named tuples' _asdict() method returns an OrderedDict, but we
939 # we want a normal
940 out['argspec'] = argspec_dict = dict(argspec._asdict())
941 # We called this varkw before argspec became a named tuple.
942 # With getfullargspec it's also called varkw.
943 if 'varkw' not in argspec_dict:
944 argspec_dict['varkw'] = argspec_dict.pop('keywords')
945
946 932 return object_info(**out)
947 933
948 934 @staticmethod
@@ -15,9 +15,11 b' rid of that dependency, we could move it there.'
15 15
16 16
17 17 import os
18 import io
18 19 import re
19 20 import sys
20 21 import tempfile
22 import subprocess
21 23
22 24 from io import UnsupportedOperation
23 25
@@ -208,9 +210,13 b' def pager_page(strng, start=0, screen_lines=0, pager_cmd=None):'
208 210 else:
209 211 try:
210 212 retval = None
211 # if I use popen4, things hang. No idea why.
212 #pager,shell_out = os.popen4(pager_cmd)
213 pager = os.popen(pager_cmd, 'w')
213 # Emulate os.popen, but redirect stderr
214 proc = subprocess.Popen(pager_cmd,
215 shell=True,
216 stdin=subprocess.PIPE,
217 stderr=subprocess.DEVNULL
218 )
219 pager = os._wrap_close(io.TextIOWrapper(proc.stdin), proc)
214 220 try:
215 221 pager_encoding = pager.encoding or sys.stdout.encoding
216 222 pager.write(strng)
@@ -20,7 +20,7 b" name = 'ipython'"
20 20 # release. 'dev' as a _version_extra string means this is a development
21 21 # version
22 22 _version_major = 7
23 _version_minor = 10
23 _version_minor = 11
24 24 _version_patch = 0
25 25 _version_extra = '.dev'
26 26 # _version_extra = 'b1'
@@ -7,7 +7,6 b" with better-isolated tests that don't rely on the global instance in iptest."
7 7 """
8 8 from IPython.core.splitinput import LineInfo
9 9 from IPython.core.prefilter import AutocallChecker
10 from IPython.utils import py3compat
11 10
12 11 def doctest_autocall():
13 12 """
@@ -49,11 +49,11 b' def test_handlers():'
49 49 # For many of the below, we're also checking that leading whitespace
50 50 # turns off the esc char, which it should unless there is a continuation
51 51 # line.
52 run([(i,py3compat.u_format(o)) for i,o in \
52 run(
53 53 [('"no change"', '"no change"'), # normal
54 54 (u"lsmagic", "get_ipython().run_line_magic('lsmagic', '')"), # magic
55 55 #("a = b # PYTHON-MODE", '_i'), # emacs -- avoids _in cache
56 ]])
56 ])
57 57
58 58 # Objects which are instances of IPyAutocall are *always* autocalled
59 59 autocallable = Autocallable()
@@ -539,6 +539,35 b' def test_run_tb():'
539 539 del ip.user_ns['bar']
540 540 del ip.user_ns['foo']
541 541
542
543 def test_multiprocessing_run():
544 """Set we can run mutiprocesgin without messing up up main namespace
545
546 Note that import `nose.tools as nt` mdify the value s
547 sys.module['__mp_main__'] so wee need to temporarily set it to None to test
548 the issue.
549 """
550 with TemporaryDirectory() as td:
551 mpm = sys.modules.get('__mp_main__')
552 assert mpm is not None
553 sys.modules['__mp_main__'] = None
554 try:
555 path = pjoin(td, 'test.py')
556 with open(path, 'w') as f:
557 f.write("import multiprocessing\nprint('hoy')")
558 with capture_output() as io:
559 _ip.run_line_magic('run', path)
560 _ip.run_cell("i_m_undefined")
561 out = io.stdout
562 nt.assert_in("hoy", out)
563 nt.assert_not_in("AttributeError", out)
564 nt.assert_in("NameError", out)
565 nt.assert_equal(out.count("---->"), 1)
566 except:
567 raise
568 finally:
569 sys.modules['__mp_main__'] = mpm
570
542 571 @dec.knownfailureif(sys.platform == 'win32', "writes to io.stdout aren't captured on Windows")
543 572 def test_script_tb():
544 573 """Test traceback offset in `ipython script.py`"""
@@ -296,6 +296,25 b' except Exception:'
296 296 tt.AssertPrints("ValueError", suppress=False):
297 297 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
298 298
299 def test_plain_direct_cause_error(self):
300 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
301 ip.run_cell("%xmode Plain")
302 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
303 ip.run_cell("%xmode Verbose")
304
305 def test_plain_exception_during_handling_error(self):
306 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
307 ip.run_cell("%xmode Plain")
308 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
309 ip.run_cell("%xmode Verbose")
310
311 def test_plain_suppress_exception_chaining(self):
312 with tt.AssertNotPrints("ZeroDivisionError"), \
313 tt.AssertPrints("ValueError", suppress=False):
314 ip.run_cell("%xmode Plain")
315 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
316 ip.run_cell("%xmode Verbose")
317
299 318
300 319 class RecursionTest(unittest.TestCase):
301 320 DEFINITIONS = """
@@ -530,6 +530,30 b' class TBTools(colorable.Colorable):'
530 530
531 531 ostream = property(_get_ostream, _set_ostream)
532 532
533 def get_parts_of_chained_exception(self, evalue):
534 def get_chained_exception(exception_value):
535 cause = getattr(exception_value, '__cause__', None)
536 if cause:
537 return cause
538 if getattr(exception_value, '__suppress_context__', False):
539 return None
540 return getattr(exception_value, '__context__', None)
541
542 chained_evalue = get_chained_exception(evalue)
543
544 if chained_evalue:
545 return chained_evalue.__class__, chained_evalue, chained_evalue.__traceback__
546
547 def prepare_chained_exception_message(self, cause):
548 direct_cause = "\nThe above exception was the direct cause of the following exception:\n"
549 exception_during_handling = "\nDuring handling of the above exception, another exception occurred:\n"
550
551 if cause:
552 message = [[direct_cause]]
553 else:
554 message = [[exception_during_handling]]
555 return message
556
533 557 def set_colors(self, *args, **kw):
534 558 """Shorthand access to the color table scheme selector method."""
535 559
@@ -603,7 +627,7 b' class ListTB(TBTools):'
603 627 self.ostream.write(self.text(etype, value, elist))
604 628 self.ostream.write('\n')
605 629
606 def structured_traceback(self, etype, value, elist, tb_offset=None,
630 def structured_traceback(self, etype, evalue, etb=None, tb_offset=None,
607 631 context=5):
608 632 """Return a color formatted string with the traceback info.
609 633
@@ -612,15 +636,16 b' class ListTB(TBTools):'
612 636 etype : exception type
613 637 Type of the exception raised.
614 638
615 value : object
639 evalue : object
616 640 Data stored in the exception
617 641
618 elist : list
619 List of frames, see class docstring for details.
642 etb : object
643 If list: List of frames, see class docstring for details.
644 If Traceback: Traceback of the exception.
620 645
621 646 tb_offset : int, optional
622 647 Number of frames in the traceback to skip. If not given, the
623 instance value is used (set in constructor).
648 instance evalue is used (set in constructor).
624 649
625 650 context : int, optional
626 651 Number of lines of context information to print.
@@ -629,6 +654,19 b' class ListTB(TBTools):'
629 654 -------
630 655 String with formatted exception.
631 656 """
657 # This is a workaround to get chained_exc_ids in recursive calls
658 # etb should not be a tuple if structured_traceback is not recursive
659 if isinstance(etb, tuple):
660 etb, chained_exc_ids = etb
661 else:
662 chained_exc_ids = set()
663
664 if isinstance(etb, list):
665 elist = etb
666 elif etb is not None:
667 elist = self._extract_tb(etb)
668 else:
669 elist = []
632 670 tb_offset = self.tb_offset if tb_offset is None else tb_offset
633 671 Colors = self.Colors
634 672 out_list = []
@@ -641,9 +679,25 b' class ListTB(TBTools):'
641 679 (Colors.normalEm, Colors.Normal) + '\n')
642 680 out_list.extend(self._format_list(elist))
643 681 # The exception info should be a single entry in the list.
644 lines = ''.join(self._format_exception_only(etype, value))
682 lines = ''.join(self._format_exception_only(etype, evalue))
645 683 out_list.append(lines)
646 684
685 exception = self.get_parts_of_chained_exception(evalue)
686
687 if exception and not id(exception[1]) in chained_exc_ids:
688 chained_exception_message = self.prepare_chained_exception_message(
689 evalue.__cause__)[0]
690 etype, evalue, etb = exception
691 # Trace exception to avoid infinite 'cause' loop
692 chained_exc_ids.add(id(exception[1]))
693 chained_exceptions_tb_offset = 0
694 out_list = (
695 self.structured_traceback(
696 etype, evalue, (etb, chained_exc_ids),
697 chained_exceptions_tb_offset, context)
698 + chained_exception_message
699 + out_list)
700
647 701 return out_list
648 702
649 703 def _format_list(self, extracted_list):
@@ -763,7 +817,7 b' class ListTB(TBTools):'
763 817 etype : exception type
764 818 value : exception value
765 819 """
766 return ListTB.structured_traceback(self, etype, value, [])
820 return ListTB.structured_traceback(self, etype, value)
767 821
768 822 def show_exception_only(self, etype, evalue):
769 823 """Only print the exception type and message, without a traceback.
@@ -1013,16 +1067,6 b' class VerboseTB(TBTools):'
1013 1067 _format_traceback_lines(lnum, index, lines, Colors, lvals,
1014 1068 _line_format)))
1015 1069
1016 def prepare_chained_exception_message(self, cause):
1017 direct_cause = "\nThe above exception was the direct cause of the following exception:\n"
1018 exception_during_handling = "\nDuring handling of the above exception, another exception occurred:\n"
1019
1020 if cause:
1021 message = [[direct_cause]]
1022 else:
1023 message = [[exception_during_handling]]
1024 return message
1025
1026 1070 def prepare_header(self, etype, long_version=False):
1027 1071 colors = self.Colors # just a shorthand + quicker name lookup
1028 1072 colorsnormal = colors.Normal # used a lot
@@ -1117,20 +1161,6 b' class VerboseTB(TBTools):'
1117 1161 info('\nUnfortunately, your original traceback can not be constructed.\n')
1118 1162 return None
1119 1163
1120 def get_parts_of_chained_exception(self, evalue):
1121 def get_chained_exception(exception_value):
1122 cause = getattr(exception_value, '__cause__', None)
1123 if cause:
1124 return cause
1125 if getattr(exception_value, '__suppress_context__', False):
1126 return None
1127 return getattr(exception_value, '__context__', None)
1128
1129 chained_evalue = get_chained_exception(evalue)
1130
1131 if chained_evalue:
1132 return chained_evalue.__class__, chained_evalue, chained_evalue.__traceback__
1133
1134 1164 def structured_traceback(self, etype, evalue, etb, tb_offset=None,
1135 1165 number_of_lines_of_context=5):
1136 1166 """Return a nice text document describing the traceback."""
@@ -1294,9 +1324,8 b' class FormattedTB(VerboseTB, ListTB):'
1294 1324 # out-of-date source code.
1295 1325 self.check_cache()
1296 1326 # Now we can extract and format the exception
1297 elist = self._extract_tb(tb)
1298 1327 return ListTB.structured_traceback(
1299 self, etype, value, elist, tb_offset, number_of_lines_of_context
1328 self, etype, value, tb, tb_offset, number_of_lines_of_context
1300 1329 )
1301 1330
1302 1331 def stb2text(self, stb):
@@ -32,15 +32,15 b' def win32_clipboard_get():'
32 32 win32clipboard.CloseClipboard()
33 33 return text
34 34
35 def osx_clipboard_get():
35 def osx_clipboard_get() -> str:
36 36 """ Get the clipboard's text on OS X.
37 37 """
38 38 p = subprocess.Popen(['pbpaste', '-Prefer', 'ascii'],
39 39 stdout=subprocess.PIPE)
40 text, stderr = p.communicate()
40 bytes_, stderr = p.communicate()
41 41 # Text comes in with old Mac \r line endings. Change them to \n.
42 text = text.replace(b'\r', b'\n')
43 text = py3compat.cast_unicode(text, py3compat.DEFAULT_ENCODING)
42 bytes_ = bytes_.replace(b'\r', b'\n')
43 text = py3compat.decode(bytes_)
44 44 return text
45 45
46 46 def tkinter_clipboard_get():
@@ -97,9 +97,6 b" __all__ = ['pretty', 'pprint', 'PrettyPrinter', 'RepresentationPrinter',"
97 97
98 98
99 99 MAX_SEQ_LENGTH = 1000
100 # The language spec says that dicts preserve order from 3.7, but CPython
101 # does so from 3.6, so it seems likely that people will expect that.
102 DICT_IS_ORDERED = True
103 100 _re_pattern_type = type(re.compile(''))
104 101
105 102 def _safe_getattr(obj, attr, default=None):
@@ -606,11 +603,6 b' def _dict_pprinter_factory(start, end):'
606 603 step = len(start)
607 604 p.begin_group(step, start)
608 605 keys = obj.keys()
609 # if dict isn't large enough to be truncated, sort keys before displaying
610 # From Python 3.7, dicts preserve order by definition, so we don't sort.
611 if not DICT_IS_ORDERED \
612 and not (p.max_seq_length and len(obj) >= p.max_seq_length):
613 keys = _sorted_for_pprint(keys)
614 606 for idx, key in p._enumerate(keys):
615 607 if idx:
616 608 p.text(',')
@@ -12,7 +12,7 b' from IPython.utils.path import ('
12 12 ensure_dir_exists, fs_encoding)
13 13 from IPython.utils import py3compat
14 14
15 def get_ipython_dir():
15 def get_ipython_dir() -> str:
16 16 """Get the IPython directory for this platform and user.
17 17
18 18 This uses the logic in `get_home_dir` to find the home directory
@@ -28,10 +28,9 b' def get_ipython_dir():'
28 28 home_dir = get_home_dir()
29 29 xdg_dir = get_xdg_dir()
30 30
31 # import pdb; pdb.set_trace() # dbg
32 31 if 'IPYTHON_DIR' in env:
33 warn('The environment variable IPYTHON_DIR is deprecated. '
34 'Please use IPYTHONDIR instead.')
32 warn('The environment variable IPYTHON_DIR is deprecated since IPython 3.0. '
33 'Please use IPYTHONDIR instead.', DeprecationWarning)
35 34 ipdir = env.get('IPYTHONDIR', env.get('IPYTHON_DIR', None))
36 35 if ipdir is None:
37 36 # not set explicitly, use ~/.ipython
@@ -67,11 +66,11 b' def get_ipython_dir():'
67 66 warn("IPython parent '{0}' is not a writable location,"
68 67 " using a temp directory.".format(parent))
69 68 ipdir = tempfile.mkdtemp()
69 assert isinstance(ipdir, str), "all path manipulation should be str(unicode), but are not."
70 return ipdir
70 71
71 return py3compat.cast_unicode(ipdir, fs_encoding)
72 72
73
74 def get_ipython_cache_dir():
73 def get_ipython_cache_dir() -> str:
75 74 """Get the cache directory it is created if it does not exist."""
76 75 xdgdir = get_xdg_cache_dir()
77 76 if xdgdir is None:
@@ -82,13 +81,14 b' def get_ipython_cache_dir():'
82 81 elif not _writable_dir(xdgdir):
83 82 return get_ipython_dir()
84 83
85 return py3compat.cast_unicode(ipdir, fs_encoding)
84 return ipdir
86 85
87 86
88 def get_ipython_package_dir():
87 def get_ipython_package_dir() -> str:
89 88 """Get the base directory where IPython itself is installed."""
90 89 ipdir = os.path.dirname(IPython.__file__)
91 return py3compat.cast_unicode(ipdir, fs_encoding)
90 assert isinstance(ipdir, str)
91 return ipdir
92 92
93 93
94 94 def get_ipython_module_path(module_str):
@@ -17,6 +17,9 b' from prompt_toolkit.shortcuts.prompt import PromptSession'
17 17 from prompt_toolkit.enums import EditingMode
18 18 from prompt_toolkit.formatted_text import PygmentsTokens
19 19
20 from prompt_toolkit import __version__ as ptk_version
21 PTK3 = ptk_version.startswith('3.')
22
20 23
21 24 class TerminalPdb(Pdb):
22 25 """Standalone IPython debugger."""
@@ -49,20 +52,23 b' class TerminalPdb(Pdb):'
49 52 & ~cursor_in_leading_ws
50 53 ))(display_completions_like_readline)
51 54
52 self.pt_app = PromptSession(
53 message=(lambda: PygmentsTokens(get_prompt_tokens())),
54 editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()),
55 key_bindings=kb,
56 history=self.shell.debugger_history,
57 completer=self._ptcomp,
58 enable_history_search=True,
59 mouse_support=self.shell.mouse_support,
60 complete_style=self.shell.pt_complete_style,
61 style=self.shell.style,
62 inputhook=self.shell.inputhook,
63 color_depth=self.shell.color_depth,
55 options = dict(
56 message=(lambda: PygmentsTokens(get_prompt_tokens())),
57 editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()),
58 key_bindings=kb,
59 history=self.shell.debugger_history,
60 completer=self._ptcomp,
61 enable_history_search=True,
62 mouse_support=self.shell.mouse_support,
63 complete_style=self.shell.pt_complete_style,
64 style=self.shell.style,
65 color_depth=self.shell.color_depth,
64 66 )
65 67
68 if not PTK3:
69 options['inputhook'] = self.shell.inputhook
70 self.pt_app = PromptSession(**options)
71
66 72 def cmdloop(self, intro=None):
67 73 """Repeatedly issue a prompt, accept input, parse an initial prefix
68 74 off the received input, and dispatch to action methods, passing them
@@ -1,5 +1,6 b''
1 1 """IPython terminal interface using prompt_toolkit"""
2 2
3 import asyncio
3 4 import os
4 5 import sys
5 6 import warnings
@@ -25,6 +26,7 b' from prompt_toolkit.patch_stdout import patch_stdout'
25 26 from prompt_toolkit.shortcuts import PromptSession, CompleteStyle, print_formatted_text
26 27 from prompt_toolkit.styles import DynamicStyle, merge_styles
27 28 from prompt_toolkit.styles.pygments import style_from_pygments_cls, style_from_pygments_dict
29 from prompt_toolkit import __version__ as ptk_version
28 30
29 31 from pygments.styles import get_style_by_name
30 32 from pygments.style import Style
@@ -38,6 +40,7 b' from .ptutils import IPythonPTCompleter, IPythonPTLexer'
38 40 from .shortcuts import create_ipython_shortcuts
39 41
40 42 DISPLAY_BANNER_DEPRECATED = object()
43 PTK3 = ptk_version.startswith('3.')
41 44
42 45
43 46 class _NoStyle(Style): pass
@@ -87,7 +90,17 b' else:'
87 90
88 91 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
89 92
93 def black_reformat_handler(text_before_cursor):
94 import black
95 formatted_text = black.format_str(text_before_cursor, mode=black.FileMode())
96 if not text_before_cursor.endswith('\n') and formatted_text.endswith('\n'):
97 formatted_text = formatted_text[:-1]
98 return formatted_text
99
100
90 101 class TerminalInteractiveShell(InteractiveShell):
102 mime_renderers = Dict().tag(config=True)
103
91 104 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
92 105 'to reserve for the completion menu'
93 106 ).tag(config=True)
@@ -120,6 +133,11 b' class TerminalInteractiveShell(InteractiveShell):'
120 133 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
121 134 ).tag(config=True)
122 135
136 autoformatter = Unicode(None,
137 help="Autoformatter to reformat Terminal code. Can be `'black'` or `None`",
138 allow_none=True
139 ).tag(config=True)
140
123 141 mouse_support = Bool(False,
124 142 help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
125 143 ).tag(config=True)
@@ -150,6 +168,16 b' class TerminalInteractiveShell(InteractiveShell):'
150 168 if self.pt_app:
151 169 self.pt_app.editing_mode = u_mode
152 170
171 @observe('autoformatter')
172 def _autoformatter_changed(self, change):
173 formatter = change.new
174 if formatter is None:
175 self.reformat_handler = lambda x:x
176 elif formatter == 'black':
177 self.reformat_handler = black_reformat_handler
178 else:
179 raise ValueError
180
153 181 @observe('highlighting_style')
154 182 @observe('colors')
155 183 def _highlighting_style_changed(self, change):
@@ -282,6 +310,7 b' class TerminalInteractiveShell(InteractiveShell):'
282 310
283 311 editing_mode = getattr(EditingMode, self.editing_mode.upper())
284 312
313 self.pt_loop = asyncio.new_event_loop()
285 314 self.pt_app = PromptSession(
286 315 editing_mode=editing_mode,
287 316 key_bindings=key_bindings,
@@ -390,7 +419,7 b' class TerminalInteractiveShell(InteractiveShell):'
390 419 # work around this.
391 420 get_message = get_message()
392 421
393 return {
422 options = {
394 423 'complete_in_thread': False,
395 424 'lexer':IPythonPTLexer(),
396 425 'reserve_space_for_menu':self.space_for_menu,
@@ -407,8 +436,11 b' class TerminalInteractiveShell(InteractiveShell):'
407 436 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
408 437 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
409 438 Condition(lambda: self.highlight_matching_brackets))],
410 'inputhook': self.inputhook,
411 439 }
440 if not PTK3:
441 options['inputhook'] = self.inputhook
442
443 return options
412 444
413 445 def prompt_for_code(self):
414 446 if self.rl_next_input:
@@ -417,11 +449,28 b' class TerminalInteractiveShell(InteractiveShell):'
417 449 else:
418 450 default = ''
419 451
420 with patch_stdout(raw=True):
421 text = self.pt_app.prompt(
422 default=default,
423 # pre_run=self.pre_prompt,# reset_current_buffer=True,
424 **self._extra_prompt_options())
452 # In order to make sure that asyncio code written in the
453 # interactive shell doesn't interfere with the prompt, we run the
454 # prompt in a different event loop.
455 # If we don't do this, people could spawn coroutine with a
456 # while/true inside which will freeze the prompt.
457
458 try:
459 old_loop = asyncio.get_event_loop()
460 except RuntimeError:
461 # This happens when the user used `asyncio.run()`.
462 old_loop = None
463
464 asyncio.set_event_loop(self.pt_loop)
465 try:
466 with patch_stdout(raw=True):
467 text = self.pt_app.prompt(
468 default=default,
469 **self._extra_prompt_options())
470 finally:
471 # Restore the original event loop.
472 asyncio.set_event_loop(old_loop)
473
425 474 return text
426 475
427 476 def enable_win_unicode_console(self):
@@ -528,12 +577,33 b' class TerminalInteractiveShell(InteractiveShell):'
528 577
529 578 active_eventloop = None
530 579 def enable_gui(self, gui=None):
531 if gui:
580 if gui and (gui != 'inline') :
532 581 self.active_eventloop, self._inputhook =\
533 582 get_inputhook_name_and_func(gui)
534 583 else:
535 584 self.active_eventloop = self._inputhook = None
536 585
586 # For prompt_toolkit 3.0. We have to create an asyncio event loop with
587 # this inputhook.
588 if PTK3:
589 import asyncio
590 from prompt_toolkit.eventloop import new_eventloop_with_inputhook
591
592 if gui == 'asyncio':
593 # When we integrate the asyncio event loop, run the UI in the
594 # same event loop as the rest of the code. don't use an actual
595 # input hook. (Asyncio is not made for nesting event loops.)
596 self.pt_loop = asyncio.get_event_loop()
597
598 elif self._inputhook:
599 # If an inputhook was set, create a new asyncio event loop with
600 # this inputhook for the prompt.
601 self.pt_loop = new_eventloop_with_inputhook(self._inputhook)
602 else:
603 # When there's no inputhook, run the prompt in a separate
604 # asyncio event loop.
605 self.pt_loop = asyncio.new_event_loop()
606
537 607 # Run !system commands directly, not through pipes, so terminal programs
538 608 # work correctly.
539 609 system = InteractiveShell.system_raw
@@ -89,3 +89,14 b' class RichPromptDisplayHook(DisplayHook):'
89 89 )
90 90 else:
91 91 sys.stdout.write(prompt_txt)
92
93 def write_format_data(self, format_dict, md_dict=None) -> None:
94 if self.shell.mime_renderers:
95
96 for mime, handler in self.shell.mime_renderers.items():
97 if mime in format_dict:
98 handler(format_dict[mime], None)
99 return
100
101 super().write_format_data(format_dict, md_dict)
102
@@ -13,6 +13,7 b' backends = ['
13 13 'wx',
14 14 'pyglet', 'glut',
15 15 'osx',
16 'asyncio'
16 17 ]
17 18
18 19 registered = {}
@@ -44,6 +44,15 b' def create_ipython_shortcuts(shell):'
44 44 & insert_mode
45 45 ))(return_handler)
46 46
47 def reformat_and_execute(event):
48 reformat_text_before_cursor(event.current_buffer, event.current_buffer.document, shell)
49 event.current_buffer.validate_and_handle()
50
51 kb.add('escape', 'enter', filter=(has_focus(DEFAULT_BUFFER)
52 & ~has_selection
53 & insert_mode
54 ))(reformat_and_execute)
55
47 56 kb.add('c-\\')(force_exit)
48 57
49 58 kb.add('c-p', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER))
@@ -86,7 +95,17 b' def create_ipython_shortcuts(shell):'
86 95 return kb
87 96
88 97
98 def reformat_text_before_cursor(buffer, document, shell):
99 text = buffer.delete_before_cursor(len(document.text[:document.cursor_position]))
100 try:
101 formatted_text = shell.reformat_handler(text)
102 buffer.insert_text(formatted_text)
103 except Exception as e:
104 buffer.insert_text(text)
105
106
89 107 def newline_or_execute_outer(shell):
108
90 109 def newline_or_execute(event):
91 110 """When the user presses return, insert a newline or execute the code."""
92 111 b = event.current_buffer
@@ -107,7 +126,12 b' def newline_or_execute_outer(shell):'
107 126 else:
108 127 check_text = d.text[:d.cursor_position]
109 128 status, indent = shell.check_complete(check_text)
110
129
130 # if all we have after the cursor is whitespace: reformat current text
131 # before cursor
132 after_cursor = d.text[d.cursor_position:]
133 if not after_cursor.strip():
134 reformat_text_before_cursor(b, d, shell)
111 135 if not (d.on_last_line or
112 136 d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()
113 137 ):
@@ -118,6 +142,7 b' def newline_or_execute_outer(shell):'
118 142 return
119 143
120 144 if (status != 'incomplete') and b.accept_handler:
145 reformat_text_before_cursor(b, d, shell)
121 146 b.validate_and_handle()
122 147 else:
123 148 if shell.autoindent:
@@ -169,7 +169,7 b' class HomeDirError(Exception):'
169 169 pass
170 170
171 171
172 def get_home_dir(require_writable=False):
172 def get_home_dir(require_writable=False) -> str:
173 173 """Return the 'home' directory, as a unicode string.
174 174
175 175 Uses os.path.expanduser('~'), and checks for writability.
@@ -197,21 +197,18 b' def get_home_dir(require_writable=False):'
197 197 if not _writable_dir(homedir) and os.name == 'nt':
198 198 # expanduser failed, use the registry to get the 'My Documents' folder.
199 199 try:
200 try:
201 import winreg as wreg # Py 3
202 except ImportError:
203 import _winreg as wreg # Py 2
204 key = wreg.OpenKey(
200 import winreg as wreg
201 with wreg.OpenKey(
205 202 wreg.HKEY_CURRENT_USER,
206 203 r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
207 )
208 homedir = wreg.QueryValueEx(key,'Personal')[0]
209 key.Close()
204 ) as key:
205 homedir = wreg.QueryValueEx(key,'Personal')[0]
210 206 except:
211 207 pass
212 208
213 209 if (not require_writable) or _writable_dir(homedir):
214 return py3compat.cast_unicode(homedir, fs_encoding)
210 assert isinstance(homedir, str), "Homedir shoudl be unicode not bytes"
211 return homedir
215 212 else:
216 213 raise HomeDirError('%s is not a writable dir, '
217 214 'set $HOME environment variable to override' % homedir)
@@ -254,31 +251,31 b' def get_xdg_cache_dir():'
254 251
255 252 @undoc
256 253 def get_ipython_dir():
257 warn("get_ipython_dir has moved to the IPython.paths module since IPython 4.0.", stacklevel=2)
254 warn("get_ipython_dir has moved to the IPython.paths module since IPython 4.0.", DeprecationWarning, stacklevel=2)
258 255 from IPython.paths import get_ipython_dir
259 256 return get_ipython_dir()
260 257
261 258 @undoc
262 259 def get_ipython_cache_dir():
263 warn("get_ipython_cache_dir has moved to the IPython.paths module since IPython 4.0.", stacklevel=2)
260 warn("get_ipython_cache_dir has moved to the IPython.paths module since IPython 4.0.", DeprecationWarning, stacklevel=2)
264 261 from IPython.paths import get_ipython_cache_dir
265 262 return get_ipython_cache_dir()
266 263
267 264 @undoc
268 265 def get_ipython_package_dir():
269 warn("get_ipython_package_dir has moved to the IPython.paths module since IPython 4.0.", stacklevel=2)
266 warn("get_ipython_package_dir has moved to the IPython.paths module since IPython 4.0.", DeprecationWarning, stacklevel=2)
270 267 from IPython.paths import get_ipython_package_dir
271 268 return get_ipython_package_dir()
272 269
273 270 @undoc
274 271 def get_ipython_module_path(module_str):
275 warn("get_ipython_module_path has moved to the IPython.paths module since IPython 4.0.", stacklevel=2)
272 warn("get_ipython_module_path has moved to the IPython.paths module since IPython 4.0.", DeprecationWarning, stacklevel=2)
276 273 from IPython.paths import get_ipython_module_path
277 274 return get_ipython_module_path(module_str)
278 275
279 276 @undoc
280 277 def locate_profile(profile='default'):
281 warn("locate_profile has moved to the IPython.paths module since IPython 4.0.", stacklevel=2)
278 warn("locate_profile has moved to the IPython.paths module since IPython 4.0.", DeprecationWarning, stacklevel=2)
282 279 from IPython.paths import locate_profile
283 280 return locate_profile(profile=profile)
284 281
@@ -171,8 +171,12 b' def test_get_home_dir_8():'
171 171 env.pop(key, None)
172 172
173 173 class key:
174 def __enter__(self):
175 pass
174 176 def Close(self):
175 177 pass
178 def __exit__(*args, **kwargs):
179 pass
176 180
177 181 with patch.object(wreg, 'OpenKey', return_value=key()), \
178 182 patch.object(wreg, 'QueryValueEx', return_value=[abspath(HOME_TEST_DIR)]):
@@ -117,3 +117,15 b' version::'
117 117 ...
118 118 install_requires=install_req
119 119 )
120
121 Alternatives to IPython
122 =======================
123
124 IPython may not be to your taste; if that's the case there might be similar
125 project that you might want to use:
126
127 - the classic Python REPL.
128 - `bpython <https://bpython-interpreter.org/>`_
129 - `mypython <https://www.asmeurer.com/mypython/>`_
130 - `ptpython and ptipython <https://pypi.org/project/ptpython/>`
131 - `xonsh <https://xon.sh/>`
@@ -29,6 +29,7 b' Extending and integrating with IPython'
29 29 extensions/index
30 30 integrating
31 31 custommagics
32 shell_mimerenderer
32 33 inputtransforms
33 34 callbacks
34 35 eventloops
@@ -1,13 +1,14 b''
1 1 .. _execution_semantics:
2 2
3 Execution semantics in the IPython kernel
4 =========================================
3 Execution of cells in the IPython kernel
4 ========================================
5 5
6 The execution of user code consists of the following phases:
6 When IPython kernel receives `execute_request <https://jupyter-client.readthedocs.io/en/latest/messaging.html#execute>`_
7 with user code, it processes the message in the following phases:
7 8
8 9 1. Fire the ``pre_execute`` event.
9 10 2. Fire the ``pre_run_cell`` event unless silent is ``True``.
10 3. Execute the ``code`` field, see below for details.
11 3. Execute ``run_cell`` method to preprocess ``code``, compile and run it, see below for details.
11 12 4. If execution succeeds, expressions in ``user_expressions`` are computed.
12 13 This ensures that any error in the expressions don't affect the main code execution.
13 14 5. Fire the ``post_execute`` event.
@@ -18,9 +19,15 b' The execution of user code consists of the following phases:'
18 19 :doc:`/config/callbacks`
19 20
20 21
21 To understand how the ``code`` field is executed, one must know that Python
22 code can be compiled in one of three modes (controlled by the ``mode`` argument
23 to the :func:`compile` builtin):
22 Running user ``code``
23 =====================
24
25 First, the ``code`` cell is transformed to expand ``%magic`` and ``!system``
26 commands by ``IPython.core.inputtransformer2``. Then expanded cell is compiled
27 using standard Python :func:`compile` function and executed.
28
29 Python :func:`compile` function provides ``mode`` argument to select one
30 of three ways of compiling code:
24 31
25 32 *single*
26 33 Valid for a single interactive statement (though the source can contain
@@ -50,16 +57,16 b" execution in 'single' mode, and then:"
50 57
51 58 - If there is more than one block:
52 59
53 * if the last one is a single line long, run all but the last in 'exec' mode
60 * if the last block is a single line long, run all but the last in 'exec' mode
54 61 and the very last one in 'single' mode. This makes it easy to type simple
55 62 expressions at the end to see computed values.
56 63
57 * if the last one is no more than two lines long, run all but the last in
64 * if the last block is no more than two lines long, run all but the last in
58 65 'exec' mode and the very last one in 'single' mode. This makes it easy to
59 66 type simple expressions at the end to see computed values. - otherwise
60 67 (last one is also multiline), run all in 'exec' mode
61 68
62 * otherwise (last one is also multiline), run all in 'exec' mode as a single
69 * otherwise (last block is also multiline), run all in 'exec' mode as a single
63 70 unit.
64 71
65 72
@@ -11,7 +11,7 b' This document describes in-flight development work.'
11 11 `docs/source/whatsnew/pr` folder
12 12
13 13
14 Released .... ...., 2017
14 Released .... ...., 2019
15 15
16 16
17 17 Need to be updated:
@@ -22,8 +22,6 b' Need to be updated:'
22 22
23 23 pr/*
24 24
25
26
27 25 .. DO NOT EDIT THIS LINE BEFORE RELEASE. FEATURE INSERTION POINT.
28 26
29 27
@@ -1,6 +1,48 b''
1 1 Issues closed in the 7.x development cycle
2 2 ==========================================
3 3
4 Issues closed in 7.10.1
5 -----------------------
6
7 GitHub stats for 2019/11/27 - 2019/12/01 (tag: 7.10.0)
8
9 These lists are automatically generated, and may be incomplete or contain duplicates.
10
11 We closed 5 issues and merged 7 pull requests.
12 The full list can be seen `on GitHub <https://github.com/ipython/ipython/issues?q=milestone%3A7.10.1>`__
13
14 The following 2 authors contributed 14 commits.
15
16 * Jonathan Slenders
17 * Matthias Bussonnier
18
19 Issues closed in 7.10
20 ---------------------
21
22 GitHub stats for 2019/10/25 - 2019/11/27 (tag: 7.9.0)
23
24 These lists are automatically generated, and may be incomplete or contain duplicates.
25
26 We closed 4 issues and merged 22 pull requests.
27 The full list can be seen `on GitHub <https://github.com/ipython/ipython/issues?q=milestone%3A7.10>`__
28
29 The following 15 authors contributed 101 commits.
30
31 * anatoly techtonik
32 * Ben Lewis
33 * Benjamin Ragan-Kelley
34 * Gerrit Buss
35 * grey275
36 * Gökcen Eraslan
37 * Jonathan Slenders
38 * Joris Van den Bossche
39 * kousik
40 * Matthias Bussonnier
41 * Nicholas Bollweg
42 * Paul McCarthy
43 * Srinivas Reddy Thatiparthy
44 * Timo Kaufmann
45 * Tony Fast
4 46
5 47 Issues closed in 7.9
6 48 --------------------
@@ -2,8 +2,156 b''
2 2 7.x Series
3 3 ============
4 4
5 .. _version 7101:
6
7 IPython 7.10.1
8 ==============
9
10 IPython 7.10.1 fix a couple of incompatibilities with Prompt toolkit 3 (please
11 update Prompt toolkit to 3.0.2 at least), and fixes some interaction with
12 headless IPython.
13
14 .. _version 7100:
15
16 IPython 7.10.0
17 ==============
18
19 IPython 7.10 is the first double digit minor release in the last decade, and
20 first since the release of IPython 1.0, previous double digit minor release was
21 in August 2009.
22
23 We've been trying to give you regular release on the last Friday of every month
24 for a guaranty of rapid access to bug fixes and new features.
25
26 Unlike the previous first few releases that have seen only a couple of code
27 changes, 7.10 bring a number of changes, new features and bugfixes.
28
29 Stop Support for Python 3.5 – Adopt NEP 29
30 ------------------------------------------
31
32 IPython has decided to follow the informational `NEP 29
33 <https://numpy.org/neps/nep-0029-deprecation_policy.html>`_ which layout a clear
34 policy as to which version of (C)Python and NumPy are supported.
35
36 We thus dropped support for Python 3.5, and cleaned up a number of code path
37 that were Python-version dependant. If you are on 3.5 or earlier pip should
38 automatically give you the latest compatible version of IPython so you do not
39 need to pin to a given version.
40
41 Support for Prompt Toolkit 3.0
42 ------------------------------
43
44 Prompt Toolkit 3.0 was release a week before IPython 7.10 and introduces a few
45 breaking changes. We believe IPython 7.10 should be compatible with both Prompt
46 Toolkit 2.x and 3.x, though it has not been extensively tested with 3.x so
47 please report any issues.
48
49
50 Prompt Rendering Performance improvements
51 -----------------------------------------
52
53 Pull Request :ghpull:`11933` introduced an optimisation in the prompt rendering
54 logic that should decrease the resource usage of IPython when using the
55 _default_ configuration but could potentially introduce a regression of
56 functionalities if you are using a custom prompt.
57
58 We know assume if you haven't changed the default keybindings that the prompt
59 **will not change** during the duration of your input – which is for example
60 not true when using vi insert mode that switches between `[ins]` and `[nor]`
61 for the current mode.
62
63 If you are experiencing any issue let us know.
64
65 Code autoformatting
66 -------------------
67
68 The IPython terminal can now auto format your code just before entering a new
69 line or executing a command. To do so use the
70 ``--TerminalInteractiveShell.autoformatter`` option and set it to ``'black'``;
71 if black is installed IPython will use black to format your code when possible.
72
73 IPython cannot always properly format your code; in particular it will
74 auto formatting with *black* will only work if:
75
76 - Your code does not contains magics or special python syntax.
77
78 - There is no code after your cursor.
79
80 The Black API is also still in motion; so this may not work with all versions of
81 black.
82
83 It should be possible to register custom formatter, though the API is till in
84 flux.
85
86 Arbitrary Mimetypes Handing in Terminal (Aka inline images in terminal)
87 -----------------------------------------------------------------------
88
89 When using IPython terminal it is now possible to register function to handle
90 arbitrary mimetypes. While rendering non-text based representation was possible in
91 many jupyter frontend; it was not possible in terminal IPython, as usually
92 terminal are limited to displaying text. As many terminal these days provide
93 escape sequences to display non-text; bringing this loved feature to IPython CLI
94 made a lot of sens. This functionality will not only allow inline images; but
95 allow opening of external program; for example ``mplayer`` to "display" sound
96 files.
97
98 So far only the hooks necessary for this are in place, but no default mime
99 renderers added; so inline images will only be available via extensions. We will
100 progressively enable these features by default in the next few releases, and
101 contribution is welcomed.
102
103 We welcome any feedback on the API. See :ref:`shell_mimerenderer` for more
104 informations.
105
106 This is originally based on work form in :ghpull:`10610` from @stephanh42
107 started over two years ago, and still a lot need to be done.
108
109 MISC
110 ----
111
112 - Completions can define their own ordering :ghpull:`11855`
113 - Enable Plotting in the same cell than the one that import matplotlib
114 :ghpull:`11916`
115 - Allow to store and restore multiple variables at once :ghpull:`11930`
116
117 You can see `all pull-requests <https://github.com/ipython/ipython/pulls?q=is%3Apr+milestone%3A7.10+is%3Aclosed>`_ for this release.
118
119 API Changes
120 -----------
121
122 Change of API and exposed objects automatically detected using `frappuccino <https://pypi.org/project/frappuccino/>`_ (still in beta):
123
124 The following items are new in IPython 7.10::
125
126 + IPython.terminal.shortcuts.reformat_text_before_cursor(buffer, document, shell)
127 + IPython.terminal.interactiveshell.PTK3
128 + IPython.terminal.interactiveshell.black_reformat_handler(text_before_cursor)
129 + IPython.terminal.prompts.RichPromptDisplayHook.write_format_data(self, format_dict, md_dict='None')
130
131 The following items have been removed in 7.10::
132
133 - IPython.lib.pretty.DICT_IS_ORDERED
134
135 The following signatures differ between versions::
136
137 - IPython.extensions.storemagic.restore_aliases(ip)
138 + IPython.extensions.storemagic.restore_aliases(ip, alias='None')
139
140 Special Thanks
141 --------------
142
143 - @stephanh42 who started the work on inline images in terminal 2 years ago
144 - @augustogoulart who spent a lot of time triaging issues and responding to
145 users.
146 - @con-f-use who is my (@Carreau) first sponsor on GitHub, as a reminder if you
147 like IPython, Jupyter and many other library of the SciPy stack you can
148 donate to numfocus.org non profit
149
5 150 .. _version 790:
6 151
152 IPython 7.9.0
153 =============
154
7 155 IPython 7.9 is a small release with a couple of improvement and bug fixes.
8 156
9 157 - Xterm terminal title should be restored on exit :ghpull:`11910`
@@ -13,12 +161,12 b' IPython 7.9 is a small release with a couple of improvement and bug fixes.'
13 161 find all objects needing reload. This should avoid large objects traversal
14 162 like pandas dataframes. :ghpull:`11876`
15 163 - Get ready for Python 4. :ghpull:`11874`
16 - `%env` Magic nonw has euristic to hide potentially sensitive values :ghpull:`11896`
164 - `%env` Magic now has heuristic to hide potentially sensitive values :ghpull:`11896`
17 165
18 166 This is a small release despite a number of Pull Request Pending that need to
19 167 be reviewed/worked on. Many of the core developers have been busy outside of
20 168 IPython/Jupyter and we thanks all contributor for their patience; we'll work on
21 these as soon as we have time.
169 these as soon as we have time.
22 170
23 171
24 172 .. _version780:
@@ -190,7 +190,7 b' install_requires = ['
190 190 'decorator',
191 191 'pickleshare',
192 192 'traitlets>=4.2',
193 'prompt_toolkit>=2.0.0,<2.1.0',
193 'prompt_toolkit>=2.0.0,<3.1.0,!=3.0.0,!=3.0.1',
194 194 'pygments',
195 195 'backcall',
196 196 ]
@@ -1,5 +1,5 b''
1 1 # Simple tool to help for release
2 # when releasing with bash, simplei source it to get asked questions.
2 # when releasing with bash, simple source it to get asked questions.
3 3
4 4 # misc check before starting
5 5
@@ -9,14 +9,6 b" python -c 'import sphinx'"
9 9 python -c 'import sphinx_rtd_theme'
10 10 python -c 'import nose'
11 11
12 echo -n 'PREV_RELEASE (X.y.z):'
13 read PREV_RELEASE
14 echo -n 'MILESTONE (X.y):'
15 read MILESTONE
16 echo -n 'VERSION (X.y.z):'
17 read VERSION
18 echo -n 'branch (master|X.y):'
19 read branch
20 12
21 13 BLACK=$(tput setaf 1)
22 14 RED=$(tput setaf 1)
@@ -28,30 +20,66 b' CYAN=$(tput setaf 6)'
28 20 WHITE=$(tput setaf 7)
29 21 NOR=$(tput sgr0)
30 22
31 echo
32 echo $BLUE"Updating what's new with informations from docs/source/whatsnew/pr"$NOR
33 python tools/update_whatsnew.py
34 23
35 echo
36 echo $BLUE"please move the contents of "docs/source/whatsnew/development.rst" to version-X.rst"$NOR
37 echo $GREEN"Press enter to continue"$NOR
38 read
24 echo -n "PREV_RELEASE (X.y.z) [$PREV_RELEASE]: "
25 read input
26 PREV_RELEASE=${input:-$PREV_RELEASE}
27 echo -n "MILESTONE (X.y) [$MILESTONE]: "
28 read input
29 MILESTONE=${input:-$MILESTONE}
30 echo -n "VERSION (X.y.z) [$VERSION]:"
31 read input
32 VERSION=${input:-$VERSION}
33 echo -n "BRANCH (master|X.y) [$BRANCH]:"
34 read input
35 BRANCH=${input:-$BRANCH}
36
37 ask_section(){
38 echo
39 echo $BLUE"$1"$NOR
40 echo -n $GREEN"Press Enter to continue, S to skip: "$GREEN
41 read -n1 value
42 echo
43 if [ -z $value ] || [ $value = 'y' ] ; then
44 return 0
45 fi
46 return 1
47 }
48
39 49
40 echo
41 echo $BLUE"here are all the authors that contributed to this release:"$NOR
42 git log --format="%aN <%aE>" $PREV_RELEASE... | sort -u -f
43 50
44 51 echo
45 echo $BLUE"If you see any duplicates cancel (Ctrl-C), then edit .mailmap."$GREEN"Press enter to continue:"$NOR
46 read
52 if ask_section "Updating what's new with informations from docs/source/whatsnew/pr"
53 then
54 python tools/update_whatsnew.py
47 55
48 echo $BLUE"generating stats"$NOR
49 python tools/github_stats.py --milestone $MILESTONE > stats.rst
56 echo
57 echo $BLUE"please move the contents of "docs/source/whatsnew/development.rst" to version-X.rst"$NOR
58 echo $GREEN"Press enter to continue"$NOR
59 read
60 fi
50 61
51 echo $BLUE"stats.rst files generated."$NOR
52 echo $GREEN"Please merge it with the right file (github-stats-X.rst) and commit."$NOR
53 echo $GREEN"press enter to continue."$NOR
54 read
62 if ask_section "Gen Stats, and authors"
63 then
64
65 echo
66 echo $BLUE"here are all the authors that contributed to this release:"$NOR
67 git log --format="%aN <%aE>" $PREV_RELEASE... | sort -u -f
68
69 echo
70 echo $BLUE"If you see any duplicates cancel (Ctrl-C), then edit .mailmap."
71 echo $GREEN"Press enter to continue:"$NOR
72 read
73
74 echo $BLUE"generating stats"$NOR
75 python tools/github_stats.py --milestone $MILESTONE > stats.rst
76
77 echo $BLUE"stats.rst files generated."$NOR
78 echo $GREEN"Please merge it with the right file (github-stats-X.rst) and commit."$NOR
79 echo $GREEN"press enter to continue."$NOR
80 read
81
82 fi
55 83
56 84 echo "Cleaning repository"
57 85 git clean -xfdi
@@ -61,30 +89,81 b' echo $GREEN"please update version number in ${RED}IPython/core/release.py${NOR} '
61 89 echo $GREEN"Press enter to continue"$NOR
62 90 read
63 91
64 echo
65 echo "Attempting to build the docs.."
66 make html -C docs
92 if ask_section "Build the documentation ?"
93 then
94 make html -C docs
95 echo
96 echo $GREEN"Check the docs, press enter to continue"$NOR
97 read
67 98
68 echo
69 echo $GREEN"Check the docs, press enter to continue"$NOR
70 read
99 fi
71 100
72 101 echo
73 102 echo $BLUE"Attempting to build package..."$NOR
74 103
75 104 tools/build_release
76
77 echo
78 echo "Let\'s commit : git commit -am \"release $VERSION\" -S"
79 echo $GREEN"Press enter to continue"$NOR
80 read
81 git commit -am "release $VERSION" -S
82
83 echo
84 echo "git push origin \$BRANCH ?"
85 echo "Press enter to continue"
86 read
87 git push origin $BRANCH
88 # git tag -am "release $VERSION" "$VERSION" -s
89 # git push origin $VERSION
90
105 rm dist/*
106
107 if ask_section "Should we commit, tag, push... etc ? "
108 then
109 echo
110 echo "Let's commit : git commit -am \"release $VERSION\" -S"
111 echo $GREEN"Press enter to commit"$NOR
112 read
113 git commit -am "release $VERSION" -S
114
115 echo
116 echo $BLUE"git push origin \$BRANCH ($BRANCH)?"$NOR
117 echo $GREEN"Make sure you can push"$NOR
118 echo $GREEN"Press enter to continue"$NOR
119 read
120 git push origin $BRANCH
121
122 echo
123 echo "Let's tag : git tag -am \"release $VERSION\" \"$VERSION\" -s"
124 echo $GREEN"Press enter to tag commit"$NOR
125 read
126 git tag -am "release $VERSION" "$VERSION" -s
127
128 echo
129 echo $BLUE"And push the tag: git push origin \$VERSION ?"$NOR
130 echo $GREEN"Press enter to continue"$NOR
131 read
132 git push origin $VERSION
133
134
135 echo $GREEN"please update version number and back to .dev in ${RED}IPython/core/release.py"
136 echo ${BLUE}"Do not commit yet – we'll do it later."$NOR
137
138 echo $GREEN"Press enter to continue"$NOR
139 read
140
141 echo
142 echo "Let's commit : git commit -am \"back to dev\" -S"
143 echo $GREEN"Press enter to commit"$NOR
144 read
145 git commit -am "back to dev" -S
146
147 echo
148 echo $BLUE"let's : git checkout $VERSION"$NOR
149 echo $GREEN"Press enter to continue"$NOR
150 read
151 git checkout $VERSION
152 fi
153
154 if ask_section "Should we build and release ?"
155 then
156
157 echo
158 echo $BLUE"Attempting to build package..."$NOR
159
160 tools/build_release
161
162 echo '$ shasum -a 256 dist/*'
163 shasum -a 256 dist/*
164
165 if ask_section "upload packages ?"
166 then
167 tools/build_release upload
168 fi
169 fi
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now