Skip to content

Commit 58af704

Browse files
authored
Lint: Fix features module (#218)
* Lint: Fix type issues in features module * Fix redundant Optional type hints in Parser init * Fix undefined feature_dir call in ValueError raise in graph function * Fix Callable type annotation in filter functions * Explicitly cast exclude_graph_view to DiGraph to fix unknown member * Raise RuntimeError in graph function if flavor is None * Tests: Cover bad path for mermaid function
1 parent 5126dc7 commit 58af704

File tree

5 files changed

+46
-38
lines changed

5 files changed

+46
-38
lines changed

src/gardenlinux/features/__main__.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
import argparse
99
import logging
1010
import os
11-
import re
12-
import sys
1311
from functools import reduce
1412
from os import path
1513
from typing import Any, List, Set
@@ -100,9 +98,9 @@ def main() -> None:
10098
commit_id = cname.commit_id
10199
version = cname.version
102100

103-
input_features = Parser.get_cname_as_feature_set(flavor)
101+
_ = Parser.get_cname_as_feature_set(flavor)
104102
else:
105-
input_features = args.features
103+
_ = args.features
106104

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

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

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

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

175173

176-
def get_cname_base(sorted_features: Set[str]):
174+
def get_cname_base(sorted_features: List[str]):
177175
"""
178176
Get the base cname for the feature set given.
179177
@@ -228,7 +226,7 @@ def get_minimal_feature_set(graph: Any) -> Set[str]:
228226
return set([node for (node, degree) in graph.in_degree() if degree == 0])
229227

230228

231-
def graph_as_mermaid_markup(flavor: str, graph: Any) -> str:
229+
def graph_as_mermaid_markup(flavor: str | None, graph: Any) -> str:
232230
"""
233231
Generates a mermaid.js representation of the graph.
234232
This is helpful to identify dependencies between features.
@@ -243,6 +241,9 @@ def graph_as_mermaid_markup(flavor: str, graph: Any) -> str:
243241
:since: 0.7.0
244242
"""
245243

244+
if flavor is None:
245+
raise RuntimeError("Error while generating graph: Flavor is None!")
246+
246247
markup = f"---\ntitle: Dependency Graph for Feature {flavor}\n---\ngraph TD;\n"
247248

248249
for u, v in graph.edges:

src/gardenlinux/features/cname.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,13 @@ def cname(self) -> str:
9292
9393
:return: (str) CName
9494
"""
95-
95+
assert self._flavor is not None, "CName flavor is not set!"
9696
cname = self._flavor
9797

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

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

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

116116
@property
117-
def flavor(self) -> str:
117+
def flavor(self) -> str | None:
118118
"""
119119
Returns the flavor for the cname parsed.
120120
@@ -140,6 +140,7 @@ def platform(self) -> str:
140140
141141
:return: (str) Flavor
142142
"""
143+
assert self._flavor is not None, "Flavor not set!"
143144

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

src/gardenlinux/features/cname_main.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import argparse
99
import logging
1010
import re
11-
from functools import reduce
1211
from os.path import basename, dirname
1312

1413
from .__main__ import (
@@ -81,8 +80,7 @@ def main():
8180

8281
generated_cname = get_cname_base(sorted_minimal_features)
8382

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

8785
if cname.version_and_commit_id is not None:
8886
generated_cname += f"-{cname.version_and_commit_id}"

src/gardenlinux/features/parser.py

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,14 @@
66

77
import logging
88
import os
9-
import re
10-
import subprocess
119
from glob import glob
12-
from typing import Callable, Optional
10+
from pathlib import Path
11+
from typing import Callable, Optional, cast
1312

1413
import networkx
1514
import yaml
1615

17-
from ..constants import (
18-
ARCHS,
19-
BARE_FLAVOR_FEATURE_CONTENT,
20-
BARE_FLAVOR_LIBC_FEATURE_CONTENT,
21-
)
16+
from ..constants import BARE_FLAVOR_FEATURE_CONTENT, BARE_FLAVOR_LIBC_FEATURE_CONTENT
2217
from ..logger import LoggerSetup
2318

2419

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

4338
def __init__(
4439
self,
45-
gardenlinux_root: Optional[str] = None,
46-
feature_dir_name: Optional[str] = "features",
40+
gardenlinux_root: str | None = None,
41+
feature_dir_name: str = "features",
4742
logger: Optional[logging.Logger] = None,
4843
):
4944
"""
@@ -59,7 +54,7 @@ def __init__(
5954
if gardenlinux_root is None:
6055
gardenlinux_root = Parser._GARDENLINUX_ROOT
6156

62-
feature_base_dir = os.path.join(gardenlinux_root, feature_dir_name)
57+
feature_base_dir = Path(gardenlinux_root).resolve() / feature_dir_name
6358

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

7267
self._feature_base_dir = feature_base_dir
73-
7468
self._graph = None
7569
self._logger = logger
7670

@@ -108,7 +102,7 @@ def graph(self) -> networkx.Graph:
108102
"{0}/{1}/info.yaml".format(self._feature_base_dir, ref)
109103
):
110104
raise ValueError(
111-
f"feature {node} references feature {ref}, but {feature_dir}/{ref}/info.yaml does not exist"
105+
f"feature {node} references feature {ref}, but {self._feature_base_dir}/{ref}/info.yaml does not exist"
112106
)
113107

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

123117
def filter(
124118
self,
125-
cname: str,
119+
cname: str | None,
126120
ignore_excludes: bool = False,
127-
additional_filter_func: Optional[Callable[(str,), bool]] = None,
121+
additional_filter_func: Optional[Callable[[str], bool]] = None,
128122
) -> networkx.Graph:
129123
"""
130124
Filters the features graph.
@@ -162,15 +156,15 @@ def filter(
162156
)
163157

164158
if not ignore_excludes:
165-
Parser._exclude_from_filter_set(graph, input_features, filter_set)
159+
Parser._exclude_from_filter_set(self, graph, input_features, filter_set)
166160

167161
return graph
168162

169163
def filter_as_dict(
170164
self,
171-
cname: str,
165+
cname: str | None,
172166
ignore_excludes: bool = False,
173-
additional_filter_func: Optional[Callable[(str,), bool]] = None,
167+
additional_filter_func: Optional[Callable[[str], bool]] = None,
174168
) -> dict:
175169
"""
176170
Filters the features graph and returns it as a dict.
@@ -200,9 +194,9 @@ def filter_as_dict(
200194

201195
def filter_as_list(
202196
self,
203-
cname: str,
197+
cname: str | None,
204198
ignore_excludes: bool = False,
205-
additional_filter_func: Optional[Callable[(str,), bool]] = None,
199+
additional_filter_func: Optional[Callable[[str], bool]] = None,
206200
) -> list:
207201
"""
208202
Filters the features graph and returns it as a list.
@@ -220,9 +214,9 @@ def filter_as_list(
220214

221215
def filter_as_string(
222216
self,
223-
cname: str,
217+
cname: str | None,
224218
ignore_excludes: bool = False,
225-
additional_filter_func: Optional[Callable[(str,), bool]] = None,
219+
additional_filter_func: Optional[Callable[[str], bool]] = None,
226220
) -> str:
227221
"""
228222
Filters the features graph and returns it as a string.
@@ -240,7 +234,7 @@ def filter_as_string(
240234

241235
return ",".join(features)
242236

243-
def _exclude_from_filter_set(graph, input_features, filter_set):
237+
def _exclude_from_filter_set(self, graph, input_features, filter_set):
244238
"""
245239
Removes the given `filter_set` out of `input_features`.
246240
@@ -250,7 +244,9 @@ def _exclude_from_filter_set(graph, input_features, filter_set):
250244
:since: 0.7.0
251245
"""
252246

253-
exclude_graph_view = Parser._get_graph_view_for_attr(graph, "exclude")
247+
exclude_graph_view = cast(
248+
networkx.DiGraph, Parser._get_graph_view_for_attr(graph, "exclude")
249+
)
254250
exclude_list = []
255251

256252
for node in networkx.lexicographical_topological_sort(graph):

tests/features/test_main.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,18 @@ class FakeGraph:
7777
assert "b-->c" in markup
7878

7979

80+
def test_graph_mermaid_raises_no_flavor():
81+
# Arrange
82+
class MockGraph:
83+
edges = [("x", "y"), ("y", "z")]
84+
85+
# Act / Assert
86+
with pytest.raises(
87+
RuntimeError, match="Error while generating graph: Flavor is None!"
88+
):
89+
fema.graph_as_mermaid_markup(None, MockGraph())
90+
91+
8092
def test_get_minimal_feature_set_filters():
8193
# Arrange
8294
class FakeGraph:

0 commit comments

Comments
 (0)