dtlib: use f-strings where it makes sense

The library was originally developed before Python 3.6 was the minimum
supported version. Use f-strings now that we can do that, as they tend
to be easier to read.

There are a few places where str.format() makes sense to preserve,
specifically where the same argument is used multiple times; leave
those alone.

Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
This commit is contained in:
Martí Bolívar 2021-08-19 21:03:10 -07:00 committed by Anas Nashif
commit 66ee3d291c

View file

@ -159,7 +159,7 @@ class Node:
""" """
s = "".join(label + ": " for label in self.labels) s = "".join(label + ": " for label in self.labels)
s += "{} {{\n".format(self.name) s += f"{self.name} {{\n"
for prop in self.props.values(): for prop in self.props.values():
s += "\t" + str(prop) + "\n" s += "\t" + str(prop) + "\n"
@ -176,8 +176,7 @@ class Node:
Returns some information about the Node instance. Called automatically Returns some information about the Node instance. Called automatically
if the Node instance is evaluated. if the Node instance is evaluated.
""" """
return "<Node {} in '{}'>" \ return f"<Node {self.path} in '{self.dt.filename}'>"
.format(self.path, self.dt.filename)
# See Property.type # See Property.type
class Type(enum.IntEnum): class Type(enum.IntEnum):
@ -381,9 +380,9 @@ class Property:
try: try:
ret = self.value.decode("utf-8")[:-1] # Strip null ret = self.value.decode("utf-8")[:-1] # Strip null
except UnicodeDecodeError: except UnicodeDecodeError:
_err("value of property '{}' ({!r}) on {} in {} is not valid UTF-8" _err(f"value of property '{self.name}' ({self.value!r}) "
.format(self.name, self.value, self.node.path, f"on {self.node.path} in {self.node.dt.filename} "
self.node.dt.filename)) "is not valid UTF-8")
return ret # The separate 'return' appeases the type checker. return ret # The separate 'return' appeases the type checker.
@ -407,9 +406,9 @@ class Property:
try: try:
ret = self.value.decode("utf-8").split("\0")[:-1] ret = self.value.decode("utf-8").split("\0")[:-1]
except UnicodeDecodeError: except UnicodeDecodeError:
_err("value of property '{}' ({!r}) on {} in {} is not valid UTF-8" _err(f"value of property '{self.name}' ({self.value!r}) "
.format(self.name, self.value, self.node.path, f"on {self.node.path} in {self.node.dt.filename} "
self.node.dt.filename)) "is not valid UTF-8")
return ret # The separate 'return' appeases the type checker. return ret # The separate 'return' appeases the type checker.
@ -479,16 +478,16 @@ class Property:
try: try:
path = self.value.decode("utf-8")[:-1] path = self.value.decode("utf-8")[:-1]
except UnicodeDecodeError: except UnicodeDecodeError:
_err("value of property '{}' ({!r}) on {} in {} is not valid UTF-8" _err(f"value of property '{self.name}' ({self.value!r}) "
.format(self.name, self.value, self.node.path, f"on {self.node.path} in {self.node.dt.filename} "
self.node.dt.filename)) "is not valid UTF-8")
try: try:
ret = self.node.dt.get_node(path) ret = self.node.dt.get_node(path)
except DTError: except DTError:
_err("property '{}' on {} in {} points to the non-existent node " _err(f"property '{self.name}' on {self.node.path} in "
"\"{}\"".format(self.name, self.node.path, f"{self.node.dt.filename} points to the non-existent node "
self.node.dt.filename, path)) f'"{path}"')
return ret # The separate 'return' appeases the type checker. return ret # The separate 'return' appeases the type checker.
@ -552,8 +551,7 @@ class Property:
if marker_type is _MarkerType.STRING: if marker_type is _MarkerType.STRING:
# end - 1 to strip off the null terminator # end - 1 to strip off the null terminator
s += ' "{}"'.format(_decode_and_escape( s += f' "{_decode_and_escape(self.value[pos:end - 1])}"'
self.value[pos:end - 1]))
if end != len(self.value): if end != len(self.value):
s += "," s += ","
elif marker_type is _MarkerType.PATH: elif marker_type is _MarkerType.PATH:
@ -564,7 +562,7 @@ class Property:
# <> or [] # <> or []
if marker_type is _MarkerType.LABEL: if marker_type is _MarkerType.LABEL:
s += " {}:".format(ref) s += f" {ref}:"
elif marker_type is _MarkerType.PHANDLE: elif marker_type is _MarkerType.PHANDLE:
s += " &" + ref s += " &" + ref
pos += 4 pos += 4
@ -578,9 +576,9 @@ class Property:
num = int.from_bytes(self.value[pos:pos + elm_size], num = int.from_bytes(self.value[pos:pos + elm_size],
"big") "big")
if elm_size == 1: if elm_size == 1:
s += " {:02X}".format(num) s += f" {num:02X}"
else: else:
s += " " + hex(num) s += f" {hex(num)}"
pos += elm_size pos += elm_size
@ -596,8 +594,8 @@ class Property:
def __repr__(self): def __repr__(self):
return "<Property '{}' at '{}' in '{}'>" \ return f"<Property '{self.name}' at '{self.node.path}' in " \
.format(self.name, self.node.path, self.node.dt.filename) f"'{self.node.dt.filename}'>"
# #
# Internal functions # Internal functions
@ -796,8 +794,8 @@ class DT:
# Path does not start with '/'. First component must be an alias. # Path does not start with '/'. First component must be an alias.
alias, _, rest = path.partition("/") alias, _, rest = path.partition("/")
if alias not in self.alias2node: if alias not in self.alias2node:
_err("no alias '{}' found -- did you forget the leading '/' in " _err(f"no alias '{alias}' found -- did you forget the leading "
"the node path?".format(alias)) "'/' in the node path?")
return _root_and_path_to_node(self.alias2node[alias], rest, path) return _root_and_path_to_node(self.alias2node[alias], rest, path)
@ -835,9 +833,8 @@ class DT:
for labels, address, offset in self.memreserves: for labels, address, offset in self.memreserves:
# List the labels in a consistent order to help with testing # List the labels in a consistent order to help with testing
for label in labels: for label in labels:
s += label + ": " s += f"{label}: "
s += "/memreserve/ {:#018x} {:#018x};\n" \ s += f"/memreserve/ {address:#018x} {offset:#018x};\n"
.format(address, offset)
s += "\n" s += "\n"
return s + str(self.root) return s + str(self.root)
@ -847,8 +844,8 @@ class DT:
Returns some information about the DT instance. Called automatically if Returns some information about the DT instance. Called automatically if
the DT instance is evaluated. the DT instance is evaluated.
""" """
return "DT(filename='{}', include_path={})" \ return f"DT(filename='{self.filename}', " \
.format(self.filename, self._include_path) f"include_path={self._include_path})"
# #
# Parsing # Parsing
@ -1119,8 +1116,8 @@ class DT:
# Try again as a signed number, in case it's negative # Try again as a signed number, in case it's negative
prop.value += num.to_bytes(n_bytes, "big", signed=True) prop.value += num.to_bytes(n_bytes, "big", signed=True)
except OverflowError: except OverflowError:
self._parse_error("{} does not fit in {} bits" self._parse_error(
.format(num, 8*n_bytes)) f"{num} does not fit in {8*n_bytes} bits")
def _parse_bytes(self, prop): def _parse_bytes(self, prop):
# Parses '[ ... ]' # Parses '[ ... ]'
@ -1178,8 +1175,7 @@ class DT:
f.seek(offset) f.seek(offset)
prop.value += f.read(size) prop.value += f.read(size)
except OSError as e: except OSError as e:
self._parse_error("could not read '{}': {}" self._parse_error(f"could not read '{filename}': {e}")
.format(filename, e))
def _parse_value_labels(self, prop): def _parse_value_labels(self, prop):
# _parse_assignment() helper for parsing labels before/after each # _parse_assignment() helper for parsing labels before/after each
@ -1465,8 +1461,7 @@ class DT:
tok = self._next_token() tok = self._next_token()
if tok.val != tok_val: if tok.val != tok_val:
self._parse_error("expected '{}', not '{}'" self._parse_error(f"expected '{tok_val}', not '{tok.val}'")
.format(tok_val, tok.val))
return tok return tok
@ -1479,12 +1474,12 @@ class DT:
return tok.val return tok.val
def _parse_error(self, s): def _parse_error(self, s):
_err("{}:{} (column {}): parse error: {}".format(
self.filename, self._lineno,
# This works out for the first line of the file too, where rfind() # This works out for the first line of the file too, where rfind()
# returns -1 # returns -1
self._tok_i - self._file_contents.rfind("\n", 0, self._tok_i + 1), column = self._tok_i - self._file_contents.rfind("\n", 0,
s)) self._tok_i + 1)
_err(f"{self.filename}:{self._lineno} (column {column}): "
f"parse error: {s}")
def _enter_file(self, filename): def _enter_file(self, filename):
# Enters the /include/d file 'filename', remembering the position in # Enters the /include/d file 'filename', remembering the position in
@ -1511,7 +1506,7 @@ class DT:
for i, parent in enumerate(self._filestack): for i, parent in enumerate(self._filestack):
if filename == parent[0]: if filename == parent[0]:
self._parse_error("recursive /include/:\n" + " ->\n".join( self._parse_error("recursive /include/:\n" + " ->\n".join(
["{}:{}".format(parent[0], parent[1]) [f"{parent[0]}:{parent[1]}"
for parent in self._filestack[i:]] + for parent in self._filestack[i:]] +
[filename])) [filename]))
@ -1546,7 +1541,7 @@ class DT:
# Path reference (&{/foo/bar}) # Path reference (&{/foo/bar})
path = s[1:-1] path = s[1:-1]
if not path.startswith("/"): if not path.startswith("/"):
_err("node path '{}' does not start with '/'".format(path)) _err(f"node path '{path}' does not start with '/'")
# Will raise DTError if the path doesn't exist # Will raise DTError if the path doesn't exist
return _root_and_path_to_node(self.root, path, path) return _root_and_path_to_node(self.root, path, path)
@ -1558,7 +1553,7 @@ class DT:
if s in node.labels: if s in node.labels:
return node return node
_err("undefined node label '{}'".format(s)) _err(f"undefined node label '{s}'")
# #
# Post-processing # Post-processing
@ -1575,8 +1570,8 @@ class DT:
phandle = node.props.get("phandle") phandle = node.props.get("phandle")
if phandle: if phandle:
if len(phandle.value) != 4: if len(phandle.value) != 4:
_err("{}: bad phandle length ({}), expected 4 bytes" _err(f"{node.path}: bad phandle length "
.format(node.path, len(phandle.value))) f"({len(phandle.value)}), expected 4 bytes")
is_self_referential = False is_self_referential = False
for marker in phandle._markers: for marker in phandle._markers:
@ -1591,8 +1586,8 @@ class DT:
is_self_referential = True is_self_referential = True
break break
_err("{}: {} refers to another node" _err(f"{node.path}: {phandle.name} "
.format(node.path, phandle.name)) "refers to another node")
# Could put on else on the 'for' above too, but keep it # Could put on else on the 'for' above too, but keep it
# somewhat readable # somewhat readable
@ -1600,13 +1595,13 @@ class DT:
phandle_val = int.from_bytes(phandle.value, "big") phandle_val = int.from_bytes(phandle.value, "big")
if phandle_val in {0, 0xFFFFFFFF}: if phandle_val in {0, 0xFFFFFFFF}:
_err("{}: bad value {:#010x} for {}" _err(f"{node.path}: bad value {phandle_val:#010x} "
.format(node.path, phandle_val, phandle.name)) f"for {phandle.name}")
if phandle_val in self.phandle2node: if phandle_val in self.phandle2node:
_err("{}: duplicated phandle {:#x} (seen before at {})" _err(f"{node.path}: duplicated phandle {phandle_val:#x} "
.format(node.path, phandle_val, "(seen before at "
self.phandle2node[phandle_val].path)) f"{self.phandle2node[phandle_val].path})")
self.phandle2node[phandle_val] = node self.phandle2node[phandle_val] = node
@ -1649,7 +1644,7 @@ class DT:
try: try:
ref_node = self._ref2node(ref) ref_node = self._ref2node(ref)
except DTError as e: except DTError as e:
_err("{}: {}".format(prop.node.path, e)) _err(f"{prop.node.path}: {e}")
# For /omit-if-no-ref/ # For /omit-if-no-ref/
ref_node._is_referenced = True ref_node._is_referenced = True
@ -1681,8 +1676,8 @@ class DT:
if aliases: if aliases:
for prop in aliases.props.values(): for prop in aliases.props.values():
if not alias_re.match(prop.name): if not alias_re.match(prop.name):
_err("/aliases: alias property name '{}' should include " _err(f"/aliases: alias property name '{prop.name}' "
"only characters from [0-9a-z-]".format(prop.name)) "should include only characters from [0-9a-z-]")
# Property.to_path() checks that the node exists, has # Property.to_path() checks that the node exists, has
# the right type, etc. Swallow errors for invalid # the right type, etc. Swallow errors for invalid
@ -1739,21 +1734,20 @@ class DT:
strings = [] strings = []
for thing in things: for thing in things:
if isinstance(thing, Node): if isinstance(thing, Node):
strings.append("on " + thing.path) strings.append(f"on {thing.path}")
elif isinstance(thing, Property): elif isinstance(thing, Property):
strings.append("on property '{}' of node {}" strings.append(f"on property '{thing.name}' "
.format(thing.name, thing.node.path)) f"of node {thing.node.path}")
else: else:
# Label within property value # Label within property value
strings.append("in the value of property '{}' of node {}" strings.append("in the value of property "
.format(thing[0].name, f"'{thing[0].name}' of node "
thing[0].node.path)) f"{thing[0].node.path}")
# Give consistent error messages to help with testing # Give consistent error messages to help with testing
strings.sort() strings.sort()
_err("Label '{}' appears ".format(label) + _err(f"Label '{label}' appears " + " and ".join(strings))
" and ".join(strings))
# #
@ -1817,7 +1811,7 @@ class DT:
self._parse_error(e) self._parse_error(e)
continue continue
self._parse_error("'{}' could not be found".format(filename)) self._parse_error(f"'{filename}' could not be found")
# #
# Public functions # Public functions
@ -1840,8 +1834,7 @@ def to_num(data: bytes, length: Optional[int] = None,
if length is not None: if length is not None:
_check_length_positive(length) _check_length_positive(length)
if len(data) != length: if len(data) != length:
_err("{!r} is {} bytes long, expected {}" _err(f"{data!r} is {len(data)} bytes long, expected {length}")
.format(data, len(data), length))
return int.from_bytes(data, "big", signed=signed) return int.from_bytes(data, "big", signed=signed)
@ -1854,8 +1847,8 @@ def to_nums(data: bytes, length: int = 4, signed: bool = False) -> List[int]:
_check_length_positive(length) _check_length_positive(length)
if len(data) % length: if len(data) % length:
_err("{!r} is {} bytes long, expected a length that's a a multiple of {}" _err(f"{data!r} is {len(data)} bytes long, "
.format(data, len(data), length)) f"expected a length that's a a multiple of {length}")
return [int.from_bytes(data[i:i + length], "big", signed=signed) return [int.from_bytes(data[i:i + length], "big", signed=signed)
for i in range(0, len(data), length)] for i in range(0, len(data), length)]
@ -1866,8 +1859,7 @@ def to_nums(data: bytes, length: int = 4, signed: bool = False) -> List[int]:
def _check_is_bytes(data): def _check_is_bytes(data):
if not isinstance(data, bytes): if not isinstance(data, bytes):
_err("'{}' has type '{}', expected 'bytes'" _err(f"'{data}' has type '{type(data).__name__}', expected 'bytes'")
.format(data, type(data).__name__))
def _check_length_positive(length): def _check_length_positive(length):
if length < 1: if length < 1:
@ -1905,8 +1897,8 @@ def _root_and_path_to_node(cur, path, fullpath):
continue continue
if component not in cur.nodes: if component not in cur.nodes:
_err("component '{}' in path '{}' does not exist" _err(f"component '{component}' in path '{fullpath}' "
.format(component, fullpath)) "does not exist")
cur = cur.nodes[component] cur = cur.nodes[component]