Now that all the other code it depends on is annotated, we can finish
up the type annotation of this module in the main DT class.
It's not worth it to try to annotate the private methods (the ones
that begin with '_'). Most of these are low level lexing helpers that
aren't particularly amenable to static type checking, because the type
of a token's value is often dependent on the token ID in ways that
static type annotations are not well equipped to capture.
Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
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>
The documentation says DT.__init__ takes any iterable for the
include_path, but this leads to bad results when you pass it something
other than a 'real' sequence (list/tuple/etc), like a generator:
>>> dt = DT('/tmp/foo.dts', (x for x in ['a', 'b', 'c']))
>>> repr(dt)
"DT(filename='/tmp/foo.dts', include_path=<generator object ...>)"
Make a copy in list form just to avoid things like this.
Add a test for this and relax the regular expression in the existing
test case related to this.
Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
Some of these are also tripping up a python 2 / python 3 warning
in mypy in the way that '{}'.format(b'foo') works, which we silence by
explicitly requesting the python 3 behavior.
Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
The way that _init_tokens() is manipulating globals() defeats static
analyses of the file that are trying to infer a type for the 'tok_id'
variable in assignment expressions like 'tok_id = _T_INCLUDE'.
To make it easier on the analyser, define the token types as an
enum.IntEnum named _T. This means we can write e.g. '_T.INCLUDE'
instead of '_T_INCLUDE', avoiding line length increases in the lexing
code.
While we're here, use '==' and '!=' instead of 'is' and 'is not'
when comparing a tok_id that is obtained from an re.Match.lastindex
with a _T.FOO value.
This is now necessary since an int object and a _T object definitely
don't point to the same memory. It worked previously because CPython
interns all integer instances from -5 to 256, but that's an
implementation detail and not a language feature. Since we're getting
the ints from an re.Match.lastindex instead of putting the exact
_T_FOO values into some list, this code probably should not strictly
speaking have been using 'is'.
Explicitly initialize the global _token_re also, to make it more
visible for static analysis.
Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
Continue annotating the module. In some cases mypy will miss that
_err() calls means the function will not return, so we return an
unnecessary local variable to appease it.
Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
Add a _MarkerType enum. A subsequent commit will use it for type
annotations in the Property class.
Fix an incorrect type in a comment while we're here.
We continue to use 'marker_type is _MarkerType.FOO' instead of
'marker_type == _MarkerType.FOO' because we are adding those actual
_MarkerType.FOO objects to each property, so 'is' comparison
is legitimate.
Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
A step along the way towards typing the whole module.
Fix an incorrect (or at best misleading) comment while we're here,
which was noticed by the type checker when I originally annotated
'props' as a Dict[str, bytes].
Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
Marking this NoReturn helps the type checker figure out that functions
which call it are only returning valid values or failing to
return. (It unfortunately doesn't always work as mypy's control flow
analysis seems to treat a direct 'raise DTError(...)' differently than
calling _err() in some situations, but it helps.)
Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
Move the DTError, Node, Type, and Property definitions to the top.
This way, class definitions occur before methods which use those
classes. This will be useful to avoid string literals in type
annotations that will be added later. Some can't be avoided due to
circular dependencies, but this will help.
Adjust whitespace.
No functional changes expected.
Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
We already have support for handling the Zephyr binding "path" type in
edtlib.Node._prop_val(), but the binding inference code isn't making
use of that. Handle this type as well, as it is just as convenient as
Type.PHANDLE and can be more idiomatic depending on the situation.
Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
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>
Add the ability to filter which properties get imported when we do an
include. We add a new YAML form for this:
include:
- name: other.yaml
property-blocklist:
- prop-to-block
or
include:
- name: other.yaml
property-allowlist:
- prop-to-allow
These lists can intermix simple file names with maps, like:
include:
- foo.yaml
- name: bar.yaml
property-allowlist:
- prop-to-allow
And you can filter from child bindings like this:
include:
- name: bar.yaml
child-binding:
property-allowlist:
- child-prop-to-allow
Signed-off-by: Kumar Gala <kumar.gala@linaro.org>
Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
We are now in the process of extracting edtlib and dtlib into a
standalone source code library that we intend to share with other
projects.
Links related to the work making this standalone:
https://pypi.org/project/devicetree/https://python-devicetree.readthedocs.io/en/latest/https://github.com/zephyrproject-rtos/python-devicetree
This standalone repo includes the same features as what we have in
Zephyr, but in its own 'devicetree' python package with PyPI
integration, etc.
To avoid making this a hard fork, move the code that's being made
standalone around in Zephyr into a new scripts/dts/python-devicetree
subdirectory, and handle the package and sys.path changes in the
various places in the tree that use it.
From now on, it will be possible to update the standalone repository
by just recursively copying scripts/dts/python-devicetree's contents
into it and committing the results.
This is an interim step; do NOT 'pip install devicetree' yet.
The code in the zephyr repository is still the canonical location.
(In the long term, people will get the devicetree package from PyPI
just like they do the 'yaml' package today, but that won't happen for
the foreseeable future.)
This commit is purely intended to avoid a hard fork for the standalone
code, and no functional changes besides the package structure and
location of the code itself are expected.
Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>