From 544fdb2b222c760b8ae457bbe94d2bf5cfc59715 Mon Sep 17 00:00:00 2001 From: Lumir Balhar Date: Tue, 7 Sep 2021 09:01:49 +0200 Subject: [PATCH] Make ipykernel work without debugpy debugpy is an optional dependency because only frontends with debugging support (Jupyter lab) can really use its features. Fixes: https://github.com/ipython/ipykernel/issues/712 --- .github/workflows/ci.yml | 41 ++++++++++++++++++++++++++++++++++++++++ ipykernel/ipkernel.py | 33 ++++++++++++++++++++------------ ipykernel/kernelspec.py | 4 +++- 3 files changed, 65 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 82bd2fb50..86fc6ff68 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -110,3 +110,44 @@ jobs: - name: Check Docstrings run: | velin . --check --compact + test_without_debugpy: + runs-on: ${{ matrix.os }}-latest + strategy: + fail-fast: false + matrix: + os: [ubuntu] + python-version: [ '3.9' ] + steps: + - name: Checkout + uses: actions/checkout@v1 + - name: Install Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + architecture: 'x64' + - name: Upgrade packaging dependencies + run: | + pip install --upgrade pip setuptools wheel --user + - name: Get pip cache dir + id: pip-cache + run: | + echo "::set-output name=dir::$(pip cache dir)" + - name: Cache pip + uses: actions/cache@v1 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('setup.py') }} + restore-keys: | + ${{ runner.os }}-pip-${{ matrix.python-version }}- + ${{ runner.os }}-pip- + - name: Install the Python dependencies without debugpy + run: | + pip install --pre --upgrade --upgrade-strategy=eager .[test] + pip uninstall --yes debugpy + - name: List installed packages + run: | + pip freeze + - name: Run the tests + timeout-minutes: 10 + run: | + pytest ipykernel -vv -s --durations 10 diff --git a/ipykernel/ipkernel.py b/ipykernel/ipkernel.py index 6943deacc..2cc8eab54 100644 --- a/ipykernel/ipkernel.py +++ b/ipykernel/ipkernel.py @@ -17,7 +17,6 @@ from .kernelbase import Kernel as KernelBase from .zmqshell import ZMQInteractiveShell from .eventloops import _use_appnope -from .debugger import Debugger from .compiler import XCachingCompiler try: @@ -34,6 +33,13 @@ except ImportError: _use_experimental_60_completion = False +try: + import debugpy + from .debugger import Debugger + _is_debugpy_available = True +except ImportError: + _is_debugpy_available = False + _EXPERIMENTAL_KEY_NAME = '_jupyter_types_experimental' @@ -46,7 +52,7 @@ class IPythonKernel(KernelBase): 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) + debugpy_stream = Instance(ZMQStream, allow_none=True) if _is_debugpy_available else None user_module = Any() @observe('user_module') @@ -72,11 +78,12 @@ def __init__(self, **kwargs): super(IPythonKernel, self).__init__(**kwargs) # Initialize the Debugger - self.debugger = Debugger(self.log, - self.debugpy_stream, - self._publish_debug_event, - self.debug_shell_socket, - self.session) + if _is_debugpy_available: + self.debugger = Debugger(self.log, + self.debugpy_stream, + self._publish_debug_event, + self.debug_shell_socket, + self.session) # Initialize the InteractiveShell subclass self.shell = self.shell_class.instance(parent=self, @@ -152,10 +159,11 @@ def __init__(self, **kwargs): } def dispatch_debugpy(self, msg): - # The first frame is the socket id, we can drop it - frame = msg[1].bytes.decode('utf-8') - self.log.debug("Debugpy received: %s", frame) - self.debugger.tcp_client.receive_dap_frame(frame) + if _is_debugpy_available: + # The first frame is the socket id, we can drop it + frame = msg[1].bytes.decode('utf-8') + self.log.debug("Debugpy received: %s", frame) + self.debugger.tcp_client.receive_dap_frame(frame) @property def banner(self): @@ -414,7 +422,8 @@ def do_complete(self, code, cursor_pos): 'status' : 'ok'} async def do_debug_request(self, msg): - return await self.debugger.process_request(msg) + if _is_debugpy_available: + return await self.debugger.process_request(msg) def _experimental_do_complete(self, code, cursor_pos): """ diff --git a/ipykernel/kernelspec.py b/ipykernel/kernelspec.py index 5f93c9fff..c7514084a 100644 --- a/ipykernel/kernelspec.py +++ b/ipykernel/kernelspec.py @@ -13,6 +13,8 @@ from jupyter_client.kernelspec import KernelSpecManager +from .ipkernel import _is_debugpy_available + pjoin = os.path.join KERNEL_NAME = 'python%i' % sys.version_info[0] @@ -52,7 +54,7 @@ def get_kernel_dict(extra_arguments=None): 'argv': make_ipkernel_cmd(extra_arguments=extra_arguments), 'display_name': 'Python %i (ipykernel)' % sys.version_info[0], 'language': 'python', - 'metadata': { 'debugger': True} + 'metadata': { 'debugger': _is_debugpy_available} }