Use element is None to test whether xml.etree.ElementTree.find() failed to find an element. An element with no child elements can test false even when it exists and contains text. That makes if element an unreliable existence check on Python versions with this behavior.
Separate missing, empty, text, and children
This fixture distinguishes four cases without loading external XML. The explicit existence checks work independently of Element truth testing.
import xml.etree.ElementTree as ET
root = ET.fromstring(
"<root><empty/><text>ready</text>"
"<parent><child/></parent></root>"
)
for name in ["missing", "empty", "text", "parent"]:
element = root.find(name)
if element is None:
print(name, "missing")
else:
print(name, "found", len(element), repr(element.text))
assert root.find("missing") is None
assert root.find("empty") is not None
assert root.find("text").text == "ready"
assert len(root.find("text")) == 0
assert len(root.find("parent")) == 1The text element is present, contains ready, and has zero child elements. Text content and child-element count answer different questions. A parser can successfully find that leaf while a truth test sends your code down its “missing” branch.
In the Python 3.11, 3.13, and 3.14 runtimes checked for this guide, childless elements tested false. The documentation marks Element truth testing as deprecated from Python 3.12 and describes a future behavior change. Do not depend on a particular future release switching the result; use explicit tests now. Deprecation warnings may be filtered in ordinary execution, so an absent warning is not proof that the pattern is supported indefinitely.
Choose the check that matches the requirement
For existence, use element is None or element is not None. For child-element count, first rule out None, then inspect len(element). For direct text, inspect element.text, which may itself be None. An empty string, whitespace-only text, and missing text may need different treatment in your application.
import xml.etree.ElementTree as ET
def require_direct_text(root, path):
element = root.find(path)
if element is None:
raise ValueError("element is missing")
text = element.text
if text is None or not text.strip():
raise ValueError("direct text is empty")
return text.strip()
assert require_direct_text(ET.fromstring("<r><x> ready </x></r>"), "x") == "ready"
for xml, message in [
("<r/>", "element is missing"),
("<r><x/></r>", "direct text is empty"),
("<r><x> </x></r>", "direct text is empty"),
]:
try:
require_direct_text(ET.fromstring(xml), "x")
except ValueError as error:
assert str(error) == message
else:
raise AssertionError("expected validation failure")
print("missing and empty direct text handled separately")This function requires direct text. It does not collect all nested text. For mixed content such as text surrounding child tags, define whether joining itertext() is appropriate before replacing .text. Likewise, stripping whitespace is an application decision; it can change meaningful content.
Replace or-based fallback too
The same trap appears in root.find("primary") or root.find("fallback"). The or operator truth-tests its first operand. A valid primary leaf can therefore be discarded in favor of a fallback element. Use an explicit missing check instead.
import xml.etree.ElementTree as ET
root = ET.fromstring("<r><primary>first</primary><fallback>second</fallback></r>")
selected = root.find("primary")
if selected is None:
selected = root.find("fallback")
assert selected is not None
assert selected.tag == "primary"
assert selected.text == "first"
print("existing primary leaf retained")Fallback is now triggered only by absence. If your business rule instead requires falling back on empty text, express that additional rule separately. It should not accidentally depend on whether a tag has children.
When find really returns None
Changing the condition cannot make an unsuccessful path match. If the result is actually None, inspect the XML shape, search context, and namespaces. A default namespace qualifies element names even when the source text displays an unprefixed tag.
import xml.etree.ElementTree as ET
root = ET.fromstring('<r xmlns="urn:example"><result>ok</result></r>')
assert root.find("result") is None
element = root.find("n:result", {"n": "urn:example"})
assert element is not None
assert element.text == "ok"
print("namespace-qualified path matched")Keep namespace diagnosis separate from the leaf truth-value issue: the former returns no element, while the latter can misclassify an element already found. These examples validate small trusted strings. They do not establish a safe XML ingestion policy for untrusted uploads; consult Python's XML security guidance for that separate input boundary.