diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 728482ffa..8de7da368 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,30 +14,6 @@ defaults: shell: bash jobs: - # Run "pre-commit run --all-files" - pre-commit: - runs-on: ubuntu-20.04 - timeout-minutes: 2 - - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - with: - python-version: 3.8 - - # ref: https://github.com/pre-commit/action - - uses: pre-commit/action@v2.0.0 - - name: Help message if pre-commit fail - if: ${{ failure() }} - run: | - echo "You can install pre-commit hooks to automatically run formatting" - echo "on each commit with:" - echo " pre-commit install" - echo "or you can run by hand on staged files with" - echo " pre-commit run" - echo "or after-the-fact on already committed files with" - echo " pre-commit run --all-files" - build: runs-on: ${{ matrix.os }} strategy: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 25b0bcb3c..fbc07722b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,21 +16,21 @@ repos: - id: check-builtin-literals - id: trailing-whitespace - # - repo: https://github.com/psf/black - # rev: 22.1.0 - # hooks: - # - id: black - # args: ["--line-length", "100"] + - repo: https://github.com/psf/black + rev: 22.3.0 + hooks: + - id: black + args: ["--line-length", "100"] - # - repo: https://github.com/PyCQA/isort - # rev: 5.10.1 - # hooks: - # - id: isort - # files: \.py$ - # args: [--profile=black] + - repo: https://github.com/PyCQA/isort + rev: 5.10.1 + hooks: + - id: isort + files: \.py$ + args: [--profile=black] - repo: https://github.com/pre-commit/mirrors-prettier - rev: v2.5.1 + rev: v2.6.1 hooks: - id: prettier @@ -46,15 +46,6 @@ repos: # ] - repo: https://github.com/pre-commit/mirrors-eslint - rev: v8.8.0 + rev: v8.12.0 hooks: - id: eslint - - - repo: https://github.com/sirosen/check-jsonschema - rev: 0.10.2 - hooks: - - id: check-jsonschema - name: "Check GitHub Workflows" - files: ^\.github/workflows/ - types: [yaml] - args: ["--schemafile", "https://json.schemastore.org/github-workflow"] diff --git a/docs/conf.py b/docs/conf.py index 086fd7e83..e331f558a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,43 +18,43 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) +# sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'myst_parser', - 'sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', - 'sphinxcontrib_github_alt', + "myst_parser", + "sphinx.ext.autodoc", + "sphinx.ext.intersphinx", + "sphinxcontrib_github_alt", ] github_project_url = "https://github.com/ipython/ipykernel" # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # source_suffix = ['.rst', '.md'] -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = 'IPython Kernel' -copyright = '2015, IPython Development Team' -author = 'IPython Development Team' +project = "IPython Kernel" +copyright = "2015, IPython Development Team" +author = "IPython Development Team" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -63,14 +63,14 @@ version_ns = {} here = os.path.dirname(__file__) -version_py = os.path.join(here, os.pardir, 'ipykernel', '_version.py') +version_py = os.path.join(here, os.pardir, "ipykernel", "_version.py") with open(version_py) as f: - exec(compile(f.read(), version_py, 'exec'), version_ns) + exec(compile(f.read(), version_py, "exec"), version_ns) # The short X.Y version. -version = '%i.%i' % version_ns['version_info'][:2] +version = "%i.%i" % version_ns["version_info"][:2] # The full version, including alpha/beta/rc tags. -release = version_ns['__version__'] +release = version_ns["__version__"] # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -81,37 +81,37 @@ # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] +exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all # documents. -default_role = 'literal' +default_role = "literal" # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False +# keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False @@ -126,26 +126,26 @@ # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -155,122 +155,121 @@ # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. -#html_extra_path = [] +# html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr' -#html_search_language = 'en' +# html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # Now only 'ja' uses this config value -#html_search_options = {'type': 'default'} +# html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. -#html_search_scorer = 'scorer.js' +# html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'ipykerneldoc' +htmlhelp_basename = "ipykerneldoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', - -# Latex figure (float) alignment -#'figure_align': 'htbp', + # The paper size ('letterpaper' or 'a4paper'). + #'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + #'preamble': '', + # Latex figure (float) alignment + #'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'ipykernel.tex', 'IPython Kernel Documentation', - 'IPython Development Team', 'manual'), + ( + master_doc, + "ipykernel.tex", + "IPython Kernel Documentation", + "IPython Development Team", + "manual", + ), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'ipykernel', 'IPython Kernel Documentation', - [author], 1) -] +man_pages = [(master_doc, "ipykernel", "IPython Kernel Documentation", [author], 1)] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------- @@ -279,32 +278,38 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'ipykernel', 'IPython Kernel Documentation', - author, 'ipykernel', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "ipykernel", + "IPython Kernel Documentation", + author, + "ipykernel", + "One line description of project.", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False +# texinfo_no_detailmenu = False # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { - 'python': ('https://docs.python.org/3/', None), - 'ipython': ('https://ipython.readthedocs.io/en/latest', None), - 'jupyter': ('https://jupyter.readthedocs.io/en/latest', None), + "python": ("https://docs.python.org/3/", None), + "ipython": ("https://ipython.readthedocs.io/en/latest", None), + "jupyter": ("https://jupyter.readthedocs.io/en/latest", None), } def setup(app): here = os.path.dirname(os.path.abspath(__file__)) - shutil.copy(os.path.join(here, '..', 'CHANGELOG.md'), 'changelog.md') + shutil.copy(os.path.join(here, "..", "CHANGELOG.md"), "changelog.md") diff --git a/examples/embedding/inprocess_qtconsole.py b/examples/embedding/inprocess_qtconsole.py index 5d9998bff..55e514343 100644 --- a/examples/embedding/inprocess_qtconsole.py +++ b/examples/embedding/inprocess_qtconsole.py @@ -2,14 +2,13 @@ import sys import tornado - -from qtconsole.rich_ipython_widget import RichIPythonWidget -from qtconsole.inprocess import QtInProcessKernelManager from IPython.lib import guisupport +from qtconsole.inprocess import QtInProcessKernelManager +from qtconsole.rich_ipython_widget import RichIPythonWidget def print_process_id(): - print('Process ID is:', os.getpid()) + print("Process ID is:", os.getpid()) def init_asyncio_patch(): @@ -23,8 +22,13 @@ def init_asyncio_patch(): FIXME: if/when tornado supports the defaults in asyncio, remove and bump tornado requirement for py38 """ - if sys.platform.startswith("win") and sys.version_info >= (3, 8) and tornado.version_info < (6, 1): + if ( + sys.platform.startswith("win") + and sys.version_info >= (3, 8) + and tornado.version_info < (6, 1) + ): import asyncio + try: from asyncio import ( WindowsProactorEventLoopPolicy, @@ -39,6 +43,7 @@ def init_asyncio_patch(): # fallback to the pre-3.8 default of Selector asyncio.set_event_loop_policy(WindowsSelectorEventLoopPolicy()) + def main(): # Print the ID of the main process print_process_id() @@ -52,8 +57,8 @@ def main(): kernel_manager = QtInProcessKernelManager() kernel_manager.start_kernel() kernel = kernel_manager.kernel - kernel.gui = 'qt4' - kernel.shell.push({'foo': 43, 'print_process_id': print_process_id}) + kernel.gui = "qt4" + kernel.shell.push({"foo": 43, "print_process_id": print_process_id}) kernel_client = kernel_manager.client() kernel_client.start_channels() @@ -72,5 +77,5 @@ def stop(): guisupport.start_event_loop_qt4(app) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/examples/embedding/inprocess_terminal.py b/examples/embedding/inprocess_terminal.py index 5ccab1654..543adba64 100644 --- a/examples/embedding/inprocess_terminal.py +++ b/examples/embedding/inprocess_terminal.py @@ -2,13 +2,13 @@ import sys import tornado +from jupyter_console.ptshell import ZMQTerminalInteractiveShell from ipykernel.inprocess import InProcessKernelManager -from jupyter_console.ptshell import ZMQTerminalInteractiveShell def print_process_id(): - print('Process ID is:', os.getpid()) + print("Process ID is:", os.getpid()) def init_asyncio_patch(): @@ -22,8 +22,13 @@ def init_asyncio_patch(): FIXME: if/when tornado supports the defaults in asyncio, remove and bump tornado requirement for py38 """ - if sys.platform.startswith("win") and sys.version_info >= (3, 8) and tornado.version_info < (6, 1): + if ( + sys.platform.startswith("win") + and sys.version_info >= (3, 8) + and tornado.version_info < (6, 1) + ): import asyncio + try: from asyncio import ( WindowsProactorEventLoopPolicy, @@ -49,8 +54,8 @@ def main(): kernel_manager = InProcessKernelManager() kernel_manager.start_kernel() kernel = kernel_manager.kernel - kernel.gui = 'qt4' - kernel.shell.push({'foo': 43, 'print_process_id': print_process_id}) + kernel.gui = "qt4" + kernel.shell.push({"foo": 43, "print_process_id": print_process_id}) client = kernel_manager.client() client.start_channels() @@ -58,5 +63,5 @@ def main(): shell.mainloop() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/examples/embedding/internal_ipkernel.py b/examples/embedding/internal_ipkernel.py index a0e89f5cc..9538aacc9 100644 --- a/examples/embedding/internal_ipkernel.py +++ b/examples/embedding/internal_ipkernel.py @@ -1,27 +1,31 @@ -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Imports -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- import sys from IPython.lib.kernel import connect_qtconsole + from ipykernel.kernelapp import IPKernelApp -#----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- # Functions and classes -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- def mpl_kernel(gui): - """Launch and return an IPython kernel with matplotlib support for the desired gui - """ + """Launch and return an IPython kernel with matplotlib support for the desired gui""" kernel = IPKernelApp.instance() - kernel.initialize(['python', '--matplotlib=%s' % gui, - #'--log-level=10' - ]) + kernel.initialize( + [ + "python", + "--matplotlib=%s" % gui, + #'--log-level=10' + ] + ) return kernel class InternalIPKernel: - def init_ipkernel(self, backend): # Start IPython kernel with GUI event loop and mpl support self.ipkernel = mpl_kernel(backend) @@ -33,14 +37,14 @@ def init_ipkernel(self, backend): # Example: a variable that will be seen by the user in the shell, and # that the GUI modifies (the 'Counter++' button increments it): - self.namespace['app_counter'] = 0 - #self.namespace['ipkernel'] = self.ipkernel # dbg + self.namespace["app_counter"] = 0 + # self.namespace['ipkernel'] = self.ipkernel # dbg def print_namespace(self, evt=None): print("\n***Variables in User namespace***") for k, v in self.namespace.items(): - if not k.startswith('_'): - print('%s -> %r' % (k, v)) + if not k.startswith("_"): + print("%s -> %r" % (k, v)) sys.stdout.flush() def new_qt_console(self, evt=None): @@ -48,7 +52,7 @@ def new_qt_console(self, evt=None): return connect_qtconsole(self.ipkernel.abs_connection_file, profile=self.ipkernel.profile) def count(self, evt=None): - self.namespace['app_counter'] += 1 + self.namespace["app_counter"] += 1 def cleanup_consoles(self, evt=None): for c in self.consoles: diff --git a/examples/embedding/ipkernel_qtapp.py b/examples/embedding/ipkernel_qtapp.py index 4f9789580..7d6e61cf0 100755 --- a/examples/embedding/ipkernel_qtapp.py +++ b/examples/embedding/ipkernel_qtapp.py @@ -14,55 +14,54 @@ Consoles attached separately from a terminal will not be terminated, though they will notice that their kernel died. """ -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Imports -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- +from internal_ipkernel import InternalIPKernel from PyQt4 import Qt -from internal_ipkernel import InternalIPKernel -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Functions and classes -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- class SimpleWindow(Qt.QWidget, InternalIPKernel): - def __init__(self, app): Qt.QWidget.__init__(self) self.app = app self.add_widgets() - self.init_ipkernel('qt') + self.init_ipkernel("qt") def add_widgets(self): self.setGeometry(300, 300, 400, 70) - self.setWindowTitle('IPython in your app') + self.setWindowTitle("IPython in your app") # Add simple buttons: - console = Qt.QPushButton('Qt Console', self) + console = Qt.QPushButton("Qt Console", self) console.setGeometry(10, 10, 100, 35) - self.connect(console, Qt.SIGNAL('clicked()'), self.new_qt_console) + self.connect(console, Qt.SIGNAL("clicked()"), self.new_qt_console) - namespace = Qt.QPushButton('Namespace', self) + namespace = Qt.QPushButton("Namespace", self) namespace.setGeometry(120, 10, 100, 35) - self.connect(namespace, Qt.SIGNAL('clicked()'), self.print_namespace) + self.connect(namespace, Qt.SIGNAL("clicked()"), self.print_namespace) - count = Qt.QPushButton('Count++', self) + count = Qt.QPushButton("Count++", self) count.setGeometry(230, 10, 80, 35) - self.connect(count, Qt.SIGNAL('clicked()'), self.count) + self.connect(count, Qt.SIGNAL("clicked()"), self.count) # Quit and cleanup - quit = Qt.QPushButton('Quit', self) + quit = Qt.QPushButton("Quit", self) quit.setGeometry(320, 10, 60, 35) - self.connect(quit, Qt.SIGNAL('clicked()'), Qt.qApp, Qt.SLOT('quit()')) + self.connect(quit, Qt.SIGNAL("clicked()"), Qt.qApp, Qt.SLOT("quit()")) - self.app.connect(self.app, Qt.SIGNAL("lastWindowClosed()"), - self.app, Qt.SLOT("quit()")) + self.app.connect(self.app, Qt.SIGNAL("lastWindowClosed()"), self.app, Qt.SLOT("quit()")) self.app.aboutToQuit.connect(self.cleanup_consoles) -#----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- # Main script -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- if __name__ == "__main__": app = Qt.QApplication([]) diff --git a/examples/embedding/ipkernel_wxapp.py b/examples/embedding/ipkernel_wxapp.py index ffb82c864..2caad5be2 100755 --- a/examples/embedding/ipkernel_wxapp.py +++ b/examples/embedding/ipkernel_wxapp.py @@ -16,18 +16,18 @@ Ref: Modified from wxPython source code wxPython/samples/simple/simple.py """ -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Imports -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- import sys import wx - from internal_ipkernel import InternalIPKernel -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Functions and classes -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + class MyFrame(wx.Frame, InternalIPKernel): """ @@ -36,8 +36,7 @@ class MyFrame(wx.Frame, InternalIPKernel): """ def __init__(self, parent, title): - wx.Frame.__init__(self, parent, -1, title, - pos=(150, 150), size=(350, 285)) + wx.Frame.__init__(self, parent, -1, title, pos=(150, 150), size=(350, 285)) # Create the menubar menuBar = wx.MenuBar() @@ -86,7 +85,7 @@ def __init__(self, parent, title): panel.Layout() # Start the IPython kernel with gui support - self.init_ipkernel('wx') + self.init_ipkernel("wx") def OnTimeToClose(self, evt): """Event handler for the button click.""" @@ -107,11 +106,12 @@ def OnInit(self): self.ipkernel = frame.ipkernel return True -#----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- # Main script -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- -if __name__ == '__main__': +if __name__ == "__main__": app = MyApp(redirect=False, clearSigInt=False) # Very important, IPython-specific step: this gets GUI event loop diff --git a/ipykernel/__init__.py b/ipykernel/__init__.py index 3f753e3ee..bc5fbb726 100644 --- a/ipykernel/__init__.py +++ b/ipykernel/__init__.py @@ -1,2 +1,7 @@ -from ._version import version_info, __version__, kernel_protocol_version_info, kernel_protocol_version +from ._version import ( + __version__, + kernel_protocol_version, + kernel_protocol_version_info, + version_info, +) from .connect import * diff --git a/ipykernel/__main__.py b/ipykernel/__main__.py index d1f0cf533..f66b36c8e 100644 --- a/ipykernel/__main__.py +++ b/ipykernel/__main__.py @@ -1,3 +1,4 @@ -if __name__ == '__main__': +if __name__ == "__main__": from ipykernel import kernelapp as app + app.launch_new_instance() diff --git a/ipykernel/_eventloop_macos.py b/ipykernel/_eventloop_macos.py index 8c7cbdd91..94762b65d 100644 --- a/ipykernel/_eventloop_macos.py +++ b/ipykernel/_eventloop_macos.py @@ -10,7 +10,7 @@ import ctypes.util from threading import Event -objc = ctypes.cdll.LoadLibrary(ctypes.util.find_library('objc')) +objc = ctypes.cdll.LoadLibrary(ctypes.util.find_library("objc")) void_p = ctypes.c_void_p @@ -25,7 +25,7 @@ def _utf8(s): """ensure utf8 bytes""" if not isinstance(s, bytes): - s = s.encode('utf8') + s = s.encode("utf8") return s @@ -42,7 +42,7 @@ def C(classname): # end obj-c boilerplate from appnope # CoreFoundation C-API calls we will use: -CoreFoundation = ctypes.cdll.LoadLibrary(ctypes.util.find_library('CoreFoundation')) +CoreFoundation = ctypes.cdll.LoadLibrary(ctypes.util.find_library("CoreFoundation")) CFAbsoluteTimeGetCurrent = CoreFoundation.CFAbsoluteTimeGetCurrent CFAbsoluteTimeGetCurrent.restype = ctypes.c_double @@ -60,43 +60,46 @@ def C(classname): CFRunLoopTimerCreate = CoreFoundation.CFRunLoopTimerCreate CFRunLoopTimerCreate.restype = void_p CFRunLoopTimerCreate.argtypes = [ - void_p, # allocator (NULL) - ctypes.c_double, # fireDate - ctypes.c_double, # interval - ctypes.c_int, # flags (0) - ctypes.c_int, # order (0) - void_p, # callout - void_p, # context + void_p, # allocator (NULL) + ctypes.c_double, # fireDate + ctypes.c_double, # interval + ctypes.c_int, # flags (0) + ctypes.c_int, # order (0) + void_p, # callout + void_p, # context ] CFRunLoopAddTimer = CoreFoundation.CFRunLoopAddTimer CFRunLoopAddTimer.restype = None -CFRunLoopAddTimer.argtypes = [ void_p, void_p, void_p ] +CFRunLoopAddTimer.argtypes = [void_p, void_p, void_p] -kCFRunLoopCommonModes = void_p.in_dll(CoreFoundation, 'kCFRunLoopCommonModes') +kCFRunLoopCommonModes = void_p.in_dll(CoreFoundation, "kCFRunLoopCommonModes") def _NSApp(): """Return the global NSApplication instance (NSApp)""" - return msg(C('NSApplication'), n('sharedApplication')) + return msg(C("NSApplication"), n("sharedApplication")) def _wake(NSApp): """Wake the Application""" - event = msg(C('NSEvent'), - n('otherEventWithType:location:modifierFlags:' - 'timestamp:windowNumber:context:subtype:data1:data2:'), - 15, # Type - 0, # location - 0, # flags - 0, # timestamp - 0, # window - None, # context - 0, # subtype - 0, # data1 - 0, # data2 + event = msg( + C("NSEvent"), + n( + "otherEventWithType:location:modifierFlags:" + "timestamp:windowNumber:context:subtype:data1:data2:" + ), + 15, # Type + 0, # location + 0, # flags + 0, # timestamp + 0, # window + None, # context + 0, # subtype + 0, # data1 + 0, # data2 ) - msg(NSApp, n('postEvent:atStart:'), void_p(event), True) + msg(NSApp, n("postEvent:atStart:"), void_p(event), True) _triggered = Event() @@ -108,8 +111,8 @@ def stop(timer=None, loop=None): NSApp = _NSApp() # if NSApp is not running, stop CFRunLoop directly, # otherwise stop and wake NSApp - if msg(NSApp, n('isRunning')): - msg(NSApp, n('stop:'), NSApp) + if msg(NSApp, n("isRunning")): + msg(NSApp, n("stop:"), NSApp) _wake(NSApp) else: CFRunLoopStop(CFRunLoopGetCurrent()) @@ -122,11 +125,11 @@ def stop(timer=None, loop=None): def _stop_after(delay): """Register callback to stop eventloop after a delay""" timer = CFRunLoopTimerCreate( - None, # allocator - CFAbsoluteTimeGetCurrent() + delay, # fireDate - 0, # interval - 0, # flags - 0, # order + None, # allocator + CFAbsoluteTimeGetCurrent() + delay, # fireDate + 0, # interval + 0, # flags + 0, # order _c_stop_callback, None, ) @@ -143,7 +146,7 @@ def mainloop(duration=1): _triggered.clear() NSApp = _NSApp() _stop_after(duration) - msg(NSApp, n('run')) + msg(NSApp, n("run")) if not _triggered.is_set(): # app closed without firing callback, # probably due to last window being closed. diff --git a/ipykernel/_version.py b/ipykernel/_version.py index 0dabd3201..85e2e8aa1 100644 --- a/ipykernel/_version.py +++ b/ipykernel/_version.py @@ -4,15 +4,15 @@ import re # Version string must appear intact for tbump versioning -__version__ = '6.10.0' +__version__ = "6.10.0" # Build up version_info tuple for backwards compatibility -pattern = r'(?P\d+).(?P\d+).(?P\d+)(?P.*)' +pattern = r"(?P\d+).(?P\d+).(?P\d+)(?P.*)" match = re.match(pattern, __version__) -parts = [int(match[part]) for part in ['major', 'minor', 'patch']] -if match['rest']: - parts.append(match['rest']) +parts = [int(match[part]) for part in ["major", "minor", "patch"]] +if match["rest"]: + parts.append(match["rest"]) version_info = tuple(parts) kernel_protocol_version_info = (5, 3) -kernel_protocol_version = '%s.%s' % kernel_protocol_version_info +kernel_protocol_version = "%s.%s" % kernel_protocol_version_info diff --git a/ipykernel/comm/__init__.py b/ipykernel/comm/__init__.py index 1faa164c0..cf9c1e983 100644 --- a/ipykernel/comm/__init__.py +++ b/ipykernel/comm/__init__.py @@ -1,2 +1,2 @@ -from .manager import * from .comm import * +from .manager import * diff --git a/ipykernel/comm/comm.py b/ipykernel/comm/comm.py index 97dff2bec..266dc048b 100644 --- a/ipykernel/comm/comm.py +++ b/ipykernel/comm/comm.py @@ -5,39 +5,44 @@ import uuid +from traitlets import Any, Bool, Bytes, Dict, Instance, Unicode, default from traitlets.config import LoggingConfigurable -from ipykernel.kernelbase import Kernel from ipykernel.jsonutil import json_clean -from traitlets import Instance, Unicode, Bytes, Bool, Dict, Any, default +from ipykernel.kernelbase import Kernel class Comm(LoggingConfigurable): """Class for communicating between a Frontend and a Kernel""" - kernel = Instance('ipykernel.kernelbase.Kernel', allow_none=True) - @default('kernel') + kernel = Instance("ipykernel.kernelbase.Kernel", allow_none=True) + + @default("kernel") def _default_kernel(self): if Kernel.initialized(): return Kernel.instance() comm_id = Unicode() - @default('comm_id') + @default("comm_id") def _default_comm_id(self): return uuid.uuid4().hex primary = Bool(True, help="Am I the primary or secondary Comm?") - target_name = Unicode('comm') - target_module = Unicode(None, allow_none=True, help="""requirejs module from - which to load comm target.""") + target_name = Unicode("comm") + target_module = Unicode( + None, + allow_none=True, + help="""requirejs module from + which to load comm target.""", + ) topic = Bytes() - @default('topic') + @default("topic") def _default_topic(self): - return ('comm-%s' % self.comm_id).encode('ascii') + return ("comm-%s" % self.comm_id).encode("ascii") _open_data = Dict(help="data dict, if any, to be included in comm_open") _close_data = Dict(help="data dict, if any, to be included in comm_close") @@ -47,9 +52,9 @@ def _default_topic(self): _closed = Bool(True) - def __init__(self, target_name='', data=None, metadata=None, buffers=None, **kwargs): + def __init__(self, target_name="", data=None, metadata=None, buffers=None, **kwargs): if target_name: - kwargs['target_name'] = target_name + kwargs["target_name"] = target_name super().__init__(**kwargs) if self.kernel: if self.primary: @@ -63,7 +68,9 @@ def _publish_msg(self, msg_type, data=None, metadata=None, buffers=None, **keys) data = {} if data is None else data metadata = {} if metadata is None else metadata content = json_clean(dict(data=data, comm_id=self.comm_id, **keys)) - self.kernel.session.send(self.kernel.iopub_socket, msg_type, + self.kernel.session.send( + self.kernel.iopub_socket, + msg_type, content, metadata=json_clean(metadata), parent=self.kernel.get_parent("shell"), @@ -81,18 +88,23 @@ def open(self, data=None, metadata=None, buffers=None): """Open the frontend-side version of this comm""" if data is None: data = self._open_data - comm_manager = getattr(self.kernel, 'comm_manager', None) + comm_manager = getattr(self.kernel, "comm_manager", None) if comm_manager is None: - raise RuntimeError("Comms cannot be opened without a kernel " - "and a comm_manager attached to that kernel.") + raise RuntimeError( + "Comms cannot be opened without a kernel " + "and a comm_manager attached to that kernel." + ) comm_manager.register_comm(self) try: - self._publish_msg('comm_open', - data=data, metadata=metadata, buffers=buffers, - target_name=self.target_name, - target_module=self.target_module, - ) + self._publish_msg( + "comm_open", + data=data, + metadata=metadata, + buffers=buffers, + target_name=self.target_name, + target_module=self.target_module, + ) self._closed = False except Exception: comm_manager.unregister_comm(self) @@ -110,8 +122,11 @@ def close(self, data=None, metadata=None, buffers=None, deleting=False): return if data is None: data = self._close_data - self._publish_msg('comm_close', - data=data, metadata=metadata, buffers=buffers, + self._publish_msg( + "comm_close", + data=data, + metadata=metadata, + buffers=buffers, ) if not deleting: # If deleting, the comm can't be registered @@ -119,8 +134,11 @@ def close(self, data=None, metadata=None, buffers=None, deleting=False): def send(self, data=None, metadata=None, buffers=None): """Send a message to the frontend-side version of this comm""" - self._publish_msg('comm_msg', - data=data, metadata=metadata, buffers=buffers, + self._publish_msg( + "comm_msg", + data=data, + metadata=metadata, + buffers=buffers, ) # registering callbacks @@ -157,10 +175,10 @@ def handle_msg(self, msg): if self._msg_callback: shell = self.kernel.shell if shell: - shell.events.trigger('pre_execute') + shell.events.trigger("pre_execute") self._msg_callback(msg) if shell: - shell.events.trigger('post_execute') + shell.events.trigger("post_execute") -__all__ = ['Comm'] +__all__ = ["Comm"] diff --git a/ipykernel/comm/manager.py b/ipykernel/comm/manager.py index 631b67b13..8dae16235 100644 --- a/ipykernel/comm/manager.py +++ b/ipykernel/comm/manager.py @@ -5,10 +5,9 @@ import logging +from traitlets import Dict, Instance from traitlets.config import LoggingConfigurable - from traitlets.utils.importstring import import_item -from traitlets import Instance, Dict from .comm import Comm @@ -16,7 +15,7 @@ class CommManager(LoggingConfigurable): """Manager for Comms in the Kernel""" - kernel = Instance('ipykernel.kernelbase.Kernel') + kernel = Instance("ipykernel.kernelbase.Kernel") comms = Dict() targets = Dict() @@ -72,13 +71,14 @@ def get_comm(self, comm_id): # Message handlers def comm_open(self, stream, ident, msg): """Handler for comm_open messages""" - content = msg['content'] - comm_id = content['comm_id'] - target_name = content['target_name'] + content = msg["content"] + comm_id = content["comm_id"] + target_name = content["target_name"] f = self.targets.get(target_name, None) - comm = Comm(comm_id=comm_id, - primary=False, - target_name=target_name, + comm = Comm( + comm_id=comm_id, + primary=False, + target_name=target_name, ) self.register_comm(comm) if f is None: @@ -94,13 +94,16 @@ def comm_open(self, stream, ident, msg): try: comm.close() except Exception: - self.log.error("""Could not close comm during `comm_open` failure - clean-up. The comm may not have been opened yet.""", exc_info=True) + self.log.error( + """Could not close comm during `comm_open` failure + clean-up. The comm may not have been opened yet.""", + exc_info=True, + ) def comm_msg(self, stream, ident, msg): """Handler for comm_msg messages""" - content = msg['content'] - comm_id = content['comm_id'] + content = msg["content"] + comm_id = content["comm_id"] comm = self.get_comm(comm_id) if comm is None: return @@ -108,12 +111,12 @@ def comm_msg(self, stream, ident, msg): try: comm.handle_msg(msg) except Exception: - self.log.error('Exception in comm_msg for %s', comm_id, exc_info=True) + self.log.error("Exception in comm_msg for %s", comm_id, exc_info=True) def comm_close(self, stream, ident, msg): """Handler for comm_close messages""" - content = msg['content'] - comm_id = content['comm_id'] + content = msg["content"] + comm_id = content["comm_id"] comm = self.get_comm(comm_id) if comm is None: return @@ -124,6 +127,7 @@ def comm_close(self, stream, ident, msg): try: comm.handle_close(msg) except Exception: - self.log.error('Exception in comm_close for %s', comm_id, exc_info=True) + self.log.error("Exception in comm_close for %s", comm_id, exc_info=True) + -__all__ = ['CommManager'] +__all__ = ["CommManager"] diff --git a/ipykernel/compiler.py b/ipykernel/compiler.py index 95770e7be..585261593 100644 --- a/ipykernel/compiler.py +++ b/ipykernel/compiler.py @@ -1,48 +1,54 @@ -from IPython.core.compilerop import CachingCompiler -import tempfile import os import sys +import tempfile + +from IPython.core.compilerop import CachingCompiler def murmur2_x86(data, seed): - m = 0x5bd1e995 + m = 0x5BD1E995 data = [chr(d) for d in str.encode(data, "utf8")] length = len(data) h = seed ^ length - rounded_end = (length & 0xfffffffc) + rounded_end = length & 0xFFFFFFFC for i in range(0, rounded_end, 4): - k = (ord(data[i]) & 0xff) | ((ord(data[i + 1]) & 0xff) << 8) | \ - ((ord(data[i + 2]) & 0xff) << 16) | (ord(data[i + 3]) << 24) - k = (k * m) & 0xffffffff + k = ( + (ord(data[i]) & 0xFF) + | ((ord(data[i + 1]) & 0xFF) << 8) + | ((ord(data[i + 2]) & 0xFF) << 16) + | (ord(data[i + 3]) << 24) + ) + k = (k * m) & 0xFFFFFFFF k ^= k >> 24 - k = (k * m) & 0xffffffff + k = (k * m) & 0xFFFFFFFF - h = (h * m) & 0xffffffff + h = (h * m) & 0xFFFFFFFF h ^= k val = length & 0x03 k = 0 if val == 3: - k = (ord(data[rounded_end + 2]) & 0xff) << 16 + k = (ord(data[rounded_end + 2]) & 0xFF) << 16 if val in [2, 3]: - k |= (ord(data[rounded_end + 1]) & 0xff) << 8 + k |= (ord(data[rounded_end + 1]) & 0xFF) << 8 if val in [1, 2, 3]: - k |= ord(data[rounded_end]) & 0xff + k |= ord(data[rounded_end]) & 0xFF h ^= k - h = (h * m) & 0xffffffff + h = (h * m) & 0xFFFFFFFF h ^= h >> 13 - h = (h * m) & 0xffffffff + h = (h * m) & 0xFFFFFFFF h ^= h >> 15 return h -convert_to_long_pathname = lambda filename:filename -if sys.platform == 'win32': +convert_to_long_pathname = lambda filename: filename + +if sys.platform == "win32": try: import ctypes - from ctypes.wintypes import MAX_PATH, LPCWSTR, LPWSTR, DWORD + from ctypes.wintypes import DWORD, LPCWSTR, LPWSTR, MAX_PATH _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW _GetLongPathName.argtypes = [LPCWSTR, LPWSTR, DWORD] @@ -62,14 +68,15 @@ def _convert_to_long_pathname(filename): else: convert_to_long_pathname = _convert_to_long_pathname + def get_tmp_directory(): tmp_dir = convert_to_long_pathname(tempfile.gettempdir()) pid = os.getpid() - return tmp_dir + os.sep + 'ipykernel_' + str(pid) + return tmp_dir + os.sep + "ipykernel_" + str(pid) def get_tmp_hash_seed(): - hash_seed = 0xc70f6907 + hash_seed = 0xC70F6907 return hash_seed @@ -77,12 +84,11 @@ def get_file_name(code): cell_name = os.environ.get("IPYKERNEL_CELL_NAME") if cell_name is None: name = murmur2_x86(code, get_tmp_hash_seed()) - cell_name = get_tmp_directory() + os.sep + str(name) + '.py' + cell_name = get_tmp_directory() + os.sep + str(name) + ".py" return cell_name class XCachingCompiler(CachingCompiler): - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.log = None diff --git a/ipykernel/connect.py b/ipykernel/connect.py index 73ff22b3f..b56b9b8ff 100644 --- a/ipykernel/connect.py +++ b/ipykernel/connect.py @@ -5,13 +5,12 @@ import json import sys -from subprocess import Popen, PIPE +from subprocess import PIPE, Popen import jupyter_client from jupyter_client import write_connection_file - def get_connection_file(app=None): """Return the path to the connection file of an app @@ -21,13 +20,15 @@ def get_connection_file(app=None): If unspecified, the currently running app will be used """ from traitlets.utils import filefind + if app is None: from ipykernel.kernelapp import IPKernelApp + if not IPKernelApp.initialized(): raise RuntimeError("app not specified, and not in a running Kernel") app = IPKernelApp.instance() - return filefind(app.connection_file, ['.', app.connection_dir]) + return filefind(app.connection_file, [".", app.connection_dir]) def _find_connection_file(connection_file): @@ -102,25 +103,25 @@ def connect_qtconsole(connection_file=None, argv=None): cf = _find_connection_file(connection_file) - cmd = ';'.join([ - "from IPython.qt.console import qtconsoleapp", - "qtconsoleapp.main()" - ]) + cmd = ";".join(["from IPython.qt.console import qtconsoleapp", "qtconsoleapp.main()"]) kwargs = {} # Launch the Qt console in a separate session & process group, so # interrupting the kernel doesn't kill it. - kwargs['start_new_session'] = True + kwargs["start_new_session"] = True - return Popen([sys.executable, '-c', cmd, '--existing', cf] + argv, - stdout=PIPE, stderr=PIPE, close_fds=(sys.platform != 'win32'), + return Popen( + [sys.executable, "-c", cmd, "--existing", cf] + argv, + stdout=PIPE, + stderr=PIPE, + close_fds=(sys.platform != "win32"), **kwargs ) __all__ = [ - 'write_connection_file', - 'get_connection_file', - 'get_connection_info', - 'connect_qtconsole', + "write_connection_file", + "get_connection_file", + "get_connection_info", + "connect_qtconsole", ] diff --git a/ipykernel/control.py b/ipykernel/control.py index cba8b0994..bb5f7b5f9 100644 --- a/ipykernel/control.py +++ b/ipykernel/control.py @@ -1,5 +1,7 @@ from threading import Thread + import zmq + if zmq.pyzmq_version_info() >= (17, 0): from tornado.ioloop import IOLoop else: @@ -8,7 +10,6 @@ class ControlThread(Thread): - def __init__(self, **kwargs): Thread.__init__(self, name="Control", **kwargs) self.io_loop = IOLoop(make_current=False) diff --git a/ipykernel/datapub.py b/ipykernel/datapub.py index fbc263d08..d8fe04093 100644 --- a/ipykernel/datapub.py +++ b/ipykernel/datapub.py @@ -2,29 +2,34 @@ """ import warnings -warnings.warn("ipykernel.datapub is deprecated. It has moved to ipyparallel.datapub", + +warnings.warn( + "ipykernel.datapub is deprecated. It has moved to ipyparallel.datapub", DeprecationWarning, - stacklevel=2 + stacklevel=2, ) # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. +from traitlets import Any, CBytes, Dict, Instance from traitlets.config import Configurable -from traitlets import Instance, Dict, CBytes, Any + from ipykernel.jsonutil import json_clean + try: # available since ipyparallel 5.0.0 from ipyparallel.serialize import serialize_object except ImportError: # Deprecated since ipykernel 4.3.0 from ipykernel.serialize import serialize_object + from jupyter_client.session import Session, extract_header class ZMQDataPublisher(Configurable): - topic = topic = CBytes(b'datapub') + topic = topic = CBytes(b"datapub") session = Instance(Session, allow_none=True) pub_socket = Any(allow_none=True) parent_header = Dict({}) @@ -42,12 +47,16 @@ def publish_data(self, data): The data to be published. Think of it as a namespace. """ session = self.session - buffers = serialize_object(data, + buffers = serialize_object( + data, buffer_threshold=session.buffer_threshold, item_threshold=session.item_threshold, ) content = json_clean(dict(keys=list(data.keys()))) - session.send(self.pub_socket, 'data_message', content=content, + session.send( + self.pub_socket, + "data_message", + content=content, parent=self.parent_header, buffers=buffers, ident=self.topic, @@ -62,10 +71,12 @@ def publish_data(data): data : dict The data to be published. Think of it as a namespace. """ - warnings.warn("ipykernel.datapub is deprecated. It has moved to ipyparallel.datapub", + warnings.warn( + "ipykernel.datapub is deprecated. It has moved to ipyparallel.datapub", DeprecationWarning, - stacklevel=2 + stacklevel=2, ) from ipykernel.zmqshell import ZMQInteractiveShell + ZMQInteractiveShell.instance().data_pub.publish_data(data) diff --git a/ipykernel/debugger.py b/ipykernel/debugger.py index f229c9402..62082a3cd 100644 --- a/ipykernel/debugger.py +++ b/ipykernel/debugger.py @@ -1,35 +1,38 @@ -import sys import os import re +import sys import threading import zmq -from zmq.utils import jsonapi - -from tornado.queues import Queue -from tornado.locks import Event - from IPython.core.getipython import get_ipython from IPython.core.inputtransformer2 import leading_empty_lines +from tornado.locks import Event +from tornado.queues import Queue +from zmq.utils import jsonapi try: from jupyter_client.jsonutil import json_default except ImportError: from jupyter_client.jsonutil import date_default as json_default -from .compiler import (get_file_name, get_tmp_directory, get_tmp_hash_seed) +from .compiler import get_file_name, get_tmp_directory, get_tmp_hash_seed try: # This import is required to have the next ones working... from debugpy.server import api # noqa - from _pydevd_bundle import pydevd_frame_utils - from _pydevd_bundle.pydevd_suspended_frames import SuspendedFramesManager, _FramesTracker + + from _pydevd_bundle import pydevd_frame_utils # isort: skip + from _pydevd_bundle.pydevd_suspended_frames import ( # isort: skip + SuspendedFramesManager, + _FramesTracker, + ) + _is_debugpy_available = True except ImportError: _is_debugpy_available = False # Required for backwards compatiblity -ROUTING_ID = getattr(zmq, 'ROUTING_ID', None) or zmq.IDENTITY +ROUTING_ID = getattr(zmq, "ROUTING_ID", None) or zmq.IDENTITY class _FakeCode: @@ -49,6 +52,7 @@ def __init__(self, f_code, f_globals, f_locals): class _DummyPyDB: def __init__(self): from _pydevd_bundle.pydevd_api import PyDevdAPI + self.variable_presentation = PyDevdAPI.VariablePresentation() @@ -61,13 +65,13 @@ def __init__(self): def track(self): var = get_ipython().user_ns - self.frame = _FakeFrame(_FakeCode('', get_file_name('sys._getframe()')), var, var) - self.tracker.track('thread1', pydevd_frame_utils.create_frames_list_from_frame(self.frame)) + self.frame = _FakeFrame(_FakeCode("", get_file_name("sys._getframe()")), var, var) + self.tracker.track("thread1", pydevd_frame_utils.create_frames_list_from_frame(self.frame)) def untrack_all(self): self.tracker.untrack_all() - def get_children_variables(self, variable_ref = None): + def get_children_variables(self, variable_ref=None): var_ref = variable_ref if not var_ref: var_ref = id(self.frame) @@ -77,13 +81,13 @@ def get_children_variables(self, variable_ref = None): class DebugpyMessageQueue: - HEADER = 'Content-Length: ' + HEADER = "Content-Length: " HEADER_LENGTH = 16 - SEPARATOR = '\r\n\r\n' + SEPARATOR = "\r\n\r\n" SEPARATOR_LENGTH = 4 def __init__(self, event_callback, log): - self.tcp_buffer = '' + self.tcp_buffer = "" self._reset_tcp_pos() self.event_callback = event_callback self.message_queue = Queue() @@ -96,21 +100,21 @@ def _reset_tcp_pos(self): self.message_pos = -1 def _put_message(self, raw_msg): - self.log.debug('QUEUE - _put_message:') + self.log.debug("QUEUE - _put_message:") msg = jsonapi.loads(raw_msg) - if msg['type'] == 'event': - self.log.debug('QUEUE - received event:') + if msg["type"] == "event": + self.log.debug("QUEUE - received event:") self.log.debug(msg) self.event_callback(msg) else: - self.log.debug('QUEUE - put message:') + self.log.debug("QUEUE - put message:") self.log.debug(msg) self.message_queue.put_nowait(msg) def put_tcp_frame(self, frame): self.tcp_buffer += frame - self.log.debug('QUEUE - received frame') + self.log.debug("QUEUE - received frame") while True: # Finds header if self.header_pos == -1: @@ -118,37 +122,39 @@ def put_tcp_frame(self, frame): if self.header_pos == -1: return - self.log.debug('QUEUE - found header at pos %i', self.header_pos) + self.log.debug("QUEUE - found header at pos %i", self.header_pos) - #Finds separator + # Finds separator if self.separator_pos == -1: hint = self.header_pos + DebugpyMessageQueue.HEADER_LENGTH self.separator_pos = self.tcp_buffer.find(DebugpyMessageQueue.SEPARATOR, hint) if self.separator_pos == -1: return - self.log.debug('QUEUE - found separator at pos %i', self.separator_pos) + self.log.debug("QUEUE - found separator at pos %i", self.separator_pos) if self.message_pos == -1: size_pos = self.header_pos + DebugpyMessageQueue.HEADER_LENGTH self.message_pos = self.separator_pos + DebugpyMessageQueue.SEPARATOR_LENGTH - self.message_size = int(self.tcp_buffer[size_pos:self.separator_pos]) + self.message_size = int(self.tcp_buffer[size_pos : self.separator_pos]) - self.log.debug('QUEUE - found message at pos %i', self.message_pos) - self.log.debug('QUEUE - message size is %i', self.message_size) + self.log.debug("QUEUE - found message at pos %i", self.message_pos) + self.log.debug("QUEUE - message size is %i", self.message_size) if len(self.tcp_buffer) - self.message_pos < self.message_size: return - self._put_message(self.tcp_buffer[self.message_pos:self.message_pos + self.message_size]) + self._put_message( + self.tcp_buffer[self.message_pos : self.message_pos + self.message_size] + ) if len(self.tcp_buffer) - self.message_pos == self.message_size: - self.log.debug('QUEUE - resetting tcp_buffer') - self.tcp_buffer = '' + self.log.debug("QUEUE - resetting tcp_buffer") + self.tcp_buffer = "" self._reset_tcp_pos() return else: - self.tcp_buffer = self.tcp_buffer[self.message_pos + self.message_size:] - self.log.debug('QUEUE - slicing tcp_buffer: %s', self.tcp_buffer) + self.tcp_buffer = self.tcp_buffer[self.message_pos + self.message_size :] + self.log.debug("QUEUE - slicing tcp_buffer: %s", self.tcp_buffer) self._reset_tcp_pos() async def get_message(self): @@ -156,13 +162,12 @@ async def get_message(self): class DebugpyClient: - def __init__(self, log, debugpy_stream, event_callback): self.log = log self.debugpy_stream = debugpy_stream self.event_callback = event_callback self.message_queue = DebugpyMessageQueue(self._forward_event, self.log) - self.debugpy_host = '127.0.0.1' + self.debugpy_host = "127.0.0.1" self.debugpy_port = -1 self.routing_id = None self.wait_for_attach = True @@ -171,12 +176,12 @@ def __init__(self, log, debugpy_stream, event_callback): def _get_endpoint(self): host, port = self.get_host_port() - return 'tcp://' + host + ':' + str(port) + return "tcp://" + host + ":" + str(port) def _forward_event(self, msg): - if msg['event'] == 'initialized': + if msg["event"] == "initialized": self.init_event.set() - self.init_event_seq = msg['seq'] + self.init_event_seq = msg["seq"] self.event_callback(msg) def _send_request(self, msg): @@ -189,7 +194,9 @@ def _send_request(self, msg): allow_nan=False, ) content_length = str(len(content)) - buf = (DebugpyMessageQueue.HEADER + content_length + DebugpyMessageQueue.SEPARATOR).encode('ascii') + buf = (DebugpyMessageQueue.HEADER + content_length + DebugpyMessageQueue.SEPARATOR).encode( + "ascii" + ) buf += content self.log.debug("DEBUGPYCLIENT:") self.log.debug(self.routing_id) @@ -208,9 +215,9 @@ async def _handle_init_sequence(self): # 2] Sends configurationDone request configurationDone = { - 'type': 'request', - 'seq': int(self.init_event_seq) + 1, - 'command': 'configurationDone' + "type": "request", + "seq": int(self.init_event_seq) + 1, + "command": "configurationDone", } self._send_request(configurationDone) @@ -224,11 +231,11 @@ async def _handle_init_sequence(self): def get_host_port(self): if self.debugpy_port == -1: socket = self.debugpy_stream.socket - socket.bind_to_random_port('tcp://' + self.debugpy_host) - self.endpoint = socket.getsockopt(zmq.LAST_ENDPOINT).decode('utf-8') + socket.bind_to_random_port("tcp://" + self.debugpy_host) + self.endpoint = socket.getsockopt(zmq.LAST_ENDPOINT).decode("utf-8") socket.unbind(self.endpoint) - index = self.endpoint.rfind(':') - self.debugpy_port = self.endpoint[index+1:] + index = self.endpoint.rfind(":") + self.debugpy_port = self.endpoint[index + 1 :] return self.debugpy_host, self.debugpy_port def connect_tcp_socket(self): @@ -247,13 +254,13 @@ def receive_dap_frame(self, frame): async def send_dap_request(self, msg): self._send_request(msg) - if self.wait_for_attach and msg['command'] == 'attach': + if self.wait_for_attach and msg["command"] == "attach": rep = await self._handle_init_sequence() self.wait_for_attach = False return rep else: rep = await self._wait_for_response() - self.log.debug('DEBUGPYCLIENT - returning:') + self.log.debug("DEBUGPYCLIENT - returning:") self.log.debug(rep) return rep @@ -262,19 +269,21 @@ class Debugger: # Requests that requires that the debugger has started started_debug_msg_types = [ - 'dumpCell', 'setBreakpoints', - 'source', 'stackTrace', - 'variables', 'attach', - 'configurationDone' + "dumpCell", + "setBreakpoints", + "source", + "stackTrace", + "variables", + "attach", + "configurationDone", ] # Requests that can be handled even if the debugger is not running - static_debug_msg_types = [ - 'debugInfo', 'inspectVariables', - 'richInspectVariables', 'modules' - ] + static_debug_msg_types = ["debugInfo", "inspectVariables", "richInspectVariables", "modules"] - def __init__(self, log, debugpy_stream, event_callback, shell_socket, session, just_my_code = True): + def __init__( + self, log, debugpy_stream, event_callback, shell_socket, session, just_my_code=True + ): self.log = log self.debugpy_client = DebugpyClient(log, debugpy_stream, self._handle_event) self.shell_socket = shell_socket @@ -298,26 +307,26 @@ def __init__(self, log, debugpy_stream, event_callback, shell_socket, session, j self.debugpy_initialized = False self._removed_cleanup = {} - self.debugpy_host = '127.0.0.1' + self.debugpy_host = "127.0.0.1" self.debugpy_port = 0 self.endpoint = None self.variable_explorer = VariableExplorer() def _handle_event(self, msg): - if msg['event'] == 'stopped': - if msg['body']['allThreadsStopped']: + if msg["event"] == "stopped": + if msg["body"]["allThreadsStopped"]: self.stopped_queue.put_nowait(msg) # Do not forward the event now, will be done in the handle_stopped_event return else: - self.stopped_threads.add(msg['body']['threadId']) + self.stopped_threads.add(msg["body"]["threadId"]) self.event_callback(msg) - elif msg['event'] == 'continued': - if msg['body']['allThreadsContinued']: + elif msg["event"] == "continued": + if msg["body"]["allThreadsContinued"]: self.stopped_threads = set() else: - self.stopped_threads.remove(msg['body']['threadId']) + self.stopped_threads.remove(msg["body"]["threadId"]) self.event_callback(msg) else: self.event_callback(msg) @@ -326,43 +335,32 @@ async def _forward_message(self, msg): return await self.debugpy_client.send_dap_request(msg) def _build_variables_response(self, request, variables): - var_list = [var for var in variables if self.accept_variable(var['name'])] + var_list = [var for var in variables if self.accept_variable(var["name"])] reply = { - 'seq': request['seq'], - 'type': 'response', - 'request_seq': request['seq'], - 'success': True, - 'command': request['command'], - 'body': { - 'variables': var_list - } + "seq": request["seq"], + "type": "response", + "request_seq": request["seq"], + "success": True, + "command": request["command"], + "body": {"variables": var_list}, } return reply def _accept_stopped_thread(self, thread_name): # TODO: identify Thread-2, Thread-3 and Thread-4. These are NOT # Control, IOPub or Heartbeat threads - forbid_list = [ - 'IPythonHistorySavingThread', - 'Thread-2', - 'Thread-3', - 'Thread-4' - ] + forbid_list = ["IPythonHistorySavingThread", "Thread-2", "Thread-3", "Thread-4"] return thread_name not in forbid_list async def handle_stopped_event(self): # Wait for a stopped event message in the stopped queue # This message is used for triggering the 'threads' request event = await self.stopped_queue.get() - req = { - 'seq': event['seq'] + 1, - 'type': 'request', - 'command': 'threads' - } + req = {"seq": event["seq"] + 1, "type": "request", "command": "threads"} rep = await self._forward_message(req) - for t in rep['body']['threads']: - if self._accept_stopped_thread(t['name']): - self.stopped_threads.add(t['id']) + for t in rep["body"]["threads"]: + if self._accept_stopped_thread(t["name"]): + self.stopped_threads.add(t["id"]) self.event_callback(event) @property @@ -375,17 +373,19 @@ def start(self): if not os.path.exists(tmp_dir): os.makedirs(tmp_dir) host, port = self.debugpy_client.get_host_port() - code = 'import debugpy;' - code += 'debugpy.listen(("' + host + '",' + port + '))' - content = { - 'code': code, - 'silent': True - } - self.session.send(self.shell_socket, 'execute_request', content, - None, (self.shell_socket.getsockopt(ROUTING_ID))) + code = "import debugpy;" + code += 'debugpy.listen(("' + host + '",' + port + "))" + content = {"code": code, "silent": True} + self.session.send( + self.shell_socket, + "execute_request", + content, + None, + (self.shell_socket.getsockopt(ROUTING_ID)), + ) ident, msg = self.session.recv(self.shell_socket, mode=0) - self.debugpy_initialized = msg['content']['status'] == 'ok' + self.debugpy_initialized = msg["content"]["status"] == "ok" # Don't remove leading empty lines when debugging so the breakpoints are correctly positioned cleanup_transforms = get_ipython().input_transformer_manager.cleanup_transforms @@ -406,20 +406,18 @@ def stop(self): cleanup_transforms.insert(index, func) async def dumpCell(self, message): - code = message['arguments']['code'] + code = message["arguments"]["code"] file_name = get_file_name(code) - with open(file_name, 'w', encoding='utf-8') as f: + with open(file_name, "w", encoding="utf-8") as f: f.write(code) reply = { - 'type': 'response', - 'request_seq': message['seq'], - 'success': True, - 'command': message['command'], - 'body': { - 'sourcePath': file_name - } + "type": "response", + "request_seq": message["seq"], + "success": True, + "command": message["command"], + "body": {"sourcePath": file_name}, } return reply @@ -429,22 +427,16 @@ async def setBreakpoints(self, message): return await self._forward_message(message) async def source(self, message): - reply = { - 'type': 'response', - 'request_seq': message['seq'], - 'command': message['command'] - } + reply = {"type": "response", "request_seq": message["seq"], "command": message["command"]} source_path = message["arguments"]["source"]["path"] if os.path.isfile(source_path): - with open(source_path, encoding='utf-8') as f: - reply['success'] = True - reply['body'] = { - 'content': f.read() - } + with open(source_path, encoding="utf-8") as f: + reply["success"] = True + reply["body"] = {"content": f.read()} else: - reply['success'] = False - reply['message'] = 'source unavailable' - reply['body'] = {} + reply["success"] = False + reply["message"] = "source unavailable" + reply["body"] = {} return reply @@ -462,105 +454,98 @@ async def stackTrace(self, message): try: sf_list = reply["body"]["stackFrames"] module_idx = len(sf_list) - next( - i - for i, v in enumerate(reversed(sf_list), 1) - if v["name"] == "" and i != 1 + i for i, v in enumerate(reversed(sf_list), 1) if v["name"] == "" and i != 1 ) - reply["body"]["stackFrames"] = reply["body"]["stackFrames"][ - : module_idx + 1 - ] + reply["body"]["stackFrames"] = reply["body"]["stackFrames"][: module_idx + 1] except StopIteration: pass return reply def accept_variable(self, variable_name): forbid_list = [ - '__name__', - '__doc__', - '__package__', - '__loader__', - '__spec__', - '__annotations__', - '__builtins__', - '__builtin__', - '__display__', - 'get_ipython', - 'debugpy', - 'exit', - 'quit', - 'In', - 'Out', - '_oh', - '_dh', - '_', - '__', - '___' + "__name__", + "__doc__", + "__package__", + "__loader__", + "__spec__", + "__annotations__", + "__builtins__", + "__builtin__", + "__display__", + "get_ipython", + "debugpy", + "exit", + "quit", + "In", + "Out", + "_oh", + "_dh", + "_", + "__", + "___", ] cond = variable_name not in forbid_list - cond = cond and not bool(re.search(r'^_\d', variable_name)) - cond = cond and variable_name[0:2] != '_i' + cond = cond and not bool(re.search(r"^_\d", variable_name)) + cond = cond and variable_name[0:2] != "_i" return cond async def variables(self, message): reply = {} if not self.stopped_threads: - variables = self.variable_explorer.get_children_variables(message['arguments']['variablesReference']) + variables = self.variable_explorer.get_children_variables( + message["arguments"]["variablesReference"] + ) return self._build_variables_response(message, variables) else: reply = await self._forward_message(message) # TODO : check start and count arguments work as expected in debugpy - reply['body']['variables'] = \ - [var for var in reply['body']['variables'] if self.accept_variable(var['name'])] + reply["body"]["variables"] = [ + var for var in reply["body"]["variables"] if self.accept_variable(var["name"]) + ] return reply async def attach(self, message): host, port = self.debugpy_client.get_host_port() - message['arguments']['connect'] = { - 'host': host, - 'port': port - } - message['arguments']['logToFile'] = True + message["arguments"]["connect"] = {"host": host, "port": port} + message["arguments"]["logToFile"] = True # Experimental option to break in non-user code. # The ipykernel source is in the call stack, so the user # has to manipulate the step-over and step-into in a wize way. # Set debugOptions for breakpoints in python standard library source. if not self.just_my_code: - message['arguments']['debugOptions'] = [ 'DebugStdLib' ] + message["arguments"]["debugOptions"] = ["DebugStdLib"] return await self._forward_message(message) async def configurationDone(self, message): reply = { - 'seq': message['seq'], - 'type': 'response', - 'request_seq': message['seq'], - 'success': True, - 'command': message['command'] + "seq": message["seq"], + "type": "response", + "request_seq": message["seq"], + "success": True, + "command": message["command"], } return reply async def debugInfo(self, message): breakpoint_list = [] for key, value in self.breakpoint_list.items(): - breakpoint_list.append({ - 'source': key, - 'breakpoints': value - }) + breakpoint_list.append({"source": key, "breakpoints": value}) reply = { - 'type': 'response', - 'request_seq': message['seq'], - 'success': True, - 'command': message['command'], - 'body': { - 'isStarted': self.is_started, - 'hashMethod': 'Murmur2', - 'hashSeed': get_tmp_hash_seed(), - 'tmpFilePrefix': get_tmp_directory() + os.sep, - 'tmpFileSuffix': '.py', - 'breakpoints': breakpoint_list, - 'stoppedThreads': list(self.stopped_threads), - 'richRendering': True, - 'exceptionPaths': ['Python Exceptions'] - } + "type": "response", + "request_seq": message["seq"], + "success": True, + "command": message["command"], + "body": { + "isStarted": self.is_started, + "hashMethod": "Murmur2", + "hashSeed": get_tmp_hash_seed(), + "tmpFilePrefix": get_tmp_directory() + os.sep, + "tmpFileSuffix": ".py", + "breakpoints": breakpoint_list, + "stoppedThreads": list(self.stopped_threads), + "richRendering": True, + "exceptionPaths": ["Python Exceptions"], + }, } return reply @@ -627,56 +612,52 @@ async def richInspectVariables(self, message): async def modules(self, message): modules = list(sys.modules.values()) - startModule = message.get('startModule', 0) - moduleCount = message.get('moduleCount', len(modules)) + startModule = message.get("startModule", 0) + moduleCount = message.get("moduleCount", len(modules)) mods = [] for i in range(startModule, moduleCount): module = modules[i] - filename = getattr(getattr(module, '__spec__', None), 'origin', None) - if filename and filename.endswith('.py'): - mods.append({ - 'id': i, - 'name': module.__name__, - 'path': filename - }) - - reply = {'body': {'modules': mods, 'totalModules': len(modules)}} + filename = getattr(getattr(module, "__spec__", None), "origin", None) + if filename and filename.endswith(".py"): + mods.append({"id": i, "name": module.__name__, "path": filename}) + + reply = {"body": {"modules": mods, "totalModules": len(modules)}} return reply async def process_request(self, message): reply = {} - if message['command'] == 'initialize': + if message["command"] == "initialize": if self.is_started: - self.log.info('The debugger has already started') + self.log.info("The debugger has already started") else: self.is_started = self.start() if self.is_started: - self.log.info('The debugger has started') + self.log.info("The debugger has started") else: reply = { - 'command': 'initialize', - 'request_seq': message['seq'], - 'seq': 3, - 'success': False, - 'type': 'response' + "command": "initialize", + "request_seq": message["seq"], + "seq": 3, + "success": False, + "type": "response", } - handler = self.static_debug_handlers.get(message['command'], None) + handler = self.static_debug_handlers.get(message["command"], None) if handler is not None: reply = await handler(message) elif self.is_started: - handler = self.started_debug_handlers.get(message['command'], None) + handler = self.started_debug_handlers.get(message["command"], None) if handler is not None: reply = await handler(message) else: reply = await self._forward_message(message) - if message['command'] == 'disconnect': + if message["command"] == "disconnect": self.stop() self.breakpoint_list = {} self.stopped_threads = set() self.is_started = False - self.log.info('The debugger has stopped') + self.log.info("The debugger has stopped") return reply diff --git a/ipykernel/displayhook.py b/ipykernel/displayhook.py index 19ee151f4..2ba2c08c3 100644 --- a/ipykernel/displayhook.py +++ b/ipykernel/displayhook.py @@ -7,15 +7,17 @@ import sys from IPython.core.displayhook import DisplayHook +from jupyter_client.session import Session, extract_header +from traitlets import Any, Dict, Instance + from ipykernel.jsonutil import encode_images, json_clean -from traitlets import Instance, Dict, Any -from jupyter_client.session import extract_header, Session class ZMQDisplayHook: """A simple displayhook that publishes the object's repr over a ZeroMQ socket.""" - topic = b'execute_result' + + topic = b"execute_result" def __init__(self, session, pub_socket): self.session = session @@ -33,11 +35,14 @@ def __call__(self, obj): builtins._ = obj sys.stdout.flush() sys.stderr.flush() - contents = {'execution_count': self.get_execution_count(), - 'data': {'text/plain': repr(obj)}, - 'metadata': {}} - self.session.send(self.pub_socket, 'execute_result', contents, - parent=self.parent_header, ident=self.topic) + contents = { + "execution_count": self.get_execution_count(), + "data": {"text/plain": repr(obj)}, + "metadata": {}, + } + self.session.send( + self.pub_socket, "execute_result", contents, parent=self.parent_header, ident=self.topic + ) def set_parent(self, parent): self.parent_header = extract_header(parent) @@ -47,7 +52,8 @@ class ZMQShellDisplayHook(DisplayHook): """A displayhook subclass that publishes data using ZeroMQ. This is intended to work with an InteractiveShell instance. It sends a dict of different representations of the object.""" - topic=None + + topic = None session = Instance(Session, allow_none=True) pub_socket = Any(allow_none=True) @@ -58,23 +64,27 @@ def set_parent(self, parent): self.parent_header = extract_header(parent) def start_displayhook(self): - self.msg = self.session.msg('execute_result', { - 'data': {}, - 'metadata': {}, - }, parent=self.parent_header) + self.msg = self.session.msg( + "execute_result", + { + "data": {}, + "metadata": {}, + }, + parent=self.parent_header, + ) def write_output_prompt(self): """Write the output prompt.""" - self.msg['content']['execution_count'] = self.prompt_count + self.msg["content"]["execution_count"] = self.prompt_count def write_format_data(self, format_dict, md_dict=None): - self.msg['content']['data'] = json_clean(encode_images(format_dict)) - self.msg['content']['metadata'] = md_dict + self.msg["content"]["data"] = json_clean(encode_images(format_dict)) + self.msg["content"]["metadata"] = md_dict def finish_displayhook(self): """Finish up all displayhook activities.""" sys.stdout.flush() sys.stderr.flush() - if self.msg['content']['data']: + if self.msg["content"]["data"]: self.session.send(self.pub_socket, self.msg, ident=self.topic) self.msg = None diff --git a/ipykernel/embed.py b/ipykernel/embed.py index 4334eb182..53c11119b 100644 --- a/ipykernel/embed.py +++ b/ipykernel/embed.py @@ -1,8 +1,8 @@ """Simple function for embedding an IPython kernel """ -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Imports -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- import sys @@ -10,9 +10,10 @@ from .kernelapp import IPKernelApp -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Code -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + def embed_kernel(module=None, local_ns=None, **kwargs): """Embed and start an IPython kernel in a given scope. diff --git a/ipykernel/eventloops.py b/ipykernel/eventloops.py index 4911c87ee..eae3a9e59 100644 --- a/ipykernel/eventloops.py +++ b/ipykernel/eventloops.py @@ -3,14 +3,13 @@ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. -from functools import partial import os -import sys import platform +import sys +from distutils.version import LooseVersion as V +from functools import partial import zmq - -from distutils.version import LooseVersion as V from traitlets.config.application import Application @@ -19,7 +18,7 @@ def _use_appnope(): Checks if we are on OS X 10.9 or greater. """ - return sys.platform == 'darwin' and V(platform.mac_ver()[0]) >= V('10.9') + return sys.platform == "darwin" and V(platform.mac_ver()[0]) >= V("10.9") def _notify_stream_qt(kernel, stream): @@ -51,16 +50,18 @@ def process_stream_events(): timer.timeout.connect(process_stream_events) timer.start(0) + # mapping of keys to loop functions loop_map = { - 'inline': None, - 'nbagg': None, - 'notebook': None, - 'ipympl': None, - 'widget': None, + "inline": None, + "nbagg": None, + "notebook": None, + "ipympl": None, + "widget": None, None: None, } + def register_integration(*toolkitnames): """Decorator to register an event loop to integrate with the IPython kernel @@ -74,6 +75,7 @@ def register_integration(*toolkitnames): :mod:`ipykernel.eventloops` provides and registers such functions for a few common event loops. """ + def decorator(func): for name in toolkitnames: loop_map[name] = func @@ -106,12 +108,12 @@ def _loop_qt(app): app._in_event_loop = False -@register_integration('qt4') +@register_integration("qt4") def loop_qt4(kernel): """Start a kernel with PyQt4 event loop integration.""" - from IPython.lib.guisupport import get_app_qt4 from IPython.external.qt_for_kernel import QtGui + from IPython.lib.guisupport import get_app_qt4 kernel.app = get_app_qt4([" "]) if isinstance(kernel.app, QtGui.QApplication): @@ -121,19 +123,21 @@ def loop_qt4(kernel): _loop_qt(kernel.app) -@register_integration('qt', 'qt5') +@register_integration("qt", "qt5") def loop_qt5(kernel): """Start a kernel with PyQt5 event loop integration.""" - if os.environ.get('QT_API', None) is None: + if os.environ.get("QT_API", None) is None: try: import PyQt5 - os.environ['QT_API'] = 'pyqt5' + + os.environ["QT_API"] = "pyqt5" except ImportError: try: import PySide2 - os.environ['QT_API'] = 'pyside2' + + os.environ["QT_API"] = "pyside2" except ImportError: - os.environ['QT_API'] = 'pyqt5' + os.environ["QT_API"] = "pyqt5" return loop_qt4(kernel) @@ -156,7 +160,7 @@ def _loop_wx(app): app._in_event_loop = False -@register_integration('wx') +@register_integration("wx") def loop_wx(kernel): """Start a kernel with wx event loop support.""" @@ -195,16 +199,14 @@ def OnInit(self): # The redirect=False here makes sure that wx doesn't replace # sys.stdout/stderr with its own classes. - if not ( - getattr(kernel, 'app', None) - and isinstance(kernel.app, wx.App) - ): + if not (getattr(kernel, "app", None) and isinstance(kernel.app, wx.App)): kernel.app = IPWxApp(redirect=False) # The import of wx on Linux sets the handler for signal.SIGINT # to 0. This is a bug in wx or gtk. We fix by just setting it # back to the Python default. import signal + if not callable(signal.getsignal(signal.SIGINT)): signal.signal(signal.SIGINT, signal.default_int_handler) @@ -214,20 +216,21 @@ def OnInit(self): @loop_wx.exit def loop_wx_exit(kernel): import wx + wx.Exit() -@register_integration('tk') +@register_integration("tk") def loop_tk(kernel): """Start a kernel with the Tk event loop.""" - from tkinter import Tk, READABLE + from tkinter import READABLE, Tk app = Tk() # Capability detection: # per https://docs.python.org/3/library/tkinter.html#file-handlers # file handlers are not available on Windows - if hasattr(app, 'createfilehandler'): + if hasattr(app, "createfilehandler"): # A basic wrapper for structural similarity with the Windows version class BasicAppWrapper: def __init__(self, app): @@ -254,7 +257,9 @@ def process_stream_events(stream, *a, **kw): else: import asyncio + import nest_asyncio + nest_asyncio.apply() doi = kernel.do_one_iteration @@ -291,7 +296,7 @@ def loop_tk_exit(kernel): pass -@register_integration('gtk') +@register_integration("gtk") def loop_gtk(kernel): """Start the kernel, coordinating with the GTK event loop""" from .gui.gtkembed import GTKEmbed @@ -306,7 +311,7 @@ def loop_gtk_exit(kernel): kernel._gtk.stop() -@register_integration('gtk3') +@register_integration("gtk3") def loop_gtk3(kernel): """Start the kernel, coordinating with the GTK event loop""" from .gui.gtk3embed import GTKEmbed @@ -321,7 +326,7 @@ def loop_gtk3_exit(kernel): kernel._gtk.stop() -@register_integration('osx') +@register_integration("osx") def loop_cocoa(kernel): """Start the kernel, coordinating with the Cocoa CFRunLoop event loop via the matplotlib MacOSX backend. @@ -329,6 +334,7 @@ def loop_cocoa(kernel): from ._eventloop_macos import mainloop, stop real_excepthook = sys.excepthook + def handle_int(etype, value, tb): """don't let KeyboardInterrupts look like crashes""" # wake the eventloop when we get a signal @@ -362,13 +368,15 @@ def handle_int(etype, value, tb): @loop_cocoa.exit def loop_cocoa_exit(kernel): from ._eventloop_macos import stop + stop() -@register_integration('asyncio') +@register_integration("asyncio") def loop_asyncio(kernel): - '''Start a kernel with asyncio event loop support.''' + """Start a kernel with asyncio event loop support.""" import asyncio + loop = asyncio.get_event_loop() # loop is already running (e.g. tornado 5), nothing left to do if loop.is_running(): @@ -409,11 +417,12 @@ def process_stream_events(stream): def loop_asyncio_exit(kernel): """Exit hook for asyncio""" import asyncio + loop = asyncio.get_event_loop() @asyncio.coroutine def close_loop(): - if hasattr(loop, 'shutdown_asyncgens'): + if hasattr(loop, "shutdown_asyncgens"): yield from loop.shutdown_asyncgens() loop._should_close = True loop.stop() @@ -433,9 +442,10 @@ def enable_gui(gui, kernel=None): raise ValueError(e) if kernel is None: if Application.initialized(): - kernel = getattr(Application.instance(), 'kernel', None) + kernel = getattr(Application.instance(), "kernel", None) if kernel is None: - raise RuntimeError("You didn't specify a kernel," + raise RuntimeError( + "You didn't specify a kernel," " and no IPython Application with a kernel appears to be running." ) loop = loop_map[gui] diff --git a/ipykernel/gui/__init__.py b/ipykernel/gui/__init__.py index 1351f3c27..bbf3b9ffc 100644 --- a/ipykernel/gui/__init__.py +++ b/ipykernel/gui/__init__.py @@ -5,11 +5,11 @@ toolkits. """ -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Copyright (C) 2010-2011 The IPython Development Team. # # Distributed under the terms of the BSD License. # # The full license is in the file COPYING.txt, distributed as part of this # software. -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- diff --git a/ipykernel/gui/gtk3embed.py b/ipykernel/gui/gtk3embed.py index 4d6803877..198f8cacd 100644 --- a/ipykernel/gui/gtk3embed.py +++ b/ipykernel/gui/gtk3embed.py @@ -1,31 +1,33 @@ """GUI support for the IPython ZeroMQ kernel - GTK toolkit support. """ -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Copyright (C) 2010-2011 The IPython Development Team # # Distributed under the terms of the BSD License. The full license is in # the file COPYING.txt, distributed as part of this software. -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Imports -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # stdlib import sys # Third-party import gi -gi.require_version ('Gdk', '3.0') -gi.require_version ('Gtk', '3.0') + +gi.require_version("Gdk", "3.0") +gi.require_version("Gtk", "3.0") from gi.repository import GObject, Gtk -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Classes and functions -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + class GTKEmbed: - """A class to embed a kernel into the GTK main event loop. - """ + """A class to embed a kernel into the GTK main event loop.""" + def __init__(self, kernel): self.kernel = kernel # These two will later store the real gtk functions when we hijack them @@ -33,8 +35,7 @@ def __init__(self, kernel): self.gtk_main_quit = None def start(self): - """Starts the GTK main event loop and sets our kernel startup routine. - """ + """Starts the GTK main event loop and sets our kernel startup routine.""" # Register our function to initiate the kernel and start gtk GObject.idle_add(self._wire_kernel) Gtk.main() @@ -46,8 +47,7 @@ def _wire_kernel(self): returns False to ensure it doesn't get run again by GTK. """ self.gtk_main, self.gtk_main_quit = self._hijack_gtk() - GObject.timeout_add(int(1000*self.kernel._poll_interval), - self.iterate_kernel) + GObject.timeout_add(int(1000 * self.kernel._poll_interval), self.iterate_kernel) return False def iterate_kernel(self): @@ -80,8 +80,10 @@ def _hijack_gtk(self): - Gtk.main - Gtk.main_quit """ + def dummy(*args, **kw): pass + # save and trap main and main_quit from gtk orig_main, Gtk.main = Gtk.main, dummy orig_main_quit, Gtk.main_quit = Gtk.main_quit, dummy diff --git a/ipykernel/gui/gtkembed.py b/ipykernel/gui/gtkembed.py index d77224a3f..19522c441 100644 --- a/ipykernel/gui/gtkembed.py +++ b/ipykernel/gui/gtkembed.py @@ -1,15 +1,15 @@ """GUI support for the IPython ZeroMQ kernel - GTK toolkit support. """ -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Copyright (C) 2010-2011 The IPython Development Team # # Distributed under the terms of the BSD License. The full license is in # the file COPYING.txt, distributed as part of this software. -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Imports -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # stdlib import sys @@ -17,13 +17,14 @@ import gobject import gtk -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Classes and functions -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + class GTKEmbed: - """A class to embed a kernel into the GTK main event loop. - """ + """A class to embed a kernel into the GTK main event loop.""" + def __init__(self, kernel): self.kernel = kernel # These two will later store the real gtk functions when we hijack them @@ -31,8 +32,7 @@ def __init__(self, kernel): self.gtk_main_quit = None def start(self): - """Starts the GTK main event loop and sets our kernel startup routine. - """ + """Starts the GTK main event loop and sets our kernel startup routine.""" # Register our function to initiate the kernel and start gtk gobject.idle_add(self._wire_kernel) gtk.main() @@ -44,8 +44,7 @@ def _wire_kernel(self): returns False to ensure it doesn't get run again by GTK. """ self.gtk_main, self.gtk_main_quit = self._hijack_gtk() - gobject.timeout_add(int(1000*self.kernel._poll_interval), - self.iterate_kernel) + gobject.timeout_add(int(1000 * self.kernel._poll_interval), self.iterate_kernel) return False def iterate_kernel(self): @@ -78,8 +77,10 @@ def _hijack_gtk(self): - gtk.main - gtk.main_quit """ + def dummy(*args, **kw): pass + # save and trap main and main_quit from gtk orig_main, gtk.main = gtk.main, dummy orig_main_quit, gtk.main_quit = gtk.main_quit, dummy diff --git a/ipykernel/heartbeat.py b/ipykernel/heartbeat.py index 77c90b974..6f5ddbec3 100644 --- a/ipykernel/heartbeat.py +++ b/ipykernel/heartbeat.py @@ -1,16 +1,16 @@ """The client and server for a basic ping-pong style heartbeat. """ -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Copyright (C) 2008-2011 The IPython Development Team # # Distributed under the terms of the BSD License. The full license is in # the file COPYING, distributed as part of this software. -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Imports -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- import errno import os @@ -18,12 +18,11 @@ from threading import Thread import zmq - from jupyter_client.localinterfaces import localhost -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Code -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- class Heartbeat(Thread): @@ -31,7 +30,7 @@ class Heartbeat(Thread): def __init__(self, context, addr=None): if addr is None: - addr = ('tcp', localhost(), 0) + addr = ("tcp", localhost(), 0) Thread.__init__(self, name="Heartbeat") self.context = context self.transport, self.ip, self.port = addr @@ -45,13 +44,13 @@ def __init__(self, context, addr=None): self.name = "Heartbeat" def pick_port(self): - if self.transport == 'tcp': + if self.transport == "tcp": s = socket.socket() # '*' means all interfaces to 0MQ, which is '' to socket.socket - s.bind(('' if self.ip == '*' else self.ip, 0)) + s.bind(("" if self.ip == "*" else self.ip, 0)) self.port = s.getsockname()[1] s.close() - elif self.transport == 'ipc': + elif self.transport == "ipc": self.port = 1 while os.path.exists("%s-%s" % (self.ip, self.port)): self.port = self.port + 1 @@ -60,8 +59,8 @@ def pick_port(self): return self.port def _try_bind_socket(self): - c = ':' if self.transport == 'tcp' else '-' - return self.socket.bind('%s://%s' % (self.transport, self.ip) + c + str(self.port)) + c = ":" if self.transport == "tcp" else "-" + return self.socket.bind("%s://%s" % (self.transport, self.ip) + c + str(self.port)) def _bind_socket(self): try: diff --git a/ipykernel/inprocess/__init__.py b/ipykernel/inprocess/__init__.py index 8da75615e..b10698910 100644 --- a/ipykernel/inprocess/__init__.py +++ b/ipykernel/inprocess/__init__.py @@ -1,8 +1,4 @@ -from .channels import ( - InProcessChannel, - InProcessHBChannel, -) - +from .blocking import BlockingInProcessKernelClient +from .channels import InProcessChannel, InProcessHBChannel from .client import InProcessKernelClient from .manager import InProcessKernelManager -from .blocking import BlockingInProcessKernelClient diff --git a/ipykernel/inprocess/blocking.py b/ipykernel/inprocess/blocking.py index a36b95a29..269eb4158 100644 --- a/ipykernel/inprocess/blocking.py +++ b/ipykernel/inprocess/blocking.py @@ -2,26 +2,25 @@ Useful for test suites and blocking terminal interfaces. """ -#----------------------------------------------------------------------------- +import sys + +# ----------------------------------------------------------------------------- # Copyright (C) 2012 The IPython Development Team # # Distributed under the terms of the BSD License. The full license is in # the file COPYING.txt, distributed as part of this software. -#----------------------------------------------------------------------------- -from queue import Queue, Empty -import sys +# ----------------------------------------------------------------------------- +from queue import Empty, Queue # IPython imports from traitlets import Type # Local imports -from .channels import ( - InProcessChannel, -) +from .channels import InProcessChannel from .client import InProcessKernelClient -class BlockingInProcessChannel(InProcessChannel): +class BlockingInProcessChannel(InProcessChannel): def __init__(self, *args, **kwds): super().__init__(*args, **kwds) self._in_queue = Queue() @@ -30,7 +29,7 @@ def call_handlers(self, msg): self._in_queue.put(msg) def get_msg(self, block=True, timeout=None): - """ Gets a message if there is one that is ready. """ + """Gets a message if there is one that is ready.""" if timeout is None: # Queue.get(timeout=None) has stupid uninteruptible # behavior, so wait for a week instead @@ -38,7 +37,7 @@ def get_msg(self, block=True, timeout=None): return self._in_queue.get(block, timeout) def get_msgs(self): - """ Get all messages that are currently ready. """ + """Get all messages that are currently ready.""" msgs = [] while True: try: @@ -48,24 +47,25 @@ def get_msgs(self): return msgs def msg_ready(self): - """ Is there a message that has been received? """ + """Is there a message that has been received?""" return not self._in_queue.empty() class BlockingInProcessStdInChannel(BlockingInProcessChannel): def call_handlers(self, msg): - """ Overridden for the in-process channel. + """Overridden for the in-process channel. This methods simply calls raw_input directly. """ - msg_type = msg['header']['msg_type'] - if msg_type == 'input_request': + msg_type = msg["header"]["msg_type"] + if msg_type == "input_request": _raw_input = self.client.kernel._sys_raw_input - prompt = msg['content']['prompt'] - print(prompt, end='', file=sys.__stdout__) + prompt = msg["content"]["prompt"] + print(prompt, end="", file=sys.__stdout__) sys.__stdout__.flush() self.client.input(_raw_input()) + class BlockingInProcessKernelClient(InProcessKernelClient): # The classes to use for the various channels. @@ -82,7 +82,7 @@ def wait_for_ready(self): except Empty: pass else: - if msg['msg_type'] == 'kernel_info_reply': + if msg["msg_type"] == "kernel_info_reply": # Checking that IOPub is connected. If it is not connected, start over. try: self.iopub_channel.get_msg(block=True, timeout=0.2) @@ -96,6 +96,6 @@ def wait_for_ready(self): while True: try: msg = self.iopub_channel.get_msg(block=True, timeout=0.2) - print(msg['msg_type']) + print(msg["msg_type"]) except Empty: break diff --git a/ipykernel/inprocess/channels.py b/ipykernel/inprocess/channels.py index 14fcf62fa..b81d69321 100644 --- a/ipykernel/inprocess/channels.py +++ b/ipykernel/inprocess/channels.py @@ -5,12 +5,14 @@ from jupyter_client.channelsabc import HBChannelABC -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Channel classes -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + class InProcessChannel: """Base class for in-process channels.""" + proxy_methods = [] def __init__(self, client=None): @@ -28,18 +30,17 @@ def stop(self): self._is_alive = False def call_handlers(self, msg): - """ This method is called in the main thread when a message arrives. + """This method is called in the main thread when a message arrives. Subclasses should override this method to handle incoming messages. """ - raise NotImplementedError('call_handlers must be defined in a subclass.') + raise NotImplementedError("call_handlers must be defined in a subclass.") def flush(self, timeout=1.0): pass - def call_handlers_later(self, *args, **kwds): - """ Call the message handlers later. + """Call the message handlers later. The default implementation just calls the handlers immediately, but this method exists so that GUI toolkits can defer calling the handlers until @@ -48,7 +49,7 @@ def call_handlers_later(self, *args, **kwds): self.call_handlers(*args, **kwds) def process_events(self): - """ Process any pending GUI events. + """Process any pending GUI events. This method will be never be called from a frontend without an event loop (e.g., a terminal frontend). @@ -56,7 +57,6 @@ def process_events(self): raise NotImplementedError - class InProcessHBChannel: """A dummy heartbeat channel interface for in-process kernels. diff --git a/ipykernel/inprocess/client.py b/ipykernel/inprocess/client.py index 84ac82a49..b8d57085f 100644 --- a/ipykernel/inprocess/client.py +++ b/ipykernel/inprocess/client.py @@ -1,32 +1,31 @@ """A client for in-process kernels.""" -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Copyright (C) 2012 The IPython Development Team # # Distributed under the terms of the BSD License. The full license is in # the file COPYING, distributed as part of this software. -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Imports -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- import asyncio -# IPython imports -from traitlets import Type, Instance, default -from jupyter_client.clientabc import KernelClientABC from jupyter_client.client import KernelClient +from jupyter_client.clientabc import KernelClientABC + +# IPython imports +from traitlets import Instance, Type, default # Local imports -from .channels import ( - InProcessChannel, - InProcessHBChannel, -) +from .channels import InProcessChannel, InProcessHBChannel -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Main kernel Client class -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + class InProcessKernelClient(KernelClient): """A client for an in-process kernel. @@ -45,21 +44,21 @@ class InProcessKernelClient(KernelClient): control_channel_class = Type(InProcessChannel) hb_channel_class = Type(InProcessHBChannel) - kernel = Instance('ipykernel.inprocess.ipkernel.InProcessKernel', - allow_none=True) + kernel = Instance("ipykernel.inprocess.ipkernel.InProcessKernel", allow_none=True) - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- # Channel management methods - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- - @default('blocking_class') + @default("blocking_class") def _default_blocking_class(self): from .blocking import BlockingInProcessKernelClient + return BlockingInProcessKernelClient def get_connection_info(self): d = super().get_connection_info() - d['kernel'] = self.kernel + d["kernel"] = self.kernel return d def start_channels(self, *args, **kwargs): @@ -99,51 +98,57 @@ def hb_channel(self): # Methods for sending specific messages # ------------------------------------- - def execute(self, code, silent=False, store_history=True, - user_expressions={}, allow_stdin=None): + def execute( + self, code, silent=False, store_history=True, user_expressions={}, allow_stdin=None + ): if allow_stdin is None: allow_stdin = self.allow_stdin - content = dict(code=code, silent=silent, store_history=store_history, - user_expressions=user_expressions, - allow_stdin=allow_stdin) - msg = self.session.msg('execute_request', content) + content = dict( + code=code, + silent=silent, + store_history=store_history, + user_expressions=user_expressions, + allow_stdin=allow_stdin, + ) + msg = self.session.msg("execute_request", content) self._dispatch_to_kernel(msg) - return msg['header']['msg_id'] + return msg["header"]["msg_id"] def complete(self, code, cursor_pos=None): if cursor_pos is None: cursor_pos = len(code) content = dict(code=code, cursor_pos=cursor_pos) - msg = self.session.msg('complete_request', content) + msg = self.session.msg("complete_request", content) self._dispatch_to_kernel(msg) - return msg['header']['msg_id'] + return msg["header"]["msg_id"] def inspect(self, code, cursor_pos=None, detail_level=0): if cursor_pos is None: cursor_pos = len(code) - content = dict(code=code, cursor_pos=cursor_pos, + content = dict( + code=code, + cursor_pos=cursor_pos, detail_level=detail_level, ) - msg = self.session.msg('inspect_request', content) + msg = self.session.msg("inspect_request", content) self._dispatch_to_kernel(msg) - return msg['header']['msg_id'] + return msg["header"]["msg_id"] - def history(self, raw=True, output=False, hist_access_type='range', **kwds): - content = dict(raw=raw, output=output, - hist_access_type=hist_access_type, **kwds) - msg = self.session.msg('history_request', content) + def history(self, raw=True, output=False, hist_access_type="range", **kwds): + content = dict(raw=raw, output=output, hist_access_type=hist_access_type, **kwds) + msg = self.session.msg("history_request", content) self._dispatch_to_kernel(msg) - return msg['header']['msg_id'] + return msg["header"]["msg_id"] def shutdown(self, restart=False): # FIXME: What to do here? - raise NotImplementedError('Cannot shutdown in-process kernel') + raise NotImplementedError("Cannot shutdown in-process kernel") def kernel_info(self): """Request kernel info.""" - msg = self.session.msg('kernel_info_request') + msg = self.session.msg("kernel_info_request") self._dispatch_to_kernel(msg) - return msg['header']['msg_id'] + return msg["header"]["msg_id"] def comm_info(self, target_name=None): """Request a dictionary of valid comms and their targets.""" @@ -151,26 +156,25 @@ def comm_info(self, target_name=None): content = {} else: content = dict(target_name=target_name) - msg = self.session.msg('comm_info_request', content) + msg = self.session.msg("comm_info_request", content) self._dispatch_to_kernel(msg) - return msg['header']['msg_id'] + return msg["header"]["msg_id"] def input(self, string): if self.kernel is None: - raise RuntimeError('Cannot send input reply. No kernel exists.') + raise RuntimeError("Cannot send input reply. No kernel exists.") self.kernel.raw_input_str = string def is_complete(self, code): - msg = self.session.msg('is_complete_request', {'code': code}) + msg = self.session.msg("is_complete_request", {"code": code}) self._dispatch_to_kernel(msg) - return msg['header']['msg_id'] + return msg["header"]["msg_id"] def _dispatch_to_kernel(self, msg): - """ Send a message to the kernel and handle a reply. - """ + """Send a message to the kernel and handle a reply.""" kernel = self.kernel if kernel is None: - raise RuntimeError('Cannot send request. No kernel exists.') + raise RuntimeError("Cannot send request. No kernel exists.") stream = kernel.shell_stream self.session.send(stream, msg) @@ -181,20 +185,20 @@ def _dispatch_to_kernel(self, msg): self.shell_channel.call_handlers_later(reply_msg) def get_shell_msg(self, block=True, timeout=None): - return self.shell_channel.get_msg(block, timeout) + return self.shell_channel.get_msg(block, timeout) def get_iopub_msg(self, block=True, timeout=None): - return self.iopub_channel.get_msg(block, timeout) + return self.iopub_channel.get_msg(block, timeout) def get_stdin_msg(self, block=True, timeout=None): - return self.stdin_channel.get_msg(block, timeout) + return self.stdin_channel.get_msg(block, timeout) def get_control_msg(self, block=True, timeout=None): - return self.control_channel.get_msg(block, timeout) + return self.control_channel.get_msg(block, timeout) -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # ABC Registration -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- KernelClientABC.register(InProcessKernelClient) diff --git a/ipykernel/inprocess/constants.py b/ipykernel/inprocess/constants.py index fe07a3406..6133c757d 100644 --- a/ipykernel/inprocess/constants.py +++ b/ipykernel/inprocess/constants.py @@ -5,4 +5,4 @@ # key everywhere. This is not just the empty bytestring to avoid tripping # certain security checks in the rest of Jupyter that assumes that empty keys # are insecure. -INPROCESS_KEY = b'inprocess' +INPROCESS_KEY = b"inprocess" diff --git a/ipykernel/inprocess/ipkernel.py b/ipykernel/inprocess/ipkernel.py index bc17236d6..d2b2e8f32 100644 --- a/ipykernel/inprocess/ipkernel.py +++ b/ipykernel/inprocess/ipkernel.py @@ -3,50 +3,48 @@ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. -from contextlib import contextmanager import logging import sys +from contextlib import contextmanager from IPython.core.interactiveshell import InteractiveShellABC -from ipykernel.jsonutil import json_clean from traitlets import Any, Enum, Instance, List, Type, default + from ipykernel.ipkernel import IPythonKernel +from ipykernel.jsonutil import json_clean from ipykernel.zmqshell import ZMQInteractiveShell +from ..iostream import BackgroundSocket, IOPubThread, OutStream from .constants import INPROCESS_KEY from .socket import DummySocket -from ..iostream import OutStream, BackgroundSocket, IOPubThread -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Main kernel class -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + class InProcessKernel(IPythonKernel): - #------------------------------------------------------------------------- + # ------------------------------------------------------------------------- # InProcessKernel interface - #------------------------------------------------------------------------- + # ------------------------------------------------------------------------- # The frontends connected to this kernel. - frontends = List( - Instance('ipykernel.inprocess.client.InProcessKernelClient', - allow_none=True) - ) + frontends = List(Instance("ipykernel.inprocess.client.InProcessKernelClient", allow_none=True)) # The GUI environment that the kernel is running under. This need not be # specified for the normal operation for the kernel, but is required for # IPython's GUI support (including pylab). The default is 'inline' because # it is safe under all GUI toolkits. - gui = Enum(('tk', 'gtk', 'wx', 'qt', 'qt4', 'inline'), - default_value='inline') + gui = Enum(("tk", "gtk", "wx", "qt", "qt4", "inline"), default_value="inline") raw_input_str = Any() stdout = Any() stderr = Any() - #------------------------------------------------------------------------- + # ------------------------------------------------------------------------- # Kernel interface - #------------------------------------------------------------------------- + # ------------------------------------------------------------------------- shell_class = Type(allow_none=True) _underlying_iopub_socket = Instance(DummySocket, ()) @@ -54,7 +52,7 @@ class InProcessKernel(IPythonKernel): shell_stream = Instance(DummySocket, ()) - @default('iopub_thread') + @default("iopub_thread") def _default_iopub_thread(self): thread = IOPubThread(self._underlying_iopub_socket) thread.start() @@ -62,7 +60,7 @@ def _default_iopub_thread(self): iopub_socket = Instance(BackgroundSocket) - @default('iopub_socket') + @default("iopub_socket") def _default_iopub_socket(self): return self.iopub_thread.background_socket @@ -71,20 +69,20 @@ def _default_iopub_socket(self): def __init__(self, **traits): super().__init__(**traits) - self._underlying_iopub_socket.observe(self._io_dispatch, names=['message_sent']) + self._underlying_iopub_socket.observe(self._io_dispatch, names=["message_sent"]) self.shell.kernel = self async def execute_request(self, stream, ident, parent): - """ Override for temporary IO redirection. """ + """Override for temporary IO redirection.""" with self._redirected_io(): await super().execute_request(stream, ident, parent) def start(self): - """ Override registration of dispatchers for streams. """ + """Override registration of dispatchers for streams.""" self.shell.exit_now = False async def _abort_queues(self): - """ The in-process kernel doesn't abort requests. """ + """The in-process kernel doesn't abort requests.""" pass async def _flush_control_queue(self): @@ -99,79 +97,77 @@ def _input_request(self, prompt, ident, parent, password=False): # Send the input request. content = json_clean(dict(prompt=prompt, password=password)) - msg = self.session.msg('input_request', content, parent) + msg = self.session.msg("input_request", content, parent) for frontend in self.frontends: - if frontend.session.session == parent['header']['session']: + if frontend.session.session == parent["header"]["session"]: frontend.stdin_channel.call_handlers(msg) break else: - logging.error('No frontend found for raw_input request') - return '' + logging.error("No frontend found for raw_input request") + return "" # Await a response. while self.raw_input_str is None: frontend.stdin_channel.process_events() return self.raw_input_str - #------------------------------------------------------------------------- + # ------------------------------------------------------------------------- # Protected interface - #------------------------------------------------------------------------- + # ------------------------------------------------------------------------- @contextmanager def _redirected_io(self): - """ Temporarily redirect IO to the kernel. - """ + """Temporarily redirect IO to the kernel.""" sys_stdout, sys_stderr = sys.stdout, sys.stderr sys.stdout, sys.stderr = self.stdout, self.stderr yield sys.stdout, sys.stderr = sys_stdout, sys_stderr - #------ Trait change handlers -------------------------------------------- + # ------ Trait change handlers -------------------------------------------- def _io_dispatch(self, change): - """ Called when a message is sent to the IO socket. - """ + """Called when a message is sent to the IO socket.""" ident, msg = self.session.recv(self.iopub_socket.io_thread.socket, copy=False) for frontend in self.frontends: frontend.iopub_channel.call_handlers(msg) - #------ Trait initializers ----------------------------------------------- + # ------ Trait initializers ----------------------------------------------- - @default('log') + @default("log") def _default_log(self): return logging.getLogger(__name__) - @default('session') + @default("session") def _default_session(self): from jupyter_client.session import Session + return Session(parent=self, key=INPROCESS_KEY) - @default('shell_class') + @default("shell_class") def _default_shell_class(self): return InProcessInteractiveShell - @default('stdout') + @default("stdout") def _default_stdout(self): - return OutStream(self.session, self.iopub_thread, 'stdout', - watchfd=False) + return OutStream(self.session, self.iopub_thread, "stdout", watchfd=False) - @default('stderr') + @default("stderr") def _default_stderr(self): - return OutStream(self.session, self.iopub_thread, 'stderr', - watchfd=False) + return OutStream(self.session, self.iopub_thread, "stderr", watchfd=False) + -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Interactive shell subclass -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + class InProcessInteractiveShell(ZMQInteractiveShell): - kernel = Instance('ipykernel.inprocess.ipkernel.InProcessKernel', - allow_none=True) + kernel = Instance("ipykernel.inprocess.ipkernel.InProcessKernel", allow_none=True) - #------------------------------------------------------------------------- + # ------------------------------------------------------------------------- # InteractiveShell interface - #------------------------------------------------------------------------- + # ------------------------------------------------------------------------- def enable_gui(self, gui=None): """Enable GUI integration for the kernel.""" @@ -189,7 +185,7 @@ def enable_pylab(self, gui=None, import_all=True, welcome_message=False): """Activate pylab support at runtime.""" if not gui: gui = self.kernel.gui - return super().enable_pylab(gui, import_all, - welcome_message) + return super().enable_pylab(gui, import_all, welcome_message) + InteractiveShellABC.register(InProcessInteractiveShell) diff --git a/ipykernel/inprocess/manager.py b/ipykernel/inprocess/manager.py index f94c3a62b..fbbd34236 100644 --- a/ipykernel/inprocess/manager.py +++ b/ipykernel/inprocess/manager.py @@ -3,10 +3,10 @@ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. -from traitlets import Instance, DottedObjectName, default -from jupyter_client.managerabc import KernelManagerABC from jupyter_client.manager import KernelManager +from jupyter_client.managerabc import KernelManagerABC from jupyter_client.session import Session +from traitlets import DottedObjectName, Instance, default from .constants import INPROCESS_KEY @@ -22,27 +22,28 @@ class InProcessKernelManager(KernelManager): """ # The kernel process with which the KernelManager is communicating. - kernel = Instance('ipykernel.inprocess.ipkernel.InProcessKernel', - allow_none=True) + kernel = Instance("ipykernel.inprocess.ipkernel.InProcessKernel", allow_none=True) # the client class for KM.client() shortcut - client_class = DottedObjectName('ipykernel.inprocess.BlockingInProcessKernelClient') + client_class = DottedObjectName("ipykernel.inprocess.BlockingInProcessKernelClient") - @default('blocking_class') + @default("blocking_class") def _default_blocking_class(self): from .blocking import BlockingInProcessKernelClient + return BlockingInProcessKernelClient - @default('session') + @default("session") def _default_session(self): # don't sign in-process messages return Session(key=INPROCESS_KEY, parent=self) - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- # Kernel management methods - #-------------------------------------------------------------------------- + # -------------------------------------------------------------------------- def start_kernel(self, **kwds): from ipykernel.inprocess.ipkernel import InProcessKernel + self.kernel = InProcessKernel(parent=self, session=self.session) def shutdown_kernel(self): @@ -70,12 +71,12 @@ def is_alive(self): return self.kernel is not None def client(self, **kwargs): - kwargs['kernel'] = self.kernel + kwargs["kernel"] = self.kernel return super().client(**kwargs) -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # ABC Registration -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- KernelManagerABC.register(InProcessKernelManager) diff --git a/ipykernel/inprocess/socket.py b/ipykernel/inprocess/socket.py index ddaad3a38..477c36a47 100644 --- a/ipykernel/inprocess/socket.py +++ b/ipykernel/inprocess/socket.py @@ -6,25 +6,26 @@ from queue import Queue import zmq - from traitlets import HasTraits, Instance, Int -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Dummy socket class -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + class DummySocket(HasTraits): - """ A dummy socket implementing (part of) the zmq.Socket interface. """ + """A dummy socket implementing (part of) the zmq.Socket interface.""" queue = Instance(Queue, ()) - message_sent = Int(0) # Should be an Event + message_sent = Int(0) # Should be an Event context = Instance(zmq.Context) + def _context_default(self): return zmq.Context() - #------------------------------------------------------------------------- + # ------------------------------------------------------------------------- # Socket interface - #------------------------------------------------------------------------- + # ------------------------------------------------------------------------- def recv_multipart(self, flags=0, copy=True, track=False): return self.queue.get_nowait() diff --git a/ipykernel/inprocess/tests/test_kernel.py b/ipykernel/inprocess/tests/test_kernel.py index 952c66905..28b176f80 100644 --- a/ipykernel/inprocess/tests/test_kernel.py +++ b/ipykernel/inprocess/tests/test_kernel.py @@ -1,20 +1,18 @@ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. -from io import StringIO import sys import unittest +from io import StringIO import pytest - import tornado +from IPython.utils.io import capture_output from ipykernel.inprocess.blocking import BlockingInProcessKernelClient -from ipykernel.inprocess.manager import InProcessKernelManager from ipykernel.inprocess.ipkernel import InProcessKernel +from ipykernel.inprocess.manager import InProcessKernelManager from ipykernel.tests.utils import assemble_output -from IPython.utils.io import capture_output - def _init_asyncio_patch(): @@ -33,8 +31,13 @@ def _init_asyncio_patch(): FIXME: if/when tornado supports the defaults in asyncio, remove and bump tornado requirement for py38 """ - if sys.platform.startswith("win") and sys.version_info >= (3, 8) and tornado.version_info < (6, 1): + if ( + sys.platform.startswith("win") + and sys.version_info >= (3, 8) + and tornado.version_info < (6, 1) + ): import asyncio + try: from asyncio import ( WindowsProactorEventLoopPolicy, @@ -51,7 +54,6 @@ def _init_asyncio_patch(): class InProcessKernelTestCase(unittest.TestCase): - def setUp(self): _init_asyncio_patch() self.km = InProcessKernelManager() @@ -62,46 +64,39 @@ def setUp(self): def test_pylab(self): """Does %pylab work in the in-process kernel?""" - matplotlib = pytest.importorskip('matplotlib', reason='This test requires matplotlib') + matplotlib = pytest.importorskip("matplotlib", reason="This test requires matplotlib") kc = self.kc - kc.execute('%pylab') + kc.execute("%pylab") out, err = assemble_output(kc.get_iopub_msg) - self.assertIn('matplotlib', out) + self.assertIn("matplotlib", out) def test_raw_input(self): - """ Does the in-process kernel handle raw_input correctly? - """ - io = StringIO('foobar\n') + """Does the in-process kernel handle raw_input correctly?""" + io = StringIO("foobar\n") sys_stdin = sys.stdin sys.stdin = io try: - self.kc.execute('x = input()') + self.kc.execute("x = input()") finally: sys.stdin = sys_stdin - assert self.km.kernel.shell.user_ns.get('x') == 'foobar' + assert self.km.kernel.shell.user_ns.get("x") == "foobar" - @pytest.mark.skipif( - '__pypy__' in sys.builtin_module_names, - reason="fails on pypy" - ) + @pytest.mark.skipif("__pypy__" in sys.builtin_module_names, reason="fails on pypy") def test_stdout(self): - """ Does the in-process kernel correctly capture IO? - """ + """Does the in-process kernel correctly capture IO?""" kernel = InProcessKernel() with capture_output() as io: kernel.shell.run_cell('print("foo")') - assert io.stdout == 'foo\n' + assert io.stdout == "foo\n" kc = BlockingInProcessKernelClient(kernel=kernel, session=kernel.session) kernel.frontends.append(kc) kc.execute('print("bar")') out, err = assemble_output(kc.get_iopub_msg) - assert out == 'bar\n' + assert out == "bar\n" - @pytest.mark.skip( - reason="Currently don't capture during test as pytest does its own capturing" - ) + @pytest.mark.skip(reason="Currently don't capture during test as pytest does its own capturing") def test_capfd(self): """Does correctly capture fd""" kernel = InProcessKernel() @@ -121,6 +116,6 @@ def test_getpass_stream(self): "Tests that kernel getpass accept the stream parameter" kernel = InProcessKernel() kernel._allow_stdin = True - kernel._input_request = lambda *args, **kwargs : None + kernel._input_request = lambda *args, **kwargs: None - kernel.getpass(stream='non empty') + kernel.getpass(stream="non empty") diff --git a/ipykernel/inprocess/tests/test_kernelmanager.py b/ipykernel/inprocess/tests/test_kernelmanager.py index bd1bb81e2..850f543ce 100644 --- a/ipykernel/inprocess/tests/test_kernelmanager.py +++ b/ipykernel/inprocess/tests/test_kernelmanager.py @@ -5,12 +5,12 @@ from ipykernel.inprocess.manager import InProcessKernelManager -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Test case -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- -class InProcessKernelManagerTestCase(unittest.TestCase): +class InProcessKernelManagerTestCase(unittest.TestCase): def setUp(self): self.km = InProcessKernelManager() @@ -19,8 +19,7 @@ def tearDown(self): self.km.shutdown_kernel() def test_interface(self): - """ Does the in-process kernel manager implement the basic KM interface? - """ + """Does the in-process kernel manager implement the basic KM interface?""" km = self.km assert not km.has_kernel @@ -49,64 +48,59 @@ def test_interface(self): assert not kc.channels_running def test_execute(self): - """ Does executing code in an in-process kernel work? - """ + """Does executing code in an in-process kernel work?""" km = self.km km.start_kernel() kc = km.client() kc.start_channels() kc.wait_for_ready() - kc.execute('foo = 1') - assert km.kernel.shell.user_ns['foo'] == 1 + kc.execute("foo = 1") + assert km.kernel.shell.user_ns["foo"] == 1 def test_complete(self): - """ Does requesting completion from an in-process kernel work? - """ + """Does requesting completion from an in-process kernel work?""" km = self.km km.start_kernel() kc = km.client() kc.start_channels() kc.wait_for_ready() - km.kernel.shell.push({'my_bar': 0, 'my_baz': 1}) - kc.complete('my_ba', 5) + km.kernel.shell.push({"my_bar": 0, "my_baz": 1}) + kc.complete("my_ba", 5) msg = kc.get_shell_msg() - assert msg['header']['msg_type'] == 'complete_reply' - self.assertEqual(sorted(msg['content']['matches']), - ['my_bar', 'my_baz']) + assert msg["header"]["msg_type"] == "complete_reply" + self.assertEqual(sorted(msg["content"]["matches"]), ["my_bar", "my_baz"]) def test_inspect(self): - """ Does requesting object information from an in-process kernel work? - """ + """Does requesting object information from an in-process kernel work?""" km = self.km km.start_kernel() kc = km.client() kc.start_channels() kc.wait_for_ready() - km.kernel.shell.user_ns['foo'] = 1 - kc.inspect('foo') + km.kernel.shell.user_ns["foo"] = 1 + kc.inspect("foo") msg = kc.get_shell_msg() - assert msg['header']['msg_type'] == 'inspect_reply' - content = msg['content'] - assert content['found'] - text = content['data']['text/plain'] - self.assertIn('int', text) + assert msg["header"]["msg_type"] == "inspect_reply" + content = msg["content"] + assert content["found"] + text = content["data"]["text/plain"] + self.assertIn("int", text) def test_history(self): - """ Does requesting history from an in-process kernel work? - """ + """Does requesting history from an in-process kernel work?""" km = self.km km.start_kernel() kc = km.client() kc.start_channels() kc.wait_for_ready() - kc.execute('1') - kc.history(hist_access_type='tail', n=1) + kc.execute("1") + kc.history(hist_access_type="tail", n=1) msg = kc.shell_channel.get_msgs()[-1] - assert msg['header']['msg_type'] == 'history_reply' - history = msg['content']['history'] + assert msg["header"]["msg_type"] == "history_reply" + history = msg["content"]["history"] assert len(history) == 1 - assert history[0][2] == '1' + assert history[0][2] == "1" -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/ipykernel/iostream.py b/ipykernel/iostream.py index 590513bc3..fa038359b 100644 --- a/ipykernel/iostream.py +++ b/ipykernel/iostream.py @@ -4,39 +4,39 @@ # Distributed under the terms of the Modified BSD License. import atexit -from binascii import b2a_hex -from collections import deque -from imp import lock_held as import_lock_held +import io import os import sys import threading -import warnings -from weakref import WeakSet import traceback +import warnings +from binascii import b2a_hex +from collections import deque +from imp import lock_held as import_lock_held from io import StringIO, TextIOBase -import io +from weakref import WeakSet import zmq + if zmq.pyzmq_version_info() >= (17, 0): from tornado.ioloop import IOLoop else: # deprecated since pyzmq 17 from zmq.eventloop.ioloop import IOLoop -from zmq.eventloop.zmqstream import ZMQStream from jupyter_client.session import extract_header +from zmq.eventloop.zmqstream import ZMQStream - -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Globals -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- MASTER = 0 CHILD = 1 -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # IO classes -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- class IOPubThread: @@ -88,8 +88,8 @@ def _setup_event_pipe(self): pipe_in = ctx.socket(zmq.PULL) pipe_in.linger = 0 - _uuid = b2a_hex(os.urandom(16)).decode('ascii') - iface = self._event_interface = 'inproc://%s' % _uuid + _uuid = b2a_hex(os.urandom(16)).decode("ascii") + iface = self._event_interface = "inproc://%s" % _uuid pipe_in.bind(iface) self._event_puller = ZMQStream(pipe_in, self.io_loop) self._event_puller.on_recv(self._handle_event) @@ -139,8 +139,9 @@ def _setup_pipe_in(self): try: self._pipe_port = pipe_in.bind_to_random_port("tcp://127.0.0.1") except zmq.ZMQError as e: - warnings.warn("Couldn't bind IOPub Pipe to 127.0.0.1: %s" % e + - "\nsubprocess output will be unavailable." + warnings.warn( + "Couldn't bind IOPub Pipe to 127.0.0.1: %s" % e + + "\nsubprocess output will be unavailable." ) self._pipe_flag = False pipe_in.close() @@ -161,7 +162,7 @@ def _setup_pipe_out(self): # must be new context after fork ctx = zmq.Context() pipe_out = ctx.socket(zmq.PUSH) - pipe_out.linger = 3000 # 3s timeout for pipe_out sends before discarding the message + pipe_out.linger = 3000 # 3s timeout for pipe_out sends before discarding the message pipe_out.connect("tcp://127.0.0.1:%i" % self._pipe_port) return ctx, pipe_out @@ -213,7 +214,7 @@ def schedule(self, f): if self.thread.is_alive(): self._events.append(f) # wake event thread (message content is ignored) - self._event_pipe.send(b'') + self._event_pipe.send(b"") else: f() @@ -222,7 +223,7 @@ def send_multipart(self, *args, **kwargs): If my thread isn't running (e.g. forked process), send immediately. """ - self.schedule(lambda : self._really_send(*args, **kwargs)) + self.schedule(lambda: self._really_send(*args, **kwargs)) def _really_send(self, msg, *args, **kwargs): """The callback that actually sends messages""" @@ -243,6 +244,7 @@ def _really_send(self, msg, *args, **kwargs): class BackgroundSocket: """Wrapper around IOPub thread that provides zmq send[_multipart]""" + io_thread = None def __init__(self, io_thread): @@ -250,7 +252,7 @@ def __init__(self, io_thread): def __getattr__(self, attr): """Wrap socket attr access for backward-compatibility""" - if attr.startswith('__') and attr.endswith('__'): + if attr.startswith("__") and attr.endswith("__"): # don't wrap magic methods super().__getattr__(attr) if hasattr(self.io_thread.socket, attr): @@ -265,7 +267,7 @@ def __getattr__(self, attr): super().__getattr__(attr) def __setattr__(self, attr, value): - if attr == 'io_thread' or (attr.startswith('__' and attr.endswith('__'))): + if attr == "io_thread" or (attr.startswith("__" and attr.endswith("__"))): super().__setattr__(attr, value) else: warnings.warn( @@ -297,8 +299,7 @@ class OutStream(TextIOBase): # The time interval between automatic flushes, in seconds. flush_interval = 0.2 topic = None - encoding = 'UTF-8' - + encoding = "UTF-8" def fileno(self): """ @@ -332,7 +333,15 @@ def _watch_pipe_fd(self): self._exc = sys.exc_info() def __init__( - self, session, pub_thread, name, pipe=None, echo=None, *, watchfd=True, isatty=False, + self, + session, + pub_thread, + name, + pipe=None, + echo=None, + *, + watchfd=True, + isatty=False, ): """ Parameters @@ -392,7 +401,7 @@ def __init__( self._setup_stream_redirects(name) if echo: - if hasattr(echo, 'read') and hasattr(echo, 'write'): + if hasattr(echo, "read") and hasattr(echo, "write"): self.echo = echo else: raise ValueError("echo argument must be a file like object") @@ -449,6 +458,7 @@ def _schedule_flush(self): # add_timeout has to be handed to the io thread via event pipe def _schedule_in_thread(): self._io_loop.call_later(self.flush_interval, self._flush) + self.pub_thread.schedule(_schedule_in_thread) def flush(self): @@ -457,10 +467,10 @@ def flush(self): send will happen in the background thread """ if ( - self.pub_thread - and self.pub_thread.thread is not None - and self.pub_thread.thread.is_alive() - and self.pub_thread.thread.ident != threading.current_thread().ident + self.pub_thread + and self.pub_thread.thread is not None + and self.pub_thread.thread.is_alive() + and self.pub_thread.thread.ident != threading.current_thread().ident ): # request flush on the background thread self.pub_thread.schedule(self._flush) @@ -492,8 +502,7 @@ def _flush(self): self.echo.flush() except OSError as e: if self.echo is not sys.__stderr__: - print(f"Flush failed: {e}", - file=sys.__stderr__) + print(f"Flush failed: {e}", file=sys.__stderr__) data = self._flush_buffer() if data: @@ -501,9 +510,14 @@ def _flush(self): # since pub_thread is itself fork-safe. # There should be a better way to do this. self.session.pid = os.getpid() - content = {'name':self.name, 'text':data} - self.session.send(self.pub_thread, 'stream', content=content, - parent=self.parent_header, ident=self.topic) + content = {"name": self.name, "text": data} + self.session.send( + self.pub_thread, + "stream", + content=content, + parent=self.parent_header, + ident=self.topic, + ) def write(self, string: str) -> int: """Write to current stream after encoding if necessary @@ -516,23 +530,20 @@ def write(self, string: str) -> int: """ if not isinstance(string, str): - raise TypeError( - f"write() argument must be str, not {type(string)}" - ) + raise TypeError(f"write() argument must be str, not {type(string)}") if self.echo is not None: try: self.echo.write(string) except OSError as e: if self.echo is not sys.__stderr__: - print(f"Write failed: {e}", - file=sys.__stderr__) + print(f"Write failed: {e}", file=sys.__stderr__) if self.pub_thread is None: - raise ValueError('I/O operation on closed file') + raise ValueError("I/O operation on closed file") else: - is_child = (not self._is_master_process()) + is_child = not self._is_master_process() # only touch the buffer in the IO thread to avoid races with self._buffer_lock: self._buffer.write(string) @@ -551,7 +562,7 @@ def write(self, string: str) -> int: def writelines(self, sequence): if self.pub_thread is None: - raise ValueError('I/O operation on closed file') + raise ValueError("I/O operation on closed file") else: for string in sequence: self.write(string) diff --git a/ipykernel/ipkernel.py b/ipykernel/ipkernel.py index 26276ca91..350f7f9fd 100644 --- a/ipykernel/ipkernel.py +++ b/ipykernel/ipkernel.py @@ -2,23 +2,23 @@ import asyncio import builtins -from contextlib import contextmanager -from functools import partial import getpass import signal import sys +from contextlib import contextmanager +from functools import partial from IPython.core import release -from IPython.utils.tokenutil import token_at_cursor, line_at_cursor -from traitlets import Instance, Type, Any, List, Bool, observe, observe_compat +from IPython.utils.tokenutil import line_at_cursor, token_at_cursor +from traitlets import Any, Bool, Instance, List, Type, observe, observe_compat from zmq.eventloop.zmqstream import ZMQStream from .comm import CommManager -from .kernelbase import Kernel as KernelBase -from .zmqshell import ZMQInteractiveShell -from .eventloops import _use_appnope from .compiler import XCachingCompiler from .debugger import Debugger, _is_debugpy_available +from .eventloops import _use_appnope +from .kernelbase import Kernel as KernelBase +from .zmqshell import ZMQInteractiveShell try: from IPython.core.interactiveshell import _asyncio_runner @@ -26,42 +26,43 @@ _asyncio_runner = None try: - from IPython.core.completer import ( - rectify_completions as _rectify_completions, - provisionalcompleter as _provisionalcompleter, - ) + from IPython.core.completer import provisionalcompleter as _provisionalcompleter + from IPython.core.completer import rectify_completions as _rectify_completions + _use_experimental_60_completion = True except ImportError: _use_experimental_60_completion = False -_EXPERIMENTAL_KEY_NAME = '_jupyter_types_experimental' +_EXPERIMENTAL_KEY_NAME = "_jupyter_types_experimental" class IPythonKernel(KernelBase): - shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', - allow_none=True) + shell = Instance("IPython.core.interactiveshell.InteractiveShellABC", allow_none=True) shell_class = Type(ZMQInteractiveShell) - use_experimental_completions = Bool(True, + use_experimental_completions = Bool( + True, help="Set this flag to False to deactivate the use of experimental IPython completion APIs.", ).tag(config=True) debugpy_stream = Instance(ZMQStream, allow_none=True) if _is_debugpy_available else None user_module = Any() - @observe('user_module') + + @observe("user_module") @observe_compat def _user_module_changed(self, change): if self.shell is not None: - self.shell.user_module = change['new'] + self.shell.user_module = change["new"] user_ns = Instance(dict, args=None, allow_none=True) - @observe('user_ns') + + @observe("user_ns") @observe_compat def _user_ns_changed(self, change): if self.shell is not None: - self.shell.user_ns = change['new'] + self.shell.user_ns = change["new"] self.shell.init_user_ns() # A reference to the Python builtin 'raw_input' function. @@ -74,90 +75,93 @@ def __init__(self, **kwargs): # Initialize the Debugger if _is_debugpy_available: - self.debugger = Debugger(self.log, - self.debugpy_stream, - self._publish_debug_event, - self.debug_shell_socket, - self.session, - self.debug_just_my_code) + self.debugger = Debugger( + self.log, + self.debugpy_stream, + self._publish_debug_event, + self.debug_shell_socket, + self.session, + self.debug_just_my_code, + ) # Initialize the InteractiveShell subclass - self.shell = self.shell_class.instance(parent=self, - profile_dir = self.profile_dir, - user_module = self.user_module, - user_ns = self.user_ns, - kernel = self, - compiler_class = XCachingCompiler, + self.shell = self.shell_class.instance( + parent=self, + profile_dir=self.profile_dir, + user_module=self.user_module, + user_ns=self.user_ns, + kernel=self, + compiler_class=XCachingCompiler, ) self.shell.displayhook.session = self.session self.shell.displayhook.pub_socket = self.iopub_socket - self.shell.displayhook.topic = self._topic('execute_result') + self.shell.displayhook.topic = self._topic("execute_result") self.shell.display_pub.session = self.session self.shell.display_pub.pub_socket = self.iopub_socket self.comm_manager = CommManager(parent=self, kernel=self) self.shell.configurables.append(self.comm_manager) - comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ] + comm_msg_types = ["comm_open", "comm_msg", "comm_close"] for msg_type in comm_msg_types: self.shell_handlers[msg_type] = getattr(self.comm_manager, msg_type) if _use_appnope() and self._darwin_app_nap: # Disable app-nap as the kernel is not a gui but can have guis import appnope + appnope.nope() - help_links = List([ - { - 'text': "Python Reference", - 'url': "https://docs.python.org/%i.%i" % sys.version_info[:2], - }, - { - 'text': "IPython Reference", - 'url': "https://ipython.org/documentation.html", - }, - { - 'text': "NumPy Reference", - 'url': "https://docs.scipy.org/doc/numpy/reference/", - }, - { - 'text': "SciPy Reference", - 'url': "https://docs.scipy.org/doc/scipy/reference/", - }, - { - 'text': "Matplotlib Reference", - 'url': "https://matplotlib.org/contents.html", - }, - { - 'text': "SymPy Reference", - 'url': "http://docs.sympy.org/latest/index.html", - }, - { - 'text': "pandas Reference", - 'url': "https://pandas.pydata.org/pandas-docs/stable/", - }, - ]).tag(config=True) + help_links = List( + [ + { + "text": "Python Reference", + "url": "https://docs.python.org/%i.%i" % sys.version_info[:2], + }, + { + "text": "IPython Reference", + "url": "https://ipython.org/documentation.html", + }, + { + "text": "NumPy Reference", + "url": "https://docs.scipy.org/doc/numpy/reference/", + }, + { + "text": "SciPy Reference", + "url": "https://docs.scipy.org/doc/scipy/reference/", + }, + { + "text": "Matplotlib Reference", + "url": "https://matplotlib.org/contents.html", + }, + { + "text": "SymPy Reference", + "url": "http://docs.sympy.org/latest/index.html", + }, + { + "text": "pandas Reference", + "url": "https://pandas.pydata.org/pandas-docs/stable/", + }, + ] + ).tag(config=True) # Kernel info fields - implementation = 'ipython' + implementation = "ipython" implementation_version = release.version language_info = { - 'name': 'python', - 'version': sys.version.split()[0], - 'mimetype': 'text/x-python', - 'codemirror_mode': { - 'name': 'ipython', - 'version': sys.version_info[0] - }, - 'pygments_lexer': 'ipython%d' % 3, - 'nbconvert_exporter': 'python', - 'file_extension': '.py' + "name": "python", + "version": sys.version.split()[0], + "mimetype": "text/x-python", + "codemirror_mode": {"name": "ipython", "version": sys.version_info[0]}, + "pygments_lexer": "ipython%d" % 3, + "nbconvert_exporter": "python", + "file_extension": ".py", } def dispatch_debugpy(self, msg): if _is_debugpy_available: # The first frame is the socket id, we can drop it - frame = msg[1].bytes.decode('utf-8') + frame = msg[1].bytes.decode("utf-8") self.log.debug("Debugpy received: %s", frame) self.debugger.tcp_client.receive_dap_frame(frame) @@ -177,14 +181,16 @@ def start(self): self.debugpy_stream.on_recv(self.dispatch_debugpy, copy=False) super().start() if self.debugpy_stream: - asyncio.run_coroutine_threadsafe(self.poll_stopped_queue(), self.control_thread.io_loop.asyncio_loop) + asyncio.run_coroutine_threadsafe( + self.poll_stopped_queue(), self.control_thread.io_loop.asyncio_loop + ) - def set_parent(self, ident, parent, channel='shell'): + def set_parent(self, ident, parent, channel="shell"): """Overridden from parent to tell the display hook and output streams about the parent message. """ super().set_parent(ident, parent, channel) - if channel == 'shell': + if channel == "shell": self.shell.set_parent(parent) def init_metadata(self, parent): @@ -195,10 +201,12 @@ def init_metadata(self, parent): md = super().init_metadata(parent) # FIXME: remove deprecated ipyparallel-specific code # This is required for ipyparallel < 5.0 - md.update({ - 'dependencies_met' : True, - 'engine' : self.ident, - }) + md.update( + { + "dependencies_met": True, + "engine": self.ident, + } + ) return md def finish_metadata(self, parent, metadata, reply_content): @@ -209,10 +217,7 @@ def finish_metadata(self, parent, metadata, reply_content): # FIXME: remove deprecated ipyparallel-specific code # This is required by ipyparallel < 5.0 metadata["status"] = reply_content["status"] - if ( - reply_content["status"] == "error" - and reply_content["ename"] == "UnmetDependency" - ): + if reply_content["status"] == "error" and reply_content["ename"] == "UnmetDependency": metadata["dependencies_met"] = False return metadata @@ -268,20 +273,17 @@ def cancel_unless_done(f, _ignored): # when sigint finishes, # abort the coroutine with CancelledError - sigint_future.add_done_callback( - partial(cancel_unless_done, future) - ) + sigint_future.add_done_callback(partial(cancel_unless_done, future)) # when the main future finishes, # stop watching for SIGINT events - future.add_done_callback( - partial(cancel_unless_done, sigint_future) - ) + future.add_done_callback(partial(cancel_unless_done, sigint_future)) def handle_sigint(*args): def set_sigint_result(): if sigint_future.cancelled() or sigint_future.done(): return sigint_future.set_result(1) + # use add_callback for thread safety self.io_loop.add_callback(set_sigint_result) @@ -293,14 +295,15 @@ def set_sigint_result(): # restore the previous sigint handler signal.signal(signal.SIGINT, save_sigint) - async def do_execute(self, code, silent, store_history=True, - user_expressions=None, allow_stdin=False): - shell = self.shell # we'll need this a lot here + async def do_execute( + self, code, silent, store_history=True, user_expressions=None, allow_stdin=False + ): + shell = self.shell # we'll need this a lot here self._forward_input(allow_stdin) reply_content = {} - if hasattr(shell, 'run_cell_async') and hasattr(shell, 'should_run_async'): + if hasattr(shell, "run_cell_async") and hasattr(shell, "should_run_async"): run_cell = shell.run_cell_async should_run_async = shell.should_run_async else: @@ -309,6 +312,7 @@ async def do_execute(self, code, silent, store_history=True, # use blocking run_cell and wrap it in coroutine async def run_cell(*args, **kwargs): return shell.run_cell(*args, **kwargs) + try: # default case: runner is asyncio and asyncio is already running @@ -336,7 +340,7 @@ async def run_cell(*args, **kwargs): store_history=store_history, silent=silent, transformed_cell=transformed_cell, - preprocessing_exc_tuple=preprocessing_exc_tuple + preprocessing_exc_tuple=preprocessing_exc_tuple, ) coro_future = asyncio.ensure_future(coro) @@ -345,9 +349,9 @@ async def run_cell(*args, **kwargs): try: res = await coro_future finally: - shell.events.trigger('post_execute') + shell.events.trigger("post_execute") if not silent: - shell.events.trigger('post_run_cell', res) + shell.events.trigger("post_run_cell", res) else: # runner isn't already running, # make synchronous call, @@ -362,42 +366,42 @@ async def run_cell(*args, **kwargs): err = res.error_in_exec if res.success: - reply_content['status'] = 'ok' + reply_content["status"] = "ok" else: - reply_content['status'] = 'error' - - reply_content.update({ - 'traceback': shell._last_traceback or [], - 'ename': str(type(err).__name__), - 'evalue': str(err), - }) + reply_content["status"] = "error" + + reply_content.update( + { + "traceback": shell._last_traceback or [], + "ename": str(type(err).__name__), + "evalue": str(err), + } + ) # FIXME: deprecated piece for ipyparallel (remove in 5.0): - e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, - method='execute') - reply_content['engine_info'] = e_info - + e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method="execute") + reply_content["engine_info"] = e_info # Return the execution counter so clients can display prompts - reply_content['execution_count'] = shell.execution_count - 1 - - if 'traceback' in reply_content: - self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback'])) + reply_content["execution_count"] = shell.execution_count - 1 + if "traceback" in reply_content: + self.log.info( + "Exception in execute request:\n%s", "\n".join(reply_content["traceback"]) + ) # At this point, we can tell whether the main code execution succeeded # or not. If it did, we proceed to evaluate user_expressions - if reply_content['status'] == 'ok': - reply_content['user_expressions'] = \ - shell.user_expressions(user_expressions or {}) + if reply_content["status"] == "ok": + reply_content["user_expressions"] = shell.user_expressions(user_expressions or {}) else: # If there was an error, don't even try to compute expressions - reply_content['user_expressions'] = {} + reply_content["user_expressions"] = {} # Payloads should be retrieved regardless of outcome, so we can both # recover partial output (that could have been generated early in a # block, before an error) and always clear the payload system. - reply_content['payload'] = shell.payload_manager.read_payload() + reply_content["payload"] = shell.payload_manager.read_payload() # Be aggressive about clearing the payload because we don't want # it to sit in memory until the next execute_request comes in. shell.payload_manager.clear_payload() @@ -416,12 +420,14 @@ def do_complete(self, code, cursor_pos): line, offset = line_at_cursor(code, cursor_pos) line_cursor = cursor_pos - offset - txt, matches = self.shell.complete('', line, line_cursor) - return {'matches' : matches, - 'cursor_end' : cursor_pos, - 'cursor_start' : cursor_pos - len(txt), - 'metadata' : {}, - 'status' : 'ok'} + txt, matches = self.shell.complete("", line, line_cursor) + return { + "matches": matches, + "cursor_end": cursor_pos, + "cursor_start": cursor_pos - len(txt), + "metadata": {}, + "status": "ok", + } async def do_debug_request(self, msg): if _is_debugpy_available: @@ -439,12 +445,14 @@ def _experimental_do_complete(self, code, cursor_pos): comps = [] for comp in completions: - comps.append(dict( - start=comp.start, - end=comp.end, - text=comp.text, - type=comp.type, - )) + comps.append( + dict( + start=comp.start, + end=comp.end, + text=comp.text, + type=comp.type, + ) + ) if completions: s = completions[0].start @@ -455,18 +463,20 @@ def _experimental_do_complete(self, code, cursor_pos): e = cursor_pos matches = [] - return {'matches': matches, - 'cursor_end': e, - 'cursor_start': s, - 'metadata': {_EXPERIMENTAL_KEY_NAME: comps}, - 'status': 'ok'} + return { + "matches": matches, + "cursor_end": e, + "cursor_start": s, + "metadata": {_EXPERIMENTAL_KEY_NAME: comps}, + "status": "ok", + } def do_inspect(self, code, cursor_pos, detail_level=0, omit_sections=()): name = token_at_cursor(code, cursor_pos) - reply_content = {'status' : 'ok'} - reply_content['data'] = {} - reply_content['metadata'] = {} + reply_content = {"status": "ok"} + reply_content["data"] = {} + reply_content["metadata"] = {} try: if release.version_info >= (8,): # `omit_sections` keyword will be available in IPython 8, see @@ -477,73 +487,84 @@ def do_inspect(self, code, cursor_pos, detail_level=0, omit_sections=()): omit_sections=omit_sections, ) else: - bundle = self.shell.object_inspect_mime( - name, - detail_level=detail_level - ) - reply_content['data'].update(bundle) + bundle = self.shell.object_inspect_mime(name, detail_level=detail_level) + reply_content["data"].update(bundle) if not self.shell.enable_html_pager: - reply_content['data'].pop('text/html') - reply_content['found'] = True + reply_content["data"].pop("text/html") + reply_content["found"] = True except KeyError: - reply_content['found'] = False + reply_content["found"] = False return reply_content - def do_history(self, hist_access_type, output, raw, session=0, start=0, - stop=None, n=None, pattern=None, unique=False): - if hist_access_type == 'tail': - hist = self.shell.history_manager.get_tail(n, raw=raw, output=output, - include_latest=True) + def do_history( + self, + hist_access_type, + output, + raw, + session=0, + start=0, + stop=None, + n=None, + pattern=None, + unique=False, + ): + if hist_access_type == "tail": + hist = self.shell.history_manager.get_tail( + n, raw=raw, output=output, include_latest=True + ) - elif hist_access_type == 'range': - hist = self.shell.history_manager.get_range(session, start, stop, - raw=raw, output=output) + elif hist_access_type == "range": + hist = self.shell.history_manager.get_range( + session, start, stop, raw=raw, output=output + ) - elif hist_access_type == 'search': + elif hist_access_type == "search": hist = self.shell.history_manager.search( - pattern, raw=raw, output=output, n=n, unique=unique) + pattern, raw=raw, output=output, n=n, unique=unique + ) else: hist = [] return { - 'status': 'ok', - 'history' : list(hist), + "status": "ok", + "history": list(hist), } def do_shutdown(self, restart): self.shell.exit_now = True - return dict(status='ok', restart=restart) + return dict(status="ok", restart=restart) def do_is_complete(self, code): - transformer_manager = getattr(self.shell, 'input_transformer_manager', None) + transformer_manager = getattr(self.shell, "input_transformer_manager", None) if transformer_manager is None: # input_splitter attribute is deprecated transformer_manager = self.shell.input_splitter status, indent_spaces = transformer_manager.check_complete(code) - r = {'status': status} - if status == 'incomplete': - r['indent'] = ' ' * indent_spaces + r = {"status": status} + if status == "incomplete": + r["indent"] = " " * indent_spaces return r def do_apply(self, content, bufs, msg_id, reply_metadata): from .serialize import serialize_object, unpack_apply_message + shell = self.shell try: working = shell.user_ns - prefix = "_"+str(msg_id).replace("-","")+"_" + prefix = "_" + str(msg_id).replace("-", "") + "_" - f,args,kwargs = unpack_apply_message(bufs, working, copy=False) + f, args, kwargs = unpack_apply_message(bufs, working, copy=False) - fname = getattr(f, '__name__', 'f') + fname = getattr(f, "__name__", "f") - fname = prefix+"f" - argname = prefix+"args" - kwargname = prefix+"kwargs" - resultname = prefix+"result" + fname = prefix + "f" + argname = prefix + "args" + kwargname = prefix + "kwargs" + resultname = prefix + "result" - ns = { fname : f, argname : args, kwargname : kwargs , resultname : None } + ns = {fname: f, argname: args, kwargname: kwargs, resultname: None} # print ns working.update(ns) code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname) @@ -554,7 +575,8 @@ def do_apply(self, content, bufs, msg_id, reply_metadata): for key in ns: working.pop(key) - result_buf = serialize_object(result, + result_buf = serialize_object( + result, buffer_threshold=self.session.buffer_threshold, item_threshold=self.session.item_threshold, ) @@ -568,29 +590,37 @@ def do_apply(self, content, bufs, msg_id, reply_metadata): "evalue": str(e), } # FIXME: deprecated piece for ipyparallel (remove in 5.0): - e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply') - reply_content['engine_info'] = e_info - - self.send_response(self.iopub_socket, 'error', reply_content, - ident=self._topic('error'), channel='shell') - self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback'])) + e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method="apply") + reply_content["engine_info"] = e_info + + self.send_response( + self.iopub_socket, + "error", + reply_content, + ident=self._topic("error"), + channel="shell", + ) + self.log.info("Exception in apply request:\n%s", "\n".join(reply_content["traceback"])) result_buf = [] - reply_content['status'] = 'error' + reply_content["status"] = "error" else: - reply_content = {'status' : 'ok'} + reply_content = {"status": "ok"} return reply_content, result_buf def do_clear(self): self.shell.reset(False) - return dict(status='ok') + return dict(status="ok") # This exists only for backwards compatibility - use IPythonKernel instead + class Kernel(IPythonKernel): def __init__(self, *args, **kwargs): import warnings - warnings.warn('Kernel is a deprecated alias of ipykernel.ipkernel.IPythonKernel', - DeprecationWarning) + + warnings.warn( + "Kernel is a deprecated alias of ipykernel.ipkernel.IPythonKernel", DeprecationWarning + ) super().__init__(*args, **kwargs) diff --git a/ipykernel/jsonutil.py b/ipykernel/jsonutil.py index a777cc72a..36565a842 100644 --- a/ipykernel/jsonutil.py +++ b/ipykernel/jsonutil.py @@ -3,45 +3,48 @@ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. -from binascii import b2a_base64 import math +import numbers import re import types +from binascii import b2a_base64 from datetime import datetime -import numbers + from jupyter_client._version import version_info as jupyter_client_version -next_attr_name = '__next__' +next_attr_name = "__next__" -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Globals and constants -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # timestamp formats ISO8601 = "%Y-%m-%dT%H:%M:%S.%f" -ISO8601_PAT=re.compile(r"^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(\.\d{1,6})?Z?([\+\-]\d{2}:?\d{2})?$") +ISO8601_PAT = re.compile( + r"^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(\.\d{1,6})?Z?([\+\-]\d{2}:?\d{2})?$" +) # holy crap, strptime is not threadsafe. # Calling it once at import seems to help. datetime.strptime("1", "%d") -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Classes and functions -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # constants for identifying png/jpeg data -PNG = b'\x89PNG\r\n\x1a\n' +PNG = b"\x89PNG\r\n\x1a\n" # front of PNG base64-encoded -PNG64 = b'iVBORw0KG' -JPEG = b'\xff\xd8' +PNG64 = b"iVBORw0KG" +JPEG = b"\xff\xd8" # front of JPEG base64-encoded -JPEG64 = b'/9' +JPEG64 = b"/9" # constants for identifying gif data -GIF_64 = b'R0lGODdh' -GIF89_64 = b'R0lGODlh' +GIF_64 = b"R0lGODdh" +GIF89_64 = b"R0lGODlh" # front of PDF base64-encoded -PDF64 = b'JVBER' +PDF64 = b"JVBER" JUPYTER_CLIENT_MAJOR_VERSION = jupyter_client_version[0] @@ -126,10 +129,11 @@ def json_clean(obj): if isinstance(obj, bytes): # unanmbiguous binary data is base64-encoded # (this probably should have happened upstream) - return b2a_base64(obj).decode('ascii') + return b2a_base64(obj).decode("ascii") if isinstance(obj, container_to_list) or ( - hasattr(obj, '__iter__') and hasattr(obj, next_attr_name)): + hasattr(obj, "__iter__") and hasattr(obj, next_attr_name) + ): obj = list(obj) if isinstance(obj, list): @@ -142,11 +146,13 @@ def json_clean(obj): nkeys = len(obj) nkeys_collapsed = len(set(map(str, obj))) if nkeys != nkeys_collapsed: - raise ValueError('dict cannot be safely converted to JSON: ' - 'key collision would lead to dropped values') + raise ValueError( + "dict cannot be safely converted to JSON: " + "key collision would lead to dropped values" + ) # If all OK, proceed by making the new dict that will be json-safe out = {} - for k,v in obj.items(): + for k, v in obj.items(): out[str(k)] = json_clean(v) return out if isinstance(obj, datetime): diff --git a/ipykernel/kernelapp.py b/ipykernel/kernelapp.py index dc33530f8..6eba8f50c 100644 --- a/ipykernel/kernelapp.py +++ b/ipykernel/kernelapp.py @@ -4,81 +4,88 @@ # Distributed under the terms of the Modified BSD License. import atexit -import os -import sys import errno +import logging +import os import signal +import sys import traceback -import logging from functools import partial -from io import TextIOWrapper, FileIO +from io import FileIO, TextIOWrapper from logging import StreamHandler -from tornado import ioloop - import zmq -from zmq.eventloop.zmqstream import ZMQStream - from IPython.core.application import ( - BaseIPythonApplication, base_flags, base_aliases, catch_config_error + BaseIPythonApplication, + base_aliases, + base_flags, + catch_config_error, ) from IPython.core.profiledir import ProfileDir -from IPython.core.shellapp import ( - InteractiveShellApp, shell_flags, shell_aliases -) +from IPython.core.shellapp import InteractiveShellApp, shell_aliases, shell_flags +from jupyter_client import write_connection_file +from jupyter_client.connect import ConnectionFileMixin +from jupyter_client.session import Session, session_aliases, session_flags +from jupyter_core.paths import jupyter_runtime_dir +from tornado import ioloop from traitlets import ( - Any, Instance, Dict, Unicode, Integer, Bool, DottedObjectName, Type, default + Any, + Bool, + Dict, + DottedObjectName, + Instance, + Integer, + Type, + Unicode, + default, ) -from traitlets.utils.importstring import import_item from traitlets.utils import filefind -from jupyter_core.paths import jupyter_runtime_dir -from jupyter_client import write_connection_file -from jupyter_client.connect import ConnectionFileMixin +from traitlets.utils.importstring import import_item +from zmq.eventloop.zmqstream import ZMQStream -# local imports -from .iostream import IOPubThread from .control import ControlThread from .heartbeat import Heartbeat + +# local imports +from .iostream import IOPubThread from .ipkernel import IPythonKernel from .parentpoller import ParentPollerUnix, ParentPollerWindows -from jupyter_client.session import ( - Session, session_flags, session_aliases, -) from .zmqshell import ZMQInteractiveShell -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Flags and Aliases -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- kernel_aliases = dict(base_aliases) -kernel_aliases.update({ - 'ip' : 'IPKernelApp.ip', - 'hb' : 'IPKernelApp.hb_port', - 'shell' : 'IPKernelApp.shell_port', - 'iopub' : 'IPKernelApp.iopub_port', - 'stdin' : 'IPKernelApp.stdin_port', - 'control' : 'IPKernelApp.control_port', - 'f' : 'IPKernelApp.connection_file', - 'transport': 'IPKernelApp.transport', -}) +kernel_aliases.update( + { + "ip": "IPKernelApp.ip", + "hb": "IPKernelApp.hb_port", + "shell": "IPKernelApp.shell_port", + "iopub": "IPKernelApp.iopub_port", + "stdin": "IPKernelApp.stdin_port", + "control": "IPKernelApp.control_port", + "f": "IPKernelApp.connection_file", + "transport": "IPKernelApp.transport", + } +) kernel_flags = dict(base_flags) -kernel_flags.update({ - 'no-stdout' : ( - {'IPKernelApp' : {'no_stdout' : True}}, - "redirect stdout to the null device"), - 'no-stderr' : ( - {'IPKernelApp' : {'no_stderr' : True}}, - "redirect stderr to the null device"), - 'pylab' : ( - {'IPKernelApp' : {'pylab' : 'auto'}}, - """Pre-load matplotlib and numpy for interactive use with - the default matplotlib backend."""), - 'trio-loop' : ( - {'InteractiveShell' : {'trio_loop' : False}}, - 'Enable Trio as main event loop.' - ), -}) +kernel_flags.update( + { + "no-stdout": ({"IPKernelApp": {"no_stdout": True}}, "redirect stdout to the null device"), + "no-stderr": ({"IPKernelApp": {"no_stderr": True}}, "redirect stderr to the null device"), + "pylab": ( + {"IPKernelApp": {"pylab": "auto"}}, + """Pre-load matplotlib and numpy for interactive use with + the default matplotlib backend.""", + ), + "trio-loop": ( + {"InteractiveShell": {"trio_loop": False}}, + "Enable Trio as main event loop.", + ), + } +) # inherit flags&aliases for any IPython shell apps kernel_aliases.update(shell_aliases) @@ -98,26 +105,28 @@ """ -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Application class for starting an IPython Kernel -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + -class IPKernelApp(BaseIPythonApplication, InteractiveShellApp, - ConnectionFileMixin): - name='ipython-kernel' +class IPKernelApp(BaseIPythonApplication, InteractiveShellApp, ConnectionFileMixin): + name = "ipython-kernel" aliases = Dict(kernel_aliases) flags = Dict(kernel_flags) classes = [IPythonKernel, ZMQInteractiveShell, ProfileDir, Session] # the kernel class, as an importstring - kernel_class = Type('ipykernel.ipkernel.IPythonKernel', - klass='ipykernel.kernelbase.Kernel', - help="""The Kernel subclass to be used. + kernel_class = Type( + "ipykernel.ipkernel.IPythonKernel", + klass="ipykernel.kernelbase.Kernel", + help="""The Kernel subclass to be used. This should allow easy re-use of the IPKernelApp entry point to configure and launch kernels other than IPython's own. - """).tag(config=True) + """, + ).tag(config=True) kernel = Any() - poller = Any() # don't restrict this even though current pollers are all Threads + poller = Any() # don't restrict this even though current pollers are all Threads heartbeat = Instance(Heartbeat, allow_none=True) context = Any() @@ -133,16 +142,16 @@ class IPKernelApp(BaseIPythonApplication, InteractiveShellApp, _ports = Dict() subcommands = { - 'install': ( - 'ipykernel.kernelspec.InstallIPythonKernelSpecApp', - 'Install the IPython kernel' + "install": ( + "ipykernel.kernelspec.InstallIPythonKernelSpecApp", + "Install the IPython kernel", ), } # connection info: connection_dir = Unicode() - @default('connection_dir') + @default("connection_dir") def _default_connection_dir(self): return jupyter_runtime_dir() @@ -158,10 +167,12 @@ def abs_connection_file(self): no_stderr = Bool(False, help="redirect stderr to the null device").tag(config=True) trio_loop = Bool(False, help="Set main event loop.").tag(config=True) quiet = Bool(True, help="Only send stdout/stderr to output stream").tag(config=True) - outstream_class = DottedObjectName('ipykernel.iostream.OutStream', - help="The importstring for the OutStream factory").tag(config=True) - displayhook_class = DottedObjectName('ipykernel.displayhook.ZMQDisplayHook', - help="The importstring for the DisplayHook factory").tag(config=True) + outstream_class = DottedObjectName( + "ipykernel.iostream.OutStream", help="The importstring for the OutStream factory" + ).tag(config=True) + displayhook_class = DottedObjectName( + "ipykernel.displayhook.ZMQDisplayHook", help="The importstring for the DisplayHook factory" + ).tag(config=True) capture_fd_output = Bool( True, @@ -170,14 +181,18 @@ def abs_connection_file(self): ).tag(config=True) # polling - parent_handle = Integer(int(os.environ.get('JPY_PARENT_PID') or 0), + parent_handle = Integer( + int(os.environ.get("JPY_PARENT_PID") or 0), help="""kill this process if its parent dies. On Windows, the argument specifies the HANDLE of the parent process, otherwise it is simply boolean. - """).tag(config=True) - interrupt = Integer(int(os.environ.get('JPY_INTERRUPT_EVENT') or 0), + """, + ).tag(config=True) + interrupt = Integer( + int(os.environ.get("JPY_INTERRUPT_EVENT") or 0), help="""ONLY USED ON WINDOWS Interrupt this process when the parent is signaled. - """).tag(config=True) + """, + ).tag(config=True) def init_crash_handler(self): sys.excepthook = self.excepthook @@ -187,7 +202,7 @@ def excepthook(self, etype, evalue, tb): traceback.print_exception(etype, evalue, tb, file=sys.__stderr__) def init_poller(self): - if sys.platform == 'win32': + if sys.platform == "win32": if self.interrupt or self.parent_handle: self.poller = ParentPollerWindows(self.interrupt, self.parent_handle) elif self.parent_handle and self.parent_handle != 1: @@ -197,13 +212,13 @@ def init_poller(self): self.poller = ParentPollerUnix() def _try_bind_socket(self, s, port): - iface = '%s://%s' % (self.transport, self.ip) - if self.transport == 'tcp': + iface = "%s://%s" % (self.transport, self.ip) + if self.transport == "tcp": if port <= 0: port = s.bind_to_random_port(iface) else: s.bind("tcp://%s:%i" % (self.ip, port)) - elif self.transport == 'ipc': + elif self.transport == "ipc": if port <= 0: port = 1 path = "%s-%i" % (self.ip, port) @@ -238,9 +253,17 @@ def write_connection_file(self): """write connection info to JSON file""" cf = self.abs_connection_file self.log.debug("Writing connection file: %s", cf) - write_connection_file(cf, ip=self.ip, key=self.session.key, transport=self.transport, - shell_port=self.shell_port, stdin_port=self.stdin_port, hb_port=self.hb_port, - iopub_port=self.iopub_port, control_port=self.control_port) + write_connection_file( + cf, + ip=self.ip, + key=self.session.key, + transport=self.transport, + shell_port=self.shell_port, + stdin_port=self.stdin_port, + hb_port=self.hb_port, + iopub_port=self.iopub_port, + control_port=self.control_port, + ) def cleanup_connection_file(self): cf = self.abs_connection_file @@ -254,9 +277,9 @@ def cleanup_connection_file(self): def init_connection_file(self): if not self.connection_file: - self.connection_file = "kernel-%s.json"%os.getpid() + self.connection_file = "kernel-%s.json" % os.getpid() try: - self.connection_file = filefind(self.connection_file, ['.', self.connection_dir]) + self.connection_file = filefind(self.connection_file, [".", self.connection_dir]) except OSError: self.log.debug("Connection file not found: %s", self.connection_file) # This means I own it, and I'll create it in this directory: @@ -267,7 +290,9 @@ def init_connection_file(self): try: self.load_connection_file() except Exception: - self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True) + self.log.error( + "Failed to load connection file: %r", self.connection_file, exc_info=True + ) self.exit(1) def init_sockets(self): @@ -287,12 +312,11 @@ def init_sockets(self): self.stdin_port = self._bind_socket(self.stdin_socket, self.stdin_port) self.log.debug("stdin ROUTER Channel on port: %i" % self.stdin_port) - if hasattr(zmq, 'ROUTER_HANDOVER'): + if hasattr(zmq, "ROUTER_HANDOVER"): # set router-handover to workaround zeromq reconnect problems # in certain rare circumstances # see ipython/ipykernel#270 and zeromq/libzmq#2892 - self.shell_socket.router_handover = \ - self.stdin_socket.router_handover = 1 + self.shell_socket.router_handover = self.stdin_socket.router_handover = 1 self.init_control(context) self.init_iopub(context) @@ -311,7 +335,7 @@ def init_control(self, context): if self.shell_socket.getsockopt(zmq.LAST_ENDPOINT): self.debug_shell_socket.connect(self.shell_socket.getsockopt(zmq.LAST_ENDPOINT)) - if hasattr(zmq, 'ROUTER_HANDOVER'): + if hasattr(zmq, "ROUTER_HANDOVER"): # set router-handover to workaround zeromq reconnect problems # in certain rare circumstances # see ipython/ipykernel#270 and zeromq/libzmq#2892 @@ -362,7 +386,7 @@ def close(self): if self.debug_shell_socket and not self.debug_shell_socket.closed: self.debug_shell_socket.close() - for channel in ('shell', 'control', 'stdin'): + for channel in ("shell", "control", "stdin"): self.log.debug("Closing %s channel", channel) socket = getattr(self, channel + "_socket", None) if socket and not socket.closed: @@ -374,8 +398,10 @@ def close(self): def log_connection_info(self): """display connection info, and store ports""" basename = os.path.basename(self.connection_file) - if basename == self.connection_file or \ - os.path.dirname(self.connection_file) == self.connection_dir: + if ( + basename == self.connection_file + or os.path.dirname(self.connection_file) == self.connection_dir + ): # use shortname tail = basename else: @@ -397,14 +423,18 @@ def log_connection_info(self): for line in lines: print(line, file=sys.__stdout__) - self._ports = dict(shell=self.shell_port, iopub=self.iopub_port, - stdin=self.stdin_port, hb=self.hb_port, - control=self.control_port) + self._ports = dict( + shell=self.shell_port, + iopub=self.iopub_port, + stdin=self.stdin_port, + hb=self.hb_port, + control=self.control_port, + ) def init_blackhole(self): """redirects stdout/stderr to devnull if necessary""" if self.no_stdout or self.no_stderr: - blackhole = open(os.devnull, 'w') + blackhole = open(os.devnull, "w") if self.no_stdout: sys.stdout = sys.__stdout__ = blackhole if self.no_stderr: @@ -423,23 +453,15 @@ def init_io(self): if not self.capture_fd_output: outstream_factory = partial(outstream_factory, watchfd=False) - sys.stdout = outstream_factory(self.session, self.iopub_thread, - 'stdout', - echo=e_stdout) + sys.stdout = outstream_factory(self.session, self.iopub_thread, "stdout", echo=e_stdout) if sys.stderr is not None: sys.stderr.flush() - sys.stderr = outstream_factory( - self.session, self.iopub_thread, "stderr", echo=e_stderr - ) + sys.stderr = outstream_factory(self.session, self.iopub_thread, "stderr", echo=e_stderr) if hasattr(sys.stderr, "_original_stdstream_copy"): for handler in self.log.handlers: - if isinstance(handler, StreamHandler) and ( - handler.stream.buffer.fileno() == 2 - ): - self.log.debug( - "Seeing logger to stderr, rerouting to raw filedescriptor." - ) + if isinstance(handler, StreamHandler) and (handler.stream.buffer.fileno() == 2): + self.log.debug("Seeing logger to stderr, rerouting to raw filedescriptor.") handler.stream = TextIOWrapper( FileIO(sys.stderr._original_stdstream_copy, "w") @@ -473,16 +495,20 @@ def patch_io(self): # change default file to __stderr__ from forwarded stderr faulthandler_enable = faulthandler.enable + def enable(file=sys.__stderr__, all_threads=True, **kwargs): return faulthandler_enable(file=file, all_threads=all_threads, **kwargs) faulthandler.enable = enable - if hasattr(faulthandler, 'register'): + if hasattr(faulthandler, "register"): faulthandler_register = faulthandler.register + def register(signum, file=sys.__stderr__, all_threads=True, chain=False, **kwargs): - return faulthandler_register(signum, file=file, all_threads=all_threads, - chain=chain, **kwargs) + return faulthandler_register( + signum, file=file, all_threads=all_threads, chain=chain, **kwargs + ) + faulthandler.register = register def init_signal(self): @@ -496,22 +522,22 @@ def init_kernel(self): self.control_thread.start() kernel_factory = self.kernel_class.instance - kernel = kernel_factory(parent=self, session=self.session, - control_stream=control_stream, - debugpy_stream=debugpy_stream, - debug_shell_socket=self.debug_shell_socket, - shell_stream=shell_stream, - control_thread=self.control_thread, - iopub_thread=self.iopub_thread, - iopub_socket=self.iopub_socket, - stdin_socket=self.stdin_socket, - log=self.log, - profile_dir=self.profile_dir, - user_ns=self.user_ns, + kernel = kernel_factory( + parent=self, + session=self.session, + control_stream=control_stream, + debugpy_stream=debugpy_stream, + debug_shell_socket=self.debug_shell_socket, + shell_stream=shell_stream, + control_thread=self.control_thread, + iopub_thread=self.iopub_thread, + iopub_socket=self.iopub_socket, + stdin_socket=self.stdin_socket, + log=self.log, + profile_dir=self.profile_dir, + user_ns=self.user_ns, ) - kernel.record_ports({ - name + '_port': port for name, port in self._ports.items() - }) + kernel.record_ports({name + "_port": port for name, port in self._ports.items()}) self.kernel = kernel # Allow the displayhook to get the execution count @@ -524,8 +550,8 @@ def init_gui_pylab(self): # this is higher priority than matplotlibrc, # but lower priority than anything else (mpl.use() for instance). # This only affects matplotlib >= 1.5 - if not os.environ.get('MPLBACKEND'): - os.environ['MPLBACKEND'] = 'module://matplotlib_inline.backend_inline' + if not os.environ.get("MPLBACKEND"): + os.environ["MPLBACKEND"] = "module://matplotlib_inline.backend_inline" # Provide a wrapper for :meth:`InteractiveShellApp.init_gui_pylab` # to ensure that any exception is printed straight to stderr. @@ -538,28 +564,28 @@ def init_gui_pylab(self): try: # replace error-sending traceback with stderr def print_tb(etype, evalue, stb): - print ("GUI event loop or pylab initialization failed", - file=sys.stderr) - print (shell.InteractiveTB.stb2text(stb), file=sys.stderr) + print("GUI event loop or pylab initialization failed", file=sys.stderr) + print(shell.InteractiveTB.stb2text(stb), file=sys.stderr) + shell._showtraceback = print_tb InteractiveShellApp.init_gui_pylab(self) finally: shell._showtraceback = _showtraceback def init_shell(self): - self.shell = getattr(self.kernel, 'shell', None) + self.shell = getattr(self.kernel, "shell", None) if self.shell: self.shell.configurables.append(self) def configure_tornado_logger(self): - """ Configure the tornado logging.Logger. + """Configure the tornado logging.Logger. Must set up the tornado logger or else tornado will call basicConfig for the root logger which makes the root logger go to the real sys.stderr instead of the capture streams. This function mimics the setup of logging.basicConfig. """ - logger = logging.getLogger('tornado') + logger = logging.getLogger("tornado") handler = logging.StreamHandler() formatter = logging.Formatter(logging.BASIC_FORMAT) handler.setFormatter(formatter) @@ -590,6 +616,7 @@ def _init_asyncio_patch(self): """ if sys.platform.startswith("win") and sys.version_info >= (3, 8): import asyncio + try: from asyncio import ( WindowsProactorEventLoopPolicy, @@ -611,7 +638,9 @@ def init_pdb(self): non-recoverable state. """ import pdb + from IPython.core import debugger + if hasattr(debugger, "InterruptiblePdb"): # Only available in newer IPython releases: debugger.Pdb = debugger.InterruptiblePdb @@ -666,6 +695,7 @@ def start(self): self.io_loop = ioloop.IOLoop.current() if self.trio_loop: from ipykernel.trio_runner import TrioRunner + tr = TrioRunner() tr.initialize(self.kernel, self.io_loop) try: @@ -689,5 +719,5 @@ def main(): app.start() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ipykernel/kernelbase.py b/ipykernel/kernelbase.py index c775398fc..12a3d93b0 100644 --- a/ipykernel/kernelbase.py +++ b/ipykernel/kernelbase.py @@ -24,8 +24,6 @@ SIGKILL = "windown-SIGKILL-sentinel" - - try: # jupyter_client >= 5, use tz-aware now from jupyter_client.session import utcnow as now @@ -39,8 +37,19 @@ from jupyter_client.session import Session from tornado import ioloop from tornado.queues import Queue, QueueEmpty -from traitlets import (Any, Bool, Dict, Float, Instance, Integer, List, Set, - Unicode, default, observe) +from traitlets import ( + Any, + Bool, + Dict, + Float, + Instance, + Integer, + List, + Set, + Unicode, + default, + observe, +) from traitlets.config.configurable import SingletonConfigurable from zmq.eventloop.zmqstream import ZMQStream @@ -51,14 +60,14 @@ class Kernel(SingletonConfigurable): - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Kernel interface - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # attribute to override with a GUI eventloop = Any(None) - @observe('eventloop') + @observe("eventloop") def _update_eventloop(self, change): """schedule call to eventloop from IOLoop""" loop = ioloop.IOLoop.current() @@ -66,7 +75,7 @@ def _update_eventloop(self, change): loop.add_callback(self.enter_eventloop) session = Instance(Session, allow_none=True) - profile_dir = Instance('IPython.core.profiledir.ProfileDir', allow_none=True) + profile_dir = Instance("IPython.core.profiledir.ProfileDir", allow_none=True) shell_stream = Instance(ZMQStream, allow_none=True) shell_streams = List( @@ -119,7 +128,7 @@ def _shell_streams_changed(self, change): int_id = Integer(-1) ident = Unicode() - @default('ident') + @default("ident") def _default_ident(self): return str(uuid.uuid4()) @@ -133,25 +142,27 @@ def _default_ident(self): # Experimental option to break in non-user code. # The ipykernel source is in the call stack, so the user # has to manipulate the step-over and step-into in a wize way. - debug_just_my_code = Bool(True, + debug_just_my_code = Bool( + True, help="""Set to False if you want to debug python standard and dependent libraries. - """ + """, ).tag(config=True) # track associations with current request # Private interface - _darwin_app_nap = Bool(True, + _darwin_app_nap = Bool( + True, help="""Whether to use appnope for compatibility with OS X App Nap. Only affects OS X >= 10.9. - """ + """, ).tag(config=True) # track associations with current request _allow_stdin = Bool(False) _parents = Dict({"shell": {}, "control": {}}) - _parent_ident = Dict({'shell': b'', 'control': b''}) + _parent_ident = Dict({"shell": b"", "control": b""}) @property def _parent_header(self): @@ -189,7 +200,7 @@ def _parent_header(self): causing significant delays, which can manifest as e.g. "Run all" in a notebook aborting some, but not all, messages after an error. - """ + """, ) # If the shutdown was requested over the network, we leave here the @@ -211,16 +222,26 @@ def _parent_header(self): execution_count = 0 msg_types = [ - 'execute_request', 'complete_request', - 'inspect_request', 'history_request', - 'comm_info_request', 'kernel_info_request', - 'connect_request', 'shutdown_request', - 'is_complete_request', 'interrupt_request', + "execute_request", + "complete_request", + "inspect_request", + "history_request", + "comm_info_request", + "kernel_info_request", + "connect_request", + "shutdown_request", + "is_complete_request", + "interrupt_request", # deprecated: - 'apply_request', + "apply_request", ] # add deprecated ipyparallel control messages - control_msg_types = msg_types + ['clear_request', 'abort_request', 'debug_request', 'usage_request'] + control_msg_types = msg_types + [ + "clear_request", + "abort_request", + "debug_request", + "usage_request", + ] def __init__(self, **kwargs): super().__init__(**kwargs) @@ -281,11 +302,11 @@ async def process_control(self, msg): self.log.debug("Control received: %s", msg) # Set the parent message for side effects. - self.set_parent(idents, msg, channel='control') - self._publish_status('busy', 'control') + self.set_parent(idents, msg, channel="control") + self._publish_status("busy", "control") - header = msg['header'] - msg_type = header['msg_type'] + header = msg["header"] + msg_type = header["msg_type"] handler = self.control_handlers.get(msg_type, None) if handler is None: @@ -300,7 +321,7 @@ async def process_control(self, msg): sys.stdout.flush() sys.stderr.flush() - self._publish_status('idle', 'control') + self._publish_status("idle", "control") # flush to ensure reply is sent self.control_stream.flush(zmq.POLLOUT) @@ -309,7 +330,7 @@ def should_handle(self, stream, msg, idents): Allows subclasses to prevent handling of certain messages (e.g. aborted requests). """ - msg_id = msg['header']['msg_id'] + msg_id = msg["header"]["msg_id"] if msg_id in self.aborted: # is it safe to assume a msg_id will not be resubmitted? self.aborted.remove(msg_id) @@ -331,15 +352,15 @@ async def dispatch_shell(self, msg): return # Set the parent message for side effects. - self.set_parent(idents, msg, channel='shell') - self._publish_status('busy', 'shell') + self.set_parent(idents, msg, channel="shell") + self._publish_status("busy", "shell") - msg_type = msg['header']['msg_type'] + msg_type = msg["header"]["msg_type"] # Only abort execute requests - if self._aborting and msg_type == 'execute_request': + if self._aborting and msg_type == "execute_request": self._send_abort_reply(self.shell_stream, msg, idents) - self._publish_status('idle', 'shell') + self._publish_status("idle", "shell") # flush to ensure reply is sent before # handling the next request self.shell_stream.flush(zmq.POLLOUT) @@ -348,8 +369,8 @@ async def dispatch_shell(self, msg): # Print some info about this message and leave a '--->' marker, so it's # easier to trace visually the message chain when debugging. Each # handler prints its message at the end. - self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type) - self.log.debug(' Content: %s\n --->\n ', msg['content']) + self.log.debug("\n*** MESSAGE TYPE:%s***", msg_type) + self.log.debug(" Content: %s\n --->\n ", msg["content"]) if not self.should_handle(self.shell_stream, msg, idents): return @@ -380,7 +401,7 @@ async def dispatch_shell(self, msg): sys.stdout.flush() sys.stderr.flush() - self._publish_status('idle', 'shell') + self._publish_status("idle", "shell") # flush to ensure reply is sent before # handling the next request self.shell_stream.flush(zmq.POLLOUT) @@ -478,7 +499,8 @@ async def dispatch_queue(self): help="""Monotonic counter of messages """, ) - @default('_message_counter') + + @default("_message_counter") def _message_counter_default(self): return itertools.count() @@ -520,8 +542,7 @@ def start(self): ) # publish idle status - self._publish_status('starting', 'shell') - + self._publish_status("starting", "shell") def record_ports(self, ports): """Record the ports that this kernel is using. @@ -531,16 +552,19 @@ def record_ports(self, ports): """ self._recorded_ports = ports - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Kernel request handlers - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def _publish_execute_input(self, code, parent, execution_count): """Publish the code request on the iopub stream.""" - self.session.send(self.iopub_socket, 'execute_input', - {'code':code, 'execution_count': execution_count}, - parent=parent, ident=self._topic('execute_input') + self.session.send( + self.iopub_socket, + "execute_input", + {"code": code, "execution_count": execution_count}, + parent=parent, + ident=self._topic("execute_input"), ) def _publish_status(self, status, channel, parent=None): @@ -562,7 +586,7 @@ def _publish_debug_event(self, event): ident=self._topic("debug_event"), ) - def set_parent(self, ident, parent, channel='shell'): + def set_parent(self, ident, parent, channel="shell"): """Set the current parent request Side effects (IOPub messages) and replies are associated with @@ -591,8 +615,18 @@ def get_parent(self, channel="shell"): """ return self._parents.get(channel, {}) - def send_response(self, stream, msg_or_type, content=None, ident=None, - buffers=None, track=False, header=None, metadata=None, channel='shell'): + def send_response( + self, + stream, + msg_or_type, + content=None, + ident=None, + buffers=None, + track=False, + header=None, + metadata=None, + channel="shell", + ): """Send a response to the message we're currently processing. This accepts all the parameters of :meth:`jupyter_client.session.Session.send` @@ -621,7 +655,7 @@ def init_metadata(self, parent): # FIXME: `started` is part of ipyparallel # Remove for ipykernel 5.0 return { - 'started': now(), + "started": now(), } def finish_metadata(self, parent, metadata, reply_content): @@ -635,18 +669,18 @@ async def execute_request(self, stream, ident, parent): """handle an execute_request""" try: - content = parent['content'] - code = content['code'] - silent = content['silent'] - store_history = content.get('store_history', not silent) - user_expressions = content.get('user_expressions', {}) - allow_stdin = content.get('allow_stdin', False) + content = parent["content"] + code = content["code"] + silent = content["silent"] + store_history = content.get("store_history", not silent) + user_expressions = content.get("user_expressions", {}) + allow_stdin = content.get("allow_stdin", False) except Exception: self.log.error("Got bad msg: ") self.log.error("%s", parent) return - stop_on_error = content.get('stop_on_error', True) + stop_on_error = content.get("stop_on_error", True) metadata = self.init_metadata(parent) @@ -657,8 +691,11 @@ async def execute_request(self, stream, ident, parent): self._publish_execute_input(code, parent, self.execution_count) reply_content = self.do_execute( - code, silent, store_history, - user_expressions, allow_stdin, + code, + silent, + store_history, + user_expressions, + allow_stdin, ) if inspect.isawaitable(reply_content): reply_content = await reply_content @@ -676,25 +713,25 @@ async def execute_request(self, stream, ident, parent): reply_content = json_clean(reply_content) metadata = self.finish_metadata(parent, metadata, reply_content) - reply_msg = self.session.send(stream, 'execute_reply', - reply_content, parent, metadata=metadata, - ident=ident) + reply_msg = self.session.send( + stream, "execute_reply", reply_content, parent, metadata=metadata, ident=ident + ) self.log.debug("%s", reply_msg) - if not silent and reply_msg['content']['status'] == 'error' and stop_on_error: + if not silent and reply_msg["content"]["status"] == "error" and stop_on_error: self._abort_queues() - def do_execute(self, code, silent, store_history=True, - user_expressions=None, allow_stdin=False): - """Execute user code. Must be overridden by subclasses. - """ + def do_execute( + self, code, silent, store_history=True, user_expressions=None, allow_stdin=False + ): + """Execute user code. Must be overridden by subclasses.""" raise NotImplementedError async def complete_request(self, stream, ident, parent): - content = parent['content'] - code = content['code'] - cursor_pos = content['cursor_pos'] + content = parent["content"] + code = content["code"] + cursor_pos = content["cursor_pos"] matches = self.do_complete(code, cursor_pos) if inspect.isawaitable(matches): @@ -704,88 +741,94 @@ async def complete_request(self, stream, ident, parent): self.session.send(stream, "complete_reply", matches, parent, ident) def do_complete(self, code, cursor_pos): - """Override in subclasses to find completions. - """ - return {'matches' : [], - 'cursor_end' : cursor_pos, - 'cursor_start' : cursor_pos, - 'metadata' : {}, - 'status' : 'ok'} + """Override in subclasses to find completions.""" + return { + "matches": [], + "cursor_end": cursor_pos, + "cursor_start": cursor_pos, + "metadata": {}, + "status": "ok", + } async def inspect_request(self, stream, ident, parent): - content = parent['content'] + content = parent["content"] reply_content = self.do_inspect( - content['code'], content['cursor_pos'], - content.get('detail_level', 0), - set(content.get('omit_sections', [])), + content["code"], + content["cursor_pos"], + content.get("detail_level", 0), + set(content.get("omit_sections", [])), ) if inspect.isawaitable(reply_content): reply_content = await reply_content # Before we send this object over, we scrub it for JSON usage reply_content = json_clean(reply_content) - msg = self.session.send(stream, 'inspect_reply', - reply_content, parent, ident) + msg = self.session.send(stream, "inspect_reply", reply_content, parent, ident) self.log.debug("%s", msg) def do_inspect(self, code, cursor_pos, detail_level=0, omit_sections=()): - """Override in subclasses to allow introspection. - """ - return {'status': 'ok', 'data': {}, 'metadata': {}, 'found': False} + """Override in subclasses to allow introspection.""" + return {"status": "ok", "data": {}, "metadata": {}, "found": False} async def history_request(self, stream, ident, parent): - content = parent['content'] + content = parent["content"] reply_content = self.do_history(**content) if inspect.isawaitable(reply_content): reply_content = await reply_content reply_content = json_clean(reply_content) - msg = self.session.send(stream, 'history_reply', - reply_content, parent, ident) + msg = self.session.send(stream, "history_reply", reply_content, parent, ident) self.log.debug("%s", msg) - def do_history(self, hist_access_type, output, raw, session=None, start=None, - stop=None, n=None, pattern=None, unique=False): - """Override in subclasses to access history. - """ - return {'status': 'ok', 'history': []} + def do_history( + self, + hist_access_type, + output, + raw, + session=None, + start=None, + stop=None, + n=None, + pattern=None, + unique=False, + ): + """Override in subclasses to access history.""" + return {"status": "ok", "history": []} async def connect_request(self, stream, ident, parent): if self._recorded_ports is not None: content = self._recorded_ports.copy() else: content = {} - content['status'] = 'ok' - msg = self.session.send(stream, 'connect_reply', - content, parent, ident) + content["status"] = "ok" + msg = self.session.send(stream, "connect_reply", content, parent, ident) self.log.debug("%s", msg) @property def kernel_info(self): return { - 'protocol_version': kernel_protocol_version, - 'implementation': self.implementation, - 'implementation_version': self.implementation_version, - 'language_info': self.language_info, - 'banner': self.banner, - 'help_links': self.help_links, + "protocol_version": kernel_protocol_version, + "implementation": self.implementation, + "implementation_version": self.implementation_version, + "language_info": self.language_info, + "banner": self.banner, + "help_links": self.help_links, } async def kernel_info_request(self, stream, ident, parent): - content = {'status': 'ok'} + content = {"status": "ok"} content.update(self.kernel_info) - msg = self.session.send(stream, 'kernel_info_reply', - content, parent, ident) + msg = self.session.send(stream, "kernel_info_reply", content, parent, ident) self.log.debug("%s", msg) async def comm_info_request(self, stream, ident, parent): - content = parent['content'] - target_name = content.get('target_name', None) + content = parent["content"] + target_name = content.get("target_name", None) # Should this be moved to ipkernel? - if hasattr(self, 'comm_manager'): + if hasattr(self, "comm_manager"): comms = { k: dict(target_name=v.target_name) for (k, v) in self.comm_manager.comms.items() @@ -793,9 +836,8 @@ async def comm_info_request(self, stream, ident, parent): } else: comms = {} - reply_content = dict(comms=comms, status='ok') - msg = self.session.send(stream, 'comm_info_reply', - reply_content, parent, ident) + reply_content = dict(comms=comms, status="ok") + msg = self.session.send(stream, "comm_info_reply", reply_content, parent, ident) self.log.debug("%s", msg) def _send_interupt_children(self): @@ -819,27 +861,25 @@ def _send_interupt_children(self): async def interrupt_request(self, stream, ident, parent): self._send_interupt_children() - content = parent['content'] - self.session.send(stream, 'interrupt_reply', content, parent, ident=ident) + content = parent["content"] + self.session.send(stream, "interrupt_reply", content, parent, ident=ident) return async def shutdown_request(self, stream, ident, parent): - content = self.do_shutdown(parent['content']['restart']) + content = self.do_shutdown(parent["content"]["restart"]) if inspect.isawaitable(content): content = await content - self.session.send(stream, 'shutdown_reply', content, parent, ident=ident) + self.session.send(stream, "shutdown_reply", content, parent, ident=ident) # same content, but different msg_id for broadcasting on IOPub - self._shutdown_message = self.session.msg('shutdown_reply', - content, parent - ) + self._shutdown_message = self.session.msg("shutdown_reply", content, parent) await self._at_shutdown() - self.log.debug('Stopping control ioloop') + self.log.debug("Stopping control ioloop") control_io_loop = self.control_stream.io_loop control_io_loop.add_callback(control_io_loop.stop) - self.log.debug('Stopping shell ioloop') + self.log.debug("Stopping shell ioloop") shell_io_loop = self.shell_stream.io_loop shell_io_loop.add_callback(shell_io_loop.stop) @@ -847,34 +887,31 @@ def do_shutdown(self, restart): """Override in subclasses to do things when the frontend shuts down the kernel. """ - return {'status': 'ok', 'restart': restart} + return {"status": "ok", "restart": restart} async def is_complete_request(self, stream, ident, parent): - content = parent['content'] - code = content['code'] + content = parent["content"] + code = content["code"] reply_content = self.do_is_complete(code) if inspect.isawaitable(reply_content): reply_content = await reply_content reply_content = json_clean(reply_content) - reply_msg = self.session.send(stream, 'is_complete_reply', - reply_content, parent, ident) + reply_msg = self.session.send(stream, "is_complete_reply", reply_content, parent, ident) self.log.debug("%s", reply_msg) def do_is_complete(self, code): - """Override in subclasses to find completions. - """ - return { 'status' : 'unknown'} + """Override in subclasses to find completions.""" + return {"status": "unknown"} async def debug_request(self, stream, ident, parent): - content = parent['content'] + content = parent["content"] reply_content = self.do_debug_request(content) if inspect.isawaitable(reply_content): reply_content = await reply_content reply_content = json_clean(reply_content) - reply_msg = self.session.send(stream, 'debug_reply', reply_content, - parent, ident) + reply_msg = self.session.send(stream, "debug_reply", reply_content, parent, ident) self.log.debug("%s", reply_msg) # Taken from https://github.com/jupyter-server/jupyter-resource-usage/blob/e6ec53fa69fdb6de8e878974bcff006310658408/jupyter_resource_usage/metrics.py#L16 @@ -892,37 +929,38 @@ def get_process_metric_value(self, process, name, attribute=None): return None async def usage_request(self, stream, ident, parent): - reply_content = { - 'hostname': socket.gethostname() - } + reply_content = {"hostname": socket.gethostname()} current_process = psutil.Process() all_processes = [current_process] + current_process.children(recursive=True) process_metric_value = self.get_process_metric_value - reply_content['kernel_cpu'] = sum([process_metric_value(process, 'cpu_percent', None) for process in all_processes]) - reply_content['kernel_memory'] = sum([process_metric_value(process, 'memory_info', 'rss') for process in all_processes]) + reply_content["kernel_cpu"] = sum( + [process_metric_value(process, "cpu_percent", None) for process in all_processes] + ) + reply_content["kernel_memory"] = sum( + [process_metric_value(process, "memory_info", "rss") for process in all_processes] + ) cpu_percent = psutil.cpu_percent() # https://psutil.readthedocs.io/en/latest/index.html?highlight=cpu#psutil.cpu_percent # The first time cpu_percent is called it will return a meaningless 0.0 value which you are supposed to ignore. if cpu_percent != None and cpu_percent != 0.0: - reply_content['host_cpu_percent'] = cpu_percent - reply_content['host_virtual_memory'] = dict(psutil.virtual_memory()._asdict()) - reply_msg = self.session.send(stream, 'usage_reply', reply_content, - parent, ident) + reply_content["host_cpu_percent"] = cpu_percent + reply_content["host_virtual_memory"] = dict(psutil.virtual_memory()._asdict()) + reply_msg = self.session.send(stream, "usage_reply", reply_content, parent, ident) self.log.debug("%s", reply_msg) async def do_debug_request(self, msg): raise NotImplementedError - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Engine methods (DEPRECATED) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- async def apply_request(self, stream, ident, parent): self.log.warning("apply_request is deprecated in kernel_base, moving to ipyparallel.") try: - content = parent['content'] - bufs = parent['buffers'] - msg_id = parent['header']['msg_id'] + content = parent["content"] + bufs = parent["buffers"] + msg_id = parent["header"]["msg_id"] except Exception: self.log.error("Got bad msg: %s", parent, exc_info=True) return @@ -937,21 +975,30 @@ async def apply_request(self, stream, ident, parent): md = self.finish_metadata(parent, md, reply_content) - self.session.send(stream, 'apply_reply', reply_content, - parent=parent, ident=ident,buffers=result_buf, metadata=md) + self.session.send( + stream, + "apply_reply", + reply_content, + parent=parent, + ident=ident, + buffers=result_buf, + metadata=md, + ) def do_apply(self, content, bufs, msg_id, reply_metadata): """DEPRECATED""" raise NotImplementedError - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Control messages (DEPRECATED) - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- async def abort_request(self, stream, ident, parent): """abort a specific msg by id""" - self.log.warning("abort_request is deprecated in kernel_base. It is only part of IPython parallel") - msg_ids = parent['content'].get('msg_ids', None) + self.log.warning( + "abort_request is deprecated in kernel_base. It is only part of IPython parallel" + ) + msg_ids = parent["content"].get("msg_ids", None) if isinstance(msg_ids, str): msg_ids = [msg_ids] if not msg_ids: @@ -959,25 +1006,27 @@ async def abort_request(self, stream, ident, parent): for mid in msg_ids: self.aborted.add(str(mid)) - content = dict(status='ok') - reply_msg = self.session.send(stream, 'abort_reply', content=content, - parent=parent, ident=ident) + content = dict(status="ok") + reply_msg = self.session.send( + stream, "abort_reply", content=content, parent=parent, ident=ident + ) self.log.debug("%s", reply_msg) async def clear_request(self, stream, idents, parent): """Clear our namespace.""" - self.log.warning("clear_request is deprecated in kernel_base. It is only part of IPython parallel") + self.log.warning( + "clear_request is deprecated in kernel_base. It is only part of IPython parallel" + ) content = self.do_clear() - self.session.send(stream, 'clear_reply', ident=idents, parent=parent, - content = content) + self.session.send(stream, "clear_reply", ident=idents, parent=parent, content=content) def do_clear(self): """DEPRECATED since 4.0.3""" raise NotImplementedError - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Protected interface - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def _topic(self, topic): """prefixed topic for IOPub messages""" @@ -1010,15 +1059,11 @@ async def stop_aborting(): # if we have a delay, give messages this long to arrive on the queue # before we stop aborting requests - asyncio.get_event_loop().call_later( - self.stop_on_error_timeout, schedule_stop_aborting - ) + asyncio.get_event_loop().call_later(self.stop_on_error_timeout, schedule_stop_aborting) def _send_abort_reply(self, stream, msg, idents): """Send a reply to an aborted request""" - self.log.info( - f"Aborting {msg['header']['msg_id']}: {msg['header']['msg_type']}" - ) + self.log.info(f"Aborting {msg['header']['msg_id']}: {msg['header']['msg_type']}") reply_type = msg["header"]["msg_type"].rsplit("_", 1)[0] + "_reply" status = {"status": "aborted"} md = self.init_metadata(msg) @@ -1026,17 +1071,22 @@ def _send_abort_reply(self, stream, msg, idents): md.update(status) self.session.send( - stream, reply_type, metadata=md, - content=status, parent=msg, ident=idents, + stream, + reply_type, + metadata=md, + content=status, + parent=msg, + ident=idents, ) def _no_raw_input(self): """Raise StdinNotImplementedError if active frontend doesn't support stdin.""" - raise StdinNotImplementedError("raw_input was called, but this " - "frontend does not support stdin.") + raise StdinNotImplementedError( + "raw_input was called, but this " "frontend does not support stdin." + ) - def getpass(self, prompt='', stream=None): + def getpass(self, prompt="", stream=None): """Forward getpass to frontends Raises @@ -1062,7 +1112,7 @@ def getpass(self, prompt='', stream=None): password=True, ) - def raw_input(self, prompt=''): + def raw_input(self, prompt=""): """Forward raw_input to frontends Raises @@ -1097,8 +1147,7 @@ def _input_request(self, prompt, ident, parent, password=False): # Send the input request. content = json_clean(dict(prompt=prompt, password=password)) - self.session.send(self.stdin_socket, 'input_request', content, parent, - ident=ident) + self.session.send(self.stdin_socket, "input_request", content, parent, ident=ident) # Await a response. while True: @@ -1109,9 +1158,7 @@ def _input_request(self, prompt, ident, parent, password=False): # zmq.select() is also uninterruptible, but at least this # way reads get noticed immediately and KeyboardInterrupts # get noticed fairly quickly by human response time standards. - rlist, _, xlist = zmq.select( - [self.stdin_socket], [], [self.stdin_socket], 0.01 - ) + rlist, _, xlist = zmq.select([self.stdin_socket], [], [self.stdin_socket], 0.01) if rlist or xlist: ident, reply = self.session.recv(self.stdin_socket) if (ident, reply) != (None, None): @@ -1126,8 +1173,8 @@ def _input_request(self, prompt, ident, parent, password=False): value = reply["content"]["value"] except Exception: self.log.error("Bad input_reply: %s", parent) - value = '' - if value == '\x04': + value = "" + if value == "\x04": # EOF raise EOFError return value @@ -1194,8 +1241,7 @@ async def _progressively_terminate_all_children(self): await asyncio.sleep(delay) async def _at_shutdown(self): - """Actions taken at shutdown by the kernel, called by python's atexit. - """ + """Actions taken at shutdown by the kernel, called by python's atexit.""" try: await self._progressively_terminate_all_children() except Exception as e: diff --git a/ipykernel/kernelspec.py b/ipykernel/kernelspec.py index 34e4960b0..585d87127 100644 --- a/ipykernel/kernelspec.py +++ b/ipykernel/kernelspec.py @@ -17,10 +17,10 @@ pjoin = os.path.join -KERNEL_NAME = 'python%i' % sys.version_info[0] +KERNEL_NAME = "python%i" % sys.version_info[0] # path to kernelspec resources -RESOURCES = pjoin(os.path.dirname(__file__), 'resources') +RESOURCES = pjoin(os.path.dirname(__file__), "resources") def make_ipkernel_cmd(mod="ipykernel_launcher", executable=None, extra_arguments=None): @@ -42,7 +42,7 @@ def make_ipkernel_cmd(mod="ipykernel_launcher", executable=None, extra_arguments if executable is None: executable = sys.executable extra_arguments = extra_arguments or [] - arguments = [executable, '-m', mod, '-f', '{connection_file}'] + arguments = [executable, "-m", mod, "-f", "{connection_file}"] arguments.extend(extra_arguments) return arguments @@ -51,10 +51,10 @@ def make_ipkernel_cmd(mod="ipykernel_launcher", executable=None, extra_arguments def get_kernel_dict(extra_arguments=None): """Construct dict for kernel.json""" return { - 'argv': make_ipkernel_cmd(extra_arguments=extra_arguments), - 'display_name': 'Python %i (ipykernel)' % sys.version_info[0], - 'language': 'python', - 'metadata': { 'debugger': _is_debugpy_available} + "argv": make_ipkernel_cmd(extra_arguments=extra_arguments), + "display_name": "Python %i (ipykernel)" % sys.version_info[0], + "language": "python", + "metadata": {"debugger": _is_debugpy_available}, } @@ -67,7 +67,7 @@ def write_kernel_spec(path=None, overrides=None, extra_arguments=None): The path to the kernelspec is always returned. """ if path is None: - path = os.path.join(tempfile.mkdtemp(suffix='_kernels'), KERNEL_NAME) + path = os.path.join(tempfile.mkdtemp(suffix="_kernels"), KERNEL_NAME) # stage resources shutil.copytree(RESOURCES, path) @@ -82,14 +82,21 @@ def write_kernel_spec(path=None, overrides=None, extra_arguments=None): if overrides: kernel_dict.update(overrides) - with open(pjoin(path, 'kernel.json'), 'w') as f: + with open(pjoin(path, "kernel.json"), "w") as f: json.dump(kernel_dict, f, indent=1) return path -def install(kernel_spec_manager=None, user=False, kernel_name=KERNEL_NAME, display_name=None, - prefix=None, profile=None, env=None): +def install( + kernel_spec_manager=None, + user=False, + kernel_name=KERNEL_NAME, + display_name=None, + prefix=None, + profile=None, + env=None, +): """Install the IPython kernelspec for Jupyter Parameters @@ -132,18 +139,20 @@ def install(kernel_spec_manager=None, user=False, kernel_name=KERNEL_NAME, displ extra_arguments = ["--profile", profile] if not display_name: # add the profile to the default display name - overrides["display_name"] = 'Python %i [profile=%s]' % (sys.version_info[0], profile) + overrides["display_name"] = "Python %i [profile=%s]" % (sys.version_info[0], profile) else: extra_arguments = None if env: - overrides['env'] = env + overrides["env"] = env path = write_kernel_spec(overrides=overrides, extra_arguments=extra_arguments) dest = kernel_spec_manager.install_kernel_spec( - path, kernel_name=kernel_name, user=user, prefix=prefix) + path, kernel_name=kernel_name, user=user, prefix=prefix + ) # cleanup afterward shutil.rmtree(path) return dest + # Entrypoint from traitlets.config import Application @@ -151,7 +160,8 @@ def install(kernel_spec_manager=None, user=False, kernel_name=KERNEL_NAME, displ class InstallIPythonKernelSpecApp(Application): """Dummy app wrapping argparse""" - name = 'ipython-kernel-install' + + name = "ipython-kernel-install" def initialize(self, argv=None): if argv is None: @@ -160,33 +170,67 @@ def initialize(self, argv=None): def start(self): import argparse - parser = argparse.ArgumentParser(prog=self.name, - description="Install the IPython kernel spec.") - parser.add_argument('--user', action='store_true', - help="Install for the current user instead of system-wide") - parser.add_argument('--name', type=str, default=KERNEL_NAME, + + parser = argparse.ArgumentParser( + prog=self.name, description="Install the IPython kernel spec." + ) + parser.add_argument( + "--user", + action="store_true", + help="Install for the current user instead of system-wide", + ) + parser.add_argument( + "--name", + type=str, + default=KERNEL_NAME, help="Specify a name for the kernelspec." - " This is needed to have multiple IPython kernels at the same time.") - parser.add_argument('--display-name', type=str, + " This is needed to have multiple IPython kernels at the same time.", + ) + parser.add_argument( + "--display-name", + type=str, help="Specify the display name for the kernelspec." - " This is helpful when you have multiple IPython kernels.") - parser.add_argument('--profile', type=str, + " This is helpful when you have multiple IPython kernels.", + ) + parser.add_argument( + "--profile", + type=str, help="Specify an IPython profile to load. " - "This can be used to create custom versions of the kernel.") - parser.add_argument('--prefix', type=str, + "This can be used to create custom versions of the kernel.", + ) + parser.add_argument( + "--prefix", + type=str, help="Specify an install prefix for the kernelspec." - " This is needed to install into a non-default location, such as a conda/virtual-env.") - parser.add_argument('--sys-prefix', action='store_const', const=sys.prefix, dest='prefix', + " This is needed to install into a non-default location, such as a conda/virtual-env.", + ) + parser.add_argument( + "--sys-prefix", + action="store_const", + const=sys.prefix, + dest="prefix", help="Install to Python's sys.prefix." - " Shorthand for --prefix='%s'. For use in conda/virtual-envs." % sys.prefix) - parser.add_argument('--env', action='append', nargs=2, metavar=('ENV', 'VALUE'), - help="Set environment variables for the kernel.") + " Shorthand for --prefix='%s'. For use in conda/virtual-envs." % sys.prefix, + ) + parser.add_argument( + "--env", + action="append", + nargs=2, + metavar=("ENV", "VALUE"), + help="Set environment variables for the kernel.", + ) opts = parser.parse_args(self.argv) if opts.env: - opts.env = {k:v for (k, v) in opts.env} + opts.env = {k: v for (k, v) in opts.env} try: - dest = install(user=opts.user, kernel_name=opts.name, profile=opts.profile, - prefix=opts.prefix, display_name=opts.display_name, env=opts.env) + dest = install( + user=opts.user, + kernel_name=opts.name, + profile=opts.profile, + prefix=opts.prefix, + display_name=opts.display_name, + env=opts.env, + ) except OSError as e: if e.errno == errno.EACCES: print(e, file=sys.stderr) @@ -197,5 +241,5 @@ def start(self): print("Installed kernelspec %s in %s" % (opts.name, dest)) -if __name__ == '__main__': +if __name__ == "__main__": InstallIPythonKernelSpecApp.launch_instance() diff --git a/ipykernel/log.py b/ipykernel/log.py index d0b03cc32..66bb6722c 100644 --- a/ipykernel/log.py +++ b/ipykernel/log.py @@ -1,24 +1,28 @@ +import warnings + from zmq.log.handlers import PUBHandler -import warnings -warnings.warn("ipykernel.log is deprecated. It has moved to ipyparallel.engine.log", +warnings.warn( + "ipykernel.log is deprecated. It has moved to ipyparallel.engine.log", DeprecationWarning, - stacklevel=2 + stacklevel=2, ) + class EnginePUBHandler(PUBHandler): """A simple PUBHandler subclass that sets root_topic""" - engine=None + + engine = None def __init__(self, engine, *args, **kwargs): - PUBHandler.__init__(self,*args, **kwargs) + PUBHandler.__init__(self, *args, **kwargs) self.engine = engine @property def root_topic(self): """this is a property, in case the handler is created before the engine gets registered with an id""" - if isinstance(getattr(self.engine, 'id', None), int): - return "engine.%i"%self.engine.id + if isinstance(getattr(self.engine, "id", None), int): + return "engine.%i" % self.engine.id else: return "engine" diff --git a/ipykernel/parentpoller.py b/ipykernel/parentpoller.py index 4a2124407..d120f5000 100644 --- a/ipykernel/parentpoller.py +++ b/ipykernel/parentpoller.py @@ -9,15 +9,15 @@ import platform import signal import time +import warnings from _thread import interrupt_main # Py 3 from threading import Thread from traitlets.log import get_logger -import warnings class ParentPollerUnix(Thread): - """ A Unix-specific daemon thread that terminates the program immediately + """A Unix-specific daemon thread that terminates the program immediately when the parent process no longer exists. """ @@ -28,6 +28,7 @@ def __init__(self): def run(self): # We cannot use os.waitpid because it works only for child processes. from errno import EINTR + while True: try: if os.getppid() == 1: @@ -41,13 +42,13 @@ def run(self): class ParentPollerWindows(Thread): - """ A Windows-specific daemon thread that listens for a special event that + """A Windows-specific daemon thread that listens for a special event that signals an interrupt and, optionally, terminates the program immediately when the parent process no longer exists. """ def __init__(self, interrupt_handle=None, parent_handle=None): - """ Create the poller. At least one of the optional parameters must be + """Create the poller. At least one of the optional parameters must be provided. Parameters @@ -59,7 +60,7 @@ def __init__(self, interrupt_handle=None, parent_handle=None): If provided, the program will terminate immediately when this handle is signaled. """ - assert(interrupt_handle or parent_handle) + assert interrupt_handle or parent_handle super().__init__() if ctypes is None: raise ImportError("ParentPollerWindows requires ctypes") @@ -68,12 +69,11 @@ def __init__(self, interrupt_handle=None, parent_handle=None): self.parent_handle = parent_handle def run(self): - """ Run the poll loop. This method never returns. - """ + """Run the poll loop. This method never returns.""" try: - from _winapi import WAIT_OBJECT_0, INFINITE + from _winapi import INFINITE, WAIT_OBJECT_0 except ImportError: - from _subprocess import WAIT_OBJECT_0, INFINITE + from _subprocess import INFINITE, WAIT_OBJECT_0 # Build the list of handle to listen on. handles = [] @@ -82,15 +82,16 @@ def run(self): if self.parent_handle: handles.append(self.parent_handle) arch = platform.architecture()[0] - c_int = ctypes.c_int64 if arch.startswith('64') else ctypes.c_int + c_int = ctypes.c_int64 if arch.startswith("64") else ctypes.c_int # Listen forever. while True: result = ctypes.windll.kernel32.WaitForMultipleObjects( - len(handles), # nCount - (c_int * len(handles))(*handles), # lpHandles - False, # bWaitAll - INFINITE) # dwMilliseconds + len(handles), # nCount + (c_int * len(handles))(*handles), # lpHandles + False, # bWaitAll + INFINITE, + ) # dwMilliseconds if WAIT_OBJECT_0 <= result < len(handles): handle = handles[result - WAIT_OBJECT_0] @@ -106,8 +107,10 @@ def run(self): os._exit(1) elif result < 0: # wait failed, just give up and stop polling. - warnings.warn("""Parent poll failed. If the frontend dies, + warnings.warn( + """Parent poll failed. If the frontend dies, the kernel may be left running. Please let us know about your system (bitness, Python, etc.) at - ipython-dev@scipy.org""") + ipython-dev@scipy.org""" + ) return diff --git a/ipykernel/pickleutil.py b/ipykernel/pickleutil.py index 834abea4d..82c588311 100644 --- a/ipykernel/pickleutil.py +++ b/ipykernel/pickleutil.py @@ -5,41 +5,44 @@ import typing import warnings -warnings.warn("ipykernel.pickleutil is deprecated. It has moved to ipyparallel.", +warnings.warn( + "ipykernel.pickleutil is deprecated. It has moved to ipyparallel.", DeprecationWarning, - stacklevel=2 + stacklevel=2, ) import copy -import sys import pickle +import sys from types import FunctionType -from traitlets.utils.importstring import import_item - # This registers a hook when it's imported from ipyparallel.serialize import codeutil # noqa F401 - from traitlets.log import get_logger +from traitlets.utils.importstring import import_item buffer = memoryview class_type = type PICKLE_PROTOCOL = pickle.DEFAULT_PROTOCOL + def _get_cell_type(a=None): """the type of a closure cell doesn't seem to be importable, so just create one """ + def inner(): return a + return type(inner.__closure__[0]) + cell_type = _get_cell_type() -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Functions -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- def interactive(f): @@ -51,12 +54,15 @@ def interactive(f): # build new FunctionType, so it can have the right globals # interactive functions never have closures, that's kind of the point if isinstance(f, FunctionType): - mainmod = __import__('__main__') - f = FunctionType(f.__code__, mainmod.__dict__, - f.__name__, f.__defaults__, + mainmod = __import__("__main__") + f = FunctionType( + f.__code__, + mainmod.__dict__, + f.__name__, + f.__defaults__, ) # associate with __main__ for uncanning - f.__module__ = '__main__' + f.__module__ = "__main__" return f @@ -84,6 +90,7 @@ def use_dill(): # disable special function handling, let dill take care of it can_map.pop(FunctionType, None) + def use_cloudpickle(): """use cloudpickle to expand serialization support @@ -105,9 +112,9 @@ def use_cloudpickle(): can_map.pop(FunctionType, None) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Classes -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- class CannedObject: @@ -152,14 +159,15 @@ def get_object(self, g=None): class Reference(CannedObject): """object for wrapping a remote reference by name.""" + def __init__(self, name): if not isinstance(name, str): - raise TypeError("illegal name: %r"%name) + raise TypeError("illegal name: %r" % name) self.name = name self.buffers = [] def __repr__(self): - return ""%self.name + return "" % self.name def get_object(self, g=None): if g is None: @@ -170,33 +178,35 @@ def get_object(self, g=None): class CannedCell(CannedObject): """Can a closure cell""" + def __init__(self, cell): self.cell_contents = can(cell.cell_contents) def get_object(self, g=None): cell_contents = uncan(self.cell_contents, g) + def inner(): return cell_contents + return inner.__closure__[0] class CannedFunction(CannedObject): - def __init__(self, f): self._check_type(f) self.code = f.__code__ if f.__defaults__: - self.defaults = [ can(fd) for fd in f.__defaults__ ] + self.defaults = [can(fd) for fd in f.__defaults__] else: self.defaults = None closure = f.__closure__ if closure: - self.closure = tuple( can(cell) for cell in closure ) + self.closure = tuple(can(cell) for cell in closure) else: self.closure = None - self.module = f.__module__ or '__main__' + self.module = f.__module__ or "__main__" self.__name__ = f.__name__ self.buffers = [] @@ -205,7 +215,7 @@ def _check_type(self, obj): def get_object(self, g=None): # try to load function back into its module: - if not self.module.startswith('__'): + if not self.module.startswith("__"): __import__(self.module) g = sys.modules[self.module].__dict__ @@ -222,22 +232,22 @@ def get_object(self, g=None): newFunc = FunctionType(self.code, g, self.__name__, defaults, closure) return newFunc -class CannedClass(CannedObject): +class CannedClass(CannedObject): def __init__(self, cls): self._check_type(cls) self.name = cls.__name__ self.old_style = not isinstance(cls, type) self._canned_dict = {} - for k,v in cls.__dict__.items(): - if k not in ('__weakref__', '__dict__'): + for k, v in cls.__dict__.items(): + if k not in ("__weakref__", "__dict__"): self._canned_dict[k] = can(v) if self.old_style: mro = [] else: mro = cls.mro() - self.parents = [ can(c) for c in mro[1:] ] + self.parents = [can(c) for c in mro[1:]] self.buffers = [] def _check_type(self, obj): @@ -247,18 +257,20 @@ def get_object(self, g=None): parents = tuple(uncan(p, g) for p in self.parents) return type(self.name, parents, uncan_dict(self._canned_dict, g=g)) + class CannedArray(CannedObject): def __init__(self, obj): from numpy import ascontiguousarray + self.shape = obj.shape self.dtype = obj.dtype.descr if obj.dtype.fields else obj.dtype.str self.pickled = False if sum(obj.shape) == 0: self.pickled = True - elif obj.dtype == 'O': + elif obj.dtype == "O": # can't handle object dtype with buffer approach self.pickled = True - elif obj.dtype.fields and any(dt == 'O' for dt,sz in obj.dtype.fields.values()): + elif obj.dtype.fields and any(dt == "O" for dt, sz in obj.dtype.fields.values()): self.pickled = True if self.pickled: # just pickle it @@ -270,6 +282,7 @@ def __init__(self, obj): def get_object(self, g=None): from numpy import frombuffer + data = self.buffers[0] if self.pickled: # we just pickled it @@ -295,23 +308,25 @@ def get_object(self, g=None): data = self.buffers[0] return self.wrap(data) + class CannedBuffer(CannedBytes): wrap = buffer + class CannedMemoryView(CannedBytes): wrap = memoryview -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Functions -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -def _import_mapping(mapping, original=None): - """import any string-keys in a type mapping - """ +def _import_mapping(mapping, original=None): + """import any string-keys in a type mapping""" log = get_logger() log.debug("Importing canning map") - for key,value in list(mapping.items()): + for key, value in list(mapping.items()): if isinstance(key, str): try: cls = import_item(key) @@ -323,6 +338,7 @@ def _import_mapping(mapping, original=None): else: mapping[cls] = mapping.pop(key) + def istype(obj, check): """like isinstance(obj, check), but strict @@ -336,12 +352,13 @@ def istype(obj, check): else: return type(obj) is check + def can(obj): """prepare an object for pickling""" import_needed = False - for cls,canner in can_map.items(): + for cls, canner in can_map.items(): if isinstance(cls, str): import_needed = True break @@ -356,12 +373,14 @@ def can(obj): return obj + def can_class(obj): - if isinstance(obj, class_type) and obj.__module__ == '__main__': + if isinstance(obj, class_type) and obj.__module__ == "__main__": return CannedClass(obj) else: return obj + def can_dict(obj): """can the *values* of a dict""" if istype(obj, dict): @@ -372,8 +391,10 @@ def can_dict(obj): else: return obj + sequence_types = (list, tuple, set) + def can_sequence(obj): """can the elements of a sequence""" if istype(obj, sequence_types): @@ -382,11 +403,12 @@ def can_sequence(obj): else: return obj + def uncan(obj, g=None): """invert canning""" import_needed = False - for cls,uncanner in uncan_map.items(): + for cls, uncanner in uncan_map.items(): if isinstance(cls, str): import_needed = True break @@ -401,42 +423,45 @@ def uncan(obj, g=None): return obj + def uncan_dict(obj, g=None): if istype(obj, dict): newobj = {} for k, v in obj.items(): - newobj[k] = uncan(v,g) + newobj[k] = uncan(v, g) return newobj else: return obj + def uncan_sequence(obj, g=None): if istype(obj, sequence_types): t = type(obj) - return t([uncan(i,g) for i in obj]) + return t([uncan(i, g) for i in obj]) else: return obj -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # API dictionaries -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # These dicts can be extended for custom serialization of new objects can_map = { - 'numpy.ndarray' : CannedArray, - FunctionType : CannedFunction, - bytes : CannedBytes, - memoryview : CannedMemoryView, - cell_type : CannedCell, - class_type : can_class, + "numpy.ndarray": CannedArray, + FunctionType: CannedFunction, + bytes: CannedBytes, + memoryview: CannedMemoryView, + cell_type: CannedCell, + class_type: can_class, } if buffer is not memoryview: can_map[buffer] = CannedBuffer uncan_map = { - CannedObject : lambda obj, g: obj.get_object(g), - dict : uncan_dict, + CannedObject: lambda obj, g: obj.get_object(g), + dict: uncan_dict, } # for use in _import_mapping: diff --git a/ipykernel/pylab/backend_inline.py b/ipykernel/pylab/backend_inline.py index c1935b4b0..b1627cac5 100644 --- a/ipykernel/pylab/backend_inline.py +++ b/ipykernel/pylab/backend_inline.py @@ -7,9 +7,8 @@ from matplotlib_inline.backend_inline import * # analysis: ignore # noqa F401 - warnings.warn( "`ipykernel.pylab.backend_inline` is deprecated, directly " "use `matplotlib_inline.backend_inline`", - DeprecationWarning + DeprecationWarning, ) diff --git a/ipykernel/pylab/config.py b/ipykernel/pylab/config.py index bc9006f5f..09e2bef6a 100644 --- a/ipykernel/pylab/config.py +++ b/ipykernel/pylab/config.py @@ -7,9 +7,7 @@ from matplotlib_inline.config import * # analysis: ignore # noqa F401 - warnings.warn( - "`ipykernel.pylab.config` is deprecated, directly " - "use `matplotlib_inline.config`", - DeprecationWarning + "`ipykernel.pylab.config` is deprecated, directly " "use `matplotlib_inline.config`", + DeprecationWarning, ) diff --git a/ipykernel/serialize.py b/ipykernel/serialize.py index 184a60ded..43247d3ff 100644 --- a/ipykernel/serialize.py +++ b/ipykernel/serialize.py @@ -4,41 +4,53 @@ # Distributed under the terms of the Modified BSD License. import warnings -warnings.warn("ipykernel.serialize is deprecated. It has moved to ipyparallel.serialize", + +warnings.warn( + "ipykernel.serialize is deprecated. It has moved to ipyparallel.serialize", DeprecationWarning, - stacklevel=2 + stacklevel=2, ) import pickle - from itertools import chain try: # available since ipyparallel 5.0.0 from ipyparallel.serialize.canning import ( - can, uncan, can_sequence, uncan_sequence, CannedObject, - istype, sequence_types, + CannedObject, + can, + can_sequence, + istype, + sequence_types, + uncan, + uncan_sequence, ) from ipyparallel.serialize.serialize import PICKLE_PROTOCOL except ImportError: # Deprecated since ipykernel 4.3.0 from ipykernel.pickleutil import ( - can, uncan, can_sequence, uncan_sequence, CannedObject, - istype, sequence_types, PICKLE_PROTOCOL, + can, + uncan, + can_sequence, + uncan_sequence, + CannedObject, + istype, + sequence_types, + PICKLE_PROTOCOL, ) -from jupyter_client.session import MAX_ITEMS, MAX_BYTES +from jupyter_client.session import MAX_BYTES, MAX_ITEMS -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Serialization Functions -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- def _extract_buffers(obj, threshold=MAX_BYTES): """extract buffers larger than a certain threshold""" buffers = [] if isinstance(obj, CannedObject) and obj.buffers: - for i,buf in enumerate(obj.buffers): + for i, buf in enumerate(obj.buffers): if len(buf) > threshold: # buffer larger than threshold, prevent pickling obj.buffers[i] = None @@ -49,13 +61,15 @@ def _extract_buffers(obj, threshold=MAX_BYTES): obj.buffers[i] = buf.tobytes() return buffers + def _restore_buffers(obj, buffers): - """restore buffers extracted by """ + """restore buffers extracted by""" if isinstance(obj, CannedObject) and obj.buffers: - for i,buf in enumerate(obj.buffers): + for i, buf in enumerate(obj.buffers): if buf is None: obj.buffers[i] = buffers.pop(0) + def serialize_object(obj, buffer_threshold=MAX_BYTES, item_threshold=MAX_ITEMS): """Serialize an object into a list of sendable buffers. @@ -93,6 +107,7 @@ def serialize_object(obj, buffer_threshold=MAX_BYTES, item_threshold=MAX_ITEMS): buffers.insert(0, pickle.dumps(cobj, PICKLE_PROTOCOL)) return buffers + def deserialize_object(buffers, g=None): """reconstruct an object serialized by serialize_object from data buffers. @@ -124,6 +139,7 @@ def deserialize_object(buffers, g=None): return newobj, bufs + def pack_apply_message(f, args, kwargs, buffer_threshold=MAX_BYTES, item_threshold=MAX_ITEMS): """pack up a function, args, and kwargs to be sent over the wire @@ -140,12 +156,16 @@ def pack_apply_message(f, args, kwargs, buffer_threshold=MAX_BYTES, item_thresho With length at least two + len(args) + len(kwargs) """ - arg_bufs = list(chain.from_iterable( - serialize_object(arg, buffer_threshold, item_threshold) for arg in args)) + arg_bufs = list( + chain.from_iterable(serialize_object(arg, buffer_threshold, item_threshold) for arg in args) + ) kw_keys = sorted(kwargs.keys()) - kwarg_bufs = list(chain.from_iterable( - serialize_object(kwargs[key], buffer_threshold, item_threshold) for key in kw_keys)) + kwarg_bufs = list( + chain.from_iterable( + serialize_object(kwargs[key], buffer_threshold, item_threshold) for key in kw_keys + ) + ) info = dict(nargs=len(args), narg_bufs=len(arg_bufs), kw_keys=kw_keys) @@ -156,28 +176,29 @@ def pack_apply_message(f, args, kwargs, buffer_threshold=MAX_BYTES, item_thresho return msg + def unpack_apply_message(bufs, g=None, copy=True): """unpack f,args,kwargs from buffers packed by pack_apply_message() Returns: original f,args,kwargs""" - bufs = list(bufs) # allow us to pop + bufs = list(bufs) # allow us to pop assert len(bufs) >= 2, "not enough buffers!" pf = bufs.pop(0) f = uncan(pickle.loads(pf), g) pinfo = bufs.pop(0) info = pickle.loads(pinfo) - arg_bufs, kwarg_bufs = bufs[:info['narg_bufs']], bufs[info['narg_bufs']:] + arg_bufs, kwarg_bufs = bufs[: info["narg_bufs"]], bufs[info["narg_bufs"] :] args = [] - for i in range(info['nargs']): + for i in range(info["nargs"]): arg, arg_bufs = deserialize_object(arg_bufs, g) args.append(arg) args = tuple(args) assert not arg_bufs, "Shouldn't be any arg bufs left over" kwargs = {} - for key in info['kw_keys']: + for key in info["kw_keys"]: kwarg, kwarg_bufs = deserialize_object(kwarg_bufs, g) kwargs[key] = kwarg assert not kwarg_bufs, "Shouldn't be any kwarg bufs left over" - return f,args,kwargs + return f, args, kwargs diff --git a/ipykernel/tests/__init__.py b/ipykernel/tests/__init__.py index 399311940..6ca7492a0 100644 --- a/ipykernel/tests/__init__.py +++ b/ipykernel/tests/__init__.py @@ -7,8 +7,9 @@ import tempfile from unittest.mock import patch -from jupyter_core import paths as jpaths from IPython import paths as ipaths +from jupyter_core import paths as jpaths + from ipykernel.kernelspec import install pjoin = os.path.join @@ -16,16 +17,20 @@ tmp = None patchers = [] + def setup(): """setup temporary env for tests""" global tmp tmp = tempfile.mkdtemp() patchers[:] = [ - patch.dict(os.environ, { - 'HOME': tmp, - # Let tests work with --user install when HOME is changed: - 'PYTHONPATH': os.pathsep.join(sys.path), - }), + patch.dict( + os.environ, + { + "HOME": tmp, + # Let tests work with --user install when HOME is changed: + "PYTHONPATH": os.pathsep.join(sys.path), + }, + ), ] for p in patchers: p.start() diff --git a/ipykernel/tests/test_async.py b/ipykernel/tests/test_async.py index 1266fa355..b36fedff0 100644 --- a/ipykernel/tests/test_async.py +++ b/ipykernel/tests/test_async.py @@ -1,15 +1,13 @@ """Test async/await integration""" -from distutils.version import LooseVersion as V import sys +from distutils.version import LooseVersion as V -import pytest import IPython +import pytest - -from .utils import execute, flush_channels, start_new_kernel, TIMEOUT from .test_message_spec import validate_message - +from .utils import TIMEOUT, execute, flush_channels, start_new_kernel KC = KM = None @@ -26,7 +24,6 @@ def teardown_function(): KM.shutdown_kernel(now=True) - def test_async_await(): flush_channels(KC) msg_id, content = execute("import asyncio; await asyncio.sleep(0.1)", KC) @@ -46,9 +43,7 @@ def test_async_interrupt(asynclib, request): assert content["status"] == "ok", content flush_channels(KC) - msg_id = KC.execute( - f"print('begin'); import {asynclib}; await {asynclib}.sleep(5)" - ) + msg_id = KC.execute(f"print('begin'); import {asynclib}; await {asynclib}.sleep(5)") busy = KC.get_iopub_msg(timeout=TIMEOUT) validate_message(busy, "status", msg_id) assert busy["content"]["execution_state"] == "busy" diff --git a/ipykernel/tests/test_connect.py b/ipykernel/tests/test_connect.py index 16c220785..8e584ce20 100644 --- a/ipykernel/tests/test_connect.py +++ b/ipykernel/tests/test_connect.py @@ -11,24 +11,23 @@ import pytest import zmq - from traitlets.config import Config + from ipykernel import connect from ipykernel.kernelapp import IPKernelApp from .utils import TemporaryWorkingDirectory - sample_info = { - 'ip': '1.2.3.4', - 'transport': 'ipc', - 'shell_port': 1, - 'hb_port': 2, - 'iopub_port': 3, - 'stdin_port': 4, - 'control_port': 5, - 'key': b'abc123', - 'signature_scheme': 'hmac-md5', + "ip": "1.2.3.4", + "transport": "ipc", + "shell_port": 1, + "hb_port": 2, + "iopub_port": 3, + "stdin_port": 4, + "control_port": 5, + "key": b"abc123", + "signature_scheme": "hmac-md5", } @@ -45,13 +44,13 @@ def test_get_connection_file(): cfg = Config() with TemporaryWorkingDirectory() as d: cfg.ProfileDir.location = d - cf = 'kernel.json' + cf = "kernel.json" app = DummyKernelApp(config=cfg, connection_file=cf) app.initialize() profile_cf = os.path.join(app.connection_dir, cf) assert profile_cf == app.abs_connection_file - with open(profile_cf, 'w') as f: + with open(profile_cf, "w") as f: f.write("{}") assert os.path.exists(profile_cf) assert connect.get_connection_file(app) == profile_cf @@ -62,7 +61,7 @@ def test_get_connection_file(): def test_get_connection_info(): with TemporaryDirectory() as d: - cf = os.path.join(d, 'kernel.json') + cf = os.path.join(d, "kernel.json") connect.write_connection_file(cf, **sample_info) json_info = connect.get_connection_info(cf) info = connect.get_connection_info(cf, unpack=True) @@ -72,7 +71,7 @@ def test_get_connection_info(): assert sub_info == sample_info info2 = json.loads(json_info) - info2['key'] = info2['key'].encode("utf-8") + info2["key"] = info2["key"].encode("utf-8") sub_info2 = {k: v for k, v in info.items() if k in sample_info} assert sub_info2 == sample_info @@ -81,11 +80,11 @@ def test_port_bind_failure_raises(request): cfg = Config() with TemporaryWorkingDirectory() as d: cfg.ProfileDir.location = d - cf = 'kernel.json' + cf = "kernel.json" app = DummyKernelApp(config=cfg, connection_file=cf) request.addfinalizer(app.close) app.initialize() - with patch.object(app, '_try_bind_socket') as mock_try_bind: + with patch.object(app, "_try_bind_socket") as mock_try_bind: mock_try_bind.side_effect = zmq.ZMQError(-100, "fails for unknown error types") with pytest.raises(zmq.ZMQError): app.init_sockets() @@ -97,34 +96,35 @@ def test_port_bind_failure_recovery(request): errno.WSAEADDRINUSE except AttributeError: # Fake windows address in-use code - p = patch.object(errno, 'WSAEADDRINUSE', 12345, create=True) + p = patch.object(errno, "WSAEADDRINUSE", 12345, create=True) p.start() request.addfinalizer(p.stop) cfg = Config() with TemporaryWorkingDirectory() as d: cfg.ProfileDir.location = d - cf = 'kernel.json' + cf = "kernel.json" app = DummyKernelApp(config=cfg, connection_file=cf) request.addfinalizer(app.close) app.initialize() - with patch.object(app, '_try_bind_socket') as mock_try_bind: + with patch.object(app, "_try_bind_socket") as mock_try_bind: mock_try_bind.side_effect = [ zmq.ZMQError(errno.EADDRINUSE, "fails for non-bind unix"), - zmq.ZMQError(errno.WSAEADDRINUSE, "fails for non-bind windows") + zmq.ZMQError(errno.WSAEADDRINUSE, "fails for non-bind windows"), ] + [0] * 100 # Shouldn't raise anything as retries will kick in app.init_sockets() + def test_port_bind_failure_gives_up_retries(request): cfg = Config() with TemporaryWorkingDirectory() as d: cfg.ProfileDir.location = d - cf = 'kernel.json' + cf = "kernel.json" app = DummyKernelApp(config=cfg, connection_file=cf) request.addfinalizer(app.close) app.initialize() - with patch.object(app, '_try_bind_socket') as mock_try_bind: + with patch.object(app, "_try_bind_socket") as mock_try_bind: mock_try_bind.side_effect = zmq.ZMQError(errno.EADDRINUSE, "fails for non-bind") with pytest.raises(zmq.ZMQError): app.init_sockets() diff --git a/ipykernel/tests/test_debugger.py b/ipykernel/tests/test_debugger.py index d670df7fd..6b9c817cd 100644 --- a/ipykernel/tests/test_debugger.py +++ b/ipykernel/tests/test_debugger.py @@ -1,7 +1,8 @@ import sys + import pytest -from .utils import TIMEOUT, new_kernel, get_reply +from .utils import TIMEOUT, get_reply, new_kernel seq = 0 @@ -64,9 +65,7 @@ def kernel_with_debug(kernel): yield kernel finally: # Detach - wait_for_debug_request( - kernel, "disconnect", {"restart": False, "terminateDebuggee": True} - ) + wait_for_debug_request(kernel, "disconnect", {"restart": False, "terminateDebuggee": True}) def test_debug_initialize(kernel): @@ -156,7 +155,7 @@ def test_stop_on_breakpoint(kernel_with_debug): # Wait for stop on breakpoint msg = {"msg_type": "", "content": {}} - while msg.get('msg_type') != 'debug_event' or msg["content"].get("event") != "stopped": + while msg.get("msg_type") != "debug_event" or msg["content"].get("event") != "stopped": msg = kernel_with_debug.get_iopub_msg(timeout=TIMEOUT) assert msg["content"]["body"]["reason"] == "breakpoint" @@ -192,7 +191,7 @@ def f(a, b): # Wait for stop on breakpoint msg = {"msg_type": "", "content": {}} - while msg.get('msg_type') != 'debug_event' or msg["content"].get("event") != "stopped": + while msg.get("msg_type") != "debug_event" or msg["content"].get("event") != "stopped": msg = kernel_with_debug.get_iopub_msg(timeout=TIMEOUT) assert msg["content"]["body"]["reason"] == "breakpoint" @@ -227,7 +226,6 @@ def test_rich_inspect_at_breakpoint(kernel_with_debug): f(2, 3)""" - r = wait_for_debug_request(kernel_with_debug, "dumpCell", {"code": code}) source = r["body"]["sourcePath"] @@ -249,16 +247,16 @@ def test_rich_inspect_at_breakpoint(kernel_with_debug): # Wait for stop on breakpoint msg = {"msg_type": "", "content": {}} - while msg.get('msg_type') != 'debug_event' or msg["content"].get("event") != "stopped": + while msg.get("msg_type") != "debug_event" or msg["content"].get("event") != "stopped": msg = kernel_with_debug.get_iopub_msg(timeout=TIMEOUT) - stacks = wait_for_debug_request(kernel_with_debug, "stackTrace", {"threadId": 1})[ - "body" - ]["stackFrames"] + stacks = wait_for_debug_request(kernel_with_debug, "stackTrace", {"threadId": 1})["body"][ + "stackFrames" + ] - scopes = wait_for_debug_request( - kernel_with_debug, "scopes", {"frameId": stacks[0]["id"]} - )["body"]["scopes"] + scopes = wait_for_debug_request(kernel_with_debug, "scopes", {"frameId": stacks[0]["id"]})[ + "body" + ]["scopes"] locals_ = wait_for_debug_request( kernel_with_debug, @@ -280,6 +278,7 @@ def test_rich_inspect_at_breakpoint(kernel_with_debug): def test_convert_to_long_pathname(): - if sys.platform == 'win32': + if sys.platform == "win32": from ipykernel.compiler import _convert_to_long_pathname + _convert_to_long_pathname(__file__) diff --git a/ipykernel/tests/test_embed_kernel.py b/ipykernel/tests/test_embed_kernel.py index 78806a30a..89c02565f 100644 --- a/ipykernel/tests/test_embed_kernel.py +++ b/ipykernel/tests/test_embed_kernel.py @@ -3,19 +3,17 @@ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. +import json import os import sys import time -import json - from contextlib import contextmanager -from subprocess import Popen, PIPE -from flaky import flaky +from subprocess import PIPE, Popen +from flaky import flaky from jupyter_client import BlockingKernelClient from jupyter_core import paths - SETUP_TIMEOUT = 60 TIMEOUT = 15 @@ -40,17 +38,19 @@ def connection_file_ready(connection_file): except ValueError: return False - kernel = Popen([sys.executable, '-c', cmd], stdout=PIPE, stderr=PIPE, encoding="utf-8") + kernel = Popen([sys.executable, "-c", cmd], stdout=PIPE, stderr=PIPE, encoding="utf-8") try: connection_file = os.path.join( paths.jupyter_runtime_dir(), - 'kernel-%i.json' % kernel.pid, + "kernel-%i.json" % kernel.pid, ) # wait for connection file to exist, timeout after 5s tic = time.time() - while not connection_file_ready(connection_file) \ - and kernel.poll() is None \ - and time.time() < tic + SETUP_TIMEOUT: + while ( + not connection_file_ready(connection_file) + and kernel.poll() is None + and time.time() < tic + SETUP_TIMEOUT + ): time.sleep(0.1) # Wait 100ms for the writing to finish @@ -80,96 +80,102 @@ def connection_file_ready(connection_file): @flaky(max_runs=3) def test_embed_kernel_basic(): """IPython.embed_kernel() is basically functional""" - cmd = '\n'.join([ - 'from IPython import embed_kernel', - 'def go():', - ' a=5', - ' b="hi there"', - ' embed_kernel()', - 'go()', - '', - ]) + cmd = "\n".join( + [ + "from IPython import embed_kernel", + "def go():", + " a=5", + ' b="hi there"', + " embed_kernel()", + "go()", + "", + ] + ) with setup_kernel(cmd) as client: # oinfo a (int) client.inspect("a") msg = client.get_shell_msg(timeout=TIMEOUT) - content = msg['content'] - assert content['found'] + content = msg["content"] + assert content["found"] client.execute("c=a*2") msg = client.get_shell_msg(timeout=TIMEOUT) - content = msg['content'] - assert content['status'] == 'ok' + content = msg["content"] + assert content["status"] == "ok" # oinfo c (should be 10) client.inspect("c") msg = client.get_shell_msg(timeout=TIMEOUT) - content = msg['content'] - assert content['found'] - text = content['data']['text/plain'] - assert '10' in text + content = msg["content"] + assert content["found"] + text = content["data"]["text/plain"] + assert "10" in text @flaky(max_runs=3) def test_embed_kernel_namespace(): """IPython.embed_kernel() inherits calling namespace""" - cmd = '\n'.join([ - 'from IPython import embed_kernel', - 'def go():', - ' a=5', - ' b="hi there"', - ' embed_kernel()', - 'go()', - '', - ]) + cmd = "\n".join( + [ + "from IPython import embed_kernel", + "def go():", + " a=5", + ' b="hi there"', + " embed_kernel()", + "go()", + "", + ] + ) with setup_kernel(cmd) as client: # oinfo a (int) client.inspect("a") msg = client.get_shell_msg(timeout=TIMEOUT) - content = msg['content'] - assert content['found'] - text = content['data']['text/plain'] - assert '5' in text + content = msg["content"] + assert content["found"] + text = content["data"]["text/plain"] + assert "5" in text # oinfo b (str) client.inspect("b") msg = client.get_shell_msg(timeout=TIMEOUT) - content = msg['content'] - assert content['found'] - text = content['data']['text/plain'] - assert 'hi there' in text + content = msg["content"] + assert content["found"] + text = content["data"]["text/plain"] + assert "hi there" in text # oinfo c (undefined) client.inspect("c") msg = client.get_shell_msg(timeout=TIMEOUT) - content = msg['content'] - assert not content['found'] + content = msg["content"] + assert not content["found"] + @flaky(max_runs=3) def test_embed_kernel_reentrant(): """IPython.embed_kernel() can be called multiple times""" - cmd = '\n'.join([ - 'from IPython import embed_kernel', - 'count = 0', - 'def go():', - ' global count', - ' embed_kernel()', - ' count = count + 1', - '', - 'while True:' - ' go()', - '', - ]) + cmd = "\n".join( + [ + "from IPython import embed_kernel", + "count = 0", + "def go():", + " global count", + " embed_kernel()", + " count = count + 1", + "", + "while True:" " go()", + "", + ] + ) with setup_kernel(cmd) as client: for i in range(5): client.inspect("count") msg = client.get_shell_msg(timeout=TIMEOUT) - content = msg['content'] - assert content['found'] - text = content['data']['text/plain'] + content = msg["content"] + assert content["found"] + text = content["data"]["text/plain"] assert str(i) in text # exit from embed_kernel diff --git a/ipykernel/tests/test_eventloop.py b/ipykernel/tests/test_eventloop.py index 1f25d97e2..c0db7ff66 100644 --- a/ipykernel/tests/test_eventloop.py +++ b/ipykernel/tests/test_eventloop.py @@ -5,7 +5,7 @@ import pytest import tornado -from .utils import flush_channels, start_new_kernel, execute +from .utils import execute, flush_channels, start_new_kernel KC = KM = None @@ -31,15 +31,15 @@ def teardown(): @pytest.mark.skipif(tornado.version_info < (5,), reason="only relevant on tornado 5") def test_asyncio_interrupt(): flush_channels(KC) - msg_id, content = execute('%gui asyncio', KC) - assert content['status'] == 'ok', content + msg_id, content = execute("%gui asyncio", KC) + assert content["status"] == "ok", content flush_channels(KC) msg_id, content = execute(async_code, KC) - assert content['status'] == 'ok', content + assert content["status"] == "ok", content KM.interrupt_kernel() flush_channels(KC) msg_id, content = execute(async_code, KC) - assert content['status'] == 'ok' + assert content["status"] == "ok" diff --git a/ipykernel/tests/test_heartbeat.py b/ipykernel/tests/test_heartbeat.py index 9340abd7f..7847595e8 100644 --- a/ipykernel/tests/test_heartbeat.py +++ b/ipykernel/tests/test_heartbeat.py @@ -14,7 +14,7 @@ def test_port_bind_failure_raises(): heart = Heartbeat(None) - with patch.object(heart, '_try_bind_socket') as mock_try_bind: + with patch.object(heart, "_try_bind_socket") as mock_try_bind: mock_try_bind.side_effect = zmq.ZMQError(-100, "fails for unknown error types") with pytest.raises(zmq.ZMQError): heart._bind_socket() @@ -23,7 +23,7 @@ def test_port_bind_failure_raises(): def test_port_bind_success(): heart = Heartbeat(None) - with patch.object(heart, '_try_bind_socket') as mock_try_bind: + with patch.object(heart, "_try_bind_socket") as mock_try_bind: heart._bind_socket() assert mock_try_bind.call_count == 1 @@ -37,10 +37,10 @@ def test_port_bind_failure_recovery(): try: heart = Heartbeat(None) - with patch.object(heart, '_try_bind_socket') as mock_try_bind: + with patch.object(heart, "_try_bind_socket") as mock_try_bind: mock_try_bind.side_effect = [ zmq.ZMQError(errno.EADDRINUSE, "fails for non-bind unix"), - zmq.ZMQError(errno.WSAEADDRINUSE, "fails for non-bind windows") + zmq.ZMQError(errno.WSAEADDRINUSE, "fails for non-bind windows"), ] + [0] * 100 # Shouldn't raise anything as retries will kick in heart._bind_socket() @@ -52,7 +52,7 @@ def test_port_bind_failure_recovery(): def test_port_bind_failure_gives_up_retries(): heart = Heartbeat(None) - with patch.object(heart, '_try_bind_socket') as mock_try_bind: + with patch.object(heart, "_try_bind_socket") as mock_try_bind: mock_try_bind.side_effect = zmq.ZMQError(errno.EADDRINUSE, "fails for non-bind") with pytest.raises(zmq.ZMQError): heart._bind_socket() diff --git a/ipykernel/tests/test_io.py b/ipykernel/tests/test_io.py index cad617879..7e2950976 100644 --- a/ipykernel/tests/test_io.py +++ b/ipykernel/tests/test_io.py @@ -2,11 +2,10 @@ import io -import zmq - import pytest - +import zmq from jupyter_client.session import Session + from ipykernel.iostream import IOPubThread, OutStream @@ -18,7 +17,7 @@ def test_io_api(): thread = IOPubThread(pub) thread.start() - stream = OutStream(session, thread, 'stdout') + stream = OutStream(session, thread, "stdout") # cleanup unused zmq objects before we start testing thread.stop() @@ -40,7 +39,8 @@ def test_io_api(): with pytest.raises(io.UnsupportedOperation): stream.tell() with pytest.raises(TypeError): - stream.write(b'') + stream.write(b"") + def test_io_isatty(): session = Session() @@ -49,5 +49,5 @@ def test_io_isatty(): thread = IOPubThread(pub) thread.start() - stream = OutStream(session, thread, 'stdout', isatty=True) + stream = OutStream(session, thread, "stdout", isatty=True) assert stream.isatty() diff --git a/ipykernel/tests/test_jsonutil.py b/ipykernel/tests/test_jsonutil.py index aaf9fdfad..0d8edbb71 100644 --- a/ipykernel/tests/test_jsonutil.py +++ b/ipykernel/tests/test_jsonutil.py @@ -3,19 +3,16 @@ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. -from binascii import a2b_base64 import json - -from datetime import datetime import numbers +from binascii import a2b_base64 +from datetime import datetime import pytest - from jupyter_client._version import version_info as jupyter_client_version from .. import jsonutil -from ..jsonutil import json_clean, encode_images - +from ..jsonutil import encode_images, json_clean JUPYTER_CLIENT_MAJOR_VERSION = jupyter_client_version[0] @@ -23,12 +20,16 @@ class MyInt: def __int__(self): return 389 + + numbers.Integral.register(MyInt) class MyFloat: def __float__(self): return 3.14 + + numbers.Real.register(MyFloat) @@ -36,25 +37,26 @@ def __float__(self): def test(): # list of input/expected output. Use None for the expected output if it # can be the same as the input. - pairs = [(1, None), # start with scalars - (1.0, None), - ('a', None), - (True, None), - (False, None), - (None, None), - # Containers - ([1, 2], None), - ((1, 2), [1, 2]), - ({1, 2}, [1, 2]), - (dict(x=1), None), - ({'x': 1, 'y':[1,2,3], '1':'int'}, None), - # More exotic objects - ((x for x in range(3)), [0, 1, 2]), - (iter([1, 2]), [1, 2]), - (datetime(1991, 7, 3, 12, 00), "1991-07-03T12:00:00.000000"), - (MyFloat(), 3.14), - (MyInt(), 389) - ] + pairs = [ + (1, None), # start with scalars + (1.0, None), + ("a", None), + (True, None), + (False, None), + (None, None), + # Containers + ([1, 2], None), + ((1, 2), [1, 2]), + ({1, 2}, [1, 2]), + (dict(x=1), None), + ({"x": 1, "y": [1, 2, 3], "1": "int"}, None), + # More exotic objects + ((x for x in range(3)), [0, 1, 2]), + (iter([1, 2]), [1, 2]), + (datetime(1991, 7, 3, 12, 00), "1991-07-03T12:00:00.000000"), + (MyFloat(), 3.14), + (MyInt(), 389), + ] for val, jval in pairs: if jval is None: @@ -69,16 +71,16 @@ def test(): @pytest.mark.skipif(JUPYTER_CLIENT_MAJOR_VERSION >= 7, reason="json_clean is a no-op") def test_encode_images(): # invalid data, but the header and footer are from real files - pngdata = b'\x89PNG\r\n\x1a\nblahblahnotactuallyvalidIEND\xaeB`\x82' - jpegdata = b'\xff\xd8\xff\xe0\x00\x10JFIFblahblahjpeg(\xa0\x0f\xff\xd9' - pdfdata = b'%PDF-1.\ntrailer<>]>>>>>>' - bindata = b'\xff\xff\xff\xff' + pngdata = b"\x89PNG\r\n\x1a\nblahblahnotactuallyvalidIEND\xaeB`\x82" + jpegdata = b"\xff\xd8\xff\xe0\x00\x10JFIFblahblahjpeg(\xa0\x0f\xff\xd9" + pdfdata = b"%PDF-1.\ntrailer<>]>>>>>>" + bindata = b"\xff\xff\xff\xff" fmt = { - 'image/png' : pngdata, - 'image/jpeg' : jpegdata, - 'application/pdf' : pdfdata, - 'application/unrecognized': bindata, + "image/png": pngdata, + "image/jpeg": jpegdata, + "application/pdf": pdfdata, + "application/unrecognized": bindata, } encoded = json_clean(encode_images(fmt)) for key, value in fmt.items(): @@ -92,17 +94,19 @@ def test_encode_images(): decoded = a2b_base64(encoded[key]) assert decoded == value + @pytest.mark.skipif(JUPYTER_CLIENT_MAJOR_VERSION >= 7, reason="json_clean is a no-op") def test_lambda(): with pytest.raises(ValueError): - json_clean(lambda : 1) + json_clean(lambda: 1) @pytest.mark.skipif(JUPYTER_CLIENT_MAJOR_VERSION >= 7, reason="json_clean is a no-op") def test_exception(): - bad_dicts = [{1:'number', '1':'string'}, - {True:'bool', 'True':'string'}, - ] + bad_dicts = [ + {1: "number", "1": "string"}, + {True: "bool", "True": "string"}, + ] for d in bad_dicts: with pytest.raises(ValueError): json_clean(d) @@ -110,6 +114,6 @@ def test_exception(): @pytest.mark.skipif(JUPYTER_CLIENT_MAJOR_VERSION >= 7, reason="json_clean is a no-op") def test_unicode_dict(): - data = {'üniço∂e': 'üniço∂e'} + data = {"üniço∂e": "üniço∂e"} clean = jsonutil.json_clean(data) assert data == clean diff --git a/ipykernel/tests/test_kernel.py b/ipykernel/tests/test_kernel.py index 896ca52ca..7118f0a4f 100644 --- a/ipykernel/tests/test_kernel.py +++ b/ipykernel/tests/test_kernel.py @@ -13,16 +13,21 @@ from subprocess import Popen from tempfile import TemporaryDirectory -from flaky import flaky +import IPython import psutil import pytest - -import IPython +from flaky import flaky from IPython.paths import locate_profile from .utils import ( - new_kernel, kernel, TIMEOUT, assemble_output, execute, - flush_channels, wait_for_idle, get_reply, + TIMEOUT, + assemble_output, + execute, + flush_channels, + get_reply, + kernel, + new_kernel, + wait_for_idle, ) @@ -36,25 +41,24 @@ def _check_master(kc, expected=True, stream="stdout"): def _check_status(content): """If status=error, show the traceback""" - if content['status'] == 'error': - assert False, ''.join(['\n'] + content['traceback']) + if content["status"] == "error": + assert False, "".join(["\n"] + content["traceback"]) # printing tests + def test_simple_print(): """simple print statement in kernel""" with kernel() as kc: msg_id, content = execute(kc=kc, code="print('hi')") stdout, stderr = assemble_output(kc.get_iopub_msg) - assert stdout == 'hi\n' - assert stderr == '' + assert stdout == "hi\n" + assert stderr == "" _check_master(kc, expected=True) -@pytest.mark.skip( - reason="Currently don't capture during test as pytest does its own capturing" -) +@pytest.mark.skip(reason="Currently don't capture during test as pytest does its own capturing") def test_capture_fd(): """simple print statement in kernel""" with kernel() as kc: @@ -66,9 +70,7 @@ def test_capture_fd(): _check_master(kc, expected=True) -@pytest.mark.skip( - reason="Currently don't capture during test as pytest does its own capturing" -) +@pytest.mark.skip(reason="Currently don't capture during test as pytest does its own capturing") def test_subprocess_peek_at_stream_fileno(): """""" with kernel() as kc: @@ -92,26 +94,25 @@ def test_sys_path(): sys.stderr.write(stderr) sys_path = ast.literal_eval(stdout.strip()) - assert '' in sys_path + assert "" in sys_path def test_sys_path_profile_dir(): """test that sys.path doesn't get messed up when `--profile-dir` is specified""" - with new_kernel(['--profile-dir', locate_profile('default')]) as kc: + with new_kernel(["--profile-dir", locate_profile("default")]) as kc: msg_id, content = execute(kc=kc, code="import sys; print(repr(sys.path))") stdout, stderr = assemble_output(kc.get_iopub_msg) # for error-output on failure sys.stderr.write(stderr) sys_path = ast.literal_eval(stdout.strip()) - assert '' in sys_path + assert "" in sys_path @flaky(max_runs=3) @pytest.mark.skipif( - sys.platform == "win32" - or (sys.platform == "darwin" and sys.version_info >= (3, 8)), + sys.platform == "win32" or (sys.platform == "darwin" and sys.version_info >= (3, 8)), reason="subprocess prints fail on Windows and MacOS Python 3.8+", ) def test_subprocess_print(): @@ -121,14 +122,16 @@ def test_subprocess_print(): _check_master(kc, expected=True) flush_channels(kc) np = 5 - code = '\n'.join([ - "import time", - "import multiprocessing as mp", - "pool = [mp.Process(target=print, args=('hello', i,)) for i in range(%i)]" % np, - "for p in pool: p.start()", - "for p in pool: p.join()", - "time.sleep(0.5)," - ]) + code = "\n".join( + [ + "import time", + "import multiprocessing as mp", + "pool = [mp.Process(target=print, args=('hello', i,)) for i in range(%i)]" % np, + "for p in pool: p.start()", + "for p in pool: p.join()", + "time.sleep(0.5),", + ] + ) msg_id, content = execute(kc=kc, code=code) stdout, stderr = assemble_output(kc.get_iopub_msg) @@ -146,17 +149,19 @@ def test_subprocess_noprint(): with kernel() as kc: np = 5 - code = '\n'.join([ - "import multiprocessing as mp", - "pool = [mp.Process(target=range, args=(i,)) for i in range(%i)]" % np, - "for p in pool: p.start()", - "for p in pool: p.join()" - ]) + code = "\n".join( + [ + "import multiprocessing as mp", + "pool = [mp.Process(target=range, args=(i,)) for i in range(%i)]" % np, + "for p in pool: p.start()", + "for p in pool: p.join()", + ] + ) msg_id, content = execute(kc=kc, code=code) stdout, stderr = assemble_output(kc.get_iopub_msg) - assert stdout == '' - assert stderr == '' + assert stdout == "" + assert stderr == "" _check_master(kc, expected=True) _check_master(kc, expected=True, stream="stderr") @@ -164,24 +169,25 @@ def test_subprocess_noprint(): @flaky(max_runs=3) @pytest.mark.skipif( - sys.platform == "win32" - or (sys.platform == "darwin" and sys.version_info >= (3, 8)), + sys.platform == "win32" or (sys.platform == "darwin" and sys.version_info >= (3, 8)), reason="subprocess prints fail on Windows and MacOS Python 3.8+", ) def test_subprocess_error(): """error in mp.Process doesn't crash""" with new_kernel() as kc: - code = '\n'.join([ - "import multiprocessing as mp", - "p = mp.Process(target=int, args=('hi',))", - "p.start()", - "p.join()", - ]) + code = "\n".join( + [ + "import multiprocessing as mp", + "p = mp.Process(target=int, args=('hi',))", + "p.start()", + "p.join()", + ] + ) msg_id, content = execute(kc=kc, code=code) stdout, stderr = assemble_output(kc.get_iopub_msg) - assert stdout == '' + assert stdout == "" assert "ValueError" in stderr _check_master(kc, expected=True) @@ -190,6 +196,7 @@ def test_subprocess_error(): # raw_input tests + def test_raw_input(): """test input""" with kernel() as kc: @@ -200,13 +207,13 @@ def test_raw_input(): code = 'print({input_f}("{theprompt}"))'.format(**locals()) msg_id = kc.execute(code, allow_stdin=True) msg = kc.get_stdin_msg(timeout=TIMEOUT) - assert msg['header']['msg_type'] == 'input_request' - content = msg['content'] - assert content['prompt'] == theprompt + assert msg["header"]["msg_type"] == "input_request" + content = msg["content"] + assert content["prompt"] == theprompt text = "some text" kc.input(text) reply = kc.get_shell_msg(timeout=TIMEOUT) - assert reply['content']['status'] == 'ok' + assert reply["content"]["status"] == "ok" stdout, stderr = assemble_output(kc.get_iopub_msg) assert stdout == text + "\n" @@ -215,30 +222,33 @@ def test_save_history(): # Saving history from the kernel with %hist -f was failing because of # unicode problems on Python 2. with kernel() as kc, TemporaryDirectory() as td: - file = os.path.join(td, 'hist.out') - execute('a=1', kc=kc) + file = os.path.join(td, "hist.out") + execute("a=1", kc=kc) wait_for_idle(kc) execute('b="abcþ"', kc=kc) wait_for_idle(kc) _, reply = execute("%hist -f " + file, kc=kc) - assert reply['status'] == 'ok' - with open(file, encoding='utf-8') as f: + assert reply["status"] == "ok" + with open(file, encoding="utf-8") as f: content = f.read() - assert 'a=1' in content + assert "a=1" in content assert 'b="abcþ"' in content def test_smoke_faulthandler(): - faulthadler = pytest.importorskip('faulthandler', reason='this test needs faulthandler') + faulthadler = pytest.importorskip("faulthandler", reason="this test needs faulthandler") with kernel() as kc: # Note: faulthandler.register is not available on windows. - code = '\n'.join([ - 'import sys', - 'import faulthandler', - 'import signal', - 'faulthandler.enable()', - 'if not sys.platform.startswith("win32"):', - ' faulthandler.register(signal.SIGTERM)']) + code = "\n".join( + [ + "import sys", + "import faulthandler", + "import signal", + "faulthandler.enable()", + 'if not sys.platform.startswith("win32"):', + " faulthandler.register(signal.SIGTERM)", + ] + ) _, reply = execute(code, kc=kc) assert reply["status"] == "ok", reply.get("traceback", "") @@ -257,67 +267,64 @@ def test_is_complete(): with kernel() as kc: # There are more test cases for this in core - here we just check # that the kernel exposes the interface correctly. - kc.is_complete('2+2') + kc.is_complete("2+2") reply = kc.get_shell_msg(timeout=TIMEOUT) - assert reply['content']['status'] == 'complete' + assert reply["content"]["status"] == "complete" # SyntaxError - kc.is_complete('raise = 2') + kc.is_complete("raise = 2") reply = kc.get_shell_msg(timeout=TIMEOUT) - assert reply['content']['status'] == 'invalid' + assert reply["content"]["status"] == "invalid" - kc.is_complete('a = [1,\n2,') + kc.is_complete("a = [1,\n2,") reply = kc.get_shell_msg(timeout=TIMEOUT) - assert reply['content']['status'] == 'incomplete' - assert reply['content']['indent'] == '' + assert reply["content"]["status"] == "incomplete" + assert reply["content"]["indent"] == "" # Cell magic ends on two blank lines for console UIs - kc.is_complete('%%timeit\na\n\n') + kc.is_complete("%%timeit\na\n\n") reply = kc.get_shell_msg(timeout=TIMEOUT) - assert reply['content']['status'] == 'complete' + assert reply["content"]["status"] == "complete" @pytest.mark.skipif(sys.platform != "win32", reason="only run on Windows") def test_complete(): with kernel() as kc: - execute('a = 1', kc=kc) + execute("a = 1", kc=kc) wait_for_idle(kc) - cell = 'import IPython\nb = a.' + cell = "import IPython\nb = a." kc.complete(cell) reply = kc.get_shell_msg(timeout=TIMEOUT) - c = reply['content'] - assert c['status'] == 'ok' - start = cell.find('a.') + c = reply["content"] + assert c["status"] == "ok" + start = cell.find("a.") end = start + 2 - assert c['cursor_end'] == cell.find('a.') + 2 - assert c['cursor_start'] <= end + assert c["cursor_end"] == cell.find("a.") + 2 + assert c["cursor_start"] <= end # there are many right answers for cursor_start, # so verify application of the completion # rather than the value of cursor_start - matches = c['matches'] + matches = c["matches"] assert matches for m in matches: - completed = cell[:c['cursor_start']] + m + completed = cell[: c["cursor_start"]] + m assert completed.startswith(cell) def test_matplotlib_inline_on_import(): - pytest.importorskip('matplotlib', reason='this test requires matplotlib') + pytest.importorskip("matplotlib", reason="this test requires matplotlib") with kernel() as kc: - cell = '\n'.join([ - 'import matplotlib, matplotlib.pyplot as plt', - 'backend = matplotlib.get_backend()' - ]) - _, reply = execute(cell, - user_expressions={'backend': 'backend'}, - kc=kc) + cell = "\n".join( + ["import matplotlib, matplotlib.pyplot as plt", "backend = matplotlib.get_backend()"] + ) + _, reply = execute(cell, user_expressions={"backend": "backend"}, kc=kc) _check_status(reply) - backend_bundle = reply['user_expressions']['backend'] + backend_bundle = reply["user_expressions"]["backend"] _check_status(backend_bundle) - assert 'backend_inline' in backend_bundle['data']['text/plain'] + assert "backend_inline" in backend_bundle["data"]["text/plain"] def test_message_order(): @@ -325,7 +332,7 @@ def test_message_order(): with kernel() as kc: _, reply = execute("a = 1", kc=kc) _check_status(reply) - offset = reply['execution_count'] + 1 + offset = reply["execution_count"] + 1 cell = "a += 1\na" msg_ids = [] # submit N executions as fast as we can @@ -334,9 +341,9 @@ def test_message_order(): # check message-handling order for i, msg_id in enumerate(msg_ids, offset): reply = kc.get_shell_msg(timeout=TIMEOUT) - _check_status(reply['content']) - assert reply['content']['execution_count'] == i - assert reply['parent_header']['msg_id'] == msg_id + _check_status(reply["content"]) + assert reply["content"]["execution_count"] == i + assert reply["parent_header"]["msg_id"] == msg_id @pytest.mark.skipif( @@ -345,29 +352,29 @@ def test_message_order(): ) def test_unc_paths(): with kernel() as kc, TemporaryDirectory() as td: - drive_file_path = os.path.join(td, 'unc.txt') - with open(drive_file_path, 'w+') as f: - f.write('# UNC test') - unc_root = '\\\\localhost\\C$' + drive_file_path = os.path.join(td, "unc.txt") + with open(drive_file_path, "w+") as f: + f.write("# UNC test") + unc_root = "\\\\localhost\\C$" file_path = os.path.splitdrive(os.path.dirname(drive_file_path))[1] unc_file_path = os.path.join(unc_root, file_path[1:]) kc.execute(f"cd {unc_file_path:s}") reply = kc.get_shell_msg(timeout=TIMEOUT) - assert reply['content']['status'] == 'ok' + assert reply["content"]["status"] == "ok" out, err = assemble_output(kc.get_iopub_msg) assert unc_file_path in out flush_channels(kc) kc.execute(code="ls") reply = kc.get_shell_msg(timeout=TIMEOUT) - assert reply['content']['status'] == 'ok' + assert reply["content"]["status"] == "ok" out, err = assemble_output(kc.get_iopub_msg) - assert 'unc.txt' in out + assert "unc.txt" in out kc.execute(code="cd") reply = kc.get_shell_msg(timeout=TIMEOUT) - assert reply['content']['status'] == 'ok' + assert reply["content"]["status"] == "ok" @pytest.mark.skipif( @@ -378,12 +385,12 @@ def test_shutdown(): """Kernel exits after polite shutdown_request""" with new_kernel() as kc: km = kc.parent - execute('a = 1', kc=kc) + execute("a = 1", kc=kc) wait_for_idle(kc) kc.shutdown() - for i in range(300): # 30s timeout + for i in range(300): # 30s timeout if km.is_alive(): - time.sleep(.1) + time.sleep(0.1) else: break assert not km.is_alive() @@ -402,19 +409,15 @@ def test_interrupt_during_input(): time.sleep(1) # Make sure it's actually waiting for input. km.interrupt_kernel() from .test_message_spec import validate_message + # If we failed to interrupt interrupt, this will timeout: reply = get_reply(kc, msg_id, TIMEOUT) - validate_message(reply, 'execute_reply', msg_id) + validate_message(reply, "execute_reply", msg_id) -@pytest.mark.skipif( - os.name == "nt", - reason="Message based interrupt not supported on Windows" -) +@pytest.mark.skipif(os.name == "nt", reason="Message based interrupt not supported on Windows") def test_interrupt_with_message(): - """ - - """ + """ """ with new_kernel() as kc: km = kc.parent km.kernel_spec.interrupt_mode = "message" @@ -422,9 +425,10 @@ def test_interrupt_with_message(): time.sleep(1) # Make sure it's actually waiting for input. km.interrupt_kernel() from .test_message_spec import validate_message + # If we failed to interrupt interrupt, this will timeout: reply = get_reply(kc, msg_id, TIMEOUT) - validate_message(reply, 'execute_reply', msg_id) + validate_message(reply, "execute_reply", msg_id) @pytest.mark.skipif( @@ -447,12 +451,13 @@ def test_interrupt_during_pdb_set_trace(): time.sleep(1) # Make sure it's actually waiting for input. km.interrupt_kernel() from .test_message_spec import validate_message + # If we failed to interrupt interrupt, this will timeout: reply = get_reply(kc, msg_id, TIMEOUT) - validate_message(reply, 'execute_reply', msg_id) + validate_message(reply, "execute_reply", msg_id) # If we failed to interrupt interrupt, this will timeout: reply = get_reply(kc, msg_id2, TIMEOUT) - validate_message(reply, 'execute_reply', msg_id2) + validate_message(reply, "execute_reply", msg_id2) def test_control_thread_priority(): @@ -548,9 +553,7 @@ def test_shutdown_subprocesses(): expressions = reply["user_expressions"] kernel_process = psutil.Process(int(expressions["pid"]["data"]["text/plain"])) child_pg = psutil.Process(int(expressions["child_pg"]["data"]["text/plain"])) - child_newpg = psutil.Process( - int(expressions["child_newpg"]["data"]["text/plain"]) - ) + child_newpg = psutil.Process(int(expressions["child_newpg"]["data"]["text/plain"])) wait_for_idle(kc) kc.shutdown() @@ -564,7 +567,7 @@ def test_shutdown_subprocesses(): # child in the process group shut down assert not child_pg.is_running() # child outside the process group was not shut down (unix only) - if os.name != 'nt': + if os.name != "nt": assert child_newpg.is_running() try: child_newpg.terminate() diff --git a/ipykernel/tests/test_kernelspec.py b/ipykernel/tests/test_kernelspec.py index 94321a4d3..31edff5bc 100644 --- a/ipykernel/tests/test_kernelspec.py +++ b/ipykernel/tests/test_kernelspec.py @@ -8,38 +8,31 @@ import tempfile from unittest import mock +import pytest from jupyter_core.paths import jupyter_data_dir from ipykernel.kernelspec import ( - make_ipkernel_cmd, - get_kernel_dict, - write_kernel_spec, - install, - InstallIPythonKernelSpecApp, KERNEL_NAME, RESOURCES, + InstallIPythonKernelSpecApp, + get_kernel_dict, + install, + make_ipkernel_cmd, + write_kernel_spec, ) -import pytest - pjoin = os.path.join def test_make_ipkernel_cmd(): cmd = make_ipkernel_cmd() - assert cmd == [ - sys.executable, - '-m', - 'ipykernel_launcher', - '-f', - '{connection_file}' - ] + assert cmd == [sys.executable, "-m", "ipykernel_launcher", "-f", "{connection_file}"] def assert_kernel_dict(d): - assert d['argv'] == make_ipkernel_cmd() - assert d['display_name'] == 'Python %i (ipykernel)' % sys.version_info[0] - assert d['language'] == 'python' + assert d["argv"] == make_ipkernel_cmd() + assert d["display_name"] == "Python %i (ipykernel)" % sys.version_info[0] + assert d["language"] == "python" def test_get_kernel_dict(): @@ -62,9 +55,9 @@ def assert_is_spec(path): for fname in os.listdir(RESOURCES): dst = pjoin(path, fname) assert os.path.exists(dst) - kernel_json = pjoin(path, 'kernel.json') + kernel_json = pjoin(path, "kernel.json") assert os.path.exists(kernel_json) - with open(kernel_json, encoding='utf8') as f: + with open(kernel_json, encoding="utf8") as f: json.load(f) @@ -95,31 +88,29 @@ def test_install_kernelspec(): def test_install_user(): tmp = tempfile.mkdtemp() - with mock.patch.dict(os.environ, {'HOME': tmp}): + with mock.patch.dict(os.environ, {"HOME": tmp}): install(user=True) data_dir = jupyter_data_dir() - assert_is_spec(os.path.join(data_dir, 'kernels', KERNEL_NAME)) + assert_is_spec(os.path.join(data_dir, "kernels", KERNEL_NAME)) def test_install(): system_jupyter_dir = tempfile.mkdtemp() - with mock.patch('jupyter_client.kernelspec.SYSTEM_JUPYTER_PATH', - [system_jupyter_dir]): + with mock.patch("jupyter_client.kernelspec.SYSTEM_JUPYTER_PATH", [system_jupyter_dir]): install() - assert_is_spec(os.path.join(system_jupyter_dir, 'kernels', KERNEL_NAME)) + assert_is_spec(os.path.join(system_jupyter_dir, "kernels", KERNEL_NAME)) def test_install_profile(): system_jupyter_dir = tempfile.mkdtemp() - with mock.patch('jupyter_client.kernelspec.SYSTEM_JUPYTER_PATH', - [system_jupyter_dir]): + with mock.patch("jupyter_client.kernelspec.SYSTEM_JUPYTER_PATH", [system_jupyter_dir]): install(profile="Test") - spec = os.path.join(system_jupyter_dir, 'kernels', KERNEL_NAME, "kernel.json") + spec = os.path.join(system_jupyter_dir, "kernels", KERNEL_NAME, "kernel.json") with open(spec) as f: spec = json.load(f) assert spec["display_name"].endswith(" [profile=Test]") @@ -129,34 +120,28 @@ def test_install_profile(): def test_install_display_name_overrides_profile(): system_jupyter_dir = tempfile.mkdtemp() - with mock.patch('jupyter_client.kernelspec.SYSTEM_JUPYTER_PATH', - [system_jupyter_dir]): + with mock.patch("jupyter_client.kernelspec.SYSTEM_JUPYTER_PATH", [system_jupyter_dir]): install(display_name="Display", profile="Test") - spec = os.path.join(system_jupyter_dir, 'kernels', KERNEL_NAME, "kernel.json") + spec = os.path.join(system_jupyter_dir, "kernels", KERNEL_NAME, "kernel.json") with open(spec) as f: spec = json.load(f) assert spec["display_name"] == "Display" -@pytest.mark.parametrize("env", [ - None, - dict(spam="spam"), - dict(spam="spam", foo='bar') -]) +@pytest.mark.parametrize("env", [None, dict(spam="spam"), dict(spam="spam", foo="bar")]) def test_install_env(tmp_path, env): # python 3.5 // tmp_path must be converted to str - with mock.patch('jupyter_client.kernelspec.SYSTEM_JUPYTER_PATH', - [str(tmp_path)]): + with mock.patch("jupyter_client.kernelspec.SYSTEM_JUPYTER_PATH", [str(tmp_path)]): install(env=env) - spec = tmp_path / 'kernels' / KERNEL_NAME / "kernel.json" + spec = tmp_path / "kernels" / KERNEL_NAME / "kernel.json" with spec.open() as f: spec = json.load(f) if env: - assert len(env) == len(spec['env']) + assert len(env) == len(spec["env"]) for k, v in env.items(): - assert spec['env'][k] == v + assert spec["env"][k] == v else: - assert 'env' not in spec + assert "env" not in spec diff --git a/ipykernel/tests/test_message_spec.py b/ipykernel/tests/test_message_spec.py index f550b898e..1a9cc58c1 100644 --- a/ipykernel/tests/test_message_spec.py +++ b/ipykernel/tests/test_message_spec.py @@ -8,29 +8,27 @@ from distutils.version import LooseVersion as V from queue import Empty -import pytest - import jupyter_client +import pytest +from traitlets import Bool, Dict, Enum, HasTraits, Integer, List, TraitError, Unicode -from traitlets import ( - HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum -) - -from .utils import (TIMEOUT, start_global_kernel, flush_channels, execute, - get_reply, ) +from .utils import TIMEOUT, execute, flush_channels, get_reply, start_global_kernel -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Globals -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- KC = None + def setup(): global KC KC = start_global_kernel() -#----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- # Message Spec References -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + class Reference(HasTraits): @@ -59,9 +57,9 @@ def check(self, d): class Version(Unicode): def __init__(self, *args, **kwargs): - self.min = kwargs.pop('min', None) - self.max = kwargs.pop('max', None) - kwargs['default_value'] = self.min + self.min = kwargs.pop("min", None) + self.max = kwargs.pop("max", None) + kwargs["default_value"] = self.min super().__init__(*args, **kwargs) def validate(self, obj, value): @@ -84,27 +82,31 @@ def check(self, d): if self.parent_header: RHeader().check(self.parent_header) + class RHeader(Reference): msg_id = Unicode() msg_type = Unicode() session = Unicode() username = Unicode() - version = Version(min='5.0') + version = Version(min="5.0") + + +mime_pat = re.compile(r"^[\w\-\+\.]+/[\w\-\+\.]+$") -mime_pat = re.compile(r'^[\w\-\+\.]+/[\w\-\+\.]+$') class MimeBundle(Reference): metadata = Dict() data = Dict() + def _data_changed(self, name, old, new): - for k,v in new.items(): + for k, v in new.items(): assert mime_pat.match(k) assert isinstance(v, str) # shell replies class Reply(Reference): - status = Enum(('ok', 'error'), default_value='ok') + status = Enum(("ok", "error"), default_value="ok") class ExecuteReply(Reply): @@ -112,28 +114,28 @@ class ExecuteReply(Reply): def check(self, d): Reference.check(self, d) - if d['status'] == 'ok': + if d["status"] == "ok": ExecuteReplyOkay().check(d) - elif d['status'] == 'error': + elif d["status"] == "error": ExecuteReplyError().check(d) - elif d['status'] == 'aborted': + elif d["status"] == "aborted": ExecuteReplyAborted().check(d) class ExecuteReplyOkay(Reply): - status = Enum(('ok',)) + status = Enum(("ok",)) user_expressions = Dict() class ExecuteReplyError(Reply): - status = Enum(('error',)) + status = Enum(("error",)) ename = Unicode() evalue = Unicode() traceback = List(Unicode()) class ExecuteReplyAborted(Reply): - status = Enum(('aborted',)) + status = Enum(("aborted",)) class InspectReply(Reply, MimeBundle): @@ -148,7 +150,7 @@ class ArgSpec(Reference): class Status(Reference): - execution_state = Enum(('busy', 'idle', 'starting'), default_value='busy') + execution_state = Enum(("busy", "idle", "starting"), default_value="busy") class CompleteReply(Reply): @@ -159,20 +161,20 @@ class CompleteReply(Reply): class LanguageInfo(Reference): - name = Unicode('python') + name = Unicode("python") version = Unicode(sys.version.split()[0]) class KernelInfoReply(Reply): - protocol_version = Version(min='5.0') - implementation = Unicode('ipython') - implementation_version = Version(min='2.1') + protocol_version = Version(min="5.0") + implementation = Unicode("ipython") + implementation_version = Version(min="2.1") language_info = Dict() banner = Unicode() def check(self, d): Reference.check(self, d) - LanguageInfo().check(d['language_info']) + LanguageInfo().check(d["language_info"]) class ConnectReply(Reference): @@ -188,11 +190,11 @@ class CommInfoReply(Reply): class IsCompleteReply(Reference): - status = Enum(('complete', 'incomplete', 'invalid', 'unknown'), default_value='complete') + status = Enum(("complete", "incomplete", "invalid", "unknown"), default_value="complete") def check(self, d): Reference.check(self, d) - if d['status'] == 'incomplete': + if d["status"] == "incomplete": IsCompleteReplyIncomplete().check(d) @@ -202,6 +204,7 @@ class IsCompleteReplyIncomplete(Reference): # IOPub messages + class ExecuteInput(Reference): code = Unicode() execution_count = Integer() @@ -209,11 +212,12 @@ class ExecuteInput(Reference): class Error(ExecuteReplyError): """Errors are the same as ExecuteReply, but without status""" - status = None # no status field + + status = None # no status field class Stream(Reference): - name = Enum(('stdout', 'stderr'), default_value='stdout') + name = Enum(("stdout", "stderr"), default_value="stdout") text = Unicode() @@ -230,21 +234,21 @@ class HistoryReply(Reply): references = { - 'execute_reply' : ExecuteReply(), - 'inspect_reply' : InspectReply(), - 'status' : Status(), - 'complete_reply' : CompleteReply(), - 'kernel_info_reply': KernelInfoReply(), - 'connect_reply': ConnectReply(), - 'comm_info_reply': CommInfoReply(), - 'is_complete_reply': IsCompleteReply(), - 'execute_input' : ExecuteInput(), - 'execute_result' : ExecuteResult(), - 'history_reply' : HistoryReply(), - 'error' : Error(), - 'stream' : Stream(), - 'display_data' : DisplayData(), - 'header' : RHeader(), + "execute_reply": ExecuteReply(), + "inspect_reply": InspectReply(), + "status": Status(), + "complete_reply": CompleteReply(), + "kernel_info_reply": KernelInfoReply(), + "connect_reply": ConnectReply(), + "comm_info_reply": CommInfoReply(), + "is_complete_reply": IsCompleteReply(), + "execute_input": ExecuteInput(), + "execute_result": ExecuteResult(), + "history_reply": HistoryReply(), + "error": Error(), + "stream": Stream(), + "display_data": DisplayData(), + "header": RHeader(), } """ Specifications of `content` part of the reply messages. @@ -262,65 +266,66 @@ def validate_message(msg, msg_type=None, parent=None): """ RMessage().check(msg) if msg_type: - assert msg['msg_type'] == msg_type + assert msg["msg_type"] == msg_type if parent: - assert msg['parent_header']['msg_id'] == parent - content = msg['content'] - ref = references[msg['msg_type']] + assert msg["parent_header"]["msg_id"] == parent + content = msg["content"] + ref = references[msg["msg_type"]] ref.check(content) -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Tests -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Shell channel + def test_execute(): flush_channels() - msg_id = KC.execute(code='x=1') + msg_id = KC.execute(code="x=1") reply = get_reply(KC, msg_id, TIMEOUT) - validate_message(reply, 'execute_reply', msg_id) + validate_message(reply, "execute_reply", msg_id) def test_execute_silent(): flush_channels() - msg_id, reply = execute(code='x=1', silent=True) + msg_id, reply = execute(code="x=1", silent=True) # flush status=idle status = KC.get_iopub_msg(timeout=TIMEOUT) - validate_message(status, 'status', msg_id) - assert status['content']['execution_state'] == 'idle' + validate_message(status, "status", msg_id) + assert status["content"]["execution_state"] == "idle" with pytest.raises(Empty): KC.get_iopub_msg(timeout=0.1) - count = reply['execution_count'] + count = reply["execution_count"] - msg_id, reply = execute(code='x=2', silent=True) + msg_id, reply = execute(code="x=2", silent=True) # flush status=idle status = KC.get_iopub_msg(timeout=TIMEOUT) - validate_message(status, 'status', msg_id) - assert status['content']['execution_state'] == 'idle' + validate_message(status, "status", msg_id) + assert status["content"]["execution_state"] == "idle" with pytest.raises(Empty): KC.get_iopub_msg(timeout=0.1) - count_2 = reply['execution_count'] + count_2 = reply["execution_count"] assert count_2 == count def test_execute_error(): flush_channels() - msg_id, reply = execute(code='1/0') - assert reply['status'] == 'error' - assert reply['ename'] == 'ZeroDivisionError' + msg_id, reply = execute(code="1/0") + assert reply["status"] == "error" + assert reply["ename"] == "ZeroDivisionError" error = KC.get_iopub_msg(timeout=TIMEOUT) - validate_message(error, 'error', msg_id) + validate_message(error, "error", msg_id) def test_execute_inc(): @@ -359,7 +364,7 @@ def test_execute_stop_on_error(): assert reply["content"]["status"] == "aborted" # second message, too reply = KC.get_shell_msg(timeout=TIMEOUT) - assert reply['content']['status'] == 'aborted' + assert reply["content"]["status"] == "aborted" flush_channels() @@ -367,101 +372,105 @@ def test_execute_stop_on_error(): KC.execute(code='print("Hello")') KC.get_shell_msg(timeout=TIMEOUT) reply = KC.get_shell_msg(timeout=TIMEOUT) - assert reply['content']['status'] == 'ok' + assert reply["content"]["status"] == "ok" def test_non_execute_stop_on_error(): """test that non-execute_request's are not aborted after an error""" flush_channels() - fail = '\n'.join([ - # sleep to ensure subsequent message is waiting in the queue to be aborted - 'import time', - 'time.sleep(0.5)', - 'raise ValueError', - ]) + fail = "\n".join( + [ + # sleep to ensure subsequent message is waiting in the queue to be aborted + "import time", + "time.sleep(0.5)", + "raise ValueError", + ] + ) KC.execute(code=fail) KC.kernel_info() KC.comm_info() KC.inspect(code="print") - reply = KC.get_shell_msg(timeout=TIMEOUT) # execute - assert reply['content']['status'] == 'error' - reply = KC.get_shell_msg(timeout=TIMEOUT) # kernel_info - assert reply['content']['status'] == 'ok' - reply = KC.get_shell_msg(timeout=TIMEOUT) # comm_info - assert reply['content']['status'] == 'ok' - reply = KC.get_shell_msg(timeout=TIMEOUT) # inspect - assert reply['content']['status'] == 'ok' + reply = KC.get_shell_msg(timeout=TIMEOUT) # execute + assert reply["content"]["status"] == "error" + reply = KC.get_shell_msg(timeout=TIMEOUT) # kernel_info + assert reply["content"]["status"] == "ok" + reply = KC.get_shell_msg(timeout=TIMEOUT) # comm_info + assert reply["content"]["status"] == "ok" + reply = KC.get_shell_msg(timeout=TIMEOUT) # inspect + assert reply["content"]["status"] == "ok" def test_user_expressions(): flush_channels() - msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1')) - user_expressions = reply['user_expressions'] - assert user_expressions == {'foo': { - 'status': 'ok', - 'data': {'text/plain': '2'}, - 'metadata': {}, - }} + msg_id, reply = execute(code="x=1", user_expressions=dict(foo="x+1")) + user_expressions = reply["user_expressions"] + assert user_expressions == { + "foo": { + "status": "ok", + "data": {"text/plain": "2"}, + "metadata": {}, + } + } def test_user_expressions_fail(): flush_channels() - msg_id, reply = execute(code='x=0', user_expressions=dict(foo='nosuchname')) - user_expressions = reply['user_expressions'] - foo = user_expressions['foo'] - assert foo['status'] == 'error' - assert foo['ename'] == 'NameError' + msg_id, reply = execute(code="x=0", user_expressions=dict(foo="nosuchname")) + user_expressions = reply["user_expressions"] + foo = user_expressions["foo"] + assert foo["status"] == "error" + assert foo["ename"] == "NameError" def test_oinfo(): flush_channels() - msg_id = KC.inspect('a') + msg_id = KC.inspect("a") reply = get_reply(KC, msg_id, TIMEOUT) - validate_message(reply, 'inspect_reply', msg_id) + validate_message(reply, "inspect_reply", msg_id) def test_oinfo_found(): flush_channels() - msg_id, reply = execute(code='a=5') + msg_id, reply = execute(code="a=5") - msg_id = KC.inspect('a') + msg_id = KC.inspect("a") reply = get_reply(KC, msg_id, TIMEOUT) - validate_message(reply, 'inspect_reply', msg_id) - content = reply['content'] - assert content['found'] - text = content['data']['text/plain'] - assert 'Type:' in text - assert 'Docstring:' in text + validate_message(reply, "inspect_reply", msg_id) + content = reply["content"] + assert content["found"] + text = content["data"]["text/plain"] + assert "Type:" in text + assert "Docstring:" in text def test_oinfo_detail(): flush_channels() - msg_id, reply = execute(code='ip=get_ipython()') + msg_id, reply = execute(code="ip=get_ipython()") - msg_id = KC.inspect('ip.object_inspect', cursor_pos=10, detail_level=1) + msg_id = KC.inspect("ip.object_inspect", cursor_pos=10, detail_level=1) reply = get_reply(KC, msg_id, TIMEOUT) - validate_message(reply, 'inspect_reply', msg_id) - content = reply['content'] - assert content['found'] - text = content['data']['text/plain'] - assert 'Signature:' in text - assert 'Source:' in text + validate_message(reply, "inspect_reply", msg_id) + content = reply["content"] + assert content["found"] + text = content["data"]["text/plain"] + assert "Signature:" in text + assert "Source:" in text def test_oinfo_not_found(): flush_channels() - msg_id = KC.inspect('dne') + msg_id = KC.inspect("dne") reply = get_reply(KC, msg_id, TIMEOUT) - validate_message(reply, 'inspect_reply', msg_id) - content = reply['content'] - assert not content['found'] + validate_message(reply, "inspect_reply", msg_id) + content = reply["content"] + assert not content["found"] def test_complete(): @@ -469,11 +478,11 @@ def test_complete(): msg_id, reply = execute(code="alpha = albert = 5") - msg_id = KC.complete('al', 2) + msg_id = KC.complete("al", 2) reply = get_reply(KC, msg_id, TIMEOUT) - validate_message(reply, 'complete_reply', msg_id) - matches = reply['content']['matches'] - for name in ('alpha', 'albert'): + validate_message(reply, "complete_reply", msg_id) + matches = reply["content"]["matches"] + for name in ("alpha", "albert"): assert name in matches @@ -482,18 +491,18 @@ def test_kernel_info_request(): msg_id = KC.kernel_info() reply = get_reply(KC, msg_id, TIMEOUT) - validate_message(reply, 'kernel_info_reply', msg_id) + validate_message(reply, "kernel_info_reply", msg_id) def test_connect_request(): flush_channels() - msg = KC.session.msg('connect_request') + msg = KC.session.msg("connect_request") KC.shell_channel.send(msg) - return msg['header']['msg_id'] + return msg["header"]["msg_id"] msg_id = KC.kernel_info() reply = get_reply(KC, msg_id, TIMEOUT) - validate_message(reply, 'connect_reply', msg_id) + validate_message(reply, "connect_reply", msg_id) @pytest.mark.skipif( @@ -504,7 +513,7 @@ def test_comm_info_request(): flush_channels() msg_id = KC.comm_info() reply = get_reply(KC, msg_id, TIMEOUT) - validate_message(reply, 'comm_info_reply', msg_id) + validate_message(reply, "comm_info_reply", msg_id) def test_single_payload(): @@ -518,19 +527,21 @@ def test_single_payload(): transform) should avoid setting multiple set_next_input). """ flush_channels() - msg_id, reply = execute(code="ip = get_ipython()\n" - "for i in range(3):\n" - " ip.set_next_input('Hello There')\n") - payload = reply['payload'] + msg_id, reply = execute( + code="ip = get_ipython()\n" "for i in range(3):\n" " ip.set_next_input('Hello There')\n" + ) + payload = reply["payload"] next_input_pls = [pl for pl in payload if pl["source"] == "set_next_input"] assert len(next_input_pls) == 1 + def test_is_complete(): flush_channels() msg_id = KC.is_complete("a = 1") reply = get_reply(KC, msg_id, TIMEOUT) - validate_message(reply, 'is_complete_reply', msg_id) + validate_message(reply, "is_complete_reply", msg_id) + def test_history_range(): flush_channels() @@ -538,11 +549,12 @@ def test_history_range(): KC.execute(code="x=1", store_history=True) KC.get_shell_msg(timeout=TIMEOUT) - msg_id = KC.history(hist_access_type = 'range', raw = True, output = True, start = 1, stop = 2, session = 0) + msg_id = KC.history(hist_access_type="range", raw=True, output=True, start=1, stop=2, session=0) reply = get_reply(KC, msg_id, TIMEOUT) - validate_message(reply, 'history_reply', msg_id) - content = reply['content'] - assert len(content['history']) == 1 + validate_message(reply, "history_reply", msg_id) + content = reply["content"] + assert len(content["history"]) == 1 + def test_history_tail(): flush_channels() @@ -550,11 +562,12 @@ def test_history_tail(): KC.execute(code="x=1", store_history=True) KC.get_shell_msg(timeout=TIMEOUT) - msg_id = KC.history(hist_access_type = 'tail', raw = True, output = True, n = 1, session = 0) + msg_id = KC.history(hist_access_type="tail", raw=True, output=True, n=1, session=0) reply = get_reply(KC, msg_id, TIMEOUT) - validate_message(reply, 'history_reply', msg_id) - content = reply['content'] - assert len(content['history']) == 1 + validate_message(reply, "history_reply", msg_id) + content = reply["content"] + assert len(content["history"]) == 1 + def test_history_search(): flush_channels() @@ -562,11 +575,14 @@ def test_history_search(): KC.execute(code="x=1", store_history=True) KC.get_shell_msg(timeout=TIMEOUT) - msg_id = KC.history(hist_access_type = 'search', raw = True, output = True, n = 1, pattern = '*', session = 0) + msg_id = KC.history( + hist_access_type="search", raw=True, output=True, n=1, pattern="*", session=0 + ) reply = get_reply(KC, msg_id, TIMEOUT) - validate_message(reply, 'history_reply', msg_id) - content = reply['content'] - assert len(content['history']) == 1 + validate_message(reply, "history_reply", msg_id) + content = reply["content"] + assert len(content["history"]) == 1 + # IOPub channel @@ -577,9 +593,9 @@ def test_stream(): msg_id, reply = execute("print('hi')") stdout = KC.get_iopub_msg(timeout=TIMEOUT) - validate_message(stdout, 'stream', msg_id) - content = stdout['content'] - assert content['text'] == 'hi\n' + validate_message(stdout, "stream", msg_id) + content = stdout["content"] + assert content["text"] == "hi\n" def test_display_data(): @@ -588,6 +604,6 @@ def test_display_data(): msg_id, reply = execute("from IPython.display import display; display(1)") display = KC.get_iopub_msg(timeout=TIMEOUT) - validate_message(display, 'display_data', parent=msg_id) - data = display['content']['data'] - assert data['text/plain'] == '1' + validate_message(display, "display_data", parent=msg_id) + data = display["content"]["data"] + assert data["text/plain"] == "1" diff --git a/ipykernel/tests/test_pickleutil.py b/ipykernel/tests/test_pickleutil.py index d81342ae0..1cba3cb99 100644 --- a/ipykernel/tests/test_pickleutil.py +++ b/ipykernel/tests/test_pickleutil.py @@ -2,16 +2,20 @@ from ipykernel.pickleutil import can, uncan + def interactive(f): - f.__module__ = '__main__' + f.__module__ = "__main__" return f + def dumps(obj): return pickle.dumps(can(obj)) + def loads(obj): return uncan(pickle.loads(obj)) + def test_no_closure(): @interactive def foo(): @@ -22,32 +26,38 @@ def foo(): bar = loads(pfoo) assert foo() == bar() + def test_generator_closure(): # this only creates a closure on Python 3 @interactive def foo(): - i = 'i' - r = [ i for j in (1,2) ] + i = "i" + r = [i for j in (1, 2)] return r pfoo = dumps(foo) bar = loads(pfoo) assert foo() == bar() + def test_nested_closure(): @interactive def foo(): - i = 'i' + i = "i" + def g(): return i + return g() pfoo = dumps(foo) bar = loads(pfoo) assert foo() == bar() + def test_closure(): - i = 'i' + i = "i" + @interactive def foo(): return i @@ -56,8 +66,9 @@ def foo(): bar = loads(pfoo) assert foo() == bar() + def test_uncan_bytes_buffer(): - data = b'data' + data = b"data" canned = can(data) canned.buffers = [memoryview(buf) for buf in canned.buffers] out = uncan(canned) diff --git a/ipykernel/tests/test_start_kernel.py b/ipykernel/tests/test_start_kernel.py index 876b5bdcc..6d6c64e47 100644 --- a/ipykernel/tests/test_start_kernel.py +++ b/ipykernel/tests/test_start_kernel.py @@ -1,7 +1,9 @@ -from .test_embed_kernel import setup_kernel -from flaky import flaky from textwrap import dedent +from flaky import flaky + +from .test_embed_kernel import setup_kernel + TIMEOUT = 15 @@ -18,10 +20,10 @@ def test_ipython_start_kernel_userns(): with setup_kernel(cmd) as client: client.inspect("tre") msg = client.get_shell_msg(timeout=TIMEOUT) - content = msg['content'] - assert content['found'] - text = content['data']['text/plain'] - assert '123' in text + content = msg["content"] + assert content["found"] + text = content["data"]["text/plain"] + assert "123" in text # user_module should be an instance of DummyMod client.execute("usermod = get_ipython().user_module") @@ -30,10 +32,10 @@ def test_ipython_start_kernel_userns(): assert content["status"] == "ok" client.inspect("usermod") msg = client.get_shell_msg(timeout=TIMEOUT) - content = msg['content'] - assert content['found'] - text = content['data']['text/plain'] - assert 'DummyMod' in text + content = msg["content"] + assert content["found"] + text = content["data"]["text/plain"] + assert "DummyMod" in text @flaky(max_runs=3) @@ -54,7 +56,7 @@ def test_ipython_start_kernel_no_userns(): assert content["status"] == "ok" client.inspect("usermod") msg = client.get_shell_msg(timeout=TIMEOUT) - content = msg['content'] - assert content['found'] - text = content['data']['text/plain'] - assert 'DummyMod' not in text + content = msg["content"] + assert content["found"] + text = content["data"]["text/plain"] + assert "DummyMod" not in text diff --git a/ipykernel/tests/test_zmq_shell.py b/ipykernel/tests/test_zmq_shell.py index 56f587e74..1a637f53e 100644 --- a/ipykernel/tests/test_zmq_shell.py +++ b/ipykernel/tests/test_zmq_shell.py @@ -3,15 +3,15 @@ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. +import unittest from queue import Queue from threading import Thread -import unittest -from traitlets import Int import zmq +from jupyter_client.session import Session +from traitlets import Int from ipykernel.zmqshell import ZMQDisplayPublisher -from jupyter_client.session import Session class NoReturnDisplayHook: @@ -20,6 +20,7 @@ class NoReturnDisplayHook: the number of times an object is called, but which does *not* return a message when it is called. """ + call_count = 0 def __call__(self, obj): @@ -32,6 +33,7 @@ class ReturnDisplayHook(NoReturnDisplayHook): as its base class, but which also returns the same message when it is called. """ + def __call__(self, obj): super().__call__(obj) return obj @@ -43,6 +45,7 @@ class CounterSession(Session): the calls made to the session object by the display publisher. """ + send_count = Int(0) def send(self, *args, **kwargs): @@ -64,10 +67,7 @@ def setUp(self): self.socket = self.context.socket(zmq.PUB) self.session = CounterSession() - self.disp_pub = ZMQDisplayPublisher( - session = self.session, - pub_socket = self.socket - ) + self.disp_pub = ZMQDisplayPublisher(session=self.session, pub_socket=self.socket) def tearDown(self): """ @@ -95,14 +95,18 @@ def test_thread_local_hooks(self): initialised with an empty list for the display hooks """ assert self.disp_pub._hooks == [] + def hook(msg): return msg + self.disp_pub.register_hook(hook) assert self.disp_pub._hooks == [hook] q = Queue() + def set_thread_hooks(): q.put(self.disp_pub._hooks) + t = Thread(target=set_thread_hooks) t.start() thread_hooks = q.get(timeout=10) @@ -113,7 +117,7 @@ def test_publish(self): Publish should prepare the message and eventually call `send` by default. """ - data = dict(a = 1) + data = dict(a=1) assert self.session.send_count == 0 self.disp_pub.publish(data) assert self.session.send_count == 1 @@ -125,7 +129,7 @@ def test_display_hook_halts_send(self): the message has been consumed, and should not be processed (`sent`) in the normal manner. """ - data = dict(a = 1) + data = dict(a=1) hook = NoReturnDisplayHook() self.disp_pub.register_hook(hook) @@ -161,7 +165,7 @@ def test_unregister_hook(self): Once a hook is unregistered, it should not be called during `publish`. """ - data = dict(a = 1) + data = dict(a=1) hook = NoReturnDisplayHook() self.disp_pub.register_hook(hook) @@ -180,7 +184,7 @@ def test_unregister_hook(self): # at the end. # # As a result, the hook call count should *not* increase, - # but the session send count *should* increase. + # but the session send count *should* increase. # first = self.disp_pub.unregister_hook(hook) self.disp_pub.publish(data) @@ -197,5 +201,5 @@ def test_unregister_hook(self): self.assertFalse(second) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/ipykernel/tests/utils.py b/ipykernel/tests/utils.py index 91d87f86e..711c45542 100644 --- a/ipykernel/tests/utils.py +++ b/ipykernel/tests/utils.py @@ -7,16 +7,14 @@ import os import platform import sys -from tempfile import TemporaryDirectory -from time import time - from contextlib import contextmanager from queue import Empty from subprocess import STDOUT +from tempfile import TemporaryDirectory +from time import time from jupyter_client import manager - STARTUP_TIMEOUT = 60 TIMEOUT = 100 @@ -29,10 +27,11 @@ def start_new_kernel(**kwargs): Integrates with our output capturing for tests. """ - kwargs['stderr'] = STDOUT + kwargs["stderr"] = STDOUT try: import nose - kwargs['stdout'] = nose.iptest_stdstreams_fileno() + + kwargs["stdout"] = nose.iptest_stdstreams_fileno() except (ImportError, AttributeError): pass return manager.start_new_kernel(startup_timeout=STARTUP_TIMEOUT, **kwargs) @@ -54,12 +53,12 @@ def flush_channels(kc=None): validate_message(msg) -def get_reply(kc, msg_id, timeout=TIMEOUT, channel='shell'): +def get_reply(kc, msg_id, timeout=TIMEOUT, channel="shell"): t0 = time() while True: - get_msg = getattr(kc, f'get_{channel}_msg') + get_msg = getattr(kc, f"get_{channel}_msg") reply = get_msg(timeout=timeout) - if reply['parent_header']['msg_id'] == msg_id: + if reply["parent_header"]["msg_id"] == msg_id: break # Allow debugging ignored replies print(f"Ignoring reply not to {msg_id}: {reply}") @@ -69,29 +68,30 @@ def get_reply(kc, msg_id, timeout=TIMEOUT, channel='shell'): return reply -def execute(code='', kc=None, **kwargs): +def execute(code="", kc=None, **kwargs): """wrapper for doing common steps for validating an execution request""" from .test_message_spec import validate_message + if kc is None: kc = KC msg_id = kc.execute(code=code, **kwargs) reply = get_reply(kc, msg_id, TIMEOUT) - validate_message(reply, 'execute_reply', msg_id) + validate_message(reply, "execute_reply", msg_id) busy = kc.get_iopub_msg(timeout=TIMEOUT) - validate_message(busy, 'status', msg_id) - assert busy['content']['execution_state'] == 'busy' + validate_message(busy, "status", msg_id) + assert busy["content"]["execution_state"] == "busy" - if not kwargs.get('silent'): + if not kwargs.get("silent"): execute_input = kc.get_iopub_msg(timeout=TIMEOUT) - validate_message(execute_input, 'execute_input', msg_id) - assert execute_input['content']['code'] == code + validate_message(execute_input, "execute_input", msg_id) + assert execute_input["content"]["code"] == code # show tracebacks if present for debugging - if reply['content'].get('traceback'): - print('\n'.join(reply['content']['traceback']), file=sys.stderr) + if reply["content"].get("traceback"): + print("\n".join(reply["content"]["traceback"]), file=sys.stderr) + return msg_id, reply["content"] - return msg_id, reply['content'] def start_global_kernel(): """start the global kernel (if it isn't running) and return its client""" @@ -103,6 +103,7 @@ def start_global_kernel(): flush_channels(KC) return KC + @contextmanager def kernel(): """Context manager for the global kernel instance @@ -115,15 +116,19 @@ def kernel(): """ yield start_global_kernel() + def uses_kernel(test_f): """Decorator for tests that use the global kernel""" + def wrapped_test(): with kernel() as kc: test_f(kc) + wrapped_test.__doc__ = test_f.__doc__ wrapped_test.__name__ = test_f.__name__ return wrapped_test + def stop_global_kernel(): """Stop the global shared kernel instance, if it exists""" global KM, KC @@ -134,6 +139,7 @@ def stop_global_kernel(): KM.shutdown_kernel(now=True) KM = None + def new_kernel(argv=None): """Context manager for a new kernel in a subprocess @@ -143,45 +149,48 @@ def new_kernel(argv=None): ------- kernel_client: connected KernelClient instance """ - kwargs = {'stderr': STDOUT} + kwargs = {"stderr": STDOUT} try: import nose - kwargs['stdout'] = nose.iptest_stdstreams_fileno() + + kwargs["stdout"] = nose.iptest_stdstreams_fileno() except (ImportError, AttributeError): pass if argv is not None: - kwargs['extra_arguments'] = argv + kwargs["extra_arguments"] = argv return manager.run_kernel(**kwargs) + def assemble_output(get_msg): """assemble stdout/err from an execution""" - stdout = '' - stderr = '' + stdout = "" + stderr = "" while True: msg = get_msg(timeout=1) - msg_type = msg['msg_type'] - content = msg['content'] - if msg_type == 'status' and content['execution_state'] == 'idle': + msg_type = msg["msg_type"] + content = msg["content"] + if msg_type == "status" and content["execution_state"] == "idle": # idle message signals end of output break - elif msg['msg_type'] == 'stream': - if content['name'] == 'stdout': - stdout += content['text'] - elif content['name'] == 'stderr': - stderr += content['text'] + elif msg["msg_type"] == "stream": + if content["name"] == "stdout": + stdout += content["text"] + elif content["name"] == "stderr": + stderr += content["text"] else: - raise KeyError("bad stream: %r" % content['name']) + raise KeyError("bad stream: %r" % content["name"]) else: # other output, ignored pass return stdout, stderr + def wait_for_idle(kc): while True: msg = kc.get_iopub_msg(timeout=1) - msg_type = msg['msg_type'] - content = msg['content'] - if msg_type == 'status' and content['execution_state'] == 'idle': + msg_type = msg["msg_type"] + content = msg["content"] + if msg_type == "status" and content["execution_state"] == "idle": break @@ -194,6 +203,7 @@ class TemporaryWorkingDirectory(TemporaryDirectory): with TemporaryWorkingDirectory() as tmpdir: ... """ + def __enter__(self): self.old_wd = os.getcwd() os.chdir(self.name) diff --git a/ipykernel/trio_runner.py b/ipykernel/trio_runner.py index d78e516aa..b6d71183e 100644 --- a/ipykernel/trio_runner.py +++ b/ipykernel/trio_runner.py @@ -15,28 +15,25 @@ def __init__(self): def initialize(self, kernel, io_loop): kernel.shell.set_trio_runner(self) - kernel.shell.run_line_magic('autoawait', 'trio') - kernel.shell.magics_manager.magics['line']['autoawait'] = \ - lambda _: warnings.warn("Autoawait isn't allowed in Trio " - "background loop mode.") - bg_thread = threading.Thread(target=io_loop.start, daemon=True, - name='TornadoBackground') + kernel.shell.run_line_magic("autoawait", "trio") + kernel.shell.magics_manager.magics["line"]["autoawait"] = lambda _: warnings.warn( + "Autoawait isn't allowed in Trio " "background loop mode." + ) + bg_thread = threading.Thread(target=io_loop.start, daemon=True, name="TornadoBackground") bg_thread.start() def interrupt(self, signum, frame): if self._cell_cancel_scope: self._cell_cancel_scope.cancel() else: - raise Exception('Kernel interrupted but no cell is running') + raise Exception("Kernel interrupted but no cell is running") def run(self): old_sig = signal.signal(signal.SIGINT, self.interrupt) def log_nursery_exc(exc): - exc = '\n'.join(traceback.format_exception(type(exc), exc, - exc.__traceback__)) - logging.error('An exception occurred in a global nursery task.\n%s', - exc) + exc = "\n".join(traceback.format_exception(type(exc), exc, exc.__traceback__)) + logging.error("An exception occurred in a global nursery task.\n%s", exc) async def trio_main(): self._trio_token = trio.lowlevel.current_trio_token() diff --git a/ipykernel/zmqshell.py b/ipykernel/zmqshell.py index 5f33054f9..c9ae6f769 100644 --- a/ipykernel/zmqshell.py +++ b/ipykernel/zmqshell.py @@ -19,35 +19,28 @@ import warnings from threading import local -from IPython.core.interactiveshell import ( - InteractiveShell, InteractiveShellABC -) -from IPython.core import page +from IPython.core import page, payloadpage from IPython.core.autocall import ZMQExitAutocall from IPython.core.displaypub import DisplayPublisher from IPython.core.error import UsageError -from IPython.core.magics import MacroToEdit, CodeMagics -from IPython.core.magic import magics_class, line_magic, Magics -from IPython.core import payloadpage +from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC +from IPython.core.magic import Magics, line_magic, magics_class +from IPython.core.magics import CodeMagics, MacroToEdit from IPython.core.usage import default_banner -from IPython.display import display, Javascript -from ipykernel import ( - get_connection_file, get_connection_info, connect_qtconsole -) +from IPython.display import Javascript, display from IPython.utils import openpy -from ipykernel.jsonutil import json_clean, encode_images from IPython.utils.process import arg_split, system -from traitlets import ( - Instance, Type, Dict, CBool, CBytes, Any, default, observe -) -from ipykernel.displayhook import ZMQShellDisplayHook - +from jupyter_client.session import Session, extract_header from jupyter_core.paths import jupyter_runtime_dir -from jupyter_client.session import extract_header, Session +from traitlets import Any, CBool, CBytes, Dict, Instance, Type, default, observe + +from ipykernel import connect_qtconsole, get_connection_file, get_connection_info +from ipykernel.displayhook import ZMQShellDisplayHook +from ipykernel.jsonutil import encode_images, json_clean -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Functions and classes -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- class ZMQDisplayPublisher(DisplayPublisher): @@ -56,11 +49,11 @@ class ZMQDisplayPublisher(DisplayPublisher): session = Instance(Session, allow_none=True) pub_socket = Any(allow_none=True) parent_header = Dict({}) - topic = CBytes(b'display_data') + topic = CBytes(b"display_data") # thread_local: # An attribute used to ensure the correct output message - # is processed. See ipykernel Issue 113 for a discussion. + # is processed. See ipykernel Issue 113 for a discussion. _thread_local = Any() def set_parent(self, parent): @@ -72,14 +65,14 @@ def _flush_streams(self): sys.stdout.flush() sys.stderr.flush() - @default('_thread_local') + @default("_thread_local") def _default_thread_local(self): """Initialize our thread local storage""" return local() @property def _hooks(self): - if not hasattr(self._thread_local, 'hooks'): + if not hasattr(self._thread_local, "hooks"): # create new list for a new thread self._thread_local.hooks = [] return self._thread_local.hooks @@ -113,19 +106,16 @@ def publish( transient = {} self._validate_data(data, metadata) content = {} - content['data'] = encode_images(data) - content['metadata'] = metadata - content['transient'] = transient + content["data"] = encode_images(data) + content["metadata"] = metadata + content["transient"] = transient - msg_type = 'update_display_data' if update else 'display_data' + msg_type = "update_display_data" if update else "display_data" # Use 2-stage process to send a message, # in order to put it through the transform # hooks before potentially sending. - msg = self.session.msg( - msg_type, json_clean(content), - parent=self.parent_header - ) + msg = self.session.msg(msg_type, json_clean(content), parent=self.parent_header) # Each transform either returns a new # message or None. If None is returned, @@ -136,7 +126,9 @@ def publish( return self.session.send( - self.pub_socket, msg, ident=self.topic, + self.pub_socket, + msg, + ident=self.topic, ) def clear_output(self, wait=False): @@ -153,8 +145,11 @@ def clear_output(self, wait=False): content = dict(wait=wait) self._flush_streams() self.session.send( - self.pub_socket, 'clear_output', content, - parent=self.parent_header, ident=self.topic, + self.pub_socket, + "clear_output", + content, + parent=self.parent_header, + ident=self.topic, ) def register_hook(self, hook): @@ -199,9 +194,9 @@ def unregister_hook(self, hook): @magics_class class KernelMagics(Magics): - #------------------------------------------------------------------------ + # ------------------------------------------------------------------------ # Magic overrides - #------------------------------------------------------------------------ + # ------------------------------------------------------------------------ # Once the base class stops inheriting from magic, this code needs to be # moved into a separate machinery as well. For now, at least isolate here # the magics which this class needs to implement differently from the base @@ -210,7 +205,7 @@ class KernelMagics(Magics): _find_edit_target = CodeMagics._find_edit_target @line_magic - def edit(self, parameter_s='', last_call=['','']): + def edit(self, parameter_s="", last_call=["", ""]): """Bring up an editor and execute the resulting code. Usage: @@ -287,7 +282,7 @@ def edit(self, parameter_s='', last_call=['','']): Note that %edit is also available through the alias %ed. """ - opts,args = self.parse_options(parameter_s, 'prn:') + opts, args = self.parse_options(parameter_s, "prn:") try: filename, lineno, _ = CodeMagics._find_edit_target(self.shell, args, opts, last_call) @@ -300,11 +295,7 @@ def edit(self, parameter_s='', last_call=['','']): # directory of client and kernel don't match filename = os.path.abspath(filename) - payload = { - 'source' : 'edit_magic', - 'filename' : filename, - 'line_number' : lineno - } + payload = {"source": "edit_magic", "filename": filename, "line_number": lineno} self.shell.payload_manager.write_payload(payload) # A few magics that are adapted to the specifics of using pexpect and a @@ -313,14 +304,14 @@ def edit(self, parameter_s='', last_call=['','']): @line_magic def clear(self, arg_s): """Clear the terminal.""" - if os.name == 'posix': + if os.name == "posix": self.shell.system("clear") else: self.shell.system("cls") - if os.name == 'nt': + if os.name == "nt": # This is the usual name in windows - cls = line_magic('cls')(clear) + cls = line_magic("cls")(clear) # Terminal pagers won't work over pexpect, but we do have our own pager @@ -330,23 +321,23 @@ def less(self, arg_s): Files ending in .py are syntax-highlighted.""" if not arg_s: - raise UsageError('Missing filename.') + raise UsageError("Missing filename.") - if arg_s.endswith('.py'): + if arg_s.endswith(".py"): cont = self.shell.pycolorize(openpy.read_py_file(arg_s, skip_encoding_cookie=False)) else: cont = open(arg_s).read() page.page(cont) - more = line_magic('more')(less) + more = line_magic("more")(less) # Man calls a pager, so we also need to redefine it - if os.name == 'posix': + if os.name == "posix": + @line_magic def man(self, arg_s): """Find the man page for the given command and display in pager.""" - page.page(self.shell.getoutput('man %s | col -b' % arg_s, - split=False)) + page.page(self.shell.getoutput("man %s | col -b" % arg_s, split=False)) @line_magic def connect_info(self, arg_s): @@ -373,9 +364,8 @@ def connect_info(self, arg_s): if jupyter_runtime_dir() == os.path.dirname(connection_file): connection_file = os.path.basename(connection_file) - - print (info + '\n') - print ( + print(info + "\n") + print( f"Paste the above JSON into a file, and connect with:\n" f" $> jupyter --existing \n" f"or, if you are local, you can connect with just:\n" @@ -395,12 +385,13 @@ def qtconsole(self, arg_s): # %qtconsole should imply bind_kernel for engines: # FIXME: move to ipyparallel Kernel subclass - if 'ipyparallel' in sys.modules: + if "ipyparallel" in sys.modules: from ipyparallel import bind_kernel + bind_kernel() try: - connect_qtconsole(argv=arg_split(arg_s, os.name=='posix')) + connect_qtconsole(argv=arg_split(arg_s, os.name == "posix")) except Exception as e: warnings.warn("Could not start qtconsole: %r" % e) return @@ -423,8 +414,9 @@ def autosave(self, arg_s): # javascript wants milliseconds milliseconds = 1000 * interval - display(Javascript("IPython.notebook.set_autosave_interval(%i)" % milliseconds), - include=['application/javascript'] + display( + Javascript("IPython.notebook.set_autosave_interval(%i)" % milliseconds), + include=["application/javascript"], ) if interval: print("Autosaving every %i seconds" % interval) @@ -441,7 +433,7 @@ class ZMQInteractiveShell(InteractiveShell): kernel = Any() parent_header = Any() - @default('banner1') + @default("banner1") def _default_banner1(self): return default_banner @@ -455,19 +447,19 @@ def _default_banner1(self): exiter = Instance(ZMQExitAutocall) - @default('exiter') + @default("exiter") def _default_exiter(self): return ZMQExitAutocall(self) - @observe('exit_now') + @observe("exit_now") def _update_exit_now(self, change): """stop eventloop when exit_now fires""" - if change['new']: - if hasattr(self.kernel, 'io_loop'): + if change["new"]: + if hasattr(self.kernel, "io_loop"): loop = self.kernel.io_loop loop.call_later(0.1, loop.stop) if self.kernel.eventloop: - exit_hook = getattr(self.kernel.eventloop, 'exit_hook', None) + exit_hook = getattr(self.kernel.eventloop, "exit_hook", None) if exit_hook: exit_hook(self.kernel) @@ -477,6 +469,7 @@ def _update_exit_now(self, change): # interactive input being read; we provide event loop support in ipkernel def enable_gui(self, gui): from .eventloops import enable_gui as real_enable_gui + try: real_enable_gui(gui) self.active_eventloop = gui @@ -487,17 +480,17 @@ def init_environment(self): """Configure the user's environment.""" env = os.environ # These two ensure 'ls' produces nice coloring on BSD-derived systems - env['TERM'] = 'xterm-color' - env['CLICOLOR'] = '1' + env["TERM"] = "xterm-color" + env["CLICOLOR"] = "1" # Since normal pagers don't work at all (over pexpect we don't have # single-key control of the subprocess), try to disable paging in # subprocesses as much as possible. - env['PAGER'] = 'cat' - env['GIT_PAGER'] = 'cat' + env["PAGER"] = "cat" + env["GIT_PAGER"] = "cat" def init_hooks(self): super().init_hooks() - self.set_hook('show_in_pager', page.as_hook(payloadpage.page), 99) + self.set_hook("show_in_pager", page.as_hook(payloadpage.page), 99) def init_data_pub(self): """Delay datapub init until request, for deprecation warnings""" @@ -505,9 +498,12 @@ def init_data_pub(self): @property def data_pub(self): - if not hasattr(self, '_data_pub'): - warnings.warn("InteractiveShell.data_pub is deprecated outside IPython parallel.", - DeprecationWarning, stacklevel=2) + if not hasattr(self, "_data_pub"): + warnings.warn( + "InteractiveShell.data_pub is deprecated outside IPython parallel.", + DeprecationWarning, + stacklevel=2, + ) self._data_pub = self.data_pub_class(parent=self) self._data_pub.session = self.display_pub.session @@ -520,9 +516,9 @@ def data_pub(self, pub): def ask_exit(self): """Engage the exit actions.""" - self.exit_now = (not self.keepkernel_on_exit) + self.exit_now = not self.keepkernel_on_exit payload = dict( - source='ask_exit', + source="ask_exit", keepkernel=self.keepkernel_on_exit, ) self.payload_manager.write_payload(payload) @@ -537,9 +533,9 @@ def _showtraceback(self, etype, evalue, stb): sys.stderr.flush() exc_content = { - 'traceback' : stb, - 'ename' : str(etype.__name__), - 'evalue' : str(evalue), + "traceback": stb, + "ename": str(etype.__name__), + "evalue": str(evalue), } dh = self.displayhook @@ -547,7 +543,7 @@ def _showtraceback(self, etype, evalue, stb): # to pick up topic = None if dh.topic: - topic = dh.topic.replace(b'execute_result', b'error') + topic = dh.topic.replace(b"execute_result", b"error") dh.session.send( dh.pub_socket, @@ -565,7 +561,7 @@ def set_next_input(self, text, replace=False): """Send the specified text to the frontend to be presented at the next input cell.""" payload = dict( - source='set_next_input', + source="set_next_input", text=text, replace=replace, ) @@ -576,7 +572,7 @@ def set_parent(self, parent): self.parent_header = parent self.displayhook.set_parent(parent) self.display_pub.set_parent(parent) - if hasattr(self, '_data_pub'): + if hasattr(self, "_data_pub"): self.data_pub.set_parent(parent) try: sys.stdout.set_parent(parent) @@ -593,7 +589,7 @@ def get_parent(self): def init_magics(self): super().init_magics() self.register_magics(KernelMagics) - self.magics_manager.register_alias('ed', 'edit') + self.magics_manager.register_alias("ed", "edit") def init_virtualenv(self): # Overridden not to do virtualenv detection, because it's probably @@ -612,7 +608,7 @@ def system_piped(self, cmd): not supported. Should not be a command that expects input other than simple text. """ - if cmd.rstrip().endswith('&'): + if cmd.rstrip().endswith("&"): # this is *far* from a rigorous test # We do not support backgrounding processes because we either use # pexpect or pipes to read from. Users can always just call @@ -625,15 +621,16 @@ def system_piped(self, cmd): # Instead, we store the exit_code in user_ns. # Also, protect system call from UNC paths on Windows here too # as is done in InteractiveShell.system_raw - if sys.platform == 'win32': + if sys.platform == "win32": cmd = self.var_expand(cmd, depth=1) from IPython.utils._process_win32 import AvoidUNCPath + with AvoidUNCPath() as path: if path is not None: - cmd = 'pushd %s &&%s' % (path, cmd) - self.user_ns['_exit_code'] = system(cmd) + cmd = "pushd %s &&%s" % (path, cmd) + self.user_ns["_exit_code"] = system(cmd) else: - self.user_ns['_exit_code'] = system(self.var_expand(cmd, depth=1)) + self.user_ns["_exit_code"] = system(self.var_expand(cmd, depth=1)) # Ensure new system_piped implementation is used system = system_piped diff --git a/ipykernel_launcher.py b/ipykernel_launcher.py index 009d21d9e..49aa2651a 100644 --- a/ipykernel_launcher.py +++ b/ipykernel_launcher.py @@ -6,11 +6,12 @@ import sys -if __name__ == '__main__': +if __name__ == "__main__": # Remove the CWD from sys.path while we load stuff. # This is added back by InteractiveShellApp.init_path() - if sys.path[0] == '': + if sys.path[0] == "": del sys.path[0] from ipykernel import kernelapp as app + app.launch_new_instance() diff --git a/setup.py b/setup.py index 1106d77be..42be037c0 100644 --- a/setup.py +++ b/setup.py @@ -3,16 +3,16 @@ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. -import sys -from glob import glob import os import shutil +import sys +from glob import glob from setuptools import setup from setuptools.command.bdist_egg import bdist_egg # the name of the package -name = 'ipykernel' +name = "ipykernel" class bdist_egg_disabled(bdist_egg): @@ -21,6 +21,7 @@ class bdist_egg_disabled(bdist_egg): Prevents setup.py install from performing setuptools' default easy_install, which it should never ever do. """ + def run(self): sys.exit("Aborting implicit building of eggs. Use `pip install .` to install from source.") @@ -31,45 +32,45 @@ def run(self): packages = [] for d, _, _ in os.walk(pjoin(here, name)): - if os.path.exists(pjoin(d, '__init__.py')): - packages.append(d[len(here)+1:].replace(os.path.sep, '.')) + if os.path.exists(pjoin(d, "__init__.py")): + packages.append(d[len(here) + 1 :].replace(os.path.sep, ".")) package_data = { - 'ipykernel': ['resources/*.*'], + "ipykernel": ["resources/*.*"], } -with open(pjoin(here, 'README.md')) as fid: +with open(pjoin(here, "README.md")) as fid: LONG_DESCRIPTION = fid.read() setup_args = dict( name=name, cmdclass={ - 'bdist_egg': bdist_egg if 'bdist_egg' in sys.argv else bdist_egg_disabled, + "bdist_egg": bdist_egg if "bdist_egg" in sys.argv else bdist_egg_disabled, }, - scripts=glob(pjoin('scripts', '*')), + scripts=glob(pjoin("scripts", "*")), packages=packages, - py_modules=['ipykernel_launcher'], + py_modules=["ipykernel_launcher"], package_data=package_data, description="IPython Kernel for Jupyter", long_description_content_type="text/markdown", - author='IPython Development Team', - author_email='ipython-dev@scipy.org', - url='https://ipython.org', - license='BSD', + author="IPython Development Team", + author_email="ipython-dev@scipy.org", + url="https://ipython.org", + license="BSD", long_description=LONG_DESCRIPTION, platforms="Linux, Mac OS X, Windows", - keywords=['Interactive', 'Interpreter', 'Shell', 'Web'], - python_requires='>=3.7', + keywords=["Interactive", "Interpreter", "Shell", "Web"], + python_requires=">=3.7", install_requires=[ - 'debugpy>=1.0.0,<2.0', - 'ipython>=7.23.1', - 'traitlets>=5.1.0,<6.0', - 'jupyter_client<8.0', - 'tornado>=5.0,<7.0', - 'matplotlib-inline>=0.1.0,<0.2.0', + "debugpy>=1.0.0,<2.0", + "ipython>=7.23.1", + "traitlets>=5.1.0,<6.0", + "jupyter_client<8.0", + "tornado>=5.0,<7.0", + "matplotlib-inline>=0.1.0,<0.2.0", 'appnope;platform_system=="Darwin"', - 'psutil', - 'nest_asyncio', + "psutil", + "nest_asyncio", ], extras_require={ "test": [ @@ -78,45 +79,45 @@ def run(self): "flaky", "ipyparallel", "pre-commit", - "pytest-timeout" + "pytest-timeout", ], }, classifiers=[ - 'Intended Audience :: Developers', - 'Intended Audience :: System Administrators', - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: BSD License', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9' + "Intended Audience :: Developers", + "Intended Audience :: System Administrators", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: BSD License", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", ], ) -if any(a.startswith(('bdist', 'install')) for a in sys.argv): +if any(a.startswith(("bdist", "install")) for a in sys.argv): sys.path.insert(0, here) - from ipykernel.kernelspec import write_kernel_spec, make_ipkernel_cmd, KERNEL_NAME + from ipykernel.kernelspec import KERNEL_NAME, make_ipkernel_cmd, write_kernel_spec # When building a wheel, the executable specified in the kernelspec is simply 'python'. - if any(a.startswith('bdist') for a in sys.argv): - argv = make_ipkernel_cmd(executable='python') + if any(a.startswith("bdist") for a in sys.argv): + argv = make_ipkernel_cmd(executable="python") # When installing from source, the full `sys.executable` can be used. - if any(a.startswith('install') for a in sys.argv): + if any(a.startswith("install") for a in sys.argv): argv = make_ipkernel_cmd() - dest = os.path.join(here, 'data_kernelspec') + dest = os.path.join(here, "data_kernelspec") if os.path.exists(dest): shutil.rmtree(dest) - write_kernel_spec(dest, overrides={'argv': argv}) + write_kernel_spec(dest, overrides={"argv": argv}) - setup_args['data_files'] = [ + setup_args["data_files"] = [ ( - pjoin('share', 'jupyter', 'kernels', KERNEL_NAME), - glob(pjoin('data_kernelspec', '*')), + pjoin("share", "jupyter", "kernels", KERNEL_NAME), + glob(pjoin("data_kernelspec", "*")), ) ] -if __name__ == '__main__': +if __name__ == "__main__": setup(**setup_args)