dtlib: add Type enum

Instead of hard-coding constants, use an IntEnum.

These is still a subclass of 'int', but is both easier to import and
easier to read during debugging.

For example, compare:

>>> Type.BYTES
<Type.BYTES: 1>

with:

>>> TYPE_BYTES
1

However, 'Type.BYTES == 1' is still True, and the enum values
otherwise behave like you would expect.

Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
This commit is contained in:
Martí Bolívar 2021-04-13 13:26:08 -07:00 committed by Carles Cufí
commit 74f95688d9
3 changed files with 96 additions and 96 deletions

View file

@ -17,6 +17,7 @@ files.
""" """
import collections import collections
import enum
import errno import errno
import os import os
import re import re
@ -1336,23 +1337,23 @@ class Property:
Assignment | Property.type Assignment | Property.type
----------------------------+------------------------ ----------------------------+------------------------
foo; | dtlib.TYPE_EMPTY foo; | dtlib.Type.EMPTY
foo = []; | dtlib.TYPE_BYTES foo = []; | dtlib.Type.BYTES
foo = [01 02]; | dtlib.TYPE_BYTES foo = [01 02]; | dtlib.Type.BYTES
foo = /bits/ 8 <1>; | dtlib.TYPE_BYTES foo = /bits/ 8 <1>; | dtlib.Type.BYTES
foo = <1>; | dtlib.TYPE_NUM foo = <1>; | dtlib.Type.NUM
foo = <>; | dtlib.TYPE_NUMS foo = <>; | dtlib.Type.NUMS
foo = <1 2 3>; | dtlib.TYPE_NUMS foo = <1 2 3>; | dtlib.Type.NUMS
foo = <1 2>, <3>; | dtlib.TYPE_NUMS foo = <1 2>, <3>; | dtlib.Type.NUMS
foo = "foo"; | dtlib.TYPE_STRING foo = "foo"; | dtlib.Type.STRING
foo = "foo", "bar"; | dtlib.TYPE_STRINGS foo = "foo", "bar"; | dtlib.Type.STRINGS
foo = <&l>; | dtlib.TYPE_PHANDLE foo = <&l>; | dtlib.Type.PHANDLE
foo = <&l1 &l2 &l3>; | dtlib.TYPE_PHANDLES foo = <&l1 &l2 &l3>; | dtlib.Type.PHANDLES
foo = <&l1 &l2>, <&l3>; | dtlib.TYPE_PHANDLES foo = <&l1 &l2>, <&l3>; | dtlib.Type.PHANDLES
foo = <&l1 1 2 &l2 3 4>; | dtlib.TYPE_PHANDLES_AND_NUMS foo = <&l1 1 2 &l2 3 4>; | dtlib.Type.PHANDLES_AND_NUMS
foo = <&l1 1 2>, <&l2 3 4>; | dtlib.TYPE_PHANDLES_AND_NUMS foo = <&l1 1 2>, <&l2 3 4>; | dtlib.Type.PHANDLES_AND_NUMS
foo = &l; | dtlib.TYPE_PATH foo = &l; | dtlib.Type.PATH
*Anything else* | dtlib.TYPE_COMPOUND *Anything else* | dtlib.Type.COMPOUND
*Anything else* includes properties mixing phandle (<&label>) and node *Anything else* includes properties mixing phandle (<&label>) and node
path (&label) references with other data. path (&label) references with other data.
@ -1404,7 +1405,7 @@ class Property:
Returns the value of the property as a number. Returns the value of the property as a number.
Raises DTError if the property was not assigned with this syntax (has Raises DTError if the property was not assigned with this syntax (has
Property.type TYPE_NUM): Property.type Type.NUM):
foo = < 1 >; foo = < 1 >;
@ -1412,7 +1413,7 @@ class Property:
If True, the value will be interpreted as signed rather than If True, the value will be interpreted as signed rather than
unsigned. unsigned.
""" """
if self.type is not TYPE_NUM: if self.type is not Type.NUM:
_err("expected property '{0}' on {1} in {2} to be assigned with " _err("expected property '{0}' on {1} in {2} to be assigned with "
"'{0} = < (number) >;', not '{3}'" "'{0} = < (number) >;', not '{3}'"
.format(self.name, self.node.path, self.node.dt.filename, .format(self.name, self.node.path, self.node.dt.filename,
@ -1425,7 +1426,7 @@ class Property:
Returns the value of the property as a list of numbers. Returns the value of the property as a list of numbers.
Raises DTError if the property was not assigned with this syntax (has Raises DTError if the property was not assigned with this syntax (has
Property.type TYPE_NUM or TYPE_NUMS): Property.type Type.NUM or Type.NUMS):
foo = < 1 2 ... >; foo = < 1 2 ... >;
@ -1433,7 +1434,7 @@ class Property:
If True, the values will be interpreted as signed rather than If True, the values will be interpreted as signed rather than
unsigned. unsigned.
""" """
if self.type not in (TYPE_NUM, TYPE_NUMS): if self.type not in (Type.NUM, Type.NUMS):
_err("expected property '{0}' on {1} in {2} to be assigned with " _err("expected property '{0}' on {1} in {2} to be assigned with "
"'{0} = < (number) (number) ... >;', not '{3}'" "'{0} = < (number) (number) ... >;', not '{3}'"
.format(self.name, self.node.path, self.node.dt.filename, .format(self.name, self.node.path, self.node.dt.filename,
@ -1448,11 +1449,11 @@ class Property:
Property.value, except with added type checking. Property.value, except with added type checking.
Raises DTError if the property was not assigned with this syntax (has Raises DTError if the property was not assigned with this syntax (has
Property.type TYPE_BYTES): Property.type Type.BYTES):
foo = [ 01 ... ]; foo = [ 01 ... ];
""" """
if self.type is not TYPE_BYTES: if self.type is not Type.BYTES:
_err("expected property '{0}' on {1} in {2} to be assigned with " _err("expected property '{0}' on {1} in {2} to be assigned with "
"'{0} = [ (byte) (byte) ... ];', not '{3}'" "'{0} = [ (byte) (byte) ... ];', not '{3}'"
.format(self.name, self.node.path, self.node.dt.filename, .format(self.name, self.node.path, self.node.dt.filename,
@ -1465,14 +1466,14 @@ class Property:
Returns the value of the property as a string. Returns the value of the property as a string.
Raises DTError if the property was not assigned with this syntax (has Raises DTError if the property was not assigned with this syntax (has
Property.type TYPE_STRING): Property.type Type.STRING):
foo = "string"; foo = "string";
This function might also raise UnicodeDecodeError if the string is This function might also raise UnicodeDecodeError if the string is
not valid UTF-8. not valid UTF-8.
""" """
if self.type is not TYPE_STRING: if self.type is not Type.STRING:
_err("expected property '{0}' on {1} in {2} to be assigned with " _err("expected property '{0}' on {1} in {2} to be assigned with "
"'{0} = \"string\";', not '{3}'" "'{0} = \"string\";', not '{3}'"
.format(self.name, self.node.path, self.node.dt.filename, .format(self.name, self.node.path, self.node.dt.filename,
@ -1490,13 +1491,13 @@ class Property:
Returns the value of the property as a list of strings. Returns the value of the property as a list of strings.
Raises DTError if the property was not assigned with this syntax (has Raises DTError if the property was not assigned with this syntax (has
Property.type TYPE_STRING or TYPE_STRINGS): Property.type Type.STRING or Type.STRINGS):
foo = "string", "string", ... ; foo = "string", "string", ... ;
Also raises DTError if any of the strings are not valid UTF-8. Also raises DTError if any of the strings are not valid UTF-8.
""" """
if self.type not in (TYPE_STRING, TYPE_STRINGS): if self.type not in (Type.STRING, Type.STRINGS):
_err("expected property '{0}' on {1} in {2} to be assigned with " _err("expected property '{0}' on {1} in {2} to be assigned with "
"'{0} = \"string\", \"string\", ... ;', not '{3}'" "'{0} = \"string\", \"string\", ... ;', not '{3}'"
.format(self.name, self.node.path, self.node.dt.filename, .format(self.name, self.node.path, self.node.dt.filename,
@ -1514,11 +1515,11 @@ class Property:
Returns the Node the phandle in the property points to. Returns the Node the phandle in the property points to.
Raises DTError if the property was not assigned with this syntax (has Raises DTError if the property was not assigned with this syntax (has
Property.type TYPE_PHANDLE). Property.type Type.PHANDLE).
foo = < &bar >; foo = < &bar >;
""" """
if self.type is not TYPE_PHANDLE: if self.type is not Type.PHANDLE:
_err("expected property '{0}' on {1} in {2} to be assigned with " _err("expected property '{0}' on {1} in {2} to be assigned with "
"'{0} = < &foo >;', not '{3}'" "'{0} = < &foo >;', not '{3}'"
.format(self.name, self.node.path, self.node.dt.filename, .format(self.name, self.node.path, self.node.dt.filename,
@ -1539,10 +1540,10 @@ class Property:
foo = < &bar ... >, < &baz ... >; foo = < &bar ... >, < &baz ... >;
""" """
def type_ok(): def type_ok():
if self.type in (TYPE_PHANDLE, TYPE_PHANDLES): if self.type in (Type.PHANDLE, Type.PHANDLES):
return True return True
# Also accept 'foo = < >;' # Also accept 'foo = < >;'
return self.type is TYPE_NUMS and not self.value return self.type is Type.NUMS and not self.value
if not type_ok(): if not type_ok():
_err("expected property '{0}' on {1} in {2} to be assigned with " _err("expected property '{0}' on {1} in {2} to be assigned with "
@ -1559,14 +1560,14 @@ class Property:
Returns the Node referenced by the path stored in the property. Returns the Node referenced by the path stored in the property.
Raises DTError if the property was not assigned with either of these Raises DTError if the property was not assigned with either of these
syntaxes (has Property.type TYPE_PATH or TYPE_STRING): syntaxes (has Property.type Type.PATH or Type.STRING):
foo = &bar; foo = &bar;
foo = "/bar"; foo = "/bar";
For the second case, DTError is raised if the path does not exist. For the second case, DTError is raised if the path does not exist.
""" """
if self.type not in (TYPE_PATH, TYPE_STRING): if self.type not in (Type.PATH, Type.STRING):
_err("expected property '{0}' on {1} in {2} to be assigned with " _err("expected property '{0}' on {1} in {2} to be assigned with "
"either '{0} = &foo' or '{0} = \"/path/to/node\"', not '{3}'" "either '{0} = &foo' or '{0} = \"/path/to/node\"', not '{3}'"
.format(self.name, self.node.path, self.node.dt.filename, .format(self.name, self.node.path, self.node.dt.filename,
@ -1597,35 +1598,35 @@ class Property:
if marker[1] != _REF_LABEL] if marker[1] != _REF_LABEL]
if not types: if not types:
return TYPE_EMPTY return Type.EMPTY
if types == [_TYPE_UINT8]: if types == [_TYPE_UINT8]:
return TYPE_BYTES return Type.BYTES
if types == [_TYPE_UINT32]: if types == [_TYPE_UINT32]:
return TYPE_NUM if len(self.value) == 4 else TYPE_NUMS return Type.NUM if len(self.value) == 4 else Type.NUMS
# Treat 'foo = <1 2 3>, <4 5>, ...' as TYPE_NUMS too # Treat 'foo = <1 2 3>, <4 5>, ...' as Type.NUMS too
if set(types) == {_TYPE_UINT32}: if set(types) == {_TYPE_UINT32}:
return TYPE_NUMS return Type.NUMS
if set(types) == {_TYPE_STRING}: if set(types) == {_TYPE_STRING}:
return TYPE_STRING if len(types) == 1 else TYPE_STRINGS return Type.STRING if len(types) == 1 else Type.STRINGS
if types == [_REF_PATH]: if types == [_REF_PATH]:
return TYPE_PATH return Type.PATH
if types == [_TYPE_UINT32, _REF_PHANDLE] and len(self.value) == 4: if types == [_TYPE_UINT32, _REF_PHANDLE] and len(self.value) == 4:
return TYPE_PHANDLE return Type.PHANDLE
if set(types) == {_TYPE_UINT32, _REF_PHANDLE}: if set(types) == {_TYPE_UINT32, _REF_PHANDLE}:
if len(self.value) == 4*types.count(_REF_PHANDLE): if len(self.value) == 4*types.count(_REF_PHANDLE):
# Array with just phandles in it # Array with just phandles in it
return TYPE_PHANDLES return Type.PHANDLES
# Array with both phandles and numbers # Array with both phandles and numbers
return TYPE_PHANDLES_AND_NUMS return Type.PHANDLES_AND_NUMS
return TYPE_COMPOUND return Type.COMPOUND
def __str__(self): def __str__(self):
s = "".join(label + ": " for label in self.labels) + self.name s = "".join(label + ": " for label in self.labels) + self.name
@ -1761,17 +1762,18 @@ def to_nums(data, length=4, signed=False):
# #
# See Property.type # See Property.type
TYPE_EMPTY = 0 class Type(enum.IntEnum):
TYPE_BYTES = 1 EMPTY = 0
TYPE_NUM = 2 BYTES = 1
TYPE_NUMS = 3 NUM = 2
TYPE_STRING = 4 NUMS = 3
TYPE_STRINGS = 5 STRING = 4
TYPE_PATH = 6 STRINGS = 5
TYPE_PHANDLE = 7 PATH = 6
TYPE_PHANDLES = 8 PHANDLE = 7
TYPE_PHANDLES_AND_NUMS = 9 PHANDLES = 8
TYPE_COMPOUND = 10 PHANDLES_AND_NUMS = 9
COMPOUND = 10
def _check_is_bytes(data): def _check_is_bytes(data):

View file

@ -81,10 +81,7 @@ try:
except ImportError: except ImportError:
from yaml import Loader from yaml import Loader
from devicetree.dtlib import \ from devicetree.dtlib import DT, DTError, to_num, to_nums, Type
DT, DTError, to_num, to_nums, TYPE_EMPTY, TYPE_BYTES, \
TYPE_NUM, TYPE_NUMS, TYPE_STRING, TYPE_STRINGS, \
TYPE_PHANDLE, TYPE_PHANDLES, TYPE_PHANDLES_AND_NUMS
from devicetree.grutils import Graph from devicetree.grutils import Graph
@ -823,26 +820,27 @@ class Node:
} }
for name, prop in self._node.props.items(): for name, prop in self._node.props.items():
pp = OrderedDict() pp = OrderedDict()
if prop.type == TYPE_EMPTY: if prop.type == Type.EMPTY:
pp["type"] = "boolean" pp["type"] = "boolean"
elif prop.type == TYPE_BYTES: elif prop.type == Type.BYTES:
pp["type"] = "uint8-array" pp["type"] = "uint8-array"
elif prop.type == TYPE_NUM: elif prop.type == Type.NUM:
pp["type"] = "int" pp["type"] = "int"
elif prop.type == TYPE_NUMS: elif prop.type == Type.NUMS:
pp["type"] = "array" pp["type"] = "array"
elif prop.type == TYPE_STRING: elif prop.type == Type.STRING:
pp["type"] = "string" pp["type"] = "string"
elif prop.type == TYPE_STRINGS: elif prop.type == Type.STRINGS:
pp["type"] = "string-array" pp["type"] = "string-array"
elif prop.type == TYPE_PHANDLE: elif prop.type == Type.PHANDLE:
pp["type"] = "phandle" pp["type"] = "phandle"
elif prop.type == TYPE_PHANDLES: elif prop.type == Type.PHANDLES:
pp["type"] = "phandles" pp["type"] = "phandles"
elif prop.type == TYPE_PHANDLES_AND_NUMS: elif prop.type == Type.PHANDLES_AND_NUMS:
pp["type"] = "phandle-array" pp["type"] = "phandle-array"
else: else:
_err(f"cannot infer binding from property: {prop}") _err(f"cannot infer binding from property: {prop} "
f"with type {prop.type}")
raw['properties'][name] = pp raw['properties'][name] = pp
# Set up Node state. # Set up Node state.
@ -1007,7 +1005,7 @@ class Node:
return False if prop_type == "boolean" else None return False if prop_type == "boolean" else None
if prop_type == "boolean": if prop_type == "boolean":
if prop.type is not TYPE_EMPTY: if prop.type is not Type.EMPTY:
_err("'{0}' in {1!r} is defined with 'type: boolean' in {2}, " _err("'{0}' in {1!r} is defined with 'type: boolean' in {2}, "
"but is assigned a value ('{3}') instead of being empty " "but is assigned a value ('{3}') instead of being empty "
"('{0};')".format(name, node, self.binding_path, prop)) "('{0};')".format(name, node, self.binding_path, prop))
@ -1038,7 +1036,7 @@ class Node:
# This type is a bit high-level for dtlib as it involves # This type is a bit high-level for dtlib as it involves
# information from bindings and *-names properties, so there's no # information from bindings and *-names properties, so there's no
# to_phandle_array() in dtlib. Do the type check ourselves. # to_phandle_array() in dtlib. Do the type check ourselves.
if prop.type not in (TYPE_PHANDLE, TYPE_PHANDLES, TYPE_PHANDLES_AND_NUMS): if prop.type not in (Type.PHANDLE, Type.PHANDLES, Type.PHANDLES_AND_NUMS):
_err(f"expected property '{name}' in {node.path} in " _err(f"expected property '{name}' in {node.path} in "
f"{node.dt.filename} to be assigned " f"{node.dt.filename} to be assigned "
f"with '{name} = < &foo ... &bar 1 ... &baz 2 3 >' " f"with '{name} = < &foo ... &bar 1 ... &baz 2 3 >' "
@ -2655,7 +2653,7 @@ def _check_dt(dt):
ranges_prop = node.props.get("ranges") ranges_prop = node.props.get("ranges")
if ranges_prop: if ranges_prop:
if ranges_prop.type not in (TYPE_EMPTY, TYPE_NUMS): if ranges_prop.type not in (Type.EMPTY, Type.NUMS):
_err("expected 'ranges = < ... >;' in {} in {}, not '{}' " _err("expected 'ranges = < ... >;' in {} in {}, not '{}' "
"(see the devicetree specification)" "(see the devicetree specification)"
.format(node.path, node.dt.filename, ranges_prop)) .format(node.path, node.dt.filename, ranges_prop))

View file

@ -1538,30 +1538,30 @@ def test_prop_type():
}; };
""") """)
verify_type("empty", dtlib.TYPE_EMPTY) verify_type("empty", dtlib.Type.EMPTY)
verify_type("bytes1", dtlib.TYPE_BYTES) verify_type("bytes1", dtlib.Type.BYTES)
verify_type("bytes2", dtlib.TYPE_BYTES) verify_type("bytes2", dtlib.Type.BYTES)
verify_type("bytes3", dtlib.TYPE_BYTES) verify_type("bytes3", dtlib.Type.BYTES)
verify_type("bytes4", dtlib.TYPE_BYTES) verify_type("bytes4", dtlib.Type.BYTES)
verify_type("bytes5", dtlib.TYPE_BYTES) verify_type("bytes5", dtlib.Type.BYTES)
verify_type("num", dtlib.TYPE_NUM) verify_type("num", dtlib.Type.NUM)
verify_type("nums1", dtlib.TYPE_NUMS) verify_type("nums1", dtlib.Type.NUMS)
verify_type("nums2", dtlib.TYPE_NUMS) verify_type("nums2", dtlib.Type.NUMS)
verify_type("nums3", dtlib.TYPE_NUMS) verify_type("nums3", dtlib.Type.NUMS)
verify_type("nums4", dtlib.TYPE_NUMS) verify_type("nums4", dtlib.Type.NUMS)
verify_type("string", dtlib.TYPE_STRING) verify_type("string", dtlib.Type.STRING)
verify_type("strings", dtlib.TYPE_STRINGS) verify_type("strings", dtlib.Type.STRINGS)
verify_type("phandle1", dtlib.TYPE_PHANDLE) verify_type("phandle1", dtlib.Type.PHANDLE)
verify_type("phandle2", dtlib.TYPE_PHANDLE) verify_type("phandle2", dtlib.Type.PHANDLE)
verify_type("phandles1", dtlib.TYPE_PHANDLES) verify_type("phandles1", dtlib.Type.PHANDLES)
verify_type("phandles2", dtlib.TYPE_PHANDLES) verify_type("phandles2", dtlib.Type.PHANDLES)
verify_type("phandle-and-nums-1", dtlib.TYPE_PHANDLES_AND_NUMS) verify_type("phandle-and-nums-1", dtlib.Type.PHANDLES_AND_NUMS)
verify_type("phandle-and-nums-2", dtlib.TYPE_PHANDLES_AND_NUMS) verify_type("phandle-and-nums-2", dtlib.Type.PHANDLES_AND_NUMS)
verify_type("phandle-and-nums-3", dtlib.TYPE_PHANDLES_AND_NUMS) verify_type("phandle-and-nums-3", dtlib.Type.PHANDLES_AND_NUMS)
verify_type("path1", dtlib.TYPE_PATH) verify_type("path1", dtlib.Type.PATH)
verify_type("path2", dtlib.TYPE_PATH) verify_type("path2", dtlib.Type.PATH)
verify_type("compound1", dtlib.TYPE_COMPOUND) verify_type("compound1", dtlib.Type.COMPOUND)
verify_type("compound2", dtlib.TYPE_COMPOUND) verify_type("compound2", dtlib.Type.COMPOUND)
def test_prop_type_casting(): def test_prop_type_casting():
'''Test Property.to_{num,nums,string,strings,node}()''' '''Test Property.to_{num,nums,string,strings,node}()'''