From 320b883ada83375f1e6929b4703b741d3c4813ce Mon Sep 17 00:00:00 2001 From: Wesley Collin Wright Date: Mon, 24 Apr 2023 16:11:40 +0000 Subject: [PATCH] Typeshed cherry-pick: stdlib/xml: fix return types for toxml/toprettyxml methods (#10061) --- mypy/typeshed/stdlib/xml/dom/minidom.pyi | 66 ++++++++++++++++++-- mypy/typeshed/test_cases/stdlib/check_xml.py | 35 +++++++++++ 2 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 mypy/typeshed/test_cases/stdlib/check_xml.py diff --git a/mypy/typeshed/stdlib/xml/dom/minidom.pyi b/mypy/typeshed/stdlib/xml/dom/minidom.pyi index e2880ae858d6..ec17f0a41497 100644 --- a/mypy/typeshed/stdlib/xml/dom/minidom.pyi +++ b/mypy/typeshed/stdlib/xml/dom/minidom.pyi @@ -1,7 +1,7 @@ import sys import xml.dom from _typeshed import Incomplete, ReadableBuffer, SupportsRead, SupportsWrite -from typing import NoReturn, TypeVar +from typing import NoReturn, TypeVar, overload from typing_extensions import Literal, Self from xml.dom.minicompat import NodeList from xml.dom.xmlbuilder import DocumentLS, DOMImplementationLS @@ -30,13 +30,69 @@ class Node(xml.dom.Node): def localName(self) -> str | None: ... def __bool__(self) -> Literal[True]: ... if sys.version_info >= (3, 9): - def toxml(self, encoding: str | None = None, standalone: bool | None = None) -> str: ... + @overload + def toxml(self, encoding: str, standalone: bool | None = None) -> bytes: ... + @overload + def toxml(self, encoding: None = None, standalone: bool | None = None) -> str: ... + @overload def toprettyxml( - self, indent: str = "\t", newl: str = "\n", encoding: str | None = None, standalone: bool | None = None + self, + indent: str = "\t", + newl: str = "\n", + # Handle any case where encoding is not provided or where it is passed with None + encoding: None = None, + standalone: bool | None = None, ) -> str: ... + @overload + def toprettyxml( + self, + indent: str, + newl: str, + # Handle cases where encoding is passed as str *positionally* + encoding: str, + standalone: bool | None = None, + ) -> bytes: ... + @overload + def toprettyxml( + self, + indent: str = "\t", + newl: str = "\n", + # Handle all cases where encoding is passed as a keyword argument; because standalone + # comes after, it will also have to be a keyword arg if encoding is + *, + encoding: str, + standalone: bool | None = None, + ) -> bytes: ... else: - def toxml(self, encoding: str | None = None): ... - def toprettyxml(self, indent: str = "\t", newl: str = "\n", encoding: str | None = None): ... + @overload + def toxml(self, encoding: str) -> bytes: ... + @overload + def toxml(self, encoding: None = None) -> str: ... + @overload + def toprettyxml( + self, + indent: str = "\t", + newl: str = "\n", + # Handle any case where encoding is not provided or where it is passed with None + encoding: None = None, + ) -> str: ... + @overload + def toprettyxml( + self, + indent: str, + newl: str, + # Handle cases where encoding is passed as str *positionally* + encoding: str, + ) -> bytes: ... + @overload + def toprettyxml( + self, + indent: str = "\t", + newl: str = "\n", + # Handle all cases where encoding is passed as a keyword argument + *, + encoding: str, + ) -> bytes: ... def hasChildNodes(self) -> bool: ... def insertBefore(self, newChild, refChild): ... diff --git a/mypy/typeshed/test_cases/stdlib/check_xml.py b/mypy/typeshed/test_cases/stdlib/check_xml.py new file mode 100644 index 000000000000..b485dac8dc29 --- /dev/null +++ b/mypy/typeshed/test_cases/stdlib/check_xml.py @@ -0,0 +1,35 @@ +from __future__ import annotations + +import sys +from typing_extensions import assert_type +from xml.dom.minidom import Document + +document = Document() + +assert_type(document.toxml(), str) +assert_type(document.toxml(encoding=None), str) +assert_type(document.toxml(encoding="UTF8"), bytes) +assert_type(document.toxml("UTF8"), bytes) +if sys.version_info >= (3, 9): + assert_type(document.toxml(standalone=True), str) + assert_type(document.toxml("UTF8", True), bytes) + assert_type(document.toxml(encoding="UTF8", standalone=True), bytes) + + +# Because toprettyxml can mix positional and keyword variants of the "encoding" argument, which +# determines the return type, the proper stub typing isn't immediately obvious. This is a basic +# brute-force sanity check. +# Test cases like toxml +assert_type(document.toprettyxml(), str) +assert_type(document.toprettyxml(encoding=None), str) +assert_type(document.toprettyxml(encoding="UTF8"), bytes) +if sys.version_info >= (3, 9): + assert_type(document.toprettyxml(standalone=True), str) + assert_type(document.toprettyxml(encoding="UTF8", standalone=True), bytes) +# Test cases unique to toprettyxml +assert_type(document.toprettyxml(" "), str) +assert_type(document.toprettyxml(" ", "\r\n"), str) +assert_type(document.toprettyxml(" ", "\r\n", "UTF8"), bytes) +if sys.version_info >= (3, 9): + assert_type(document.toprettyxml(" ", "\r\n", "UTF8", True), bytes) + assert_type(document.toprettyxml(" ", "\r\n", standalone=True), str)