mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-07 21:33:54 +00:00
feat(tlc): add command gen-header
Introduce the gen-header command to the tool, enabling developers to create language bindings. Currently, it supports generating C headers from a transfer list. Change-Id: Ibec75639c38577802d5abe55c7bc718740aad2b8 Signed-off-by: Harrison Mutai <harrison.mutai@arm.com>
This commit is contained in:
parent
38487c7fd3
commit
9b05c3739c
7 changed files with 164 additions and 45 deletions
67
poetry.lock
generated
67
poetry.lock
generated
|
@ -44,13 +44,13 @@ dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"]
|
|||
|
||||
[[package]]
|
||||
name = "build"
|
||||
version = "1.2.1"
|
||||
version = "1.2.2"
|
||||
description = "A simple, correct Python build frontend"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "build-1.2.1-py3-none-any.whl", hash = "sha256:75e10f767a433d9a86e50d83f418e83efc18ede923ee5ff7df93b6cb0306c5d4"},
|
||||
{file = "build-1.2.1.tar.gz", hash = "sha256:526263f4870c26f26c433545579475377b2b7588b6f1eac76a001e873ae3e19d"},
|
||||
{file = "build-1.2.2-py3-none-any.whl", hash = "sha256:277ccc71619d98afdd841a0e96ac9fe1593b823af481d3b0cea748e8894e0613"},
|
||||
{file = "build-1.2.2.tar.gz", hash = "sha256:119b2fb462adef986483438377a13b2f42064a2a3a4161f24a0cca698a07ac8c"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -80,13 +80,13 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2024.7.4"
|
||||
version = "2024.8.30"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"},
|
||||
{file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"},
|
||||
{file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"},
|
||||
{file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -282,19 +282,19 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "filelock"
|
||||
version = "3.15.4"
|
||||
version = "3.16.0"
|
||||
description = "A platform independent file lock."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"},
|
||||
{file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"},
|
||||
{file = "filelock-3.16.0-py3-none-any.whl", hash = "sha256:f6ed4c963184f4c84dd5557ce8fece759a3724b37b80c6c4f20a2f63a4dc6609"},
|
||||
{file = "filelock-3.16.0.tar.gz", hash = "sha256:81de9eb8453c769b63369f87f11131a7ab04e367f8d97ad39dc230daa07e3bec"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
|
||||
testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"]
|
||||
typing = ["typing-extensions (>=4.8)"]
|
||||
docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"]
|
||||
testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.1.1)", "pytest (>=8.3.2)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.3)"]
|
||||
typing = ["typing-extensions (>=4.12.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
|
@ -610,29 +610,29 @@ testing = ["flit-core (>=2,<4)", "poetry-core (>=1.0.0)", "pytest (>=7.2.0)", "p
|
|||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "4.2.2"
|
||||
version = "4.3.2"
|
||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"},
|
||||
{file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"},
|
||||
{file = "platformdirs-4.3.2-py3-none-any.whl", hash = "sha256:eb1c8582560b34ed4ba105009a4badf7f6f85768b30126f351328507b2beb617"},
|
||||
{file = "platformdirs-4.3.2.tar.gz", hash = "sha256:9e5e27a08aa095dd127b9f2e764d74254f482fef22b0970773bfba79d091ab8c"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"]
|
||||
type = ["mypy (>=1.8)"]
|
||||
docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"]
|
||||
type = ["mypy (>=1.11.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "plotly"
|
||||
version = "5.23.0"
|
||||
version = "5.24.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"},
|
||||
{file = "plotly-5.24.0-py3-none-any.whl", hash = "sha256:0e54efe52c8cef899f7daa41be9ed97dfb6be622613a2a8f56a86a0634b2b67e"},
|
||||
{file = "plotly-5.24.0.tar.gz", hash = "sha256:eae9f4f54448682442c92c1e97148e3ad0c52f0cf86306e1b76daba24add554a"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -869,13 +869,13 @@ jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"]
|
|||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "74.0.0"
|
||||
version = "74.1.2"
|
||||
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "setuptools-74.0.0-py3-none-any.whl", hash = "sha256:0274581a0037b638b9fc1c6883cc71c0210865aaa76073f7882376b641b84e8f"},
|
||||
{file = "setuptools-74.0.0.tar.gz", hash = "sha256:a85e96b8be2b906f3e3e789adec6a9323abf79758ecfa3065bd740d81158b11e"},
|
||||
{file = "setuptools-74.1.2-py3-none-any.whl", hash = "sha256:5f4c08aa4d3ebcb57a50c33b1b07e94315d7fc7230f7115e47fc99776c8ce308"},
|
||||
{file = "setuptools-74.1.2.tar.gz", hash = "sha256:95b40ed940a1c67eb70fc099094bd6e99c6ee7c23aa2306f4d2697ba7916f9c6"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
|
@ -1147,6 +1147,7 @@ develop = true
|
|||
|
||||
[package.dependencies]
|
||||
click = "^8.1.7"
|
||||
jinja2 = "^3.1.4"
|
||||
pyyaml = "^6.0.1"
|
||||
rich = "^10.14.0"
|
||||
tox = "^4.18.0"
|
||||
|
@ -1169,17 +1170,17 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "tox"
|
||||
version = "4.18.0"
|
||||
version = "4.18.1"
|
||||
description = "tox is a generic virtualenv management and test command line tool"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "tox-4.18.0-py3-none-any.whl", hash = "sha256:0a457400cf70615dc0627eb70d293e80cd95d8ce174bb40ac011011f0c03a249"},
|
||||
{file = "tox-4.18.0.tar.gz", hash = "sha256:5dfa1cab9f146becd6e351333a82f9e0ade374451630ba65ee54584624c27b58"},
|
||||
{file = "tox-4.18.1-py3-none-any.whl", hash = "sha256:35d472032ee1f73fe20c3e0e73d7073a4e85075c86ff02c576f9fc7c6a15a578"},
|
||||
{file = "tox-4.18.1.tar.gz", hash = "sha256:3c0c96bc3a568a5c7e66387a4cfcf8c875b52e09f4d47c9f7a277ec82f1a0b11"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
cachetools = ">=5.4"
|
||||
cachetools = ">=5.5"
|
||||
chardet = ">=5.2"
|
||||
colorama = ">=0.4.6"
|
||||
filelock = ">=3.15.4"
|
||||
|
@ -1191,8 +1192,8 @@ tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""}
|
|||
virtualenv = ">=20.26.3"
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2024.7.18)", "sphinx (>=7.4.7)", "sphinx-argparse-cli (>=1.16)", "sphinx-autodoc-typehints (>=2.2.3)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.11)"]
|
||||
testing = ["build[virtualenv] (>=1.2.1)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.2)", "devpi-process (>=1)", "diff-cover (>=9.1.1)", "distlib (>=0.3.8)", "flaky (>=3.8.1)", "hatch-vcs (>=0.4)", "hatchling (>=1.25)", "psutil (>=6)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-xdist (>=3.6.1)", "re-assert (>=1.1)", "setuptools (>=70.3)", "time-machine (>=2.14.2)", "wheel (>=0.43)"]
|
||||
docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-argparse-cli (>=1.17)", "sphinx-autodoc-typehints (>=2.4)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=24.8)"]
|
||||
testing = ["build[virtualenv] (>=1.2.2)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.2)", "devpi-process (>=1)", "diff-cover (>=9.1.1)", "distlib (>=0.3.8)", "flaky (>=3.8.1)", "hatch-vcs (>=0.4)", "hatchling (>=1.25)", "psutil (>=6)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-xdist (>=3.6.1)", "re-assert (>=1.1)", "setuptools (>=74.1.2)", "time-machine (>=2.15)", "wheel (>=0.44)"]
|
||||
|
||||
[[package]]
|
||||
name = "typer"
|
||||
|
@ -1246,13 +1247,13 @@ zstd = ["zstandard (>=0.18.0)"]
|
|||
|
||||
[[package]]
|
||||
name = "virtualenv"
|
||||
version = "20.26.3"
|
||||
version = "20.26.4"
|
||||
description = "Virtual Python Environment builder"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"},
|
||||
{file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"},
|
||||
{file = "virtualenv-20.26.4-py3-none-any.whl", hash = "sha256:48f2695d9809277003f30776d155615ffc11328e6a0a8c1f0ec80188d7874a55"},
|
||||
{file = "virtualenv-20.26.4.tar.gz", hash = "sha256:c17f4e0f3e6036e9f26700446f85c76ab11df65ff6d8a9cbfad9f71aabfcf23c"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
2
tools/tlc/poetry.lock
generated
2
tools/tlc/poetry.lock
generated
|
@ -1431,4 +1431,4 @@ files = [
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.8"
|
||||
content-hash = "9ad739a282f180a9c47cf8cb8f1f53a167e1739b1c5bf572932384b45749df26"
|
||||
content-hash = "aac9123f3fa544b8c3e9b085f41f5a1c6c4ed2d59ce3236dcda6ea2aef5a694c"
|
||||
|
|
|
@ -38,6 +38,7 @@ typer = {extras = ["all"], version = "^0.4.0"}
|
|||
rich = "^10.14.0"
|
||||
click = "^8.1.7"
|
||||
pyyaml = "^6.0.1"
|
||||
tox = "^4.18.0"
|
||||
jinja2 = "^3.1.4"
|
||||
|
||||
[tool.poetry.group.dev]
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
from math import ceil, log2
|
||||
from pathlib import Path
|
||||
from re import findall, search
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
@ -383,6 +384,69 @@ def test_create_from_yaml_check_exact_data(
|
|||
assert actual == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("option", ["-O", "--output"])
|
||||
def test_gen_tl_header_with_output_name(tlcrunner, tmptlstr, option, filename="test.h"):
|
||||
with tlcrunner.isolated_filesystem():
|
||||
result = tlcrunner.invoke(
|
||||
cli,
|
||||
[
|
||||
"gen-header",
|
||||
option,
|
||||
filename,
|
||||
tmptlstr,
|
||||
],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert Path(filename).exists()
|
||||
|
||||
|
||||
def test_gen_tl_with_fdt_header(tmptlstr, tmpfdt):
|
||||
tlcrunner = CliRunner()
|
||||
|
||||
with tlcrunner.isolated_filesystem():
|
||||
tlcrunner.invoke(cli, ["create", "--size", 1000, "--fdt", tmpfdt, tmptlstr])
|
||||
|
||||
result = tlcrunner.invoke(
|
||||
cli,
|
||||
[
|
||||
"gen-header",
|
||||
tmptlstr,
|
||||
],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert Path("header.h").exists()
|
||||
|
||||
with open("header.h", "r") as f:
|
||||
dtb_match = search(r"DTB_OFFSET\s+(\d+)", "".join(f.readlines()))
|
||||
assert dtb_match and dtb_match[1].isnumeric()
|
||||
|
||||
|
||||
def test_gen_empty_tl_c_header(tlcrunner, tmptlstr):
|
||||
with tlcrunner.isolated_filesystem():
|
||||
result = tlcrunner.invoke(
|
||||
cli,
|
||||
[
|
||||
"gen-header",
|
||||
tmptlstr,
|
||||
],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert Path("header.h").exists()
|
||||
|
||||
with open("header.h", "r") as f:
|
||||
lines = "".join(f.readlines())
|
||||
|
||||
assert TransferList.hdr_size == int(
|
||||
findall(r"SIZE\s+(0x[0-9a-fA-F]+|\d+)", lines)[0], 16
|
||||
)
|
||||
assert TransferList.version == int(
|
||||
findall(r"VERSION.+(0x[0-9a-fA-F]+|\d+)", lines)[0]
|
||||
)
|
||||
|
||||
|
||||
def bytes_to_hex(data: bytes) -> str:
|
||||
"""Convert bytes to a hex string in the same format as the debugger in
|
||||
ArmDS
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
from pathlib import Path
|
||||
|
||||
import click
|
||||
import jinja2
|
||||
import yaml
|
||||
|
||||
from tlc.tl import *
|
||||
|
@ -164,6 +165,34 @@ def unpack(filename, c):
|
|||
f.write(te.data)
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.argument("filename", type=click.Path(exists=True, dir_okay=False))
|
||||
@click.option(
|
||||
"--output",
|
||||
"-O",
|
||||
type=click.Path(exists=False),
|
||||
help="Output filename for the header",
|
||||
default=Path("header.h"),
|
||||
)
|
||||
def gen_header(filename, output):
|
||||
"""Generate a header with common definitions."""
|
||||
tl = TransferList.fromfile(filename)
|
||||
tmp_keys = tl.__dict__
|
||||
tmp_keys["header_guard"] = Path(output).name.replace(".", "_").upper()
|
||||
|
||||
dtb_te = tl.get_entry(1)
|
||||
|
||||
if dtb_te:
|
||||
tmp_keys["dtb_offset"] = dtb_te.offset + dtb_te.hdr_size
|
||||
|
||||
env = jinja2.Environment(
|
||||
loader=jinja2.PackageLoader("tlc", "templates"),
|
||||
)
|
||||
template = env.get_template("header.h.j2")
|
||||
with open(output, "w") as f:
|
||||
f.write(template.render(tmp_keys))
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.argument("filename", type=click.Path(exists=True, dir_okay=False))
|
||||
def validate(filename):
|
||||
|
|
16
tools/tlc/tlc/templates/header.h.j2
Normal file
16
tools/tlc/tlc/templates/header.h.j2
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Auto-generated by TLC, this file includes declarations and macros
|
||||
* derived from a Transfer List input.
|
||||
*/
|
||||
|
||||
#ifndef {{ header_guard }}
|
||||
#define {{ header_guard }}
|
||||
|
||||
{% if dtb_offset -%}
|
||||
#define TRANSFER_LIST_DTB_OFFSET {{ "0x%x" % dtb_offset }}
|
||||
{%- endif %}
|
||||
#define TRANSFER_LIST_CONVENTION_VERSION {{ version }}
|
||||
#define TRANSFER_LIST_HEADER_SIZE {{ "0x%x" % hdr_size }}
|
||||
#define TRANSFER_LIST_SIZE {{ "0x%x" % size }}
|
||||
|
||||
#endif /* {{ header_guard }} */
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
"""Module containing definitions pertaining to the 'Transfer List' (TL) type."""
|
||||
|
||||
from typing import Any, Dict, List
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
import math
|
||||
import struct
|
||||
|
@ -93,7 +93,7 @@ class TransferList:
|
|||
self.size = self.hdr_size
|
||||
self.total_size = max_size
|
||||
self.flags = flags
|
||||
self.entries: List["TransferEntry"] = []
|
||||
self.entries: List[TransferEntry] = []
|
||||
self.update_checksum()
|
||||
|
||||
def __str__(self) -> str:
|
||||
|
@ -197,15 +197,23 @@ class TransferList:
|
|||
sum(self.header_to_bytes()) + sum(te.sum_of_bytes for te in self.entries)
|
||||
) % 256
|
||||
|
||||
def get_entry_data_offset(self, tag_id: int) -> int:
|
||||
"""Returns offset of data of a TE from the base of the TL."""
|
||||
def get_entry(self, tag_id: int) -> Optional[TransferEntry]:
|
||||
for te in self.entries:
|
||||
if te.id == tag_id:
|
||||
return te.offset + te.hdr_size
|
||||
return te
|
||||
|
||||
raise ValueError(f"Tag {tag_id} not found in TL!")
|
||||
return None
|
||||
|
||||
def add_transfer_entry(self, tag_id: int, data: bytes) -> "TransferEntry":
|
||||
def get_entry_data_offset(self, tag_id: int) -> int:
|
||||
"""Returns offset of data of a TE from the base of the TL."""
|
||||
te = self.get_entry(tag_id)
|
||||
|
||||
if not te:
|
||||
raise ValueError(f"Tag {tag_id} not found in TL!")
|
||||
|
||||
return te.offset + te.hdr_size
|
||||
|
||||
def add_transfer_entry(self, tag_id: int, data: bytes) -> TransferEntry:
|
||||
"""Appends a TransferEntry into the internal list of TE's."""
|
||||
if not (self.total_size >= self.size + TransferEntry.hdr_size + len(data)):
|
||||
raise MemoryError(
|
||||
|
@ -220,14 +228,14 @@ class TransferList:
|
|||
|
||||
def add_transfer_entry_from_struct_format(
|
||||
self, tag_id: int, struct_format: str, *args: Any
|
||||
) -> "TransferEntry":
|
||||
) -> TransferEntry:
|
||||
struct_format = "<" + struct_format
|
||||
data = struct.pack(struct_format, *args)
|
||||
return self.add_transfer_entry(tag_id, data)
|
||||
|
||||
def add_entry_point_info_transfer_entry(
|
||||
self, entry: Dict[str, Any]
|
||||
) -> "TransferEntry":
|
||||
) -> TransferEntry:
|
||||
"""Add entry_point_info transfer entry
|
||||
|
||||
:param entry: Dictionary of the transfer entry, in the same format as
|
||||
|
@ -285,7 +293,7 @@ class TransferList:
|
|||
def add_transfer_entry_from_dict(
|
||||
self,
|
||||
entry: Dict[str, Any],
|
||||
) -> "TransferEntry":
|
||||
) -> TransferEntry:
|
||||
"""Add a transfer entry from data in a dictionary
|
||||
|
||||
The dictionary should have the same format as the entries in the yaml
|
||||
|
@ -320,7 +328,7 @@ class TransferList:
|
|||
else:
|
||||
raise ValueError(f"Invalid transfer entry {entry}.")
|
||||
|
||||
def add_transfer_entry_from_file(self, tag_id: int, path: Path) -> "TransferEntry":
|
||||
def add_transfer_entry_from_file(self, tag_id: int, path: Path) -> TransferEntry:
|
||||
with open(path, "rb") as f:
|
||||
return self.add_transfer_entry(tag_id, f.read())
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue