dtlib: add type checking for DT.root

We'd like users of this API to know that DT.root is always a Node,
and not an Optional[Node].

However, although DT.__init__ throws an exception if the resulting DT
object would have no root node, static analysis can't tell that since
the root instance attribute starts out as None during initialization,
so checkers like mypy are convinced it's Optional[Node].

Since this is really OK, we'll quiet the type checker down by stashing
the instance attribute in self._root instead, and providing a root
property accessor that is annotated to return Node instead of
Optional[Node]. We can tell mypy to ignore what looks like a potential
None here to allow callers to treat the result as a Node.

Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
This commit is contained in:
Martí Bolívar 2021-04-23 13:58:04 -07:00 committed by Kumar Gala
commit c6bb336bc1

View file

@ -723,6 +723,8 @@ class DT:
self._lineno = 1 self._lineno = 1
self._root: Optional[Node] = None
self._parse_dt() self._parse_dt()
self._register_phandles() self._register_phandles()
@ -731,6 +733,16 @@ class DT:
self._remove_unreferenced() self._remove_unreferenced()
self._register_labels() self._register_labels()
@property
def root(self) -> Node:
"""
See the class documentation.
"""
# This is necessary because mypy can't tell that we never
# treat self._root as a non-None value until it's initialized
# properly in _parse_dt().
return self._root # type: ignore
def get_node(self, path): def get_node(self, path):
""" """
Returns the Node instance for the node with path or alias 'path' (a Returns the Node instance for the node with path or alias 'path' (a
@ -827,15 +839,13 @@ class DT:
self._parse_header() self._parse_header()
self._parse_memreserves() self._parse_memreserves()
self.root = None
while True: while True:
tok = self._next_token() tok = self._next_token()
if tok.val == "/": if tok.val == "/":
# '/ { ... };', the root node # '/ { ... };', the root node
if not self.root: if not self._root:
self.root = Node(name="/", parent=None, dt=self) self._root = Node(name="/", parent=None, dt=self)
self._parse_node(self.root) self._parse_node(self.root)
elif tok.id in (_T.LABEL, _T.REF): elif tok.id in (_T.LABEL, _T.REF):
@ -868,7 +878,7 @@ class DT:
self._expect_token(";") self._expect_token(";")
elif tok.id == _T.EOF: elif tok.id == _T.EOF:
if not self.root: if not self._root:
self._parse_error("no root node defined") self._parse_error("no root node defined")
return return