getroot() method of xml.etree.ElementTree return type should be nullable #7770
Copy link
Copy link
Closed
Description
Hello,
I'm one of the maintainers of Sonar Python analyzer and we use typeshed for our type inference engine. (btw thanks a lot for maintaining typeshed!)
While investigating a false positive reported by a user, I noticed that that getroot() method of ElementTreeclass has return type annotation -> Element (see here).
I think getroot() return type annotation should beElementTree | None instead, because it can returns None when root element is not provided:
import xml.etree.ElementTree as ET
tree = ET.ElementTree()
print(tree.getroot()) # prints NoneIn fact, e.g. 3.10 implementation of ElementTree is defined as follows:
class ElementTree:
def __init__(self, element=None, file=None):
# assert element is None or iselement(element)
self._root = element # first node
if file:
self.parse(file)
def getroot(self):
"""Return root element of this tree."""
return self._rootI'm happy to open a PR if you think the reported issue is valid.
Thanks,
Andrea
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
srittau commentedon May 3, 2022
The problem with adding
Noneto the return type is that users that know that a root exists (for example when parsing an ElementTree) have to do an extra check, something we like to avoid in typeshed. E.g.:Our usual solution would be to return
Element | Anyto at least somewhat reflect the fact thatgetroot()can not only returnElement, but something else as well.Obligatory comment that python/typing#566 would help with that as well.
Use the `Any` trick for `xml.etree.ElementTree.getroot()`
Use the `Any` trick for `xml.etree.ElementTree.getroot()` (#8069)
linzouzou commentedon Nov 29, 2023
if "return Element | Any" were used, it's kind of overlapped since "Any" includes "Element".
Akuli commentedon Dec 1, 2023
Indeed. You would expect
Element | Anyto be same as justAny, but it isn't.For example, consider this: (mostly copy/paste from xml module docs)
Anymeans "please do not complain" to type checkers. Ifroothas typeAny, you will no error for this.Elementmeans "will always be an Element", which is wrong, and would cause type checkers to emit errors for code likeif root is None.Element | Nonemeans "you must check for None", which is correct but can get annoying. If I understand correctly (I don't use xml often), it is common to do things likeET.parse("file.xml").getroot().iter("whatever").Element | Anymeans "must be prepared to handle an Element". You will get an error forroot.tagg, because it is not valid whenrootis anElement. But type heckers are happy withif root is Nonechecks, because we're saying it can also be something else than anElement.In typeshed we unofficially call this "the Any trick". We tend to use it whenever something can be
None, but requiring users to check forNonewould be more painful than helpful.Another example of the Any trick is
restubs, where various functions return a match object orNone. Regexes are often used so that the regex matches any string, or the string has already been validated so that the regex will match. But type checkers don't know this, so complaining about theNonewould get very annoying.