socx-cli 0.13.7__tar.gz → 0.13.9__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 (119) hide show
  1. {socx_cli-0.13.7 → socx_cli-0.13.9}/PKG-INFO +3 -1
  2. {socx_cli-0.13.7 → socx_cli-0.13.9}/pyproject.toml +4 -2
  3. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/__init__.py +2 -0
  4. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/cli/callbacks.py +2 -0
  5. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/config/_config.py +5 -1
  6. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/config/converters.py +8 -9
  7. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/core/__init__.py +2 -0
  8. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/core/enums.py +4 -7
  9. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/core/schema/__init__.py +2 -0
  10. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/core/schema/plugin.py +59 -63
  11. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/core/schema/types.py +10 -0
  12. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/io/console.py +2 -2
  13. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/io/log.py +14 -14
  14. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/regression/regression.py +73 -40
  15. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/regression/test.py +117 -82
  16. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/static/settings/cli.yaml +1 -6
  17. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/static/settings/logging.yaml +6 -6
  18. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/static/settings/regression.yaml +51 -1
  19. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/static/settings/settings.yaml +1 -0
  20. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_plugins/regression/callbacks.py +1 -1
  21. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_plugins/regression/cli.py +2 -0
  22. socx_cli-0.13.9/socx_plugins/regression/serve.py +29 -0
  23. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_plugins/regression/tui.py +5 -2
  24. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_tui/regression/app.py +33 -40
  25. socx_cli-0.13.9/socx_tui/regression/details.py +323 -0
  26. socx_cli-0.13.9/socx_tui/regression/dialog.py +223 -0
  27. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_tui/regression/tree.py +15 -9
  28. socx_cli-0.13.9/socx_tui/regression/widget.py +701 -0
  29. socx_cli-0.13.9/socx_tui/static/tcss/regression/app.tcss +276 -0
  30. socx_cli-0.13.7/socx_tui/regression/details.py +0 -178
  31. socx_cli-0.13.7/socx_tui/regression/dialog.py +0 -184
  32. socx_cli-0.13.7/socx_tui/regression/widget.py +0 -487
  33. socx_cli-0.13.7/socx_tui/static/tcss/regression/app.tcss +0 -224
  34. {socx_cli-0.13.7 → socx_cli-0.13.9}/.gitignore +0 -0
  35. {socx_cli-0.13.7 → socx_cli-0.13.9}/LICENSE +0 -0
  36. {socx_cli-0.13.7 → socx_cli-0.13.9}/README.md +0 -0
  37. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/__main__.py +0 -0
  38. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/cli/__init__.py +0 -0
  39. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/cli/_cli.py +0 -0
  40. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/cli/_jinja.py +0 -0
  41. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/cli/cfg.py +0 -0
  42. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/cli/cli.py +0 -0
  43. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/cli/params.py +0 -0
  44. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/cli/types.py +0 -0
  45. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/config/__init__.py +0 -0
  46. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/config/_settings.py +0 -0
  47. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/config/encoders.py +0 -0
  48. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/config/formatters.py +0 -0
  49. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/config/serializers.py +0 -0
  50. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/config/validators.py +0 -0
  51. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/core/_paths.py +0 -0
  52. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/core/encoder.py +0 -0
  53. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/core/funcs.py +0 -0
  54. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/core/metadata.py +0 -0
  55. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/core/paths.py +0 -0
  56. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/core/schema/git/__init__.py +0 -0
  57. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/core/schema/git/git.py +0 -0
  58. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/core/schema/git/manifest.py +0 -0
  59. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/core/serializer.py +0 -0
  60. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/git/__init__.py +0 -0
  61. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/git/_git.py +0 -0
  62. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/git/_manifest.py +0 -0
  63. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/git/_ssh.py +0 -0
  64. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/io/__init__.py +0 -0
  65. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/io/decorators.py +0 -0
  66. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/patterns/__init__.py +0 -0
  67. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/patterns/mixins/__init__.py +0 -0
  68. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/patterns/mixins/proxy.py +0 -0
  69. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/patterns/mixins/uid.py +0 -0
  70. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/patterns/singleton/__init__.py +0 -0
  71. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/patterns/singleton/singleton.py +0 -0
  72. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/patterns/visitor/__init__.py +0 -0
  73. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/patterns/visitor/protocol.py +0 -0
  74. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/patterns/visitor/traversal.py +0 -0
  75. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/regression/__init__.py +0 -0
  76. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/regression/progress.py +0 -0
  77. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/regression/status.py +0 -0
  78. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/regression/validator.py +0 -0
  79. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/regression/visitor.py +0 -0
  80. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/static/settings/console.yaml +0 -0
  81. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/static/settings/git.yaml +0 -0
  82. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/static/settings/plugins.yaml +0 -0
  83. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/static/settings/rich_click.yaml +0 -0
  84. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/static/sql/socx.sql +0 -0
  85. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/utils/__init__.py +0 -0
  86. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx/utils/decorators.py +0 -0
  87. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_plugins/config/__init__.py +0 -0
  88. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_plugins/config/_config.py +0 -0
  89. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_plugins/config/edit.py +0 -0
  90. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_plugins/git/__init__.py +0 -0
  91. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_plugins/git/arguments.py +0 -0
  92. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_plugins/git/callbacks.py +0 -0
  93. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_plugins/git/cli.py +0 -0
  94. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_plugins/git/manifest.py +0 -0
  95. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_plugins/git/renderables.py +0 -0
  96. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_plugins/git/summary.py +0 -0
  97. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_plugins/git/utils.py +0 -0
  98. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_plugins/plugin/__init__.py +0 -0
  99. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_plugins/plugin/example.py +0 -0
  100. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_plugins/plugin/schema.py +0 -0
  101. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_plugins/regression/__init__.py +0 -0
  102. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_plugins/regression/_run.py +0 -0
  103. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_plugins/regression/run.py +0 -0
  104. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_plugins/version/__init__.py +0 -0
  105. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_plugins/version/__main__.py +0 -0
  106. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_tui/__init__.py +0 -0
  107. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_tui/regression/__init__.py +0 -0
  108. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_tui/regression/__main__.py +0 -0
  109. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_tui/regression/bindings/__init__.py +0 -0
  110. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_tui/regression/bindings/vim/__init__.py +0 -0
  111. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_tui/regression/bindings/vim/mode.py +0 -0
  112. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_tui/regression/bindings/vim/vim.py +0 -0
  113. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_tui/regression/containers.py +0 -0
  114. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_tui/regression/mixins/__init__.py +0 -0
  115. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_tui/regression/mixins/composable.py +0 -0
  116. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_tui/regression/mixins/configurable.py +0 -0
  117. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_tui/regression/preview.py +0 -0
  118. {socx_cli-0.13.7 → socx_cli-0.13.9}/socx_tui/regression/table.py +0 -0
  119. {socx_cli-0.13.7 → socx_cli-0.13.9}/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.9
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,6 +29,7 @@ Classifier: Topic :: Software Development
29
29
  Classifier: Topic :: Utilities
30
30
  Requires-Python: >=3.12
31
31
  Requires-Dist: anyio[trio]>=4.12.1
32
+ Requires-Dist: blinker>=1.9.0
32
33
  Requires-Dist: click
33
34
  Requires-Dist: copier~=9.11
34
35
  Requires-Dist: declare
@@ -53,6 +54,7 @@ Requires-Dist: sh~=2.2
53
54
  Requires-Dist: sqlmodel>=0.0.31
54
55
  Requires-Dist: textual
55
56
  Requires-Dist: textual-fspicker>=0.6.0
57
+ Requires-Dist: textual-serve>=1.1.3
56
58
  Requires-Dist: textual-speedups
57
59
  Requires-Dist: typer~=0.19
58
60
  Requires-Dist: uv
@@ -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.9"
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" }]
@@ -83,6 +83,8 @@ dependencies = [
83
83
  "pydantic-settings>=2.12.0",
84
84
  "sqlmodel>=0.0.31",
85
85
  "anyio[trio]>=4.12.1",
86
+ "blinker>=1.9.0",
87
+ "textual-serve>=1.1.3",
86
88
  ]
87
89
 
88
90
  [project.urls]
@@ -129,7 +131,7 @@ docs = [
129
131
  "mkdocs-llmstxt>=0.4.0",
130
132
  "mkdocs-coverage",
131
133
  "mkdocs-glightbox",
132
- "mkdocstrings-python",
134
+ "mkdocstrings[python]>=0.18",
133
135
  "mkdocs-material[git, recommended, imaging]",
134
136
  "mkdocs-section-index",
135
137
  "griffe-typingdoc>=0.3.0",
@@ -90,6 +90,7 @@ __all__ = (
90
90
  # config
91
91
  "schema",
92
92
  "settings",
93
+ "Model",
93
94
  "Script",
94
95
  "NewPath",
95
96
  "FilePath",
@@ -139,6 +140,7 @@ __all__ = (
139
140
 
140
141
  from socx.core import enums as enums
141
142
  from socx.core import schema as schema
143
+ from socx.core import Model as Model
142
144
  from socx.core import Script as Script
143
145
  from socx.core import NewPath as NewPath
144
146
  from socx.core import FilePath as FilePath
@@ -46,10 +46,12 @@ def cwd_cb(ctx: Context, param: Parameter, value: Path) -> Path:
46
46
  def debug_cb(_: Context, param: Parameter, value: bool) -> bool:
47
47
  """Enable debug logging and persist the CLI switch to settings."""
48
48
  socx_logger = _get_logger()
49
+ settings[param.name] = value
49
50
  settings.cli.params[param.name] = value
50
51
  if value:
51
52
  set_level(Level.DEBUG, socx_logger)
52
53
  settings.cli.params["verbosity"] = get_level().name
54
+ settings.logging.handlers.console.level = Level.DEBUG.name
53
55
  return value
54
56
 
55
57
 
@@ -10,6 +10,7 @@ from typing import Any
10
10
  from pathlib import Path
11
11
 
12
12
  from werkzeug.local import LocalProxy
13
+ from dynaconf.base import Settings as DynaSettings
13
14
 
14
15
  from socx.config import converters
15
16
  from socx.core import (
@@ -21,7 +22,10 @@ from socx.config._settings import Settings
21
22
  from socx.patterns.mixins.proxy import ProxyMixin
22
23
 
23
24
 
24
- class SettingsProxy(ProxyMixin[Settings], Settings): ...
25
+ class SettingsProxyBase(Settings, DynaSettings): ...
26
+
27
+
28
+ class SettingsProxy(ProxyMixin[SettingsProxyBase], SettingsProxyBase): ...
25
29
 
26
30
 
27
31
  logger = logging.getLogger(__name__)
@@ -25,7 +25,6 @@ from importlib import import_module
25
25
  from functools import cached_property, singledispatchmethod
26
26
  from collections.abc import Iterable, Callable
27
27
 
28
- import sh
29
28
  import rich_click as click
30
29
  import rich_click.patch as click_patch
31
30
  import rich_click.rich_click_theme as click_theme
@@ -310,8 +309,8 @@ class SymbolConverter(Converter[str | Lazy, Any]):
310
309
 
311
310
  class CommandConverter(
312
311
  Converter[
313
- str | Lazy | sh.Command | click.Command | PluginModel,
314
- str | Lazy | click.Command,
312
+ str | Lazy | BaseCommand | click.Command | PluginModel,
313
+ str | Lazy | click.Command | click.Group,
315
314
  ]
316
315
  ):
317
316
  """Turn module or script references into Rich Click commands."""
@@ -351,7 +350,7 @@ class CommandConverter(
351
350
  @overload
352
351
  def __call__(
353
352
  self,
354
- value: sh.Command,
353
+ value: BaseCommand,
355
354
  *args: Any,
356
355
  **kwargs: Any,
357
356
  ) -> click.Command: ...
@@ -374,10 +373,10 @@ class CommandConverter(
374
373
  @_validate
375
374
  def __call__(
376
375
  self,
377
- value: str | Lazy | sh.Command | click.Command | PluginModel,
376
+ value: str | Lazy | BaseCommand | click.Command | PluginModel,
378
377
  *args: Any,
379
378
  **kwargs: Any,
380
- ) -> Lazy | click.Command | click.Group:
379
+ ) -> str | Lazy | click.Command | click.Group:
381
380
  """Build a Click command from dotted paths or reuse existing ones."""
382
381
  if isinstance(value, Lazy):
383
382
  if value.casting is self:
@@ -401,9 +400,9 @@ class CommandConverter(
401
400
  nonlocal value
402
401
 
403
402
  if TYPE_CHECKING:
404
- value = cast(str | sh.Command | PluginModel, value)
403
+ value = cast(str | BaseCommand | PluginModel, value)
405
404
 
406
- if isinstance(value, sh.Command):
405
+ if isinstance(value, str | BaseCommand):
407
406
  return self._run_shell_script(value, *args)
408
407
 
409
408
  if isinstance(value, PluginModel) and value.is_script():
@@ -679,7 +678,7 @@ class CommandConverter(
679
678
  )
680
679
 
681
680
  def is_shell_command(self, value: Any) -> bool:
682
- return isinstance(value, sh.Command)
681
+ return isinstance(value, BaseCommand)
683
682
 
684
683
  def is_click_command(self, value: Any) -> bool:
685
684
  return isinstance(value, click.Command)
@@ -7,6 +7,7 @@ __all__ = (
7
7
  "schema",
8
8
  "metadata",
9
9
  # types
10
+ "Model",
10
11
  "Script",
11
12
  "NewPath",
12
13
  "FilePath",
@@ -57,6 +58,7 @@ from . import paths as paths
57
58
  from . import schema as schema
58
59
  from . import metadata as metadata
59
60
 
61
+ from socx.core.schema import Model as Model
60
62
  from socx.core.schema import Script as Script
61
63
  from socx.core.schema import NewPath as NewPath
62
64
  from socx.core.schema import FilePath as FilePath
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
  import enum
4
4
  from typing import Self
5
5
  from pathlib import Path
6
- from functools import cache
7
6
 
8
7
 
9
8
  class AutoNumber(int, enum.ReprEnum):
@@ -18,22 +17,20 @@ class AutoNumber(int, enum.ReprEnum):
18
17
 
19
18
 
20
19
  class SettingsFormat(AutoNumber):
21
- Json = ".json"
22
- Yaml = ".yaml", ".yml"
20
+ Ini = ".ini", ".conf"
23
21
  Toml = ".toml"
24
- Ini = ".ini"
25
- Python = ".python"
22
+ Yaml = ".yaml", ".yml"
23
+ Json = ".json", ".jsonc", ".json5"
24
+ Python = ".py"
26
25
 
27
26
  def __init__(self, extension: str, *extensions: str) -> None:
28
27
  self.extensions = {extension, *extensions}
29
28
 
30
29
  @classmethod
31
- @cache
32
30
  def all_extensions(cls) -> set[str]:
33
31
  return {extension for member in cls for extension in member.extensions}
34
32
 
35
33
  @classmethod
36
- @cache
37
34
  def from_path(cls, path: str | Path) -> SettingsFormat:
38
35
  if isinstance(path, str):
39
36
  path = Path(path)
@@ -2,6 +2,7 @@ __all__ = (
2
2
  "git",
3
3
  "types",
4
4
  "plugin",
5
+ "Model",
5
6
  "Script",
6
7
  "NewPath",
7
8
  "FilePath",
@@ -15,6 +16,7 @@ from . import plugin as plugin
15
16
 
16
17
  from .git import git as git
17
18
 
19
+ from socx.core.schema.types import Model as Model
18
20
  from socx.core.schema.types import Script as Script
19
21
  from socx.core.schema.types import NewPath as NewPath
20
22
  from socx.core.schema.types import FilePath as FilePath
@@ -7,18 +7,14 @@ from pathlib import Path
7
7
 
8
8
  from box import SBox
9
9
  import rich_click as click
10
- from pydantic import Field, BaseModel, ConfigDict
10
+ from pydantic import Field
11
11
 
12
- from socx.core.schema.types import DirectoryPath, Script
12
+ from socx.core.schema.types import DirectoryPath, Script, Model
13
13
 
14
14
 
15
- class PluginModel(BaseModel):
15
+ class PluginModel(Model):
16
16
  """Metadata describing a plugin-backed CLI command."""
17
17
 
18
- name: str = Field(
19
- ..., pattern=r"[a-zA-Z0-9_-]+", description="Name of the plugin."
20
- )
21
-
22
18
  cwd: DirectoryPath = Field(
23
19
  default_factory=Path.cwd,
24
20
  description=dedent("""
@@ -29,83 +25,89 @@ class PluginModel(BaseModel):
29
25
 
30
26
  env: dict[str, str] = Field(
31
27
  default_factory=dict,
32
- description="""
33
- Environment variables that should be present when the
34
- command/script is invoked
35
- """.strip(),
28
+ description=dedent("""
29
+ Environment variables that should be present when the
30
+ command/script is invoked
31
+ """),
32
+ )
33
+
34
+ name: str = Field(
35
+ ..., pattern=r"[a-zA-Z0-9_-]+", description="Name of the plugin."
36
+ )
37
+
38
+ help: str = Field(
39
+ default="",
40
+ description=dedent("""
41
+ Description of what the plugin does to be printed during plugin
42
+ invocation if any of -h or --help flags were passed with the
43
+ command.
44
+ """),
45
+ )
46
+
47
+ enabled: bool = Field(
48
+ True,
49
+ description=dedent("""
50
+ Enable/disable the plugin. Disabled plugins are hidden and cannot be
51
+ invoked from the commandline. Defaults to True.
52
+ """),
36
53
  )
37
54
 
38
55
  timeout: float | None = Field(
39
- default=None,
56
+ None,
40
57
  ge=0,
41
- description="""
58
+ description=dedent("""
42
59
  An optional timeout in seconds for the plugin execution.
43
60
  If left unspecified, then plugin execution may last indefinitely.
44
- """,
45
- )
46
-
47
- enabled: bool = Field(
48
- default=True,
49
- description="""
50
- Whether or not the plugin should be enabled.
51
- If left unspecified, defaults to True.
52
- """,
61
+ """),
53
62
  )
54
63
 
55
64
  fresh_env: bool = Field(
56
- default=False,
57
- description="""
58
- Whether or not to execute the plugin in a fresh environment.
59
- A fresh environment is an environment with no environment
60
- variables defined other than those defined in the ``env`` field.
61
- A non-fresh environment will contain all environment variables of
62
- the current process, as well as any variables defined in the
63
- ``env`` field.
64
- If left unspecified, defaults to False.
65
- """,
65
+ False,
66
+ description=dedent("""
67
+ Whether or not to execute the plugin in a fresh environment.
68
+ A fresh environment is an environment with no environment
69
+ variables defined other than those defined in the ``env`` field.
70
+ A non-fresh environment will contain all environment variables of
71
+ the current process, as well as any variables defined in the
72
+ ``env`` field.
73
+ If left unspecified, defaults to False.
74
+ """),
66
75
  )
67
76
 
68
77
  script: Script = Field(
69
- default="",
70
- description="""
71
- A shell command or a path to an executable file to run on plugin
72
- invocation.
73
- """.strip(),
78
+ "",
79
+ description=dedent("""
80
+ A shell command or a path to an executable file to run on plugin
81
+ invocation.
82
+ """),
83
+ exclude_if=bool,
74
84
  )
75
85
 
76
86
  command: str | click.Command = Field(
77
87
  default="",
78
88
  pattern=r"(((((\w+)(.|/))*)(\w+))(:(\w+))?)?",
79
- description="""
80
- A path to a python module or symbol that will be called upon
81
- plugin invocation specified in the form of
82
- `<module_path/file_path>[:<symbol_name>]`.
83
- """.strip(),
84
- )
85
-
86
- help: str = Field(
87
- default="",
88
- description="""
89
- Description of what the plugin does to be printed during plugin
90
- invocation if any of -h or --help flags were passed with the
91
- command.
92
- """.strip(),
89
+ exclude_if=bool,
90
+ description=dedent("""
91
+ A path to a python module or symbol that will be called upon
92
+ plugin invocation specified in the form of
93
+ `<module_path/file_path>[:<symbol_name>]`.
94
+ """),
93
95
  )
94
96
 
95
97
  panel: str = Field(
96
98
  default="Plugins",
97
- description="""
99
+ description=dedent("""
98
100
  Custom panel name in which plugin help text will be displayed when
99
101
  CLI is invoked with the -h/--help flag.
100
- """.strip(),
102
+ """),
101
103
  )
102
104
 
103
105
  epilog: str = Field(
104
106
  default="",
105
- description="""
106
- Help string printed at the end of the help page after everything
107
- else.
108
- """.strip(),
107
+ description=dedent("""
108
+ Help string printed at the end of the help page after everything
109
+ else.
110
+ """),
109
111
  )
110
112
 
111
113
  aliases: tuple[str, ...] = Field(
@@ -118,12 +120,6 @@ class PluginModel(BaseModel):
118
120
  description="The short help to use for this command",
119
121
  )
120
122
 
121
- model_config = ConfigDict(
122
- extra="allow",
123
- from_attributes=True,
124
- arbitrary_types_allowed=True,
125
- )
126
-
127
123
  def is_script(self) -> bool:
128
124
  return bool(self.script)
129
125
 
@@ -13,6 +13,8 @@ from pydantic import (
13
13
  GetJsonSchemaHandler,
14
14
  BeforeValidator,
15
15
  PlainSerializer,
16
+ ConfigDict,
17
+ BaseModel,
16
18
  )
17
19
 
18
20
 
@@ -232,3 +234,11 @@ Script = Annotated[
232
234
  BeforeValidator(validate_script),
233
235
  PlainSerializer(str, str),
234
236
  ]
237
+
238
+
239
+ class Model(BaseModel):
240
+ model_config = ConfigDict(
241
+ extra="allow",
242
+ from_attributes=True,
243
+ arbitrary_types_allowed=True,
244
+ )
@@ -121,7 +121,7 @@ _console_cv = contextvars.ContextVar[Console]("console")
121
121
 
122
122
  _console_cv.set(_console)
123
123
 
124
- console: ConsoleProxy = LocalProxy( # type: ignore[assignment]
124
+ console: ConsoleProxy = LocalProxy(
125
125
  _console_cv,
126
126
  unbound_message="""
127
127
  Working outside of application context.
@@ -129,4 +129,4 @@ console: ConsoleProxy = LocalProxy( # type: ignore[assignment]
129
129
  Attempted to use functionality that expected a current application to
130
130
  be set. To solve this, set up an app context.
131
131
  """,
132
- )
132
+ ) # ty:ignore[invalid-assignment]
@@ -139,26 +139,26 @@ def _get_file_handler(
139
139
  tracebacks_suppress: Iterable[ModuleType] | None = None,
140
140
  tracebacks_show_locals: bool = True,
141
141
  ) -> logging.Handler:
142
- import atexit
143
142
 
144
143
  def close_if_open(file: IO) -> None:
145
144
  if not file.closed:
146
145
  file.close()
147
146
 
148
147
  mode = mode or "a"
149
- file = open(path, mode=mode) # noqa: SIM115
150
- atexit.register(close_if_open, file)
151
- handler = _get_console_handler(
152
- file=file,
153
- level=level,
154
- stderr=stderr,
155
- tab_size=tab_size,
156
- tracebacks=tracebacks,
157
- force_terminal=force_terminal,
158
- tracebacks_theme=tracebacks_theme,
159
- tracebacks_suppress=tracebacks_suppress,
160
- tracebacks_show_locals=tracebacks_show_locals,
161
- )
148
+ # file = open(path, mode=mode)
149
+ # atexit.register(close_if_open, file)
150
+ # handler = _get_console_handler(
151
+ # file=file,
152
+ # level=level,
153
+ # stderr=stderr,
154
+ # tab_size=tab_size,
155
+ # tracebacks=tracebacks,
156
+ # force_terminal=force_terminal,
157
+ # tracebacks_theme=tracebacks_theme,
158
+ # tracebacks_suppress=tracebacks_suppress,
159
+ # tracebacks_show_locals=tracebacks_show_locals,
160
+ # )
161
+ handler = logging.handlers.WatchedFileHandler(path, mode)
162
162
  handler.name = "file"
163
163
  handler.setFormatter(DEFAULT_CHILD_FORMATTER)
164
164
  return handler