Narrow some more types via TypedDicts - #2
Open
FichteFoll wants to merge 7 commits into
Open
Conversation
The reference only annotates it as a bare dict; the official API docs and community docs go no further than naming the system/theme/color_scheme top-level keys. The shapes are reverse engineered from a live call and have no counterpart in sublime_types at runtime, so they are stub-only additions via a new EXTRA_TYPE_ALIAS_CLASSES table for generator content that isn't keyed to anything in the reference. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The reference returns a bare `dict[str, Value]`, but its docstring (references/python38/sublime.py:3057-3080) enumerates every key, so the shape transcribes directly into a `TypedDict`. The keys it flags "(only if set)" become `NotRequired`; the rest stay required, which is what makes reading `style["foreground"]` legal while `background` still needs `.get`. This is the first use of `NotRequired`, hence the new entry in the generator's `TYPING_EXTENSIONS_NAMES`. Two of the documented types are wrong. sublime.py:3074-3076 claims `"source_line": str` and `"source_file": int`, but a live `view.style_for_scope(...)` call returns `'source_line': -1` and `'source_file': 'Packages/Theme - Nil/Tubnil_mod.tmTheme'`. The stubs follow the runtime, as they already do for `QuickPanelItem.details`. Prose inside an `EXTRA_TYPE_ALIAS_CLASSES` block has to avoid the bare word `sublime`: the import builder scans the generated body for module names, so a `references/python38/sublime.py` citation there would add an unused `import sublime` to `sublime_types`. The citation therefore lives in the surrounding Python comment, as it does for `UIInfo`. `SUBLIME_TYPES_REEXPORTS["sublime"]` is wrapped one name per line, since it no longer fits on one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The reference returns a bare `list[dict]`, but its docstring (references/python38/sublime.py:1314-1320) says each entry "will contain the keys `"command"` and `"args"`", so both fields transcribe as required rather than `NotRequired`. `CommandArgs` is already `dict[str, Value] | None`, so a command with no arguments is covered by its `None` arm without widening `args` any further. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`Window.layout()`, `Window.get_layout()` and `Window.set_layout()`
returned or accepted a bare `dict[str, Value]`, but the reference
(references/python38/sublime.py:1679-1695) documents no keys at all,
only "Get/Set the group layout of the window". The `cols`, `rows` and
`cells` keys below come from a live `window.layout()` call, which
returned `{'cells': [[0, 0, 1, 1]], 'cols': [0.0, 1.0], 'rows': [0.0,
1.0]}`; all three were present, so the class is total.
`set_layout`'s parameter needs its own `PARAMS` override to
`WindowLayout`, not just the `RETURNS` change on `layout()` and
`get_layout()`. A `TypedDict` is not assignable to `dict[str, Value]`,
so without it `window.set_layout(window.layout())` -- the round trip
the getter/setter pair exists for -- would stop type-checking the
moment `layout()` returns a `WindowLayout` instead.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The reference returns a bare `dict[str, str]`, but its docstring (references/python38/sublime.py:2017-2038) enumerates every key. It introduces that list with "May contain:", so not one of them is guaranteed: the class is uniformly `total=False` rather than marking all thirteen fields `NotRequired`, which would say the same thing at greater length. `expand_variables` has to name both `dict[str, str]` and `WindowVariables` in its `variables` parameter. sublime.py:2036 tells the user the result of `extract_variables()` is "suitable for use with `expand_variables()`", but a `TypedDict` is assignable to neither `dict[str, str]` nor `Mapping[str, str]` -- only to `Mapping[str, object]`, which would wrongly accept any mapping of arbitrary values. Without the union, typing the result at all would break the round trip the reference itself recommends. The win over `dict[str, str]` is rejecting an unknown or misspelled key. That is a negative property, and the sample code deliberately asserts none: the repository has no `type: ignore` precedent, and `reportUnnecessaryTypeIgnoreComment` plus mypy's `warn_unused_ignores` would make such an assertion fragile across four checkers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The reference (references/python38/sublime.py:925-947) documents no
types at all, only the example `{ "font_face": "monospace" }` at
sublime.py:932. A live `choose_font_dialog(print)` call passed
`{'font_face': 'Sans', 'font_size': 10}` to the callback, which is
where `font_size: int` comes from.
`FontOptions` is `total=False` because the same type describes both
the callback argument and the `default` parameter, and the input side
reads both fields through `.get` (sublime.py:941-943:
`default.get("font_face")` and `default.get("font_size")`).
The `| None` sits on the callback argument, not on `default`: the
dialog "will be called with ``None`` if the dialog is cancelled"
(sublime.py:933-934), so the callback has to accept that. `default`
needs no `| None` of its own -- the generator already emits
`FontOptions | None = ...` from the reference's `= None` default.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
FichteFoll
force-pushed
the
feature/more-type-narrowing
branch
from
August 9, 2026 20:48
4d6e36b to
32cfef3
Compare
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.
The lack of a
TypedDictdefinition forui_infowas discovered via PackageDev, after which I started looking for more definitions that were more generic than necessary. We now define several more types insublime_types(re-exported insublimefor compatibility) that do not exist upstream but that provide more precise type information.This also surfaced an error in the documentation about the types in
style_for_scope's return value.Note: All of this was done by Claude Opus (but I had to manually append the "Co-Authored-By" signature to each commit's message 😔 ).