From c9407768d78d8c2eb6e852b4c6997b16c0005ea0 Mon Sep 17 00:00:00 2001 From: Akuli Date: Tue, 21 Jul 2020 13:31:47 +0300 Subject: [PATCH 1/9] add tkinter.font stub --- stdlib/3/tkinter/font.pyi | 107 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 stdlib/3/tkinter/font.pyi diff --git a/stdlib/3/tkinter/font.pyi b/stdlib/3/tkinter/font.pyi new file mode 100644 index 000000000000..cafd0fb51a5e --- /dev/null +++ b/stdlib/3/tkinter/font.pyi @@ -0,0 +1,107 @@ +import tkinter +from typing import Any, List, Optional, Tuple, TypedDict, Union, overload +from typing_extensions import Literal + +NORMAL: Literal["normal"] +ROMAN: Literal["roman"] +BOLD: Literal["bold"] +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, Tuple[str, ...]], Tuple[str, int, List[str]], +] + +class _FontDict(TypedDict): + family: str + size: int + weight: Literal["normal", "bold"] + slant: Literal["roman", "italic"] + underline: Literal[0, 1] + overstrike: Literal[0, 1] + +class _MetricsDict(TypedDict): + ascent: int + descent: int + linespace: int + fixed: Literal[0, 1] + +class Font: + name: str + delete_font: bool + def __init__( + self, + # 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] = ..., + name: Optional[str] = ..., + exists: bool = ..., + *, + family: str = ..., + size: int = ..., + weight: Literal["normal", "bold"] = ..., + slant: Literal["roman", "italic"] = ..., + underline: bool = ..., + overstrike: bool = ..., + ) -> None: ... + @overload + def cget(self, option: Literal["family"]) -> str: ... + @overload + def cget(self, option: Literal["size"]) -> int: ... + @overload + def cget(self, option: Literal["weight"]) -> Literal["normal", "bold"]: ... + @overload + def cget(self, option: Literal["slant"]) -> Literal["roman", "italic"]: ... + @overload + def cget(self, option: Literal["underline", "overstrike"]) -> Literal[0, 1]: ... + __getitem__ = cget + @overload + def actual(self, option: Literal["family"], displayof: Optional[tkinter.Misc] = ...) -> str: ... + @overload + def actual(self, option: Literal["size"], displayof: Optional[tkinter.Misc] = ...) -> int: ... + @overload + def actual(self, option: Literal["weight"], displayof: Optional[tkinter.Misc] = ...) -> Literal["normal", "bold"]: ... + @overload + def actual(self, option: Literal["slant"], displayof: Optional[tkinter.Misc] = ...) -> Literal["roman", "italic"]: ... + @overload + def actual(self, option: Literal["underline", "overstrike"], displayof: Optional[tkinter.Misc] = ...) -> Literal[0, 1]: ... + @overload + def actual(self, *, displayof: Optional[tkinter.Misc] = ...) -> _FontDict: ... + @overload + def __setitem__(self, key: Literal["family"], value: str) -> None: ... + @overload + def __setitem__(self, key: Literal["size"], value: int) -> None: ... + @overload + def __setitem__(self, key: Literal["weight"], value: Literal["normal", "bold"]) -> None: ... + @overload + def __setitem__(self, key: Literal["slant"], value: Literal["roman", "italic"]) -> None: ... + @overload + def __setitem__(self, key: Literal["underline"], value: bool) -> None: ... + @overload + def __setitem__(self, key: Literal["overstrike"], value: bool) -> None: ... + def config( + self, + *, + family: str = ..., + size: int = ..., + weight: Literal["normal", "bold"] = ..., + slant: Literal["roman", "italic"] = ..., + underline: bool = ..., + overstrike: bool = ..., + ) -> Optional[_FontDict]: ... + configure = config + def copy(self) -> Font: ... + @overload + def metrics(self, __option: Literal["ascent", "descent", "linespace"], *, displayof: Optional[tkinter.Misc] = ...) -> int: ... + @overload + def metrics(self, __option: Literal["fixed"], *, displayof: Optional[tkinter.Misc] = ...) -> Literal[0, 1]: ... + @overload + def metrics(self, *, displayof: Optional[tkinter.Misc] = ...) -> _MetricsDict: ... + def measure(self, text: str, displayof: Optional[tkinter.Misc] = ...) -> int: ... + +def families(root: Optional[tkinter.Misc] = ..., displayof: Optional[tkinter.Misc] = ...) -> Tuple[str, ...]: ... +def names(root: Optional[tkinter.Misc] = ...) -> Tuple[str, ...]: ... From 15e5f67d9e58858d37a7d4e6649d9940be164146 Mon Sep 17 00:00:00 2001 From: Akuli Date: Tue, 21 Jul 2020 14:59:26 +0300 Subject: [PATCH 2/9] use typing_extensions when importing TypedDict --- stdlib/3/tkinter/font.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/3/tkinter/font.pyi b/stdlib/3/tkinter/font.pyi index cafd0fb51a5e..f1bad3b3b0ac 100644 --- a/stdlib/3/tkinter/font.pyi +++ b/stdlib/3/tkinter/font.pyi @@ -1,6 +1,6 @@ import tkinter -from typing import Any, List, Optional, Tuple, TypedDict, Union, overload -from typing_extensions import Literal +from typing import Any, List, Optional, Tuple, Union, overload +from typing_extensions import Literal, TypedDict NORMAL: Literal["normal"] ROMAN: Literal["roman"] From 45f506898fe44de7e8c99d9e558e9eb786d4066f Mon Sep 17 00:00:00 2001 From: Akuli Date: Tue, 21 Jul 2020 15:07:50 +0300 Subject: [PATCH 3/9] fix Font.metrics overload --- stdlib/3/tkinter/font.pyi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stdlib/3/tkinter/font.pyi b/stdlib/3/tkinter/font.pyi index f1bad3b3b0ac..ab83b5381af1 100644 --- a/stdlib/3/tkinter/font.pyi +++ b/stdlib/3/tkinter/font.pyi @@ -100,6 +100,8 @@ class Font: @overload def metrics(self, __option: Literal["fixed"], *, displayof: Optional[tkinter.Misc] = ...) -> Literal[0, 1]: ... @overload + def metrics(self, __option: None = ..., *, displayof: Optional[tkinter.Misc] = ...) -> _MetricsDict: ... + @overload def metrics(self, *, displayof: Optional[tkinter.Misc] = ...) -> _MetricsDict: ... def measure(self, text: str, displayof: Optional[tkinter.Misc] = ...) -> int: ... From 9d39a6b2f137636e3e3daf830e79da9f698060c6 Mon Sep 17 00:00:00 2001 From: Akuli Date: Tue, 21 Jul 2020 15:09:41 +0300 Subject: [PATCH 4/9] actually fix Font.metrics overload --- stdlib/3/tkinter/font.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/3/tkinter/font.pyi b/stdlib/3/tkinter/font.pyi index ab83b5381af1..5a818260ecde 100644 --- a/stdlib/3/tkinter/font.pyi +++ b/stdlib/3/tkinter/font.pyi @@ -100,7 +100,7 @@ class Font: @overload def metrics(self, __option: Literal["fixed"], *, displayof: Optional[tkinter.Misc] = ...) -> Literal[0, 1]: ... @overload - def metrics(self, __option: None = ..., *, displayof: Optional[tkinter.Misc] = ...) -> _MetricsDict: ... + def metrics(self, __option: None, *, displayof: Optional[tkinter.Misc] = ...) -> _MetricsDict: ... @overload def metrics(self, *, displayof: Optional[tkinter.Misc] = ...) -> _MetricsDict: ... def measure(self, text: str, displayof: Optional[tkinter.Misc] = ...) -> int: ... From 87331243bd6a243f4df40204fb2e1a181c727ac2 Mon Sep 17 00:00:00 2001 From: Akuli Date: Tue, 21 Jul 2020 15:19:39 +0300 Subject: [PATCH 5/9] maybe it actually gets fixed now --- stdlib/3/tkinter/font.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/3/tkinter/font.pyi b/stdlib/3/tkinter/font.pyi index 5a818260ecde..bed92e235f56 100644 --- a/stdlib/3/tkinter/font.pyi +++ b/stdlib/3/tkinter/font.pyi @@ -70,6 +70,8 @@ class Font: @overload def actual(self, option: Literal["underline", "overstrike"], displayof: Optional[tkinter.Misc] = ...) -> Literal[0, 1]: ... @overload + def actual(self, option: None, *, displayof: Optional[tkinter.Misc] = ...) -> _FontDict: ... + @overload def actual(self, *, displayof: Optional[tkinter.Misc] = ...) -> _FontDict: ... @overload def __setitem__(self, key: Literal["family"], value: str) -> None: ... @@ -100,8 +102,6 @@ class Font: @overload def metrics(self, __option: Literal["fixed"], *, displayof: Optional[tkinter.Misc] = ...) -> Literal[0, 1]: ... @overload - def metrics(self, __option: None, *, displayof: Optional[tkinter.Misc] = ...) -> _MetricsDict: ... - @overload def metrics(self, *, displayof: Optional[tkinter.Misc] = ...) -> _MetricsDict: ... def measure(self, text: str, displayof: Optional[tkinter.Misc] = ...) -> int: ... From a57b0e016b4f5cb6300d51a3ccae494f6881ca64 Mon Sep 17 00:00:00 2001 From: Akuli Date: Wed, 22 Jul 2020 12:34:40 +0300 Subject: [PATCH 6/9] add comment about why not Sequence --- stdlib/3/tkinter/font.pyi | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/stdlib/3/tkinter/font.pyi b/stdlib/3/tkinter/font.pyi index bed92e235f56..dd5b2cda957d 100644 --- a/stdlib/3/tkinter/font.pyi +++ b/stdlib/3/tkinter/font.pyi @@ -10,7 +10,13 @@ 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. +# inside Tuple doesn't work. Also, some Sequences don't work, and we really +# need this to accept just List or Tuple: +# +# >>> tkinter.Label(font=('Helvetica', 12, collections.deque(['bold']))) +# Traceback (most recent call last): +# ... +# _tkinter.TclError: unknown font style "deque(['bold'])" _FontDescription = Union[ str, Font, Tuple[str, int], Tuple[str, int, Tuple[str, ...]], Tuple[str, int, List[str]], ] From e7604dafc322a2db14478bd6bddd6fe83afe3153 Mon Sep 17 00:00:00 2001 From: Akuli Date: Wed, 22 Jul 2020 12:35:12 +0300 Subject: [PATCH 7/9] remove unnecessary import --- stdlib/3/tkinter/font.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/3/tkinter/font.pyi b/stdlib/3/tkinter/font.pyi index dd5b2cda957d..501765893121 100644 --- a/stdlib/3/tkinter/font.pyi +++ b/stdlib/3/tkinter/font.pyi @@ -1,5 +1,5 @@ import tkinter -from typing import Any, List, Optional, Tuple, Union, overload +from typing import List, Optional, Tuple, Union, overload from typing_extensions import Literal, TypedDict NORMAL: Literal["normal"] From 8f42080566d4a43ef1df928395e9911937aff242 Mon Sep 17 00:00:00 2001 From: Akuli Date: Wed, 22 Jul 2020 13:45:34 +0300 Subject: [PATCH 8/9] introduce _TkinterSequence (will be useful for other tkinter stubs too) --- stdlib/3/tkinter/font.pyi | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/stdlib/3/tkinter/font.pyi b/stdlib/3/tkinter/font.pyi index 501765893121..99d09bcb1fa2 100644 --- a/stdlib/3/tkinter/font.pyi +++ b/stdlib/3/tkinter/font.pyi @@ -1,5 +1,5 @@ import tkinter -from typing import List, Optional, Tuple, Union, overload +from typing import List, Optional, Tuple, TypeVar, Union, overload from typing_extensions import Literal, TypedDict NORMAL: Literal["normal"] @@ -9,16 +9,21 @@ 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. Also, some Sequences don't work, and we really -# need this to accept just List or Tuple: +# _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, Tuple[str, ...]], Tuple[str, int, List[str]], + str, Font, Tuple[str, int], Tuple[str, int, _TkinterSequence[str]], ] class _FontDict(TypedDict): From 6e08c8a47f4d86b2185bffef51a7549ea9be0ad4 Mon Sep 17 00:00:00 2001 From: Akuli Date: Wed, 22 Jul 2020 13:46:53 +0300 Subject: [PATCH 9/9] support some_font.actual(None, None) --- stdlib/3/tkinter/font.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/3/tkinter/font.pyi b/stdlib/3/tkinter/font.pyi index 99d09bcb1fa2..cd9aca4d0bb9 100644 --- a/stdlib/3/tkinter/font.pyi +++ b/stdlib/3/tkinter/font.pyi @@ -81,7 +81,7 @@ class Font: @overload def actual(self, option: Literal["underline", "overstrike"], displayof: Optional[tkinter.Misc] = ...) -> Literal[0, 1]: ... @overload - def actual(self, option: None, *, displayof: Optional[tkinter.Misc] = ...) -> _FontDict: ... + def actual(self, option: None, displayof: Optional[tkinter.Misc] = ...) -> _FontDict: ... @overload def actual(self, *, displayof: Optional[tkinter.Misc] = ...) -> _FontDict: ... @overload