socx-cli 0.13.7__tar.gz → 0.13.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. {socx_cli-0.13.7 → socx_cli-0.13.8}/PKG-INFO +1 -1
  2. {socx_cli-0.13.7 → socx_cli-0.13.8}/pyproject.toml +1 -1
  3. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/config/converters.py +7 -7
  4. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/regression/test.py +29 -7
  5. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_tui/regression/details.py +4 -0
  6. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_tui/regression/dialog.py +6 -8
  7. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_tui/regression/widget.py +18 -7
  8. {socx_cli-0.13.7 → socx_cli-0.13.8}/.gitignore +0 -0
  9. {socx_cli-0.13.7 → socx_cli-0.13.8}/LICENSE +0 -0
  10. {socx_cli-0.13.7 → socx_cli-0.13.8}/README.md +0 -0
  11. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/__init__.py +0 -0
  12. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/__main__.py +0 -0
  13. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/cli/__init__.py +0 -0
  14. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/cli/_cli.py +0 -0
  15. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/cli/_jinja.py +0 -0
  16. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/cli/callbacks.py +0 -0
  17. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/cli/cfg.py +0 -0
  18. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/cli/cli.py +0 -0
  19. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/cli/params.py +0 -0
  20. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/cli/types.py +0 -0
  21. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/config/__init__.py +0 -0
  22. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/config/_config.py +0 -0
  23. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/config/_settings.py +0 -0
  24. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/config/encoders.py +0 -0
  25. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/config/formatters.py +0 -0
  26. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/config/serializers.py +0 -0
  27. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/config/validators.py +0 -0
  28. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/core/__init__.py +0 -0
  29. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/core/_paths.py +0 -0
  30. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/core/encoder.py +0 -0
  31. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/core/enums.py +0 -0
  32. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/core/funcs.py +0 -0
  33. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/core/metadata.py +0 -0
  34. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/core/paths.py +0 -0
  35. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/core/schema/__init__.py +0 -0
  36. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/core/schema/git/__init__.py +0 -0
  37. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/core/schema/git/git.py +0 -0
  38. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/core/schema/git/manifest.py +0 -0
  39. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/core/schema/plugin.py +0 -0
  40. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/core/schema/types.py +0 -0
  41. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/core/serializer.py +0 -0
  42. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/git/__init__.py +0 -0
  43. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/git/_git.py +0 -0
  44. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/git/_manifest.py +0 -0
  45. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/git/_ssh.py +0 -0
  46. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/io/__init__.py +0 -0
  47. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/io/console.py +0 -0
  48. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/io/decorators.py +0 -0
  49. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/io/log.py +0 -0
  50. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/patterns/__init__.py +0 -0
  51. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/patterns/mixins/__init__.py +0 -0
  52. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/patterns/mixins/proxy.py +0 -0
  53. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/patterns/mixins/uid.py +0 -0
  54. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/patterns/singleton/__init__.py +0 -0
  55. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/patterns/singleton/singleton.py +0 -0
  56. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/patterns/visitor/__init__.py +0 -0
  57. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/patterns/visitor/protocol.py +0 -0
  58. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/patterns/visitor/traversal.py +0 -0
  59. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/regression/__init__.py +0 -0
  60. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/regression/progress.py +0 -0
  61. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/regression/regression.py +0 -0
  62. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/regression/status.py +0 -0
  63. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/regression/validator.py +0 -0
  64. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/regression/visitor.py +0 -0
  65. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/static/settings/cli.yaml +0 -0
  66. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/static/settings/console.yaml +0 -0
  67. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/static/settings/git.yaml +0 -0
  68. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/static/settings/logging.yaml +0 -0
  69. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/static/settings/plugins.yaml +0 -0
  70. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/static/settings/regression.yaml +0 -0
  71. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/static/settings/rich_click.yaml +0 -0
  72. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/static/settings/settings.yaml +0 -0
  73. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/static/sql/socx.sql +0 -0
  74. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/utils/__init__.py +0 -0
  75. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx/utils/decorators.py +0 -0
  76. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_plugins/config/__init__.py +0 -0
  77. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_plugins/config/_config.py +0 -0
  78. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_plugins/config/edit.py +0 -0
  79. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_plugins/git/__init__.py +0 -0
  80. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_plugins/git/arguments.py +0 -0
  81. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_plugins/git/callbacks.py +0 -0
  82. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_plugins/git/cli.py +0 -0
  83. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_plugins/git/manifest.py +0 -0
  84. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_plugins/git/renderables.py +0 -0
  85. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_plugins/git/summary.py +0 -0
  86. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_plugins/git/utils.py +0 -0
  87. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_plugins/plugin/__init__.py +0 -0
  88. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_plugins/plugin/example.py +0 -0
  89. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_plugins/plugin/schema.py +0 -0
  90. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_plugins/regression/__init__.py +0 -0
  91. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_plugins/regression/_run.py +0 -0
  92. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_plugins/regression/callbacks.py +0 -0
  93. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_plugins/regression/cli.py +0 -0
  94. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_plugins/regression/run.py +0 -0
  95. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_plugins/regression/tui.py +0 -0
  96. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_plugins/version/__init__.py +0 -0
  97. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_plugins/version/__main__.py +0 -0
  98. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_tui/__init__.py +0 -0
  99. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_tui/regression/__init__.py +0 -0
  100. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_tui/regression/__main__.py +0 -0
  101. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_tui/regression/app.py +0 -0
  102. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_tui/regression/bindings/__init__.py +0 -0
  103. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_tui/regression/bindings/vim/__init__.py +0 -0
  104. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_tui/regression/bindings/vim/mode.py +0 -0
  105. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_tui/regression/bindings/vim/vim.py +0 -0
  106. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_tui/regression/containers.py +0 -0
  107. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_tui/regression/mixins/__init__.py +0 -0
  108. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_tui/regression/mixins/composable.py +0 -0
  109. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_tui/regression/mixins/configurable.py +0 -0
  110. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_tui/regression/preview.py +0 -0
  111. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_tui/regression/table.py +0 -0
  112. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_tui/regression/tree.py +0 -0
  113. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_tui/static/tcss/regression/app.tcss +0 -0
  114. {socx_cli-0.13.7 → socx_cli-0.13.8}/socx_tui/static/tcss/regression/preview.tcss +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: socx-cli
3
- Version: 0.13.7
3
+ Version: 0.13.8
4
4
  Summary: System on chip verification and tooling infrastructure.
5
5
  Project-URL: Issues, https://github.com/sagikimhi/socx-cli/issues
6
6
  Project-URL: Homepage, https://sagikimhi.dev/socx-cli
@@ -29,7 +29,7 @@ socx = 'socx.__main__:main'
29
29
  [project]
30
30
  name = "socx-cli"
31
31
  readme = "README.md"
32
- version = "0.13.7"
32
+ version = "0.13.8"
33
33
  license = "Apache-2.0"
34
34
  authors = [{ name = "Sagi Kimhi", email = "sagi.kim5@gmail.com" }]
35
35
  maintainers = [{ name = "Sagi Kimhi", email = "sagi.kim5@gmail.com" }]
@@ -310,8 +310,8 @@ class SymbolConverter(Converter[str | Lazy, Any]):
310
310
 
311
311
  class CommandConverter(
312
312
  Converter[
313
- str | Lazy | sh.Command | click.Command | PluginModel,
314
- str | Lazy | click.Command,
313
+ str | Lazy | BaseCommand | click.Command | PluginModel,
314
+ str | Lazy | click.Command | click.Group,
315
315
  ]
316
316
  ):
317
317
  """Turn module or script references into Rich Click commands."""
@@ -351,7 +351,7 @@ class CommandConverter(
351
351
  @overload
352
352
  def __call__(
353
353
  self,
354
- value: sh.Command,
354
+ value: BaseCommand,
355
355
  *args: Any,
356
356
  **kwargs: Any,
357
357
  ) -> click.Command: ...
@@ -374,10 +374,10 @@ class CommandConverter(
374
374
  @_validate
375
375
  def __call__(
376
376
  self,
377
- value: str | Lazy | sh.Command | click.Command | PluginModel,
377
+ value: str | Lazy | BaseCommand | click.Command | PluginModel,
378
378
  *args: Any,
379
379
  **kwargs: Any,
380
- ) -> Lazy | click.Command | click.Group:
380
+ ) -> str | Lazy | click.Command | click.Group:
381
381
  """Build a Click command from dotted paths or reuse existing ones."""
382
382
  if isinstance(value, Lazy):
383
383
  if value.casting is self:
@@ -401,9 +401,9 @@ class CommandConverter(
401
401
  nonlocal value
402
402
 
403
403
  if TYPE_CHECKING:
404
- value = cast(str | sh.Command | PluginModel, value)
404
+ value = cast(str | BaseCommand | PluginModel, value)
405
405
 
406
- if isinstance(value, sh.Command):
406
+ if isinstance(value, str | BaseCommand):
407
407
  return self._run_shell_script(value, *args)
408
408
 
409
409
  if isinstance(value, PluginModel) and value.is_script():
@@ -10,6 +10,7 @@ import signal
10
10
  import asyncio as aio
11
11
  import logging
12
12
  from textwrap import dedent
13
+ from typing import Any
13
14
  from enum import IntEnum, StrEnum, auto
14
15
  from pathlib import Path
15
16
  from subprocess import CalledProcessError
@@ -27,7 +28,6 @@ from pydantic import (
27
28
  computed_field,
28
29
  )
29
30
  from anyio.abc import Process, TaskStatus
30
- from anyio.to_thread import run_sync
31
31
 
32
32
  from socx.patterns import Visitor
33
33
  from socx.core.schema import Script, DirectoryPath
@@ -96,6 +96,22 @@ class TestBase(BaseModel):
96
96
  """,
97
97
  )
98
98
 
99
+ fresh_env: bool = Field(
100
+ default=False,
101
+ description="""
102
+ Whether or not to execute the test in a fresh environment.
103
+
104
+ A fresh environment is an environment with no environment
105
+ variables defined other than those defined in the ``env`` field.
106
+
107
+ A non-fresh environment will contain all environment variables of
108
+ the current process, as well as any variables defined in the
109
+ ``env`` field.
110
+
111
+ If left unspecified, defaults to False.
112
+ """,
113
+ )
114
+
99
115
  model_config = ConfigDict(
100
116
  from_attributes=True,
101
117
  arbitrary_types_allowed=True,
@@ -112,6 +128,12 @@ class TestBase(BaseModel):
112
128
  _output_dir: Path | None = PrivateAttr(None)
113
129
  _termination_requested: bool = PrivateAttr(False)
114
130
 
131
+ def model_post_init(self, context: Any) -> None:
132
+ if not self.fresh_env:
133
+ env = os.environ.copy()
134
+ env.update(self.env)
135
+ self.env = env
136
+
115
137
  @computed_field
116
138
  @property
117
139
  def status(self) -> TestStatus:
@@ -301,7 +323,7 @@ class TestBase(BaseModel):
301
323
  return
302
324
 
303
325
  async with self.mutex:
304
- await run_sync(self._do_reset)
326
+ self._do_reset()
305
327
 
306
328
  async def restart(self, auto_start: bool = True) -> None:
307
329
  """Terminate, reset, and execute the test again."""
@@ -377,7 +399,7 @@ class Test(TestBase):
377
399
  """Concrete test model with subprocess execution support."""
378
400
 
379
401
  exec: Script = Field(
380
- default=...,
402
+ "",
381
403
  validation_alias=AliasChoices("exec", "command", "script"),
382
404
  description=(
383
405
  "A shell command or a path to an executable file to run on test "
@@ -480,7 +502,7 @@ class Test(TestBase):
480
502
  ) -> None:
481
503
  async with anyio.create_task_group() as tg:
482
504
  self._process = await anyio.open_process(
483
- str(self.exec),
505
+ command=str(self.exec),
484
506
  cwd=self.cwd,
485
507
  env=self.env,
486
508
  stdout=aio.subprocess.PIPE,
@@ -522,11 +544,11 @@ class Test(TestBase):
522
544
  elif self._retcode == 0:
523
545
  self._status = TestStatus.Finished
524
546
  self._result = TestResult.Passed
525
- elif self._retcode is None or 0 < self._retcode < 0x80:
526
- self._status = TestStatus.Terminated
547
+ elif self._retcode is not None and 0 < self._retcode < 0x80:
548
+ self._status = TestStatus.Finished
527
549
  self._result = TestResult.Failed
528
550
  else:
529
- self._status = TestStatus.Finished
551
+ self._status = TestStatus.Terminated
530
552
  self._result = TestResult.Failed
531
553
 
532
554
  self._process = None
@@ -98,6 +98,7 @@ class RegressionStaticDetails(Static):
98
98
  self.format_header(model),
99
99
  f"💡 Status: {self.format_status(model.status)}",
100
100
  f"🚩 Result: {self.format_result(model.result)}",
101
+ f"🧑‍💻 Exit Code: {self.format_retcode(model.retcode)}"
101
102
  f"⌛ Elapsed Time: {self.format_timedelta(model.elapsed_time)}", # noqa: E501
102
103
  f"⌛ Started Time: {self.format_time(model.started_time)}",
103
104
  f"⌛ Finished Time: {self.format_time(model.finished_time)}", # noqa: E501
@@ -126,6 +127,9 @@ class RegressionStaticDetails(Static):
126
127
  ["## 📜 Command/Script", "```bash", script_str, "```"]
127
128
  )
128
129
 
130
+ def format_retcode(self, retcode: int | None) -> str:
131
+ return str(retcode)
132
+
129
133
  def format_result(self, result: TestResult | str) -> str:
130
134
  if isinstance(result, TestResult):
131
135
  return result.value
@@ -161,24 +161,22 @@ class RestartSelectionDialog(ModalScreen[str | None]):
161
161
  yield Button(
162
162
  "Restart\nAll", id="restart-scope-all", variant="success"
163
163
  )
164
- yield Button(
165
- "Restart\nFailed or Stopped",
166
- id="restart-scope-failed-cancelled",
167
- variant="primary",
168
- )
169
- yield Button("Restart\nStopped", id="restart-scope-cancelled")
170
164
  yield Button(
171
165
  "Restart\nFailed",
172
166
  id="restart-scope-failed",
173
167
  variant="error",
174
168
  )
169
+ yield Button(
170
+ "Restart\nStopped",
171
+ id="restart-scope-cancelled",
172
+ variant="primary",
173
+ )
175
174
 
176
175
  def on_button_pressed(self, event: Button.Pressed) -> None:
177
176
  button_id = event.button.id
178
177
  mapping = {
179
178
  "restart-scope-all": "all",
180
- "restart-scope-failed-cancelled": "failed_or_cancelled",
181
- "restart-scope-cancelled": "cancelled",
182
179
  "restart-scope-failed": "failed",
180
+ "restart-scope-cancelled": "cancelled",
183
181
  }
184
182
  self.dismiss(mapping.get(button_id))
@@ -9,7 +9,15 @@ from typing import Any, ClassVar
9
9
 
10
10
  import rich.repr
11
11
  from rich.text import Text
12
- from socx import Regression, Test, TestBase, TestResult, settings, enums
12
+ from socx import (
13
+ Regression,
14
+ Test,
15
+ TestBase,
16
+ TestResult,
17
+ settings,
18
+ enums,
19
+ TestStatus,
20
+ )
13
21
  from textual import on, work
14
22
  from textual.app import ComposeResult
15
23
  from textual.binding import Binding, BindingType
@@ -237,12 +245,10 @@ class RegressionWidget(Widget, can_focus=False, inherit_bindings=True):
237
245
  match scope:
238
246
  case "all":
239
247
  return True
240
- case "cancelled":
241
- return t.terminated
242
248
  case "failed":
243
- return t.failed and not t.terminated
244
- case "failed_or_cancelled":
245
- return t.failed
249
+ return t.result is TestResult.Failed
250
+ case "cancelled":
251
+ return t.status is TestStatus.Terminated
246
252
  case _:
247
253
  return False
248
254
 
@@ -480,7 +486,12 @@ class RegressionWidget(Widget, can_focus=False, inherit_bindings=True):
480
486
  def _format_test_status_label(self, test: TestBase) -> Text:
481
487
  status = f"💡 {self.details_view.details.format_status(test.status)}"
482
488
  result = f"🚩 {self.details_view.details.format_result(test.result)}"
483
- return Text.assemble("[", "|".join([status, result]), "]")
489
+
490
+ if isinstance(test, Test):
491
+ retcode = f"🧑‍💻 {self.details_view.details.format_retcode(test.retcode)}" # noqa: E501
492
+ return Text.assemble("[", "|".join([status, result, retcode]), "]")
493
+ else:
494
+ return Text.assemble("[", "|".join([status, result]), "]")
484
495
 
485
496
  def _no_model_selected_notification(self) -> None:
486
497
  msg = "Select a regression or test first."
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes