Search code examples
pythonmypypython-typing

Why does mypy flag "Item None has no attribute x" error even if I check for None?


Trying to do Python (3.8.8) with type hinting and getting errors from mypy (0.931) that I can't really understand.

import xml.etree.ElementTree as ET
tree = ET.parse('plant_catalog.xml')  # read in file and parse as XML
root = tree.getroot()  # get root node
for plant in root:  # loop through children
    if plant.find("LIGHT") and plant.find("LIGHT").text == "sun" 
        print("foo")

This raises the mypy error Item "None" of "Optional[Element]" has no attribute "text". But why? I do check for the possibility of plant.find("LIGHT") returning None in the first half of the if clause. The second part accessing the .text attribute isn't even executed if the first part fails.

If I modify to

    lights = plant.find("LIGHT")
    if lights:
        if lights.text == selection:            
            print("foo")

the error is gone.

So is this because the plant object might still change in between the first check and the second? But assigning to a variable doesn't automatically copy the content, its still just a reference to an object that might change. So why does it pass the second time?

(Yes, I know that repeating the .find() twice is also not time-efficient.)


Solution

  • mypy doesn't know that plant.find("LIGHT") always returns the same value, so it doesn't know that your test is a proper guard.

    So you need to assign it to a variable. As far as mypy is concerned, the variable can't change from one object to another without being reassigned, and its contents can't change if you don't perform some other operation on it.