From d0046f3536e086294f892c7591b20b59e6fdff93 Mon Sep 17 00:00:00 2001 From: Vincent Michel Date: Tue, 31 Jan 2023 11:23:35 +0100 Subject: [PATCH 1/3] Discriminate between cancellation after our sigint handler and other cancellations --- .github/workflows/ci.yml | 2 +- .pre-commit-config.yaml | 2 +- aioconsole/console.py | 8 ++++++++ setup.py | 7 ++++++- tests/test_interact.py | 19 ++++++++++++++----- 5 files changed, 30 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f3120ec..a7a329d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11.0-alpha.2", "pypy-3.7", "pypy-3.8"] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "pypy-3.7", "pypy-3.8"] exclude: # The install of `atomicwrites` fails for some reason - os: windows-latest diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 892fccd..a3fff3b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ repos: hooks: - id: black language_version: python3 -- repo: https://gitlab.com/pycqa/flake8 +- repo: https://github.com/pycqa/flake8 rev: 4.0.1 hooks: - id: flake8 diff --git a/aioconsole/console.py b/aioconsole/console.py index ae75917..b88d0ca 100644 --- a/aioconsole/console.py +++ b/aioconsole/console.py @@ -63,6 +63,8 @@ def __init__( self.locals["print"] = self.print self.locals["help"] = self.help self.locals["ainput"] = self.ainput + # Internals + self._sigint_received = False @functools.wraps(print) def print(self, *args, **kwargs): @@ -120,6 +122,7 @@ def resetbuffer(self): self.buffer = [] def handle_sigint(self, task): + self._sigint_received = True task.cancel() if task._fut_waiter._loop is not self.loop: task._wakeup(task._fut_waiter) @@ -200,6 +203,11 @@ async def _interact(self, banner=None): else: more = await self.push(line) except asyncio.CancelledError: + # Not our cancellation + if not self._sigint_received: + raise + # Manage cancellation + self._sigint_received = False self.write("\nKeyboardInterrupt\n") await self.flush() self.resetbuffer() diff --git a/setup.py b/setup.py index c4d8063..61d87a4 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,12 @@ packages=["aioconsole"], entry_points={"console_scripts": ["apython = aioconsole:run_apython"]}, setup_requires=["pytest-runner" if TESTING else ""], - tests_require=["pytest", "pytest-asyncio", "pytest-cov", "pytest-repeat"], + tests_require=[ + "pytest", + "pytest-asyncio", + "pytest-cov", + "pytest-repeat", + ], license="GPLv3", python_requires=">=3.7", classifiers=CLASSIFIERS, diff --git a/tests/test_interact.py b/tests/test_interact.py index 84ffaa1..04c5ca2 100644 --- a/tests/test_interact.py +++ b/tests/test_interact.py @@ -91,11 +91,7 @@ async def test_interact_syntax_error(event_loop, monkeypatch): # Skip line await reader.readline() await assert_stream(reader, " a b") - if ( - sys.version_info >= (3, 10, 0) - and sys.version_info < (3, 10, 1) - or sys.version_info >= (3, 11) - ): + if sys.version_info >= (3, 10, 0) and sys.version_info < (3, 10, 1): await assert_stream(reader, " ^^^", loose=True) await assert_stream( reader, "SyntaxError: invalid syntax. Perhaps you forgot a comma?" @@ -158,3 +154,16 @@ async def test_interact_multiple_indented_lines(event_loop, monkeypatch): await interact(banner=banner, stop=False) await assert_stream(reader, banner) await assert_stream(reader, sys.ps1 + sys.ps2 * 3 + sys.ps1 + "1\n2") + + +@pytest.mark.asyncio +async def test_interact_cancellation(event_loop, monkeypatch): + with stdcontrol(event_loop, monkeypatch) as (reader, writer): + banner = "A BANNER" + task = asyncio.ensure_future(interact(banner=banner, stop=False)) + # Wait for banner + await assert_stream(reader, banner) + task.cancel() + with pytest.raises(asyncio.CancelledError): + await task + assert task.cancelled From 1aef2745b2361426f67b619b1736f8af0dee8748 Mon Sep 17 00:00:00 2001 From: Vincent Michel Date: Tue, 31 Jan 2023 14:20:16 +0100 Subject: [PATCH 2/3] Avoid using loop.stop() --- aioconsole/console.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aioconsole/console.py b/aioconsole/console.py index b88d0ca..199671f 100644 --- a/aioconsole/console.py +++ b/aioconsole/console.py @@ -158,6 +158,8 @@ async def interact(self, banner=None, stop=True, handle_sigint=True): if handle_sigint: self.add_sigint_handler() await self._interact(banner) + if stop: + raise SystemExit # Exit except SystemExit: if stop: @@ -166,8 +168,6 @@ async def interact(self, banner=None, stop=True, handle_sigint=True): finally: if handle_sigint: self.remove_sigint_handler() - if stop: - self.loop.stop() async def _interact(self, banner=None): # Get ps1 and ps2 From e2ec085a2f1e5df84ca9fd0f04597159577908b3 Mon Sep 17 00:00:00 2001 From: Vincent Michel Date: Tue, 31 Jan 2023 14:20:50 +0100 Subject: [PATCH 3/3] Something wrong with coverage[toml]>=5.2.1 on macos --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a7a329d..7b4af7f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,6 +26,9 @@ jobs: python-version: "pypy-3.7" - os: windows-latest python-version: "pypy-3.8" + # The install of pytest-cov fails for some reason + - os: macos-latest + python-version: "3.9" env: OS: ${{ matrix.os }} PYTHON: ${{ matrix.python-version }}