diff -Nru ipython-7.12.0/debian/changelog ipython-7.13.0/debian/changelog --- ipython-7.12.0/debian/changelog 2020-02-04 20:04:33.000000000 +0000 +++ ipython-7.13.0/debian/changelog 2020-03-04 12:03:33.000000000 +0000 @@ -1,3 +1,13 @@ +ipython (7.13.0-1) unstable; urgency=medium + + [ Gordon Ball ] + * New upstream version 7.13.0 + + [ Julien Puydt ] + * Suggest the doc package (Closes: #807125). + + -- Julien Puydt Wed, 04 Mar 2020 13:03:33 +0100 + ipython (7.12.0-1) unstable; urgency=medium * New upstream version 7.12.0 diff -Nru ipython-7.12.0/debian/control ipython-7.13.0/debian/control --- ipython-7.12.0/debian/control 2020-02-04 20:04:33.000000000 +0000 +++ ipython-7.13.0/debian/control 2020-03-04 12:03:33.000000000 +0000 @@ -46,6 +46,7 @@ ${misc:Depends}, ${python3:Depends}, python3-pexpect, +Suggests: python-ipython-doc Description: Enhanced interactive Python shell (Python 3 version) IPython can be used as a replacement for the standard Python shell, or it can be used as a complete working environment for scientific diff -Nru ipython-7.12.0/docs/source/whatsnew/github-stats-7.rst ipython-7.13.0/docs/source/whatsnew/github-stats-7.rst --- ipython-7.12.0/docs/source/whatsnew/github-stats-7.rst 2020-02-01 01:37:48.000000000 +0000 +++ ipython-7.13.0/docs/source/whatsnew/github-stats-7.rst 2020-02-29 00:45:41.000000000 +0000 @@ -1,6 +1,32 @@ Issues closed in the 7.x development cycle ========================================== +Issues closed in 8.12 +--------------------- + + +GitHub stats for 2020/02/01 - 2020/02/28 (tag: 7.12.0) + +These lists are automatically generated, and may be incomplete or contain duplicates. + +We closed 1 issues and merged 24 pull requests. +The full list can be seen `on GitHub `__ + +The following 12 authors contributed 108 commits. + +* Alex Hall +* Augusto +* Coon, Ethan T +* Daniel Hahler +* Inception95 +* Itamar Turner-Trauring +* Jonas Haag +* Jonathan Slenders +* linar-jether +* Matthias Bussonnier +* Nathan Goldbaum +* Terry Davis + Issues closed in 7.12 --------------------- diff -Nru ipython-7.12.0/docs/source/whatsnew/version7.rst ipython-7.13.0/docs/source/whatsnew/version7.rst --- ipython-7.12.0/docs/source/whatsnew/version7.rst 2020-02-01 01:37:48.000000000 +0000 +++ ipython-7.13.0/docs/source/whatsnew/version7.rst 2020-02-29 00:45:41.000000000 +0000 @@ -2,6 +2,28 @@ 7.x Series ============ +.. _version 713: + +IPython 7.13 +============ + +IPython 7.13 is the first release of the 7.x branch since master is diverging +toward an 8.0. Exiting new features have already been merged in 8.0 and will +not be available on the 7.x branch. All the changes bellow have been backported +from the master branch. + + + - Fix inability to run PDB when inside an event loop :ghpull:`12141` + - Fix ability to interrupt some processes on windows :ghpull:`12137` + - Fix debugger shortcuts :ghpull:`12132` + - improve tab completion when inside a string by removing irrelevant elements :ghpull:`12128` + - Fix display of filename tab completion when the path is long :ghpull:`12122` + - Many removal of Python 2 specific code path :ghpull:`12110` + - displaying wav files do not require NumPy anymore, and is 5x to 30x faster :ghpull:`12113` + +See the list of all closed issues and pull request on `github +`_. + .. _version 712: IPython 7.12 diff -Nru "/tmp/tmprYRkuK/mn_JyRExUP/ipython-7.12.0/examples/IPython Kernel/ipython-get-history.py" "/tmp/tmprYRkuK/Hb0dpefeLy/ipython-7.13.0/examples/IPython Kernel/ipython-get-history.py" --- "/tmp/tmprYRkuK/mn_JyRExUP/ipython-7.12.0/examples/IPython Kernel/ipython-get-history.py" 2020-02-01 01:37:48.000000000 +0000 +++ "/tmp/tmprYRkuK/Hb0dpefeLy/ipython-7.13.0/examples/IPython Kernel/ipython-get-history.py" 2020-02-29 00:45:41.000000000 +0000 @@ -35,5 +35,4 @@ hist = HistoryAccessor() for session, lineno, cell in hist.get_range(session=session_number, raw=raw): - cell = cell.encode('utf-8') # This line is only needed on Python 2. dest.write(cell + '\n') diff -Nru ipython-7.12.0/IPython/core/completer.py ipython-7.13.0/IPython/core/completer.py --- ipython-7.12.0/IPython/core/completer.py 2020-02-01 01:37:48.000000000 +0000 +++ ipython-7.13.0/IPython/core/completer.py 2020-02-29 00:45:41.000000000 +0000 @@ -626,6 +626,8 @@ else: self.global_namespace = global_namespace + self.custom_matchers = [] + super(Completer, self).__init__(**kwargs) def complete(self, text, state): @@ -1122,12 +1124,14 @@ if self.use_jedi: return [ + *self.custom_matchers, self.file_matches, self.magic_matches, self.dict_key_matches, ] else: return [ + *self.custom_matchers, self.python_matches, self.file_matches, self.magic_matches, @@ -1371,18 +1375,18 @@ try_jedi = True try: - # should we check the type of the node is Error ? + # find the first token in the current tree -- if it is a ' or " then we are in a string + completing_string = False try: - # jedi < 0.11 - from jedi.parser.tree import ErrorLeaf - except ImportError: - # jedi >= 0.11 - from parso.tree import ErrorLeaf + first_child = next(c for c in interpreter._get_module().tree_node.children if hasattr(c, 'value')) + except StopIteration: + pass + else: + # note the value may be ', ", or it may also be ''' or """, or + # in some cases, """what/you/typed..., but all of these are + # strings. + completing_string = len(first_child.value) > 0 and first_child.value[0] in {"'", '"'} - next_to_last_tree = interpreter._get_module().tree_node.children[-2] - completing_string = False - if isinstance(next_to_last_tree, ErrorLeaf): - completing_string = next_to_last_tree.value.lstrip()[0] in {'"', "'"} # if we are in a string jedi is likely not the right candidate for # now. Skip it. try_jedi = not completing_string diff -Nru ipython-7.12.0/IPython/core/interactiveshell.py ipython-7.13.0/IPython/core/interactiveshell.py --- ipython-7.12.0/IPython/core/interactiveshell.py 2020-02-01 01:37:48.000000000 +0000 +++ ipython-7.13.0/IPython/core/interactiveshell.py 2020-02-29 00:45:41.000000000 +0000 @@ -2218,7 +2218,7 @@ list where you want the completer to be inserted.""" newcomp = types.MethodType(completer,self.Completer) - self.Completer.matchers.insert(pos,newcomp) + self.Completer.custom_matchers.insert(pos,newcomp) def set_completer_frame(self, frame=None): """Set the frame of the completer.""" diff -Nru ipython-7.12.0/IPython/core/oinspect.py ipython-7.13.0/IPython/core/oinspect.py --- ipython-7.12.0/IPython/core/oinspect.py 2020-02-01 01:37:48.000000000 +0000 +++ ipython-7.13.0/IPython/core/oinspect.py 2020-02-29 00:45:41.000000000 +0000 @@ -198,8 +198,7 @@ @undoc def getargspec(obj): - """Wrapper around :func:`inspect.getfullargspec` on Python 3, and - :func:inspect.getargspec` on Python 2. + """Wrapper around :func:`inspect.getfullargspec` In addition to functions and methods, this can also handle objects with a ``__call__`` attribute. diff -Nru ipython-7.12.0/IPython/core/release.py ipython-7.13.0/IPython/core/release.py --- ipython-7.12.0/IPython/core/release.py 2020-02-01 01:37:48.000000000 +0000 +++ ipython-7.13.0/IPython/core/release.py 2020-02-29 00:45:41.000000000 +0000 @@ -20,7 +20,7 @@ # release. 'dev' as a _version_extra string means this is a development # version _version_major = 7 -_version_minor = 12 +_version_minor = 13 _version_patch = 0 _version_extra = '.dev' # _version_extra = 'b1' diff -Nru ipython-7.12.0/IPython/core/shellapp.py ipython-7.13.0/IPython/core/shellapp.py --- ipython-7.12.0/IPython/core/shellapp.py 2020-02-01 01:37:48.000000000 +0000 +++ ipython-7.13.0/IPython/core/shellapp.py 2020-02-29 00:45:41.000000000 +0000 @@ -60,6 +60,10 @@ colours.""", "Disable using colors for info related things." ) +addflag('ignore-cwd', 'InteractiveShellApp.ignore_cwd', + "Exclude the current working directory from sys.path", + "Include the current working directory in sys.path", +) nosep_config = Config() nosep_config.InteractiveShell.separate_in = '' nosep_config.InteractiveShell.separate_out = '' @@ -168,6 +172,12 @@ When False, pylab mode should not import any names into the user namespace. """ ).tag(config=True) + ignore_cwd = Bool( + False, + help="""If True, IPython will not add the current working directory to sys.path. + When False, the current working directory is added to sys.path, allowing imports + of modules defined in the current directory.""" + ).tag(config=True) shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True) # whether interact-loop should start @@ -189,8 +199,10 @@ .. versionchanged:: 7.2 Try to insert after the standard library, instead of first. + .. versionchanged:: 8.0 + Allow optionally not including the current directory in sys.path """ - if '' in sys.path: + if '' in sys.path or self.ignore_cwd: return for idx, path in enumerate(sys.path): parent, last_part = os.path.split(path) diff -Nru ipython-7.12.0/IPython/core/tests/test_interactiveshell.py ipython-7.13.0/IPython/core/tests/test_interactiveshell.py --- ipython-7.12.0/IPython/core/tests/test_interactiveshell.py 2020-02-01 01:37:48.000000000 +0000 +++ ipython-7.13.0/IPython/core/tests/test_interactiveshell.py 2020-02-29 00:45:41.000000000 +0000 @@ -998,3 +998,21 @@ assert not ip.should_run_async("a = 5") assert ip.should_run_async("await x") assert ip.should_run_async("import asyncio; await asyncio.sleep(1)") + + +def test_set_custom_completer(): + num_completers = len(ip.Completer.matchers) + + def foo(*args, **kwargs): + return "I'm a completer!" + + ip.set_custom_completer(foo, 0) + + # check that we've really added a new completer + assert len(ip.Completer.matchers) == num_completers + 1 + + # check that the first completer is the function we defined + assert ip.Completer.matchers[0]() == "I'm a completer!" + + # clean up + ip.Completer.custom_matchers.pop() diff -Nru ipython-7.12.0/IPython/core/ultratb.py ipython-7.13.0/IPython/core/ultratb.py --- ipython-7.12.0/IPython/core/ultratb.py 2020-02-01 01:37:48.000000000 +0000 +++ ipython-7.13.0/IPython/core/ultratb.py 2020-02-29 00:45:41.000000000 +0000 @@ -101,10 +101,7 @@ import tokenize import traceback -try: # Python 2 - generate_tokens = tokenize.generate_tokens -except AttributeError: # Python 3 - generate_tokens = tokenize.tokenize +from tokenize import generate_tokens # For purposes of monkeypatching inspect to fix a bug in it. from inspect import getsourcefile, getfile, getmodule, \ diff -Nru ipython-7.12.0/IPython/lib/deepreload.py ipython-7.13.0/IPython/lib/deepreload.py --- ipython-7.12.0/IPython/lib/deepreload.py 2020-02-01 01:37:48.000000000 +0000 +++ ipython-7.13.0/IPython/lib/deepreload.py 2020-02-29 00:45:41.000000000 +0000 @@ -7,13 +7,7 @@ imported from that module, which is useful when you're changing files deep inside a package. -To use this as your default reload function, type this for Python 2:: - - import __builtin__ - from IPython.lib import deepreload - __builtin__.reload = deepreload.reload - -Or this for Python 3:: +To use this as your default reload function, type this:: import builtins from IPython.lib import deepreload diff -Nru ipython-7.12.0/IPython/lib/display.py ipython-7.13.0/IPython/lib/display.py --- ipython-7.12.0/IPython/lib/display.py 2020-02-01 01:37:48.000000000 +0000 +++ ipython-7.13.0/IPython/lib/display.py 2020-02-29 00:45:41.000000000 +0000 @@ -130,7 +130,6 @@ @staticmethod def _make_wav(data, rate, normalize): """ Transform a numpy array to a PCM bytestring """ - import struct from io import BytesIO import wave @@ -145,7 +144,7 @@ waveobj.setframerate(rate) waveobj.setsampwidth(2) waveobj.setcomptype('NONE','NONE') - waveobj.writeframes(b''.join([struct.pack(' 3: return '{}.{}\N{HORIZONTAL ELLIPSIS}{}.{}'.format(object_parts[0], object_parts[1][0], object_parts[-2][-1], object_parts[-1]) diff -Nru ipython-7.12.0/IPython/terminal/tests/test_interactivshell.py ipython-7.13.0/IPython/terminal/tests/test_interactivshell.py --- ipython-7.12.0/IPython/terminal/tests/test_interactivshell.py 2020-02-01 01:37:48.000000000 +0000 +++ ipython-7.13.0/IPython/terminal/tests/test_interactivshell.py 2020-02-29 00:45:41.000000000 +0000 @@ -5,6 +5,7 @@ import sys import unittest +import os from IPython.core.inputtransformer import InputTransformer from IPython.testing import tools as tt @@ -19,6 +20,10 @@ _elide('concatenate((a1, a2, ...), axis') # do not raise _elide('concatenate((a1, a2, ..), . axis') # do not raise nt.assert_equal(_elide('aaaa.bbbb.ccccc.dddddd.eeeee.fffff.gggggg.hhhhhh'), 'aaaa.b…g.hhhhhh') + + test_string = os.sep.join(['', 10*'a', 10*'b', 10*'c', '']) + expect_stirng = os.sep + 'a' + '\N{HORIZONTAL ELLIPSIS}' + 'b' + os.sep + 10*'c' + nt.assert_equal(_elide(test_string), expect_stirng) class TestContextAwareCompletion(unittest.TestCase): diff -Nru ipython-7.12.0/IPython/utils/encoding.py ipython-7.13.0/IPython/utils/encoding.py --- ipython-7.12.0/IPython/utils/encoding.py 2020-02-01 01:37:48.000000000 +0000 +++ ipython-7.13.0/IPython/utils/encoding.py 2020-02-29 00:45:41.000000000 +0000 @@ -44,7 +44,7 @@ Then fall back on locale.getpreferredencoding(), which should be a sensible platform default (that respects LANG environment), and finally to sys.getdefaultencoding() which is the most conservative option, - and usually ASCII on Python 2 or UTF8 on Python 3. + and usually UTF8 as of Python 3. """ enc = None if prefer_stream: diff -Nru ipython-7.12.0/IPython/utils/openpy.py ipython-7.13.0/IPython/utils/openpy.py --- ipython-7.12.0/IPython/utils/openpy.py 2020-02-01 01:37:48.000000000 +0000 +++ ipython-7.13.0/IPython/utils/openpy.py 2020-02-29 00:45:41.000000000 +0000 @@ -66,8 +66,7 @@ The path to the file to read. skip_encoding_cookie : bool If True (the default), and the encoding declaration is found in the first - two lines, that line will be excluded from the output - compiling a - unicode string with an encoding declaration is a SyntaxError in Python 2. + two lines, that line will be excluded from the output. Returns ------- @@ -91,8 +90,7 @@ bytes.decode(), but here 'replace' is the default. skip_encoding_cookie : bool If True (the default), and the encoding declaration is found in the first - two lines, that line will be excluded from the output - compiling a - unicode string with an encoding declaration is a SyntaxError in Python 2. + two lines, that line will be excluded from the output. Returns ------- diff -Nru ipython-7.12.0/IPython/utils/_process_win32.py ipython-7.13.0/IPython/utils/_process_win32.py --- ipython-7.12.0/IPython/utils/_process_win32.py 2020-02-01 01:37:48.000000000 +0000 +++ ipython-7.13.0/IPython/utils/_process_win32.py 2020-02-29 00:45:41.000000000 +0000 @@ -18,10 +18,12 @@ import os import sys import ctypes +import time from ctypes import c_int, POINTER from ctypes.wintypes import LPCWSTR, HLOCAL -from subprocess import STDOUT +from subprocess import STDOUT, TimeoutExpired +from threading import Thread # our own imports from ._process_common import read_no_interrupt, process_handler, arg_split as py_arg_split @@ -93,15 +95,29 @@ def _system_body(p): """Callback for _system.""" enc = DEFAULT_ENCODING - for line in read_no_interrupt(p.stdout).splitlines(): - line = line.decode(enc, 'replace') - print(line, file=sys.stdout) - for line in read_no_interrupt(p.stderr).splitlines(): - line = line.decode(enc, 'replace') - print(line, file=sys.stderr) - # Wait to finish for returncode - return p.wait() + def stdout_read(): + for line in read_no_interrupt(p.stdout).splitlines(): + line = line.decode(enc, 'replace') + print(line, file=sys.stdout) + + def stderr_read(): + for line in read_no_interrupt(p.stderr).splitlines(): + line = line.decode(enc, 'replace') + print(line, file=sys.stderr) + + Thread(target=stdout_read).start() + Thread(target=stderr_read).start() + + # Wait to finish for returncode. Unfortunately, Python has a bug where + # wait() isn't interruptible (https://bugs.python.org/issue28168) so poll in + # a loop instead of just doing `return p.wait()`. + while True: + result = p.poll() + if result is None: + time.sleep(0.01) + else: + return result def system(cmd): @@ -116,9 +132,7 @@ Returns ------- - None : we explicitly do NOT return the subprocess status code, as this - utility is meant to be used extensively in IPython, where any return value - would trigger :func:`sys.displayhook` calls. + int : child process' exit code. """ # The controller provides interactivity with both # stdin and stdout diff -Nru ipython-7.12.0/IPython/utils/tests/test_process.py ipython-7.13.0/IPython/utils/tests/test_process.py --- ipython-7.12.0/IPython/utils/tests/test_process.py 2020-02-01 01:37:48.000000000 +0000 +++ ipython-7.13.0/IPython/utils/tests/test_process.py 2020-02-29 00:45:41.000000000 +0000 @@ -15,13 +15,19 @@ #----------------------------------------------------------------------------- import sys +import signal import os +import time +from _thread import interrupt_main # Py 3 +import threading +from unittest import SkipTest import nose.tools as nt from IPython.utils.process import (find_cmd, FindCmdError, arg_split, system, getoutput, getoutputerror, get_output_error_code) +from IPython.utils.capture import capture_output from IPython.testing import decorators as dec from IPython.testing import tools as tt @@ -107,6 +113,49 @@ status = system('%s -c "import sys"' % python) self.assertEqual(status, 0) + def assert_interrupts(self, command): + """ + Interrupt a subprocess after a second. + """ + if threading.main_thread() != threading.current_thread(): + raise nt.SkipTest("Can't run this test if not in main thread.") + + # Some tests can overwrite SIGINT handler (by using pdb for example), + # which then breaks this test, so just make sure it's operating + # normally. + signal.signal(signal.SIGINT, signal.default_int_handler) + + def interrupt(): + # Wait for subprocess to start: + time.sleep(0.5) + interrupt_main() + + threading.Thread(target=interrupt).start() + start = time.time() + try: + result = command() + except KeyboardInterrupt: + # Success! + pass + end = time.time() + self.assertTrue( + end - start < 2, "Process didn't die quickly: %s" % (end - start) + ) + return result + + def test_system_interrupt(self): + """ + When interrupted in the way ipykernel interrupts IPython, the + subprocess is interrupted. + """ + def command(): + return system('%s -c "import time; time.sleep(5)"' % python) + + status = self.assert_interrupts(command) + self.assertNotEqual( + status, 0, "The process wasn't interrupted. Status: %s" % (status,) + ) + def test_getoutput(self): out = getoutput('%s "%s"' % (python, self.fname)) # we can't rely on the order the line buffered streams are flushed @@ -131,7 +180,7 @@ out, err = getoutputerror('%s "%s"' % (python, self.fname)) self.assertEqual(out, 'on stdout') self.assertEqual(err, 'on stderr') - + def test_get_output_error_code(self): quiet_exit = '%s -c "import sys; sys.exit(1)"' % python out, err, code = get_output_error_code(quiet_exit) @@ -142,3 +191,5 @@ self.assertEqual(out, 'on stdout') self.assertEqual(err, 'on stderr') self.assertEqual(code, 0) + + diff -Nru ipython-7.12.0/tools/autoformat_file ipython-7.13.0/tools/autoformat_file --- ipython-7.12.0/tools/autoformat_file 1970-01-01 00:00:00.000000000 +0000 +++ ipython-7.13.0/tools/autoformat_file 2020-02-29 00:45:41.000000000 +0000 @@ -0,0 +1,46 @@ +#!/bin/bash + +set -ueo pipefail +FILE=$1 + +echo "will update $FILE" + +echo $LINENO $? +pyupgrade --py36-plus --exit-zero-even-if-changed $FILE + +echo $LINENO $? +git commit -am"Apply pyupgrade to $FILE + + pyupgrade --py36-plus $FILE + +To ignore those changes when using git blame see the content of +.git-blame-ignore-revs" + +HASH=$(git rev-parse HEAD) + +echo "$HASH # apply pyupgrade to $FILE" >> .git-blame-ignore-revs + +git commit -am'Update .git-blame-ignore-revs with previous commit' + +##### + +black --target-version py36 $FILE + + +git commit -am"Apply black to $FILE + + black --target-version py36 $FILE + +To ignore those changes when using git blame see the content of +.git-blame-ignore-revs" + +HASH=$(git rev-parse HEAD) + +echo "$HASH # apply black to $FILE" >> .git-blame-ignore-revs + +git commit -am'Update .git-blame-ignore-revs with previous commit' + +echo +echo "Updating, reformatting and adding to .git-blame-ignore-revs successful" + + diff -Nru ipython-7.12.0/.travis.yml ipython-7.13.0/.travis.yml --- ipython-7.12.0/.travis.yml 2020-02-01 01:37:48.000000000 +0000 +++ ipython-7.13.0/.travis.yml 2020-02-29 00:45:41.000000000 +0000 @@ -24,7 +24,7 @@ if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then env | sort if ! which python$TRAVIS_PYTHON_VERSION; then - HOMEBREW_NO_AUTO_UPDATE=1 brew tap carreau/homebrew-python-frameworks + HOMEBREW_NO_AUTO_UPDATE=1 brew tap minrk/homebrew-python-frameworks HOMEBREW_NO_AUTO_UPDATE=1 brew cask install python-framework-${TRAVIS_PYTHON_VERSION/./} fi python3 -m pip install virtualenv @@ -72,7 +72,7 @@ - arch: arm64 python: "3.7" dist: xenial - env: ARM64=True + env: ARM64=True IPYTHON_TESTING_TIMEOUT_SCALE=2 sudo: true - arch: amd64 python: "3.8-dev"