Skip to content
Merged
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
17 changes: 9 additions & 8 deletions src/gardenlinux/features/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
import argparse
import logging
import os
import re
import sys
from functools import reduce
from os import path
from typing import Any, List, Set
Expand Down Expand Up @@ -100,9 +98,9 @@ def main() -> None:
commit_id = cname.commit_id
version = cname.version

input_features = Parser.get_cname_as_feature_set(flavor)
_ = Parser.get_cname_as_feature_set(flavor)
else:
input_features = args.features
_ = args.features

if arch is None or arch == "" and (args.type in ("cname", "arch")):
raise RuntimeError(
Expand Down Expand Up @@ -140,10 +138,10 @@ def main() -> None:
cname = flavor

if arch is not None:
cname += f"-{arch}"
cname += f"-{arch}" # type: ignore - None check is carried out.

if commit_id is not None:
cname += f"-{version}-{commit_id}"
cname += f"-{version}-{commit_id}" # type: ignore - None check is carried out.

print(cname)
elif args.type == "graph":
Expand Down Expand Up @@ -173,7 +171,7 @@ def main() -> None:
print(f"{version}-{commit_id}")


def get_cname_base(sorted_features: Set[str]):
def get_cname_base(sorted_features: List[str]):
"""
Get the base cname for the feature set given.

Expand Down Expand Up @@ -228,7 +226,7 @@ def get_minimal_feature_set(graph: Any) -> Set[str]:
return set([node for (node, degree) in graph.in_degree() if degree == 0])


def graph_as_mermaid_markup(flavor: str, graph: Any) -> str:
def graph_as_mermaid_markup(flavor: str | None, graph: Any) -> str:
"""
Generates a mermaid.js representation of the graph.
This is helpful to identify dependencies between features.
Expand All @@ -243,6 +241,9 @@ def graph_as_mermaid_markup(flavor: str, graph: Any) -> str:
:since: 0.7.0
"""

if flavor is None:
raise RuntimeError("Error while generating graph: Flavor is None!")

markup = f"---\ntitle: Dependency Graph for Feature {flavor}\n---\ngraph TD;\n"

for u, v in graph.edges:
Expand Down
7 changes: 4 additions & 3 deletions src/gardenlinux/features/cname.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,13 @@ def cname(self) -> str:

:return: (str) CName
"""

assert self._flavor is not None, "CName flavor is not set!"
cname = self._flavor

if self._arch is not None:
cname += f"-{self._arch}"

if self._commit_id is not None:
if self._commit_id is not None and self._version is not None:
cname += f"-{self.version_and_commit_id}"

return cname
Expand All @@ -114,7 +114,7 @@ def commit_id(self) -> Optional[str]:
return self._commit_id

@property
def flavor(self) -> str:
def flavor(self) -> str | None:
"""
Returns the flavor for the cname parsed.

Expand All @@ -140,6 +140,7 @@ def platform(self) -> str:

:return: (str) Flavor
"""
assert self._flavor is not None, "Flavor not set!"

return re.split("[_-]", self._flavor, maxsplit=1)[0]

Expand Down
4 changes: 1 addition & 3 deletions src/gardenlinux/features/cname_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import argparse
import logging
import re
from functools import reduce
from os.path import basename, dirname

from .__main__ import (
Expand Down Expand Up @@ -81,8 +80,7 @@ def main():

generated_cname = get_cname_base(sorted_minimal_features)

if cname.arch is not None:
generated_cname += f"-{cname.arch}"
generated_cname += f"-{cname.arch}"

if cname.version_and_commit_id is not None:
generated_cname += f"-{cname.version_and_commit_id}"
Expand Down
44 changes: 20 additions & 24 deletions src/gardenlinux/features/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,14 @@

import logging
import os
import re
import subprocess
from glob import glob
from typing import Callable, Optional
from pathlib import Path
from typing import Callable, Optional, cast

import networkx
import yaml

from ..constants import (
ARCHS,
BARE_FLAVOR_FEATURE_CONTENT,
BARE_FLAVOR_LIBC_FEATURE_CONTENT,
)
from ..constants import BARE_FLAVOR_FEATURE_CONTENT, BARE_FLAVOR_LIBC_FEATURE_CONTENT
from ..logger import LoggerSetup


Expand All @@ -42,8 +37,8 @@ class Parser(object):

def __init__(
self,
gardenlinux_root: Optional[str] = None,
feature_dir_name: Optional[str] = "features",
gardenlinux_root: str | None = None,
feature_dir_name: str = "features",
logger: Optional[logging.Logger] = None,
):
"""
Expand All @@ -59,7 +54,7 @@ def __init__(
if gardenlinux_root is None:
gardenlinux_root = Parser._GARDENLINUX_ROOT

feature_base_dir = os.path.join(gardenlinux_root, feature_dir_name)
feature_base_dir = Path(gardenlinux_root).resolve() / feature_dir_name

if not os.access(feature_base_dir, os.R_OK):
raise ValueError(
Expand All @@ -70,7 +65,6 @@ def __init__(
logger = LoggerSetup.get_logger("gardenlinux.features")

self._feature_base_dir = feature_base_dir

self._graph = None
self._logger = logger

Expand Down Expand Up @@ -108,7 +102,7 @@ def graph(self) -> networkx.Graph:
"{0}/{1}/info.yaml".format(self._feature_base_dir, ref)
):
raise ValueError(
f"feature {node} references feature {ref}, but {feature_dir}/{ref}/info.yaml does not exist"
f"feature {node} references feature {ref}, but {self._feature_base_dir}/{ref}/info.yaml does not exist"
)

feature_graph.add_edge(node, ref, attr=attr)
Expand All @@ -122,9 +116,9 @@ def graph(self) -> networkx.Graph:

def filter(
self,
cname: str,
cname: str | None,
ignore_excludes: bool = False,
additional_filter_func: Optional[Callable[(str,), bool]] = None,
additional_filter_func: Optional[Callable[[str], bool]] = None,
) -> networkx.Graph:
"""
Filters the features graph.
Expand Down Expand Up @@ -162,15 +156,15 @@ def filter(
)

if not ignore_excludes:
Parser._exclude_from_filter_set(graph, input_features, filter_set)
Parser._exclude_from_filter_set(self, graph, input_features, filter_set)

return graph

def filter_as_dict(
self,
cname: str,
cname: str | None,
ignore_excludes: bool = False,
additional_filter_func: Optional[Callable[(str,), bool]] = None,
additional_filter_func: Optional[Callable[[str], bool]] = None,
) -> dict:
"""
Filters the features graph and returns it as a dict.
Expand Down Expand Up @@ -200,9 +194,9 @@ def filter_as_dict(

def filter_as_list(
self,
cname: str,
cname: str | None,
ignore_excludes: bool = False,
additional_filter_func: Optional[Callable[(str,), bool]] = None,
additional_filter_func: Optional[Callable[[str], bool]] = None,
) -> list:
"""
Filters the features graph and returns it as a list.
Expand All @@ -220,9 +214,9 @@ def filter_as_list(

def filter_as_string(
self,
cname: str,
cname: str | None,
ignore_excludes: bool = False,
additional_filter_func: Optional[Callable[(str,), bool]] = None,
additional_filter_func: Optional[Callable[[str], bool]] = None,
) -> str:
"""
Filters the features graph and returns it as a string.
Expand All @@ -240,7 +234,7 @@ def filter_as_string(

return ",".join(features)

def _exclude_from_filter_set(graph, input_features, filter_set):
def _exclude_from_filter_set(self, graph, input_features, filter_set):
"""
Removes the given `filter_set` out of `input_features`.

Expand All @@ -250,7 +244,9 @@ def _exclude_from_filter_set(graph, input_features, filter_set):
:since: 0.7.0
"""

exclude_graph_view = Parser._get_graph_view_for_attr(graph, "exclude")
exclude_graph_view = cast(
networkx.DiGraph, Parser._get_graph_view_for_attr(graph, "exclude")
)
exclude_list = []

for node in networkx.lexicographical_topological_sort(graph):
Expand Down
12 changes: 12 additions & 0 deletions tests/features/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@ class FakeGraph:
assert "b-->c" in markup


def test_graph_mermaid_raises_no_flavor():
# Arrange
class MockGraph:
edges = [("x", "y"), ("y", "z")]

# Act / Assert
with pytest.raises(
RuntimeError, match="Error while generating graph: Flavor is None!"
):
fema.graph_as_mermaid_markup(None, MockGraph())


def test_get_minimal_feature_set_filters():
# Arrange
class FakeGraph:
Expand Down
Loading