Skip to content

Commit

Permalink
Discriminate between cancellation from our sigint handler and other c…
Browse files Browse the repository at this point in the history
…ancellations

PR #105 issue #104
  • Loading branch information
vxgmichel authored Feb 3, 2023
2 parents 08d67de + e2ec085 commit db0a900
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 10 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@ 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
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 }}
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
12 changes: 10 additions & 2 deletions aioconsole/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -155,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:
Expand All @@ -163,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
Expand Down Expand Up @@ -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()
Expand Down
7 changes: 6 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
19 changes: 14 additions & 5 deletions tests/test_interact.py
Original file line number Diff line number Diff line change
Expand Up @@ -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?"
Expand Down Expand Up @@ -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

0 comments on commit db0a900

Please sign in to comment.