Skip to content

Commit fa9886f

Browse files
committed
Rework architecture of the plugins and add tasks functionality
Added copy and call_command action
1 parent b5d9def commit fa9886f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+856
-211
lines changed

socon_embedded/action/__init__.py

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from typing import Union
2+
3+
from socon.core.manager import Hook
4+
from pydantic import BaseModel, ConfigDict
5+
6+
from socon_embedded.schema.task import Task
7+
8+
9+
class ActionSchema(BaseModel):
10+
model_config = ConfigDict(extra="forbid")
11+
12+
13+
class ActionBase(Hook, abstract=True):
14+
15+
manager = "ActionManager"
16+
schema = None
17+
18+
def __init__(self, task: Task) -> None:
19+
self._task = task
20+
self.args = self.schema(**self._task.args)
21+
22+
def run(self, tasks_vars=None):
23+
"""Run the task"""
24+
return {"failed": False}
25+
26+
def cleanup(self, force: bool = False):
27+
"""Method to perform a clean up at the end of an action plugin execution
28+
29+
Action plugins may override this if they deem necessary
30+
"""

socon_embedded/action/call.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from typing import Optional
2+
3+
from socon.core.management import call_command
4+
from socon.core.management.base import CommandError
5+
from socon_embedded.action import ActionBase, ActionSchema
6+
7+
8+
class Schema(ActionSchema):
9+
cmd: str
10+
argv: Optional[list[str]] = []
11+
12+
13+
class CallCommandAction(ActionBase):
14+
name = "call_command"
15+
schema = Schema
16+
17+
def run(self, tasks_vars=None):
18+
result = super().run(tasks_vars)
19+
try:
20+
call_command(self.args.cmd, *self.args.argv)
21+
except CommandError as e:
22+
result["failed"] = True
23+
result["msg"] = f"Command error: {e}"
24+
return result

socon_embedded/action/copy.py

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import os
2+
import tempfile
3+
import json
4+
import shutil
5+
6+
from pathlib import Path
7+
from typing import Optional
8+
9+
from socon_embedded.action import ActionBase, ActionSchema
10+
11+
from pydantic import model_validator
12+
13+
from socon_embedded.utils.converter import to_bytes
14+
15+
16+
class CopySchema(ActionSchema):
17+
dest: str
18+
src: Optional[str] = None
19+
content: Optional[str] = None
20+
21+
@model_validator(mode="after")
22+
def basic_validation(self):
23+
"""If src is a directory, dest must be a directory too."""
24+
if self.src is None and self.content is None:
25+
raise ValueError("'src' or 'content' field must be supply")
26+
if self.src is not None and self.content is not None:
27+
raise ValueError("Only 'src' or 'content' must be supply at the same time")
28+
if self.content is not None and Path(self.dest).is_dir():
29+
raise ValueError("Can not use 'content' with a dir as 'dest'")
30+
return self
31+
32+
33+
class CopyAction(ActionBase):
34+
name = "copy"
35+
schema = CopySchema
36+
37+
def run(self, tasks_vars=None):
38+
if tasks_vars is None:
39+
tasks_vars = dict()
40+
41+
# Result of the task stored in a dictionary
42+
result = super().run(tasks_vars)
43+
44+
source = self.args.src
45+
content = self.args.content
46+
47+
# Define content_tempfile in case we set it after finding content populated.
48+
content_tempfile = None
49+
50+
if content is not None:
51+
try:
52+
# If content comes to us as a dict it should be decoded json.
53+
# We need to encode it back into a string to write it out.
54+
if isinstance(content, dict) or isinstance(content, list):
55+
content_tempfile = self._create_content_tempfile(
56+
json.dumps(content)
57+
)
58+
else:
59+
content_tempfile = self._create_content_tempfile(content)
60+
source = content_tempfile
61+
except Exception as err:
62+
result["failed"] = True
63+
result["msg"] = f"could not write content temp file: {err}"
64+
return result
65+
66+
source = Path(source).expanduser().absolute()
67+
if source.is_dir():
68+
shutil.copytree(source, self.args.dest, dirs_exist_ok=True)
69+
else:
70+
shutil.copy2(source, self.args.dest)
71+
72+
return result
73+
74+
def _create_content_tempfile(self, content):
75+
"""Create a tempfile containing defined content"""
76+
fd, content_tempfile = tempfile.mkstemp(prefix=".")
77+
f = os.fdopen(fd, "wb")
78+
content = to_bytes(content)
79+
try:
80+
f.write(content)
81+
except Exception as err:
82+
os.remove(content_tempfile)
83+
raise Exception(err)
84+
finally:
85+
f.close()
86+
return content_tempfile

socon_embedded/action/move.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from socon_embedded.action import ActionBase, ActionSchema
2+
3+
4+
class MoveActionShema(ActionSchema):
5+
pass
6+
7+
8+
class MoveAction(ActionBase):
9+
name = "move"
10+
action_schema = MoveActionShema
11+
12+
def run(self, tasks_vars=None):
13+
pass

socon_embedded/builder/__init__.py

+14-20
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ def __str__(self) -> str:
4040
@dataclass
4141
class BuildInfo:
4242
"""Store building information"""
43+
4344
app: str
4445
builder: str
4546
project_file: str
@@ -50,7 +51,7 @@ def __str__(self) -> str:
5051
output = [
5152
f"Application: {self.app}",
5253
f"Input file: {self.project_file}",
53-
f"Builder: {self.builder}"
54+
f"Builder: {self.builder}",
5455
]
5556
variant_args = self.variant_args
5657
if variant_args:
@@ -60,11 +61,7 @@ def __str__(self) -> str:
6061

6162
def get_case_name(self) -> str:
6263
"""Get the testcase name that will be used in the junit report"""
63-
return " - ".join([
64-
self.app,
65-
self.builder,
66-
*self.variant_args.values()
67-
])
64+
return " - ".join([self.app, self.builder, *self.variant_args.values()])
6865

6966

7067
class Builder(Hook, abstract=True):
@@ -81,7 +78,7 @@ class Builder(Hook, abstract=True):
8178
def __init__(
8279
self,
8380
name: Optional[str] = None,
84-
executable: Optional[Union[str, os.PathLike]] = None
81+
executable: Optional[Union[str, os.PathLike]] = None,
8582
) -> None:
8683
self.name = name or getattr(self, "name", self.__class__.__name__)
8784
self.executable = executable or self.get_executable()
@@ -107,7 +104,7 @@ def build(
107104
warning_as_error: bool = False,
108105
output_file: Union[str, os.PathLike] = None,
109106
clean: bool = False,
110-
vars: dict = {},
107+
variables: dict = {},
111108
**kwargs: Any,
112109
) -> BuildResult:
113110
"""
@@ -149,12 +146,12 @@ def build(
149146
builder=self.name,
150147
project_file=project_file,
151148
cmdline=cmdline,
152-
variant_args=variant_args
149+
variant_args=variant_args,
153150
)
154151
self.display_build_info(buildinfo)
155152

156153
# Run pre_build method if required
157-
self.pre_build(buildinfo, **vars)
154+
self.pre_build(buildinfo, **variables)
158155

159156
# Get an approximation build time execution
160157
time_started = time.time()
@@ -164,9 +161,7 @@ def build(
164161
try:
165162
status_code, output = self.execute(cmdline, **kwargs)
166163
except BuildCommandNotFound as e:
167-
build_result = BuildResult(
168-
Result(Status.FAILURE, str(e)), str(e)
169-
)
164+
build_result = BuildResult(Result(Status.FAILURE, str(e)), str(e))
170165
else:
171166
# Get the execution time of the build
172167
execution_time = time.time() - time_started
@@ -184,12 +179,12 @@ def build(
184179

185180
# Save the log into the artifact path
186181
if output_file:
187-
with open(output_file, 'w+') as f:
182+
with open(output_file, "w+") as f:
188183
f.write(build_result.output)
189184

190185
# call the post_build method
191186
output_dir = Path(output_file).parent
192-
self.post_build(build_result, output_dir, **vars)
187+
self.post_build(build_result, output_dir, **variables)
193188

194189
return build_result
195190

@@ -201,8 +196,7 @@ def get_main_args(
201196
pass
202197

203198
def execute(
204-
self, commands: list[str],
205-
**subprocess_args: Any
199+
self, commands: list[str], **subprocess_args: Any
206200
) -> Tuple[int, Union[str, bytes], str]:
207201
return self._execute(commands, **subprocess_args)
208202

@@ -216,11 +210,11 @@ def display_result(self, build_result: BuildResult) -> None:
216210
terminal.line(build_result.result.message)
217211
terminal.line(f"Result: {status_msg}\n", fg=color)
218212

219-
def pre_build(self, build_info: BuildInfo, **vars):
213+
def pre_build(self, build_info: BuildInfo, **variables):
220214
"""Method executed just before the build execution"""
221215
pass
222216

223-
def post_build(self, result: BuildResult, **vars):
217+
def post_build(self, result: BuildResult, output_dir: str, **variables):
224218
"""Method executed after the build execution"""
225219
pass
226220

@@ -270,7 +264,7 @@ def _execute(
270264
stdout=subprocess.PIPE,
271265
stderr=subprocess.STDOUT,
272266
universal_newlines=True,
273-
**subprocess_kwargs
267+
**subprocess_kwargs,
274268
)
275269

276270
if silent is True:
File renamed without changes.

0 commit comments

Comments
 (0)