Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion nix/tui.nix
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ let
src = ../ui-tui;
npmDeps = pkgs.fetchNpmDeps {
inherit src;
hash = "sha256-kJdrhcyCtRTecQBMYbv05ZBD0trnKRbpKhej5eGDJpw=";
hash = "sha256-q3Dqx7B9AK/H7ji/XoMkLOxUNH0uTUqoemu+hSiqr5I=";
};

npm = hermesNpmLib.mkNpmPassthru { folder = "ui-tui"; attr = "tui"; pname = "hermes-tui"; };
Expand Down
2 changes: 1 addition & 1 deletion nix/web.nix
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ let
src = ../web;
npmDeps = pkgs.fetchNpmDeps {
inherit src;
hash = "sha256-ZIcAGppxrXBIIdRgV8V2HHFVkwzFjwUgLLfc5wDDLo8=";
hash = "sha256-peA7M8lvRRVnsM6vl4QbYEr0ElEaGU4zsCENnq8TLBc=";
};

npm = hermesNpmLib.mkNpmPassthru { folder = "web"; attr = "web"; pname = "hermes-web"; };
Expand Down
14 changes: 7 additions & 7 deletions plugins/platforms/discord/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -1906,13 +1906,13 @@ async def join_voice_channel(self, channel) -> bool:
try:
import nacl # noqa: F401 — presence check only
except ImportError:
logger.warning(
"Discord voice channel join failed: PyNaCl is not installed. "
"The `voice` extra no longer ships PyNaCl (vulnerable pin). "
"Install it manually (`pip install PyNaCl>=1.6.2`) to use "
"Discord voice channels."
)
return False
# Raise so GatewayRunner._handle_voice_channel_join can surface the
# PyNaCl-specific install guidance (return False falls through to the
# generic "Check bot permissions" message — Codex PR #25).
raise RuntimeError(
"PyNaCl is not installed. The voice extra no longer ships PyNaCl "
"(vulnerable pin). Install manually: pip install PyNaCl>=1.6.2"
) from None

async with self._voice_locks.setdefault(guild_id, asyncio.Lock()):
# Already connected in this guild?
Expand Down
16 changes: 8 additions & 8 deletions tests/gateway/test_discord_race_polish.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ async def slow_connect(self):
from plugins.platforms.discord import adapter as discord_mod
# Ensure the PyNaCl guard inside join_voice_channel is a no-op for this
# test (the test venv may not have nacl). Use patch.dict so the mock
# doesn't leak into other test files in the same pytest session.
# does not leak into other test files in the same pytest session.
with patch.dict("sys.modules", {"nacl": MagicMock()}):
with patch.object(discord_mod, "VoiceReceiver",
MagicMock(return_value=MagicMock(start=lambda: None))):
Expand All @@ -88,11 +88,11 @@ async def slow_connect(self):
# ---------------------------------------------------------------------------

@pytest.mark.asyncio
async def test_join_voice_returns_false_when_pynacl_missing():
"""When PyNaCl is not installed, join_voice_channel must fail fast with
a logged warning instead of crashing inside channel.connect() with an
opaque missing-module error. The `voice` extra no longer ships PyNaCl
(vulnerable pin), so this guard is the user-facing safety net."""
async def test_join_voice_raises_when_pynacl_missing():
"""When PyNaCl is not installed, join_voice_channel must raise RuntimeError
so GatewayRunner._handle_voice_channel_join can surface the PyNaCl-specific
install guidance. Returning False would fall through to the generic
"Check bot permissions" message instead."""
import builtins

adapter = _make_adapter()
Expand All @@ -108,9 +108,9 @@ def _block_nacl(name, *args, **kwargs):
channel.guild.id = 123

with patch("builtins.__import__", side_effect=_block_nacl):
result = await adapter.join_voice_channel(channel)
with pytest.raises(RuntimeError, match="PyNaCl is not installed"):
await adapter.join_voice_channel(channel)

assert result is False
# channel.connect() must NOT have been called — the guard fires before it.
channel.connect.assert_not_called()

Expand Down
7 changes: 6 additions & 1 deletion tools/lazy_deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,12 @@ def active_features() -> list[str]:
"""
active = []
for feature, specs in LAZY_DEPS.items():
if any(_is_present(s) for s in specs):
# Check only the first spec (the primary/unique package for this feature).
# Checking any spec would cause false activations when a transitive package
# (e.g. aiohttp, cbor2, starlette) is installed by a different feature —
# see Codex review on PR #25. By convention the first tuple element must be
# a package installed exclusively by this feature.
if _is_present(specs[0]):
active.append(feature)
return active

Expand Down
Loading