Browse Source
This change fixes several breakages that were introduced in some build configurations by the introduction of the cot-dt2c tool. Some Python environments cannot be managed directly via `pip`, and invocations of `make`, including `make distclean`, would cause errors along the lines of: error: externally-managed-environment × This environment is externally managed ╰─> To install Python packages system-wide, try apt install python3-xyz, where xyz is the package you are trying to install. This change has been resolved by ensuring that calls to the cot-dt2c tool from the build system happen exclusively through Poetry, which automatically sets up a virtual environment that *can* be modified. Some environments saw the following error when building platforms where the cot-dt2c tool was used: make: *** No rule to make target '<..>/debug/bl2_cot.c', needed by '<..>/debug/bl2/bl2_cot.o'. Stop. Additionally, environments with a more recent version of Python saw the following error: File "<...>/lib/python3.12/site-packages/cot_dt2c/cot_parser.py", line 637, in img_to_c if ifdef: ^^^^^ NameError: name 'ifdef' is not defined Both of these errors have now been resolved by modifications to the build system and the cot-dt2c tool to enable preprocessing of the device tree source file before it is processed by the tool. As a consequence of this change, the `pydevicetree` library is no longer vendored into the repository tree, and we instead pull it in via a dependency in Poetry. This change also resolves several MyPy warnings and errors related to missing type hints. Change-Id: I72b2d01caca3fcb789d3fe2549f318a9c92d77d1 Signed-off-by: Chris Kay <chris.kay@arm.com>pull/2005/merge
Chris Kay
3 months ago
22 changed files with 752 additions and 1824 deletions
@ -0,0 +1,176 @@ |
|||
# Created by https://www.toptal.com/developers/gitignore/api/python |
|||
# Edit at https://www.toptal.com/developers/gitignore?templates=python |
|||
|
|||
### Python ### |
|||
# Byte-compiled / optimized / DLL files |
|||
__pycache__/ |
|||
*.py[cod] |
|||
*$py.class |
|||
|
|||
# C extensions |
|||
*.so |
|||
|
|||
# Distribution / packaging |
|||
.Python |
|||
build/ |
|||
develop-eggs/ |
|||
dist/ |
|||
downloads/ |
|||
eggs/ |
|||
.eggs/ |
|||
lib/ |
|||
lib64/ |
|||
parts/ |
|||
sdist/ |
|||
var/ |
|||
wheels/ |
|||
share/python-wheels/ |
|||
*.egg-info/ |
|||
.installed.cfg |
|||
*.egg |
|||
MANIFEST |
|||
|
|||
# PyInstaller |
|||
# Usually these files are written by a python script from a template |
|||
# before PyInstaller builds the exe, so as to inject date/other infos into it. |
|||
*.manifest |
|||
*.spec |
|||
|
|||
# Installer logs |
|||
pip-log.txt |
|||
pip-delete-this-directory.txt |
|||
|
|||
# Unit test / coverage reports |
|||
htmlcov/ |
|||
.tox/ |
|||
.nox/ |
|||
.coverage |
|||
.coverage.* |
|||
.cache |
|||
nosetests.xml |
|||
coverage.xml |
|||
*.cover |
|||
*.py,cover |
|||
.hypothesis/ |
|||
.pytest_cache/ |
|||
cover/ |
|||
|
|||
# Translations |
|||
*.mo |
|||
*.pot |
|||
|
|||
# Django stuff: |
|||
*.log |
|||
local_settings.py |
|||
db.sqlite3 |
|||
db.sqlite3-journal |
|||
|
|||
# Flask stuff: |
|||
instance/ |
|||
.webassets-cache |
|||
|
|||
# Scrapy stuff: |
|||
.scrapy |
|||
|
|||
# Sphinx documentation |
|||
docs/_build/ |
|||
|
|||
# PyBuilder |
|||
.pybuilder/ |
|||
target/ |
|||
|
|||
# Jupyter Notebook |
|||
.ipynb_checkpoints |
|||
|
|||
# IPython |
|||
profile_default/ |
|||
ipython_config.py |
|||
|
|||
# pyenv |
|||
# For a library or package, you might want to ignore these files since the code is |
|||
# intended to run in multiple environments; otherwise, check them in: |
|||
# .python-version |
|||
|
|||
# pipenv |
|||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. |
|||
# However, in case of collaboration, if having platform-specific dependencies or dependencies |
|||
# having no cross-platform support, pipenv may install dependencies that don't work, or not |
|||
# install all needed dependencies. |
|||
#Pipfile.lock |
|||
|
|||
# poetry |
|||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. |
|||
# This is especially recommended for binary packages to ensure reproducibility, and is more |
|||
# commonly ignored for libraries. |
|||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control |
|||
#poetry.lock |
|||
|
|||
# pdm |
|||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. |
|||
#pdm.lock |
|||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it |
|||
# in version control. |
|||
# https://pdm.fming.dev/#use-with-ide |
|||
.pdm.toml |
|||
|
|||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm |
|||
__pypackages__/ |
|||
|
|||
# Celery stuff |
|||
celerybeat-schedule |
|||
celerybeat.pid |
|||
|
|||
# SageMath parsed files |
|||
*.sage.py |
|||
|
|||
# Environments |
|||
.env |
|||
.venv |
|||
env/ |
|||
venv/ |
|||
ENV/ |
|||
env.bak/ |
|||
venv.bak/ |
|||
|
|||
# Spyder project settings |
|||
.spyderproject |
|||
.spyproject |
|||
|
|||
# Rope project settings |
|||
.ropeproject |
|||
|
|||
# mkdocs documentation |
|||
/site |
|||
|
|||
# mypy |
|||
.mypy_cache/ |
|||
.dmypy.json |
|||
dmypy.json |
|||
|
|||
# Pyre type checker |
|||
.pyre/ |
|||
|
|||
# pytype static type analyzer |
|||
.pytype/ |
|||
|
|||
# Cython debug symbols |
|||
cython_debug/ |
|||
|
|||
# PyCharm |
|||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can |
|||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore |
|||
# and can be added to the global gitignore or merged into this file. For a more nuclear |
|||
# option (not recommended) you can uncomment the following to ignore the entire idea folder. |
|||
#.idea/ |
|||
|
|||
### Python Patch ### |
|||
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration |
|||
poetry.toml |
|||
|
|||
# ruff |
|||
.ruff_cache/ |
|||
|
|||
# LSP config files |
|||
pyrightconfig.json |
|||
|
|||
# End of https://www.toptal.com/developers/gitignore/api/python |
@ -1,68 +0,0 @@ |
|||
#
|
|||
# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
|
|||
#
|
|||
# SPDX-License-Identifier: BSD-3-Clause
|
|||
|
|||
##* Variables
|
|||
SHELL := /usr/bin/env bash |
|||
PYTHON := python |
|||
PYTHONPATH := `pwd` |
|||
|
|||
.PHONY: dist |
|||
dist: clean |
|||
poetry build |
|||
|
|||
#* Installation
|
|||
.PHONY: dev-install |
|||
dev-install: |
|||
pip3 install mypy |
|||
pip3 install pytest |
|||
pip install -r requirements.txt |
|||
poetry lock -n && poetry export --without-hashes > requirements.txt |
|||
poetry install -n |
|||
-poetry run mypy --install-types --non-interactive ./ |
|||
|
|||
.PHONY: install |
|||
install: dist |
|||
pip install mypy |
|||
pip install pytest |
|||
pip install -r requirements.txt |
|||
pip install dist/*.whl |
|||
|
|||
clean-test: ## remove test and coverage artifacts
|
|||
rm -fr .tox/ |
|||
rm -f .coverage |
|||
rm -fr htmlcov/ |
|||
|
|||
clean-pyc: ## remove Python file artifacts
|
|||
find . -name '*.pyc' -exec rm -f {} + |
|||
find . -name '*.pyo' -exec rm -f {} + |
|||
find . -name '*~' -exec rm -f {} + |
|||
find . -name '__pycache__' -exec rm -fr {} + |
|||
find . | grep -E ".pytest_cache" | xargs rm -rf |
|||
find . | grep -E ".mypy_cache" | xargs rm -rf |
|||
|
|||
|
|||
clean-build: ## remove build artifacts
|
|||
rm -fr build/ |
|||
rm -fr dist/ |
|||
rm -fr .eggs/ |
|||
find . -name '*.egg-info' -exec rm -fr {} + |
|||
find . -name '*.egg' -exec rm -f {} + |
|||
|
|||
clean-tmp: |
|||
rm -rf ./tmp |
|||
|
|||
#* Cleaning
|
|||
.PHONY: clean clean-build clean-pyc clean-test |
|||
clean: uninstall clean-build clean-pyc clean-test clean-tmp ## remove all build, test, coverage and Python artifacts
|
|||
|
|||
uninstall: |
|||
pip uninstall -y cot-dt2c |
|||
|
|||
.PHONY: reinstall |
|||
reinstall: clean install |
|||
|
|||
.PHONY: test |
|||
test: |
|||
PYTHONPATH=$(PYTHONPATH) poetry run pytest -c pyproject.toml tests/ |
@ -1,202 +0,0 @@ |
|||
|
|||
Apache License |
|||
Version 2.0, January 2004 |
|||
http://www.apache.org/licenses/ |
|||
|
|||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
|||
|
|||
1. Definitions. |
|||
|
|||
"License" shall mean the terms and conditions for use, reproduction, |
|||
and distribution as defined by Sections 1 through 9 of this document. |
|||
|
|||
"Licensor" shall mean the copyright owner or entity authorized by |
|||
the copyright owner that is granting the License. |
|||
|
|||
"Legal Entity" shall mean the union of the acting entity and all |
|||
other entities that control, are controlled by, or are under common |
|||
control with that entity. For the purposes of this definition, |
|||
"control" means (i) the power, direct or indirect, to cause the |
|||
direction or management of such entity, whether by contract or |
|||
otherwise, or (ii) ownership of fifty percent (50%) or more of the |
|||
outstanding shares, or (iii) beneficial ownership of such entity. |
|||
|
|||
"You" (or "Your") shall mean an individual or Legal Entity |
|||
exercising permissions granted by this License. |
|||
|
|||
"Source" form shall mean the preferred form for making modifications, |
|||
including but not limited to software source code, documentation |
|||
source, and configuration files. |
|||
|
|||
"Object" form shall mean any form resulting from mechanical |
|||
transformation or translation of a Source form, including but |
|||
not limited to compiled object code, generated documentation, |
|||
and conversions to other media types. |
|||
|
|||
"Work" shall mean the work of authorship, whether in Source or |
|||
Object form, made available under the License, as indicated by a |
|||
copyright notice that is included in or attached to the work |
|||
(an example is provided in the Appendix below). |
|||
|
|||
"Derivative Works" shall mean any work, whether in Source or Object |
|||
form, that is based on (or derived from) the Work and for which the |
|||
editorial revisions, annotations, elaborations, or other modifications |
|||
represent, as a whole, an original work of authorship. For the purposes |
|||
of this License, Derivative Works shall not include works that remain |
|||
separable from, or merely link (or bind by name) to the interfaces of, |
|||
the Work and Derivative Works thereof. |
|||
|
|||
"Contribution" shall mean any work of authorship, including |
|||
the original version of the Work and any modifications or additions |
|||
to that Work or Derivative Works thereof, that is intentionally |
|||
submitted to Licensor for inclusion in the Work by the copyright owner |
|||
or by an individual or Legal Entity authorized to submit on behalf of |
|||
the copyright owner. For the purposes of this definition, "submitted" |
|||
means any form of electronic, verbal, or written communication sent |
|||
to the Licensor or its representatives, including but not limited to |
|||
communication on electronic mailing lists, source code control systems, |
|||
and issue tracking systems that are managed by, or on behalf of, the |
|||
Licensor for the purpose of discussing and improving the Work, but |
|||
excluding communication that is conspicuously marked or otherwise |
|||
designated in writing by the copyright owner as "Not a Contribution." |
|||
|
|||
"Contributor" shall mean Licensor and any individual or Legal Entity |
|||
on behalf of whom a Contribution has been received by Licensor and |
|||
subsequently incorporated within the Work. |
|||
|
|||
2. Grant of Copyright License. Subject to the terms and conditions of |
|||
this License, each Contributor hereby grants to You a perpetual, |
|||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
|||
copyright license to reproduce, prepare Derivative Works of, |
|||
publicly display, publicly perform, sublicense, and distribute the |
|||
Work and such Derivative Works in Source or Object form. |
|||
|
|||
3. Grant of Patent License. Subject to the terms and conditions of |
|||
this License, each Contributor hereby grants to You a perpetual, |
|||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
|||
(except as stated in this section) patent license to make, have made, |
|||
use, offer to sell, sell, import, and otherwise transfer the Work, |
|||
where such license applies only to those patent claims licensable |
|||
by such Contributor that are necessarily infringed by their |
|||
Contribution(s) alone or by combination of their Contribution(s) |
|||
with the Work to which such Contribution(s) was submitted. If You |
|||
institute patent litigation against any entity (including a |
|||
cross-claim or counterclaim in a lawsuit) alleging that the Work |
|||
or a Contribution incorporated within the Work constitutes direct |
|||
or contributory patent infringement, then any patent licenses |
|||
granted to You under this License for that Work shall terminate |
|||
as of the date such litigation is filed. |
|||
|
|||
4. Redistribution. You may reproduce and distribute copies of the |
|||
Work or Derivative Works thereof in any medium, with or without |
|||
modifications, and in Source or Object form, provided that You |
|||
meet the following conditions: |
|||
|
|||
(a) You must give any other recipients of the Work or |
|||
Derivative Works a copy of this License; and |
|||
|
|||
(b) You must cause any modified files to carry prominent notices |
|||
stating that You changed the files; and |
|||
|
|||
(c) You must retain, in the Source form of any Derivative Works |
|||
that You distribute, all copyright, patent, trademark, and |
|||
attribution notices from the Source form of the Work, |
|||
excluding those notices that do not pertain to any part of |
|||
the Derivative Works; and |
|||
|
|||
(d) If the Work includes a "NOTICE" text file as part of its |
|||
distribution, then any Derivative Works that You distribute must |
|||
include a readable copy of the attribution notices contained |
|||
within such NOTICE file, excluding those notices that do not |
|||
pertain to any part of the Derivative Works, in at least one |
|||
of the following places: within a NOTICE text file distributed |
|||
as part of the Derivative Works; within the Source form or |
|||
documentation, if provided along with the Derivative Works; or, |
|||
within a display generated by the Derivative Works, if and |
|||
wherever such third-party notices normally appear. The contents |
|||
of the NOTICE file are for informational purposes only and |
|||
do not modify the License. You may add Your own attribution |
|||
notices within Derivative Works that You distribute, alongside |
|||
or as an addendum to the NOTICE text from the Work, provided |
|||
that such additional attribution notices cannot be construed |
|||
as modifying the License. |
|||
|
|||
You may add Your own copyright statement to Your modifications and |
|||
may provide additional or different license terms and conditions |
|||
for use, reproduction, or distribution of Your modifications, or |
|||
for any such Derivative Works as a whole, provided Your use, |
|||
reproduction, and distribution of the Work otherwise complies with |
|||
the conditions stated in this License. |
|||
|
|||
5. Submission of Contributions. Unless You explicitly state otherwise, |
|||
any Contribution intentionally submitted for inclusion in the Work |
|||
by You to the Licensor shall be under the terms and conditions of |
|||
this License, without any additional terms or conditions. |
|||
Notwithstanding the above, nothing herein shall supersede or modify |
|||
the terms of any separate license agreement you may have executed |
|||
with Licensor regarding such Contributions. |
|||
|
|||
6. Trademarks. This License does not grant permission to use the trade |
|||
names, trademarks, service marks, or product names of the Licensor, |
|||
except as required for reasonable and customary use in describing the |
|||
origin of the Work and reproducing the content of the NOTICE file. |
|||
|
|||
7. Disclaimer of Warranty. Unless required by applicable law or |
|||
agreed to in writing, Licensor provides the Work (and each |
|||
Contributor provides its Contributions) on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
|||
implied, including, without limitation, any warranties or conditions |
|||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
|||
PARTICULAR PURPOSE. You are solely responsible for determining the |
|||
appropriateness of using or redistributing the Work and assume any |
|||
risks associated with Your exercise of permissions under this License. |
|||
|
|||
8. Limitation of Liability. In no event and under no legal theory, |
|||
whether in tort (including negligence), contract, or otherwise, |
|||
unless required by applicable law (such as deliberate and grossly |
|||
negligent acts) or agreed to in writing, shall any Contributor be |
|||
liable to You for damages, including any direct, indirect, special, |
|||
incidental, or consequential damages of any character arising as a |
|||
result of this License or out of the use or inability to use the |
|||
Work (including but not limited to damages for loss of goodwill, |
|||
work stoppage, computer failure or malfunction, or any and all |
|||
other commercial damages or losses), even if such Contributor |
|||
has been advised of the possibility of such damages. |
|||
|
|||
9. Accepting Warranty or Additional Liability. While redistributing |
|||
the Work or Derivative Works thereof, You may choose to offer, |
|||
and charge a fee for, acceptance of support, warranty, indemnity, |
|||
or other liability obligations and/or rights consistent with this |
|||
License. However, in accepting such obligations, You may act only |
|||
on Your own behalf and on Your sole responsibility, not on behalf |
|||
of any other Contributor, and only if You agree to indemnify, |
|||
defend, and hold each Contributor harmless for any liability |
|||
incurred by, or claims asserted against, such Contributor by reason |
|||
of your accepting any such warranty or additional liability. |
|||
|
|||
END OF TERMS AND CONDITIONS |
|||
|
|||
APPENDIX: How to apply the Apache License to your work. |
|||
|
|||
To apply the Apache License to your work, attach the following |
|||
boilerplate notice, with the fields enclosed by brackets "[]" |
|||
replaced with your own identifying information. (Don't include |
|||
the brackets!) The text should be enclosed in the appropriate |
|||
comment syntax for the file format. We also recommend that a |
|||
file or class name and description of purpose be included on the |
|||
same "printed page" as the copyright notice for easier |
|||
identification within third-party archives. |
|||
|
|||
Copyright [yyyy] [name of copyright owner] |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
@ -1,5 +0,0 @@ |
|||
#!/usr/bin/env python3 |
|||
# Copyright (c) 2019 SiFive Inc. |
|||
# SPDX-License-Identifier: Apache-2.0 |
|||
|
|||
from cot_dt2c.pydevicetree.ast import Devicetree, Node, Property, Directive, CellArray, LabelReference |
@ -1,9 +0,0 @@ |
|||
#!/usr/bin/env python3 |
|||
# Copyright (c) 2019 SiFive Inc. |
|||
# SPDX-License-Identifier: Apache-2.0 |
|||
|
|||
from cot_dt2c.pydevicetree.ast.directive import Directive |
|||
from cot_dt2c.pydevicetree.ast.node import Node, NodeReference, Devicetree |
|||
from cot_dt2c.pydevicetree.ast.property import PropertyValues, Bytestring, CellArray, StringList, Property, \ |
|||
RegArray, OneString |
|||
from cot_dt2c.pydevicetree.ast.reference import Label, Path, Reference, LabelReference, PathReference |
@ -1,46 +0,0 @@ |
|||
#!/usr/bin/env python3 |
|||
# Copyright (c) 2019 SiFive Inc. |
|||
# SPDX-License-Identifier: Apache-2.0 |
|||
|
|||
from typing import Any |
|||
|
|||
from cot_dt2c.pydevicetree.ast.helpers import formatLevel, wrapStrings |
|||
|
|||
class Directive: |
|||
"""Represents a Devicetree directive |
|||
|
|||
Directives in Devicetree source are statements of the form |
|||
|
|||
/directive-name/ [option1 [option2 [...]]]; |
|||
|
|||
Common directive examples include: |
|||
|
|||
/dts-v1/; |
|||
/include/ "overlay.dtsi"; |
|||
/delete-node/ &uart0; |
|||
/delete-property/ status; |
|||
|
|||
Their semantic meaning depends on the directive name, their location in the Devicetree, |
|||
and their options. |
|||
""" |
|||
def __init__(self, directive: str, option: Any = None): |
|||
"""Create a directive object""" |
|||
self.directive = directive |
|||
self.option = option |
|||
|
|||
def __repr__(self) -> str: |
|||
return "<Directive %s>" % self.directive |
|||
|
|||
def __str__(self) -> str: |
|||
return self.to_dts() |
|||
|
|||
def to_dts(self, level: int = 0) -> str: |
|||
"""Format the Directive in Devicetree Source format""" |
|||
if isinstance(self.option, list): |
|||
return formatLevel(level, "%s %s;\n" % (self.directive, |
|||
wrapStrings(self.option))) |
|||
if isinstance(self.option, str): |
|||
if self.directive == "/include/": |
|||
return formatLevel(level, "%s \"%s\"\n" % (self.directive, self.option)) |
|||
return formatLevel(level, "%s \"%s\";\n" % (self.directive, self.option)) |
|||
return formatLevel(level, "%s;\n" % self.directive) |
@ -1,28 +0,0 @@ |
|||
#!/usr/bin/env python3 |
|||
# Copyright (c) 2019 SiFive Inc. |
|||
# SPDX-License-Identifier: Apache-2.0 |
|||
|
|||
from typing import List, Any |
|||
|
|||
from cot_dt2c.pydevicetree.ast.reference import Reference |
|||
|
|||
def formatLevel(level: int, s: str) -> str: |
|||
"""Helper to indent a string with a number of tabs""" |
|||
return "\t" * level + s |
|||
|
|||
def wrapStrings(values: List[Any], formatHex: bool = False) -> List[Any]: |
|||
"""Helper to wrap strings in quotes where appropriate""" |
|||
wrapped = [] |
|||
for v in values: |
|||
if isinstance(v, Reference): |
|||
wrapped.append(v.to_dts()) |
|||
elif isinstance(v, str): |
|||
wrapped.append("\"%s\"" % v) |
|||
elif isinstance(v, int): |
|||
if formatHex: |
|||
wrapped.append("0x%x" % v) |
|||
else: |
|||
wrapped.append(str(v)) |
|||
else: |
|||
wrapped.append(str(v)) |
|||
return wrapped |
@ -1,514 +0,0 @@ |
|||
#!/usr/bin/env python3 |
|||
# Copyright (c) 2019 SiFive Inc. |
|||
# SPDX-License-Identifier: Apache-2.0 |
|||
|
|||
import re |
|||
import os |
|||
from typing import List, Union, Optional, Iterable, Callable, Any, cast, Pattern |
|||
|
|||
from cot_dt2c.pydevicetree.ast.helpers import formatLevel |
|||
from cot_dt2c.pydevicetree.ast.property import Property, PropertyValues, RegArray, RangeArray |
|||
from cot_dt2c.pydevicetree.ast.directive import Directive |
|||
from cot_dt2c.pydevicetree.ast.reference import Label, Path, Reference, LabelReference, PathReference |
|||
|
|||
# Type signature for elements passed to Devicetree constructor |
|||
ElementList = Iterable[Union['Node', Property, Directive]] |
|||
|
|||
# Callback type signatures for Devicetree.match() and Devicetree.chosen() |
|||
MatchFunc = Callable[['Node'], bool] |
|||
MatchCallback = Optional[Callable[['Node'], None]] |
|||
ChosenCallback = Optional[Callable[[PropertyValues], None]] |
|||
|
|||
class Node: |
|||
"""Represents a Devicetree Node |
|||
|
|||
A Devicetree Node generally takes the form |
|||
|
|||
[label:] node-name@unit-address { |
|||
[directives] |
|||
[properties] |
|||
[child nodes] |
|||
}; |
|||
|
|||
The structure formed by creating trees of Nodes is the bulk of any Devicetree. As the naming |
|||
system implies, then, each node roughly corresponds to some conceptual device, subsystem of |
|||
devices, bus, etc. |
|||
|
|||
Devices can be referenced by label or by path, and are generally uniquely identified by a |
|||
collection of string identifiers assigned to the "compatible" property. |
|||
|
|||
For instance, a UART device might look like |
|||
|
|||
uart0: uart@10013000 { |
|||
compatible = "sifive,uart0"; |
|||
reg = <0x10013000 0x1000>; |
|||
reg-names = "control"; |
|||
interrupt-parent = <&plic>; |
|||
interrupts = <3>; |
|||
clocks = <&busclk>; |
|||
status = "okay"; |
|||
}; |
|||
|
|||
This node can be identified in the following ways: |
|||
|
|||
- By label: uart0 |
|||
- By path: /path/to/uart@10013000 |
|||
- By name: uart@10013000 (for example when referenced in a /delete-node/ directive) |
|||
""" |
|||
# pylint: disable=too-many-arguments |
|||
def __init__(self, name: str, label: Optional[str], address: Optional[int], |
|||
properties: List[Property], directives: List[Directive], |
|||
children: List['Node']): |
|||
"""Initializes a Devicetree Node |
|||
|
|||
Also evaluates the /delete-node/ and /delete-property/ directives found in the node |
|||
and deletes the respective nodes and properties. |
|||
""" |
|||
self.name = name |
|||
self.parent = None # type: Optional['Node'] |
|||
|
|||
self.label = label |
|||
self.address = address |
|||
self.properties = properties |
|||
self.directives = directives |
|||
self.children = children |
|||
self.ifdef = [] |
|||
|
|||
for d in self.directives: |
|||
if d.directive == "/delete-node/": |
|||
if isinstance(d.option, LabelReference): |
|||
node = self.get_by_reference(d.option) |
|||
elif isinstance(d.option, str): |
|||
node = self.__get_child_by_handle(d.option) |
|||
if node: |
|||
self.remove_child(node) |
|||
elif d.directive == "/delete-property/": |
|||
# pylint: disable=cell-var-from-loop |
|||
properties = list(filter(lambda p: p.name == d.option, self.properties)) |
|||
if properties: |
|||
del self.properties[self.properties.index(properties[0])] |
|||
|
|||
def __repr__(self) -> str: |
|||
if self.address: |
|||
return "<Node %s@%x>" % (self.name, self.address) |
|||
return "<Node %s>" % self.name |
|||
|
|||
def __str__(self) -> str: |
|||
return self.to_dts() |
|||
|
|||
def __eq__(self, other) -> bool: |
|||
return self.name == other.name and self.address == other.address |
|||
|
|||
def __hash__(self): |
|||
return hash((self.name, self.address)) |
|||
|
|||
@staticmethod |
|||
def from_dts(source: str) -> 'Node': |
|||
"""Create a node from Devicetree Source""" |
|||
# pylint: disable=import-outside-toplevel,cyclic-import |
|||
from pydevicetree.source import parseNode |
|||
return parseNode(source) |
|||
|
|||
def add_child(self, node: 'Node', merge: bool = True): |
|||
"""Add a child node and merge it into the tree""" |
|||
node.parent = self |
|||
self.children.append(node) |
|||
if merge: |
|||
self.merge_tree() |
|||
|
|||
def to_dts(self, level: int = 0) -> str: |
|||
"""Format the subtree starting at the node as Devicetree Source""" |
|||
out = "" |
|||
if isinstance(self.address, int) and self.label: |
|||
out += formatLevel(level, |
|||
"%s: %s@%x {\n" % (self.label, self.name, self.address)) |
|||
elif isinstance(self.address, int): |
|||
out += formatLevel(level, "%s@%x {\n" % (self.name, self.address)) |
|||
elif self.label: |
|||
out += formatLevel(level, "%s: %s {\n" % (self.label, self.name)) |
|||
elif self.name != "": |
|||
out += formatLevel(level, "%s {\n" % self.name) |
|||
|
|||
for d in self.directives: |
|||
out += d.to_dts(level + 1) |
|||
for p in self.properties: |
|||
out += p.to_dts(level + 1) |
|||
for c in self.children: |
|||
out += c.to_dts(level + 1) |
|||
|
|||
if self.name != "": |
|||
out += formatLevel(level, "};\n") |
|||
|
|||
return out |
|||
|
|||
def merge_tree(self): |
|||
"""Recursively merge child nodes into a single tree |
|||
|
|||
Parsed Devicetrees can describe the same tree multiple times, adding nodes and properties |
|||
each time. After parsing, this method is called to recursively merge the tree. |
|||
""" |
|||
partitioned_children = [] |
|||
for n in self.children: |
|||
partitioned_children.append([e for e in self.children if e == n]) |
|||
|
|||
new_children = [] |
|||
for part in partitioned_children: |
|||
first = part[0] |
|||
rest = part[1:] |
|||
if first not in new_children: |
|||
for n in rest: |
|||
first.merge(n) |
|||
new_children.append(first) |
|||
|
|||
self.children = new_children |
|||
|
|||
for n in self.children: |
|||
n.parent = self |
|||
n.merge_tree() |
|||
|
|||
def merge(self, other: 'Node'): |
|||
"""Merge the contents of a node into this node. |
|||
|
|||
Used by Node.merge_trees() |
|||
""" |
|||
if not self.label and other.label: |
|||
self.label = other.label |
|||
self.properties += other.properties |
|||
self.directives += other.directives |
|||
self.children += other.children |
|||
self.ifdef += other.ifdef |
|||
|
|||
def get_path(self, includeAddress: bool = True) -> str: |
|||
"""Get the path of a node (ex. /cpus/cpu@0)""" |
|||
if self.name == "/": |
|||
return "" |
|||
if self.parent is None: |
|||
return "/" + self.name |
|||
if isinstance(self.address, int) and includeAddress: |
|||
return self.parent.get_path() + "/" + self.name + "@" + ("%x" % self.address) |
|||
return self.parent.get_path() + "/" + self.name |
|||
|
|||
def get_by_reference(self, reference: Reference) -> Optional['Node']: |
|||
"""Get a node from the subtree by reference (ex. &label, &{/path/to/node})""" |
|||
if isinstance(reference, LabelReference): |
|||
return self.get_by_label(reference.label) |
|||
if isinstance(reference, PathReference): |
|||
return self.get_by_path(reference.path) |
|||
|
|||
return None |
|||
|
|||
def get_by_label(self, label: Union[Label, str]) -> Optional['Node']: |
|||
"""Get a node from the subtree by label""" |
|||
matching_nodes = list(filter(lambda n: n.label == label, self.child_nodes())) |
|||
if len(matching_nodes) != 0: |
|||
return matching_nodes[0] |
|||
return None |
|||
|
|||
def __get_child_by_handle(self, handle: str) -> Optional['Node']: |
|||
"""Get a child node by name or name and unit address""" |
|||
if '@' in handle: |
|||
name, addr_s = handle.split('@') |
|||
address = int(addr_s, base=16) |
|||
nodes = list(filter(lambda n: n.name == name and n.address == address, self.children)) |
|||
else: |
|||
name = handle |
|||
nodes = list(filter(lambda n: n.name == name, self.children)) |
|||
|
|||
if not nodes: |
|||
return None |
|||
if len(nodes) > 1: |
|||
raise Exception("Handle %s is ambiguous!" % handle) |
|||
return nodes[0] |
|||
|
|||
def get_by_path(self, path: Union[Path, str]) -> Optional['Node']: |
|||
"""Get a node in the subtree by path""" |
|||
matching_nodes = list(filter(lambda n: path == n.get_path(includeAddress=True), \ |
|||
self.child_nodes())) |
|||
if len(matching_nodes) != 0: |
|||
return matching_nodes[0] |
|||
|
|||
matching_nodes = list(filter(lambda n: path == n.get_path(includeAddress=False), \ |
|||
self.child_nodes())) |
|||
if len(matching_nodes) != 0: |
|||
return matching_nodes[0] |
|||
return None |
|||
|
|||
def filter(self, matchFunc: MatchFunc, cbFunc: MatchCallback = None) -> List['Node']: |
|||
"""Filter all child nodes by matchFunc |
|||
|
|||
If cbFunc is provided, this method will iterate over the Nodes selected by matchFunc |
|||
and call cbFunc on each Node |
|||
|
|||
Returns a list of all matching Nodes |
|||
""" |
|||
nodes = list(filter(matchFunc, self.child_nodes())) |
|||
|
|||
if cbFunc is not None: |
|||
for n in nodes: |
|||
cbFunc(n) |
|||
|
|||
return nodes |
|||
|
|||
def match(self, compatible: Pattern, func: MatchCallback = None) -> List['Node']: |
|||
"""Get a node from the subtree by compatible string |
|||
|
|||
Accepts a regular expression to match one of the strings in the compatible property. |
|||
""" |
|||
regex = re.compile(compatible) |
|||
|
|||
def match_compat(node: Node) -> bool: |
|||
compatibles = node.get_fields("compatible") |
|||
if compatibles is not None: |
|||
return any(regex.match(c) for c in compatibles) |
|||
return False |
|||
|
|||
return self.filter(match_compat, func) |
|||
|
|||
def child_nodes(self) -> Iterable['Node']: |
|||
"""Get an iterable over all the nodes in the subtree""" |
|||
for n in self.children: |
|||
yield n |
|||
for m in n.child_nodes(): |
|||
yield m |
|||
|
|||
def remove_child(self, node): |
|||
"""Remove a child node""" |
|||
del self.children[self.children.index(node)] |
|||
|
|||
def get_fields(self, field_name: str) -> Optional[PropertyValues]: |
|||
"""Get all the values of a property""" |
|||
for p in self.properties: |
|||
if p.name == field_name: |
|||
return p.values |
|||
return None |
|||
|
|||
def has_field(self, field_name: str) -> bool: |
|||
for p in self.properties: |
|||
if p.name == field_name: |
|||
return True |
|||
return False |
|||
|
|||
def get_field(self, field_name: str) -> Any: |
|||
"""Get the first value of a property""" |
|||
fields = self.get_fields(field_name) |
|||
if fields is not None: |
|||
if len(cast(PropertyValues, fields)) != 0: |
|||
return fields[0] |
|||
return None |
|||
|
|||
def get_reg(self) -> Optional[RegArray]: |
|||
"""If the node defines a `reg` property, return a RegArray for easier querying""" |
|||
reg = self.get_fields("reg") |
|||
reg_names = self.get_fields("reg-names") |
|||
if reg is not None: |
|||
if reg_names is not None: |
|||
return RegArray(reg.values, self.address_cells(), self.size_cells(), |
|||
reg_names.values) |
|||
return RegArray(reg.values, self.address_cells(), self.size_cells()) |
|||
return None |
|||
|
|||
def get_ranges(self) -> Optional[RangeArray]: |
|||
"""If the node defines a `ranges` property, return a RangeArray for easier querying""" |
|||
ranges = self.get_fields("ranges") |
|||
child_address_cells = self.get_field("#address-cells") |
|||
parent_address_cells = self.address_cells() |
|||
size_cells = self.get_field("#size-cells") |
|||
if ranges is not None: |
|||
return RangeArray(ranges.values, child_address_cells, parent_address_cells, size_cells) |
|||
return None |
|||
|
|||
def address_cells(self): |
|||
"""Get the number of address cells |
|||
|
|||
The #address-cells property is defined by the parent of a node and describes how addresses |
|||
are encoded in cell arrays. If no property is defined, the default value is 2. |
|||
""" |
|||
if self.parent is not None: |
|||
cells = self.parent.get_field("#address-cells") |
|||
if cells is not None: |
|||
return cells |
|||
return 2 |
|||
return 2 |
|||
|
|||
def size_cells(self): |
|||
"""Get the number of size cells |
|||
|
|||
The #size-cells property is defined by the parent of a node and describes how addresses |
|||
are encoded in cell arrays. If no property is defined, the default value is 1. |
|||
""" |
|||
if self.parent is not None: |
|||
cells = self.parent.get_field("#size-cells") |
|||
if cells is not None: |
|||
return cells |
|||
return 1 |
|||
return 1 |
|||
|
|||
class NodeReference(Node): |
|||
"""A NodeReference is used to extend the definition of a previously-defined Node |
|||
|
|||
NodeReferences are commonly used by Devicetree "overlays" to extend the properties of a node |
|||
or add child devices, such as to a bus like I2C. |
|||
""" |
|||
def __init__(self, reference: Reference, properties: List[Property], |
|||
directives: List[Directive], children: List[Node]): |
|||
"""Instantiate a Node identified by reference to another node""" |
|||
self.reference = reference |
|||
Node.__init__(self, label=None, name="", address=None, properties=properties, |
|||
directives=directives, children=children) |
|||
|
|||
def __repr__(self) -> str: |
|||
return "<NodeReference %s>" % self.reference.to_dts() |
|||
|
|||
def resolve_reference(self, tree: 'Devicetree') -> Node: |
|||
"""Given the full tree, get the node being referenced""" |
|||
node = tree.get_by_reference(self.reference) |
|||
if node is None: |
|||
raise Exception("Node reference %s cannot be resolved" % self.reference.to_dts()) |
|||
return cast(Node, node) |
|||
|
|||
def to_dts(self, level: int = 0) -> str: |
|||
out = formatLevel(level, self.reference.to_dts() + " {\n") |
|||
|
|||
for d in self.directives: |
|||
out += d.to_dts(level + 1) |
|||
for p in self.properties: |
|||
out += p.to_dts(level + 1) |
|||
for c in self.children: |
|||
out += c.to_dts(level + 1) |
|||
|
|||
out += formatLevel(level, "};\n") |
|||
|
|||
return out |
|||
|
|||
|
|||
class Devicetree(Node): |
|||
"""A Devicetree object describes the full Devicetree tree |
|||
|
|||
This class encapsulates both the tree itself (starting at the root node /) and any Directives |
|||
or nodes which exist at the top level of the Devicetree Source files. |
|||
|
|||
Devicetree Source files can be parsed by calling Devicetree.parseFile(). |
|||
""" |
|||
def __init__(self, elements: ElementList): |
|||
"""Instantiate a Devicetree with the list of parsed elements |
|||
|
|||
Resolves all reference nodes and merges the tree to combine all identical nodes. |
|||
""" |
|||
properties = [] # type: List[Property] |
|||
directives = [] # type: List[Directive] |
|||
children = [] # type: List[Node] |
|||
|
|||
for e in elements: |
|||
if isinstance(e, Node): |
|||
children.append(cast(Node, e)) |
|||
elif isinstance(e, Property): |
|||
properties.append(cast(Property, e)) |
|||
elif isinstance(e, Directive): |
|||
directives.append(cast(Directive, e)) |
|||
|
|||
Node.__init__(self, label=None, name="", address=None, |
|||
properties=properties, directives=directives, children=children) |
|||
|
|||
for node in self.children: |
|||
node.parent = self |
|||
|
|||
reference_nodes = self.filter(lambda n: isinstance(n, NodeReference)) |
|||
for refnode in reference_nodes: |
|||
refnode = cast(NodeReference, refnode) |
|||
|
|||
node = refnode.resolve_reference(self) |
|||
|
|||
if refnode.parent: |
|||
cast(Node, refnode.parent).remove_child(refnode) |
|||
|
|||
node.properties += refnode.properties |
|||
node.directives += refnode.directives |
|||
node.children += refnode.children |
|||
|
|||
self.merge_tree() |
|||
|
|||
def __repr__(self) -> str: |
|||
name = self.root().get_field("compatible") |
|||
return "<Devicetree %s>" % name |
|||
|
|||
def to_dts(self, level: int = 0) -> str: |
|||
"""Convert the tree back to Devicetree Source""" |
|||
out = "" |
|||
|
|||
for d in self.directives: |
|||
out += d.to_dts() |
|||
for p in self.properties: |
|||
out += p.to_dts() |
|||
for c in self.children: |
|||
out += c.to_dts() |
|||
|
|||
return out |
|||
|
|||
def get_by_path(self, path: Union[Path, str]) -> Optional[Node]: |
|||
"""Get a node in the tree by path (ex. /cpus/cpu@0)""" |
|||
|
|||
# Find and replace all aliases in the path |
|||
aliases = self.aliases() |
|||
if aliases: |
|||
for prop in aliases.properties: |
|||
if prop.name in path and len(prop.values) > 0: |
|||
path = path.replace(prop.name, prop.values[0]) |
|||
|
|||
return self.root().get_by_path(path) |
|||
|
|||
@staticmethod |
|||
# pylint: disable=arguments-differ |
|||
def from_dts(dts: str) -> 'Devicetree': |
|||
"""Parse a string and return a Devicetree object""" |
|||
# pylint: disable=import-outside-toplevel,cyclic-import |
|||
from pydevicetree.source import parseTree |
|||
return parseTree(dts) |
|||
|
|||
@staticmethod |
|||
def parseFile(filename: str, followIncludes: bool = False) -> 'Devicetree': |
|||
"""Parse a file and return a Devicetree object""" |
|||
# pylint: disable=import-outside-toplevel,cyclic-import |
|||
from cot_dt2c.pydevicetree.source.parser import parseTree |
|||
with open(filename, 'r') as f: |
|||
contents = f.read() |
|||
dirname = os.path.dirname(filename) |
|||
if dirname != "": |
|||
dirname += "/" |
|||
return parseTree(contents, dirname, followIncludes) |
|||
|
|||
@staticmethod |
|||
def parseStr(input: str, followIncludes: bool = False) -> 'Devicetree': |
|||
from cot_dt2c.pydevicetree.source.parser import parseTree |
|||
return parseTree(input, "", followIncludes) |
|||
|
|||
def all_nodes(self) -> Iterable[Node]: |
|||
"""Get an iterable over all nodes in the tree""" |
|||
return self.child_nodes() |
|||
|
|||
def root(self) -> Node: |
|||
"""Get the root node of the tree""" |
|||
for n in self.all_nodes(): |
|||
if n.name == "/": |
|||
return n |
|||
raise Exception("Devicetree has no root node!") |
|||
|
|||
def aliases(self) -> Optional[Node]: |
|||
"""Get the aliases node of the tree if it exists""" |
|||
for n in self.all_nodes(): |
|||
if n.name == "aliases": |
|||
return n |
|||
return None |
|||
|
|||
def chosen(self, property_name: str, func: ChosenCallback = None) -> Optional[PropertyValues]: |
|||
"""Get the values associated with one of the properties in the chosen node""" |
|||
def match_chosen(node: Node) -> bool: |
|||
return node.name == "chosen" |
|||
|
|||
for n in filter(match_chosen, self.all_nodes()): |
|||
for p in n.properties: |
|||
if p.name == property_name: |
|||
if func is not None: |
|||
func(p.values) |
|||
return p.values |
|||
|
|||
return None |
@ -1,278 +0,0 @@ |
|||
#!/usr/bin/env python3 |
|||
# Copyright (c) 2019 SiFive Inc. |
|||
# SPDX-License-Identifier: Apache-2.0 |
|||
|
|||
from typing import List, Any, cast, Tuple, Optional, Iterable |
|||
from itertools import zip_longest |
|||
|
|||
from cot_dt2c.pydevicetree.ast.helpers import wrapStrings, formatLevel |
|||
|
|||
class PropertyValues: |
|||
"""PropertyValues is the parent class of all values which can be assigned to a Property |
|||
|
|||
Child classes include |
|||
|
|||
Bytestring |
|||
CellArray |
|||
StringList |
|||
""" |
|||
def __init__(self, values: List[Any]): |
|||
"""Create a PropertyValue""" |
|||
self.values = values |
|||
|
|||
def __repr__(self) -> str: |
|||
return "<PropertyValues " + self.values.__repr__() + ">" |
|||
|
|||
def __str__(self) -> str: |
|||
return self.to_dts() |
|||
|
|||
def __iter__(self): |
|||
return iter(self.values) |
|||
|
|||
def __len__(self) -> int: |
|||
return len(self.values) |
|||
|
|||
def to_dts(self, formatHex: bool = False) -> str: |
|||
"""Format the values in Devicetree Source format""" |
|||
return ", ".join(wrapStrings(self.values, formatHex)) |
|||
|
|||
def __getitem__(self, key) -> Any: |
|||
return self.values[key] |
|||
|
|||
def __eq__(self, other) -> bool: |
|||
if isinstance(other, PropertyValues): |
|||
return self.values == other.values |
|||
return self.values == other |
|||
|
|||
class Bytestring(PropertyValues): |
|||
"""A Bytestring is a sequence of bytes |
|||
|
|||
In Devicetree, Bytestrings are represented as a sequence of two-digit hexadecimal integers, |
|||
optionally space-separated, enclosed by square brackets: |
|||
|
|||
[de ad be eef] |
|||
""" |
|||
def __init__(self, bytelist: List[int]): |
|||
"""Create a Bytestring object""" |
|||
PropertyValues.__init__(self, cast(List[Any], bytearray(bytelist))) |
|||
|
|||
def __repr__(self) -> str: |
|||
return "<Bytestring " + str(self.values) + ">" |
|||
|
|||
def to_dts(self, formatHex: bool = False) -> str: |
|||
"""Format the bytestring in Devicetree Source format""" |
|||
return "[" + " ".join("%02x" % v for v in self.values) + "]" |
|||
|
|||
class CellArray(PropertyValues): |
|||
"""A CellArray is an array of integer values |
|||
|
|||
CellArrays are commonly used as the value of Devicetree properties like `reg` and `interrupts`. |
|||
The interpretation of each element of a CellArray is device-dependent. For example, the `reg` |
|||
property encodes a CellArray as a list of tuples (base address, size), while the `interrupts` |
|||
property encodes a CellArray as simply a list of interrupt line numbers. |
|||
""" |
|||
def __init__(self, cells: List[Any]): |
|||
"""Create a CellArray object""" |
|||
PropertyValues.__init__(self, cells) |
|||
|
|||
def __repr__(self) -> str: |
|||
return "<CellArray " + self.values.__repr__() + ">" |
|||
|
|||
def to_dts(self, formatHex: bool = False) -> str: |
|||
"""Format the cell array in Devicetree Source format""" |
|||
dtsValues = [] |
|||
for i in self.values: |
|||
if not isinstance(i, OneString) and not isinstance(i, str): |
|||
dtsValues.append(i) |
|||
return "<" + " ".join(wrapStrings(dtsValues, formatHex)) + ">" |
|||
|
|||
class RegArray(CellArray): |
|||
"""A RegArray is the CellArray assigned to the reg property""" |
|||
def __init__(self, cells: List[int], |
|||
address_cells: int, size_cells: int, |
|||
names: Optional[List[str]] = None): |
|||
"""Create a RegArray from a list of ints""" |
|||
# pylint: disable=too-many-locals |
|||
CellArray.__init__(self, cells) |
|||
self.address_cells = address_cells |
|||
self.size_cells = size_cells |
|||
|
|||
self.tuples = [] # type: List[Tuple[int, int, Optional[str]]] |
|||
|
|||
group_size = self.address_cells + self.size_cells |
|||
|
|||
if len(cells) % group_size != 0: |
|||
raise Exception("CellArray does not contain enough cells") |
|||
|
|||
grouped_cells = [cells[i:i+group_size] for i in range(0, len(cells), group_size)] |
|||
|
|||
if not names: |
|||
names = [] |
|||
|
|||
for group, name in zip_longest(grouped_cells, cast(Iterable[Any], names)): |
|||
address = 0 |
|||
a_cells = list(reversed(group[:self.address_cells])) |
|||
for a, i in zip(a_cells, range(len(a_cells))): |
|||
address += (1 << (32 * i)) * a |
|||
|
|||
size = 0 |
|||
s_cells = list(reversed(group[self.address_cells:])) |
|||
for s, i in zip(s_cells, range(len(s_cells))): |
|||
size += (1 << (32 * i)) * s |
|||
|
|||
self.tuples.append(cast(Tuple[int, int, Optional[str]], tuple([address, size, name]))) |
|||
|
|||
def get_by_name(self, name: str) -> Optional[Tuple[int, int]]: |
|||
"""Returns the (address, size) tuple with a given name""" |
|||
for t in self.tuples: |
|||
if t[2] == name: |
|||
return cast(Tuple[int, int], tuple(t[:2])) |
|||
return None |
|||
|
|||
def __repr__(self) -> str: |
|||
return "<RegArray " + self.values.__repr__() + ">" |
|||
|
|||
def __iter__(self) -> Iterable[Tuple[int, int]]: |
|||
return cast(Iterable[Tuple[int, int]], map(lambda t: tuple(t[:2]), self.tuples)) |
|||
|
|||
def __len__(self) -> int: |
|||
return len(self.tuples) |
|||
|
|||
def __getitem__(self, key) -> Optional[Tuple[int, int]]: |
|||
return list(self.__iter__())[key] |
|||
|
|||
class RangeArray(CellArray): |
|||
"""A RangeArray is the CellArray assigned to the range property""" |
|||
def __init__(self, cells: List[int], child_address_cells: int, |
|||
parent_address_cells: int, size_cells: int): |
|||
"""Create a RangeArray from a list of ints""" |
|||
# pylint: disable=too-many-locals |
|||
CellArray.__init__(self, cells) |
|||
self.child_address_cells = child_address_cells |
|||
self.parent_address_cells = parent_address_cells |
|||
self.size_cells = size_cells |
|||
|
|||
self.tuples = [] # type: List[Tuple[int, int, int]] |
|||
|
|||
group_size = self.child_address_cells + self.parent_address_cells + self.size_cells |
|||
|
|||
if len(cells) % group_size != 0: |
|||
raise Exception("CellArray does not contain enough cells") |
|||
|
|||
grouped_cells = [cells[i:i+group_size] for i in range(0, len(cells), group_size)] |
|||
|
|||
def sum_cells(cells: List[int]): |
|||
value = 0 |
|||
for cell, index in zip(list(reversed(cells)), range(len(cells))): |
|||
value += (1 << (32 * index)) * cell |
|||
return value |
|||
|
|||
for group in grouped_cells: |
|||
child_address = sum_cells(group[:self.child_address_cells]) |
|||
parent_address = sum_cells(group[self.child_address_cells: \ |
|||
self.child_address_cells + self.parent_address_cells]) |
|||
size = sum_cells(group[self.child_address_cells + self.parent_address_cells:]) |
|||
|
|||
self.tuples.append(cast(Tuple[int, int, int], |
|||
tuple([child_address, parent_address, size]))) |
|||
|
|||
def __repr__(self) -> str: |
|||
return "<RangeArray " + self.values.__repr__() + ">" |
|||
|
|||
def __iter__(self): |
|||
return iter(self.tuples) |
|||
|
|||
def __len__(self) -> int: |
|||
return len(self.tuples) |
|||
|
|||
def __getitem__(self, key) -> Any: |
|||
return self.tuples[key] |
|||
|
|||
class StringList(PropertyValues): |
|||
"""A StringList is a list of null-terminated strings |
|||
|
|||
The most common use of a StringList in Devicetree is to describe the `compatible` property. |
|||
""" |
|||
def __init__(self, strings: List[str]): |
|||
"""Create a StringList object""" |
|||
PropertyValues.__init__(self, strings) |
|||
|
|||
def __repr__(self) -> str: |
|||
return "<StringList " + self.values.__repr__() + ">" |
|||
|
|||
def to_dts(self, formatHex: bool = False) -> str: |
|||
"""Format the list of strings in Devicetree Source format""" |
|||
return ", ".join(wrapStrings(self.values)) |
|||
|
|||
class OneString(PropertyValues): |
|||
def __init__(self, string: str): |
|||
PropertyValues.__init__(self, string) |
|||
|
|||
def __repr__(self) -> str: |
|||
return self.values.__repr__() |
|||
|
|||
def to_dts(self, formatHex: bool = False) -> str: |
|||
return super().to_dts(formatHex) |
|||
|
|||
class Property: |
|||
"""A Property is a key-value pair for a Devicetree Node |
|||
|
|||
Properties are used to describe Nodes in the tree. There are many common properties, like |
|||
|
|||
- compatible |
|||
- reg |
|||
- reg-names |
|||
- ranges |
|||
- interrupt-controller |
|||
- interrupts |
|||
- interrupt-parent |
|||
- clocks |
|||
- status |
|||
|
|||
Which might commonly describe many or all nodes in a tree, and there are device, vendor, |
|||
operating system, runtime-specific properties. |
|||
|
|||
Properties can possess no value, conveing meaning solely by their presence: |
|||
|
|||
interrupt-controller; |
|||
|
|||
Properties can also possess values such as an array of cells, a list of strings, etc. |
|||
|
|||
reg = <0x10013000 0x1000>; |
|||
compatible = "sifive,rocket0", "riscv"; |
|||
|
|||
And properties can posses arbitrarily complex values, such as the following from the |
|||
Devicetree specification: |
|||
|
|||
example = <0xf00f0000 19>, "a strange property format"; |
|||
""" |
|||
def __init__(self, name: str, values: PropertyValues): |
|||
"""Create a Property object""" |
|||
self.name = name |
|||
self.values = values |
|||
|
|||
def __repr__(self) -> str: |
|||
return "<Property %s>" % self.name |
|||
|
|||
def __str__(self) -> str: |
|||
return self.to_dts() |
|||
|
|||
@staticmethod |
|||
def from_dts(dts: str) -> 'Property': |
|||
"""Parse a file and return a Devicetree object""" |
|||
# pylint: disable=import-outside-toplevel,cyclic-import |
|||
from pydevicetree.source import parseProperty |
|||
return parseProperty(dts) |
|||
|
|||
def to_dts(self, level: int = 0) -> str: |
|||
"""Format the Property assignment in Devicetree Source format""" |
|||
if self.name in ["reg", "ranges"]: |
|||
value = self.values.to_dts(formatHex=True) |
|||
else: |
|||
value = self.values.to_dts(formatHex=False) |
|||
|
|||
if value != "": |
|||
return formatLevel(level, "%s = %s;\n" % (self.name, value)) |
|||
if self.name == "ifdef": |
|||
return "" |
|||
return formatLevel(level, "%s;\n" % self.name) |
@ -1,111 +0,0 @@ |
|||
#!/usr/bin/env python3 |
|||
# Copyright (c) 2019 SiFive Inc. |
|||
# SPDX-License-Identifier: Apache-2.0 |
|||
|
|||
from typing import Union, Iterator |
|||
|
|||
class Label: |
|||
"""A Label is a unique identifier for a Node |
|||
|
|||
For example, the following node has the label "uart0": |
|||
|
|||
uart0: uart@10013000 { |
|||
... |
|||
}; |
|||
""" |
|||
def __init__(self, name: str): |
|||
"""Create a Label""" |
|||
self.name = name |
|||
|
|||
def __repr__(self) -> str: |
|||
return "<Label " + self.name + ">" |
|||
|
|||
def __eq__(self, other: object) -> bool: |
|||
if isinstance(other, Label): |
|||
return self.name == other.name |
|||
if isinstance(other, str): |
|||
return self.name == other |
|||
return False |
|||
|
|||
def to_dts(self) -> str: |
|||
"""Format the label in Devicetree Source format""" |
|||
return self.name + ":" |
|||
|
|||
class Path: |
|||
"""A Path uniquely identifies a Node by its parents and (optionally) unit address""" |
|||
def __init__(self, path: str): |
|||
"""Create a path out of a string""" |
|||
self.path = path |
|||
|
|||
def to_dts(self) -> str: |
|||
"""Format the Path in Devicetree Source format""" |
|||
return self.path |
|||
|
|||
def __repr__(self) -> str: |
|||
return "<Path " + self.to_dts() + ">" |
|||
|
|||
def __eq__(self, other: object) -> bool: |
|||
if isinstance(other, Path): |
|||
return self.to_dts() == other.to_dts() |
|||
if isinstance(other, str): |
|||
return self.to_dts() == other |
|||
return False |
|||
|
|||
def __iter__(self) -> Iterator[str]: |
|||
return iter(self.path.split("/")) |
|||
|
|||
def replace(self, old: str, new: str) -> 'Path': |
|||
"""Replace any elements of the path which match 'old' with a new element 'new'""" |
|||
return Path(self.path.replace(old, new)) |
|||
|
|||
class Reference: |
|||
"""A Reference is a Devicetree construct which points to a Node in the tree |
|||
|
|||
The following are types of references: |
|||
|
|||
- A reference to a label: |
|||
|
|||
&my-label; |
|||
|
|||
- A reference to a node by path: |
|||
|
|||
&{/path/to/node@deadbeef} |
|||
|
|||
This is the parent class for both types of references, LabelReference and PathReference |
|||
""" |
|||
# pylint: disable=no-self-use |
|||
def to_dts(self, formatHex: bool = False) -> str: |
|||
"""Format the Reference in Devicetree Source format""" |
|||
return "" |
|||
|
|||
class LabelReference(Reference): |
|||
"""A LabelReference is a reference to a Node by label""" |
|||
def __init__(self, label: Union[Label, str]): |
|||
"""Create a LabelReference from a Label or string""" |
|||
if isinstance(label, Label): |
|||
self.label = label |
|||
elif isinstance(label, str): |
|||
self.label = Label(label) |
|||
|
|||
def __repr__(self) -> str: |
|||
return "<LabelReference " + self.to_dts() + ">" |
|||
|
|||
def to_dts(self, formatHex: bool = False) -> str: |
|||
"""Format the LabelReference in Devicetree Source format""" |
|||
return "&" + self.label.name |
|||
|
|||
class PathReference(Reference): |
|||
"""A PathReference is a reference to a Node by path""" |
|||
def __init__(self, path: Union[Path, str]): |
|||
"""Create a PathReference from a Path or string""" |
|||
if isinstance(path, Path): |
|||
self.path = path |
|||
elif isinstance(path, str): |
|||
self.path = Path(path) |
|||
|
|||
def __repr__(self) -> str: |
|||
return "<PathReference " + self.to_dts() + ">" |
|||
|
|||
def to_dts(self, formatHex: bool = False) -> str: |
|||
"""Format the PathReference in Devicetree Source format""" |
|||
return "&{" + self.path.to_dts() + "}" |
@ -1,5 +0,0 @@ |
|||
#!/usr/bin/env python3 |
|||
# Copyright (c) 2019 SiFive Inc. |
|||
# SPDX-License-Identifier: Apache-2.0 |
|||
|
|||
from cot_dt2c.pydevicetree.source.parser import parseTree, parseNode, parseProperty |
@ -1,95 +0,0 @@ |
|||
#!/usr/bin/env python3 |
|||
# Copyright (c) 2019 SiFive Inc. |
|||
# SPDX-License-Identifier: Apache-2.0 |
|||
|
|||
import os |
|||
import sys |
|||
|
|||
import pyparsing as p # type: ignore |
|||
|
|||
ENV_CACHE_OPTION = "PYDEVICETREE_CACHE_SIZE_BOUND" |
|||
|
|||
cache_bound = None |
|||
if ENV_CACHE_OPTION in os.environ: |
|||
option = os.environ[ENV_CACHE_OPTION] |
|||
if option != "None": |
|||
try: |
|||
cache_bound = int(option) |
|||
except ValueError: |
|||
print("%s requires a valid integer" % ENV_CACHE_OPTION, file=sys.stderr) |
|||
p.ParserElement.enablePackrat(cache_bound) |
|||
|
|||
node_name = p.Word(p.alphanums + ",.-+_") ^ p.Literal("/") |
|||
integer = p.pyparsing_common.integer ^ (p.Literal("0x").suppress() + p.pyparsing_common.hex_integer) |
|||
unit_address = p.pyparsing_common.hex_integer |
|||
unit_addresses = p.delimitedList(unit_address("address"), delim=",") |
|||
node_handle = node_name("node_name") + p.Optional(p.Literal("@") + unit_addresses) |
|||
property_name = p.Word(p.alphanums + ",.-_+?#") |
|||
label = p.Word(p.alphanums + "_").setResultsName("label") |
|||
label_creation = p.Combine(label + p.Literal(":")) |
|||
string = p.QuotedString(quoteChar='"') |
|||
stringlist = p.delimitedList(string) |
|||
node_path = p.Combine(p.Literal("/") + \ |
|||
p.delimitedList(node_handle, delim="/", combine=True)).setResultsName("path") |
|||
path_reference = p.Literal("&{").suppress() + node_path + p.Literal("}").suppress() |
|||
label_reference = p.Literal("&").suppress() + label |
|||
label_raw = p.Word(p.alphanums + "_") |
|||
reference = path_reference ^ label_reference ^ label_raw |
|||
include_directive = p.Literal("/include/") + p.QuotedString(quoteChar='"') |
|||
generic_directive = p.QuotedString(quoteChar="/", unquoteResults=False) + \ |
|||
p.Optional(string ^ property_name ^ node_name ^ reference ^ (integer * 2)) + \ |
|||
p.Literal(";").suppress() |
|||
directive = include_directive ^ generic_directive |
|||
|
|||
operator = p.oneOf("~ ! * / + - << >> < <= > >= == != & ^ | && ||") |
|||
arith_expr = p.Forward() |
|||
ternary_element = arith_expr ^ integer |
|||
ternary_expr = ternary_element + p.Literal("?") + ternary_element + p.Literal(":") + ternary_element |
|||
arith_expr = p.nestedExpr(content=(p.OneOrMore(operator ^ integer) ^ ternary_expr)) |
|||
arth_str = p.Forward() |
|||
arith_str_expr = p.nestedExpr(content=(p.OneOrMore(operator ^ integer ^ label_raw ^ p.Literal(",")) ^ ternary_expr)) |
|||
|
|||
label_list = p.OneOrMore(p.Combine(label + p.Literal("\n"))) |
|||
|
|||
cell_array = p.Literal("<").suppress() + \ |
|||
p.ZeroOrMore(integer ^ arith_expr ^ arith_str_expr ^ label_list ^ string ^ reference ^ label_creation.suppress()) + \ |
|||
p.Literal(">").suppress() |
|||
bytestring = p.Literal("[").suppress() + \ |
|||
(p.OneOrMore(p.Word(p.hexnums, exact=2) ^ label_creation.suppress())) + \ |
|||
p.Literal("]").suppress() |
|||
property_values = p.Forward() |
|||
property_values = p.delimitedList(property_values ^ cell_array ^ bytestring ^ stringlist ^ \ |
|||
reference ^ label_raw) |
|||
property_assignment = property_name("property_name") + p.Optional(p.Literal("=").suppress() + \ |
|||
(property_values)).setResultsName("value") + p.Optional(p.Literal(";").suppress()) |
|||
|
|||
ifdef_label = p.ZeroOrMore(p.Word(p.alphanums + " _|//*=/(/)")) |
|||
ifdef_define = p.Combine(p.Keyword("#if") + ifdef_label) |
|||
ifdef_end = p.Combine(p.Keyword("#endif") + ifdef_label) |
|||
ifdef_define_values = p.Forward() |
|||
ifdef_define_values = p.ZeroOrMore(ifdef_define) |
|||
ifdef_end_values = p.Forward() |
|||
ifdef_end_values = p.ZeroOrMore(ifdef_end) |
|||
|
|||
node_opener = ifdef_define_values + p.Optional(label_creation) + node_handle + p.Literal("{").suppress() |
|||
node_reference_opener = reference + p.Literal("{").suppress() |
|||
node_closer = p.Literal("}").suppress() + p.Literal(";").suppress() + ifdef_end_values |
|||
node_definition = p.Forward() |
|||
# pylint: disable=expression-not-assigned |
|||
node_definition << (node_opener ^ node_reference_opener) + \ |
|||
p.ZeroOrMore(property_assignment ^ directive ^ node_definition ^ ifdef_define ^ ifdef_end) + \ |
|||
node_closer |
|||
|
|||
devicetree = p.ZeroOrMore(directive ^ node_definition) |
|||
|
|||
devicetree.ignore(p.cStyleComment) |
|||
devicetree.ignore("//" + p.SkipTo(p.lineEnd)) |
|||
devicetree.ignore("#include" + p.SkipTo(p.lineEnd)) |
|||
devicetree.ignore("#define" + p.SkipTo(p.lineEnd)) |
|||
devicetree.ignore("#else" + p.SkipTo(p.lineEnd)) |
|||
devicetree.ignore("#error" + p.SkipTo(p.lineEnd)) |
|||
devicetree.ignore("#ifndef" + p.SkipTo(p.lineEnd)) |
|||
|
|||
if __name__ == "__main__": |
|||
if len(sys.argv) > 1: |
|||
devicetree.parseFile(sys.argv[1]).pprint() |
@ -1,238 +0,0 @@ |
|||
#!/usr/bin/env python3 |
|||
# Copyright (c) 2019 SiFive Inc. |
|||
# SPDX-License-Identifier: Apache-2.0 |
|||
|
|||
from itertools import chain |
|||
|
|||
from cot_dt2c.pydevicetree.source import grammar |
|||
from cot_dt2c.pydevicetree.ast import * |
|||
|
|||
ifdef_stack = [] |
|||
|
|||
def transformNode(string, location, tokens): |
|||
"""Transforms a ParseResult into a Node""" |
|||
properties = [e for e in tokens.asList() if isinstance(e, Property)] |
|||
directives = [e for e in tokens.asList() if isinstance(e, Directive)] |
|||
children = [e for e in tokens.asList() if isinstance(e, Node)] |
|||
|
|||
if isinstance(tokens[0], Reference): |
|||
return NodeReference(tokens[0], properties=properties, |
|||
directives=directives, children=children) |
|||
return Node(tokens.node_name, tokens.label, tokens.address, properties=properties, |
|||
directives=directives, children=children) |
|||
|
|||
def transformPropertyAssignment(string, location, tokens): |
|||
"""Transforms a ParseResult into a Property""" |
|||
for v in tokens.value: |
|||
if isinstance(v, PropertyValues): |
|||
return Property(tokens.property_name, v) |
|||
if isinstance(v, CellArray): |
|||
return Property(tokens.property_name, v) |
|||
if isinstance(v, StringList): |
|||
return Property(tokens.property_name, v) |
|||
if isinstance(v, Reference): |
|||
return Property(tokens.property_name, v) |
|||
|
|||
return Property(tokens.property_name, PropertyValues([])) |
|||
|
|||
def transformDirective(string, location, tokens): |
|||
"""Transforms a ParseResult into a Directive""" |
|||
if len(tokens.asList()) > 1: |
|||
return Directive(tokens[0], tokens[1]) |
|||
return Directive(tokens[0]) |
|||
|
|||
def evaluateArithExpr(string, location, tokens): |
|||
"""Evaluates a ParseResult as a python expression""" |
|||
flat_tokens = list(chain.from_iterable(tokens.asList())) |
|||
expr = " ".join(str(t) for t in flat_tokens) |
|||
# pylint: disable=eval-used |
|||
return eval(expr) |
|||
|
|||
def transformTernary(string, location, tokens): |
|||
"""Evaluates a ParseResult as a ternary expression""" |
|||
# pylint: disable=eval-used |
|||
return eval(str(tokens[2]) +" if " + str(tokens[0]) + " else " + str(tokens[4])) |
|||
|
|||
def transformPropertyValues(string, location, tokens): |
|||
"""Transforms a ParseResult into a PropertyValues""" |
|||
if len(tokens.asList()) == 1: |
|||
return tokens.asList()[0] |
|||
return PropertyValues(tokens.asList()) |
|||
|
|||
def transformStringList(string, location, tokens): |
|||
"""Transforms a ParseResult into a StringList""" |
|||
return StringList(tokens.asList()) |
|||
|
|||
def transformString(string, location, token): |
|||
return OneString(token) |
|||
|
|||
def transformIfdefMacro(string, location, tokens): |
|||
tokenlist = tokens.asList() |
|||
for t in tokenlist: |
|||
ifdef_stack.append(t) |
|||
return Property("ifdef", PropertyValues(ifdef_stack.copy())) |
|||
|
|||
def transformIfdefEnd(string, location, tokens): |
|||
tokenlist = tokens.asList() |
|||
for t in tokenlist: |
|||
ifdef_stack.pop() |
|||
|
|||
def transformIfdef(string, location, tokens): |
|||
return Property("ifdef", PropertyValues(tokens)) |
|||
|
|||
def evaluateStrArithExpr(string, location, tokens): |
|||
"""Evaluates a ParseResult as a python expression""" |
|||
flat_tokens = list(chain.from_iterable(tokens.asList())) |
|||
for i, t in enumerate(flat_tokens): |
|||
if isinstance(t, int): |
|||
flat_tokens[i] = "(" + str(t) + ")" |
|||
expr = " ".join(str(t) for t in flat_tokens) |
|||
# pylint: disable=eval-used |
|||
return expr |
|||
|
|||
def transformBytestring(string, location, tokens): |
|||
"""Transforms a ParseResult into a Bytestring""" |
|||
inttokens = [] |
|||
for t in tokens.asList(): |
|||
if all(c in "0123456789abcdefABCDEF" for c in t): |
|||
inttokens.append(int(t, base=16)) |
|||
return Bytestring(inttokens) |
|||
|
|||
def transformCellArray(string, location, tokens): |
|||
"""Transforms a ParseResult into a CellArray""" |
|||
return CellArray(tokens.asList()) |
|||
|
|||
def transformLabel(string, location, tokens): |
|||
"""Transforms a ParseResult into a Label""" |
|||
return Label(tokens.label) |
|||
|
|||
def transformPath(string, location, tokens): |
|||
"""Transforms a ParseResult into a Path""" |
|||
path = "" |
|||
for handle in tokens.path[0].split("/"): |
|||
if "@" in handle: |
|||
node, address = handle.split("@") |
|||
path += "/%s@%x" % (node, int(address)) |
|||
elif handle != "": |
|||
path += "/" + handle |
|||
return Path(path) |
|||
|
|||
def transformPathReference(string, location, tokens): |
|||
"""Transforms a ParseResult into a PathReference""" |
|||
return PathReference(tokens[0]) |
|||
|
|||
def transformLabelReference(string, location, tokens): |
|||
"""Transforms a ParseResult into a LabelReference""" |
|||
return LabelReference(tokens[0]) |
|||
|
|||
def transformReference(string, location, tokens): |
|||
"""Transforms a ParseResult into a Reference""" |
|||
if isinstance(tokens[0], Reference): |
|||
return tokens[0] |
|||
return None |
|||
|
|||
grammar.label.setParseAction(transformLabel) |
|||
grammar.node_path.setParseAction(transformPath) |
|||
grammar.path_reference.setParseAction(transformPathReference) |
|||
grammar.label_reference.setParseAction(transformLabelReference) |
|||
grammar.reference.setParseAction(transformReference) |
|||
grammar.node_definition.setParseAction(transformNode) |
|||
grammar.property_assignment.setParseAction(transformPropertyAssignment) |
|||
grammar.directive.setParseAction(transformDirective) |
|||
grammar.arith_expr.setParseAction(evaluateArithExpr) |
|||
grammar.ternary_expr.setParseAction(transformTernary) |
|||
grammar.stringlist.setParseAction(transformStringList) |
|||
grammar.bytestring.setParseAction(transformBytestring) |
|||
grammar.cell_array.setParseAction(transformCellArray) |
|||
grammar.property_values.setParseAction(transformPropertyValues) |
|||
grammar.label_raw.setParseAction(transformString) |
|||
grammar.ifdef_define_values.setParseAction(transformIfdefMacro) |
|||
grammar.ifdef_end_values.setParseAction(transformIfdefEnd) |
|||
grammar.arith_str_expr.setParseAction(transformPropertyValues) |
|||
|
|||
def printTree(tree, level=0): |
|||
"""Helper function to print a bunch of elements as a tree""" |
|||
def printlevel(level, s): |
|||
print(" " * level + s) |
|||
|
|||
for item in tree: |
|||
if isinstance(item, Node): |
|||
if item.address: |
|||
printlevel(level, "Node %s@%x" % (item.name, item.address)) |
|||
else: |
|||
printlevel(level, "Node %s" % item.name) |
|||
|
|||
if item.label: |
|||
printlevel(level, " Label: %s" % item.label) |
|||
|
|||
if item.parent: |
|||
printlevel(level, " Parent: %s" % item.parent) |
|||
|
|||
printTree(item.properties, level=(level + 1)) |
|||
|
|||
printTree(item.children, level=(level + 1)) |
|||
elif isinstance(item, Property): |
|||
if item.values: |
|||
printlevel(level, "Property %s: %s" % (item.name, item.values)) |
|||
else: |
|||
printlevel(level, "Property %s" % item.name) |
|||
elif isinstance(item, Directive): |
|||
if item.options: |
|||
printlevel(level, "Directive %s: %s" % (item.directive, item.options)) |
|||
else: |
|||
printlevel(level, "Directive %s" % item.directive) |
|||
|
|||
def parentNodes(tree, parent=None): |
|||
"""Walks a tree and sets Nodes' parent field to point at their parent""" |
|||
for item in tree: |
|||
if isinstance(item, Node): |
|||
item.parent = parent |
|||
parentNodes(item.children, item) |
|||
|
|||
def recurseIncludeFiles(elements, pwd): |
|||
"""Recursively follows and parses /include/ directives an a tree""" |
|||
for e in elements: |
|||
if isinstance(e, Directive): |
|||
if e.directive == "/include/": |
|||
# Prefix with current directory if path is not absolute |
|||
if e.option[0] != '/': |
|||
e.option = pwd + e.option |
|||
|
|||
with open(e.option, 'r') as f: |
|||
contents = f.read() |
|||
|
|||
elements += parseElements(contents) |
|||
|
|||
del elements[elements.asList().index(e)] |
|||
|
|||
def parseElements(dts, pwd="", followIncludes=False): |
|||
"""Parses a string into a list of elements""" |
|||
elements = grammar.devicetree.parseString(dts, parseAll=True) |
|||
parentNodes(elements) |
|||
if followIncludes: |
|||
recurseIncludeFiles(elements, pwd) |
|||
return elements |
|||
|
|||
def parseTree(dts, pwd="", followIncludes=False): |
|||
"""Parses a string into a full Devicetree""" |
|||
return Devicetree(parseElements(dts, pwd, followIncludes)) |
|||
|
|||
def parseNode(dts): |
|||
"""Parses a string into a Devictreee Node""" |
|||
return grammar.node_definition.parseString(dts, parseAll=True)[0] |
|||
|
|||
def parseProperty(dts): |
|||
"""Parses a string into a Devicetree Property""" |
|||
return grammar.property_assignment.parseString(dts, parseAll=True)[0] |
|||
|
|||
if __name__ == "__main__": |
|||
import sys |
|||
if len(sys.argv) > 1: |
|||
with open(sys.argv[1], 'r') as f: |
|||
dts = f.read() |
|||
tree = parseTree(dts) |
|||
printTree(tree) |
|||
print(tree) |
|||
else: |
|||
print("Please pass the devicetree source file as an argument") |
|||
sys.exit(1) |
@ -0,0 +1,334 @@ |
|||
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. |
|||
|
|||
[[package]] |
|||
name = "atomicwrites" |
|||
version = "1.4.1" |
|||
description = "Atomic file writes." |
|||
optional = false |
|||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" |
|||
files = [ |
|||
{file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}, |
|||
] |
|||
|
|||
[[package]] |
|||
name = "attrs" |
|||
version = "24.2.0" |
|||
description = "Classes Without Boilerplate" |
|||
optional = false |
|||
python-versions = ">=3.7" |
|||
files = [ |
|||
{file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, |
|||
{file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, |
|||
] |
|||
|
|||
[package.extras] |
|||
benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] |
|||
cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] |
|||
dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] |
|||
docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] |
|||
tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] |
|||
tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] |
|||
|
|||
[[package]] |
|||
name = "click" |
|||
version = "8.1.7" |
|||
description = "Composable command line interface toolkit" |
|||
optional = false |
|||
python-versions = ">=3.7" |
|||
files = [ |
|||
{file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, |
|||
{file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, |
|||
] |
|||
|
|||
[package.dependencies] |
|||
colorama = {version = "*", markers = "platform_system == \"Windows\""} |
|||
|
|||
[[package]] |
|||
name = "colorama" |
|||
version = "0.4.6" |
|||
description = "Cross-platform colored terminal text." |
|||
optional = false |
|||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" |
|||
files = [ |
|||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, |
|||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, |
|||
] |
|||
|
|||
[[package]] |
|||
name = "igraph" |
|||
version = "0.11.6" |
|||
description = "High performance graph data structures and algorithms" |
|||
optional = false |
|||
python-versions = ">=3.8" |
|||
files = [ |
|||
{file = "igraph-0.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3f8b837181e8e87676be3873ce87cc92cc234efd58a2da2f6b4e050db150fcf4"}, |
|||
{file = "igraph-0.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:245c4b7d7657849eff80416f5df4525c8fc44c74a981ee4d44f0ef2612c3bada"}, |
|||
{file = "igraph-0.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdb7be3d165073c0136295c0808e9edc57ba096cdb26e94086abb04561f7a292"}, |
|||
{file = "igraph-0.11.6-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58974e20df2986a1ae52a16e51ecb387cc0cbeb41c5c0ddff4d373a1bbf1d9c5"}, |
|||
{file = "igraph-0.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bef14de5e8ab70724a43808b1ed14aaa6fe1002f87e592289027a3827a8f44a"}, |
|||
{file = "igraph-0.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:86c1e98de2e32d074df8510bf18abfa1f4c5fda4cb28a009985a5d746b0c0125"}, |
|||
{file = "igraph-0.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ebc5b3d702158abeb2e4d2414374586a2b932e1a07e48352b470600e1733d528"}, |
|||
{file = "igraph-0.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0efe6d0fb22d3987a800eb3857ed04df9eb4c5dddd0998be05232cb646f1c337"}, |
|||
{file = "igraph-0.11.6-cp38-cp38-win32.whl", hash = "sha256:f4e68b27497b1c8ada2fb2bc35ef3fa7b0d72e84306b3d648d3de240fc618c32"}, |
|||
{file = "igraph-0.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:5665b33dfbfca5f54ce9b4fea6b97903bd0e99fb1b02acf5e57e600bdfa5a355"}, |
|||
{file = "igraph-0.11.6-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:8aabef03d787b519d1075dfc0da4a1109fb113b941334883e3e7947ac30a459e"}, |
|||
{file = "igraph-0.11.6-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1f2cc4a518d99cdf6cae514f85e93e56852bc8c325b3abb96037d1d690b5975f"}, |
|||
{file = "igraph-0.11.6-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1e859238be52ab8ccc614d18f9362942bc88ce543afc12548f81ae99b10801d"}, |
|||
{file = "igraph-0.11.6-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d61fbe5e85eb4ae9efe08c461f9bdeedb02a2b5739fbc223d324a71f40a28be2"}, |
|||
{file = "igraph-0.11.6-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6620ba39df29fd42151becf82309b54e57148233c9c3ef890eed62e25eed8a5"}, |
|||
{file = "igraph-0.11.6-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59666589bb3d07f310cda2c5106a8adeeb77c2ef27fecf1c6438b6091f4ca69d"}, |
|||
{file = "igraph-0.11.6-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:8750b6d6caebf199cf7dc41c931f58e330153779707391e30f0a29f02666fb6e"}, |
|||
{file = "igraph-0.11.6-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:967d6f2c30fe94317da15e459374d0fb8ca3e56020412f201ecd07dd5b5352f2"}, |
|||
{file = "igraph-0.11.6-cp39-abi3-win32.whl", hash = "sha256:9744f95a67319eb6cb487ceabf30f5d7940de34bada51f0ba63adbd23e0f94ad"}, |
|||
{file = "igraph-0.11.6-cp39-abi3-win_amd64.whl", hash = "sha256:b80e69eb11faa9c57330a9ffebdde5808966efe1c1f638d4d4827ea04df7aca8"}, |
|||
{file = "igraph-0.11.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0329c16092e2ea7930d5f8368666ce7cb704900cc0ea04e4afe9ea1dd46e44af"}, |
|||
{file = "igraph-0.11.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21752313f449bd8688e5688e95ea7231cea5e9199c7162535029be0d9af848ac"}, |
|||
{file = "igraph-0.11.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea25e136c6c4161f53ff58868b23ff6c845193050ab0e502236d68e5d4174e32"}, |
|||
{file = "igraph-0.11.6-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac84433a03aef15e4b810010b08882b09854a3669450ccf31e392dbe295d2a66"}, |
|||
{file = "igraph-0.11.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac697a44e3573169fa2b28c9c37dcf9cf01e0f558b845dd7123860d4c7c8fb89"}, |
|||
{file = "igraph-0.11.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bdeae8bf35316eb1fb27bf667dcf5ecf5fcfb0b8f51831bc1b00c39c09c2d73b"}, |
|||
{file = "igraph-0.11.6-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ad7e4aa442935de72554b96733bf6d7f09eac5cee97988a2562bdd3ca173cfa3"}, |
|||
{file = "igraph-0.11.6-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:8d2818780358a686178866d01568b9df1f29678581734ad7a78882bab54df004"}, |
|||
{file = "igraph-0.11.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2352276a20d979f1dea360af4202bb9f0c9a7d2c77f51815c0e625165e82013d"}, |
|||
{file = "igraph-0.11.6-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:687fdab543b507d622fa3043f4227e5b26dc61dcf8ff8c0919fccddcc655f8b8"}, |
|||
{file = "igraph-0.11.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57f7f8214cd48c9a4d97f7346a4152ba2d4ac95fb5ee0df4ecf224fce4ba3d14"}, |
|||
{file = "igraph-0.11.6-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:2b9cc69ede53f76ffae03b066609aa90184dd68ef15da8c104a97cebb9210838"}, |
|||
{file = "igraph-0.11.6-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:591e1e447c3f0092daf7613a3eaedab83f9a0b0adbaf7702724c5117ded038a5"}, |
|||
{file = "igraph-0.11.6-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ca558eb331bc687bc33e5cd23717e22676e9412f8cda3a31d30c996a0487610d"}, |
|||
{file = "igraph-0.11.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf43c30e08debb087c9e3da69aa5cf1b6732968da34d55a614e3421b9a452146"}, |
|||
{file = "igraph-0.11.6-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d38e8d7db72b187d9d2211d0d06b3271fa9f32b04d49d789e2859b5480db0d0"}, |
|||
{file = "igraph-0.11.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a318b059051ff78144a1c3cb880f4d933c812bcdb3d833a49cd7168d0427672"}, |
|||
{file = "igraph-0.11.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2c54027add809b3c5b6685b8deca4ea4763fd000b9ea45c7ee46b7c9d61ff15e"}, |
|||
{file = "igraph-0.11.6.tar.gz", hash = "sha256:837f233256c3319f2a35a6a80d94eafe47b43791ef4c6f9e9871061341ac8e28"}, |
|||
] |
|||
|
|||
[package.dependencies] |
|||
texttable = ">=1.6.2" |
|||
|
|||
[package.extras] |
|||
cairo = ["cairocffi (>=1.2.0)"] |
|||
doc = ["Sphinx (>=7.0.0)", "pydoctor (>=23.4.0)", "sphinx-gallery (>=0.14.0)", "sphinx-rtd-theme (>=1.3.0)"] |
|||
matplotlib = ["matplotlib (>=3.6.0)"] |
|||
plotly = ["plotly (>=5.3.0)"] |
|||
plotting = ["cairocffi (>=1.2.0)"] |
|||
test = ["Pillow (>=9)", "cairocffi (>=1.2.0)", "matplotlib (>=3.6.0)", "networkx (>=2.5)", "numpy (>=1.19.0)", "pandas (>=1.1.0)", "plotly (>=5.3.0)", "pytest (>=7.0.1)", "pytest-timeout (>=2.1.0)", "scipy (>=1.5.0)"] |
|||
test-musl = ["cairocffi (>=1.2.0)", "networkx (>=2.5)", "pytest (>=7.0.1)", "pytest-timeout (>=2.1.0)"] |
|||
|
|||
[[package]] |
|||
name = "iniconfig" |
|||
version = "2.0.0" |
|||
description = "brain-dead simple config-ini parsing" |
|||
optional = false |
|||
python-versions = ">=3.7" |
|||
files = [ |
|||
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, |
|||
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, |
|||
] |
|||
|
|||
[[package]] |
|||
name = "mypy" |
|||
version = "0.910" |
|||
description = "Optional static typing for Python" |
|||
optional = false |
|||
python-versions = ">=3.5" |
|||
files = [ |
|||
{file = "mypy-0.910-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457"}, |
|||
{file = "mypy-0.910-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb"}, |
|||
{file = "mypy-0.910-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9"}, |
|||
{file = "mypy-0.910-cp35-cp35m-win_amd64.whl", hash = "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e"}, |
|||
{file = "mypy-0.910-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921"}, |
|||
{file = "mypy-0.910-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6"}, |
|||
{file = "mypy-0.910-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212"}, |
|||
{file = "mypy-0.910-cp36-cp36m-win_amd64.whl", hash = "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885"}, |
|||
{file = "mypy-0.910-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0"}, |
|||
{file = "mypy-0.910-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de"}, |
|||
{file = "mypy-0.910-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703"}, |
|||
{file = "mypy-0.910-cp37-cp37m-win_amd64.whl", hash = "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a"}, |
|||
{file = "mypy-0.910-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504"}, |
|||
{file = "mypy-0.910-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9"}, |
|||
{file = "mypy-0.910-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072"}, |
|||
{file = "mypy-0.910-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811"}, |
|||
{file = "mypy-0.910-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e"}, |
|||
{file = "mypy-0.910-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b"}, |
|||
{file = "mypy-0.910-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2"}, |
|||
{file = "mypy-0.910-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97"}, |
|||
{file = "mypy-0.910-cp39-cp39-win_amd64.whl", hash = "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8"}, |
|||
{file = "mypy-0.910-py3-none-any.whl", hash = "sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d"}, |
|||
{file = "mypy-0.910.tar.gz", hash = "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150"}, |
|||
] |
|||
|
|||
[package.dependencies] |
|||
mypy-extensions = ">=0.4.3,<0.5.0" |
|||
toml = "*" |
|||
typing-extensions = ">=3.7.4" |
|||
|
|||
[package.extras] |
|||
dmypy = ["psutil (>=4.0)"] |
|||
python2 = ["typed-ast (>=1.4.0,<1.5.0)"] |
|||
|
|||
[[package]] |
|||
name = "mypy-extensions" |
|||
version = "0.4.4" |
|||
description = "Experimental type system extensions for programs checked with the mypy typechecker." |
|||
optional = false |
|||
python-versions = ">=2.7" |
|||
files = [ |
|||
{file = "mypy_extensions-0.4.4.tar.gz", hash = "sha256:c8b707883a96efe9b4bb3aaf0dcc07e7e217d7d8368eec4db4049ee9e142f4fd"}, |
|||
] |
|||
|
|||
[[package]] |
|||
name = "packaging" |
|||
version = "24.1" |
|||
description = "Core utilities for Python packages" |
|||
optional = false |
|||
python-versions = ">=3.8" |
|||
files = [ |
|||
{file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, |
|||
{file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, |
|||
] |
|||
|
|||
[[package]] |
|||
name = "plotly" |
|||
version = "5.23.0" |
|||
description = "An open-source, interactive data visualization library for Python" |
|||
optional = false |
|||
python-versions = ">=3.8" |
|||
files = [ |
|||
{file = "plotly-5.23.0-py3-none-any.whl", hash = "sha256:76cbe78f75eddc10c56f5a4ee3e7ccaade7c0a57465546f02098c0caed6c2d1a"}, |
|||
{file = "plotly-5.23.0.tar.gz", hash = "sha256:89e57d003a116303a34de6700862391367dd564222ab71f8531df70279fc0193"}, |
|||
] |
|||
|
|||
[package.dependencies] |
|||
packaging = "*" |
|||
tenacity = ">=6.2.0" |
|||
|
|||
[[package]] |
|||
name = "pluggy" |
|||
version = "1.5.0" |
|||
description = "plugin and hook calling mechanisms for python" |
|||
optional = false |
|||
python-versions = ">=3.8" |
|||
files = [ |
|||
{file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, |
|||
{file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, |
|||
] |
|||
|
|||
[package.extras] |
|||
dev = ["pre-commit", "tox"] |
|||
testing = ["pytest", "pytest-benchmark"] |
|||
|
|||
[[package]] |
|||
name = "py" |
|||
version = "1.11.0" |
|||
description = "library with cross-python path, ini-parsing, io, code, log facilities" |
|||
optional = false |
|||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" |
|||
files = [ |
|||
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, |
|||
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, |
|||
] |
|||
|
|||
[[package]] |
|||
name = "pydevicetree" |
|||
version = "0.0.13" |
|||
description = "A library for parsing Devicetree Source v1" |
|||
optional = false |
|||
python-versions = ">=3.5" |
|||
files = [ |
|||
{file = "pydevicetree-0.0.13-py3-none-any.whl", hash = "sha256:d61c695cec925b90a8b5740053f4b604e51154a9b36e62a2f12ed9ceaf2f8c38"}, |
|||
{file = "pydevicetree-0.0.13.tar.gz", hash = "sha256:5700c05df89bad8fd729c11aa6f764a3323bcb3796f13b32481ae34445cfc1b7"}, |
|||
] |
|||
|
|||
[package.dependencies] |
|||
pyparsing = "*" |
|||
|
|||
[[package]] |
|||
name = "pyparsing" |
|||
version = "3.1.2" |
|||
description = "pyparsing module - Classes and methods to define and execute parsing grammars" |
|||
optional = false |
|||
python-versions = ">=3.6.8" |
|||
files = [ |
|||
{file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, |
|||
{file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, |
|||
] |
|||
|
|||
[package.extras] |
|||
diagrams = ["jinja2", "railroad-diagrams"] |
|||
|
|||
[[package]] |
|||
name = "pytest" |
|||
version = "6.2.5" |
|||
description = "pytest: simple powerful testing with Python" |
|||
optional = false |
|||
python-versions = ">=3.6" |
|||
files = [ |
|||
{file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, |
|||
{file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, |
|||
] |
|||
|
|||
[package.dependencies] |
|||
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} |
|||
attrs = ">=19.2.0" |
|||
colorama = {version = "*", markers = "sys_platform == \"win32\""} |
|||
iniconfig = "*" |
|||
packaging = "*" |
|||
pluggy = ">=0.12,<2.0" |
|||
py = ">=1.8.2" |
|||
toml = "*" |
|||
|
|||
[package.extras] |
|||
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] |
|||
|
|||
[[package]] |
|||
name = "tenacity" |
|||
version = "9.0.0" |
|||
description = "Retry code until it succeeds" |
|||
optional = false |
|||
python-versions = ">=3.8" |
|||
files = [ |
|||
{file = "tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539"}, |
|||
{file = "tenacity-9.0.0.tar.gz", hash = "sha256:807f37ca97d62aa361264d497b0e31e92b8027044942bfa756160d908320d73b"}, |
|||
] |
|||
|
|||
[package.extras] |
|||
doc = ["reno", "sphinx"] |
|||
test = ["pytest", "tornado (>=4.5)", "typeguard"] |
|||
|
|||
[[package]] |
|||
name = "texttable" |
|||
version = "1.7.0" |
|||
description = "module to create simple ASCII tables" |
|||
optional = false |
|||
python-versions = "*" |
|||
files = [ |
|||
{file = "texttable-1.7.0-py2.py3-none-any.whl", hash = "sha256:72227d592c82b3d7f672731ae73e4d1f88cd8e2ef5b075a7a7f01a23a3743917"}, |
|||
{file = "texttable-1.7.0.tar.gz", hash = "sha256:2d2068fb55115807d3ac77a4ca68fa48803e84ebb0ee2340f858107a36522638"}, |
|||
] |
|||
|
|||
[[package]] |
|||
name = "toml" |
|||
version = "0.10.2" |
|||
description = "Python Library for Tom's Obvious, Minimal Language" |
|||
optional = false |
|||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" |
|||
files = [ |
|||
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, |
|||
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, |
|||
] |
|||
|
|||
[[package]] |
|||
name = "typing-extensions" |
|||
version = "4.12.2" |
|||
description = "Backported and Experimental Type Hints for Python 3.8+" |
|||
optional = false |
|||
python-versions = ">=3.8" |
|||
files = [ |
|||
{file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, |
|||
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, |
|||
] |
|||
|
|||
[metadata] |
|||
lock-version = "2.0" |
|||
python-versions = "^3.8" |
|||
content-hash = "afa5cb49be96467a848bab753a630c6f5ec42d6750d67d29920c3e3971774e36" |
@ -1,6 +0,0 @@ |
|||
mypy |
|||
pylint |
|||
pyparsing |
|||
igraph |
|||
pandas |
|||
plotly |
Loading…
Reference in new issue