Fix mypy type errors and update to iterm2 v2.13 API#4
Merged
Conversation
- Pin iterm2>=2.13 to ensure py.typed marker is available for type checking - Update mypy python_version from 3.8 to 3.9 (3.8 unsupported by mypy 1.x) - Remove redundant iterm2.* overrides (py.typed makes ignore_missing_imports irrelevant for iterm2) This enables mypy to detect incorrect iterm2 API usage at build time. Currently 51 type errors are expected and will be fixed in subsequent commits.
Tests now mock the real iterm2 v2.13 methods instead of non-existent ones: - iterm2.Window.async_create instead of app.async_create_window - window.async_get_fullscreen instead of async_is_fullscreen - window.async_set_fullscreen instead of async_toggle_fullscreen - iterm2.Arrangement.async_save/restore/list instead of app methods 15 tests now fail against the buggy window.py code, confirming the tests correctly detect the API mismatch.
Replace non-existent API calls with real iterm2 v2.13 methods: - app.async_create_window() → iterm2.Window.async_create(connection, ...) - window.async_is_fullscreen() → window.async_get_fullscreen() - window.async_toggle_fullscreen() → window.async_set_fullscreen(bool) - app.async_save_window_arrangement() → iterm2.Arrangement.async_save() - app.async_list_window_saved_arrangements() → iterm2.Arrangement.async_list() - app.async_restore_window_arrangement() → iterm2.Arrangement.async_restore() Also update shortcut tests to mock the correct API for 'it2 new'.
Tab.async_move_to_window_index() doesn't exist in iterm2 v2.13. The correct API is Tab.async_move_to_window() which moves a tab to its own new window (no index parameter). Update tests to reflect the corrected command interface. 4 tests now fail against the buggy tab.py code.
Tab.async_move_to_window_index() doesn't exist in iterm2 v2.13. Replace with Tab.async_move_to_window() which moves the tab to its own new window. Remove the index argument since the API doesn't support index-based tab reordering.
Update test_session_read and test_session_capture mocks to use the correct ScreenContents API (line(i).string iteration) instead of the nonexistent string_ignoring_hard_newlines() method. These tests now fail against the current session.py code, which still uses the wrong API.
- read: replace nonexistent string_lines_ignoring_hard_newlines() and string_ignoring_hard_newlines() with line(i).string iteration - capture: replace async_get_contents() (no args) with async_get_line_info() + async_get_contents(first_line, total) for scrollback history; use line(i).string iteration for visible contents
- hide: mock MainMenu.async_select_menu_item instead of app.async_hide - quit: unskip test, mock MainMenu.async_select_menu_item - theme: change to test read-only async_get_theme (set not supported) - broadcast on/off: mock iterm2.async_set_broadcast_domains - Remove create-hotkey-window test (HotkeyWindow doesn't exist) - Remove skipped theme_dark/theme_light tests These tests now fail against the current app.py code.
- hide: use MainMenu.async_select_menu_item instead of nonexistent App.async_hide - quit: use MainMenu.async_select_menu_item instead of nonexistent connection.async_send_notification/TerminateAppRequest - broadcast on/off/add: use module-level iterm2.async_set_broadcast_domains with BroadcastDomain instead of nonexistent Tab/Session.async_set_broadcast_domains - theme: change to read-only (async_get_theme) since async_set_theme and iterm2.Theme enum don't exist - Remove create-hotkey-window command (iterm2.HotkeyWindow doesn't exist)
- list: verify JSON output - show: expect normal_font as str with parsed font_size - set font-size: expect async_set_normal_font call - set bg-color: expect async_set_background_color with int Color - create_removed: verify create command is gone (no async_create) These tests fail against the current profile.py code.
- show: parse normal_font string (e.g. "Monaco 12") instead of accessing nonexistent .size/.family attributes - set: use async_set_* methods (async_set_normal_font, async_set_background_color, etc.) instead of setattr + nonexistent async_save - _parse_color: use int 0-255 for Color constructor instead of float - Remove create command (Profile.async_create doesn't exist)
- output: expect line(i).string iteration instead of string_ignoring_hard_newlines - variable: expect VariableMonitor with 4 args (connection, scope, name, identifier) These tests fail against the current monitor.py code.
- output: replace nonexistent string_ignoring_hard_newlines() with line(i).string iteration on ScreenContents - variable: use VariableMonitor with 4 args (connection, scope, name, identifier) and VariableScopes.SESSION enum instead of passing Session object as scope - prompt: use PromptMonitor instead of get_screen_streamer with nonexistent want_prompt_notifications param - activity: add None checks for current_terminal_window chain
- get_app: add assertion to narrow Optional[App] return type - close: remove nonexistent Connection.async_close() call - run_until_complete: pass callable (not awaitable) matching expected Callable[[Connection], Coroutine] signature
- session_handler.py: check current_terminal_window, current_tab, and current_session individually instead of chaining on Optional values - config_commands.py: add missing None check for current_tab before accessing current_session
- window.py: add type: ignore for Window.async_create Optional args (upstream typed as str but defaults to None), cast dict values to str for Table.add_row - tab.py: add None check for tab.current_session, cast dict values to str for Table.add_row, fix variable shadowing for Optional[Window]
The iTerm2 Python API does not expose version or build number through its variable system. The global scope only has 3 variables: applicationPID, localhostName, effectiveTheme. The version command always returned None for both values.
- Add `app version` command using PreferenceKey.ITERM_VERSION - Extend `app theme` to accept optional value for setting theme via PreferenceKey.THEME (light, dark, light-hc, dark-hc, automatic, minimal) - Update README.md and IMPLEMENTATION.md to reflect current commands (remove profile create, fix theme examples, update test counts, correct Python version requirement to 3.9+)
- Remove unused _THEME_NAMES mapping (app.py) - Fix keystroke monitor: use KeystrokeMonitor(session=) for filtering and keystroke.characters instead of non-existent .session/.keystrokes - Align ruff/black target-version to py39 (matching mypy) - Add keystroke monitor tests (with pattern filtering) - Add monitor output pattern filter test
Update deprecated typing imports (List→list, Dict→dict, Tuple→tuple, Pattern→re.Pattern, Awaitable→collections.abc.Awaitable), fix import sorting, add pytest fixture/mark parentheses, and ignore S603 for session.py's hardcoded /usr/bin/pbcopy subprocess call.
Previous commit added parentheses to @pytest.fixture() and @pytest.mark.asyncio() based on local ruff 0.5.5, but the lockfile pins ruff 0.12.3 which requires the opposite (no parentheses). Synced local env and re-ran auto-fix with the correct version.
Click option --all maps to parameter name "all" by default, but the function expected "all_sessions". This caused a TypeError that was swallowed by run_command's broad except, showing a misleading "not running inside iTerm2" error. Add explicit parameter name "all_sessions" to the click.option decorator.
int(font_size) truncated fractional sizes (e.g. 13.5 → 13) when changing font family. Use the float value directly to preserve the original size. Also add supported property list to `profile set --help`.
- window.py: improve type: ignore comment with specific reasoning (iterm2 stubs type profile/command as str but implementation accepts None) - connection.py: replace assert with explicit RuntimeError raise (assert is stripped with -O flag) - profile.py: _parse_font_string now raises ValueError instead of returning 0.0 fallback that could silently set font size to zero; show command gracefully handles parse failures - app.py: document that broadcast on/add replace all existing broadcast domains (API constraint)
async_create has `profile: str = None` instead of `Optional[str] = None`.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
iterm2>=2.13and updatepython_versionto 3.9 for mypyapp versionandapp theme setcommands using the Preferences API (PreferenceKey.ITERM_VERSION,PreferenceKey.THEME)Breaking Changes
Removed commands
it2 profile create— removed becauseiterm2.Profile.async_create()does not exist in the iterm2 libraryit2 app create-hotkey-window— removed becauseiterm2.HotkeyWindowclass does not exist in the iterm2 libraryChanged CLI interfaces
it2 tab move— previouslyit2 tab move INDEX [TAB_ID]which moved a tab to a specific index. Nowit2 tab move [TAB_ID]which moves the tab to its own new window. TheINDEXargument was removed becauseTab.async_move_to_window_index()does not exist;Tab.async_move_to_window()is the correct API.it2 app theme— previously acceptedlight|dark|auto. Now acceptslight|dark|light-hc|dark-hc|automatic|minimal(the full set supported byPreferenceKey.THEME). Theautochoice is replaced byautomatic.it2 app version— previously showed version and build number (both were alwaysNonedue to non-existent variables). Now shows only the version string viaPreferenceKey.ITERM_VERSION. Build number is no longer displayed.Changes
API fixes (all modules)
async_set_fullscreen()/async_get_fullscreen(),async_invoke_function()for arrangements, correctScreenContentsiterationasync_move_to_window()(move to new window), remove non-existentasync_move_to_window_index()ScreenContentsline-by-line iteration via.line(i).string, correctasync_send_text()calls.size/.familyattrs onnormal_font), useasync_set_*methods for property changes, fixColor()to use int 0-255MainMenufor hide/quit, useBroadcastDomaincorrectlyScreenStreamerfor output monitoring,KeystrokeMonitor(session=)for keystroke monitoring with correctkeystroke.characterspropertyConnection.async_create()external connection patternNew/restored commands
it2 app version— reads iTerm2 version viaPreferenceKey.ITERM_VERSIONit2 app theme [light|dark|light-hc|dark-hc|automatic|minimal]— show or set theme viaPreferenceKey.THEMEConfig
pyproject.toml: Align ruff/black/mypy target-version to py39Tests
test_monitor_commands.py(5 tests),test_profile_commands.py(5 tests)Test plan
pytest— 132 passed, 5 skippedmypy src/it2/— no errors (17 files checked)black --check src/ tests/— all formattedruff check src tests— all passed