Skip to content

Commit 273eab1

Browse files
committed
Change internal "imported plan" from a single plan to a list of plans
This will be needed for #1770 when we dive into importing multiple plans.
1 parent 2937ad3 commit 273eab1

File tree

2 files changed

+55
-35
lines changed

2 files changed

+55
-35
lines changed

tmt/base.py

+47-27
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import enum
88
import functools
99
import itertools
10+
import operator
1011
import os
1112
import re
1213
import shutil
@@ -1852,8 +1853,8 @@ class Plan(
18521853
_original_plan: Optional['Plan'] = field(default=None, internal=True)
18531854
_original_plan_fmf_id: Optional[FmfId] = field(default=None, internal=True)
18541855

1855-
_imported_plan_fmf_id: Optional[FmfId] = field(default=None, internal=True)
1856-
_imported_plan: Optional['Plan'] = field(default=None, internal=True)
1856+
_imported_plan_fmf_ids: list[FmfId] = field(default_factory=list, internal=True)
1857+
_imported_plans: list['Plan'] = field(default_factory=list, internal=True)
18571858

18581859
_derived_plans: list['Plan'] = field(default_factory=list, internal=True)
18591860
derived_id: Optional[int] = field(default=None, internal=True)
@@ -1901,12 +1902,14 @@ def __init__(
19011902
# set, incorrect default value is generated, and the field ends up being
19021903
# set to `None`. See https://github.com/teemtee/tmt/issues/2630.
19031904
self._applied_cli_invocations = []
1905+
self._imported_plan_fmf_ids = []
1906+
self._imported_plans = []
19041907
self._derived_plans = []
19051908

19061909
# Check for possible remote plan reference first
19071910
reference = self.node.get(['plan', 'import'])
19081911
if reference is not None:
1909-
self._imported_plan_fmf_id = FmfId.from_spec(reference)
1912+
self._imported_plan_fmf_ids = [FmfId.from_spec(reference)]
19101913

19111914
# Save the run, prepare worktree and plan data directory
19121915
self.my_run = run
@@ -2345,19 +2348,23 @@ def show(self) -> None:
23452348
self._show_additional_keys()
23462349

23472350
# Show fmf id of the remote plan in verbose mode
2348-
if (self._original_plan or self._imported_plan_fmf_id) and self.verbosity_level:
2351+
if (self._original_plan or self._imported_plan_fmf_ids) and self.verbosity_level:
23492352
# Pick fmf id from the original plan by default, use the
23502353
# current plan in shallow mode when no plans are fetched.
23512354

2355+
def _show_imported(fmf_id: FmfId) -> None:
2356+
echo(tmt.utils.format('import', '', key_color='blue'))
2357+
2358+
for key, value in fmf_id.items():
2359+
echo(tmt.utils.format(key, value, key_color='green'))
2360+
23522361
if self._original_plan is not None:
2353-
fmf_id = self._original_plan._imported_plan_fmf_id
2354-
else:
2355-
fmf_id = self._imported_plan_fmf_id
2362+
for fmf_id in self._original_plan._imported_plan_fmf_ids:
2363+
_show_imported(fmf_id)
23562364

2357-
echo(tmt.utils.format('import', '', key_color='blue'))
2358-
assert fmf_id is not None # narrow type
2359-
for key, value in fmf_id.items():
2360-
echo(tmt.utils.format(key, value, key_color='green'))
2365+
else:
2366+
for fmf_id in self._imported_plan_fmf_ids:
2367+
_show_imported(fmf_id)
23612368

23622369
# FIXME - Make additional attributes configurable
23632370
def lint_unknown_keys(self) -> LinterReturn:
@@ -2735,20 +2742,16 @@ def is_remote_plan_reference(self) -> bool:
27352742
"""
27362743
Check whether the plan is a remote plan reference
27372744
"""
2738-
return self._imported_plan_fmf_id is not None
2745+
return bool(self._imported_plan_fmf_ids)
27392746

2740-
def import_plan(self) -> Optional['Plan']:
2741-
"""
2742-
Import plan from a remote repository, return a Plan instance
2747+
def _resolve_import(self, plan_id: FmfId) -> Iterator['Plan']:
27432748
"""
2744-
if not self.is_remote_plan_reference:
2745-
return None
2749+
Discover and import plans matching a given fmf id.
27462750
2747-
if self._imported_plan:
2748-
return self._imported_plan
2751+
:param plan_id: fmf id representing one or more plans to import.
2752+
:yields: new :py:class:`Plan` for each imported plan.
2753+
"""
27492754

2750-
assert self._imported_plan_fmf_id is not None # narrow type
2751-
plan_id = self._imported_plan_fmf_id
27522755
self.debug(f"Import remote plan '{plan_id.name}' from '{plan_id.url}'.", level=3)
27532756

27542757
# Clone the whole git repository if executing tests (run is attached)
@@ -2842,15 +2845,32 @@ def import_plan(self) -> Optional['Plan']:
28422845

28432846
# Override the plan name with the local one to ensure unique names
28442847
node.name = self.name
2845-
# Create the plan object, save links between both plans
2846-
self._imported_plan = Plan(node=node, run=self.my_run, logger=self._logger)
2847-
self._imported_plan._original_plan = self
2848-
self._imported_plan._original_plan_fmf_id = self.fmf_id
28492848

28502849
with self.environment.as_environ():
28512850
expand_node_data(node.data, self._fmf_context)
28522851

2853-
return self._imported_plan
2852+
yield Plan(node=node, run=self.my_run, logger=self._logger)
2853+
2854+
def resolve_imports(self) -> list['Plan']:
2855+
"""
2856+
Resolve possible references to remote plans.
2857+
2858+
:returns: one or more plans replacing the current one. The
2859+
current plan may also be one of the returned ones.
2860+
"""
2861+
2862+
if not self.is_remote_plan_reference:
2863+
return [self]
2864+
2865+
if not self._imported_plans:
2866+
for plan_id in self._imported_plan_fmf_ids:
2867+
for imported_plan in self._resolve_import(plan_id):
2868+
imported_plan._original_plan = self
2869+
imported_plan._original_plan_fmf_id = self.fmf_id
2870+
2871+
self._imported_plans.append(imported_plan)
2872+
2873+
return self._imported_plans
28542874

28552875
def derive_plan(self, derived_id: int, tests: dict[str, list[Test]]) -> 'Plan':
28562876
"""
@@ -3580,7 +3600,7 @@ def plans(
35803600
]
35813601

35823602
if not Plan._opt('shallow'):
3583-
plans = [plan.import_plan() or plan for plan in plans]
3603+
plans = functools.reduce(operator.iadd, (plan.resolve_imports() for plan in plans), [])
35843604

35853605
return self._filters_conditions(
35863606
sorted(plans, key=lambda plan: plan.order), filters, conditions, links, excludes

tmt/steps/discover/__init__.py

+8-8
Original file line numberDiff line numberDiff line change
@@ -193,18 +193,18 @@ def log_import_plan_details(self) -> None:
193193
Log details about the imported plan
194194
"""
195195

196-
parent = cast(Optional[tmt.steps.discover.Discover], self.parent)
196+
parent = cast(Optional[Discover], self.parent)
197197
if (
198198
parent
199199
and parent.plan._original_plan
200-
and parent.plan._original_plan._imported_plan_fmf_id
200+
and parent.plan._original_plan._imported_plan_fmf_ids
201201
):
202-
remote_plan_id = parent.plan._original_plan._imported_plan_fmf_id
203-
# FIXME: cast() - https://github.com/python/mypy/issues/7981
204-
# Note the missing Optional for values - to_minimal_dict() would
205-
# not include unset keys, therefore all values should be valid.
206-
for key, value in cast(dict[str, str], remote_plan_id.to_minimal_spec()).items():
207-
self.verbose(f'import {key}', value, 'green')
202+
for remote_plan_id in parent.plan._original_plan._imported_plan_fmf_ids:
203+
# FIXME: cast() - https://github.com/python/mypy/issues/7981
204+
# Note the missing Optional for values - to_minimal_dict() would
205+
# not include unset keys, therefore all values should be valid.
206+
for key, value in cast(dict[str, str], remote_plan_id.to_minimal_spec()).items():
207+
self.verbose(f'import {key}', value, 'green')
208208

209209
def post_dist_git(self, created_content: list[Path]) -> None:
210210
"""

0 commit comments

Comments
 (0)