diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 2778805..aceca00 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -2,9 +2,9 @@ name: Run MyPy on: push: - branches: [ master ] + branches: [ master, 7.x] pull_request: - branches: [ master ] + branches: [ master, 7.x] jobs: build: diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 80497bb..db57170 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -5,9 +5,9 @@ name: Python package on: push: - branches: [ master ] + branches: [ master, 7.x ] pull_request: - branches: [ master ] + branches: [ master, 7.x ] jobs: build: diff --git a/.travis.yml b/.travis.yml index 3702df2..db02c50 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,7 +37,7 @@ install: - pip install -e file://$PWD#egg=ipython[test] --upgrade - pip install trio curio --upgrade --upgrade-strategy eager - pip install 'pytest' 'matplotlib !=3.2.0' - - pip install codecov check-manifest pytest-cov --upgrade anyio pytest-asyncio + - pip install codecov check-manifest pytest-cov --upgrade anyio pytest-trio script: diff --git a/IPython/core/compilerop.py b/IPython/core/compilerop.py index c4771af..50672a1 100644 --- a/IPython/core/compilerop.py +++ b/IPython/core/compilerop.py @@ -99,7 +99,7 @@ class CachingCompiler(codeop.Compile): Arguments are exactly the same as ast.parse (in the standard library), and are passed to the built-in compile function.""" return compile(source, filename, symbol, self.flags | PyCF_ONLY_AST, 1) - + def reset_compiler_flags(self): """Reset compiler flags to default state.""" # This value is copied from codeop.Compile.__init__, so if that ever @@ -112,25 +112,53 @@ class CachingCompiler(codeop.Compile): """ return self.flags - def cache(self, code, number=0): + def get_code_name(self, raw_code, transformed_code, number): + """Compute filename given the code, and the cell number. + + Parameters + ---------- + raw_code : str + The raw cell code. + transformed_code : str + The executable Python source code to cache and compile. + number : int + A number which forms part of the code's name. Used for the execution + counter. + + Returns + ------- + The computed filename. + """ + return code_name(transformed_code, number) + + def cache(self, transformed_code, number=0, raw_code=None): """Make a name for a block of code, and cache the code. Parameters ---------- - code : str - The Python source code to cache. + transformed_code : str + The executable Python source code to cache and compile. number : int A number which forms part of the code's name. Used for the execution counter. + raw_code : str + The raw code before transformation, if None, set to `transformed_code`. Returns ------- The name of the cached code (as a string). Pass this as the filename argument to compilation, so that tracebacks are correctly hooked up. """ - name = code_name(code, number) - entry = (len(code), time.time(), - [line+'\n' for line in code.splitlines()], name) + if raw_code is None: + raw_code = transformed_code + + name = self.get_code_name(raw_code, transformed_code, number) + entry = ( + len(transformed_code), + time.time(), + [line + "\n" for line in transformed_code.splitlines()], + name, + ) linecache.cache[name] = entry linecache._ipython_cache[name] = entry return name @@ -146,7 +174,7 @@ class CachingCompiler(codeop.Compile): yield finally: # turn off only the bits we turned on so that something like - # __future__ that set flags stays. + # __future__ that set flags stays. self.flags &= ~turn_on_bits diff --git a/IPython/core/completer.py b/IPython/core/completer.py index 0100bfc..78bc57e 100644 --- a/IPython/core/completer.py +++ b/IPython/core/completer.py @@ -201,7 +201,9 @@ def provisionalcompleter(action='ignore'): >>> completer.do_experimental_things() # raises. - .. note:: Unstable + .. note:: + + Unstable By using this context manager you agree that the API in use may change without warning, and that you won't complain if they do so. @@ -356,7 +358,9 @@ class Completion: """ Completion object used and return by IPython completers. - .. warning:: Unstable + .. warning:: + + Unstable This function is unstable, API may change without warning. It will also raise unless use in proper context manager. @@ -419,7 +423,9 @@ def _deduplicate_completions(text: str, completions: _IC)-> _IC: """ Deduplicate a set of completions. - .. warning:: Unstable + .. warning:: + + Unstable This function is unstable, API may change without warning. @@ -459,7 +465,9 @@ def rectify_completions(text: str, completions: _IC, *, _debug=False)->_IC: """ Rectify a set of completions to all have the same ``start`` and ``end`` - .. warning:: Unstable + .. warning:: + + Unstable This function is unstable, API may change without warning. It will also raise unless use in proper context manager. @@ -1837,7 +1845,9 @@ class IPCompleter(Completer): """ Returns an iterator over the possible completions - .. warning:: Unstable + .. warning:: + + Unstable This function is unstable, API may change without warning. It will also raise unless use in proper context manager. diff --git a/IPython/core/crashhandler.py b/IPython/core/crashhandler.py index 1e0b429..2cfe85c 100644 --- a/IPython/core/crashhandler.py +++ b/IPython/core/crashhandler.py @@ -23,6 +23,7 @@ import os import sys import traceback from pprint import pformat +from pathlib import Path from IPython.core import ultratb from IPython.core.release import author_email @@ -151,10 +152,10 @@ class CrashHandler(object): try: rptdir = self.app.ipython_dir except: - rptdir = os.getcwd() - if rptdir is None or not os.path.isdir(rptdir): - rptdir = os.getcwd() - report_name = os.path.join(rptdir,self.crash_report_fname) + rptdir = Path.cwd() + if rptdir is None or not Path.is_dir(rptdir): + rptdir = Path.cwd() + report_name = rptdir / self.crash_report_fname # write the report filename into the instance dict so it can get # properly expanded out in the user message template self.crash_report_fname = report_name diff --git a/IPython/core/debugger.py b/IPython/core/debugger.py index dde8492..b0e62ca 100644 --- a/IPython/core/debugger.py +++ b/IPython/core/debugger.py @@ -43,13 +43,14 @@ from IPython.testing.skipdoctest import skip_doctest prompt = 'ipdb> ' -#We have to check this directly from sys.argv, config struct not yet available +# We have to check this directly from sys.argv, config struct not yet available from pdb import Pdb as OldPdb # Allow the set_trace code to operate outside of an ipython instance, even if # it does so with some limitations. The rest of this support is implemented in # the Tracer constructor. + def make_arrow(pad): """generate the leading arrow in front of traceback or debugger""" if pad >= 2: @@ -67,16 +68,16 @@ def BdbQuit_excepthook(et, ev, tb, excepthook=None): """ warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1", DeprecationWarning, stacklevel=2) - if et==bdb.BdbQuit: + if et == bdb.BdbQuit: print('Exiting Debugger.') elif excepthook is not None: excepthook(et, ev, tb) else: # Backwards compatibility. Raise deprecation warning? - BdbQuit_excepthook.excepthook_ori(et,ev,tb) + BdbQuit_excepthook.excepthook_ori(et, ev, tb) -def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None): +def BdbQuit_IPython_excepthook(self, et, ev, tb, tb_offset=None): warnings.warn( "`BdbQuit_IPython_excepthook` is deprecated since version 5.1", DeprecationWarning, stacklevel=2) @@ -203,7 +204,7 @@ class Pdb(OldPdb): def __init__(self, color_scheme=None, completekey=None, stdin=None, stdout=None, context=5, **kwargs): """Create a new IPython debugger. - + :param color_scheme: Deprecated, do not use. :param completekey: Passed to pdb.Pdb. :param stdin: Passed to pdb.Pdb. @@ -237,7 +238,7 @@ class Pdb(OldPdb): self.shell = TerminalInteractiveShell.instance() # needed by any code which calls __import__("__main__") after # the debugger was entered. See also #9941. - sys.modules['__main__'] = save_main + sys.modules["__main__"] = save_main if color_scheme is not None: warnings.warn( @@ -272,7 +273,6 @@ class Pdb(OldPdb): cst['Neutral'].colors.breakpoint_enabled = C.LightRed cst['Neutral'].colors.breakpoint_disabled = C.Red - # Add a python parser so we can syntax highlight source while # debugging. self.parser = PyColorize.Parser(style=color_scheme) @@ -320,13 +320,25 @@ class Pdb(OldPdb): except KeyboardInterrupt: self.stdout.write("\n" + self.shell.get_exception_only()) + def precmd(self, line): + """Perform useful escapes on the command before it is executed.""" + + if line.endswith("??"): + line = "pinfo2 " + line[:-2] + elif line.endswith("?"): + line = "pinfo " + line[:-1] + + line = super().precmd(line) + + return line + def new_do_frame(self, arg): OldPdb.do_frame(self, arg) def new_do_quit(self, arg): if hasattr(self, 'old_all_completions'): - self.shell.Completer.all_completions=self.old_all_completions + self.shell.Completer.all_completions = self.old_all_completions return OldPdb.do_quit(self, arg) @@ -344,7 +356,7 @@ class Pdb(OldPdb): if context is None: context = self.context try: - context=int(context) + context = int(context) if context <= 0: raise ValueError("Context must be a positive integer") except (TypeError, ValueError) as e: @@ -373,7 +385,7 @@ class Pdb(OldPdb): if context is None: context = self.context try: - context=int(context) + context = int(context) if context <= 0: raise ValueError("Context must be a positive integer") except (TypeError, ValueError) as e: @@ -390,7 +402,7 @@ class Pdb(OldPdb): if context is None: context = self.context try: - context=int(context) + context = int(context) if context <= 0: print("Context must be a positive integer", file=self.stdout) except (TypeError, ValueError): @@ -402,11 +414,10 @@ class Pdb(OldPdb): Colors = self.color_scheme_table.active_colors ColorsNormal = Colors.Normal - tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal) - tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal) - tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal) - tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, - ColorsNormal) + tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal) + tpl_call = "%s%%s%s%%s%s" % (Colors.vName, Colors.valEm, ColorsNormal) + tpl_line = "%%s%s%%s %s%%s" % (Colors.lineno, ColorsNormal) + tpl_line_em = "%%s%s%%s %s%%s%s" % (Colors.linenoEm, Colors.line, ColorsNormal) frame, lineno = frame_lineno @@ -439,8 +450,8 @@ class Pdb(OldPdb): if frame is self.curframe: ret.append('> ') else: - ret.append(' ') - ret.append(u'%s(%s)%s\n' % (link,lineno,call)) + ret.append(" ") + ret.append("%s(%s)%s\n" % (link, lineno, call)) start = lineno - 1 - context//2 lines = linecache.getlines(filename) @@ -448,17 +459,17 @@ class Pdb(OldPdb): start = max(start, 0) lines = lines[start : start + context] - for i,line in enumerate(lines): - show_arrow = (start + 1 + i == lineno) - linetpl = (frame is self.curframe or show_arrow) \ - and tpl_line_em \ - or tpl_line - ret.append(self.__format_line(linetpl, filename, - start + 1 + i, line, - arrow = show_arrow) ) - return ''.join(ret) - - def __format_line(self, tpl_line, filename, lineno, line, arrow = False): + for i, line in enumerate(lines): + show_arrow = start + 1 + i == lineno + linetpl = (frame is self.curframe or show_arrow) and tpl_line_em or tpl_line + ret.append( + self.__format_line( + linetpl, filename, start + 1 + i, line, arrow=show_arrow + ) + ) + return "".join(ret) + + def __format_line(self, tpl_line, filename, lineno, line, arrow=False): bp_mark = "" bp_mark_color = "" @@ -488,7 +499,6 @@ class Pdb(OldPdb): return tpl_line % (bp_mark_color + bp_mark, num, line) - def print_list_lines(self, filename, first, last): """The printing (as opposed to the parsing part of a 'list' command.""" @@ -507,9 +517,13 @@ class Pdb(OldPdb): break if lineno == self.curframe.f_lineno: - line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True) + line = self.__format_line( + tpl_line_em, filename, lineno, line, arrow=True + ) else: - line = self.__format_line(tpl_line, filename, lineno, line, arrow = False) + line = self.__format_line( + tpl_line, filename, lineno, line, arrow=False + ) src.append(line) self.lineno = lineno @@ -706,7 +720,7 @@ class Pdb(OldPdb): Will skip hidden frames. """ - ## modified version of upstream that skips + # modified version of upstream that skips # frames with __tracebackide__ if self.curindex == 0: self.error("Oldest frame") @@ -720,11 +734,9 @@ class Pdb(OldPdb): if count < 0: _newframe = 0 else: - _newindex = self.curindex counter = 0 hidden_frames = self.hidden_frames(self.stack) for i in range(self.curindex - 1, -1, -1): - frame = self.stack[i][0] if hidden_frames[i] and self.skip_hidden: skipped += 1 continue @@ -765,12 +777,10 @@ class Pdb(OldPdb): if count < 0: _newframe = len(self.stack) - 1 else: - _newindex = self.curindex counter = 0 skipped = 0 hidden_frames = self.hidden_frames(self.stack) for i in range(self.curindex + 1, len(self.stack)): - frame = self.stack[i][0] if hidden_frames[i] and self.skip_hidden: skipped += 1 continue @@ -796,6 +806,20 @@ class Pdb(OldPdb): do_d = do_down do_u = do_up + def do_context(self, context): + """context number_of_lines + Set the number of lines of source code to show when displaying + stacktrace information. + """ + try: + new_context = int(context) + if new_context <= 0: + raise ValueError() + self.context = new_context + except ValueError: + self.error("The 'context' command requires a positive integer argument.") + + class InterruptiblePdb(Pdb): """Version of debugger where KeyboardInterrupt exits the debugger altogether.""" diff --git a/IPython/core/display.py b/IPython/core/display.py index 2920b84..bd098e7 100644 --- a/IPython/core/display.py +++ b/IPython/core/display.py @@ -567,7 +567,7 @@ class JSON(DisplayObject): Path to a local file to load the data from. expanded : boolean Metadata to control whether a JSON display component is expanded. - metadata: dict + metadata : dict Specify extra metadata to attach to the json display object. root : str The name of the root element of the JSON tree @@ -651,12 +651,11 @@ class GeoJSON(JSON): A URL to download the data from. filename : unicode Path to a local file to load the data from. - metadata: dict + metadata : dict Specify extra metadata to attach to the json display object. Examples -------- - The following will display an interactive map of Mars with a point of interest on frontend that do support GeoJSON display. @@ -723,7 +722,7 @@ class Javascript(TextDisplayObject): running the source code. The full URLs of the libraries should be given. A single Javascript library URL can also be given as a string. - css: : list or str + css : list or str A sequence of css files to load before running the source code. The full URLs of the css files should be given. A single css URL can also be given as a string. @@ -844,25 +843,33 @@ class Image(DisplayObject): from image data. For non-embedded images, you can just set the desired display width and height directly. - unconfined: bool + unconfined : bool Set unconfined=True to disable max-width confinement of the image. - metadata: dict + metadata : dict Specify extra metadata to attach to the image. Examples -------- - # embedded image data, works in qtconsole and notebook - # when passed positionally, the first arg can be any of raw image data, - # a URL, or a filename from which to load image data. - # The result is always embedding image data for inline images. - Image('http://www.google.fr/images/srpr/logo3w.png') - Image('/path/to/image.jpg') - Image(b'RAW_PNG_DATA...') - - # Specifying Image(url=...) does not embed the image data, - # it only generates `` tag with a link to the source. - # This will not work in the qtconsole or offline. - Image(url='http://www.google.fr/images/srpr/logo3w.png') + embedded image data, works in qtconsole and notebook + when passed positionally, the first arg can be any of raw image data, + a URL, or a filename from which to load image data. + The result is always embedding image data for inline images. + + >>> Image('http://www.google.fr/images/srpr/logo3w.png') + + + >>> Image('/path/to/image.jpg') + + + >>> Image(b'RAW_PNG_DATA...') + + + Specifying Image(url=...) does not embed the image data, + it only generates ```` tag with a link to the source. + This will not work in the qtconsole or offline. + + >>> Image(url='http://www.google.fr/images/srpr/logo3w.png') + """ if isinstance(data, (Path, PurePath)): @@ -1036,25 +1043,24 @@ class Video(DisplayObject): ---------- data : unicode, str or bytes The raw video data or a URL or filename to load the data from. - Raw data will require passing `embed=True`. + Raw data will require passing ``embed=True``. url : unicode - A URL for the video. If you specify `url=`, + A URL for the video. If you specify ``url=``, the image data will not be embedded. filename : unicode Path to a local file containing the video. - Will be interpreted as a local URL unless `embed=True`. + Will be interpreted as a local URL unless ``embed=True``. embed : bool Should the video be embedded using a data URI (True) or be loaded using a