Skip to content

Commit

Permalink
call pthred_sigmask to restore CTRL-C handling
Browse files Browse the repository at this point in the history
  • Loading branch information
dholth committed Oct 28, 2023
1 parent 52b84ac commit b5976fc
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 1 deletion.
22 changes: 22 additions & 0 deletions conda_libmamba_solver/mamba_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
# 2022.11.14: only keeping channel prioritization and context initialization logic now

import logging
import signal
from functools import lru_cache
from importlib.metadata import version
from typing import Dict

import libmambapy as api
from conda.base.constants import ChannelPriority
from conda.base.context import context
from conda.common.compat import on_win

log = logging.getLogger(f"conda.{__name__}")

Expand Down Expand Up @@ -84,8 +86,28 @@ def set_channel_priorities(index: Dict[str, "_ChannelRepoInfo"], has_priority: b
return index


_once = False


def init_api_context() -> api.Context:
global _once

api_ctx = api.Context()
# Override mamba's signal handling which would otherwise disable CTRL-C etc.
# in Python. The signal handling doesn't appear to be used in solver, but is
# definitely used to make other parts of libmamba interruptible. Those parts
# of libmamba might also set and reset the signal handler. An alternative
# and possible better solution would be to override libmamba's signal
# handler to also call
# https://docs.python.org/3/c-api/exceptions.html#c.PyErr_SetInterruptEx or
# to have an overridable callback for that system, that would then call
# PyErr_SetInterruptEx.
if not _once:
_once = True
if not on_win:
signal.pthread_sigmask(signal.SIG_UNBLOCK, signal.valid_signals())
else:
signal.signal(signal.SIGINT, signal.SIG_DFL)

# Output params
# We use this getattr() trick to guarantee backwards compatibility
Expand Down
19 changes: 19 additions & 0 deletions news/339-pthread-sigmask
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
### Enhancements

* <news item>

### Bug fixes

* Call `pthread_sigmask()` to override Mamba's signal handling, allowing `CTRL-C` to raise `KeyboardInterrupt` in Python. (#339)

### Deprecations

* <news item>

### Docs

* <news item>

### Other

* <news item>
38 changes: 37 additions & 1 deletion tests/test_workarounds.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
# Copyright (C) 2023 conda
# SPDX-License-Identifier: BSD-3-Clause
import json
import signal
import sys
from subprocess import PIPE, check_call, run
import time
from subprocess import PIPE, Popen, check_call, run

import pytest


def test_matchspec_star_version():
Expand Down Expand Up @@ -56,3 +60,35 @@ def test_build_string_filters():
assert pkg["version"].startswith("3.8")
if pkg["name"] == "numpy":
assert "py38" in pkg["build_string"]


@pytest.mark.parametrize("stage", ["Collecting package metadata", "Solving environment"])
def test_ctrl_c(stage):
p = Popen(
[
sys.executable,
"-m",
"conda",
"create",
"-p",
"UNUSED",
"--dry-run",
"--solver=libmamba",
"--override-channels",
"--channel=conda-forge",
"vaex",
],
text=True,
stdout=PIPE,
stderr=PIPE,
)
t0 = time.time()
while stage not in p.stdout.readline():
time.sleep(0.1)
if time.time() - t0 > 30:
raise RuntimeError("Timeout")

p.send_signal(signal.SIGINT if sys.platform != "win32" else signal.CTRL_C_EVENT)
p.wait()
assert p.returncode != 0
assert "KeyboardInterrupt" in p.stdout.read() + p.stderr.read()

0 comments on commit b5976fc

Please sign in to comment.