Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions src/vss_tools/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,14 +223,9 @@ def get_trees(
log.critical(e)
exit(1)

unique_include_dirs = []
for include_dir in include_dirs:
if include_dir not in unique_include_dirs:
unique_include_dirs.append(include_dir)

try:
types_root = get_types_root(types, unique_include_dirs)
vspec_data = load_vspec(unique_include_dirs, [vspec] + list(overlays))
types_root = get_types_root(types, list(include_dirs))
vspec_data = load_vspec(list(include_dirs), [vspec] + list(overlays))
except (InvalidSpecDuplicatedEntryException, InvalidSpecException) as e:
log.critical(e)
exit(1)
Expand Down
51 changes: 43 additions & 8 deletions src/vss_tools/vspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import yaml

from vss_tools import log
from vss_tools.model import NodeType


class IncludeStatementException(Exception):
Expand Down Expand Up @@ -43,7 +44,7 @@ def __init__(self, statement: str, prefix: str | None = None):
split = statement.split()
if len(split) < 2:
raise IncludeStatementException(f"Malformed include statement: {statement}")
self.target = split[1]
self.target = Path(split[1])
self.prefix = prefix
if len(split) == 3:
if self.prefix is not None:
Expand All @@ -52,12 +53,23 @@ def __init__(self, statement: str, prefix: str | None = None):
self.prefix = split[2]

def resolve_path(self, include_dirs: list[Path]) -> Path:
for dir in include_dirs:
path = dir / self.target
if path.exists():
log.debug(f"'{self.statement}', resolved={path}")
return path
raise IncludeNotFoundException(f"Unable to find include {self.target}. Include dirs: {include_dirs}")
unique_include_dirs = list(dict.fromkeys(include_dirs))
candidates = [self.target]

if self.target.suffix == "":
candidates = [
self.target.with_suffix(".vspec"),
self.target.with_suffix(".yaml"),
self.target.with_suffix(".yml"),
]

for dir in unique_include_dirs:
for candidate in candidates:
path = dir / candidate
if path.exists():
log.debug(f"'{self.statement}', resolved={path.absolute()}")
return path
raise IncludeNotFoundException(f"Unable to find include {self.target}. Include dirs: {unique_include_dirs}")


def deep_update(base: dict[str, Any], update: dict[str, Any]) -> None:
Expand Down Expand Up @@ -86,10 +98,33 @@ def __init__(
if self.data is None:
self.data = {}

self.includes = []

for key, value in self.data.items():
if not isinstance(value, dict):
raise InvalidSpecException(f"{self.source.absolute()}, Invalid key value: {key}={value}")

# only branches can include things
if value.get("type") != NodeType.BRANCH.value:
continue

includes = value.get("includes", None)
if includes is None:
continue

if not isinstance(includes, list):
raise InvalidSpecException(f"{self.source.absolute()}, Invalid 'includes' definition (not a list)")

for include in includes:
if not isinstance(include, str):
raise InvalidSpecException(
f"{self.source.absolute()}, Invalid 'include' definition (not a str): {include}"
)

self.includes.append(Include(f"#include {include} {key}", prefix))

del value["includes"]

if prefix:
tmp_data = {}
for k, v in self.data.items():
Expand All @@ -99,7 +134,7 @@ def __init__(

lines = content.splitlines()
include_statements = [line.strip() for line in lines if line.strip().startswith("#include")]
self.includes = [Include(statement, prefix) for statement in include_statements]
self.includes.extend([Include(statement, prefix) for statement in include_statements])

def __str__(self) -> str:
return f"{self.__class__.__name__}, src={self.source}, prefix={self.prefix}, includes={len(self.includes)}"
Expand Down
13 changes: 13 additions & 0 deletions tests/vspec/test_include/A.vspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
A:
type: branch
description: d
includes:
- B

#include C A

A.X:
type: branch
description: d

#include D.vspec A.X
5 changes: 5 additions & 0 deletions tests/vspec/test_include/B.vspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
B:
type: branch
description: d
includes:
- B2.vspec
3 changes: 3 additions & 0 deletions tests/vspec/test_include/B2.vspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
B2:
type: branch
description: d
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
# Should be put under A.B branch
S3:
datatype: float
type: sensor
unit: km
description: A sensor.
C:
type: branch
description: d

# Intentionally no newline,
# see previous bug in https://github.com/COVESA/vehicle_signal_specification/issues/145
# No prefix specified so contents shall be put in same branch as S3
#include include_c.vspec
#include C2.vspec
3 changes: 3 additions & 0 deletions tests/vspec/test_include/C2.vspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
C2:
type: branch
description: d
3 changes: 3 additions & 0 deletions tests/vspec/test_include/D.vspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
D:
type: branch
description: d
38 changes: 0 additions & 38 deletions tests/vspec/test_include/expected.json

This file was deleted.

24 changes: 0 additions & 24 deletions tests/vspec/test_include/expected.plantuml

This file was deleted.

27 changes: 27 additions & 0 deletions tests/vspec/test_include/expected.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
A:
type: branch
description: d

A.X:
type: branch
description: d

A.X.D:
type: branch
description: d

A.B:
type: branch
description: d

A.B.B2:
type: branch
description: d

A.C:
type: branch
description: d

A.C2:
type: branch
description: d
6 changes: 0 additions & 6 deletions tests/vspec/test_include/include.vspec

This file was deleted.

6 changes: 0 additions & 6 deletions tests/vspec/test_include/include_c.vspec

This file was deleted.

19 changes: 0 additions & 19 deletions tests/vspec/test_include/test.vspec

This file was deleted.

6 changes: 3 additions & 3 deletions tests/vspec/test_include/test_error.vspec
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ A:

# Not specifying prefix, then we will have a dual root problem as contents from file
# are added on root level
#include include.vspec
#include B.vspec

# Make sure that we can have additional signals after
A.S1:
A.X:
datatype: float
type: sensor
unit: km
Expand All @@ -20,4 +20,4 @@ A.B:

# As "A.BBBBBB" does not exist we shall get a warning
# It will be treated as an implicit branch, which likely is not intentional
#include include_b.vspec A.BBBBBB
#include B.vspec A.BBBBBB
16 changes: 7 additions & 9 deletions tests/vspec/test_include/test_include.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,21 @@
from pathlib import Path

HERE = Path(__file__).resolve().parent
TEST_UNITS = HERE / ".." / "test_units.yaml"
TEST_QUANT = HERE / ".." / "test_quantities.yaml"


def test_include(tmp_path):
spec = HERE / "test.vspec"
output = tmp_path / "out.json"
expected = HERE / "expected.json"
cmd = f"vspec export json -u {TEST_UNITS} -q {TEST_QUANT} --pretty --vspec {spec} --output {output}"
spec = HERE / "A.vspec"
output = tmp_path / "out.yaml"
expected = HERE / "expected.yaml"
cmd = f"vspec export yaml --vspec {spec} --output {output}"
subprocess.run(cmd.split(), check=True)
filecmp.cmp(output, expected)
assert filecmp.cmp(output, expected)


def test_error(tmp_path):
spec = HERE / "test_error.vspec"
output = tmp_path / "out.json"
cmd = f"vspec export json -u {TEST_UNITS} -q {TEST_QUANT} --pretty --vspec {spec} --output {output}"
output = tmp_path / "out.yaml"
cmd = f"vspec export yaml --vspec {spec} --output {output}"
process = subprocess.run(cmd.split(), capture_output=True, text=True)
assert process.returncode != 0

Expand Down
Loading