From c713a1a91d05d13392bdacd69313d786cf4579e8 Mon Sep 17 00:00:00 2001 From: Akuli Date: Sat, 25 Jul 2020 16:31:11 +0300 Subject: [PATCH 01/45] Add type hints for tkinter widget options --- .travis.yml | 7 + stdlib/3/tkinter/__init__.pyi | 2320 +++++++++++++++++++++++++++++++- stdlib/3/tkinter/constants.pyi | 156 +-- stdlib/3/tkinter/font.pyi | 24 +- stdlib/3/tkinter/ttk.pyi | 1019 +++++++++++++- tests/pytype_test.py | 15 +- tests/tkinter_options_test.py | 368 +++++ 7 files changed, 3745 insertions(+), 164 deletions(-) create mode 100755 tests/tkinter_options_test.py diff --git a/.travis.yml b/.travis.yml index f49c35724eb8..2dd5c38fc922 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,13 @@ jobs: python: 3.8 install: pip install -U git+git://github.com/python/mypy script: ./tests/mypy_test.py --platform=linux + - name: "tkinter options test" + # Run this with latest python supported by typeshed + python: 3.8 + install: pip install -U typing-inspect + script: ./tests/tkinter_options_test.py + services: + - xvfb # needed to run tkinter - name: "mypy (Windows)" install: pip install -U git+git://github.com/python/mypy script: ./tests/mypy_test.py --platform=win32 diff --git a/stdlib/3/tkinter/__init__.pyi b/stdlib/3/tkinter/__init__.pyi index 8f03107c984a..b674c7424b1d 100644 --- a/stdlib/3/tkinter/__init__.pyi +++ b/stdlib/3/tkinter/__init__.pyi @@ -1,9 +1,12 @@ import sys from enum import Enum -from tkinter.constants import * # noqa: F403 +from tkinter import font +from tkinter.constants import * # comment this out to find undefined identifier names with flake8 from types import TracebackType from typing import Any, Callable, Dict, Generic, Optional, Tuple, Type, TypeVar, Union, overload from typing_extensions import Literal +from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Type, TypeVar, Union, overload +from typing_extensions import Literal, TypedDict TclError: Any wantobjects: Any @@ -13,6 +16,228 @@ READABLE: Any WRITABLE: Any EXCEPTION: Any +# *************************************************************** +# ** Quick guide for figuring out which widget class to choose ** +# *************************************************************** +# - Misc: any widget (don't use BaseWidget because Tk doesn't inherit from BaseWidget) +# - Widget: anything that is meant to be put into another widget with e.g. pack or grid +# - Wm: a toplevel window, Tk or Toplevel +# +# +# ******************** +# ** Widget options ** +# ******************** +# These are the things passed to configure(), e.g. the text of a label. +# Currently widget options work like this: +# - configure and config are identical (tkinter classes do config = configure) +# - configure keyword-only arguments are specified exactly with type hints +# - cget takes Literal[...] as the string argument and returns Any +# - __setitem__ and __getitem__ use string arguments and Any +# +# The idea is that configure() and cget() are more strictly typed than +# __setitem__ and __getitem__. This way the tkinter user gets to choose what +# kind of typing to use with widget options. So, if your code does +# +# widget[some_string_variable] = some_value +# +# then you don't need to change anything to make it type check with these +# stubs. However, if you instead have the less readable +# +# widget.config(**{some_string_variable: some_value}), +# +# then you might get mypy warnings from doing that. Doing it this way also +# decreases the amount of required copy/pasta a lot. +# +# Getting the value of an option gives Any. See this to get an idea of why I +# ended up doing it that way: +# +# >>> l = tkinter.ttk.Label() +# >>> l.cget('wraplength') +# '' +# >>> l.config(wraplength='2c') +# >>> l.cget('wraplength') +# +# >>> l.config(wraplength=0) +# >>> l.cget('wraplength') +# 0 +# +# Here getting the same option returns 3 different types: string, +# _tkinter.Tcl_Obj and int. There's no documentation about this and I don't +# have a good way to know whether these types found by trying out things on the +# prompt are really all the possible types that could be returned. Also, +# returning Union is considered bad in general. +# +# Any-typing the values of options is still not perfect. For example, this +# works at runtime and type checks: +# +# label1 = tkinter.ttk.Label() +# label2 = tkinter.ttk.Label() +# label1.config(wraplength=label2.cget('wraplength')) # right side has type Any +# +# This works at runtime but does NOT type check (although it really seems to +# require deliberately doing something funny for this to be a problem): +# +# label1 = tkinter.ttk.Label() +# label2 = tkinter.ttk.Label() +# label2['wraplength'] = '2c' +# obj = label2.cget('wraplength') +# assert isinstance(obj, _tkinter.Tcl_Obj) +# label1.config(wraplength=obj) # mypy error because Tcl_Obj is not valid wraplength +# +# Here obj is Tcl_Obj, which is not one of the things that can be assigned to a +# wraplength. Tcl_Obj in general is not something that comes up frequently in +# tkinter, and when it does come up, the right thing to do is usually str()ing +# it or passing it back to tkinter. I tend to mostly ignore Tcl_Obj because I +# don't want to clutter all option type hints with Union[..., Tcl_Obj]. +# +# If you want to write code like the above, you still don't need to resort to +# type ignore comments, because __setitem__ is less strictly typed: +# +# label1['wraplength'] = obj +# +# Instructions for figuring out the correct type of each option: +# - Find the option from the manual page of the widget. Usually the manual +# page of a non-ttk widget has the same name as the tkinter class, in the +# 3tk section: +# +# $ sudo apt install tk-doc +# $ man 3tk label +# +# Ttk manual pages tend to have ttk_ prefixed names: +# +# $ man 3tk ttk_label +# +# Non-GUI things like the .after() method are often in the 3tcl section: +# +# $ sudo apt install tcl-doc +# $ man 3tcl after +# +# If you don't have man or apt, you can read these manual pages online: +# +# https://www.tcl.tk/doc/ +# +# Every option has '-' in front of its name in the manual page (and in Tcl). +# For example, there's an option named '-text' in the label manual page. +# +# - Tkinter has some options documented in docstrings, but don't rely on them. +# They aren't updated when a new version of Tk comes out, so the latest Tk +# manual pages (see above) are much more likely to actually contain all +# possible options. +# +# Also, reading tkinter's source code typically won't help much because it +# uses a lot of **kwargs and duck typing. Typically every argument goes into +# self.tk.call, which is _tkinter.TkappType.call, and the return value is +# whatever that returns. The type of that depends on how the Tcl interpreter +# represents the return value of the executed Tcl code. +# +# - If you think that int is an appropriate type for something, then you may +# actually want _ScreenUnits instead. +# +# - If you think that Callable[something] is an appropriate type for +# something, then you may actually want Union[Callable[something], str], +# because it's often possible to specify a string of Tcl code. +# +# - Some options can be set only in __init__, but all options are available +# when getting their values with configure's return value or cget. +# +# - Asks other tkinter users if you haven't worked much with tkinter. + +# _TkinterSequence[T] represents a sequence that tkinter understands. It +# differs from typing.Sequence[T]. For example, collections.deque a valid +# Sequence but not a valid _TkinterSequence: +# +# >>> tkinter.Label(font=('Helvetica', 12, collections.deque(['bold']))) +# Traceback (most recent call last): +# ... +# _tkinter.TclError: unknown font style "deque(['bold'])" +_T = TypeVar("_T") +_TkinterSequence = Union[List[_T], Tuple[_T, ...]] + +# If a manual page mentions Tk_GetAnchor or refers to another manual page named +# 'options', then it means this. Note that some ttk widgets have other things +# named 'anchor' with a different set of allowed values. +_Anchor = Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] + +# if manual page mentions Tk_GetBitmap, then it means this. Bitmap names can be +# arbitrary strings. You can also specify a path, but you need to put '@' in +# front of it and tkinter doesn't do that automatically if you e.g. pass in a +# pathlib.Path object. +_Bitmap = str + +# Return value of the function will be returned by Button.invoke(), possibly +# somehow ruined by Python -> Tcl -> Python conversions. This works the same +# way for tkinter.ttk.Button even though its manual page says nothing about +# the return value. +_ButtonCommand = Union[str, Callable[[], Any]] + +# Color strings are typically '#rrggbb', '#rgb' or color names. +_Color = str + +# This is the type of -compound from manual page named 'options'. Note that +# sometimes there is an option named -compound that takes something else than +# one of these. +_Compound = Literal["top", "left", "center", "right", "bottom", "none"] + +# Tk_GetCursor documents many possibilities, but they're all just Tcl lists of +# strings (corresponding to Python tuples of strings), at least 1 string and at +# most 4 strings. +_Cursor = Union[str, Tuple[str], Tuple[str, str], Tuple[str, str, str], Tuple[str, str, str, str]] + +# This is for Entry. Currently there seems to be no way to make use of the +# substitutions described in entry manual page unless your validatecommand is a +# Tcl command, e.g.: +# +# tcl_print = e.register(print) +# e['invalidcommand'] = [tcl_print, '%P'] +# +# Specifying a sequence does the correct kind of escaping for Tcl. +_EntryValidateCommand = Union[Callable[[], bool], str, _TkinterSequence[str]] + +# See 'FONT DESCRIPTIONS' in font man page. This uses str because Literal +# inside Tuple doesn't work. +# +# Putting this to font.pyi breaks pytype: https://github.com/google/pytype/issues/626 +# +# Using anything from tkinter.font in this file means that 'import tkinter' +# seems to also load tkinter.font. That's not how it actually works, but +# unfortunately not much can be done about it. https://github.com/python/typeshed/pull/4346 +_FontDescription = Union[ + str, font.Font, Tuple[str, int], Tuple[str, int, _TkinterSequence[str]], +] + +# str could be e.g. from tkinter.image_names() +_ImageSpec = Union[Image, str] + +# Padding should be set to 1, 2, 3 or 4 screen distances in tuple or list. +# If just 1, then no need to wrap in tuple or list. +# +# When getting the padding, it can be empty string. Again, we cheat by not +# creating a Union return so that looping over the return value and +# treating the items as tkinter._ScreenUnits actually works. +_Padding = Union[ + _ScreenUnits, + Tuple[_ScreenUnits], + Tuple[_ScreenUnits, _ScreenUnits], + Tuple[_ScreenUnits, _ScreenUnits, _ScreenUnits], + Tuple[_ScreenUnits, _ScreenUnits, _ScreenUnits, _ScreenUnits], +] + +# If manual page says Tk_GetRelief then it means this. +_Relief = Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] + +# string must be e.g. '12.34', '12.34c', '12.34i', '12.34m', '12.34p' +# see Tk_GetPixels man page for what each suffix means +# Some ttk widgets also use empty string. +_ScreenUnits = Union[str, float] + +# -xscrollcommand and -yscrollcommand in 'options' manual page +_XYScrollCommand = Union[str, Callable[[float, float], None]] + +# Returning None from a takefocus callback seems to work (man page says to +# return empty string instead). But setting options to None doesn't work +# in general. Use nametowidget() to handle the argument of the callback. +_TakeFocusValue = Union[bool, Literal[""], Callable[[str], Optional[bool]]] + if sys.version_info >= (3, 6): class EventType(str, Enum): Activate: str = ... @@ -273,12 +498,7 @@ class Misc: def quit(self): ... def nametowidget(self, name): ... register: Any - def configure(self, cnf: Optional[Any] = ..., **kw): ... - config: Any - def cget(self, key): ... - __getitem__: Any - def __setitem__(self, key, value): ... - def keys(self): ... + def keys(self) -> List[str]: ... def pack_propagate(self, flag=...): ... propagate: Any def pack_slaves(self): ... @@ -305,6 +525,9 @@ class Misc: def event_info(self, virtual: Optional[Any] = ...): ... def image_names(self): ... def image_types(self): ... + # See comments in beginning of this file. + def __setitem__(self, key: str, value: Any) -> None: ... + def __getitem__(self, key: str) -> Any: ... class CallWrapper: func: Any @@ -399,6 +622,29 @@ class Wm: def wm_withdraw(self): ... withdraw: Any +_TkOptionName = Literal[ + "background", + "bd", + "bg", + "borderwidth", + "class", + "colormap", + "container", + "cursor", + "height", + "highlightbackground", + "highlightcolor", + "highlightthickness", + "menu", + "padx", + "pady", + "relief", + "takefocus", + "use", + "visual", + "width", +] + class Tk(Misc, Wm): master: Optional[Any] children: Dict[str, Any] @@ -412,6 +658,31 @@ class Tk(Misc, Wm): sync: bool = ..., use: Optional[str] = ..., ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + borderwidth: _ScreenUnits = ..., + cursor: _Cursor = ..., + height: _ScreenUnits = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + menu: Menu = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + relief: _Relief = ..., + takefocus: _TakeFocusValue = ..., + width: _ScreenUnits = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _TkOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _TkOptionName) -> Any: ... def loadtk(self) -> None: ... def destroy(self) -> None: ... def readprofile(self, baseName: str, className: str) -> None: ... @@ -478,15 +749,280 @@ class Widget(BaseWidget, Pack, Place, Grid): def bind(self, *, func: str, add: Optional[bool] = ...) -> None: ... class Toplevel(BaseWidget, Wm): - def __init__(self, master: Optional[Any] = ..., cnf=..., **kw): ... + def __init__( + self, + master: Optional[Misc] = ..., + cnf: Dict[str, Any] = ..., + *, + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + borderwidth: _ScreenUnits = ..., + class_: str = ..., + colormap: Union[Literal["new", ""], Misc] = ..., + container: bool = ..., + cursor: _Cursor = ..., + height: _ScreenUnits = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + menu: Menu = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + relief: _Relief = ..., + takefocus: _TakeFocusValue = ..., + use: int = ..., + visual: Union[str, Tuple[str, int]] = ..., + width: _ScreenUnits = ..., + ) -> None: ... + # Toplevel and Tk have the same options because they correspond to the same + # Tcl/Tk toplevel widget. + configure = Tk.configure + config = Tk.config + cget = Tk.cget + +_ButtonOptionName = Literal[ + "activebackground", + "activeforeground", + "anchor", + "background", + "bd", + "bg", + "bitmap", + "borderwidth", + "command", + "compound", + "cursor", + "default", + "disabledforeground", + "fg", + "font", + "foreground", + "height", + "highlightbackground", + "highlightcolor", + "highlightthickness", + "image", + "justify", + "overrelief", + "padx", + "pady", + "relief", + "repeatdelay", + "repeatinterval", + "state", + "takefocus", + "text", + "textvariable", + "underline", + "width", + "wraplength", +] class Button(Widget): - def __init__(self, master: Optional[Any] = ..., cnf=..., **kw): ... + def __init__( + self, + master: Optional[Misc] = ..., + cnf: Dict[str, Any] = ..., + *, + activebackground: _Color = ..., + activeforeground: _Color = ..., + anchor: _Anchor = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + bitmap: _Bitmap = ..., + borderwidth: _ScreenUnits = ..., + command: _ButtonCommand = ..., + compound: _Compound = ..., + cursor: _Cursor = ..., + default: Literal["normal", "active", "disabled"] = ..., + disabledforeground: _Color = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + # width and height must be int for buttons containing just text, but + # ints are also valid _ScreenUnits + height: _ScreenUnits = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + image: _ImageSpec = ..., + justify: Literal["left", "center", "right"] = ..., + overrelief: _Relief = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + relief: _Relief = ..., + repeatdelay: int = ..., + repeatinterval: int = ..., + state: Literal["normal", "active", "disabled"] = ..., + takefocus: _TakeFocusValue = ..., + text: str = ..., + # We allow the textvariable to be any Variable, not necessarly + # StringVar. This is useful for e.g. a button that displays the value + # of an IntVar. + textvariable: Variable = ..., + underline: int = ..., + width: _ScreenUnits = ..., + wraplength: _ScreenUnits = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + activebackground: _Color = ..., + activeforeground: _Color = ..., + anchor: _Anchor = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + bitmap: _Bitmap = ..., + borderwidth: _ScreenUnits = ..., + command: _ButtonCommand = ..., + compound: _Compound = ..., + cursor: _Cursor = ..., + default: Literal["normal", "active", "disabled"] = ..., + disabledforeground: _Color = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + height: _ScreenUnits = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + image: _ImageSpec = ..., + justify: Literal["left", "center", "right"] = ..., + overrelief: _Relief = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + relief: _Relief = ..., + repeatdelay: int = ..., + repeatinterval: int = ..., + state: Literal["normal", "active", "disabled"] = ..., + takefocus: _TakeFocusValue = ..., + text: str = ..., + textvariable: Variable = ..., + underline: int = ..., + width: _ScreenUnits = ..., + wraplength: _ScreenUnits = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _ButtonOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _ButtonOptionName) -> Any: ... def flash(self): ... def invoke(self): ... +_CanvasOptionName = Literal[ + "background", + "bd", + "bg", + "borderwidth", + "closeenough", + "confine", + "cursor", + "height", + "highlightbackground", + "highlightcolor", + "highlightthickness", + "insertbackground", + "insertborderwidth", + "insertofftime", + "insertontime", + "insertwidth", + "relief", + "scrollregion", + "selectbackground", + "selectborderwidth", + "selectforeground", + "state", + "takefocus", + "width", + "xscrollcommand", + "xscrollincrement", + "yscrollcommand", + "yscrollincrement", +] + class Canvas(Widget, XView, YView): - def __init__(self, master: Optional[Any] = ..., cnf=..., **kw): ... + def __init__( + self, + master: Optional[Misc] = ..., + cnf: Dict[str, Any] = ..., + *, + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + borderwidth: _ScreenUnits = ..., + closeenough: float = ..., + confine: bool = ..., + cursor: _Cursor = ..., + # canvas manual page has a section named COORDINATES, and the first + # part of it describes _ScreenUnits. + height: _ScreenUnits = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + insertbackground: _Color = ..., + insertborderwidth: _ScreenUnits = ..., + insertofftime: int = ..., + insertontime: int = ..., + insertwidth: _ScreenUnits = ..., + relief: _Relief = ..., + # Setting scrollregion to None doesn't reset it back to empty, + # but setting it to () does. + scrollregion: Union[Tuple[_ScreenUnits, _ScreenUnits, _ScreenUnits, _ScreenUnits], Tuple[()]] = ..., + selectbackground: _Color = ..., + selectborderwidth: _ScreenUnits = ..., + selectforeground: _Color = ..., + # man page says that state can be 'hidden', but it can't + state: Literal["normal", "disabled"] = ..., + takefocus: _TakeFocusValue = ..., + width: _ScreenUnits = ..., + xscrollcommand: _XYScrollCommand = ..., + xscrollincrement: _ScreenUnits = ..., + yscrollcommand: _XYScrollCommand = ..., + yscrollincrement: _ScreenUnits = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + borderwidth: _ScreenUnits = ..., + closeenough: float = ..., + confine: bool = ..., + cursor: _Cursor = ..., + height: _ScreenUnits = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + insertbackground: _Color = ..., + insertborderwidth: _ScreenUnits = ..., + insertofftime: int = ..., + insertontime: int = ..., + insertwidth: _ScreenUnits = ..., + relief: _Relief = ..., + scrollregion: Union[Tuple[_ScreenUnits, _ScreenUnits, _ScreenUnits, _ScreenUnits], Tuple[()]] = ..., + selectbackground: _Color = ..., + selectborderwidth: _ScreenUnits = ..., + selectforeground: _Color = ..., + state: Literal["normal", "disabled"] = ..., + takefocus: _TakeFocusValue = ..., + width: _ScreenUnits = ..., + xscrollcommand: _XYScrollCommand = ..., + xscrollincrement: _ScreenUnits = ..., + yscrollcommand: _XYScrollCommand = ..., + yscrollincrement: _ScreenUnits = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _CanvasOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _CanvasOptionName) -> Any: ... def addtag(self, *args): ... def addtag_above(self, newtag, tagOrId): ... def addtag_all(self, newtag): ... @@ -558,16 +1094,287 @@ class Canvas(Widget, XView, YView): def select_to(self, tagOrId, index): ... def type(self, tagOrId): ... +_CheckbuttonOptionName = Literal[ + "activebackground", + "activeforeground", + "anchor", + "background", + "bd", + "bg", + "bitmap", + "borderwidth", + "command", + "compound", + "cursor", + "disabledforeground", + "fg", + "font", + "foreground", + "height", + "highlightbackground", + "highlightcolor", + "highlightthickness", + "image", + "indicatoron", + "justify", + "offrelief", + "offvalue", + "onvalue", + "overrelief", + "padx", + "pady", + "relief", + "selectcolor", + "selectimage", + "state", + "takefocus", + "text", + "textvariable", + "tristateimage", + "tristatevalue", + "underline", + "variable", + "width", + "wraplength", +] + class Checkbutton(Widget): - def __init__(self, master: Optional[Any] = ..., cnf=..., **kw): ... + def __init__( + self, + master: Optional[Misc] = ..., + cnf: Dict[str, Any] = ..., + *, + activebackground: _Color = ..., + activeforeground: _Color = ..., + anchor: _Anchor = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + bitmap: _Bitmap = ..., + borderwidth: _ScreenUnits = ..., + command: _ButtonCommand = ..., + compound: _Compound = ..., + cursor: _Cursor = ..., + disabledforeground: _Color = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + height: _ScreenUnits = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + image: _ImageSpec = ..., + indicatoron: bool = ..., + justify: Literal["left", "center", "right"] = ..., + offrelief: _Relief = ..., + # The checkbutton puts a value to its variable when it's checked or + # unchecked. We don't restrict the type of that value here, so + # Any-typing is fine. + # + # I think Checkbutton shouldn't be generic, because then specifying + # "any checkbutton regardless of what variable it uses" would be + # difficult, and we might run into issues just like how List[float] + # and List[int] are incompatible. Also, we would need a way to + # specify "Checkbutton not associated with any variable", which is + # done by setting variable to empty string (the default). + offvalue: Any = ..., + onvalue: Any = ..., + overrelief: _Relief = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + relief: _Relief = ..., + selectcolor: _Color = ..., + selectimage: _ImageSpec = ..., + state: Literal["normal", "active", "disabled"] = ..., + takefocus: _TakeFocusValue = ..., + text: str = ..., + textvariable: Variable = ..., + tristateimage: _ImageSpec = ..., + tristatevalue: Any = ..., + underline: int = ..., + variable: Union[Variable, Literal[""]] = ..., + width: _ScreenUnits = ..., + wraplength: _ScreenUnits = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + activebackground: _Color = ..., + activeforeground: _Color = ..., + anchor: _Anchor = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + bitmap: _Bitmap = ..., + borderwidth: _ScreenUnits = ..., + command: _ButtonCommand = ..., + compound: _Compound = ..., + cursor: _Cursor = ..., + disabledforeground: _Color = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + height: _ScreenUnits = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + image: _ImageSpec = ..., + indicatoron: bool = ..., + justify: Literal["left", "center", "right"] = ..., + offrelief: _Relief = ..., + offvalue: Any = ..., + onvalue: Any = ..., + overrelief: _Relief = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + relief: _Relief = ..., + selectcolor: _Color = ..., + selectimage: _ImageSpec = ..., + state: Literal["normal", "active", "disabled"] = ..., + takefocus: _TakeFocusValue = ..., + text: str = ..., + textvariable: Variable = ..., + tristateimage: _ImageSpec = ..., + tristatevalue: Any = ..., + underline: int = ..., + variable: Union[Variable, Literal[""]] = ..., + width: _ScreenUnits = ..., + wraplength: _ScreenUnits = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _CheckbuttonOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _CheckbuttonOptionName) -> Any: ... def deselect(self): ... def flash(self): ... def invoke(self): ... def select(self): ... def toggle(self): ... +_EntryOptionName = Literal[ + "background", + "bd", + "bg", + "borderwidth", + "cursor", + "disabledbackground", + "disabledforeground", + "exportselection", + "fg", + "font", + "foreground", + "highlightbackground", + "highlightcolor", + "highlightthickness", + "insertbackground", + "insertborderwidth", + "insertofftime", + "insertontime", + "insertwidth", + "invalidcommand", + "justify", + "readonlybackground", + "relief", + "selectbackground", + "selectborderwidth", + "selectforeground", + "show", + "state", + "takefocus", + "textvariable", + "validate", + "validatecommand", + "width", + "xscrollcommand", +] + class Entry(Widget, XView): - def __init__(self, master: Optional[Any] = ..., cnf=..., **kw): ... + def __init__( + self, + master: Optional[Misc] = ..., + cnf: Dict[str, Any] = ..., + *, + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + borderwidth: _ScreenUnits = ..., + cursor: _Cursor = ..., + disabledbackground: _Color = ..., + disabledforeground: _Color = ..., + exportselection: bool = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + insertbackground: _Color = ..., + insertborderwidth: _ScreenUnits = ..., + insertofftime: int = ..., + insertontime: int = ..., + insertwidth: _ScreenUnits = ..., + invalidcommand: _EntryValidateCommand = ..., + justify: Literal["left", "center", "right"] = ..., + readonlybackground: _Color = ..., + relief: _Relief = ..., + selectbackground: _Color = ..., + selectborderwidth: _ScreenUnits = ..., + selectforeground: _Color = ..., + show: str = ..., + state: Literal["normal", "disabled", "readonly"] = ..., + takefocus: _TakeFocusValue = ..., + textvariable: Variable = ..., + validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., + validatecommand: _EntryValidateCommand = ..., + width: int = ..., + xscrollcommand: _XYScrollCommand = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + borderwidth: _ScreenUnits = ..., + cursor: _Cursor = ..., + disabledbackground: _Color = ..., + disabledforeground: _Color = ..., + exportselection: bool = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + insertbackground: _Color = ..., + insertborderwidth: _ScreenUnits = ..., + insertofftime: int = ..., + insertontime: int = ..., + insertwidth: _ScreenUnits = ..., + invalidcommand: _EntryValidateCommand = ..., + justify: Literal["left", "center", "right"] = ..., + readonlybackground: _Color = ..., + relief: _Relief = ..., + selectbackground: _Color = ..., + selectborderwidth: _ScreenUnits = ..., + selectforeground: _Color = ..., + show: str = ..., + state: Literal["normal", "disabled", "readonly"] = ..., + takefocus: _TakeFocusValue = ..., + textvariable: Variable = ..., + validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., + validatecommand: _EntryValidateCommand = ..., + width: int = ..., + xscrollcommand: _XYScrollCommand = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _EntryOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _EntryOptionName) -> Any: ... def delete(self, first, last: Optional[Any] = ...): ... def get(self): ... def icursor(self, index): ... @@ -588,14 +1395,306 @@ class Entry(Widget, XView): def selection_to(self, index): ... select_to: Any +_FrameOptionName = Literal[ + "background", + "bd", + "bg", + "borderwidth", + "class", + "colormap", + "container", + "cursor", + "height", + "highlightbackground", + "highlightcolor", + "highlightthickness", + "padx", + "pady", + "relief", + "takefocus", + "visual", + "width", +] + class Frame(Widget): - def __init__(self, master: Optional[Any] = ..., cnf=..., **kw): ... + def __init__( + self, + master: Optional[Misc] = ..., + cnf: Dict[str, Any] = ..., + *, + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + borderwidth: _ScreenUnits = ..., + class_: str = ..., + colormap: Union[Literal["new", ""], Misc] = ..., + container: bool = ..., + cursor: _Cursor = ..., + height: _ScreenUnits = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + relief: _Relief = ..., + takefocus: _TakeFocusValue = ..., + visual: Union[str, Tuple[str, int]] = ..., + width: _ScreenUnits = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + borderwidth: _ScreenUnits = ..., + cursor: _Cursor = ..., + height: _ScreenUnits = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + relief: _Relief = ..., + takefocus: _TakeFocusValue = ..., + width: _ScreenUnits = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _FrameOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _FrameOptionName) -> Any: ... + +_LabelOptionName = Literal[ + "activebackground", + "activeforeground", + "anchor", + "background", + "bd", + "bg", + "bitmap", + "borderwidth", + "compound", + "cursor", + "disabledforeground", + "fg", + "font", + "foreground", + "height", + "highlightbackground", + "highlightcolor", + "highlightthickness", + "image", + "justify", + "padx", + "pady", + "relief", + "state", + "takefocus", + "text", + "textvariable", + "underline", + "width", + "wraplength", +] class Label(Widget): - def __init__(self, master: Optional[Any] = ..., cnf=..., **kw): ... + def __init__( + self, + master: Optional[Misc] = ..., + cnf: Dict[str, Any] = ..., + *, + activebackground: _Color = ..., + activeforeground: _Color = ..., + anchor: _Anchor = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + bitmap: _Bitmap = ..., + borderwidth: _ScreenUnits = ..., + compound: _Compound = ..., + cursor: _Cursor = ..., + disabledforeground: _Color = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + height: _ScreenUnits = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + image: _ImageSpec = ..., + justify: Literal["left", "center", "right"] = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + relief: _Relief = ..., + state: Literal["normal", "active", "disabled"] = ..., + takefocus: _TakeFocusValue = ..., + text: str = ..., + textvariable: Variable = ..., + underline: int = ..., + width: _ScreenUnits = ..., + wraplength: _ScreenUnits = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + activebackground: _Color = ..., + activeforeground: _Color = ..., + anchor: _Anchor = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + bitmap: _Bitmap = ..., + borderwidth: _ScreenUnits = ..., + compound: _Compound = ..., + cursor: _Cursor = ..., + disabledforeground: _Color = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + height: _ScreenUnits = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + image: _ImageSpec = ..., + justify: Literal["left", "center", "right"] = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + relief: _Relief = ..., + state: Literal["normal", "active", "disabled"] = ..., + takefocus: _TakeFocusValue = ..., + text: str = ..., + textvariable: Variable = ..., + underline: int = ..., + width: _ScreenUnits = ..., + wraplength: _ScreenUnits = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _LabelOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _LabelOptionName) -> Any: ... + +_ListboxOptionName = Literal[ + "activestyle", + "background", + "bd", + "bg", + "borderwidth", + "cursor", + "disabledforeground", + "exportselection", + "fg", + "font", + "foreground", + "height", + "highlightbackground", + "highlightcolor", + "highlightthickness", + "justify", + "listvariable", + "relief", + "selectbackground", + "selectborderwidth", + "selectforeground", + "selectmode", + "setgrid", + "state", + "takefocus", + "width", + "xscrollcommand", + "yscrollcommand", +] class Listbox(Widget, XView, YView): - def __init__(self, master: Optional[Any] = ..., cnf=..., **kw): ... + def __init__( + self, + master: Optional[Misc] = ..., + cnf: Dict[str, Any] = ..., + *, + activestyle: Literal["dotbox", "none", "underline"] = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + borderwidth: _ScreenUnits = ..., + cursor: _Cursor = ..., + disabledforeground: _Color = ..., + exportselection: bool = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + height: int = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + justify: Literal["left", "center", "right"] = ..., + # There's no tkinter.ListVar, but seems like bare tkinter.Variable + # actually works for this: + # + # >>> import tkinter + # >>> lb = tkinter.Listbox() + # >>> var = lb['listvariable'] = tkinter.Variable() + # >>> var.set(['foo', 'bar', 'baz']) + # >>> lb.get(0, 'end') + # ('foo', 'bar', 'baz') + listvariable: Variable = ..., + relief: _Relief = ..., + selectbackground: _Color = ..., + selectborderwidth: _ScreenUnits = ..., + selectforeground: _Color = ..., + # from listbox man page: "The value of the [selectmode] option may be + # arbitrary, but the default bindings expect it to be ..." + # + # I have never seen anyone setting this to something else than what + # "the default bindings expect", but let's support it anyway. + selectmode: str = ..., + setgrid: bool = ..., + state: Literal["normal", "disabled"] = ..., + takefocus: _TakeFocusValue = ..., + width: int = ..., + xscrollcommand: _XYScrollCommand = ..., + yscrollcommand: _XYScrollCommand = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + activestyle: Literal["dotbox", "none", "underline"] = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + borderwidth: _ScreenUnits = ..., + cursor: _Cursor = ..., + disabledforeground: _Color = ..., + exportselection: bool = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + height: int = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + justify: Literal["left", "center", "right"] = ..., + listvariable: Variable = ..., + relief: _Relief = ..., + selectbackground: _Color = ..., + selectborderwidth: _ScreenUnits = ..., + selectforeground: _Color = ..., + selectmode: str = ..., + setgrid: bool = ..., + state: Literal["normal", "disabled"] = ..., + takefocus: _TakeFocusValue = ..., + width: int = ..., + xscrollcommand: _XYScrollCommand = ..., + yscrollcommand: _XYScrollCommand = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _ListboxOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _ListboxOptionName) -> Any: ... def activate(self, index): ... def bbox(self, index): ... def curselection(self): ... @@ -620,8 +1719,89 @@ class Listbox(Widget, XView, YView): def itemconfigure(self, index, cnf: Optional[Any] = ..., **kw): ... itemconfig: Any +_MenuOptionName = Literal[ + "activebackground", + "activeborderwidth", + "activeforeground", + "background", + "bd", + "bg", + "borderwidth", + "cursor", + "disabledforeground", + "fg", + "font", + "foreground", + "postcommand", + "relief", + "selectcolor", + "takefocus", + "tearoff", + "tearoffcommand", + "title", + "type", +] + class Menu(Widget): - def __init__(self, master: Optional[Any] = ..., cnf=..., **kw): ... + def __init__( + self, + master: Optional[Misc] = ..., + cnf: Dict[str, Any] = ..., + *, + activebackground: _Color = ..., + activeborderwidth: _ScreenUnits = ..., + activeforeground: _Color = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + borderwidth: _ScreenUnits = ..., + cursor: _Cursor = ..., + disabledforeground: _Color = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + postcommand: Union[Callable[[], None], str] = ..., + relief: _Relief = ..., + selectcolor: _Color = ..., + takefocus: _TakeFocusValue = ..., + tearoff: bool = ..., + # I guess tearoffcommand arguments are supposed to be widget objects, + # but they are widget name strings. Use nametowidget() to handle the + # arguments of tearoffcommand. + tearoffcommand: Union[Callable[[str, str], None], str] = ..., + title: str = ..., + type: Literal["menubar", "tearoff", "normal"] = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + activebackground: _Color = ..., + activeborderwidth: _ScreenUnits = ..., + activeforeground: _Color = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + borderwidth: _ScreenUnits = ..., + cursor: _Cursor = ..., + disabledforeground: _Color = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + postcommand: Union[Callable[[], None], str] = ..., + relief: _Relief = ..., + selectcolor: _Color = ..., + takefocus: _TakeFocusValue = ..., + tearoff: bool = ..., + tearoffcommand: Union[Callable[[str, str], None], str] = ..., + title: str = ..., + type: Literal["menubar", "tearoff", "normal"] = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _MenuOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _MenuOptionName) -> Any: ... def tk_popup(self, x, y, entry: str = ...): ... if sys.version_info < (3, 6): def tk_bindForTraversal(self): ... @@ -650,28 +1830,607 @@ class Menu(Widget): def xposition(self, index): ... def yposition(self, index): ... +_MenubuttonOptionName = Literal[ + "activebackground", + "activeforeground", + "anchor", + "background", + "bd", + "bg", + "bitmap", + "borderwidth", + "compound", + "cursor", + "direction", + "disabledforeground", + "fg", + "font", + "foreground", + "height", + "highlightbackground", + "highlightcolor", + "highlightthickness", + "image", + "indicatoron", + "justify", + "menu", + "padx", + "pady", + "relief", + "state", + "takefocus", + "text", + "textvariable", + "underline", + "width", + "wraplength", +] + class Menubutton(Widget): - def __init__(self, master: Optional[Any] = ..., cnf=..., **kw): ... + def __init__( + self, + master: Optional[Misc] = ..., + cnf: Dict[str, Any] = ..., + *, + activebackground: _Color = ..., + activeforeground: _Color = ..., + anchor: _Anchor = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + bitmap: _Bitmap = ..., + borderwidth: _ScreenUnits = ..., + compound: _Compound = ..., + cursor: _Cursor = ..., + direction: Literal["above", "below", "left", "right", "flush"] = ..., + disabledforeground: _Color = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + height: _ScreenUnits = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + image: _ImageSpec = ..., + indicatoron: bool = ..., + justify: Literal["left", "center", "right"] = ..., + menu: Menu = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + relief: _Relief = ..., + state: Literal["normal", "active", "disabled"] = ..., + takefocus: _TakeFocusValue = ..., + text: str = ..., + textvariable: Variable = ..., + underline: int = ..., + width: _ScreenUnits = ..., + wraplength: _ScreenUnits = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + activebackground: _Color = ..., + activeforeground: _Color = ..., + anchor: _Anchor = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + bitmap: _Bitmap = ..., + borderwidth: _ScreenUnits = ..., + compound: _Compound = ..., + cursor: _Cursor = ..., + direction: Literal["above", "below", "left", "right", "flush"] = ..., + disabledforeground: _Color = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + height: _ScreenUnits = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + image: _ImageSpec = ..., + indicatoron: bool = ..., + justify: Literal["left", "center", "right"] = ..., + menu: Menu = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + relief: _Relief = ..., + state: Literal["normal", "active", "disabled"] = ..., + takefocus: _TakeFocusValue = ..., + text: str = ..., + textvariable: Variable = ..., + underline: int = ..., + width: _ScreenUnits = ..., + wraplength: _ScreenUnits = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _MenubuttonOptionName) -> Tuple[str, str, str, Any, Any]: ... + # config is just like configure, but copy/pasta, because assigning + # 'config = configure' creates mypy errors here for some reason + @overload + def config( + self, + cnf: Dict[str, Any] = ..., + *, + activebackground: _Color = ..., + activeforeground: _Color = ..., + anchor: _Anchor = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + bitmap: _Bitmap = ..., + borderwidth: _ScreenUnits = ..., + compound: _Compound = ..., + cursor: _Cursor = ..., + direction: Literal["above", "below", "left", "right", "flush"] = ..., + disabledforeground: _Color = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + height: _ScreenUnits = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + image: _ImageSpec = ..., + indicatoron: bool = ..., + justify: Literal["left", "center", "right"] = ..., + menu: Menu = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + relief: _Relief = ..., + state: Literal["normal", "active", "disabled"] = ..., + takefocus: _TakeFocusValue = ..., + text: str = ..., + textvariable: Variable = ..., + underline: int = ..., + width: _ScreenUnits = ..., + wraplength: _ScreenUnits = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def config(self, cnf: _MenubuttonOptionName) -> Tuple[str, str, str, Any, Any]: ... + def cget(self, key: _MenubuttonOptionName) -> Any: ... + +_MessageOptionName = Literal[ + "anchor", + "aspect", + "background", + "bd", + "bg", + "borderwidth", + "cursor", + "fg", + "font", + "foreground", + "highlightbackground", + "highlightcolor", + "highlightthickness", + "justify", + "padx", + "pady", + "relief", + "takefocus", + "text", + "textvariable", + "width", +] class Message(Widget): - def __init__(self, master: Optional[Any] = ..., cnf=..., **kw): ... + def __init__( + self, + master: Optional[Misc] = ..., + cnf: Dict[str, Any] = ..., + *, + anchor: _Anchor = ..., + aspect: int = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + borderwidth: _ScreenUnits = ..., + cursor: _Cursor = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + justify: Literal["left", "center", "right"] = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + relief: _Relief = ..., + takefocus: _TakeFocusValue = ..., + text: str = ..., + textvariable: Variable = ..., + # there's width but no height + width: _ScreenUnits = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + anchor: _Anchor = ..., + aspect: int = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + borderwidth: _ScreenUnits = ..., + cursor: _Cursor = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + justify: Literal["left", "center", "right"] = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + relief: _Relief = ..., + takefocus: _TakeFocusValue = ..., + text: str = ..., + textvariable: Variable = ..., + width: _ScreenUnits = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _MessageOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _MessageOptionName) -> Any: ... + +_RadiobuttonOptionName = Literal[ + "activebackground", + "activeforeground", + "anchor", + "background", + "bd", + "bg", + "bitmap", + "borderwidth", + "command", + "compound", + "cursor", + "disabledforeground", + "fg", + "font", + "foreground", + "height", + "highlightbackground", + "highlightcolor", + "highlightthickness", + "image", + "indicatoron", + "justify", + "offrelief", + "overrelief", + "padx", + "pady", + "relief", + "selectcolor", + "selectimage", + "state", + "takefocus", + "text", + "textvariable", + "tristateimage", + "tristatevalue", + "underline", + "value", + "variable", + "width", + "wraplength", +] class Radiobutton(Widget): - def __init__(self, master: Optional[Any] = ..., cnf=..., **kw): ... + def __init__( + self, + master: Optional[Misc] = ..., + cnf: Dict[str, Any] = ..., + *, + activebackground: _Color = ..., + activeforeground: _Color = ..., + anchor: _Anchor = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + bitmap: _Bitmap = ..., + borderwidth: _ScreenUnits = ..., + command: _ButtonCommand = ..., + compound: _Compound = ..., + cursor: _Cursor = ..., + disabledforeground: _Color = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + height: _ScreenUnits = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + image: _ImageSpec = ..., + indicatoron: bool = ..., + justify: Literal["left", "center", "right"] = ..., + offrelief: _Relief = ..., + overrelief: _Relief = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + relief: _Relief = ..., + selectcolor: _Color = ..., + selectimage: _ImageSpec = ..., + state: Literal["normal", "active", "disabled"] = ..., + takefocus: _TakeFocusValue = ..., + text: str = ..., + textvariable: Variable = ..., + tristateimage: _ImageSpec = ..., + tristatevalue: Any = ..., + underline: int = ..., + value: Any = ..., + variable: Union[Variable, Literal[""]] = ..., + width: _ScreenUnits = ..., + wraplength: _ScreenUnits = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + activebackground: _Color = ..., + activeforeground: _Color = ..., + anchor: _Anchor = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + bitmap: _Bitmap = ..., + borderwidth: _ScreenUnits = ..., + command: _ButtonCommand = ..., + compound: _Compound = ..., + cursor: _Cursor = ..., + disabledforeground: _Color = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + height: _ScreenUnits = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + image: _ImageSpec = ..., + indicatoron: bool = ..., + justify: Literal["left", "center", "right"] = ..., + offrelief: _Relief = ..., + overrelief: _Relief = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + relief: _Relief = ..., + selectcolor: _Color = ..., + selectimage: _ImageSpec = ..., + state: Literal["normal", "active", "disabled"] = ..., + takefocus: _TakeFocusValue = ..., + text: str = ..., + textvariable: Variable = ..., + tristateimage: _ImageSpec = ..., + tristatevalue: Any = ..., + underline: int = ..., + value: Any = ..., + variable: Union[Variable, Literal[""]] = ..., + width: _ScreenUnits = ..., + wraplength: _ScreenUnits = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _RadiobuttonOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _RadiobuttonOptionName) -> Any: ... def deselect(self): ... def flash(self): ... def invoke(self): ... def select(self): ... +_ScaleOptionName = Literal[ + "activebackground", + "background", + "bd", + "bg", + "bigincrement", + "borderwidth", + "command", + "cursor", + "digits", + "fg", + "font", + "foreground", + "from", + "highlightbackground", + "highlightcolor", + "highlightthickness", + "label", + "length", + "orient", + "relief", + "repeatdelay", + "repeatinterval", + "resolution", + "showvalue", + "sliderlength", + "sliderrelief", + "state", + "takefocus", + "tickinterval", + "to", + "troughcolor", + "variable", + "width", +] + class Scale(Widget): - def __init__(self, master: Optional[Any] = ..., cnf=..., **kw): ... + def __init__( + self, + master: Optional[Misc] = ..., + cnf: Dict[str, Any] = ..., + *, + activebackground: _Color = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + bigincrement: float = ..., + borderwidth: _ScreenUnits = ..., + # don't know why the callback gets string instead of float + command: Union[str, Callable[[str], None]] = ..., + cursor: _Cursor = ..., + digits: int = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + from_: float = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + label: str = ..., + length: _ScreenUnits = ..., + orient: Literal["horizontal", "vertical"] = ..., + relief: _Relief = ..., + repeatdelay: int = ..., + repeatinterval: int = ..., + resolution: float = ..., + showvalue: bool = ..., + sliderlength: _ScreenUnits = ..., + sliderrelief: _Relief = ..., + state: Literal["normal", "active", "disabled"] = ..., + takefocus: _TakeFocusValue = ..., + tickinterval: float = ..., + to: float = ..., + troughcolor: _Color = ..., + variable: DoubleVar = ..., + width: _ScreenUnits = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + activebackground: _Color = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + bigincrement: float = ..., + borderwidth: _ScreenUnits = ..., + command: Union[str, Callable[[str], None]] = ..., + cursor: _Cursor = ..., + digits: int = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + from_: float = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + label: str = ..., + length: _ScreenUnits = ..., + orient: Literal["horizontal", "vertical"] = ..., + relief: _Relief = ..., + repeatdelay: int = ..., + repeatinterval: int = ..., + resolution: float = ..., + showvalue: bool = ..., + sliderlength: _ScreenUnits = ..., + sliderrelief: _Relief = ..., + state: Literal["normal", "active", "disabled"] = ..., + takefocus: _TakeFocusValue = ..., + tickinterval: float = ..., + to: float = ..., + troughcolor: _Color = ..., + variable: DoubleVar = ..., + width: _ScreenUnits = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _ScaleOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _ScaleOptionName) -> Any: ... def get(self): ... def set(self, value): ... def coords(self, value: Optional[Any] = ...): ... def identify(self, x, y): ... +_ScrollbarOptionName = Literal[ + "activebackground", + "activerelief", + "background", + "bd", + "bg", + "borderwidth", + "command", + "cursor", + "elementborderwidth", + "highlightbackground", + "highlightcolor", + "highlightthickness", + "jump", + "orient", + "relief", + "repeatdelay", + "repeatinterval", + "takefocus", + "troughcolor", + "width", +] + class Scrollbar(Widget): - def __init__(self, master: Optional[Any] = ..., cnf=..., **kw): ... + def __init__( + self, + master: Optional[Misc] = ..., + cnf: Dict[str, Any] = ..., + *, + activebackground: _Color = ..., + activerelief: _Relief = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + borderwidth: _ScreenUnits = ..., + # There are many ways how the command may get called. Search for + # 'SCROLLING COMMANDS' in scrollbar man page. There doesn't seem to + # be any way to specify an overloaded callback function, so we say + # that it can take any args while it can't in reality. + command: Union[Callable[..., Optional[Tuple[float, float]]], str] = ..., + cursor: _Cursor = ..., + elementborderwidth: _ScreenUnits = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + jump: bool = ..., + orient: Literal["horizontal", "vertical"] = ..., + relief: _Relief = ..., + repeatdelay: int = ..., + repeatinterval: int = ..., + takefocus: _TakeFocusValue = ..., + troughcolor: _Color = ..., + width: _ScreenUnits = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + activebackground: _Color = ..., + activerelief: _Relief = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + borderwidth: _ScreenUnits = ..., + command: Union[Callable[..., Optional[Tuple[float, float]]], str] = ..., + cursor: _Cursor = ..., + elementborderwidth: _ScreenUnits = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + jump: bool = ..., + orient: Literal["horizontal", "vertical"] = ..., + relief: _Relief = ..., + repeatdelay: int = ..., + repeatinterval: int = ..., + takefocus: _TakeFocusValue = ..., + troughcolor: _Color = ..., + width: _ScreenUnits = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _ScrollbarOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _ScrollbarOptionName) -> Any: ... def activate(self, index: Optional[Any] = ...): ... def delta(self, deltax, deltay): ... def fraction(self, x, y): ... @@ -679,8 +2438,162 @@ class Scrollbar(Widget): def get(self): ... def set(self, first, last): ... +_TextOptionName = Literal[ + "autoseparators", + "background", + "bd", + "bg", + "blockcursor", + "borderwidth", + "cursor", + "endline", + "exportselection", + "fg", + "font", + "foreground", + "height", + "highlightbackground", + "highlightcolor", + "highlightthickness", + "inactiveselectbackground", + "insertbackground", + "insertborderwidth", + "insertofftime", + "insertontime", + "insertunfocussed", + "insertwidth", + "maxundo", + "padx", + "pady", + "relief", + "selectbackground", + "selectborderwidth", + "selectforeground", + "setgrid", + "spacing1", + "spacing2", + "spacing3", + "startline", + "state", + "tabs", + "tabstyle", + "takefocus", + "undo", + "width", + "wrap", + "xscrollcommand", + "yscrollcommand", +] + class Text(Widget, XView, YView): - def __init__(self, master: Optional[Any] = ..., cnf=..., **kw): ... + def __init__( + self, + master: Optional[Misc] = ..., + cnf: Dict[str, Any] = ..., + *, + autoseparators: bool = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + blockcursor: bool = ..., + borderwidth: _ScreenUnits = ..., + cursor: _Cursor = ..., + endline: Union[int, Literal[""]] = ..., + exportselection: bool = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + # width is always int, but height is allowed to be ScreenUnits. + # This doesn't make any sense to me, and this isn't documented. + # The docs seem to say that both should be integers. + height: _ScreenUnits = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + inactiveselectbackground: _Color = ..., + insertbackground: _Color = ..., + insertborderwidth: _ScreenUnits = ..., + insertofftime: int = ..., + insertontime: int = ..., + insertunfocussed: Literal["none", "hollow", "solid"] = ..., + insertwidth: _ScreenUnits = ..., + maxundo: int = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + relief: _Relief = ..., + selectbackground: _Color = ..., + selectborderwidth: _ScreenUnits = ..., + selectforeground: _Color = ..., + setgrid: bool = ..., + spacing1: _ScreenUnits = ..., + spacing2: _ScreenUnits = ..., + spacing3: _ScreenUnits = ..., + startline: Union[int, Literal[""]] = ..., + state: Literal["normal", "disabled"] = ..., + # Literal inside Tuple doesn't actually work + tabs: Tuple[Union[_ScreenUnits, str], ...] = ..., + tabstyle: Literal["tabular", "wordprocessor"] = ..., + takefocus: _TakeFocusValue = ..., + undo: bool = ..., + width: int = ..., + wrap: Literal["none", "char", "word"] = ..., + xscrollcommand: _XYScrollCommand = ..., + yscrollcommand: _XYScrollCommand = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + autoseparators: bool = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + blockcursor: bool = ..., + borderwidth: _ScreenUnits = ..., + cursor: _Cursor = ..., + endline: Union[int, Literal[""]] = ..., + exportselection: bool = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + height: _ScreenUnits = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + inactiveselectbackground: _Color = ..., + insertbackground: _Color = ..., + insertborderwidth: _ScreenUnits = ..., + insertofftime: int = ..., + insertontime: int = ..., + insertunfocussed: Literal["none", "hollow", "solid"] = ..., + insertwidth: _ScreenUnits = ..., + maxundo: int = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + relief: _Relief = ..., + selectbackground: _Color = ..., + selectborderwidth: _ScreenUnits = ..., + selectforeground: _Color = ..., + setgrid: bool = ..., + spacing1: _ScreenUnits = ..., + spacing2: _ScreenUnits = ..., + spacing3: _ScreenUnits = ..., + startline: Union[int, Literal[""]] = ..., + state: Literal["normal", "disabled"] = ..., + tabs: Tuple[Union[_ScreenUnits, str], ...] = ..., + tabstyle: Literal["tabular", "wordprocessor"] = ..., + takefocus: _TakeFocusValue = ..., + undo: bool = ..., + width: int = ..., + wrap: Literal["none", "char", "word"] = ..., + xscrollcommand: _XYScrollCommand = ..., + yscrollcommand: _XYScrollCommand = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _TextOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _TextOptionName) -> Any: ... def bbox(self, index): ... def compare(self, index1, op, index2): ... def count(self, index1, index2, *args): ... @@ -761,17 +2674,39 @@ class _setit: def __init__(self, var, value, callback: Optional[Any] = ...): ... def __call__(self, *args): ... +# This widget is weird: +# - Manual page is named tk_optionMenu instead of menu. +# - It's not actually a Tk widget but a Tcl procedure that creates two +# associated widgets. +# - Its __init__ takes in arguments differently from all other widgets +# (self + required positional arguments + special keyword-only arguments). +# - Tkinter uses the exact same options as Menubutton except that __getitem__ +# is overrided to give a tkinter widget when you request its 'menu' option. +# However, querying it with 'cget' gives you a string instead of a widget +# object. +# +# This doesn't support any other variables than StringVar, which could be +# useful for an OptionMenu with a dropdown of integers to choose from. Add +# overrides to __init__ if this is a problem in practice. class OptionMenu(Menubutton): widgetName: Any menuname: Any - def __init__(self, master, variable, value, *values, **kwargs): ... - def __getitem__(self, name): ... - def destroy(self): ... + def __init__( + self, + master: Optional[Misc], + variable: StringVar, + value: str, + *values: str, + # kwarg only from now on + command: Optional[Callable[[StringVar], None]] = ..., + ) -> None: ... + # configure, config, cget are inherited from Menubutton + # destroy and __setitem__ are overrided, signature does not change class Image: name: Any tk: Any - def __init__(self, imgtype, name: Optional[Any] = ..., cnf=..., master: Optional[Any] = ..., **kw): ... + def __init__(self, imgtype, name: Optional[Any] = ..., cnf=..., master: Optional[Misc] = ..., **kw): ... def __del__(self): ... def __setitem__(self, key, value): ... def __getitem__(self, key): ... @@ -782,7 +2717,7 @@ class Image: def width(self): ... class PhotoImage(Image): - def __init__(self, name: Optional[Any] = ..., cnf=..., master: Optional[Any] = ..., **kw): ... + def __init__(self, name: Optional[Any] = ..., cnf=..., master: Optional[Misc] = ..., **kw): ... def blank(self): ... def cget(self, option): ... def __getitem__(self, key): ... @@ -797,13 +2732,173 @@ class PhotoImage(Image): def transparency_set(self, x: int, y: int, boolean: bool) -> None: ... class BitmapImage(Image): - def __init__(self, name: Optional[Any] = ..., cnf=..., master: Optional[Any] = ..., **kw): ... + def __init__(self, name: Optional[Any] = ..., cnf=..., master: Optional[Misc] = ..., **kw): ... def image_names(): ... def image_types(): ... +_SpinboxOptionName = Literal[ + "activebackground", + "background", + "bd", + "bg", + "borderwidth", + "buttonbackground", + "buttoncursor", + "buttondownrelief", + "buttonuprelief", + "command", + "cursor", + "disabledbackground", + "disabledforeground", + "exportselection", + "fg", + "font", + "foreground", + "format", + "from", + "highlightbackground", + "highlightcolor", + "highlightthickness", + "increment", + "insertbackground", + "insertborderwidth", + "insertofftime", + "insertontime", + "insertwidth", + "invalidcommand", + "justify", + "readonlybackground", + "relief", + "repeatdelay", + "repeatinterval", + "selectbackground", + "selectborderwidth", + "selectforeground", + "state", + "takefocus", + "textvariable", + "to", + "validate", + "validatecommand", + "values", + "width", + "wrap", + "xscrollcommand", +] + class Spinbox(Widget, XView): - def __init__(self, master: Optional[Any] = ..., cnf=..., **kw): ... + def __init__( + self, + master: Optional[Misc] = ..., + cnf: Dict[str, Any] = ..., + *, + activebackground: _Color = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + borderwidth: _ScreenUnits = ..., + buttonbackground: _Color = ..., + buttoncursor: _Cursor = ..., + buttondownrelief: _Relief = ..., + buttonuprelief: _Relief = ..., + # percent substitutions don't seem to be supported, it's similar to Entry's validion stuff + command: Union[Callable[[], None], str, _TkinterSequence[str]] = ..., + cursor: _Cursor = ..., + disabledbackground: _Color = ..., + disabledforeground: _Color = ..., + exportselection: bool = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + format: str = ..., + from_: float = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + increment: float = ..., + insertbackground: _Color = ..., + insertborderwidth: _ScreenUnits = ..., + insertofftime: int = ..., + insertontime: int = ..., + insertwidth: _ScreenUnits = ..., + invalidcommand: _EntryValidateCommand = ..., + justify: Literal["left", "center", "right"] = ..., + readonlybackground: _Color = ..., + relief: _Relief = ..., + repeatdelay: int = ..., + repeatinterval: int = ..., + selectbackground: _Color = ..., + selectborderwidth: _ScreenUnits = ..., + selectforeground: _Color = ..., + state: Literal["normal", "disabled", "readonly"] = ..., + takefocus: _TakeFocusValue = ..., + textvariable: Variable = ..., + to: float = ..., + validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., + validatecommand: _EntryValidateCommand = ..., + values: Union[_TkinterSequence[str], _TkinterSequence[float]] = ..., + width: int = ..., + wrap: bool = ..., + xscrollcommand: _XYScrollCommand = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + activebackground: _Color = ..., + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + borderwidth: _ScreenUnits = ..., + buttonbackground: _Color = ..., + buttoncursor: _Cursor = ..., + buttondownrelief: _Relief = ..., + buttonuprelief: _Relief = ..., + command: Union[Callable[[], None], str, _TkinterSequence[str]] = ..., + cursor: _Cursor = ..., + disabledbackground: _Color = ..., + disabledforeground: _Color = ..., + exportselection: bool = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + format: str = ..., + from_: float = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + increment: float = ..., + insertbackground: _Color = ..., + insertborderwidth: _ScreenUnits = ..., + insertofftime: int = ..., + insertontime: int = ..., + insertwidth: _ScreenUnits = ..., + invalidcommand: _EntryValidateCommand = ..., + justify: Literal["left", "center", "right"] = ..., + readonlybackground: _Color = ..., + relief: _Relief = ..., + repeatdelay: int = ..., + repeatinterval: int = ..., + selectbackground: _Color = ..., + selectborderwidth: _ScreenUnits = ..., + selectforeground: _Color = ..., + state: Literal["normal", "disabled", "readonly"] = ..., + takefocus: _TakeFocusValue = ..., + textvariable: Variable = ..., + to: float = ..., + validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., + validatecommand: _EntryValidateCommand = ..., + values: Union[_TkinterSequence[str], _TkinterSequence[float]] = ..., + width: int = ..., + wrap: bool = ..., + xscrollcommand: _XYScrollCommand = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _SpinboxOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _SpinboxOptionName) -> Any: ... def bbox(self, index): ... def delete(self, first, last: Optional[Any] = ...): ... def get(self): ... @@ -825,11 +2920,174 @@ class Spinbox(Widget, XView): def selection_range(self, start: int, end: int) -> None: ... def selection_to(self, index: int) -> None: ... +_LabelFrameOptionName = Literal[ + "background", + "bd", + "bg", + "borderwidth", + "class", + "colormap", + "cursor", + "fg", + "font", + "foreground", + "height", + "highlightbackground", + "highlightcolor", + "highlightthickness", + "labelanchor", + "labelwidget", + "padx", + "pady", + "relief", + "takefocus", + "text", + "visual", + "width", +] + class LabelFrame(Widget): - def __init__(self, master: Optional[Any] = ..., cnf=..., **kw): ... + def __init__( + self, + master: Optional[Misc] = ..., + cnf: Dict[str, Any] = ..., + *, + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + borderwidth: _ScreenUnits = ..., + class_: str = ..., + colormap: Union[Literal["new", ""], Misc] = ..., + cursor: _Cursor = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + height: _ScreenUnits = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + # 'ne' and 'en' are valid labelanchors, but only 'ne' is a valid _Anchor. + labelanchor: Literal["nw", "n", "ne", "en", "e", "es", "se", "s", "sw", "ws", "w", "wn"] = ..., + labelwidget: Misc = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + relief: _Relief = ..., + takefocus: _TakeFocusValue = ..., + text: str = ..., + visual: Union[str, Tuple[str, int]] = ..., + width: _ScreenUnits = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + borderwidth: _ScreenUnits = ..., + cursor: _Cursor = ..., + fg: _Color = ..., + font: _FontDescription = ..., + foreground: _Color = ..., + height: _ScreenUnits = ..., + highlightbackground: _Color = ..., + highlightcolor: _Color = ..., + highlightthickness: _ScreenUnits = ..., + labelanchor: Literal["nw", "n", "ne", "en", "e", "es", "se", "s", "sw", "ws", "w", "wn"] = ..., + labelwidget: Misc = ..., + padx: _ScreenUnits = ..., + pady: _ScreenUnits = ..., + relief: _Relief = ..., + takefocus: _TakeFocusValue = ..., + text: str = ..., + width: _ScreenUnits = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _LabelFrameOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _LabelFrameOptionName) -> Any: ... + +_PanedWindowOptionName = Literal[ + "background", + "bd", + "bg", + "borderwidth", + "cursor", + "handlepad", + "handlesize", + "height", + "opaqueresize", + "orient", + "proxybackground", + "proxyborderwidth", + "proxyrelief", + "relief", + "sashcursor", + "sashpad", + "sashrelief", + "sashwidth", + "showhandle", + "width", +] class PanedWindow(Widget): - def __init__(self, master: Optional[Any] = ..., cnf=..., **kw): ... + def __init__( + self, + master: Optional[Misc] = ..., + cnf: Dict[str, Any] = ..., + *, + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + borderwidth: _ScreenUnits = ..., + cursor: _Cursor = ..., + handlepad: _ScreenUnits = ..., + handlesize: _ScreenUnits = ..., + height: _ScreenUnits = ..., + opaqueresize: bool = ..., + orient: Literal["horizontal", "vertical"] = ..., + proxybackground: _Color = ..., + proxyborderwidth: _ScreenUnits = ..., + proxyrelief: _Relief = ..., + relief: _Relief = ..., + sashcursor: _Cursor = ..., + sashpad: _ScreenUnits = ..., + sashrelief: _Relief = ..., + sashwidth: _ScreenUnits = ..., + showhandle: bool = ..., + width: _ScreenUnits = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + background: _Color = ..., + bd: _ScreenUnits = ..., + bg: _Color = ..., + borderwidth: _ScreenUnits = ..., + cursor: _Cursor = ..., + handlepad: _ScreenUnits = ..., + handlesize: _ScreenUnits = ..., + height: _ScreenUnits = ..., + opaqueresize: bool = ..., + orient: Literal["horizontal", "vertical"] = ..., + proxybackground: _Color = ..., + proxyborderwidth: _ScreenUnits = ..., + proxyrelief: _Relief = ..., + relief: _Relief = ..., + sashcursor: _Cursor = ..., + sashpad: _ScreenUnits = ..., + sashrelief: _Relief = ..., + sashwidth: _ScreenUnits = ..., + showhandle: bool = ..., + width: _ScreenUnits = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _PanedWindowOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _PanedWindowOptionName) -> Any: ... def add(self, child, **kw): ... def remove(self, child): ... forget: Any diff --git a/stdlib/3/tkinter/constants.pyi b/stdlib/3/tkinter/constants.pyi index e21a93e69cea..97ad295b3977 100644 --- a/stdlib/3/tkinter/constants.pyi +++ b/stdlib/3/tkinter/constants.pyi @@ -1,79 +1,79 @@ -from typing import Any +from typing_extensions import Literal -NO: Any -YES: Any -TRUE: Any -FALSE: Any -ON: Any -OFF: Any -N: Any -S: Any -W: Any -E: Any -NW: Any -SW: Any -NE: Any -SE: Any -NS: Any -EW: Any -NSEW: Any -CENTER: Any -NONE: Any -X: Any -Y: Any -BOTH: Any -LEFT: Any -TOP: Any -RIGHT: Any -BOTTOM: Any -RAISED: Any -SUNKEN: Any -FLAT: Any -RIDGE: Any -GROOVE: Any -SOLID: Any -HORIZONTAL: Any -VERTICAL: Any -NUMERIC: Any -CHAR: Any -WORD: Any -BASELINE: Any -INSIDE: Any -OUTSIDE: Any -SEL: Any -SEL_FIRST: Any -SEL_LAST: Any -END: Any -INSERT: Any -CURRENT: Any -ANCHOR: Any -ALL: Any -NORMAL: Any -DISABLED: Any -ACTIVE: Any -HIDDEN: Any -CASCADE: Any -CHECKBUTTON: Any -COMMAND: Any -RADIOBUTTON: Any -SEPARATOR: Any -SINGLE: Any -BROWSE: Any -MULTIPLE: Any -EXTENDED: Any -DOTBOX: Any -UNDERLINE: Any -PIESLICE: Any -CHORD: Any -ARC: Any -FIRST: Any -LAST: Any -BUTT: Any -PROJECTING: Any -ROUND: Any -BEVEL: Any -MITER: Any -MOVETO: Any -SCROLL: Any -UNITS: Any -PAGES: Any +NO: Literal[0] +YES: Literal[1] +TRUE: Literal[1] +FALSE: Literal[0] +ON: Literal[1] +OFF: Literal[0] +N: Literal["n"] +S: Literal["s"] +W: Literal["w"] +E: Literal["e"] +NW: Literal["nw"] +SW: Literal["sw"] +NE: Literal["ne"] +SE: Literal["se"] +NS: Literal["ns"] +EW: Literal["ew"] +NSEW: Literal["nsew"] +CENTER: Literal["center"] +NONE: Literal["none"] +X: Literal["x"] +Y: Literal["y"] +BOTH: Literal["both"] +LEFT: Literal["left"] +TOP: Literal["top"] +RIGHT: Literal["right"] +BOTTOM: Literal["bottom"] +RAISED: Literal["raised"] +SUNKEN: Literal["sunken"] +FLAT: Literal["flat"] +RIDGE: Literal["ridge"] +GROOVE: Literal["groove"] +SOLID: Literal["solid"] +HORIZONTAL: Literal["horizontal"] +VERTICAL: Literal["vertical"] +NUMERIC: Literal["numeric"] +CHAR: Literal["char"] +WORD: Literal["word"] +BASELINE: Literal["baseline"] +INSIDE: Literal["inside"] +OUTSIDE: Literal["outside"] +SEL: Literal["sel"] +SEL_FIRST: Literal["sel.first"] +SEL_LAST: Literal["sel.last"] +END: Literal["end"] +INSERT: Literal["insert"] +CURRENT: Literal["current"] +ANCHOR: Literal["anchor"] +ALL: Literal["all"] +NORMAL: Literal["normal"] +DISABLED: Literal["disabled"] +ACTIVE: Literal["active"] +HIDDEN: Literal["hidden"] +CASCADE: Literal["cascade"] +CHECKBUTTON: Literal["checkbutton"] +COMMAND: Literal["command"] +RADIOBUTTON: Literal["radiobutton"] +SEPARATOR: Literal["separator"] +SINGLE: Literal["single"] +BROWSE: Literal["browse"] +MULTIPLE: Literal["multiple"] +EXTENDED: Literal["extended"] +DOTBOX: Literal["dotbox"] +UNDERLINE: Literal["underline"] +PIESLICE: Literal["pieslice"] +CHORD: Literal["chord"] +ARC: Literal["arc"] +FIRST: Literal["first"] +LAST: Literal["last"] +BUTT: Literal["butt"] +PROJECTING: Literal["projecting"] +ROUND: Literal["round"] +BEVEL: Literal["bevel"] +MITER: Literal["miter"] +MOVETO: Literal["moveto"] +SCROLL: Literal["scroll"] +UNITS: Literal["units"] +PAGES: Literal["pages"] diff --git a/stdlib/3/tkinter/font.pyi b/stdlib/3/tkinter/font.pyi index cd9aca4d0bb9..2cf04afe9cdd 100644 --- a/stdlib/3/tkinter/font.pyi +++ b/stdlib/3/tkinter/font.pyi @@ -2,6 +2,11 @@ import tkinter from typing import List, Optional, Tuple, TypeVar, Union, overload from typing_extensions import Literal, TypedDict +# copy/pasta from __init__.py to make pytype happy +# https://github.com/google/pytype/issues/626 +_T = TypeVar("_T") +_TkinterSequence = Union[List[_T], Tuple[_T, ...]] + NORMAL: Literal["normal"] ROMAN: Literal["roman"] BOLD: Literal["bold"] @@ -9,23 +14,6 @@ ITALIC: Literal["italic"] def nametofont(name: str) -> Font: ... -# _TkinterSequence[T] represents a sequence that tkinter understands. It -# differs from typing.Sequence[T]. For example, collections.deque a valid -# Sequence but not a valid _TkinterSequence: -# -# >>> tkinter.Label(font=('Helvetica', 12, collections.deque(['bold']))) -# Traceback (most recent call last): -# ... -# _tkinter.TclError: unknown font style "deque(['bold'])" -_T = TypeVar("_T") -_TkinterSequence = Union[List[_T], Tuple[_T, ...]] - -# See 'FONT DESCRIPTIONS' in font man page. This uses str because Literal -# inside Tuple doesn't work. -_FontDescription = Union[ - str, Font, Tuple[str, int], Tuple[str, int, _TkinterSequence[str]], -] - class _FontDict(TypedDict): family: str size: int @@ -48,7 +36,7 @@ class Font: # In tkinter, 'root' refers to tkinter.Tk by convention, but the code # actually works with any tkinter widget so we use tkinter.Misc. root: Optional[tkinter.Misc] = ..., - font: Optional[_FontDescription] = ..., + font: Optional[tkinter._FontDescription] = ..., name: Optional[str] = ..., exists: bool = ..., *, diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index 4cd08dfa052e..677a08330eaa 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -3,14 +3,28 @@ import tkinter from tkinter import Event from typing import Any, Callable, Dict, List, Optional, Tuple, Type, TypeVar, Union, overload from typing_extensions import Literal +from enum import Enum +from types import TracebackType +from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Type, TypeVar, Union, overload +from typing_extensions import Literal + +import _tkinter + +# copy/pasta from __init__.py to make pytype happy +# https://github.com/google/pytype/issues/626 +_T = TypeVar("_T") +_TkinterSequence = Union[List[_T], Tuple[_T, ...]] def tclobjs_to_py(adict): ... def setup_master(master: Optional[Any] = ...): ... +# from ttk_widget (aka ttk::widget) manual page, differs from tkinter._Compound +_TtkCompound = Literal["text", "image", tkinter._Compound] + class Style: - master: Any + master: tkinter.Misc tk: Any - def __init__(self, master: Optional[Any] = ...): ... + def __init__(self, master: Optional[tkinter.Misc] = ...): ... def configure(self, style, query_opt: Optional[Any] = ..., **kw): ... def map(self, style, query_opt: Optional[Any] = ..., **kw): ... def lookup(self, style, option, state: Optional[Any] = ..., default: Optional[Any] = ...): ... @@ -24,46 +38,569 @@ class Style: def theme_use(self, themename: Optional[Any] = ...): ... class Widget(tkinter.Widget): - def __init__(self, master, widgetname, kw: Optional[Any] = ...): ... + def __init__(self, master: Optional[tkinter.Misc], widgetname, kw: Optional[Any] = ...): ... def identify(self, x, y): ... def instate(self, statespec, callback: Optional[Any] = ..., *args, **kw): ... def state(self, statespec: Optional[Any] = ...): ... +_ButtonOptionName = Literal[ + "class", + "command", + "compound", + "cursor", + "default", + "image", + "state", + "style", + "takefocus", + "text", + "textvariable", + "underline", + "width", +] + class Button(Widget): - def __init__(self, master: Optional[Any] = ..., **kw): ... + def __init__( + self, + master: Optional[tkinter.Misc] = ..., + *, + class_: str = ..., + command: tkinter._ButtonCommand = ..., + compound: _TtkCompound = ..., + cursor: tkinter._Cursor = ..., + default: Literal["normal", "active", "disabled"] = ..., + image: tkinter._ImageSpec = ..., + state: Literal["normal", "disabled"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + text: str = ..., + textvariable: tkinter.Variable = ..., + underline: int = ..., + width: Union[int, Literal[""]] = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + command: tkinter._ButtonCommand = ..., + compound: _TtkCompound = ..., + cursor: tkinter._Cursor = ..., + default: Literal["normal", "active", "disabled"] = ..., + image: tkinter._ImageSpec = ..., + state: Literal["normal", "disabled"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + text: str = ..., + textvariable: tkinter.Variable = ..., + underline: int = ..., + width: Union[int, Literal[""]] = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _ButtonOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _ButtonOptionName) -> Any: ... def invoke(self): ... +_CheckbuttonOptionName = Literal[ + "class", + "command", + "compound", + "cursor", + "image", + "offvalue", + "onvalue", + "state", + "style", + "takefocus", + "text", + "textvariable", + "underline", + "variable", + "width", +] + class Checkbutton(Widget): - def __init__(self, master: Optional[Any] = ..., **kw): ... + def __init__( + self, + master: Optional[tkinter.Misc] = ..., + *, + class_: str = ..., + command: tkinter._ButtonCommand = ..., + compound: _TtkCompound = ..., + cursor: tkinter._Cursor = ..., + image: tkinter._ImageSpec = ..., + offvalue: Any = ..., + onvalue: Any = ..., + state: Literal["normal", "disabled"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + text: str = ..., + textvariable: tkinter.Variable = ..., + underline: int = ..., + # Seems like variable can be empty string, but actually setting it to + # empty string segfaults before Tcl 8.6.9. Search for ttk::checkbutton + # here: https://sourceforge.net/projects/tcl/files/Tcl/8.6.9/tcltk-release-notes-8.6.9.txt/view + variable: tkinter.Variable = ..., + width: Union[int, Literal[""]] = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + command: tkinter._ButtonCommand = ..., + compound: _TtkCompound = ..., + cursor: tkinter._Cursor = ..., + image: tkinter._ImageSpec = ..., + offvalue: Any = ..., + onvalue: Any = ..., + state: Literal["normal", "disabled"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + text: str = ..., + textvariable: tkinter.Variable = ..., + underline: int = ..., + variable: tkinter.Variable = ..., + width: Union[int, Literal[""]] = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _CheckbuttonOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _CheckbuttonOptionName) -> Any: ... def invoke(self): ... -class Entry(Widget, tkinter.Entry): - def __init__(self, master: Optional[Any] = ..., widget: Optional[Any] = ..., **kw): ... +_EntryOptionName = Literal[ + "class", + "cursor", + "exportselection", + "invalidcommand", + "justify", + "show", + "state", + "style", + "takefocus", + "textvariable", + "validate", + "validatecommand", + "width", + "xscrollcommand", +] + +# This actually inherits from tkinter.Entry, but it doesn't have all +# options of tkinter.Entry so we can't make the stubs look like it inherits +# from tkinter.Entry. +class Entry(Widget): # actually inherits from tkinter.Entry + def __init__( + self, + master: Optional[tkinter.Misc] = ..., + *, + class_: str = ..., + cursor: tkinter._Cursor = ..., + exportselection: bool = ..., + invalidcommand: tkinter._EntryValidateCommand = ..., + justify: Literal["left", "center", "right"] = ..., + show: str = ..., + state: Literal["normal", "disabled", "readonly"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + textvariable: tkinter.Variable = ..., + validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., + validatecommand: tkinter._EntryValidateCommand = ..., + width: int = ..., + xscrollcommand: tkinter._XYScrollCommand = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + cursor: tkinter._Cursor = ..., + exportselection: bool = ..., + invalidcommand: tkinter._EntryValidateCommand = ..., + justify: Literal["left", "center", "right"] = ..., + show: str = ..., + state: Literal["normal", "disabled", "readonly"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + textvariable: tkinter.Variable = ..., + validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., + validatecommand: tkinter._EntryValidateCommand = ..., + width: int = ..., + xscrollcommand: tkinter._XYScrollCommand = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _EntryOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _EntryOptionName) -> Any: ... def bbox(self, index): ... def identify(self, x, y): ... def validate(self): ... + delete = tkinter.Entry.delete + get = tkinter.Entry.get + icursor = tkinter.Entry.icursor + index = tkinter.Entry.index + insert = tkinter.Entry.insert + scan_mark = tkinter.Entry.scan_mark + scan_dragto = tkinter.Entry.scan_dragto + selection_adjust = tkinter.Entry.selection_adjust + select_adjust = tkinter.Entry.select_adjust + selection_clear = tkinter.Entry.selection_clear + select_clear = tkinter.Entry.select_clear + selection_from = tkinter.Entry.selection_from + select_from = tkinter.Entry.select_from + selection_present = tkinter.Entry.selection_present + select_present = tkinter.Entry.select_present + selection_range = tkinter.Entry.selection_range + select_range = tkinter.Entry.select_range + selection_to = tkinter.Entry.selection_to + select_to = tkinter.Entry.select_to + +_ComboboxOptionName = Literal[ + "class", + "cursor", + "exportselection", + "height", + "justify", + "postcommand", + "state", + "style", + "takefocus", + "textvariable", + "values", + "width", +] -class Combobox(Entry): - def __init__(self, master: Optional[Any] = ..., **kw): ... +class Combobox(Widget): # actually inherits from Entry + def __init__( + self, + master: Optional[tkinter.Misc] = ..., + *, + class_: str = ..., + cursor: tkinter._Cursor = ..., + exportselection: bool = ..., + height: int = ..., + justify: Literal["left", "center", "right"] = ..., + postcommand: Union[Callable[[], None], str] = ..., + state: Literal["normal", "readonly", "disabled"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + textvariable: tkinter.Variable = ..., + values: Union[_TkinterSequence[str], _TkinterSequence[float]] = ..., + width: int = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + cursor: tkinter._Cursor = ..., + exportselection: bool = ..., + height: int = ..., + justify: Literal["left", "center", "right"] = ..., + postcommand: Union[Callable[[], None], str] = ..., + state: Literal["normal", "readonly", "disabled"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + textvariable: tkinter.Variable = ..., + values: Union[_TkinterSequence[str], _TkinterSequence[float]] = ..., + width: int = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _ComboboxOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _ComboboxOptionName) -> Any: ... def current(self, newindex: Optional[Any] = ...): ... def set(self, value): ... + bbox = Entry.bbox + identify = Entry.identify + validate = Entry.validate + delete = Entry.delete + get = Entry.get + icursor = Entry.icursor + index = Entry.index + insert = Entry.insert + scan_mark = Entry.scan_mark + scan_dragto = Entry.scan_dragto + selection_adjust = Entry.selection_adjust + select_adjust = Entry.select_adjust + selection_clear = Entry.selection_clear + select_clear = Entry.select_clear + selection_from = Entry.selection_from + select_from = Entry.select_from + selection_present = Entry.selection_present + select_present = Entry.select_present + selection_range = Entry.selection_range + select_range = Entry.select_range + selection_to = Entry.selection_to + select_to = Entry.select_to + +_FrameOptionName = Literal["borderwidth", "class", "cursor", "height", "padding", "relief", "style", "takefocus", "width"] class Frame(Widget): - def __init__(self, master: Optional[Any] = ..., **kw): ... + def __init__( + self, + master: Optional[tkinter.Misc] = ..., + *, + borderwidth: tkinter._ScreenUnits = ..., + class_: str = ..., + cursor: tkinter._Cursor = ..., + height: tkinter._ScreenUnits = ..., + padding: tkinter._Padding = ..., + relief: tkinter._Relief = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + width: tkinter._ScreenUnits = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + borderwidth: tkinter._ScreenUnits = ..., + cursor: tkinter._Cursor = ..., + height: tkinter._ScreenUnits = ..., + padding: tkinter._Padding = ..., + relief: tkinter._Relief = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + width: tkinter._ScreenUnits = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _FrameOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _FrameOptionName) -> Any: ... + +_LabelOptionName = Literal[ + "anchor", + "background", + "class", + "compound", + "cursor", + "font", + "foreground", + "image", + "justify", + "padding", + "relief", + "state", + "style", + "takefocus", + "text", + "textvariable", + "underline", + "width", + "wraplength", +] class Label(Widget): - def __init__(self, master: Optional[Any] = ..., **kw): ... + def __init__( + self, + master: Optional[tkinter.Misc] = ..., + *, + anchor: tkinter._Anchor = ..., + background: tkinter._Color = ..., + class_: str = ..., + compound: _TtkCompound = ..., + cursor: tkinter._Cursor = ..., + font: tkinter._FontDescription = ..., + foreground: tkinter._Color = ..., + image: tkinter._ImageSpec = ..., + justify: Literal["left", "center", "right"] = ..., + padding: tkinter._Padding = ..., + relief: tkinter._Relief = ..., + state: Literal["normal", "disabled"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + text: str = ..., + textvariable: tkinter.Variable = ..., + underline: int = ..., + width: Union[int, Literal[""]] = ..., + wraplength: tkinter._ScreenUnits = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + anchor: tkinter._Anchor = ..., + background: tkinter._Color = ..., + compound: _TtkCompound = ..., + cursor: tkinter._Cursor = ..., + font: tkinter._FontDescription = ..., + foreground: tkinter._Color = ..., + image: tkinter._ImageSpec = ..., + justify: Literal["left", "center", "right"] = ..., + padding: tkinter._Padding = ..., + relief: tkinter._Relief = ..., + state: Literal["normal", "disabled"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + text: str = ..., + textvariable: tkinter.Variable = ..., + underline: int = ..., + width: Union[int, Literal[""]] = ..., + wraplength: tkinter._ScreenUnits = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _LabelOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _LabelOptionName) -> Any: ... + +_LabelframeOptionName = Literal[ + "class", "cursor", "height", "labelanchor", "labelwidget", "padding", "style", "takefocus", "text", "underline", "width", +] class Labelframe(Widget): - def __init__(self, master: Optional[Any] = ..., **kw): ... + def __init__( + self, + master: Optional[tkinter.Misc] = ..., + *, + class_: str = ..., + cursor: tkinter._Cursor = ..., + height: tkinter._ScreenUnits = ..., + labelanchor: Literal["nw", "n", "ne", "en", "e", "es", "se", "s", "sw", "ws", "w", "wn"] = ..., + labelwidget: tkinter.Misc = ..., + padding: tkinter._Padding = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + text: str = ..., + underline: int = ..., + width: tkinter._ScreenUnits = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + cursor: tkinter._Cursor = ..., + height: tkinter._ScreenUnits = ..., + labelanchor: Literal["nw", "n", "ne", "en", "e", "es", "se", "s", "sw", "ws", "w", "wn"] = ..., + labelwidget: tkinter.Misc = ..., + padding: tkinter._Padding = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + text: str = ..., + underline: int = ..., + width: tkinter._ScreenUnits = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _LabelframeOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _LabelframeOptionName) -> Any: ... + +LabelFrame = Labelframe -LabelFrame: Any +_MenubuttonOptionName = Literal[ + "class", + "compound", + "cursor", + "direction", + "image", + "menu", + "state", + "style", + "takefocus", + "text", + "textvariable", + "underline", + "width", +] class Menubutton(Widget): - def __init__(self, master: Optional[Any] = ..., **kw): ... + def __init__( + self, + master: Optional[tkinter.Misc] = ..., + *, + class_: str = ..., + compound: _TtkCompound = ..., + cursor: tkinter._Cursor = ..., + direction: Literal["above", "below", "left", "right", "flush"] = ..., + image: tkinter._ImageSpec = ..., + menu: tkinter.Menu = ..., + state: Literal["normal", "disabled"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + text: str = ..., + textvariable: tkinter.Variable = ..., + underline: int = ..., + width: Union[int, Literal[""]] = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + compound: _TtkCompound = ..., + cursor: tkinter._Cursor = ..., + direction: Literal["above", "below", "left", "right", "flush"] = ..., + image: tkinter._ImageSpec = ..., + menu: tkinter.Menu = ..., + state: Literal["normal", "disabled"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + text: str = ..., + textvariable: tkinter.Variable = ..., + underline: int = ..., + width: Union[int, Literal[""]] = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _MenubuttonOptionName) -> Tuple[str, str, str, Any, Any]: ... + # config is just like configure, but copy/pasta, because assigning + # 'config = configure' creates mypy errors here for some reason + @overload + def config( + self, + cnf: Dict[str, Any] = ..., + *, + compound: _TtkCompound = ..., + cursor: tkinter._Cursor = ..., + direction: Literal["above", "below", "left", "right", "flush"] = ..., + image: tkinter._ImageSpec = ..., + menu: tkinter.Menu = ..., + state: Literal["normal", "disabled"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + text: str = ..., + textvariable: tkinter.Variable = ..., + underline: int = ..., + width: Union[int, Literal[""]] = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def config(self, cnf: _MenubuttonOptionName) -> Tuple[str, str, str, Any, Any]: ... + def cget(self, key: _MenubuttonOptionName) -> Any: ... class Notebook(Widget): - def __init__(self, master: Optional[Any] = ..., **kw): ... + def __init__( + self, + master: Optional[tkinter.Misc] = ..., + *, + class_: str = ..., + cursor: tkinter._Cursor = ..., + height: int = ..., + padding: tkinter._Padding = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + width: int = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + cursor: tkinter._Cursor = ..., + height: int = ..., + padding: tkinter._Padding = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + width: int = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure( + self, cnf: Literal["class", "cursor", "height", "padding", "style", "takefocus", "width"] + ) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: Literal["class", "cursor", "height", "padding", "style", "takefocus", "width"]) -> Any: ... def add(self, child, **kw): ... def forget(self, tab_id): ... def hide(self, tab_id): ... @@ -75,46 +612,438 @@ class Notebook(Widget): def tabs(self): ... def enable_traversal(self): ... -class Panedwindow(Widget, tkinter.PanedWindow): - def __init__(self, master: Optional[Any] = ..., **kw): ... +class Panedwindow(Widget): # actally inherits from tkinter.PanedWindow + def __init__( + self, + master: Optional[tkinter.Misc] = ..., + *, + class_: str = ..., + cursor: tkinter._Cursor = ..., + # width and height for tkinter.ttk.Panedwindow are int but for tkinter.PanedWindow they are screen units + height: int = ..., + orient: Literal["vertical", "horizontal"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + width: int = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + cursor: tkinter._Cursor = ..., + height: int = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + width: int = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure( + self, cnf: Literal["class", "cursor", "height", "orient", "style", "takefocus", "width"] + ) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: Literal["class", "cursor", "height", "orient", "style", "takefocus", "width"]) -> Any: ... forget: Any def insert(self, pos, child, **kw): ... def pane(self, pane, option: Optional[Any] = ..., **kw): ... def sashpos(self, index, newpos: Optional[Any] = ...): ... + add = tkinter.PanedWindow.add + remove = tkinter.PanedWindow.remove + forget = tkinter.PanedWindow.forget + identify = tkinter.PanedWindow.identify + proxy = tkinter.PanedWindow.proxy + proxy_coord = tkinter.PanedWindow.proxy_coord + proxy_forget = tkinter.PanedWindow.proxy_forget + proxy_place = tkinter.PanedWindow.proxy_place + sash = tkinter.PanedWindow.sash + sash_coord = tkinter.PanedWindow.sash_coord + sash_mark = tkinter.PanedWindow.sash_mark + sash_place = tkinter.PanedWindow.sash_place + panecget = tkinter.PanedWindow.panecget + paneconfigure = tkinter.PanedWindow.paneconfigure + paneconfig = tkinter.PanedWindow.paneconfig + panes = tkinter.PanedWindow.panes + +PanedWindow = Panedwindow -PanedWindow: Any +_ProgressbarOptionName = Literal[ + "class", "cursor", "length", "maximum", "mode", "orient", "phase", "style", "takefocus", "value", "variable" +] class Progressbar(Widget): - def __init__(self, master: Optional[Any] = ..., **kw): ... + def __init__( + self, + master: Optional[tkinter.Misc] = ..., + *, + class_: str = ..., + cursor: tkinter._Cursor = ..., + length: tkinter._ScreenUnits = ..., + maximum: float = ..., + mode: Literal["determinate", "indeterminate"] = ..., + orient: Literal["horizontal", "vertical"] = ..., + phase: int = ..., # docs say read-only but assigning int to this works + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + value: float = ..., + variable: tkinter.DoubleVar = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + cursor: tkinter._Cursor = ..., + length: tkinter._ScreenUnits = ..., + maximum: float = ..., + mode: Literal["determinate", "indeterminate"] = ..., + orient: Literal["horizontal", "vertical"] = ..., + phase: int = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + value: float = ..., + variable: tkinter.DoubleVar = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _ProgressbarOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _ProgressbarOptionName) -> Any: ... def start(self, interval: Optional[Any] = ...): ... def step(self, amount: Optional[Any] = ...): ... def stop(self): ... +_RadiobuttonOptionName = Literal[ + "class", + "command", + "compound", + "cursor", + "image", + "state", + "style", + "takefocus", + "text", + "textvariable", + "underline", + "value", + "variable", + "width", +] + class Radiobutton(Widget): - def __init__(self, master: Optional[Any] = ..., **kw): ... + def __init__( + self, + master: Optional[tkinter.Misc] = ..., + *, + class_: str = ..., + command: tkinter._ButtonCommand = ..., + compound: _TtkCompound = ..., + cursor: tkinter._Cursor = ..., + image: tkinter._ImageSpec = ..., + state: Literal["normal", "disabled"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + text: str = ..., + textvariable: tkinter.Variable = ..., + underline: int = ..., + value: Any = ..., + variable: Union[tkinter.Variable, Literal[""]] = ..., + width: Union[int, Literal[""]] = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + command: tkinter._ButtonCommand = ..., + compound: _TtkCompound = ..., + cursor: tkinter._Cursor = ..., + image: tkinter._ImageSpec = ..., + state: Literal["normal", "disabled"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + text: str = ..., + textvariable: tkinter.Variable = ..., + underline: int = ..., + value: Any = ..., + variable: Union[tkinter.Variable, Literal[""]] = ..., + width: Union[int, Literal[""]] = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _RadiobuttonOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _RadiobuttonOptionName) -> Any: ... def invoke(self): ... -class Scale(Widget, tkinter.Scale): - def __init__(self, master: Optional[Any] = ..., **kw): ... - def configure(self, cnf: Optional[Any] = ..., **kw): ... +_ScaleOptionName = Literal[ + "class", "command", "cursor", "from", "length", "orient", "style", "takefocus", "to", "value", "variable" +] + +class Scale(Widget): # actually inherits from tkinter.Scale + def __init__( + self, + master: Optional[tkinter.Misc] = ..., + *, + class_: str = ..., + command: Union[str, Callable[[str], None]] = ..., + cursor: tkinter._Cursor = ..., + from_: float = ..., + length: tkinter._ScreenUnits = ..., + orient: Literal["horizontal", "vertical"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + to: float = ..., + value: float = ..., + variable: tkinter.DoubleVar = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + command: Union[str, Callable[[str], None]] = ..., + cursor: tkinter._Cursor = ..., + from_: float = ..., + length: tkinter._ScreenUnits = ..., + orient: Literal["horizontal", "vertical"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + to: float = ..., + value: float = ..., + variable: tkinter.DoubleVar = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _ScaleOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _ScaleOptionName) -> Any: ... def get(self, x: Optional[Any] = ..., y: Optional[Any] = ...): ... + set = tkinter.Scale.set + coords = tkinter.Scale.coords + identify = tkinter.Scale.identify -class Scrollbar(Widget, tkinter.Scrollbar): - def __init__(self, master: Optional[Any] = ..., **kw): ... +class Scrollbar(Widget): # actually inherits from tkinter.Scrollbar + def __init__( + self, + master: Optional[tkinter.Misc] = ..., + *, + class_: str = ..., + command: Union[Callable[..., Optional[Tuple[float, float]]], str] = ..., + cursor: tkinter._Cursor = ..., + orient: Literal["horizontal", "vertical"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + command: Union[Callable[..., Optional[Tuple[float, float]]], str] = ..., + cursor: tkinter._Cursor = ..., + orient: Literal["horizontal", "vertical"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure( + self, cnf: Literal["class", "command", "cursor", "orient", "style", "takefocus"] + ) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: Literal["class", "command", "cursor", "orient", "style", "takefocus"]) -> Any: ... + activate = tkinter.Scrollbar.activate + delta = tkinter.Scrollbar.delta + fraction = tkinter.Scrollbar.fraction + identify = tkinter.Scrollbar.identify + get = tkinter.Scrollbar.get + set = tkinter.Scrollbar.set class Separator(Widget): - def __init__(self, master: Optional[Any] = ..., **kw): ... + def __init__( + self, + master: Optional[tkinter.Misc] = ..., + *, + class_: str = ..., + cursor: tkinter._Cursor = ..., + orient: Literal["horizontal", "vertical"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + cursor: tkinter._Cursor = ..., + orient: Literal["horizontal", "vertical"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: Literal["class", "cursor", "orient", "style", "takefocus"]) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: Literal["class", "cursor", "orient", "style", "takefocus"]) -> Any: ... class Sizegrip(Widget): - def __init__(self, master: Optional[Any] = ..., **kw): ... + def __init__( + self, + master: Optional[tkinter.Misc] = ..., + *, + class_: str = ..., + cursor: tkinter._Cursor = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + cursor: tkinter._Cursor = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: Literal["class", "cursor", "style", "takefocus"]) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: Literal["class", "cursor", "style", "takefocus"]) -> Any: ... if sys.version_info >= (3, 7): - class Spinbox(Entry): - def __init__(self, master: Any = ..., **kw: Any) -> None: ... + _SpinboxOptionName = Literal[ + "class", + "command", + "cursor", + "format", + "from", + "increment", + "state", + "style", + "takefocus", + "to", + "values", + "wrap", + "xscrollcommand", + ] + class Spinbox(Widget): # actually inherits from Entry + def __init__( + self, + master: Optional[tkinter.Misc] = ..., + *, + class_: str = ..., + command: Union[Callable[[], None], str, tkinter._TkinterSequence[str]] = ..., + cursor: tkinter._Cursor = ..., + format: str = ..., + from_: float = ..., + increment: float = ..., + state: Literal["normal", "disabled"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + to: float = ..., + values: Union[_TkinterSequence[str], _TkinterSequence[float]] = ..., + wrap: bool = ..., + xscrollcommand: tkinter._XYScrollCommand = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + command: Union[Callable[[], None], str, tkinter._TkinterSequence[str]] = ..., + cursor: tkinter._Cursor = ..., + format: str = ..., + from_: float = ..., + increment: float = ..., + state: Literal["normal", "disabled"] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + to: float = ..., + values: Union[_TkinterSequence[str], _TkinterSequence[float]] = ..., + wrap: bool = ..., + xscrollcommand: tkinter._XYScrollCommand = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _SpinboxOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _SpinboxOptionName) -> Any: ... def set(self, value: Any) -> None: ... + bbox = Entry.bbox + identify = Entry.identify + validate = Entry.validate + delete = Entry.delete + get = Entry.get + icursor = Entry.icursor + index = Entry.index + insert = Entry.insert + scan_mark = Entry.scan_mark + scan_dragto = Entry.scan_dragto + selection_adjust = Entry.selection_adjust + select_adjust = Entry.select_adjust + selection_clear = Entry.selection_clear + select_clear = Entry.select_clear + selection_from = Entry.selection_from + select_from = Entry.select_from + selection_present = Entry.selection_present + select_present = Entry.select_present + selection_range = Entry.selection_range + select_range = Entry.select_range + selection_to = Entry.selection_to + select_to = Entry.select_to + +_TreeviewOptionName = Literal[ + "class", + "columns", + "cursor", + "displaycolumns", + "height", + "padding", + "selectmode", + "show", + "style", + "takefocus", + "xscrollcommand", + "yscrollcommand", +] class Treeview(Widget, tkinter.XView, tkinter.YView): - def __init__(self, master: Optional[Any] = ..., **kw): ... + def __init__( + self, + master: Optional[tkinter.Misc] = ..., + *, + class_: str = ..., + columns: _TkinterSequence[str] = ..., + cursor: tkinter._Cursor = ..., + displaycolumns: Union[_TkinterSequence[str], _TkinterSequence[int], Literal["#all"]] = ..., + height: int = ..., + padding: tkinter._Padding = ..., + selectmode: Literal["extended", "browse", "none"] = ..., + # _TkinterSequences of Literal don't actually work, using str instead. + # + # 'tree headings' is same as ['tree', 'headings'], and I wouldn't be + # surprised if someone was using it. + show: Union[Literal["tree", "headings", "tree headings"], _TkinterSequence[str]] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + xscrollcommand: tkinter._XYScrollCommand = ..., + yscrollcommand: tkinter._XYScrollCommand = ..., + ) -> None: ... + @overload + def configure( + self, + cnf: Dict[str, Any] = ..., + *, + columns: _TkinterSequence[str] = ..., + cursor: tkinter._Cursor = ..., + displaycolumns: Union[_TkinterSequence[str], _TkinterSequence[int], Literal["#all"]] = ..., + height: int = ..., + padding: tkinter._Padding = ..., + selectmode: Literal["extended", "browse", "none"] = ..., + show: Union[Literal["tree", "headings", "tree headings"], _TkinterSequence[str]] = ..., + style: str = ..., + takefocus: tkinter._TakeFocusValue = ..., + xscrollcommand: tkinter._XYScrollCommand = ..., + yscrollcommand: tkinter._XYScrollCommand = ..., + ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... + @overload + def configure(self, cnf: _TreeviewOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure + def cget(self, key: _TreeviewOptionName) -> Any: ... def bbox(self, item, column: Optional[Any] = ...): ... def get_children(self, item: Optional[Any] = ...): ... def set_children(self, item, *newchildren): ... @@ -166,12 +1095,32 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): class LabeledScale(Frame): label: Any scale: Any - def __init__(self, master: Optional[Any] = ..., variable: Optional[Any] = ..., from_: int = ..., to: int = ..., **kw): ... - def destroy(self): ... + # TODO: don't any-type **kw. That goes to Frame.__init__. + def __init__( + self, + master: Optional[tkinter.Misc] = ..., + variable: Optional[Union[tkinter.IntVar, tkinter.DoubleVar]] = ..., + from_: float = ..., + to: float = ..., + *, + compound: Union[Literal["top"], Literal["bottom"]] = ..., + **kw: Any, + ) -> None: ... + # destroy is overrided, signature does not change value: Any class OptionMenu(Menubutton): - def __init__(self, master, variable, default: Optional[Any] = ..., *values, **kwargs): ... - def __getitem__(self, item): ... + def __init__( + self, + master, + variable, + default: Optional[str] = ..., + *values: str, + # rest of these are keyword-only because *args syntax used above + style: str = ..., + direction: Union[Literal["above"], Literal["below"], Literal["left"], Literal["right"], Literal["flush"]] = ..., + command: Optional[Callable[[tkinter.StringVar], None]] = ..., + ) -> None: ... + # configure, config, cget, destroy are inherited from Menubutton + # destroy and __setitem__ are overrided, signature does not change def set_menu(self, default: Optional[Any] = ..., *values): ... - def destroy(self): ... diff --git a/tests/pytype_test.py b/tests/pytype_test.py index 79a9176b61af..e81c341959a2 100755 --- a/tests/pytype_test.py +++ b/tests/pytype_test.py @@ -40,12 +40,19 @@ def main() -> None: python36_exe=args.python36_exe, print_stderr=args.print_stderr, dry_run=args.dry_run, + verbose=args.verbose, ) def create_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description="Pytype/typeshed tests.") parser.add_argument("-n", "--dry-run", action="store_true", default=False, help="Don't actually run tests") + parser.add_argument( + "--verbose", + action="store_true", + default=False, + help="Print all filenames being tested (helps to find code that causes pytype to hang)", + ) # Default to '' so that symlinking typeshed subdirs in cwd will work. parser.add_argument("--typeshed-location", type=str, default="", help="Path to typeshed installation.") # Set to true to print a stack trace every time an exception is thrown. @@ -199,13 +206,17 @@ def run_all_tests( python27_exe: str, python36_exe: str, print_stderr: bool, - dry_run: bool + dry_run: bool, + verbose: bool, ) -> None: bad = [] errors = 0 total_tests = len(files_to_test) print("Testing files with pytype...") for i, (f, version) in enumerate(files_to_test): + if verbose: + print(f"{f} with Python {version}") + stderr = ( run_pytype( filename=f, @@ -224,7 +235,7 @@ def run_all_tests( bad.append((_get_relative(f), stacktrace_final_line)) runs = i + 1 - if runs % 25 == 0: + if runs % 25 == 0 and not verbose: print(" {:3d}/{:d} with {:3d} errors".format(runs, total_tests, errors)) print("Ran pytype with {:d} pyis, got {:d} errors.".format(total_tests, errors)) diff --git a/tests/tkinter_options_test.py b/tests/tkinter_options_test.py new file mode 100755 index 000000000000..0246c7b99517 --- /dev/null +++ b/tests/tkinter_options_test.py @@ -0,0 +1,368 @@ +#!/usr/bin/env python3 +""" +This script checks that the widget options specified in tkinter stubs are +actually supported by the Tk widgets at runtime. Unfortunately stubtest isn't +sufficient for checking this because the python code uses **kwargs for the +options. This script creates values that those options can be set to according +to the type hints and then tries to set the options. For example, if the stubs +say that an option should be bool, then this script sets it to True and then to +False, and fails if an exception is raised. + +In particular, this script does NOT check whether all options supported by the +widgets are also supported by the stubs. This way CI builds don't break when a +new release of Tcl/Tk with new widget options comes out. There's a big comment +in tkinter/__init__.pyi describing how to add new options to the stubs. + +It's best to run this file only in the latest supported Python version +(corresponding to latest Tcl/Tk version): + - The latest Tcl/Tk includes all widget options of the previous Tcl/Tk + releases. There's currently no way to say that a widget option is available + only starting at Tk version X.Y, and tkinter generally doesn't make a big + effort to document those things either. The CI build fails if a widget + option is missing from the Tcl/Tk that tkinter is using. + - It's nice to use new Python features in this file. + +This script parses a subset of Python's type hint syntax with ast. Only +configure() methods defined in tkinter's widget classes are checked. +""" + +import ast +import builtins +import itertools +import pathlib +import sys +import tkinter +import tkinter.font +import tkinter.ttk +from typing import Dict, Iterable, List, Sequence, Tuple, Union, cast + + +def create_child_widgets(parent: tkinter.Misc) -> Dict[str, tkinter.Misc]: + img = tkinter.image_names()[0] + + # image can't be given to __init__, must config afterwards + nonttk_optionmenu = tkinter.OptionMenu(parent, tkinter.StringVar(), "foo") + ttk_optionmenu = tkinter.ttk.OptionMenu(parent, tkinter.StringVar(), "foo") + nonttk_optionmenu.config(image=img) + ttk_optionmenu.config(image=img) + + return { + # width and height can be arbitrary _ScreenUnits only if these widgets have an image + "tkinter.Button": tkinter.Button(image=img), + "tkinter.Checkbutton": tkinter.Checkbutton(image=img), + "tkinter.Label": tkinter.Label(image=img), + "tkinter.Menubutton": tkinter.Menubutton(image=img), + "tkinter.OptionMenu": nonttk_optionmenu, + "tkinter.Radiobutton": tkinter.Radiobutton(image=img), + "tkinter.ttk.Button": tkinter.ttk.Button(image=img), + "tkinter.ttk.Checkbutton": tkinter.ttk.Checkbutton(image=img), + "tkinter.ttk.Label": tkinter.ttk.Label(image=img), + "tkinter.ttk.Menubutton": tkinter.ttk.Menubutton(image=img), + "tkinter.ttk.OptionMenu": ttk_optionmenu, + "tkinter.ttk.Radiobutton": tkinter.ttk.Radiobutton(image=img), + # these don't need image + "tkinter.Canvas": tkinter.Canvas(), + "tkinter.Entry": tkinter.Entry(), + "tkinter.Frame": tkinter.Frame(), + "tkinter.LabelFrame": tkinter.LabelFrame(), + "tkinter.Listbox": tkinter.Listbox(), + "tkinter.Menu": tkinter.Menu(), + "tkinter.Message": tkinter.Message(), + "tkinter.PanedWindow": tkinter.PanedWindow(), + "tkinter.Scale": tkinter.Scale(), + "tkinter.Scrollbar": tkinter.Scrollbar(), + "tkinter.Spinbox": tkinter.Spinbox(), + "tkinter.Text": tkinter.Text(), + "tkinter.ttk.Combobox": tkinter.ttk.Combobox(), + "tkinter.ttk.Entry": tkinter.ttk.Entry(), + "tkinter.ttk.Frame": tkinter.ttk.Frame(), + "tkinter.ttk.Labelframe": tkinter.ttk.Labelframe(), + "tkinter.ttk.Notebook": tkinter.ttk.Notebook(), + "tkinter.ttk.Panedwindow": tkinter.ttk.Panedwindow(), + "tkinter.ttk.Progressbar": tkinter.ttk.Progressbar(), + "tkinter.ttk.Scale": tkinter.ttk.Scale(), + "tkinter.ttk.Scrollbar": tkinter.ttk.Scrollbar(), + "tkinter.ttk.Separator": tkinter.ttk.Separator(), + "tkinter.ttk.Sizegrip": tkinter.ttk.Sizegrip(), + "tkinter.ttk.Spinbox": tkinter.ttk.Spinbox(), + "tkinter.ttk.Treeview": tkinter.ttk.Treeview(), + } + + +# useful for detecting Tuple[foo, ...] +def is_dotdotdot(node: ast.expr) -> bool: + return isinstance(node, ast.Constant) and node.value is Ellipsis + + +# attribute_string() --> 'tkinter.Label' +def attribute_string(typehint: ast.expr) -> str: + if isinstance(typehint, ast.Name): + return typehint.id + if isinstance(typehint, ast.Attribute): + return attribute_string(typehint.value) + "." + typehint.attr + raise NotImplementedError(typehint) + + +# Convert type hint into values that should be possible to set to a widget that +# uses the type hint. Examples: +# Literal['foo', 'bar'] --> ['foo', 'bar'] +# bool --> [True, False] +# float --> [123, 456.789] +class ValueGenerator: + def __init__( + self, import_mapping: Dict[str, str], widget: tkinter.Misc, modulename: str, images: List[tkinter.Image] + ) -> None: + self.import_mapping = import_mapping + self.widget = widget + self.modulename = modulename + self.images = images + + def _full_name(self, name: str) -> str: + first_part, possible_dot, possible_suffix = name.partition(".") + try: + # tkinter.font --> self.import_mapping['tkinter'] + '.font' + return self.import_mapping[first_part] + possible_dot + possible_suffix + except KeyError: + return self.modulename + "." + name + + def _get_list_values(self, item_type: ast.expr) -> Sequence[List[object]]: + list_value = list(self.get(item_type)) + return [[], list_value, list_value[::-1], list_value * 2] + + def get(self, typehint: ast.expr) -> Sequence[object]: + if isinstance(typehint, (ast.Name, ast.Attribute)): + full_name = self._full_name(attribute_string(typehint)) + if full_name in {"builtins.str", "typing.Any"}: + # Unfortunately str doesn't always mean that *any* string is + # acceptable. Not much can be done here. + return [] + if full_name == "builtins.bool": + return [True, False] + if full_name == "builtins.int": + return [123] + if full_name == "builtins.float": + return [123, 456.789] + if full_name == "tkinter._Anchor": + return ["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] + if full_name == "tkinter._Bitmap": + return ["hourglass"] + if full_name == "tkinter._Color": + return ["red", "#f00", "#ff0000"] + compound_list = ["top", "left", "center", "right", "bottom", "none"] + if full_name == "tkinter._Compound": + return compound_list + if full_name == "tkinter._Cursor": + return ["hand2", ("hand2", "red"), ("hand2", "red", "blue")] + if full_name == "tkinter._ImageSpec": + return cast(List[object], self.images) + list(tkinter.image_names()) + if full_name == "tkinter._Padding": + return ["2c", ("2c",), ("2c", "3c"), ("2c", "3c", "4c"), ("2c", "3c", "4c", "5c")] + if full_name == "tkinter._Relief": + return ["flat", "raised", "sunken", "groove", "solid", "ridge"] + if full_name == "tkinter._ScreenUnits": + return [12, 34.56, "78.9c"] + if full_name == "tkinter._TakeFocusValue": + return [True, False, "", print] + if full_name == "tkinter.ttk._TtkCompound": + return compound_list + ["text", "image"] + if full_name == "tkinter._FontDescription": + return [("Helvetica", 12, "bold"), tkinter.font.Font()] + if full_name == "tkinter.Variable": + return [tkinter.Variable(), tkinter.StringVar(), tkinter.IntVar(), tkinter.DoubleVar()] + if full_name == "tkinter.DoubleVar": + return [tkinter.DoubleVar()] + if full_name in {"tkinter._XYScrollCommand", "tkinter._EntryValidateCommand", "tkinter._ButtonCommand"}: + # Tkinter converts all callables to tcl commands the same way. + return [print] + + # For widgets, return a child widget of the given widget. Some + # options require this and others don't. + child_widgets = create_child_widgets(self.widget) + if full_name in child_widgets: + return [child_widgets[full_name]] + if full_name == "tkinter.Misc": + # Any widget that can be put inside another widget, except Menu + # which causes issues for whatever reason. + return [widget for widget in child_widgets.values() if not isinstance(widget, tkinter.Menu)] + + if isinstance(typehint, ast.Subscript): # typehint.value[typehint.slice] + assert isinstance(typehint.value, (ast.Name, ast.Attribute)) + assert isinstance(typehint.slice, ast.Index) + full_name = self._full_name(attribute_string(typehint.value)) + + if full_name == "typing_extensions.Literal": + if isinstance(typehint.slice.value, ast.Tuple): + tuple_content = typehint.slice.value.elts + else: + tuple_content = [typehint.slice.value] + assert all(isinstance(node, ast.Str) for node in tuple_content) + return [cast(ast.Str, node).s for node in tuple_content] + + if full_name == "typing.Callable": + return [print] + + if full_name == "typing.Union": + assert isinstance(typehint.slice.value, ast.Tuple) + result: List[object] = [] + for unioned_item in typehint.slice.value.elts: + result.extend(self.get(unioned_item)) + return result + + if full_name == "typing.Tuple": + if isinstance(typehint.slice.value, ast.Tuple): + if len(typehint.slice.value.elts) == 2 and is_dotdotdot(typehint.slice.value.elts[1]): + # Tuple[foo, ...] is similar to List[foo] + list_values = self._get_list_values(typehint.slice.value.elts[0]) + return [tuple(lizt) for lizt in list_values] + values_for_each_element = [self.get(element) for element in typehint.slice.value.elts] + else: + values_for_each_element = [self.get(typehint.slice.value)] + return list(itertools.product(*values_for_each_element)) + + if full_name in {"tkinter._TkinterSequence", "tkinter.ttk._TkinterSequence"}: + TkinterSequence = Union[List[object], Tuple[object, ...]] + lists: List[TkinterSequence] = list(self._get_list_values(typehint.slice.value)) + tuples: List[TkinterSequence] = [tuple(lizt) for lizt in lists] + return lists + tuples + + # If this error gets raised with an error message mentioning Ellipsis, + # then make sure that you're running Python 3.8 or newer. Seems like + # something changed in the ast. + raise NotImplementedError(typehint, ast.dump(typehint)) + + +# from enum import Enum --> {'Enum': 'enum.Enum'} +def get_import_mapping(parsed: ast.Module) -> Dict[str, str]: + result = {} + + # similar to: from builtins import * + for name in dir(builtins): + if not name.startswith("_"): + result[name] = "builtins." + name + + for stmt in parsed.body: + if isinstance(stmt, ast.ImportFrom): + for name_node in stmt.names: + assert stmt.module is not None + no_dots = name_node.name + result[no_dots] = stmt.module + "." + no_dots + elif isinstance(stmt, ast.Import): + for name_node in stmt.names: + assert name_node.asname is None + # import tkinter.font --> result['tkinter'] = 'tkinter' + first_part, *junk = name_node.name.split(".") + result[first_part] = first_part + + return result + + +def find_all_classdefs(parsed: ast.Module) -> Iterable[ast.ClassDef]: + for stmt in parsed.body: + if isinstance(stmt, ast.ClassDef): + yield stmt + elif isinstance(stmt, ast.If): + # handle classes e.g. under 'if sys.version_info >= bla' + for inner in stmt.body: + if isinstance(inner, ast.ClassDef): + yield inner + + +def is_configure_method_with_options_as_kwargs(node: ast.stmt) -> bool: + if (not isinstance(node, ast.FunctionDef)) or node.name != "configure": + return False + + self, cnf = node.args.args # positional args + assert cnf.arg == "cnf" + assert cnf.annotation is not None + + [expected_cnf_annotation] = ast.parse("Dict[str, Any]").body + assert isinstance(expected_cnf_annotation, ast.Expr) + return ast.dump(cnf.annotation) == ast.dump(expected_cnf_annotation.value) + + +def find_configure_method(classname: str, classdefs: Dict[str, ast.ClassDef]) -> ast.FunctionDef: + if classname in "tkinter.OptionMenu": + # OptionMenus get their configure methods with inheritance + classdef = classdefs["tkinter.Menubutton"] # inherited from here + elif classname in "tkinter.ttk.OptionMenu": + classdef = classdefs["tkinter.ttk.Menubutton"] + elif classname == "tkinter.Toplevel": + # Toplevel does 'configure = Tk.configure' + classdef = classdefs["tkinter.Tk"] + else: + classdef = classdefs[classname] + + [configure] = filter(is_configure_method_with_options_as_kwargs, classdef.body) + assert isinstance(configure, ast.FunctionDef) + return configure + + +def get_tkinter_dir() -> pathlib.Path: + tests_dir = pathlib.Path(__file__).parent + project_dir = tests_dir.parent + return project_dir / "stdlib" / "3" / "tkinter" + + +def main() -> None: + # tkinter.Tk must be created first, other things use that implicitly + root = tkinter.Tk() + + print("Python version:", repr(sys.version)) + print("Tcl version:", root.tk.eval("info patchlevel")) + + test_widgets: Dict[str, tkinter.Misc] = { + "tkinter.Tk": root, + "tkinter.Toplevel": tkinter.Toplevel(), + **create_child_widgets(root), + } + + # This must be somewhat long-living because tkinter uses __del__ hack. + test_images = [tkinter.BitmapImage(), tkinter.PhotoImage()] + + module_list = [ + ("tkinter", get_tkinter_dir() / "__init__.pyi"), + ("tkinter.ttk", get_tkinter_dir() / "ttk.pyi"), + ] + + import_mappings: Dict[str, Dict[str, str]] = {} + classdefs: Dict[str, ast.ClassDef] = {} + + for modulename, path in module_list: + print("Parsing", path) + parsed = ast.parse(path.read_text()) + import_mappings[modulename] = get_import_mapping(parsed) + classdefs.update({modulename + "." + classdef.name: classdef for classdef in find_all_classdefs(parsed)}) + + for classname, widget in test_widgets.items(): + print("Testing", classname) + modulename = classname.rsplit(".", 1)[0] + value_getter = ValueGenerator(import_mappings[modulename], widget, modulename, test_images) + configure = find_configure_method(classname, classdefs) + + for arg in configure.args.kwonlyargs: + option_name = arg.arg.rstrip("_") # class_=foo --> widget['class'] = foo + + assert arg.annotation is not None + values = value_getter.get(arg.annotation) + if values: + for value in values: + print(f"widget[{option_name!r}] = {value!r}") + try: + widget[option_name] = value + except tkinter.TclError as e: + # these errors indicate that types were correct, + # but the values were wrong + assert str(e) in {"-to value must be greater than -from value", "Column index 123 out of bounds"} + else: + # Can't test option by assigning values to it according to + # type hint, but this ensures that it exists and isn't + # __init__ only. + print(f"widget[{option_name!r}] gets assigned to itself") + widget[option_name] = widget[option_name] + + print() + print("ok") + + +if __name__ == "__main__": + main() From 51dc8ecb4f4421f51663375fd36f7322d72b12f1 Mon Sep 17 00:00:00 2001 From: Akuli Date: Sat, 25 Jul 2020 16:40:53 +0300 Subject: [PATCH 02/45] clean up imports --- stdlib/3/tkinter/__init__.pyi | 4 +--- stdlib/3/tkinter/ttk.pyi | 6 +----- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/stdlib/3/tkinter/__init__.pyi b/stdlib/3/tkinter/__init__.pyi index b674c7424b1d..0b9b547ca0c4 100644 --- a/stdlib/3/tkinter/__init__.pyi +++ b/stdlib/3/tkinter/__init__.pyi @@ -3,9 +3,7 @@ from enum import Enum from tkinter import font from tkinter.constants import * # comment this out to find undefined identifier names with flake8 from types import TracebackType -from typing import Any, Callable, Dict, Generic, Optional, Tuple, Type, TypeVar, Union, overload -from typing_extensions import Literal -from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Type, TypeVar, Union, overload +from typing import Any, Callable, Dict, Generic, List, Optional, Sequence, Tuple, Type, TypeVar, Union, overload from typing_extensions import Literal, TypedDict TclError: Any diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index 677a08330eaa..ab8117fe4636 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -1,15 +1,11 @@ import sys import tkinter -from tkinter import Event -from typing import Any, Callable, Dict, List, Optional, Tuple, Type, TypeVar, Union, overload -from typing_extensions import Literal from enum import Enum +from tkinter import Event from types import TracebackType from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Type, TypeVar, Union, overload from typing_extensions import Literal -import _tkinter - # copy/pasta from __init__.py to make pytype happy # https://github.com/google/pytype/issues/626 _T = TypeVar("_T") From 7bb805da44d86fe4cbe692ee647589ece0163fc8 Mon Sep 17 00:00:00 2001 From: Akuli Date: Sat, 25 Jul 2020 17:11:17 +0300 Subject: [PATCH 03/45] fix pytype error --- stdlib/3/tkinter/ttk.pyi | 1 - 1 file changed, 1 deletion(-) diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index ab8117fe4636..1dd1f1895864 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -639,7 +639,6 @@ class Panedwindow(Widget): # actally inherits from tkinter.PanedWindow ) -> Tuple[str, str, str, Any, Any]: ... config = configure def cget(self, key: Literal["class", "cursor", "height", "orient", "style", "takefocus", "width"]) -> Any: ... - forget: Any def insert(self, pos, child, **kw): ... def pane(self, pane, option: Optional[Any] = ..., **kw): ... def sashpos(self, index, newpos: Optional[Any] = ...): ... From 9389fa7ce888338b55c9ba1be7c2befce95d38cd Mon Sep 17 00:00:00 2001 From: Akuli Date: Sat, 25 Jul 2020 17:36:33 +0300 Subject: [PATCH 04/45] clean up imports more --- stdlib/3/tkinter/__init__.pyi | 4 ++-- stdlib/3/tkinter/ttk.pyi | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/stdlib/3/tkinter/__init__.pyi b/stdlib/3/tkinter/__init__.pyi index 0b9b547ca0c4..b9abb165d547 100644 --- a/stdlib/3/tkinter/__init__.pyi +++ b/stdlib/3/tkinter/__init__.pyi @@ -3,8 +3,8 @@ from enum import Enum from tkinter import font from tkinter.constants import * # comment this out to find undefined identifier names with flake8 from types import TracebackType -from typing import Any, Callable, Dict, Generic, List, Optional, Sequence, Tuple, Type, TypeVar, Union, overload -from typing_extensions import Literal, TypedDict +from typing import Any, Callable, Dict, Generic, List, Optional, Tuple, Type, TypeVar, Union, overload +from typing_extensions import Literal TclError: Any wantobjects: Any diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index 1dd1f1895864..12d9a5f79bb4 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -1,9 +1,7 @@ import sys import tkinter -from enum import Enum from tkinter import Event -from types import TracebackType -from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Type, TypeVar, Union, overload +from typing import Any, Callable, Dict, List, Optional, Tuple, TypeVar, Union, overload from typing_extensions import Literal # copy/pasta from __init__.py to make pytype happy From 55508216202a7efbb5a0044ed4a33e4385864131 Mon Sep 17 00:00:00 2001 From: Akuli Date: Sat, 25 Jul 2020 17:36:56 +0300 Subject: [PATCH 05/45] add ignore comments --- tests/tkinter_options_test.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/tkinter_options_test.py b/tests/tkinter_options_test.py index 0246c7b99517..e9ca8c318f42 100755 --- a/tests/tkinter_options_test.py +++ b/tests/tkinter_options_test.py @@ -38,7 +38,8 @@ def create_child_widgets(parent: tkinter.Misc) -> Dict[str, tkinter.Misc]: - img = tkinter.image_names()[0] + # TODO: remove ignore comment when image_names() has type hints + img: str = tkinter.image_names()[0] # type: ignore # image can't be given to __init__, must config afterwards nonttk_optionmenu = tkinter.OptionMenu(parent, tkinter.StringVar(), "foo") @@ -154,7 +155,9 @@ def get(self, typehint: ast.expr) -> Sequence[object]: if full_name == "tkinter._Cursor": return ["hand2", ("hand2", "red"), ("hand2", "red", "blue")] if full_name == "tkinter._ImageSpec": - return cast(List[object], self.images) + list(tkinter.image_names()) + # TODO: remove ignore comment when image_names() has type hints + image_names: Tuple[str] = tkinter.image_names() # type: ignore + return cast(List[object], self.images) + list(image_names) if full_name == "tkinter._Padding": return ["2c", ("2c",), ("2c", "3c"), ("2c", "3c", "4c"), ("2c", "3c", "4c", "5c")] if full_name == "tkinter._Relief": From 729da887f8e1ac0f1e94b17e910c262ec772aaee Mon Sep 17 00:00:00 2001 From: Akuli Date: Sat, 25 Jul 2020 18:07:19 +0300 Subject: [PATCH 06/45] make cnf arguments Optional --- stdlib/3/tkinter/__init__.pyi | 74 +++++++++++++++++------------------ stdlib/3/tkinter/ttk.pyi | 38 +++++++++--------- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/stdlib/3/tkinter/__init__.pyi b/stdlib/3/tkinter/__init__.pyi index b9abb165d547..6f46ad69d504 100644 --- a/stdlib/3/tkinter/__init__.pyi +++ b/stdlib/3/tkinter/__init__.pyi @@ -659,7 +659,7 @@ class Tk(Misc, Wm): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, background: _Color = ..., bd: _ScreenUnits = ..., @@ -750,7 +750,7 @@ class Toplevel(BaseWidget, Wm): def __init__( self, master: Optional[Misc] = ..., - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, background: _Color = ..., bd: _ScreenUnits = ..., @@ -821,7 +821,7 @@ class Button(Widget): def __init__( self, master: Optional[Misc] = ..., - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, activebackground: _Color = ..., activeforeground: _Color = ..., @@ -867,7 +867,7 @@ class Button(Widget): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, activebackground: _Color = ..., activeforeground: _Color = ..., @@ -947,7 +947,7 @@ class Canvas(Widget, XView, YView): def __init__( self, master: Optional[Misc] = ..., - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, background: _Color = ..., bd: _ScreenUnits = ..., @@ -986,7 +986,7 @@ class Canvas(Widget, XView, YView): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, background: _Color = ..., bd: _ScreenUnits = ..., @@ -1140,7 +1140,7 @@ class Checkbutton(Widget): def __init__( self, master: Optional[Misc] = ..., - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, activebackground: _Color = ..., activeforeground: _Color = ..., @@ -1197,7 +1197,7 @@ class Checkbutton(Widget): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, activebackground: _Color = ..., activeforeground: _Color = ..., @@ -1292,7 +1292,7 @@ class Entry(Widget, XView): def __init__( self, master: Optional[Misc] = ..., - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, background: _Color = ..., bd: _ScreenUnits = ..., @@ -1332,7 +1332,7 @@ class Entry(Widget, XView): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, background: _Color = ..., bd: _ScreenUnits = ..., @@ -1418,7 +1418,7 @@ class Frame(Widget): def __init__( self, master: Optional[Misc] = ..., - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, background: _Color = ..., bd: _ScreenUnits = ..., @@ -1442,7 +1442,7 @@ class Frame(Widget): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, background: _Color = ..., bd: _ScreenUnits = ..., @@ -1501,7 +1501,7 @@ class Label(Widget): def __init__( self, master: Optional[Misc] = ..., - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, activebackground: _Color = ..., activeforeground: _Color = ..., @@ -1537,7 +1537,7 @@ class Label(Widget): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, activebackground: _Color = ..., activeforeground: _Color = ..., @@ -1610,7 +1610,7 @@ class Listbox(Widget, XView, YView): def __init__( self, master: Optional[Misc] = ..., - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, activestyle: Literal["dotbox", "none", "underline"] = ..., background: _Color = ..., @@ -1658,7 +1658,7 @@ class Listbox(Widget, XView, YView): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, activestyle: Literal["dotbox", "none", "underline"] = ..., background: _Color = ..., @@ -1744,7 +1744,7 @@ class Menu(Widget): def __init__( self, master: Optional[Misc] = ..., - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, activebackground: _Color = ..., activeborderwidth: _ScreenUnits = ..., @@ -1773,7 +1773,7 @@ class Menu(Widget): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, activebackground: _Color = ..., activeborderwidth: _ScreenUnits = ..., @@ -1868,7 +1868,7 @@ class Menubutton(Widget): def __init__( self, master: Optional[Misc] = ..., - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, activebackground: _Color = ..., activeforeground: _Color = ..., @@ -1907,7 +1907,7 @@ class Menubutton(Widget): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, activebackground: _Color = ..., activeforeground: _Color = ..., @@ -1950,7 +1950,7 @@ class Menubutton(Widget): @overload def config( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, activebackground: _Color = ..., activeforeground: _Color = ..., @@ -2018,7 +2018,7 @@ class Message(Widget): def __init__( self, master: Optional[Misc] = ..., - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, anchor: _Anchor = ..., aspect: int = ..., @@ -2046,7 +2046,7 @@ class Message(Widget): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, anchor: _Anchor = ..., aspect: int = ..., @@ -2122,7 +2122,7 @@ class Radiobutton(Widget): def __init__( self, master: Optional[Misc] = ..., - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, activebackground: _Color = ..., activeforeground: _Color = ..., @@ -2168,7 +2168,7 @@ class Radiobutton(Widget): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, activebackground: _Color = ..., activeforeground: _Color = ..., @@ -2260,7 +2260,7 @@ class Scale(Widget): def __init__( self, master: Optional[Misc] = ..., - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, activebackground: _Color = ..., background: _Color = ..., @@ -2300,7 +2300,7 @@ class Scale(Widget): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, activebackground: _Color = ..., background: _Color = ..., @@ -2372,7 +2372,7 @@ class Scrollbar(Widget): def __init__( self, master: Optional[Misc] = ..., - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, activebackground: _Color = ..., activerelief: _Relief = ..., @@ -2402,7 +2402,7 @@ class Scrollbar(Widget): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, activebackground: _Color = ..., activerelief: _Relief = ..., @@ -2487,7 +2487,7 @@ class Text(Widget, XView, YView): def __init__( self, master: Optional[Misc] = ..., - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, autoseparators: bool = ..., background: _Color = ..., @@ -2541,7 +2541,7 @@ class Text(Widget, XView, YView): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, autoseparators: bool = ..., background: _Color = ..., @@ -2789,7 +2789,7 @@ class Spinbox(Widget, XView): def __init__( self, master: Optional[Misc] = ..., - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, activebackground: _Color = ..., background: _Color = ..., @@ -2843,7 +2843,7 @@ class Spinbox(Widget, XView): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, activebackground: _Color = ..., background: _Color = ..., @@ -2948,7 +2948,7 @@ class LabelFrame(Widget): def __init__( self, master: Optional[Misc] = ..., - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, background: _Color = ..., bd: _ScreenUnits = ..., @@ -2978,7 +2978,7 @@ class LabelFrame(Widget): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, background: _Color = ..., bd: _ScreenUnits = ..., @@ -3033,7 +3033,7 @@ class PanedWindow(Widget): def __init__( self, master: Optional[Misc] = ..., - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, background: _Color = ..., bd: _ScreenUnits = ..., @@ -3059,7 +3059,7 @@ class PanedWindow(Widget): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, background: _Color = ..., bd: _ScreenUnits = ..., diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index 12d9a5f79bb4..4c9c966b0137 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -75,7 +75,7 @@ class Button(Widget): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, command: tkinter._ButtonCommand = ..., compound: _TtkCompound = ..., @@ -141,7 +141,7 @@ class Checkbutton(Widget): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, command: tkinter._ButtonCommand = ..., compound: _TtkCompound = ..., @@ -207,7 +207,7 @@ class Entry(Widget): # actually inherits from tkinter.Entry @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, cursor: tkinter._Cursor = ..., exportselection: bool = ..., @@ -286,7 +286,7 @@ class Combobox(Widget): # actually inherits from Entry @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, cursor: tkinter._Cursor = ..., exportselection: bool = ..., @@ -349,7 +349,7 @@ class Frame(Widget): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, borderwidth: tkinter._ScreenUnits = ..., cursor: tkinter._Cursor = ..., @@ -415,7 +415,7 @@ class Label(Widget): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, anchor: tkinter._Anchor = ..., background: tkinter._Color = ..., @@ -465,7 +465,7 @@ class Labelframe(Widget): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, cursor: tkinter._Cursor = ..., height: tkinter._ScreenUnits = ..., @@ -523,7 +523,7 @@ class Menubutton(Widget): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, compound: _TtkCompound = ..., cursor: tkinter._Cursor = ..., @@ -545,7 +545,7 @@ class Menubutton(Widget): @overload def config( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, compound: _TtkCompound = ..., cursor: tkinter._Cursor = ..., @@ -580,7 +580,7 @@ class Notebook(Widget): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, cursor: tkinter._Cursor = ..., height: int = ..., @@ -623,7 +623,7 @@ class Panedwindow(Widget): # actally inherits from tkinter.PanedWindow @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, cursor: tkinter._Cursor = ..., height: int = ..., @@ -683,7 +683,7 @@ class Progressbar(Widget): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, cursor: tkinter._Cursor = ..., length: tkinter._ScreenUnits = ..., @@ -744,7 +744,7 @@ class Radiobutton(Widget): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, command: tkinter._ButtonCommand = ..., compound: _TtkCompound = ..., @@ -790,7 +790,7 @@ class Scale(Widget): # actually inherits from tkinter.Scale @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, command: Union[str, Callable[[str], None]] = ..., cursor: tkinter._Cursor = ..., @@ -827,7 +827,7 @@ class Scrollbar(Widget): # actually inherits from tkinter.Scrollbar @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, command: Union[Callable[..., Optional[Tuple[float, float]]], str] = ..., cursor: tkinter._Cursor = ..., @@ -862,7 +862,7 @@ class Separator(Widget): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, cursor: tkinter._Cursor = ..., orient: Literal["horizontal", "vertical"] = ..., @@ -887,7 +887,7 @@ class Sizegrip(Widget): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, cursor: tkinter._Cursor = ..., style: str = ..., @@ -936,7 +936,7 @@ if sys.version_info >= (3, 7): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, command: Union[Callable[[], None], str, tkinter._TkinterSequence[str]] = ..., cursor: tkinter._Cursor = ..., @@ -1019,7 +1019,7 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): @overload def configure( self, - cnf: Dict[str, Any] = ..., + cnf: Optional[Dict[str, Any]] = ..., *, columns: _TkinterSequence[str] = ..., cursor: tkinter._Cursor = ..., From 3b45c1a6d38c58096723a77f8a1d931c634128a7 Mon Sep 17 00:00:00 2001 From: Akuli Date: Sat, 25 Jul 2020 18:10:54 +0300 Subject: [PATCH 07/45] add widget name option to tkinter.ttk.Entry --- stdlib/3/tkinter/ttk.pyi | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index 4c9c966b0137..4e00a9e1bc12 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -188,6 +188,7 @@ class Entry(Widget): # actually inherits from tkinter.Entry def __init__( self, master: Optional[tkinter.Misc] = ..., + widget: Optional[str] = ..., *, class_: str = ..., cursor: tkinter._Cursor = ..., From cbeab827a3e6afd1e09c8684c51b729e99b22344 Mon Sep 17 00:00:00 2001 From: Akuli Date: Sat, 25 Jul 2020 18:15:00 +0300 Subject: [PATCH 08/45] update tkinter_options_test.py to use Optional cnf annotation --- tests/tkinter_options_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tkinter_options_test.py b/tests/tkinter_options_test.py index e9ca8c318f42..372fde2037c6 100755 --- a/tests/tkinter_options_test.py +++ b/tests/tkinter_options_test.py @@ -278,7 +278,7 @@ def is_configure_method_with_options_as_kwargs(node: ast.stmt) -> bool: assert cnf.arg == "cnf" assert cnf.annotation is not None - [expected_cnf_annotation] = ast.parse("Dict[str, Any]").body + [expected_cnf_annotation] = ast.parse("Optional[Dict[str, Any]]").body assert isinstance(expected_cnf_annotation, ast.Expr) return ast.dump(cnf.annotation) == ast.dump(expected_cnf_annotation.value) From cacae458b354ddbdb319e33ed3093f2cf6c73156 Mon Sep 17 00:00:00 2001 From: Akuli Date: Fri, 31 Jul 2020 13:08:29 +0300 Subject: [PATCH 09/45] update pytype and delete copy/pasta --- requirements-tests-py3.txt | 2 +- stdlib/3/tkinter/font.pyi | 5 ----- stdlib/3/tkinter/ttk.pyi | 25 ++++++++++--------------- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/requirements-tests-py3.txt b/requirements-tests-py3.txt index 44832310f88d..dfd31e6fbb18 100644 --- a/requirements-tests-py3.txt +++ b/requirements-tests-py3.txt @@ -5,4 +5,4 @@ flake8==3.8.3 flake8-bugbear==20.1.4 flake8-pyi==20.5.0 isort[pyproject]==5.1.1 -pytype>=2020.07.24 +pytype>=2020.07.30 diff --git a/stdlib/3/tkinter/font.pyi b/stdlib/3/tkinter/font.pyi index 2cf04afe9cdd..8b974c8df8ad 100644 --- a/stdlib/3/tkinter/font.pyi +++ b/stdlib/3/tkinter/font.pyi @@ -2,11 +2,6 @@ import tkinter from typing import List, Optional, Tuple, TypeVar, Union, overload from typing_extensions import Literal, TypedDict -# copy/pasta from __init__.py to make pytype happy -# https://github.com/google/pytype/issues/626 -_T = TypeVar("_T") -_TkinterSequence = Union[List[_T], Tuple[_T, ...]] - NORMAL: Literal["normal"] ROMAN: Literal["roman"] BOLD: Literal["bold"] diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index 4e00a9e1bc12..2d9fdf5a12c8 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -4,11 +4,6 @@ from tkinter import Event from typing import Any, Callable, Dict, List, Optional, Tuple, TypeVar, Union, overload from typing_extensions import Literal -# copy/pasta from __init__.py to make pytype happy -# https://github.com/google/pytype/issues/626 -_T = TypeVar("_T") -_TkinterSequence = Union[List[_T], Tuple[_T, ...]] - def tclobjs_to_py(adict): ... def setup_master(master: Optional[Any] = ...): ... @@ -281,7 +276,7 @@ class Combobox(Widget): # actually inherits from Entry style: str = ..., takefocus: tkinter._TakeFocusValue = ..., textvariable: tkinter.Variable = ..., - values: Union[_TkinterSequence[str], _TkinterSequence[float]] = ..., + values: Union[tkinter._TkinterSequence[str], tkinter._TkinterSequence[float]] = ..., width: int = ..., ) -> None: ... @overload @@ -298,7 +293,7 @@ class Combobox(Widget): # actually inherits from Entry style: str = ..., takefocus: tkinter._TakeFocusValue = ..., textvariable: tkinter.Variable = ..., - values: Union[_TkinterSequence[str], _TkinterSequence[float]] = ..., + values: Union[tkinter._TkinterSequence[str], tkinter._TkinterSequence[float]] = ..., width: int = ..., ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... @overload @@ -930,7 +925,7 @@ if sys.version_info >= (3, 7): style: str = ..., takefocus: tkinter._TakeFocusValue = ..., to: float = ..., - values: Union[_TkinterSequence[str], _TkinterSequence[float]] = ..., + values: Union[tkinter._TkinterSequence[str], tkinter._TkinterSequence[float]] = ..., wrap: bool = ..., xscrollcommand: tkinter._XYScrollCommand = ..., ) -> None: ... @@ -948,7 +943,7 @@ if sys.version_info >= (3, 7): style: str = ..., takefocus: tkinter._TakeFocusValue = ..., to: float = ..., - values: Union[_TkinterSequence[str], _TkinterSequence[float]] = ..., + values: Union[tkinter._TkinterSequence[str], tkinter._TkinterSequence[float]] = ..., wrap: bool = ..., xscrollcommand: tkinter._XYScrollCommand = ..., ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... @@ -1001,9 +996,9 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): master: Optional[tkinter.Misc] = ..., *, class_: str = ..., - columns: _TkinterSequence[str] = ..., + columns: tkinter._TkinterSequence[str] = ..., cursor: tkinter._Cursor = ..., - displaycolumns: Union[_TkinterSequence[str], _TkinterSequence[int], Literal["#all"]] = ..., + displaycolumns: Union[tkinter._TkinterSequence[str], tkinter._TkinterSequence[int], Literal["#all"]] = ..., height: int = ..., padding: tkinter._Padding = ..., selectmode: Literal["extended", "browse", "none"] = ..., @@ -1011,7 +1006,7 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): # # 'tree headings' is same as ['tree', 'headings'], and I wouldn't be # surprised if someone was using it. - show: Union[Literal["tree", "headings", "tree headings"], _TkinterSequence[str]] = ..., + show: Union[Literal["tree", "headings", "tree headings"], tkinter._TkinterSequence[str]] = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., xscrollcommand: tkinter._XYScrollCommand = ..., @@ -1022,13 +1017,13 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): self, cnf: Optional[Dict[str, Any]] = ..., *, - columns: _TkinterSequence[str] = ..., + columns: tkinter._TkinterSequence[str] = ..., cursor: tkinter._Cursor = ..., - displaycolumns: Union[_TkinterSequence[str], _TkinterSequence[int], Literal["#all"]] = ..., + displaycolumns: Union[tkinter._TkinterSequence[str], tkinter._TkinterSequence[int], Literal["#all"]] = ..., height: int = ..., padding: tkinter._Padding = ..., selectmode: Literal["extended", "browse", "none"] = ..., - show: Union[Literal["tree", "headings", "tree headings"], _TkinterSequence[str]] = ..., + show: Union[Literal["tree", "headings", "tree headings"], tkinter._TkinterSequence[str]] = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., xscrollcommand: tkinter._XYScrollCommand = ..., From 0fb14fc2827e71a6a0510e4575f62d5230bedab0 Mon Sep 17 00:00:00 2001 From: Akuli Date: Fri, 31 Jul 2020 13:12:03 +0300 Subject: [PATCH 10/45] require Combobox and Spinbox values to be strings Previously I allowed them to be numbers too, which could be useful for letting the user e.g. choose a port number from a list of acceptable ports numbers. I think that doesn't come up as often as would often as accidentally passing numbers for values. More importantly, this makes the type more readable in e.g. IDE autocompletion or mypy errors. --- stdlib/3/tkinter/__init__.pyi | 4 ++-- stdlib/3/tkinter/ttk.pyi | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/stdlib/3/tkinter/__init__.pyi b/stdlib/3/tkinter/__init__.pyi index 6f46ad69d504..99bc4f040dd5 100644 --- a/stdlib/3/tkinter/__init__.pyi +++ b/stdlib/3/tkinter/__init__.pyi @@ -2835,7 +2835,7 @@ class Spinbox(Widget, XView): to: float = ..., validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., validatecommand: _EntryValidateCommand = ..., - values: Union[_TkinterSequence[str], _TkinterSequence[float]] = ..., + values: _TkinterSequence[str] = ..., width: int = ..., wrap: bool = ..., xscrollcommand: _XYScrollCommand = ..., @@ -2888,7 +2888,7 @@ class Spinbox(Widget, XView): to: float = ..., validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., validatecommand: _EntryValidateCommand = ..., - values: Union[_TkinterSequence[str], _TkinterSequence[float]] = ..., + values: _TkinterSequence[str] = ..., width: int = ..., wrap: bool = ..., xscrollcommand: _XYScrollCommand = ..., diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index 2d9fdf5a12c8..9ea1835a81cf 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -276,7 +276,7 @@ class Combobox(Widget): # actually inherits from Entry style: str = ..., takefocus: tkinter._TakeFocusValue = ..., textvariable: tkinter.Variable = ..., - values: Union[tkinter._TkinterSequence[str], tkinter._TkinterSequence[float]] = ..., + values: tkinter._TkinterSequence[str] = ..., width: int = ..., ) -> None: ... @overload @@ -293,7 +293,7 @@ class Combobox(Widget): # actually inherits from Entry style: str = ..., takefocus: tkinter._TakeFocusValue = ..., textvariable: tkinter.Variable = ..., - values: Union[tkinter._TkinterSequence[str], tkinter._TkinterSequence[float]] = ..., + values: tkinter._TkinterSequence[str] = ..., width: int = ..., ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... @overload @@ -925,7 +925,7 @@ if sys.version_info >= (3, 7): style: str = ..., takefocus: tkinter._TakeFocusValue = ..., to: float = ..., - values: Union[tkinter._TkinterSequence[str], tkinter._TkinterSequence[float]] = ..., + values: tkinter._TkinterSequence[str] = ..., wrap: bool = ..., xscrollcommand: tkinter._XYScrollCommand = ..., ) -> None: ... @@ -943,7 +943,7 @@ if sys.version_info >= (3, 7): style: str = ..., takefocus: tkinter._TakeFocusValue = ..., to: float = ..., - values: Union[tkinter._TkinterSequence[str], tkinter._TkinterSequence[float]] = ..., + values: tkinter._TkinterSequence[str] = ..., wrap: bool = ..., xscrollcommand: tkinter._XYScrollCommand = ..., ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... From 7dd64bde0cd41d7d8b01c1df4d08d5f963e868e8 Mon Sep 17 00:00:00 2001 From: Akuli Date: Fri, 7 Aug 2020 16:16:44 +0300 Subject: [PATCH 11/45] remove unused imports --- stdlib/3/tkinter/ttk.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index 9ea1835a81cf..71c4ae12bf41 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -1,7 +1,7 @@ import sys import tkinter from tkinter import Event -from typing import Any, Callable, Dict, List, Optional, Tuple, TypeVar, Union, overload +from typing import Any, Callable, Dict, List, Optional, Tuple, Union, overload from typing_extensions import Literal def tclobjs_to_py(adict): ... From 29fc43a8d4ff9f9e9d17cc6a3dcba640d9354ddf Mon Sep 17 00:00:00 2001 From: Akuli Date: Sat, 8 Aug 2020 11:43:22 +0300 Subject: [PATCH 12/45] simplify find_all_classdefs --- tests/tkinter_options_test.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/tests/tkinter_options_test.py b/tests/tkinter_options_test.py index 372fde2037c6..169fdf1788c6 100755 --- a/tests/tkinter_options_test.py +++ b/tests/tkinter_options_test.py @@ -260,14 +260,9 @@ def get_import_mapping(parsed: ast.Module) -> Dict[str, str]: def find_all_classdefs(parsed: ast.Module) -> Iterable[ast.ClassDef]: - for stmt in parsed.body: - if isinstance(stmt, ast.ClassDef): - yield stmt - elif isinstance(stmt, ast.If): - # handle classes e.g. under 'if sys.version_info >= bla' - for inner in stmt.body: - if isinstance(inner, ast.ClassDef): - yield inner + for node in ast.walk(parsed): # handle classes e.g. under 'if sys.version_info >= bla' + if isinstance(node, ast.ClassDef): + yield node def is_configure_method_with_options_as_kwargs(node: ast.stmt) -> bool: From f9bffea242a723630aa805c4ea46826fb7ca89e6 Mon Sep 17 00:00:00 2001 From: Akuli Date: Sat, 8 Aug 2020 15:10:37 +0300 Subject: [PATCH 13/45] make comments brief --- stdlib/3/tkinter/__init__.pyi | 191 +++++----------------------------- 1 file changed, 26 insertions(+), 165 deletions(-) diff --git a/stdlib/3/tkinter/__init__.pyi b/stdlib/3/tkinter/__init__.pyi index ee1364a7e300..cd556ad8adc6 100644 --- a/stdlib/3/tkinter/__init__.pyi +++ b/stdlib/3/tkinter/__init__.pyi @@ -14,86 +14,14 @@ READABLE: Any WRITABLE: Any EXCEPTION: Any -# *************************************************************** -# ** Quick guide for figuring out which widget class to choose ** -# *************************************************************** -# - Misc: any widget (don't use BaseWidget because Tk doesn't inherit from BaseWidget) -# - Widget: anything that is meant to be put into another widget with e.g. pack or grid -# - Wm: a toplevel window, Tk or Toplevel +# Quick guide for figuring out which widget class to choose: +# - Misc: any widget (don't use BaseWidget because Tk doesn't inherit from BaseWidget) +# - Widget: anything that is meant to be put into another widget with e.g. pack or grid +# - Wm: a toplevel window, Tk or Toplevel # +# Instructions for figuring out the correct type of each widget option: +# - See discussion on #4363. # -# ******************** -# ** Widget options ** -# ******************** -# These are the things passed to configure(), e.g. the text of a label. -# Currently widget options work like this: -# - configure and config are identical (tkinter classes do config = configure) -# - configure keyword-only arguments are specified exactly with type hints -# - cget takes Literal[...] as the string argument and returns Any -# - __setitem__ and __getitem__ use string arguments and Any -# -# The idea is that configure() and cget() are more strictly typed than -# __setitem__ and __getitem__. This way the tkinter user gets to choose what -# kind of typing to use with widget options. So, if your code does -# -# widget[some_string_variable] = some_value -# -# then you don't need to change anything to make it type check with these -# stubs. However, if you instead have the less readable -# -# widget.config(**{some_string_variable: some_value}), -# -# then you might get mypy warnings from doing that. Doing it this way also -# decreases the amount of required copy/pasta a lot. -# -# Getting the value of an option gives Any. See this to get an idea of why I -# ended up doing it that way: -# -# >>> l = tkinter.ttk.Label() -# >>> l.cget('wraplength') -# '' -# >>> l.config(wraplength='2c') -# >>> l.cget('wraplength') -# -# >>> l.config(wraplength=0) -# >>> l.cget('wraplength') -# 0 -# -# Here getting the same option returns 3 different types: string, -# _tkinter.Tcl_Obj and int. There's no documentation about this and I don't -# have a good way to know whether these types found by trying out things on the -# prompt are really all the possible types that could be returned. Also, -# returning Union is considered bad in general. -# -# Any-typing the values of options is still not perfect. For example, this -# works at runtime and type checks: -# -# label1 = tkinter.ttk.Label() -# label2 = tkinter.ttk.Label() -# label1.config(wraplength=label2.cget('wraplength')) # right side has type Any -# -# This works at runtime but does NOT type check (although it really seems to -# require deliberately doing something funny for this to be a problem): -# -# label1 = tkinter.ttk.Label() -# label2 = tkinter.ttk.Label() -# label2['wraplength'] = '2c' -# obj = label2.cget('wraplength') -# assert isinstance(obj, _tkinter.Tcl_Obj) -# label1.config(wraplength=obj) # mypy error because Tcl_Obj is not valid wraplength -# -# Here obj is Tcl_Obj, which is not one of the things that can be assigned to a -# wraplength. Tcl_Obj in general is not something that comes up frequently in -# tkinter, and when it does come up, the right thing to do is usually str()ing -# it or passing it back to tkinter. I tend to mostly ignore Tcl_Obj because I -# don't want to clutter all option type hints with Union[..., Tcl_Obj]. -# -# If you want to write code like the above, you still don't need to resort to -# type ignore comments, because __setitem__ is less strictly typed: -# -# label1['wraplength'] = obj -# -# Instructions for figuring out the correct type of each option: # - Find the option from the manual page of the widget. Usually the manual # page of a non-ttk widget has the same name as the tkinter class, in the # 3tk section: @@ -151,49 +79,17 @@ EXCEPTION: Any _T = TypeVar("_T") _TkinterSequence = Union[List[_T], Tuple[_T, ...]] -# If a manual page mentions Tk_GetAnchor or refers to another manual page named -# 'options', then it means this. Note that some ttk widgets have other things -# named 'anchor' with a different set of allowed values. -_Anchor = Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] - -# if manual page mentions Tk_GetBitmap, then it means this. Bitmap names can be -# arbitrary strings. You can also specify a path, but you need to put '@' in -# front of it and tkinter doesn't do that automatically if you e.g. pass in a -# pathlib.Path object. -_Bitmap = str - -# Return value of the function will be returned by Button.invoke(), possibly -# somehow ruined by Python -> Tcl -> Python conversions. This works the same -# way for tkinter.ttk.Button even though its manual page says nothing about -# the return value. -_ButtonCommand = Union[str, Callable[[], Any]] - -# Color strings are typically '#rrggbb', '#rgb' or color names. -_Color = str - -# This is the type of -compound from manual page named 'options'. Note that -# sometimes there is an option named -compound that takes something else than -# one of these. -_Compound = Literal["top", "left", "center", "right", "bottom", "none"] - -# Tk_GetCursor documents many possibilities, but they're all just Tcl lists of -# strings (corresponding to Python tuples of strings), at least 1 string and at -# most 4 strings. -_Cursor = Union[str, Tuple[str], Tuple[str, str], Tuple[str, str, str], Tuple[str, str, str, str]] - -# This is for Entry. Currently there seems to be no way to make use of the -# substitutions described in entry manual page unless your validatecommand is a -# Tcl command, e.g.: -# -# tcl_print = e.register(print) -# e['invalidcommand'] = [tcl_print, '%P'] -# -# Specifying a sequence does the correct kind of escaping for Tcl. -_EntryValidateCommand = Union[Callable[[], bool], str, _TkinterSequence[str]] - -# See 'FONT DESCRIPTIONS' in font man page. This uses str because Literal -# inside Tuple doesn't work. -# +# Some widgets have an option named -compound that accepts different values +# than the _Compound defined here. Manu other options have similar things. +_Anchor = Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] # manual page: Tk_GetAnchor +_Bitmap = str # manual page: Tk_GetBitmap +_ButtonCommand = Union[str, Callable[[], Any]] # return value is returned from Button.invoke() +_Color = str # typically '#rrggbb', '#rgb' or color names. +_Compound = Literal["top", "left", "center", "right", "bottom", "none"] # -compound in manual page named 'options' +_Cursor = Union[str, Tuple[str], Tuple[str, str], Tuple[str, str, str], Tuple[str, str, str, str]] # manual page: Tk_GetCursor +_EntryValidateCommand = Union[ + Callable[[], bool], str, _TkinterSequence[str] +] # example when it's sequence: entry['invalidcommand'] = [entry.register(print), '%P'] # Putting this to font.pyi breaks pytype: https://github.com/google/pytype/issues/626 # # Using anything from tkinter.font in this file means that 'import tkinter' @@ -201,17 +97,8 @@ _EntryValidateCommand = Union[Callable[[], bool], str, _TkinterSequence[str]] # unfortunately not much can be done about it. https://github.com/python/typeshed/pull/4346 _FontDescription = Union[ str, font.Font, Tuple[str, int], Tuple[str, int, _TkinterSequence[str]], -] - -# str could be e.g. from tkinter.image_names() -_ImageSpec = Union[Image, str] - -# Padding should be set to 1, 2, 3 or 4 screen distances in tuple or list. -# If just 1, then no need to wrap in tuple or list. -# -# When getting the padding, it can be empty string. Again, we cheat by not -# creating a Union return so that looping over the return value and -# treating the items as tkinter._ScreenUnits actually works. +] # 'FONT DESCRIPTIONS' in 'font' manual page +_ImageSpec = Union[Image, str] # str can be from e.g. tkinter.image_names() _Padding = Union[ _ScreenUnits, Tuple[_ScreenUnits], @@ -219,22 +106,10 @@ _Padding = Union[ Tuple[_ScreenUnits, _ScreenUnits, _ScreenUnits], Tuple[_ScreenUnits, _ScreenUnits, _ScreenUnits, _ScreenUnits], ] - -# If manual page says Tk_GetRelief then it means this. -_Relief = Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] - -# string must be e.g. '12.34', '12.34c', '12.34i', '12.34m', '12.34p' -# see Tk_GetPixels man page for what each suffix means -# Some ttk widgets also use empty string. -_ScreenUnits = Union[str, float] - -# -xscrollcommand and -yscrollcommand in 'options' manual page -_XYScrollCommand = Union[str, Callable[[float, float], None]] - -# Returning None from a takefocus callback seems to work (man page says to -# return empty string instead). But setting options to None doesn't work -# in general. Use nametowidget() to handle the argument of the callback. -_TakeFocusValue = Union[bool, Literal[""], Callable[[str], Optional[bool]]] +_Relief = Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] # manual page: Tk_GetRelief +_ScreenUnits = Union[str, float] # manual page: Tk_GetPixels +_XYScrollCommand = Union[str, Callable[[float, float], None]] # -xscrollcommand and -yscrollcommand in 'options' manual page +_TakeFocusValue = Union[bool, Literal[""], Callable[[str], Optional[bool]]] # -takefocus in manual page named 'options' if sys.version_info >= (3, 6): class EventType(str, Enum): @@ -352,8 +227,6 @@ getdouble: Any def getboolean(s): ... -# This class is the base class of all widgets. Don't use BaseWidget or Widget -# for that because Tk doesn't inherit from Widget or BaseWidget. class Misc: def destroy(self): ... def deletecommand(self, name): ... @@ -2800,24 +2673,12 @@ class _setit: def __init__(self, var, value, callback: Optional[Any] = ...): ... def __call__(self, *args): ... -# This widget is weird: -# - Manual page is named tk_optionMenu instead of menu. -# - It's not actually a Tk widget but a Tcl procedure that creates two -# associated widgets. -# - Its __init__ takes in arguments differently from all other widgets -# (self + required positional arguments + special keyword-only arguments). -# - Tkinter uses the exact same options as Menubutton except that __getitem__ -# is overrided to give a tkinter widget when you request its 'menu' option. -# However, querying it with 'cget' gives you a string instead of a widget -# object. -# -# This doesn't support any other variables than StringVar, which could be -# useful for an OptionMenu with a dropdown of integers to choose from. Add -# overrides to __init__ if this is a problem in practice. +# manual page: tk_optionMenu class OptionMenu(Menubutton): widgetName: Any menuname: Any def __init__( + # differs from other widgets self, master: Optional[Misc], variable: StringVar, @@ -2827,7 +2688,7 @@ class OptionMenu(Menubutton): command: Optional[Callable[[StringVar], None]] = ..., ) -> None: ... # configure, config, cget are inherited from Menubutton - # destroy and __setitem__ are overrided, signature does not change + # destroy and __getitem__ are overrided, signature does not change class Image: name: Any From 68b023f1f2e4f0b14dde7b916d293e54f52568d1 Mon Sep 17 00:00:00 2001 From: Akuli Date: Sat, 8 Aug 2020 15:13:09 +0300 Subject: [PATCH 14/45] fix typo --- stdlib/3/tkinter/__init__.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/3/tkinter/__init__.pyi b/stdlib/3/tkinter/__init__.pyi index cd556ad8adc6..6347cb42a3e5 100644 --- a/stdlib/3/tkinter/__init__.pyi +++ b/stdlib/3/tkinter/__init__.pyi @@ -80,7 +80,7 @@ _T = TypeVar("_T") _TkinterSequence = Union[List[_T], Tuple[_T, ...]] # Some widgets have an option named -compound that accepts different values -# than the _Compound defined here. Manu other options have similar things. +# than the _Compound defined here. Many other options have similar things. _Anchor = Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] # manual page: Tk_GetAnchor _Bitmap = str # manual page: Tk_GetBitmap _ButtonCommand = Union[str, Callable[[], Any]] # return value is returned from Button.invoke() From 6863bec4466712d002907fefe6d17f301dbcc860 Mon Sep 17 00:00:00 2001 From: Akuli Date: Sat, 8 Aug 2020 15:24:04 +0300 Subject: [PATCH 15/45] better comment --- stdlib/3/tkinter/__init__.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/3/tkinter/__init__.pyi b/stdlib/3/tkinter/__init__.pyi index d15e31352932..40999a32c919 100644 --- a/stdlib/3/tkinter/__init__.pyi +++ b/stdlib/3/tkinter/__init__.pyi @@ -417,7 +417,7 @@ class Misc: def event_info(self, virtual: Optional[Any] = ...): ... def image_names(self): ... def image_types(self): ... - # See comments in beginning of this file. + # See #4363 def __setitem__(self, key: str, value: Any) -> None: ... def __getitem__(self, key: str) -> Any: ... From 96673b53ec2a3fd3a8be05e93daa17aba1e8f2b8 Mon Sep 17 00:00:00 2001 From: Akuli Date: Sun, 9 Aug 2020 22:25:31 +0300 Subject: [PATCH 16/45] more permissive type for tabs option of tkinter.Text --- stdlib/3/tkinter/__init__.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/3/tkinter/__init__.pyi b/stdlib/3/tkinter/__init__.pyi index 40999a32c919..4c66c534de04 100644 --- a/stdlib/3/tkinter/__init__.pyi +++ b/stdlib/3/tkinter/__init__.pyi @@ -2563,7 +2563,7 @@ class Text(Widget, XView, YView): startline: Union[int, Literal[""]] = ..., state: Literal["normal", "disabled"] = ..., # Literal inside Tuple doesn't actually work - tabs: Tuple[Union[_ScreenUnits, str], ...] = ..., + tabs: Union[_ScreenUnits, str, Tuple[Union[_ScreenUnits, str], ...]] = ..., tabstyle: Literal["tabular", "wordprocessor"] = ..., takefocus: _TakeFocusValue = ..., undo: bool = ..., @@ -2613,7 +2613,7 @@ class Text(Widget, XView, YView): spacing3: _ScreenUnits = ..., startline: Union[int, Literal[""]] = ..., state: Literal["normal", "disabled"] = ..., - tabs: Tuple[Union[_ScreenUnits, str], ...] = ..., + tabs: Union[_ScreenUnits, str, Tuple[Union[_ScreenUnits, str], ...]] = ..., tabstyle: Literal["tabular", "wordprocessor"] = ..., takefocus: _TakeFocusValue = ..., undo: bool = ..., From bde9e118db6bec1e3def4e56fd6d8ccdd9e2e94a Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 10 Aug 2020 12:25:29 +0300 Subject: [PATCH 17/45] don't lie about inheritance --- stdlib/3/tkinter/ttk.pyi | 115 +++++++-------------------------------- 1 file changed, 20 insertions(+), 95 deletions(-) diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index 6dbfa40f7d83..ed7d9f02caec 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -176,10 +176,7 @@ _EntryOptionName = Literal[ "xscrollcommand", ] -# This actually inherits from tkinter.Entry, but it doesn't have all -# options of tkinter.Entry so we can't make the stubs look like it inherits -# from tkinter.Entry. -class Entry(Widget): # actually inherits from tkinter.Entry +class Entry(tkinter.Entry): def __init__( self, master: Optional[tkinter.Misc] = ..., @@ -200,7 +197,7 @@ class Entry(Widget): # actually inherits from tkinter.Entry width: int = ..., xscrollcommand: tkinter._XYScrollCommand = ..., ) -> None: ... - @overload + @overload # type: ignore def configure( self, cnf: Optional[Dict[str, Any]] = ..., @@ -221,30 +218,11 @@ class Entry(Widget): # actually inherits from tkinter.Entry ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... @overload def configure(self, cnf: _EntryOptionName) -> Tuple[str, str, str, Any, Any]: ... - config = configure - def cget(self, key: _EntryOptionName) -> Any: ... + config = configure # type: ignore + def cget(self, key: _EntryOptionName) -> Any: ... # type: ignore def bbox(self, index): ... def identify(self, x, y): ... def validate(self): ... - delete = tkinter.Entry.delete - get = tkinter.Entry.get - icursor = tkinter.Entry.icursor - index = tkinter.Entry.index - insert = tkinter.Entry.insert - scan_mark = tkinter.Entry.scan_mark - scan_dragto = tkinter.Entry.scan_dragto - selection_adjust = tkinter.Entry.selection_adjust - select_adjust = tkinter.Entry.select_adjust - selection_clear = tkinter.Entry.selection_clear - select_clear = tkinter.Entry.select_clear - selection_from = tkinter.Entry.selection_from - select_from = tkinter.Entry.select_from - selection_present = tkinter.Entry.selection_present - select_present = tkinter.Entry.select_present - selection_range = tkinter.Entry.selection_range - select_range = tkinter.Entry.select_range - selection_to = tkinter.Entry.selection_to - select_to = tkinter.Entry.select_to _ComboboxOptionName = Literal[ "class", @@ -261,7 +239,7 @@ _ComboboxOptionName = Literal[ "width", ] -class Combobox(Widget): # actually inherits from Entry +class Combobox(Entry): def __init__( self, master: Optional[tkinter.Misc] = ..., @@ -279,7 +257,7 @@ class Combobox(Widget): # actually inherits from Entry values: tkinter._TkinterSequence[str] = ..., width: int = ..., ) -> None: ... - @overload + @overload # type: ignore def configure( self, cnf: Optional[Dict[str, Any]] = ..., @@ -298,32 +276,10 @@ class Combobox(Widget): # actually inherits from Entry ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... @overload def configure(self, cnf: _ComboboxOptionName) -> Tuple[str, str, str, Any, Any]: ... - config = configure - def cget(self, key: _ComboboxOptionName) -> Any: ... + config = configure # type: ignore + def cget(self, key: _ComboboxOptionName) -> Any: ... # type: ignore def current(self, newindex: Optional[Any] = ...): ... def set(self, value): ... - bbox = Entry.bbox - identify = Entry.identify - validate = Entry.validate - delete = Entry.delete - get = Entry.get - icursor = Entry.icursor - index = Entry.index - insert = Entry.insert - scan_mark = Entry.scan_mark - scan_dragto = Entry.scan_dragto - selection_adjust = Entry.selection_adjust - select_adjust = Entry.select_adjust - selection_clear = Entry.selection_clear - select_clear = Entry.select_clear - selection_from = Entry.selection_from - select_from = Entry.select_from - selection_present = Entry.selection_present - select_present = Entry.select_present - selection_range = Entry.selection_range - select_range = Entry.select_range - selection_to = Entry.selection_to - select_to = Entry.select_to _FrameOptionName = Literal["borderwidth", "class", "cursor", "height", "padding", "relief", "style", "takefocus", "width"] @@ -766,7 +722,7 @@ _ScaleOptionName = Literal[ "class", "command", "cursor", "from", "length", "orient", "style", "takefocus", "to", "value", "variable" ] -class Scale(Widget): # actually inherits from tkinter.Scale +class Scale(Widget, tkinter.Scale): def __init__( self, master: Optional[tkinter.Misc] = ..., @@ -783,7 +739,7 @@ class Scale(Widget): # actually inherits from tkinter.Scale value: float = ..., variable: tkinter.DoubleVar = ..., ) -> None: ... - @overload + @overload # type: ignore def configure( self, cnf: Optional[Dict[str, Any]] = ..., @@ -801,14 +757,11 @@ class Scale(Widget): # actually inherits from tkinter.Scale ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... @overload def configure(self, cnf: _ScaleOptionName) -> Tuple[str, str, str, Any, Any]: ... - config = configure - def cget(self, key: _ScaleOptionName) -> Any: ... + config = configure # type: ignore + def cget(self, key: _ScaleOptionName) -> Any: ... # type: ignore def get(self, x: Optional[Any] = ..., y: Optional[Any] = ...): ... - set = tkinter.Scale.set - coords = tkinter.Scale.coords - identify = tkinter.Scale.identify -class Scrollbar(Widget): # actually inherits from tkinter.Scrollbar +class Scrollbar(Widget, tkinter.Scrollbar): def __init__( self, master: Optional[tkinter.Misc] = ..., @@ -820,7 +773,7 @@ class Scrollbar(Widget): # actually inherits from tkinter.Scrollbar style: str = ..., takefocus: tkinter._TakeFocusValue = ..., ) -> None: ... - @overload + @overload # type: ignore def configure( self, cnf: Optional[Dict[str, Any]] = ..., @@ -835,14 +788,8 @@ class Scrollbar(Widget): # actually inherits from tkinter.Scrollbar def configure( self, cnf: Literal["class", "command", "cursor", "orient", "style", "takefocus"] ) -> Tuple[str, str, str, Any, Any]: ... - config = configure - def cget(self, key: Literal["class", "command", "cursor", "orient", "style", "takefocus"]) -> Any: ... - activate = tkinter.Scrollbar.activate - delta = tkinter.Scrollbar.delta - fraction = tkinter.Scrollbar.fraction - identify = tkinter.Scrollbar.identify - get = tkinter.Scrollbar.get - set = tkinter.Scrollbar.set + config = configure # type: ignore + def cget(self, key: Literal["class", "command", "cursor", "orient", "style", "takefocus"]) -> Any: ... # type: ignore class Separator(Widget): def __init__( @@ -910,7 +857,7 @@ if sys.version_info >= (3, 7): "wrap", "xscrollcommand", ] - class Spinbox(Widget): # actually inherits from Entry + class Spinbox(Entry): def __init__( self, master: Optional[tkinter.Misc] = ..., @@ -929,7 +876,7 @@ if sys.version_info >= (3, 7): wrap: bool = ..., xscrollcommand: tkinter._XYScrollCommand = ..., ) -> None: ... - @overload + @overload # type: ignore def configure( self, cnf: Optional[Dict[str, Any]] = ..., @@ -949,31 +896,9 @@ if sys.version_info >= (3, 7): ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... @overload def configure(self, cnf: _SpinboxOptionName) -> Tuple[str, str, str, Any, Any]: ... - config = configure - def cget(self, key: _SpinboxOptionName) -> Any: ... + config = configure # type: ignore + def cget(self, key: _SpinboxOptionName) -> Any: ... # type: ignore def set(self, value: Any) -> None: ... - bbox = Entry.bbox - identify = Entry.identify - validate = Entry.validate - delete = Entry.delete - get = Entry.get - icursor = Entry.icursor - index = Entry.index - insert = Entry.insert - scan_mark = Entry.scan_mark - scan_dragto = Entry.scan_dragto - selection_adjust = Entry.selection_adjust - select_adjust = Entry.select_adjust - selection_clear = Entry.selection_clear - select_clear = Entry.select_clear - selection_from = Entry.selection_from - select_from = Entry.select_from - selection_present = Entry.selection_present - select_present = Entry.select_present - selection_range = Entry.selection_range - select_range = Entry.select_range - selection_to = Entry.selection_to - select_to = Entry.select_to _TreeviewOptionName = Literal[ "class", From d28aff4f8baa6c1df4e0ea7dd721e1a788764a0a Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 10 Aug 2020 12:26:51 +0300 Subject: [PATCH 18/45] remove unnecessary copy/pasta --- stdlib/3/tkinter/__init__.pyi | 44 +---------------------------------- stdlib/3/tkinter/ttk.pyi | 23 +----------------- 2 files changed, 2 insertions(+), 65 deletions(-) diff --git a/stdlib/3/tkinter/__init__.pyi b/stdlib/3/tkinter/__init__.pyi index 4c66c534de04..26614f0462b1 100644 --- a/stdlib/3/tkinter/__init__.pyi +++ b/stdlib/3/tkinter/__init__.pyi @@ -1979,49 +1979,7 @@ class Menubutton(Widget): ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... @overload def configure(self, cnf: _MenubuttonOptionName) -> Tuple[str, str, str, Any, Any]: ... - # config is just like configure, but copy/pasta, because assigning - # 'config = configure' creates mypy errors here for some reason - @overload - def config( - self, - cnf: Optional[Dict[str, Any]] = ..., - *, - activebackground: _Color = ..., - activeforeground: _Color = ..., - anchor: _Anchor = ..., - background: _Color = ..., - bd: _ScreenUnits = ..., - bg: _Color = ..., - bitmap: _Bitmap = ..., - borderwidth: _ScreenUnits = ..., - compound: _Compound = ..., - cursor: _Cursor = ..., - direction: Literal["above", "below", "left", "right", "flush"] = ..., - disabledforeground: _Color = ..., - fg: _Color = ..., - font: _FontDescription = ..., - foreground: _Color = ..., - height: _ScreenUnits = ..., - highlightbackground: _Color = ..., - highlightcolor: _Color = ..., - highlightthickness: _ScreenUnits = ..., - image: _ImageSpec = ..., - indicatoron: bool = ..., - justify: Literal["left", "center", "right"] = ..., - menu: Menu = ..., - padx: _ScreenUnits = ..., - pady: _ScreenUnits = ..., - relief: _Relief = ..., - state: Literal["normal", "active", "disabled"] = ..., - takefocus: _TakeFocusValue = ..., - text: str = ..., - textvariable: Variable = ..., - underline: int = ..., - width: _ScreenUnits = ..., - wraplength: _ScreenUnits = ..., - ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... - @overload - def config(self, cnf: _MenubuttonOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure def cget(self, key: _MenubuttonOptionName) -> Any: ... _MessageOptionName = Literal[ diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index ed7d9f02caec..cff201d0b5d3 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -492,28 +492,7 @@ class Menubutton(Widget): ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... @overload def configure(self, cnf: _MenubuttonOptionName) -> Tuple[str, str, str, Any, Any]: ... - # config is just like configure, but copy/pasta, because assigning - # 'config = configure' creates mypy errors here for some reason - @overload - def config( - self, - cnf: Optional[Dict[str, Any]] = ..., - *, - compound: _TtkCompound = ..., - cursor: tkinter._Cursor = ..., - direction: Literal["above", "below", "left", "right", "flush"] = ..., - image: tkinter._ImageSpec = ..., - menu: tkinter.Menu = ..., - state: Literal["normal", "disabled"] = ..., - style: str = ..., - takefocus: tkinter._TakeFocusValue = ..., - text: str = ..., - textvariable: tkinter.Variable = ..., - underline: int = ..., - width: Union[int, Literal[""]] = ..., - ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... - @overload - def config(self, cnf: _MenubuttonOptionName) -> Tuple[str, str, str, Any, Any]: ... + config = configure def cget(self, key: _MenubuttonOptionName) -> Any: ... class Notebook(Widget): From 77fade260407ae77fe4c4e5cb14ceffeb5512b51 Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 10 Aug 2020 12:39:40 +0300 Subject: [PATCH 19/45] revert changes to master arguments (belongs to separate PR) --- stdlib/3/tkinter/__init__.pyi | 4 ++-- stdlib/3/tkinter/ttk.pyi | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/3/tkinter/__init__.pyi b/stdlib/3/tkinter/__init__.pyi index 26614f0462b1..a3f350f774dd 100644 --- a/stdlib/3/tkinter/__init__.pyi +++ b/stdlib/3/tkinter/__init__.pyi @@ -2697,7 +2697,7 @@ class Image: def width(self): ... class PhotoImage(Image): - def __init__(self, name: Optional[Any] = ..., cnf=..., master: Optional[Misc] = ..., **kw): ... + def __init__(self, name: Optional[Any] = ..., cnf=..., master: Optional[Any] = ..., **kw): ... def blank(self): ... def cget(self, option): ... def __getitem__(self, key): ... @@ -2712,7 +2712,7 @@ class PhotoImage(Image): def transparency_set(self, x: int, y: int, boolean: bool) -> None: ... class BitmapImage(Image): - def __init__(self, name: Optional[Any] = ..., cnf=..., master: Optional[Misc] = ..., **kw): ... + def __init__(self, name: Optional[Any] = ..., cnf=..., master: Optional[Any] = ..., **kw): ... def image_names(): ... def image_types(): ... diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index cff201d0b5d3..291b8b17c69d 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -11,9 +11,9 @@ def setup_master(master: Optional[Any] = ...): ... _TtkCompound = Literal["text", "image", tkinter._Compound] class Style: - master: tkinter.Misc + master: Any tk: _tkinter.TkappType - def __init__(self, master: Optional[tkinter.Misc] = ...): ... + def __init__(self, master: Optional[Any] = ...): ... def configure(self, style, query_opt: Optional[Any] = ..., **kw): ... def map(self, style, query_opt: Optional[Any] = ..., **kw): ... def lookup(self, style, option, state: Optional[Any] = ..., default: Optional[Any] = ...): ... From 0d6a93d7350c426df8350397f186925cba24b1b0 Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 10 Aug 2020 12:45:14 +0300 Subject: [PATCH 20/45] remove more "fake inheritance" --- stdlib/3/tkinter/ttk.pyi | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index 291b8b17c69d..dbb5d528b9f5 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -537,7 +537,7 @@ class Notebook(Widget): def tabs(self): ... def enable_traversal(self): ... -class Panedwindow(Widget): # actally inherits from tkinter.PanedWindow +class Panedwindow(Widget, tkinter.PanedWindow): def __init__( self, master: Optional[tkinter.Misc] = ..., @@ -568,25 +568,10 @@ class Panedwindow(Widget): # actally inherits from tkinter.PanedWindow ) -> Tuple[str, str, str, Any, Any]: ... config = configure def cget(self, key: Literal["class", "cursor", "height", "orient", "style", "takefocus", "width"]) -> Any: ... + forget: Any def insert(self, pos, child, **kw): ... def pane(self, pane, option: Optional[Any] = ..., **kw): ... def sashpos(self, index, newpos: Optional[Any] = ...): ... - add = tkinter.PanedWindow.add - remove = tkinter.PanedWindow.remove - forget = tkinter.PanedWindow.forget - identify = tkinter.PanedWindow.identify - proxy = tkinter.PanedWindow.proxy - proxy_coord = tkinter.PanedWindow.proxy_coord - proxy_forget = tkinter.PanedWindow.proxy_forget - proxy_place = tkinter.PanedWindow.proxy_place - sash = tkinter.PanedWindow.sash - sash_coord = tkinter.PanedWindow.sash_coord - sash_mark = tkinter.PanedWindow.sash_mark - sash_place = tkinter.PanedWindow.sash_place - panecget = tkinter.PanedWindow.panecget - paneconfigure = tkinter.PanedWindow.paneconfigure - paneconfig = tkinter.PanedWindow.paneconfig - panes = tkinter.PanedWindow.panes PanedWindow = Panedwindow From b45827aa9db6e1325987ec31dde8d8942d896c81 Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 10 Aug 2020 12:48:44 +0300 Subject: [PATCH 21/45] fix Entry base classes --- stdlib/3/tkinter/ttk.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index dbb5d528b9f5..3712aa8ad556 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -176,7 +176,7 @@ _EntryOptionName = Literal[ "xscrollcommand", ] -class Entry(tkinter.Entry): +class Entry(Widget, tkinter.Entry): def __init__( self, master: Optional[tkinter.Misc] = ..., From f2cf579b59697cd6ab75fd1ea57819f012bd2609 Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 10 Aug 2020 12:51:41 +0300 Subject: [PATCH 22/45] add missing type ignore comments --- stdlib/3/tkinter/ttk.pyi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index 3712aa8ad556..f26ce82d7d4c 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -551,7 +551,7 @@ class Panedwindow(Widget, tkinter.PanedWindow): takefocus: tkinter._TakeFocusValue = ..., width: int = ..., ) -> None: ... - @overload + @overload # type: ignore def configure( self, cnf: Optional[Dict[str, Any]] = ..., @@ -566,8 +566,8 @@ class Panedwindow(Widget, tkinter.PanedWindow): def configure( self, cnf: Literal["class", "cursor", "height", "orient", "style", "takefocus", "width"] ) -> Tuple[str, str, str, Any, Any]: ... - config = configure - def cget(self, key: Literal["class", "cursor", "height", "orient", "style", "takefocus", "width"]) -> Any: ... + config = configure # type: ignore + def cget(self, key: Literal["class", "cursor", "height", "orient", "style", "takefocus", "width"]) -> Any: ... # type: ignore forget: Any def insert(self, pos, child, **kw): ... def pane(self, pane, option: Optional[Any] = ..., **kw): ... From b89e62bd5fbdeb20f562d23fc6abbecd94ccfdeb Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 10 Aug 2020 12:56:35 +0300 Subject: [PATCH 23/45] clean up repeated option name Literals into type aliases --- stdlib/3/tkinter/ttk.pyi | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index f26ce82d7d4c..1df12e21678b 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -495,6 +495,8 @@ class Menubutton(Widget): config = configure def cget(self, key: _MenubuttonOptionName) -> Any: ... +_NotebookOptionName = Literal["class", "cursor", "height", "padding", "style", "takefocus", "width"] + class Notebook(Widget): def __init__( self, @@ -521,11 +523,9 @@ class Notebook(Widget): width: int = ..., ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... @overload - def configure( - self, cnf: Literal["class", "cursor", "height", "padding", "style", "takefocus", "width"] - ) -> Tuple[str, str, str, Any, Any]: ... + def configure(self, cnf: _NotebookOptionName) -> Tuple[str, str, str, Any, Any]: ... config = configure - def cget(self, key: Literal["class", "cursor", "height", "padding", "style", "takefocus", "width"]) -> Any: ... + def cget(self, key: _NotebookOptionName) -> Any: ... def add(self, child, **kw): ... def forget(self, tab_id): ... def hide(self, tab_id): ... @@ -537,6 +537,8 @@ class Notebook(Widget): def tabs(self): ... def enable_traversal(self): ... +_PanedwindowOptionName = Literal["class", "cursor", "height", "orient", "style", "takefocus", "width"] + class Panedwindow(Widget, tkinter.PanedWindow): def __init__( self, @@ -563,11 +565,9 @@ class Panedwindow(Widget, tkinter.PanedWindow): width: int = ..., ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... @overload - def configure( - self, cnf: Literal["class", "cursor", "height", "orient", "style", "takefocus", "width"] - ) -> Tuple[str, str, str, Any, Any]: ... + def configure(self, cnf: _PanedwindowOptionName) -> Tuple[str, str, str, Any, Any]: ... config = configure # type: ignore - def cget(self, key: Literal["class", "cursor", "height", "orient", "style", "takefocus", "width"]) -> Any: ... # type: ignore + def cget(self, key: _PanedwindowOptionName) -> Any: ... # type: ignore forget: Any def insert(self, pos, child, **kw): ... def pane(self, pane, option: Optional[Any] = ..., **kw): ... @@ -725,6 +725,8 @@ class Scale(Widget, tkinter.Scale): def cget(self, key: _ScaleOptionName) -> Any: ... # type: ignore def get(self, x: Optional[Any] = ..., y: Optional[Any] = ...): ... +_ScrollbarOptionName = Literal["class", "command", "cursor", "orient", "style", "takefocus"] + class Scrollbar(Widget, tkinter.Scrollbar): def __init__( self, @@ -749,11 +751,11 @@ class Scrollbar(Widget, tkinter.Scrollbar): takefocus: tkinter._TakeFocusValue = ..., ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... @overload - def configure( - self, cnf: Literal["class", "command", "cursor", "orient", "style", "takefocus"] - ) -> Tuple[str, str, str, Any, Any]: ... + def configure(self, cnf: _ScrollbarOptionName) -> Tuple[str, str, str, Any, Any]: ... config = configure # type: ignore - def cget(self, key: Literal["class", "command", "cursor", "orient", "style", "takefocus"]) -> Any: ... # type: ignore + def cget(self, key: _ScrollbarOptionName) -> Any: ... # type: ignore + +_SeparatorOptionName = Literal["class", "cursor", "orient", "style", "takefocus"] class Separator(Widget): def __init__( @@ -777,9 +779,11 @@ class Separator(Widget): takefocus: tkinter._TakeFocusValue = ..., ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... @overload - def configure(self, cnf: Literal["class", "cursor", "orient", "style", "takefocus"]) -> Tuple[str, str, str, Any, Any]: ... + def configure(self, cnf: _SeparatorOptionName) -> Tuple[str, str, str, Any, Any]: ... config = configure - def cget(self, key: Literal["class", "cursor", "orient", "style", "takefocus"]) -> Any: ... + def cget(self, key: _SeparatorOptionName) -> Any: ... + +_SizegripOptionName = Literal["class", "cursor", "style", "takefocus"] class Sizegrip(Widget): def __init__( @@ -801,9 +805,9 @@ class Sizegrip(Widget): takefocus: tkinter._TakeFocusValue = ..., ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... @overload - def configure(self, cnf: Literal["class", "cursor", "style", "takefocus"]) -> Tuple[str, str, str, Any, Any]: ... + def configure(self, cnf: _SizegripOptionName) -> Tuple[str, str, str, Any, Any]: ... config = configure - def cget(self, key: Literal["class", "cursor", "style", "takefocus"]) -> Any: ... + def cget(self, key: _SizegripOptionName) -> Any: ... if sys.version_info >= (3, 7): _SpinboxOptionName = Literal[ From c42a72c53e552ab501455def07017181f0158e80 Mon Sep 17 00:00:00 2001 From: Akuli Date: Sun, 16 Aug 2020 19:56:28 +0300 Subject: [PATCH 24/45] delete tkinter options test --- .travis.yml | 7 - tests/tkinter_options_test.py | 366 ---------------------------------- 2 files changed, 373 deletions(-) delete mode 100755 tests/tkinter_options_test.py diff --git a/.travis.yml b/.travis.yml index 2dd5c38fc922..f49c35724eb8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,13 +16,6 @@ jobs: python: 3.8 install: pip install -U git+git://github.com/python/mypy script: ./tests/mypy_test.py --platform=linux - - name: "tkinter options test" - # Run this with latest python supported by typeshed - python: 3.8 - install: pip install -U typing-inspect - script: ./tests/tkinter_options_test.py - services: - - xvfb # needed to run tkinter - name: "mypy (Windows)" install: pip install -U git+git://github.com/python/mypy script: ./tests/mypy_test.py --platform=win32 diff --git a/tests/tkinter_options_test.py b/tests/tkinter_options_test.py deleted file mode 100755 index 169fdf1788c6..000000000000 --- a/tests/tkinter_options_test.py +++ /dev/null @@ -1,366 +0,0 @@ -#!/usr/bin/env python3 -""" -This script checks that the widget options specified in tkinter stubs are -actually supported by the Tk widgets at runtime. Unfortunately stubtest isn't -sufficient for checking this because the python code uses **kwargs for the -options. This script creates values that those options can be set to according -to the type hints and then tries to set the options. For example, if the stubs -say that an option should be bool, then this script sets it to True and then to -False, and fails if an exception is raised. - -In particular, this script does NOT check whether all options supported by the -widgets are also supported by the stubs. This way CI builds don't break when a -new release of Tcl/Tk with new widget options comes out. There's a big comment -in tkinter/__init__.pyi describing how to add new options to the stubs. - -It's best to run this file only in the latest supported Python version -(corresponding to latest Tcl/Tk version): - - The latest Tcl/Tk includes all widget options of the previous Tcl/Tk - releases. There's currently no way to say that a widget option is available - only starting at Tk version X.Y, and tkinter generally doesn't make a big - effort to document those things either. The CI build fails if a widget - option is missing from the Tcl/Tk that tkinter is using. - - It's nice to use new Python features in this file. - -This script parses a subset of Python's type hint syntax with ast. Only -configure() methods defined in tkinter's widget classes are checked. -""" - -import ast -import builtins -import itertools -import pathlib -import sys -import tkinter -import tkinter.font -import tkinter.ttk -from typing import Dict, Iterable, List, Sequence, Tuple, Union, cast - - -def create_child_widgets(parent: tkinter.Misc) -> Dict[str, tkinter.Misc]: - # TODO: remove ignore comment when image_names() has type hints - img: str = tkinter.image_names()[0] # type: ignore - - # image can't be given to __init__, must config afterwards - nonttk_optionmenu = tkinter.OptionMenu(parent, tkinter.StringVar(), "foo") - ttk_optionmenu = tkinter.ttk.OptionMenu(parent, tkinter.StringVar(), "foo") - nonttk_optionmenu.config(image=img) - ttk_optionmenu.config(image=img) - - return { - # width and height can be arbitrary _ScreenUnits only if these widgets have an image - "tkinter.Button": tkinter.Button(image=img), - "tkinter.Checkbutton": tkinter.Checkbutton(image=img), - "tkinter.Label": tkinter.Label(image=img), - "tkinter.Menubutton": tkinter.Menubutton(image=img), - "tkinter.OptionMenu": nonttk_optionmenu, - "tkinter.Radiobutton": tkinter.Radiobutton(image=img), - "tkinter.ttk.Button": tkinter.ttk.Button(image=img), - "tkinter.ttk.Checkbutton": tkinter.ttk.Checkbutton(image=img), - "tkinter.ttk.Label": tkinter.ttk.Label(image=img), - "tkinter.ttk.Menubutton": tkinter.ttk.Menubutton(image=img), - "tkinter.ttk.OptionMenu": ttk_optionmenu, - "tkinter.ttk.Radiobutton": tkinter.ttk.Radiobutton(image=img), - # these don't need image - "tkinter.Canvas": tkinter.Canvas(), - "tkinter.Entry": tkinter.Entry(), - "tkinter.Frame": tkinter.Frame(), - "tkinter.LabelFrame": tkinter.LabelFrame(), - "tkinter.Listbox": tkinter.Listbox(), - "tkinter.Menu": tkinter.Menu(), - "tkinter.Message": tkinter.Message(), - "tkinter.PanedWindow": tkinter.PanedWindow(), - "tkinter.Scale": tkinter.Scale(), - "tkinter.Scrollbar": tkinter.Scrollbar(), - "tkinter.Spinbox": tkinter.Spinbox(), - "tkinter.Text": tkinter.Text(), - "tkinter.ttk.Combobox": tkinter.ttk.Combobox(), - "tkinter.ttk.Entry": tkinter.ttk.Entry(), - "tkinter.ttk.Frame": tkinter.ttk.Frame(), - "tkinter.ttk.Labelframe": tkinter.ttk.Labelframe(), - "tkinter.ttk.Notebook": tkinter.ttk.Notebook(), - "tkinter.ttk.Panedwindow": tkinter.ttk.Panedwindow(), - "tkinter.ttk.Progressbar": tkinter.ttk.Progressbar(), - "tkinter.ttk.Scale": tkinter.ttk.Scale(), - "tkinter.ttk.Scrollbar": tkinter.ttk.Scrollbar(), - "tkinter.ttk.Separator": tkinter.ttk.Separator(), - "tkinter.ttk.Sizegrip": tkinter.ttk.Sizegrip(), - "tkinter.ttk.Spinbox": tkinter.ttk.Spinbox(), - "tkinter.ttk.Treeview": tkinter.ttk.Treeview(), - } - - -# useful for detecting Tuple[foo, ...] -def is_dotdotdot(node: ast.expr) -> bool: - return isinstance(node, ast.Constant) and node.value is Ellipsis - - -# attribute_string() --> 'tkinter.Label' -def attribute_string(typehint: ast.expr) -> str: - if isinstance(typehint, ast.Name): - return typehint.id - if isinstance(typehint, ast.Attribute): - return attribute_string(typehint.value) + "." + typehint.attr - raise NotImplementedError(typehint) - - -# Convert type hint into values that should be possible to set to a widget that -# uses the type hint. Examples: -# Literal['foo', 'bar'] --> ['foo', 'bar'] -# bool --> [True, False] -# float --> [123, 456.789] -class ValueGenerator: - def __init__( - self, import_mapping: Dict[str, str], widget: tkinter.Misc, modulename: str, images: List[tkinter.Image] - ) -> None: - self.import_mapping = import_mapping - self.widget = widget - self.modulename = modulename - self.images = images - - def _full_name(self, name: str) -> str: - first_part, possible_dot, possible_suffix = name.partition(".") - try: - # tkinter.font --> self.import_mapping['tkinter'] + '.font' - return self.import_mapping[first_part] + possible_dot + possible_suffix - except KeyError: - return self.modulename + "." + name - - def _get_list_values(self, item_type: ast.expr) -> Sequence[List[object]]: - list_value = list(self.get(item_type)) - return [[], list_value, list_value[::-1], list_value * 2] - - def get(self, typehint: ast.expr) -> Sequence[object]: - if isinstance(typehint, (ast.Name, ast.Attribute)): - full_name = self._full_name(attribute_string(typehint)) - if full_name in {"builtins.str", "typing.Any"}: - # Unfortunately str doesn't always mean that *any* string is - # acceptable. Not much can be done here. - return [] - if full_name == "builtins.bool": - return [True, False] - if full_name == "builtins.int": - return [123] - if full_name == "builtins.float": - return [123, 456.789] - if full_name == "tkinter._Anchor": - return ["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] - if full_name == "tkinter._Bitmap": - return ["hourglass"] - if full_name == "tkinter._Color": - return ["red", "#f00", "#ff0000"] - compound_list = ["top", "left", "center", "right", "bottom", "none"] - if full_name == "tkinter._Compound": - return compound_list - if full_name == "tkinter._Cursor": - return ["hand2", ("hand2", "red"), ("hand2", "red", "blue")] - if full_name == "tkinter._ImageSpec": - # TODO: remove ignore comment when image_names() has type hints - image_names: Tuple[str] = tkinter.image_names() # type: ignore - return cast(List[object], self.images) + list(image_names) - if full_name == "tkinter._Padding": - return ["2c", ("2c",), ("2c", "3c"), ("2c", "3c", "4c"), ("2c", "3c", "4c", "5c")] - if full_name == "tkinter._Relief": - return ["flat", "raised", "sunken", "groove", "solid", "ridge"] - if full_name == "tkinter._ScreenUnits": - return [12, 34.56, "78.9c"] - if full_name == "tkinter._TakeFocusValue": - return [True, False, "", print] - if full_name == "tkinter.ttk._TtkCompound": - return compound_list + ["text", "image"] - if full_name == "tkinter._FontDescription": - return [("Helvetica", 12, "bold"), tkinter.font.Font()] - if full_name == "tkinter.Variable": - return [tkinter.Variable(), tkinter.StringVar(), tkinter.IntVar(), tkinter.DoubleVar()] - if full_name == "tkinter.DoubleVar": - return [tkinter.DoubleVar()] - if full_name in {"tkinter._XYScrollCommand", "tkinter._EntryValidateCommand", "tkinter._ButtonCommand"}: - # Tkinter converts all callables to tcl commands the same way. - return [print] - - # For widgets, return a child widget of the given widget. Some - # options require this and others don't. - child_widgets = create_child_widgets(self.widget) - if full_name in child_widgets: - return [child_widgets[full_name]] - if full_name == "tkinter.Misc": - # Any widget that can be put inside another widget, except Menu - # which causes issues for whatever reason. - return [widget for widget in child_widgets.values() if not isinstance(widget, tkinter.Menu)] - - if isinstance(typehint, ast.Subscript): # typehint.value[typehint.slice] - assert isinstance(typehint.value, (ast.Name, ast.Attribute)) - assert isinstance(typehint.slice, ast.Index) - full_name = self._full_name(attribute_string(typehint.value)) - - if full_name == "typing_extensions.Literal": - if isinstance(typehint.slice.value, ast.Tuple): - tuple_content = typehint.slice.value.elts - else: - tuple_content = [typehint.slice.value] - assert all(isinstance(node, ast.Str) for node in tuple_content) - return [cast(ast.Str, node).s for node in tuple_content] - - if full_name == "typing.Callable": - return [print] - - if full_name == "typing.Union": - assert isinstance(typehint.slice.value, ast.Tuple) - result: List[object] = [] - for unioned_item in typehint.slice.value.elts: - result.extend(self.get(unioned_item)) - return result - - if full_name == "typing.Tuple": - if isinstance(typehint.slice.value, ast.Tuple): - if len(typehint.slice.value.elts) == 2 and is_dotdotdot(typehint.slice.value.elts[1]): - # Tuple[foo, ...] is similar to List[foo] - list_values = self._get_list_values(typehint.slice.value.elts[0]) - return [tuple(lizt) for lizt in list_values] - values_for_each_element = [self.get(element) for element in typehint.slice.value.elts] - else: - values_for_each_element = [self.get(typehint.slice.value)] - return list(itertools.product(*values_for_each_element)) - - if full_name in {"tkinter._TkinterSequence", "tkinter.ttk._TkinterSequence"}: - TkinterSequence = Union[List[object], Tuple[object, ...]] - lists: List[TkinterSequence] = list(self._get_list_values(typehint.slice.value)) - tuples: List[TkinterSequence] = [tuple(lizt) for lizt in lists] - return lists + tuples - - # If this error gets raised with an error message mentioning Ellipsis, - # then make sure that you're running Python 3.8 or newer. Seems like - # something changed in the ast. - raise NotImplementedError(typehint, ast.dump(typehint)) - - -# from enum import Enum --> {'Enum': 'enum.Enum'} -def get_import_mapping(parsed: ast.Module) -> Dict[str, str]: - result = {} - - # similar to: from builtins import * - for name in dir(builtins): - if not name.startswith("_"): - result[name] = "builtins." + name - - for stmt in parsed.body: - if isinstance(stmt, ast.ImportFrom): - for name_node in stmt.names: - assert stmt.module is not None - no_dots = name_node.name - result[no_dots] = stmt.module + "." + no_dots - elif isinstance(stmt, ast.Import): - for name_node in stmt.names: - assert name_node.asname is None - # import tkinter.font --> result['tkinter'] = 'tkinter' - first_part, *junk = name_node.name.split(".") - result[first_part] = first_part - - return result - - -def find_all_classdefs(parsed: ast.Module) -> Iterable[ast.ClassDef]: - for node in ast.walk(parsed): # handle classes e.g. under 'if sys.version_info >= bla' - if isinstance(node, ast.ClassDef): - yield node - - -def is_configure_method_with_options_as_kwargs(node: ast.stmt) -> bool: - if (not isinstance(node, ast.FunctionDef)) or node.name != "configure": - return False - - self, cnf = node.args.args # positional args - assert cnf.arg == "cnf" - assert cnf.annotation is not None - - [expected_cnf_annotation] = ast.parse("Optional[Dict[str, Any]]").body - assert isinstance(expected_cnf_annotation, ast.Expr) - return ast.dump(cnf.annotation) == ast.dump(expected_cnf_annotation.value) - - -def find_configure_method(classname: str, classdefs: Dict[str, ast.ClassDef]) -> ast.FunctionDef: - if classname in "tkinter.OptionMenu": - # OptionMenus get their configure methods with inheritance - classdef = classdefs["tkinter.Menubutton"] # inherited from here - elif classname in "tkinter.ttk.OptionMenu": - classdef = classdefs["tkinter.ttk.Menubutton"] - elif classname == "tkinter.Toplevel": - # Toplevel does 'configure = Tk.configure' - classdef = classdefs["tkinter.Tk"] - else: - classdef = classdefs[classname] - - [configure] = filter(is_configure_method_with_options_as_kwargs, classdef.body) - assert isinstance(configure, ast.FunctionDef) - return configure - - -def get_tkinter_dir() -> pathlib.Path: - tests_dir = pathlib.Path(__file__).parent - project_dir = tests_dir.parent - return project_dir / "stdlib" / "3" / "tkinter" - - -def main() -> None: - # tkinter.Tk must be created first, other things use that implicitly - root = tkinter.Tk() - - print("Python version:", repr(sys.version)) - print("Tcl version:", root.tk.eval("info patchlevel")) - - test_widgets: Dict[str, tkinter.Misc] = { - "tkinter.Tk": root, - "tkinter.Toplevel": tkinter.Toplevel(), - **create_child_widgets(root), - } - - # This must be somewhat long-living because tkinter uses __del__ hack. - test_images = [tkinter.BitmapImage(), tkinter.PhotoImage()] - - module_list = [ - ("tkinter", get_tkinter_dir() / "__init__.pyi"), - ("tkinter.ttk", get_tkinter_dir() / "ttk.pyi"), - ] - - import_mappings: Dict[str, Dict[str, str]] = {} - classdefs: Dict[str, ast.ClassDef] = {} - - for modulename, path in module_list: - print("Parsing", path) - parsed = ast.parse(path.read_text()) - import_mappings[modulename] = get_import_mapping(parsed) - classdefs.update({modulename + "." + classdef.name: classdef for classdef in find_all_classdefs(parsed)}) - - for classname, widget in test_widgets.items(): - print("Testing", classname) - modulename = classname.rsplit(".", 1)[0] - value_getter = ValueGenerator(import_mappings[modulename], widget, modulename, test_images) - configure = find_configure_method(classname, classdefs) - - for arg in configure.args.kwonlyargs: - option_name = arg.arg.rstrip("_") # class_=foo --> widget['class'] = foo - - assert arg.annotation is not None - values = value_getter.get(arg.annotation) - if values: - for value in values: - print(f"widget[{option_name!r}] = {value!r}") - try: - widget[option_name] = value - except tkinter.TclError as e: - # these errors indicate that types were correct, - # but the values were wrong - assert str(e) in {"-to value must be greater than -from value", "Column index 123 out of bounds"} - else: - # Can't test option by assigning values to it according to - # type hint, but this ensures that it exists and isn't - # __init__ only. - print(f"widget[{option_name!r}] gets assigned to itself") - widget[option_name] = widget[option_name] - - print() - print("ok") - - -if __name__ == "__main__": - main() From d41ba5f0b14815c2daee71dfd16ead054f56faa6 Mon Sep 17 00:00:00 2001 From: Akuli Date: Sun, 16 Aug 2020 19:57:34 +0300 Subject: [PATCH 25/45] revert pytype_test changes --- tests/pytype_test.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/tests/pytype_test.py b/tests/pytype_test.py index e81c341959a2..79a9176b61af 100755 --- a/tests/pytype_test.py +++ b/tests/pytype_test.py @@ -40,19 +40,12 @@ def main() -> None: python36_exe=args.python36_exe, print_stderr=args.print_stderr, dry_run=args.dry_run, - verbose=args.verbose, ) def create_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description="Pytype/typeshed tests.") parser.add_argument("-n", "--dry-run", action="store_true", default=False, help="Don't actually run tests") - parser.add_argument( - "--verbose", - action="store_true", - default=False, - help="Print all filenames being tested (helps to find code that causes pytype to hang)", - ) # Default to '' so that symlinking typeshed subdirs in cwd will work. parser.add_argument("--typeshed-location", type=str, default="", help="Path to typeshed installation.") # Set to true to print a stack trace every time an exception is thrown. @@ -206,17 +199,13 @@ def run_all_tests( python27_exe: str, python36_exe: str, print_stderr: bool, - dry_run: bool, - verbose: bool, + dry_run: bool ) -> None: bad = [] errors = 0 total_tests = len(files_to_test) print("Testing files with pytype...") for i, (f, version) in enumerate(files_to_test): - if verbose: - print(f"{f} with Python {version}") - stderr = ( run_pytype( filename=f, @@ -235,7 +224,7 @@ def run_all_tests( bad.append((_get_relative(f), stacktrace_final_line)) runs = i + 1 - if runs % 25 == 0 and not verbose: + if runs % 25 == 0: print(" {:3d}/{:d} with {:3d} errors".format(runs, total_tests, errors)) print("Ran pytype with {:d} pyis, got {:d} errors.".format(total_tests, errors)) From 68c318f2aa6c807a175c1cbc8165e52b921ff1ee Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 17 Aug 2020 12:14:11 +0300 Subject: [PATCH 26/45] add border as an alias for borderwidth --- stdlib/3/tkinter/__init__.pyi | 60 +++++++++++++++++++++++++++++++++-- stdlib/3/tkinter/ttk.pyi | 6 +++- 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/stdlib/3/tkinter/__init__.pyi b/stdlib/3/tkinter/__init__.pyi index a3f350f774dd..70b5ccee1093 100644 --- a/stdlib/3/tkinter/__init__.pyi +++ b/stdlib/3/tkinter/__init__.pyi @@ -518,6 +518,7 @@ _TkOptionName = Literal[ "background", "bd", "bg", + "border", "borderwidth", "class", "colormap", @@ -557,6 +558,7 @@ class Tk(Misc, Wm): background: _Color = ..., bd: _ScreenUnits = ..., bg: _Color = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., cursor: _Cursor = ..., height: _ScreenUnits = ..., @@ -789,6 +791,7 @@ class Toplevel(BaseWidget, Wm): background: _Color = ..., bd: _ScreenUnits = ..., bg: _Color = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., class_: str = ..., colormap: Union[Literal["new", ""], Misc] = ..., @@ -818,16 +821,17 @@ _ButtonOptionName = Literal[ "activeforeground", "anchor", "background", - "bd", - "bg", + "bd", # same as borderwidth + "bg", # same as background "bitmap", + "border", # same as borderwidth "borderwidth", "command", "compound", "cursor", "default", "disabledforeground", - "fg", + "fg", # same as foreground "font", "foreground", "height", @@ -864,6 +868,7 @@ class Button(Widget): bd: _ScreenUnits = ..., bg: _Color = ..., bitmap: _Bitmap = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., command: _ButtonCommand = ..., compound: _Compound = ..., @@ -910,6 +915,7 @@ class Button(Widget): bd: _ScreenUnits = ..., bg: _Color = ..., bitmap: _Bitmap = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., command: _ButtonCommand = ..., compound: _Compound = ..., @@ -950,6 +956,7 @@ _CanvasOptionName = Literal[ "background", "bd", "bg", + "border", "borderwidth", "closeenough", "confine", @@ -986,6 +993,7 @@ class Canvas(Widget, XView, YView): background: _Color = ..., bd: _ScreenUnits = ..., bg: _Color = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., closeenough: float = ..., confine: bool = ..., @@ -1025,6 +1033,7 @@ class Canvas(Widget, XView, YView): background: _Color = ..., bd: _ScreenUnits = ..., bg: _Color = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., closeenough: float = ..., confine: bool = ..., @@ -1134,6 +1143,7 @@ _CheckbuttonOptionName = Literal[ "bd", "bg", "bitmap", + "border", "borderwidth", "command", "compound", @@ -1183,6 +1193,7 @@ class Checkbutton(Widget): bd: _ScreenUnits = ..., bg: _Color = ..., bitmap: _Bitmap = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., command: _ButtonCommand = ..., compound: _Compound = ..., @@ -1240,6 +1251,7 @@ class Checkbutton(Widget): bd: _ScreenUnits = ..., bg: _Color = ..., bitmap: _Bitmap = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., command: _ButtonCommand = ..., compound: _Compound = ..., @@ -1289,6 +1301,7 @@ _EntryOptionName = Literal[ "background", "bd", "bg", + "border", "borderwidth", "cursor", "disabledbackground", @@ -1331,6 +1344,7 @@ class Entry(Widget, XView): background: _Color = ..., bd: _ScreenUnits = ..., bg: _Color = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., cursor: _Cursor = ..., disabledbackground: _Color = ..., @@ -1371,6 +1385,7 @@ class Entry(Widget, XView): background: _Color = ..., bd: _ScreenUnits = ..., bg: _Color = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., cursor: _Cursor = ..., disabledbackground: _Color = ..., @@ -1431,6 +1446,7 @@ _FrameOptionName = Literal[ "background", "bd", "bg", + "border", "borderwidth", "class", "colormap", @@ -1457,6 +1473,7 @@ class Frame(Widget): background: _Color = ..., bd: _ScreenUnits = ..., bg: _Color = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., class_: str = ..., colormap: Union[Literal["new", ""], Misc] = ..., @@ -1481,6 +1498,7 @@ class Frame(Widget): background: _Color = ..., bd: _ScreenUnits = ..., bg: _Color = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., cursor: _Cursor = ..., height: _ScreenUnits = ..., @@ -1506,6 +1524,7 @@ _LabelOptionName = Literal[ "bd", "bg", "bitmap", + "border", "borderwidth", "compound", "cursor", @@ -1544,6 +1563,7 @@ class Label(Widget): bd: _ScreenUnits = ..., bg: _Color = ..., bitmap: _Bitmap = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., compound: _Compound = ..., cursor: _Cursor = ..., @@ -1580,6 +1600,7 @@ class Label(Widget): bd: _ScreenUnits = ..., bg: _Color = ..., bitmap: _Bitmap = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., compound: _Compound = ..., cursor: _Cursor = ..., @@ -1614,6 +1635,7 @@ _ListboxOptionName = Literal[ "background", "bd", "bg", + "border", "borderwidth", "cursor", "disabledforeground", @@ -1650,6 +1672,7 @@ class Listbox(Widget, XView, YView): background: _Color = ..., bd: _ScreenUnits = ..., bg: _Color = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., cursor: _Cursor = ..., disabledforeground: _Color = ..., @@ -1698,6 +1721,7 @@ class Listbox(Widget, XView, YView): background: _Color = ..., bd: _ScreenUnits = ..., bg: _Color = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., cursor: _Cursor = ..., disabledforeground: _Color = ..., @@ -1758,6 +1782,7 @@ _MenuOptionName = Literal[ "background", "bd", "bg", + "border", "borderwidth", "cursor", "disabledforeground", @@ -1786,6 +1811,7 @@ class Menu(Widget): background: _Color = ..., bd: _ScreenUnits = ..., bg: _Color = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., cursor: _Cursor = ..., disabledforeground: _Color = ..., @@ -1815,6 +1841,7 @@ class Menu(Widget): background: _Color = ..., bd: _ScreenUnits = ..., bg: _Color = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., cursor: _Cursor = ..., disabledforeground: _Color = ..., @@ -1870,6 +1897,7 @@ _MenubuttonOptionName = Literal[ "bd", "bg", "bitmap", + "border", "borderwidth", "compound", "cursor", @@ -1911,6 +1939,7 @@ class Menubutton(Widget): bd: _ScreenUnits = ..., bg: _Color = ..., bitmap: _Bitmap = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., compound: _Compound = ..., cursor: _Cursor = ..., @@ -1950,6 +1979,7 @@ class Menubutton(Widget): bd: _ScreenUnits = ..., bg: _Color = ..., bitmap: _Bitmap = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., compound: _Compound = ..., cursor: _Cursor = ..., @@ -1988,6 +2018,7 @@ _MessageOptionName = Literal[ "background", "bd", "bg", + "border", "borderwidth", "cursor", "fg", @@ -2017,6 +2048,7 @@ class Message(Widget): background: _Color = ..., bd: _ScreenUnits = ..., bg: _Color = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., cursor: _Cursor = ..., fg: _Color = ..., @@ -2045,6 +2077,7 @@ class Message(Widget): background: _Color = ..., bd: _ScreenUnits = ..., bg: _Color = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., cursor: _Cursor = ..., fg: _Color = ..., @@ -2075,6 +2108,7 @@ _RadiobuttonOptionName = Literal[ "bd", "bg", "bitmap", + "border", "borderwidth", "command", "compound", @@ -2123,6 +2157,7 @@ class Radiobutton(Widget): bd: _ScreenUnits = ..., bg: _Color = ..., bitmap: _Bitmap = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., command: _ButtonCommand = ..., compound: _Compound = ..., @@ -2169,6 +2204,7 @@ class Radiobutton(Widget): bd: _ScreenUnits = ..., bg: _Color = ..., bitmap: _Bitmap = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., command: _ButtonCommand = ..., compound: _Compound = ..., @@ -2218,6 +2254,7 @@ _ScaleOptionName = Literal[ "bd", "bg", "bigincrement", + "border", "borderwidth", "command", "cursor", @@ -2259,6 +2296,7 @@ class Scale(Widget): bd: _ScreenUnits = ..., bg: _Color = ..., bigincrement: float = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., # don't know why the callback gets string instead of float command: Union[str, Callable[[str], None]] = ..., @@ -2299,6 +2337,7 @@ class Scale(Widget): bd: _ScreenUnits = ..., bg: _Color = ..., bigincrement: float = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., command: Union[str, Callable[[str], None]] = ..., cursor: _Cursor = ..., @@ -2343,6 +2382,7 @@ _ScrollbarOptionName = Literal[ "background", "bd", "bg", + "border", "borderwidth", "command", "cursor", @@ -2371,6 +2411,7 @@ class Scrollbar(Widget): background: _Color = ..., bd: _ScreenUnits = ..., bg: _Color = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., # There are many ways how the command may get called. Search for # 'SCROLLING COMMANDS' in scrollbar man page. There doesn't seem to @@ -2401,6 +2442,7 @@ class Scrollbar(Widget): background: _Color = ..., bd: _ScreenUnits = ..., bg: _Color = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., command: Union[Callable[..., Optional[Tuple[float, float]]], str] = ..., cursor: _Cursor = ..., @@ -2434,6 +2476,7 @@ _TextOptionName = Literal[ "bd", "bg", "blockcursor", + "border", "borderwidth", "cursor", "endline", @@ -2486,6 +2529,7 @@ class Text(Widget, XView, YView): bd: _ScreenUnits = ..., bg: _Color = ..., blockcursor: bool = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., cursor: _Cursor = ..., endline: Union[int, Literal[""]] = ..., @@ -2540,6 +2584,7 @@ class Text(Widget, XView, YView): bd: _ScreenUnits = ..., bg: _Color = ..., blockcursor: bool = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., cursor: _Cursor = ..., endline: Union[int, Literal[""]] = ..., @@ -2722,6 +2767,7 @@ _SpinboxOptionName = Literal[ "background", "bd", "bg", + "border", "borderwidth", "buttonbackground", "buttoncursor", @@ -2777,6 +2823,7 @@ class Spinbox(Widget, XView): background: _Color = ..., bd: _ScreenUnits = ..., bg: _Color = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., buttonbackground: _Color = ..., buttoncursor: _Cursor = ..., @@ -2831,6 +2878,7 @@ class Spinbox(Widget, XView): background: _Color = ..., bd: _ScreenUnits = ..., bg: _Color = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., buttonbackground: _Color = ..., buttoncursor: _Cursor = ..., @@ -2904,6 +2952,7 @@ _LabelFrameOptionName = Literal[ "background", "bd", "bg", + "border", "borderwidth", "class", "colormap", @@ -2935,6 +2984,7 @@ class LabelFrame(Widget): background: _Color = ..., bd: _ScreenUnits = ..., bg: _Color = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., class_: str = ..., colormap: Union[Literal["new", ""], Misc] = ..., @@ -2965,6 +3015,7 @@ class LabelFrame(Widget): background: _Color = ..., bd: _ScreenUnits = ..., bg: _Color = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., cursor: _Cursor = ..., fg: _Color = ..., @@ -2992,6 +3043,7 @@ _PanedWindowOptionName = Literal[ "background", "bd", "bg", + "border", "borderwidth", "cursor", "handlepad", @@ -3020,6 +3072,7 @@ class PanedWindow(Widget): background: _Color = ..., bd: _ScreenUnits = ..., bg: _Color = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., cursor: _Cursor = ..., handlepad: _ScreenUnits = ..., @@ -3046,6 +3099,7 @@ class PanedWindow(Widget): background: _Color = ..., bd: _ScreenUnits = ..., bg: _Color = ..., + border: _ScreenUnits = ..., borderwidth: _ScreenUnits = ..., cursor: _Cursor = ..., handlepad: _ScreenUnits = ..., diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index 1df12e21678b..5f56708c32a3 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -281,13 +281,16 @@ class Combobox(Entry): def current(self, newindex: Optional[Any] = ...): ... def set(self, value): ... -_FrameOptionName = Literal["borderwidth", "class", "cursor", "height", "padding", "relief", "style", "takefocus", "width"] +_FrameOptionName = Literal[ + "border", "borderwidth", "class", "cursor", "height", "padding", "relief", "style", "takefocus", "width" +] class Frame(Widget): def __init__( self, master: Optional[tkinter.Misc] = ..., *, + border: tkinter._ScreenUnits = ..., borderwidth: tkinter._ScreenUnits = ..., class_: str = ..., cursor: tkinter._Cursor = ..., @@ -303,6 +306,7 @@ class Frame(Widget): self, cnf: Optional[Dict[str, Any]] = ..., *, + border: tkinter._ScreenUnits = ..., borderwidth: tkinter._ScreenUnits = ..., cursor: tkinter._Cursor = ..., height: tkinter._ScreenUnits = ..., From 88ac630f873a5e61918054d5a08199ab5de35c8d Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 17 Aug 2020 12:25:43 +0300 Subject: [PATCH 27/45] accept str for _TkinterSequence[str] when appropriate --- stdlib/3/tkinter/__init__.pyi | 2 +- stdlib/3/tkinter/ttk.pyi | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/stdlib/3/tkinter/__init__.pyi b/stdlib/3/tkinter/__init__.pyi index 70b5ccee1093..7adc422e6e11 100644 --- a/stdlib/3/tkinter/__init__.pyi +++ b/stdlib/3/tkinter/__init__.pyi @@ -97,7 +97,7 @@ _EntryValidateCommand = Union[ # seems to also load tkinter.font. That's not how it actually works, but # unfortunately not much can be done about it. https://github.com/python/typeshed/pull/4346 _FontDescription = Union[ - str, font.Font, Tuple[str, int], Tuple[str, int, _TkinterSequence[str]], + str, font.Font, Tuple[str, int], Tuple[str, int, str], Tuple[str, int, _TkinterSequence[str]], ] # 'FONT DESCRIPTIONS' in 'font' manual page _ImageSpec = Union[Image, str] # str can be from e.g. tkinter.image_names() _Padding = Union[ diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index 5f56708c32a3..a7c1f54c47dd 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -893,9 +893,9 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): master: Optional[tkinter.Misc] = ..., *, class_: str = ..., - columns: tkinter._TkinterSequence[str] = ..., + columns: Union[str, tkinter._TkinterSequence[str]] = ..., cursor: tkinter._Cursor = ..., - displaycolumns: Union[tkinter._TkinterSequence[str], tkinter._TkinterSequence[int], Literal["#all"]] = ..., + displaycolumns: Union[str, tkinter._TkinterSequence[str], tkinter._TkinterSequence[int], Literal["#all"]] = ..., height: int = ..., padding: tkinter._Padding = ..., selectmode: Literal["extended", "browse", "none"] = ..., @@ -914,9 +914,9 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): self, cnf: Optional[Dict[str, Any]] = ..., *, - columns: tkinter._TkinterSequence[str] = ..., + columns: Union[str, tkinter._TkinterSequence[str]] = ..., cursor: tkinter._Cursor = ..., - displaycolumns: Union[tkinter._TkinterSequence[str], tkinter._TkinterSequence[int], Literal["#all"]] = ..., + displaycolumns: Union[str, tkinter._TkinterSequence[str], tkinter._TkinterSequence[int], Literal["#all"]] = ..., height: int = ..., padding: tkinter._Padding = ..., selectmode: Literal["extended", "browse", "none"] = ..., From d26428c6ef40ab82f95032661ba0d629248e740f Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 17 Aug 2020 13:00:19 +0300 Subject: [PATCH 28/45] add screen option to Tk and Toplevel --- stdlib/3/tkinter/__init__.pyi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stdlib/3/tkinter/__init__.pyi b/stdlib/3/tkinter/__init__.pyi index 7adc422e6e11..390a0552142d 100644 --- a/stdlib/3/tkinter/__init__.pyi +++ b/stdlib/3/tkinter/__init__.pyi @@ -532,6 +532,7 @@ _TkOptionName = Literal[ "padx", "pady", "relief", + "screen", # can't be changed after creating widget "takefocus", "use", "visual", @@ -805,6 +806,7 @@ class Toplevel(BaseWidget, Wm): padx: _ScreenUnits = ..., pady: _ScreenUnits = ..., relief: _Relief = ..., + screen: str = ..., takefocus: _TakeFocusValue = ..., use: int = ..., visual: Union[str, Tuple[str, int]] = ..., From c9033ed0d5c221f88020e17ad39c832537c45df2 Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 17 Aug 2020 13:11:11 +0300 Subject: [PATCH 29/45] add undocumented offset option --- stdlib/3/tkinter/__init__.pyi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stdlib/3/tkinter/__init__.pyi b/stdlib/3/tkinter/__init__.pyi index 390a0552142d..504f7d512ff5 100644 --- a/stdlib/3/tkinter/__init__.pyi +++ b/stdlib/3/tkinter/__init__.pyi @@ -972,6 +972,7 @@ _CanvasOptionName = Literal[ "insertofftime", "insertontime", "insertwidth", + "offset", "relief", "scrollregion", "selectbackground", @@ -1011,6 +1012,7 @@ class Canvas(Widget, XView, YView): insertofftime: int = ..., insertontime: int = ..., insertwidth: _ScreenUnits = ..., + offset: Any, # undocumented relief: _Relief = ..., # Setting scrollregion to None doesn't reset it back to empty, # but setting it to () does. @@ -1049,6 +1051,7 @@ class Canvas(Widget, XView, YView): insertofftime: int = ..., insertontime: int = ..., insertwidth: _ScreenUnits = ..., + offset: Any, # undocumented relief: _Relief = ..., scrollregion: Union[Tuple[_ScreenUnits, _ScreenUnits, _ScreenUnits, _ScreenUnits], Tuple[()]] = ..., selectbackground: _Color = ..., From e9c96fca0ba7a9c6241d88132551eeba9048ee2c Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 17 Aug 2020 13:15:56 +0300 Subject: [PATCH 30/45] support invcmd and vcmd --- stdlib/3/tkinter/__init__.pyi | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/stdlib/3/tkinter/__init__.pyi b/stdlib/3/tkinter/__init__.pyi index 504f7d512ff5..da7a31f966a6 100644 --- a/stdlib/3/tkinter/__init__.pyi +++ b/stdlib/3/tkinter/__init__.pyi @@ -1324,6 +1324,7 @@ _EntryOptionName = Literal[ "insertontime", "insertwidth", "invalidcommand", + "invcmd", # same as invalidcommand "justify", "readonlybackground", "relief", @@ -1336,6 +1337,7 @@ _EntryOptionName = Literal[ "textvariable", "validate", "validatecommand", + "vcmd", # same as validatecommand "width", "xscrollcommand", ] @@ -1367,6 +1369,7 @@ class Entry(Widget, XView): insertontime: int = ..., insertwidth: _ScreenUnits = ..., invalidcommand: _EntryValidateCommand = ..., + invcmd: _EntryValidateCommand = ..., justify: Literal["left", "center", "right"] = ..., readonlybackground: _Color = ..., relief: _Relief = ..., @@ -1379,6 +1382,7 @@ class Entry(Widget, XView): textvariable: Variable = ..., validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., validatecommand: _EntryValidateCommand = ..., + vcmd: _EntryValidateCommand = ..., width: int = ..., xscrollcommand: _XYScrollCommand = ..., ) -> None: ... @@ -1408,6 +1412,7 @@ class Entry(Widget, XView): insertontime: int = ..., insertwidth: _ScreenUnits = ..., invalidcommand: _EntryValidateCommand = ..., + invcmd: _EntryValidateCommand = ..., justify: Literal["left", "center", "right"] = ..., readonlybackground: _Color = ..., relief: _Relief = ..., @@ -1420,6 +1425,7 @@ class Entry(Widget, XView): textvariable: Variable = ..., validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., validatecommand: _EntryValidateCommand = ..., + vcmd: _EntryValidateCommand = ..., width: int = ..., xscrollcommand: _XYScrollCommand = ..., ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... @@ -2798,6 +2804,7 @@ _SpinboxOptionName = Literal[ "insertontime", "insertwidth", "invalidcommand", + "invcmd", "justify", "readonlybackground", "relief", @@ -2812,6 +2819,7 @@ _SpinboxOptionName = Literal[ "to", "validate", "validatecommand", + "vcmd", "values", "width", "wrap", @@ -2855,6 +2863,7 @@ class Spinbox(Widget, XView): insertontime: int = ..., insertwidth: _ScreenUnits = ..., invalidcommand: _EntryValidateCommand = ..., + invcmd: _EntryValidateCommand = ..., justify: Literal["left", "center", "right"] = ..., readonlybackground: _Color = ..., relief: _Relief = ..., @@ -2869,6 +2878,7 @@ class Spinbox(Widget, XView): to: float = ..., validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., validatecommand: _EntryValidateCommand = ..., + vcmd: _EntryValidateCommand = ..., values: _TkinterSequence[str] = ..., width: int = ..., wrap: bool = ..., @@ -2909,6 +2919,7 @@ class Spinbox(Widget, XView): insertontime: int = ..., insertwidth: _ScreenUnits = ..., invalidcommand: _EntryValidateCommand = ..., + invcmd: _EntryValidateCommand = ..., justify: Literal["left", "center", "right"] = ..., readonlybackground: _Color = ..., relief: _Relief = ..., @@ -2923,6 +2934,7 @@ class Spinbox(Widget, XView): to: float = ..., validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., validatecommand: _EntryValidateCommand = ..., + vcmd: _EntryValidateCommand = ..., values: _TkinterSequence[str] = ..., width: int = ..., wrap: bool = ..., From 5cd4649f11e82eb55cd52e30a741d1b654be2fae Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 17 Aug 2020 13:24:04 +0300 Subject: [PATCH 31/45] add undocumented container option to LabelFrame --- stdlib/3/tkinter/__init__.pyi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stdlib/3/tkinter/__init__.pyi b/stdlib/3/tkinter/__init__.pyi index da7a31f966a6..20e9fa6b2de1 100644 --- a/stdlib/3/tkinter/__init__.pyi +++ b/stdlib/3/tkinter/__init__.pyi @@ -2973,6 +2973,7 @@ _LabelFrameOptionName = Literal[ "borderwidth", "class", "colormap", + "container", "cursor", "fg", "font", @@ -3005,6 +3006,7 @@ class LabelFrame(Widget): borderwidth: _ScreenUnits = ..., class_: str = ..., colormap: Union[Literal["new", ""], Misc] = ..., + container: bool = ..., # undocumented cursor: _Cursor = ..., fg: _Color = ..., font: _FontDescription = ..., From b11fb4f891516aa99b492e6c467013b3c8cf9e8f Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 17 Aug 2020 14:07:03 +0300 Subject: [PATCH 32/45] add undocumented padding option to some ttk widgets --- stdlib/3/tkinter/ttk.pyi | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index a7c1f54c47dd..bc5ccc8a051c 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -39,6 +39,7 @@ _ButtonOptionName = Literal[ "cursor", "default", "image", + "padding", "state", "style", "takefocus", @@ -59,6 +60,7 @@ class Button(Widget): cursor: tkinter._Cursor = ..., default: Literal["normal", "active", "disabled"] = ..., image: tkinter._ImageSpec = ..., + padding: Any = ..., # undocumented state: Literal["normal", "disabled"] = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., @@ -77,6 +79,7 @@ class Button(Widget): cursor: tkinter._Cursor = ..., default: Literal["normal", "active", "disabled"] = ..., image: tkinter._ImageSpec = ..., + padding: Any = ..., state: Literal["normal", "disabled"] = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., @@ -99,6 +102,7 @@ _CheckbuttonOptionName = Literal[ "image", "offvalue", "onvalue", + "padding", "state", "style", "takefocus", @@ -121,6 +125,7 @@ class Checkbutton(Widget): image: tkinter._ImageSpec = ..., offvalue: Any = ..., onvalue: Any = ..., + padding: Any = ..., # undocumented state: Literal["normal", "disabled"] = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., @@ -144,6 +149,7 @@ class Checkbutton(Widget): image: tkinter._ImageSpec = ..., offvalue: Any = ..., onvalue: Any = ..., + padding: Any = ..., state: Literal["normal", "disabled"] = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., @@ -448,6 +454,7 @@ _MenubuttonOptionName = Literal[ "direction", "image", "menu", + "padding", "state", "style", "takefocus", @@ -468,6 +475,7 @@ class Menubutton(Widget): direction: Literal["above", "below", "left", "right", "flush"] = ..., image: tkinter._ImageSpec = ..., menu: tkinter.Menu = ..., + padding: Any = ..., # undocumented state: Literal["normal", "disabled"] = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., @@ -486,6 +494,7 @@ class Menubutton(Widget): direction: Literal["above", "below", "left", "right", "flush"] = ..., image: tkinter._ImageSpec = ..., menu: tkinter.Menu = ..., + padding: Any = ..., state: Literal["normal", "disabled"] = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., @@ -630,6 +639,7 @@ _RadiobuttonOptionName = Literal[ "compound", "cursor", "image", + "padding", "state", "style", "takefocus", @@ -651,6 +661,7 @@ class Radiobutton(Widget): compound: _TtkCompound = ..., cursor: tkinter._Cursor = ..., image: tkinter._ImageSpec = ..., + padding: Any = ..., # undocumented state: Literal["normal", "disabled"] = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., @@ -670,6 +681,7 @@ class Radiobutton(Widget): compound: _TtkCompound = ..., cursor: tkinter._Cursor = ..., image: tkinter._ImageSpec = ..., + padding: Any = ..., state: Literal["normal", "disabled"] = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., From 9f8c2ae528a022c6d030b3c0486a3aecadbac074 Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 17 Aug 2020 14:13:05 +0300 Subject: [PATCH 33/45] add undocumented background,foreground,font options to some ttk classes --- stdlib/3/tkinter/ttk.pyi | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index bc5ccc8a051c..e505ade45fd3 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -166,9 +166,12 @@ class Checkbutton(Widget): def invoke(self): ... _EntryOptionName = Literal[ + "background", "class", "cursor", "exportselection", + "font", + "foreground", "invalidcommand", "justify", "show", @@ -188,9 +191,12 @@ class Entry(Widget, tkinter.Entry): master: Optional[tkinter.Misc] = ..., widget: Optional[str] = ..., *, + background: tkinter._Color = ..., # undocumented class_: str = ..., cursor: tkinter._Cursor = ..., exportselection: bool = ..., + font: tkinter._FontDescription = ..., + foreground: tkinter._Color = ..., invalidcommand: tkinter._EntryValidateCommand = ..., justify: Literal["left", "center", "right"] = ..., show: str = ..., @@ -208,8 +214,11 @@ class Entry(Widget, tkinter.Entry): self, cnf: Optional[Dict[str, Any]] = ..., *, + background: tkinter._Color = ..., cursor: tkinter._Cursor = ..., exportselection: bool = ..., + font: tkinter._FontDescription = ..., + foreground: tkinter._Color = ..., invalidcommand: tkinter._EntryValidateCommand = ..., justify: Literal["left", "center", "right"] = ..., show: str = ..., @@ -231,9 +240,12 @@ class Entry(Widget, tkinter.Entry): def validate(self): ... _ComboboxOptionName = Literal[ + "background", "class", "cursor", "exportselection", + "font", + "foreground", "height", "justify", "postcommand", @@ -250,9 +262,12 @@ class Combobox(Entry): self, master: Optional[tkinter.Misc] = ..., *, + background: tkinter._Color = ..., # undocumented class_: str = ..., cursor: tkinter._Cursor = ..., exportselection: bool = ..., + font: tkinter._FontDescription = ..., # undocumented + foreground: tkinter._Color = ..., # undocumented height: int = ..., justify: Literal["left", "center", "right"] = ..., postcommand: Union[Callable[[], None], str] = ..., @@ -268,8 +283,11 @@ class Combobox(Entry): self, cnf: Optional[Dict[str, Any]] = ..., *, + background: tkinter._Color = ..., cursor: tkinter._Cursor = ..., exportselection: bool = ..., + font: tkinter._FontDescription = ..., + foreground: tkinter._Color = ..., height: int = ..., justify: Literal["left", "center", "right"] = ..., postcommand: Union[Callable[[], None], str] = ..., @@ -827,9 +845,12 @@ class Sizegrip(Widget): if sys.version_info >= (3, 7): _SpinboxOptionName = Literal[ + "background", "class", "command", "cursor", + "font", + "foreground", "format", "from", "increment", @@ -846,9 +867,12 @@ if sys.version_info >= (3, 7): self, master: Optional[tkinter.Misc] = ..., *, + background: tkinter._Color = ..., # undocumented class_: str = ..., command: Union[Callable[[], None], str, tkinter._TkinterSequence[str]] = ..., cursor: tkinter._Cursor = ..., + font: tkinter._FontDescription = ..., # undocumented + foreground: tkinter._Color = ..., # undocumented format: str = ..., from_: float = ..., increment: float = ..., @@ -865,8 +889,11 @@ if sys.version_info >= (3, 7): self, cnf: Optional[Dict[str, Any]] = ..., *, + background: tkinter.Color = ..., command: Union[Callable[[], None], str, tkinter._TkinterSequence[str]] = ..., cursor: tkinter._Cursor = ..., + font: tkinter._FontDescription = ..., + foreground: tkinter._Color = ..., format: str = ..., from_: float = ..., increment: float = ..., From fc73134b6faf5a4e075035fa94ca3575cc0e3bf2 Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 17 Aug 2020 14:20:38 +0300 Subject: [PATCH 34/45] fix missing orient copy/pasta --- stdlib/3/tkinter/ttk.pyi | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index e505ade45fd3..de5f04089243 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -591,6 +591,7 @@ class Panedwindow(Widget, tkinter.PanedWindow): *, cursor: tkinter._Cursor = ..., height: int = ..., + orient: Literal["vertical", "horizontal"] = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., width: int = ..., From b414bb0d6bd3cd58ea9b9d987de6613658b168bf Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 17 Aug 2020 14:26:56 +0300 Subject: [PATCH 35/45] add undocumented borderwidth option to ttk.Label and ttk.Labelframe --- stdlib/3/tkinter/ttk.pyi | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index de5f04089243..0ae98583da79 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -348,6 +348,8 @@ class Frame(Widget): _LabelOptionName = Literal[ "anchor", "background", + "border", + "borderwidth", "class", "compound", "cursor", @@ -374,6 +376,8 @@ class Label(Widget): *, anchor: tkinter._Anchor = ..., background: tkinter._Color = ..., + border: tkinter._ScreenUnits = ..., # alias for borderwidth + borderwidth: tkinter._ScreenUnits = ..., # undocumented class_: str = ..., compound: _TtkCompound = ..., cursor: tkinter._Cursor = ..., @@ -399,6 +403,8 @@ class Label(Widget): *, anchor: tkinter._Anchor = ..., background: tkinter._Color = ..., + border: tkinter._ScreenUnits = ..., + borderwidth: tkinter._ScreenUnits = ..., compound: _TtkCompound = ..., cursor: tkinter._Cursor = ..., font: tkinter._FontDescription = ..., @@ -422,7 +428,19 @@ class Label(Widget): def cget(self, key: _LabelOptionName) -> Any: ... _LabelframeOptionName = Literal[ - "class", "cursor", "height", "labelanchor", "labelwidget", "padding", "style", "takefocus", "text", "underline", "width", + "border", + "borderwidth", + "class", + "cursor", + "height", + "labelanchor", + "labelwidget", + "padding", + "style", + "takefocus", + "text", + "underline", + "width", ] class Labelframe(Widget): @@ -430,6 +448,8 @@ class Labelframe(Widget): self, master: Optional[tkinter.Misc] = ..., *, + border: tkinter._ScreenUnits = ..., + borderwidth: tkinter._ScreenUnits = ..., # undocumented class_: str = ..., cursor: tkinter._Cursor = ..., height: tkinter._ScreenUnits = ..., @@ -447,6 +467,8 @@ class Labelframe(Widget): self, cnf: Optional[Dict[str, Any]] = ..., *, + border: tkinter._ScreenUnits = ..., + borderwidth: tkinter._ScreenUnits = ..., cursor: tkinter._Cursor = ..., height: tkinter._ScreenUnits = ..., labelanchor: Literal["nw", "n", "ne", "en", "e", "es", "se", "s", "sw", "ws", "w", "wn"] = ..., From f4c8e12add9b612d60757ef1e6f026ad737db204 Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 17 Aug 2020 14:34:15 +0300 Subject: [PATCH 36/45] add validate, invalidcommand and validatecommand to ttk.Combobox and ttk.Spinbox --- stdlib/3/tkinter/ttk.pyi | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index 0ae98583da79..2410e4ecddb3 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -247,12 +247,15 @@ _ComboboxOptionName = Literal[ "font", "foreground", "height", + "invalidcommand", "justify", "postcommand", "state", "style", "takefocus", "textvariable", + "validate", + "validatecommand", "values", "width", ] @@ -269,12 +272,15 @@ class Combobox(Entry): font: tkinter._FontDescription = ..., # undocumented foreground: tkinter._Color = ..., # undocumented height: int = ..., + invalidcommand: tkinter._EntryValidateCommand = ..., # undocumented justify: Literal["left", "center", "right"] = ..., postcommand: Union[Callable[[], None], str] = ..., state: Literal["normal", "readonly", "disabled"] = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., textvariable: tkinter.Variable = ..., + validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., # undocumented + validatecommand: tkinter._EntryValidateCommand = ..., # undocumented values: tkinter._TkinterSequence[str] = ..., width: int = ..., ) -> None: ... @@ -289,12 +295,15 @@ class Combobox(Entry): font: tkinter._FontDescription = ..., foreground: tkinter._Color = ..., height: int = ..., + invalidcommand: tkinter._EntryValidateCommand = ..., justify: Literal["left", "center", "right"] = ..., postcommand: Union[Callable[[], None], str] = ..., state: Literal["normal", "readonly", "disabled"] = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., textvariable: tkinter.Variable = ..., + validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., + validatecommand: tkinter._EntryValidateCommand = ..., values: tkinter._TkinterSequence[str] = ..., width: int = ..., ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... @@ -877,10 +886,13 @@ if sys.version_info >= (3, 7): "format", "from", "increment", + "invalidcommand", "state", "style", "takefocus", "to", + "validate", + "validatecommand", "values", "wrap", "xscrollcommand", @@ -899,10 +911,13 @@ if sys.version_info >= (3, 7): format: str = ..., from_: float = ..., increment: float = ..., + invalidcommand: tkinter._EntryValidateCommand = ..., # undocumented state: Literal["normal", "disabled"] = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., to: float = ..., + validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., + validatecommand: tkinter._EntryValidateCommand = ..., values: tkinter._TkinterSequence[str] = ..., wrap: bool = ..., xscrollcommand: tkinter._XYScrollCommand = ..., @@ -920,10 +935,13 @@ if sys.version_info >= (3, 7): format: str = ..., from_: float = ..., increment: float = ..., + invalidcommand: tkinter._EntryValidateCommand = ..., state: Literal["normal", "disabled"] = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., to: float = ..., + validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., + validatecommand: tkinter._EntryValidateCommand = ..., values: tkinter._TkinterSequence[str] = ..., wrap: bool = ..., xscrollcommand: tkinter._XYScrollCommand = ..., From 398eb8e4372c964f709e73ecba0df75938556961 Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 17 Aug 2020 14:38:46 +0300 Subject: [PATCH 37/45] add undocumented show option to ttk.Combobox and ttk.Spinbox --- stdlib/3/tkinter/ttk.pyi | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index 2410e4ecddb3..7ccab9ec55f0 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -250,6 +250,7 @@ _ComboboxOptionName = Literal[ "invalidcommand", "justify", "postcommand", + "show", "state", "style", "takefocus", @@ -275,6 +276,7 @@ class Combobox(Entry): invalidcommand: tkinter._EntryValidateCommand = ..., # undocumented justify: Literal["left", "center", "right"] = ..., postcommand: Union[Callable[[], None], str] = ..., + show: Any = ..., # undocumented state: Literal["normal", "readonly", "disabled"] = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., @@ -298,6 +300,7 @@ class Combobox(Entry): invalidcommand: tkinter._EntryValidateCommand = ..., justify: Literal["left", "center", "right"] = ..., postcommand: Union[Callable[[], None], str] = ..., + show: Any = ..., state: Literal["normal", "readonly", "disabled"] = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., @@ -887,6 +890,7 @@ if sys.version_info >= (3, 7): "from", "increment", "invalidcommand", + "show", "state", "style", "takefocus", @@ -912,6 +916,7 @@ if sys.version_info >= (3, 7): from_: float = ..., increment: float = ..., invalidcommand: tkinter._EntryValidateCommand = ..., # undocumented + show: Any = ..., # undocumented state: Literal["normal", "disabled"] = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., @@ -936,6 +941,7 @@ if sys.version_info >= (3, 7): from_: float = ..., increment: float = ..., invalidcommand: tkinter._EntryValidateCommand = ..., + show: Any = ..., state: Literal["normal", "disabled"] = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., From 9d57ce7f84c69a4852b2add2a7bfa9ce99501342 Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 17 Aug 2020 14:40:35 +0300 Subject: [PATCH 38/45] add undocumented xscrollcommand to Combobox --- stdlib/3/tkinter/ttk.pyi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index 7ccab9ec55f0..17b07717a541 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -259,6 +259,7 @@ _ComboboxOptionName = Literal[ "validatecommand", "values", "width", + "xscrollcommand", ] class Combobox(Entry): @@ -285,6 +286,7 @@ class Combobox(Entry): validatecommand: tkinter._EntryValidateCommand = ..., # undocumented values: tkinter._TkinterSequence[str] = ..., width: int = ..., + xscrollcommand: tkinter._XYScrollCommand = ..., # undocumented ) -> None: ... @overload # type: ignore def configure( @@ -309,6 +311,7 @@ class Combobox(Entry): validatecommand: tkinter._EntryValidateCommand = ..., values: tkinter._TkinterSequence[str] = ..., width: int = ..., + xscrollcommand: tkinter._XYScrollCommand = ..., ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... @overload def configure(self, cnf: _ComboboxOptionName) -> Tuple[str, str, str, Any, Any]: ... From a533c50beae61cb5a908c9eb835c122ca48da068 Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 17 Aug 2020 14:43:24 +0300 Subject: [PATCH 39/45] add undocumented state option to ttk.Scale --- stdlib/3/tkinter/ttk.pyi | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index 17b07717a541..469164654d71 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -755,7 +755,7 @@ class Radiobutton(Widget): def invoke(self): ... _ScaleOptionName = Literal[ - "class", "command", "cursor", "from", "length", "orient", "style", "takefocus", "to", "value", "variable" + "class", "command", "cursor", "from", "length", "orient", "state", "style", "takefocus", "to", "value", "variable" ] class Scale(Widget, tkinter.Scale): @@ -769,6 +769,7 @@ class Scale(Widget, tkinter.Scale): from_: float = ..., length: tkinter._ScreenUnits = ..., orient: Literal["horizontal", "vertical"] = ..., + state: Any = ..., # undocumented style: str = ..., takefocus: tkinter._TakeFocusValue = ..., to: float = ..., @@ -785,6 +786,7 @@ class Scale(Widget, tkinter.Scale): from_: float = ..., length: tkinter._ScreenUnits = ..., orient: Literal["horizontal", "vertical"] = ..., + state: Any = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., to: float = ..., From b9d38f2fb18110575a8a5eb0cbaa9bd71e57a7df Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 17 Aug 2020 14:44:49 +0300 Subject: [PATCH 40/45] add undocumented relief option to ttk.Labelframe --- stdlib/3/tkinter/ttk.pyi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index 469164654d71..7580a83db269 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -451,6 +451,7 @@ _LabelframeOptionName = Literal[ "labelanchor", "labelwidget", "padding", + "relief", "style", "takefocus", "text", @@ -471,6 +472,7 @@ class Labelframe(Widget): labelanchor: Literal["nw", "n", "ne", "en", "e", "es", "se", "s", "sw", "ws", "w", "wn"] = ..., labelwidget: tkinter.Misc = ..., padding: tkinter._Padding = ..., + relief: tkinter._Relief = ..., # undocumented style: str = ..., takefocus: tkinter._TakeFocusValue = ..., text: str = ..., @@ -489,6 +491,7 @@ class Labelframe(Widget): labelanchor: Literal["nw", "n", "ne", "en", "e", "es", "se", "s", "sw", "ws", "w", "wn"] = ..., labelwidget: tkinter.Misc = ..., padding: tkinter._Padding = ..., + relief: tkinter._Relief = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., text: str = ..., From dfbcbda518adb28258ccb4111caf2e0ff0673e61 Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 17 Aug 2020 14:48:19 +0300 Subject: [PATCH 41/45] add some ttk.Entry options to ttk.Spinbox --- stdlib/3/tkinter/ttk.pyi | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index 7580a83db269..a749906e2c3d 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -892,20 +892,24 @@ if sys.version_info >= (3, 7): "class", "command", "cursor", + "exportselection", "font", "foreground", "format", "from", "increment", "invalidcommand", + "justify", "show", "state", "style", "takefocus", + "textvariable", "to", "validate", "validatecommand", "values", + "width", "wrap", "xscrollcommand", ] @@ -918,20 +922,24 @@ if sys.version_info >= (3, 7): class_: str = ..., command: Union[Callable[[], None], str, tkinter._TkinterSequence[str]] = ..., cursor: tkinter._Cursor = ..., + exportselection: bool = ..., # undocumented font: tkinter._FontDescription = ..., # undocumented foreground: tkinter._Color = ..., # undocumented format: str = ..., from_: float = ..., increment: float = ..., invalidcommand: tkinter._EntryValidateCommand = ..., # undocumented + justify: Literal["left", "center", "right"] = ..., # undocumented show: Any = ..., # undocumented state: Literal["normal", "disabled"] = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., + textvariable: tkinter.Variable = ..., # undocumented to: float = ..., validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., validatecommand: tkinter._EntryValidateCommand = ..., values: tkinter._TkinterSequence[str] = ..., + width: int = ..., # undocumented wrap: bool = ..., xscrollcommand: tkinter._XYScrollCommand = ..., ) -> None: ... @@ -943,20 +951,24 @@ if sys.version_info >= (3, 7): background: tkinter.Color = ..., command: Union[Callable[[], None], str, tkinter._TkinterSequence[str]] = ..., cursor: tkinter._Cursor = ..., + exportselection: bool = ..., font: tkinter._FontDescription = ..., foreground: tkinter._Color = ..., format: str = ..., from_: float = ..., increment: float = ..., invalidcommand: tkinter._EntryValidateCommand = ..., + justify: Literal["left", "center", "right"] = ..., show: Any = ..., state: Literal["normal", "disabled"] = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., + textvariable: tkinter.Variable = ..., to: float = ..., validate: Literal["none", "focus", "focusin", "focusout", "key", "all"] = ..., validatecommand: tkinter._EntryValidateCommand = ..., values: tkinter._TkinterSequence[str] = ..., + width: int = ..., wrap: bool = ..., xscrollcommand: tkinter._XYScrollCommand = ..., ) -> Optional[Dict[str, Tuple[str, str, str, Any, Any]]]: ... From 58b445a1cbeca83f5de0884759a855547c752373 Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 17 Aug 2020 14:49:32 +0300 Subject: [PATCH 42/45] fix typo --- stdlib/3/tkinter/ttk.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index a749906e2c3d..17d6d6802e10 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -948,7 +948,7 @@ if sys.version_info >= (3, 7): self, cnf: Optional[Dict[str, Any]] = ..., *, - background: tkinter.Color = ..., + background: tkinter._Color = ..., command: Union[Callable[[], None], str, tkinter._TkinterSequence[str]] = ..., cursor: tkinter._Cursor = ..., exportselection: bool = ..., From 3ea4aed0c308dafa6ea9a92de5e578ce9f6bb6d2 Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 17 Aug 2020 14:52:16 +0300 Subject: [PATCH 43/45] Revert "fix missing orient copy/pasta" This reverts commit fc73134b6faf5a4e075035fa94ca3575cc0e3bf2. --- stdlib/3/tkinter/ttk.pyi | 1 - 1 file changed, 1 deletion(-) diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index 17d6d6802e10..e36c1d42d24f 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -631,7 +631,6 @@ class Panedwindow(Widget, tkinter.PanedWindow): *, cursor: tkinter._Cursor = ..., height: int = ..., - orient: Literal["vertical", "horizontal"] = ..., style: str = ..., takefocus: tkinter._TakeFocusValue = ..., width: int = ..., From 2c40d0791882d91e03d7fb9a0aa8a728de92d5a9 Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 17 Aug 2020 15:12:09 +0300 Subject: [PATCH 44/45] move _FontDescription back to tkinter.font --- stdlib/3/tkinter/__init__.pyi | 14 +++++--------- stdlib/3/tkinter/font.pyi | 8 +++++++- stdlib/3/tkinter/ttk.pyi | 17 +++++++++-------- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/stdlib/3/tkinter/__init__.pyi b/stdlib/3/tkinter/__init__.pyi index 698ec35be85e..b06043619dad 100644 --- a/stdlib/3/tkinter/__init__.pyi +++ b/stdlib/3/tkinter/__init__.pyi @@ -1,12 +1,16 @@ import _tkinter import sys from enum import Enum -from tkinter import font from tkinter.constants import * # comment this out to find undefined identifier names with flake8 +from tkinter.font import _FontDescription from types import TracebackType from typing import Any, Callable, Dict, Generic, List, Optional, Tuple, Type, TypeVar, Union, overload from typing_extensions import Literal, TypedDict +# Using anything from tkinter.font in this file means that 'import tkinter' +# seems to also load tkinter.font. That's not how it actually works, but +# unfortunately not much can be done about it. https://github.com/python/typeshed/pull/4346 + TclError = _tkinter.TclError wantobjects: Any TkVersion: Any @@ -91,14 +95,6 @@ _Cursor = Union[str, Tuple[str], Tuple[str, str], Tuple[str, str, str], Tuple[st _EntryValidateCommand = Union[ Callable[[], bool], str, _TkinterSequence[str] ] # example when it's sequence: entry['invalidcommand'] = [entry.register(print), '%P'] -# Putting this to font.pyi breaks pytype: https://github.com/google/pytype/issues/626 -# -# Using anything from tkinter.font in this file means that 'import tkinter' -# seems to also load tkinter.font. That's not how it actually works, but -# unfortunately not much can be done about it. https://github.com/python/typeshed/pull/4346 -_FontDescription = Union[ - str, font.Font, Tuple[str, int], Tuple[str, int, str], Tuple[str, int, _TkinterSequence[str]], -] # 'FONT DESCRIPTIONS' in 'font' manual page _ImageSpec = Union[Image, str] # str can be from e.g. tkinter.image_names() _Padding = Union[ _ScreenUnits, diff --git a/stdlib/3/tkinter/font.pyi b/stdlib/3/tkinter/font.pyi index 8b974c8df8ad..58f0b2f9c7ee 100644 --- a/stdlib/3/tkinter/font.pyi +++ b/stdlib/3/tkinter/font.pyi @@ -9,6 +9,12 @@ ITALIC: Literal["italic"] def nametofont(name: str) -> Font: ... +# See 'FONT DESCRIPTIONS' in font man page. This uses str because Literal +# inside Tuple doesn't work. +_FontDescription = Union[ + str, Font, Tuple[str, int], Tuple[str, int, tkinter._TkinterSequence[str]], +] + class _FontDict(TypedDict): family: str size: int @@ -31,7 +37,7 @@ class Font: # In tkinter, 'root' refers to tkinter.Tk by convention, but the code # actually works with any tkinter widget so we use tkinter.Misc. root: Optional[tkinter.Misc] = ..., - font: Optional[tkinter._FontDescription] = ..., + font: Optional[_FontDescription] = ..., name: Optional[str] = ..., exists: bool = ..., *, diff --git a/stdlib/3/tkinter/ttk.pyi b/stdlib/3/tkinter/ttk.pyi index e36c1d42d24f..564ddae3abc1 100644 --- a/stdlib/3/tkinter/ttk.pyi +++ b/stdlib/3/tkinter/ttk.pyi @@ -1,6 +1,7 @@ import _tkinter import sys import tkinter +from tkinter.font import _FontDescription from typing import Any, Callable, Dict, List, Optional, Tuple, Union, overload from typing_extensions import Literal @@ -195,7 +196,7 @@ class Entry(Widget, tkinter.Entry): class_: str = ..., cursor: tkinter._Cursor = ..., exportselection: bool = ..., - font: tkinter._FontDescription = ..., + font: _FontDescription = ..., foreground: tkinter._Color = ..., invalidcommand: tkinter._EntryValidateCommand = ..., justify: Literal["left", "center", "right"] = ..., @@ -217,7 +218,7 @@ class Entry(Widget, tkinter.Entry): background: tkinter._Color = ..., cursor: tkinter._Cursor = ..., exportselection: bool = ..., - font: tkinter._FontDescription = ..., + font: _FontDescription = ..., foreground: tkinter._Color = ..., invalidcommand: tkinter._EntryValidateCommand = ..., justify: Literal["left", "center", "right"] = ..., @@ -271,7 +272,7 @@ class Combobox(Entry): class_: str = ..., cursor: tkinter._Cursor = ..., exportselection: bool = ..., - font: tkinter._FontDescription = ..., # undocumented + font: _FontDescription = ..., # undocumented foreground: tkinter._Color = ..., # undocumented height: int = ..., invalidcommand: tkinter._EntryValidateCommand = ..., # undocumented @@ -296,7 +297,7 @@ class Combobox(Entry): background: tkinter._Color = ..., cursor: tkinter._Cursor = ..., exportselection: bool = ..., - font: tkinter._FontDescription = ..., + font: _FontDescription = ..., foreground: tkinter._Color = ..., height: int = ..., invalidcommand: tkinter._EntryValidateCommand = ..., @@ -396,7 +397,7 @@ class Label(Widget): class_: str = ..., compound: _TtkCompound = ..., cursor: tkinter._Cursor = ..., - font: tkinter._FontDescription = ..., + font: _FontDescription = ..., foreground: tkinter._Color = ..., image: tkinter._ImageSpec = ..., justify: Literal["left", "center", "right"] = ..., @@ -422,7 +423,7 @@ class Label(Widget): borderwidth: tkinter._ScreenUnits = ..., compound: _TtkCompound = ..., cursor: tkinter._Cursor = ..., - font: tkinter._FontDescription = ..., + font: _FontDescription = ..., foreground: tkinter._Color = ..., image: tkinter._ImageSpec = ..., justify: Literal["left", "center", "right"] = ..., @@ -922,7 +923,7 @@ if sys.version_info >= (3, 7): command: Union[Callable[[], None], str, tkinter._TkinterSequence[str]] = ..., cursor: tkinter._Cursor = ..., exportselection: bool = ..., # undocumented - font: tkinter._FontDescription = ..., # undocumented + font: _FontDescription = ..., # undocumented foreground: tkinter._Color = ..., # undocumented format: str = ..., from_: float = ..., @@ -951,7 +952,7 @@ if sys.version_info >= (3, 7): command: Union[Callable[[], None], str, tkinter._TkinterSequence[str]] = ..., cursor: tkinter._Cursor = ..., exportselection: bool = ..., - font: tkinter._FontDescription = ..., + font: _FontDescription = ..., foreground: tkinter._Color = ..., format: str = ..., from_: float = ..., From c6b47e445e2ee34f202d4839141814355b0fccb7 Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 17 Aug 2020 16:13:52 +0300 Subject: [PATCH 45/45] add another pytype issue comment --- tests/pytype_exclude_list.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/pytype_exclude_list.txt b/tests/pytype_exclude_list.txt index 00f4ea263d5c..23b922ab5767 100644 --- a/tests/pytype_exclude_list.txt +++ b/tests/pytype_exclude_list.txt @@ -18,7 +18,9 @@ third_party/2and3/pynamodb/models.pyi third_party/3/six/__init__.pyi third_party/3/six/moves/__init__.pyi -# anything that imports tkinter (https://github.com/google/pytype/issues/637) +# anything that imports tkinter +# https://github.com/google/pytype/issues/637 +# https://github.com/google/pytype/issues/644 stdlib/3/tkinter/__init__.pyi stdlib/3/tkinter/dialog.pyi stdlib/3/tkinter/filedialog.pyi