Handle empty iterables before testing their truthiness

This commit is contained in:
白鸽 2026-09-22 16:30:29 +08:00
parent 1c40a44f0c
commit 629fec3750
4 changed files with 85 additions and 6 deletions

View file

@ -737,6 +737,7 @@ class Boss:
if isinstance(args, SpecialWindowInstance):
sw: SpecialWindowInstance | None = args
else:
args = tuple(args)
sw = self.args_to_special_window(args, cwd_from) if args else None
startup_session = next(create_sessions(get_options(), special_window=sw, cwd_from=cwd_from))
startup_session.session_name = ''
@ -3303,10 +3304,11 @@ class Boss:
def _new_tab(self, args: SpecialWindowInstance | Iterable[str], cwd_from: CwdRequest | None = None, as_neighbor: bool = False) -> Tab | None:
special_window = None
if args:
if isinstance(args, SpecialWindowInstance):
special_window = args
else:
if isinstance(args, SpecialWindowInstance):
special_window = args
else:
args = tuple(args)
if args:
special_window = self.args_to_special_window(args, cwd_from=cwd_from)
if not self.os_window_map:
self.add_os_window()
@ -3607,7 +3609,7 @@ class Boss:
if file:
a(f'In file {file}:')
[a(format_bad_line(x)) for x in groups[file]]
if misc_errors:
if misc_errors := tuple(misc_errors):
a('In final effective configuration:')
for line in misc_errors:
a(line)

View file

@ -519,6 +519,7 @@ watcher_modules: dict[str, Any] = {}
def load_watch_modules(watchers: Iterable[str]) -> Watchers | None:
watchers = tuple(watchers)
if not watchers:
return None
import runpy

View file

@ -944,7 +944,7 @@ ParallelRun = Callable[[Iterable[tuple[bool, str, list[str]]]], None]
def create_specialisations(sources: dict[str, SlangFile], build_dir: str) -> Iterator[Command]:
for _, base_build, _, _, sfile in iter_entry_point_shaders(sources, build_dir, build_dir):
if sfile.entry_points and sfile.specializations:
if sfile.entry_points:
for sp in sfile.specializations:
if not sp.variables:
continue

View file

@ -0,0 +1,76 @@
#!/usr/bin/env python
# License: GPLv3 Copyright: 2026, kitty contributors
from functools import partial
from types import SimpleNamespace
from unittest.mock import Mock, patch
from kitty.boss import Boss
from kitty.launch import load_watch_modules
from kitty.tabs import SpecialWindow
from .base import BaseTest
class IterableArgumentsTest(BaseTest):
def test_window_creation_with_iterable_arguments(self):
for method in ('_new_os_window', '_new_tab'):
for container in (list, tuple, iter):
for values in ((), ('/bin/echo', 'hello')):
with self.subTest(method=method, container=container, values=values):
tm = SimpleNamespace(new_tab=Mock())
boss = SimpleNamespace(active_window=None, os_window_map={1: tm}, active_tab_manager=tm, add_os_window=Mock(return_value=1))
boss.args_to_special_window = partial(Boss.args_to_special_window, boss)
with (
patch('kitty.boss.get_options'),
patch('kitty.boss.create_sessions', return_value=iter((SimpleNamespace(),))) as sessions,
):
getattr(Boss, method)(boss, container(values))
call = sessions.call_args if method == '_new_os_window' else tm.new_tab.call_args
special = call.kwargs['special_window']
if values:
self.ae(special.cmd, list(values))
else:
self.assertIsNone(special)
def test_special_window_arguments_are_preserved(self):
special = SpecialWindow(['/bin/echo', 'hello'])
tm = SimpleNamespace(new_tab=Mock())
boss = SimpleNamespace(os_window_map={1: tm}, active_tab_manager=tm, add_os_window=Mock(return_value=1))
with (
patch('kitty.boss.get_options'),
patch('kitty.boss.create_sessions', return_value=iter((SimpleNamespace(),))) as sessions,
):
Boss._new_os_window(boss, special)
Boss._new_tab(boss, special)
self.assertIs(sessions.call_args.kwargs['special_window'], special)
self.assertIs(tm.new_tab.call_args.kwargs['special_window'], special)
def test_config_error_iterable(self):
for container in (list, tuple, iter):
for values in ((), ('configuration error',)):
with self.subTest(container=container, values=values):
boss = SimpleNamespace(show_error=Mock())
Boss.show_bad_config_lines(boss, (), container(values))
message = boss.show_error.call_args.args[1]
self.ae(message, 'In final effective configuration:\nconfiguration error' if values else '')
def test_watcher_iterable(self):
for container in (list, tuple, iter):
for values in ((), ('watcher.py',)):
with self.subTest(container=container, values=values):
on_load, on_close = Mock(), Mock()
with (
patch('kitty.launch.get_boss', return_value=None),
patch('kitty.launch.resolve_custom_file', side_effect=lambda path: path),
patch.dict('kitty.launch.watcher_modules', {}, clear=True),
patch('runpy.run_path', return_value={'on_load': on_load, 'on_close': on_close}) as load,
):
watchers = load_watch_modules(container(values))
if values:
self.ae(watchers.on_close, [on_close])
load.assert_called_once_with('watcher.py', run_name='__kitty_watcher__')
on_load.assert_called_once_with(None, {})
else:
self.assertIsNone(watchers)
load.assert_not_called()