bakefile 0.0.5__tar.gz → 0.0.7__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 (67) hide show
  1. {bakefile-0.0.5 → bakefile-0.0.7}/PKG-INFO +2 -1
  2. {bakefile-0.0.5 → bakefile-0.0.7}/pyproject.toml +9 -8
  3. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/__init__.py +2 -2
  4. bakefile-0.0.7/src/bake/cli/bakefile/export.py +212 -0
  5. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/cli/bakefile/main.py +2 -0
  6. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/cli/common/app.py +1 -1
  7. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/cli/common/context.py +33 -33
  8. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/cli/common/obj.py +3 -1
  9. bakefile-0.0.7/src/bake/manage/write_bakefile.py +39 -0
  10. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/samples/simple.py +1 -2
  11. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/ui/__init__.py +2 -1
  12. bakefile-0.0.7/src/bake/ui/params.py +5 -0
  13. bakefile-0.0.7/src/bake/ui/run/__init__.py +5 -0
  14. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/ui/run/run.py +60 -55
  15. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/ui/run/splitter.py +13 -1
  16. bakefile-0.0.7/src/bakelib/space/base.py +193 -0
  17. bakefile-0.0.7/src/bakelib/space/python.py +80 -0
  18. bakefile-0.0.7/src/bakelib/space/utils.py +118 -0
  19. bakefile-0.0.5/src/bake/manage/write_bakefile.py +0 -20
  20. bakefile-0.0.5/src/bake/ui/run/__init__.py +0 -11
  21. bakefile-0.0.5/src/bakelib/space/base.py +0 -73
  22. bakefile-0.0.5/src/bakelib/space/python.py +0 -42
  23. bakefile-0.0.5/src/bakelib/space/utils.py +0 -55
  24. {bakefile-0.0.5 → bakefile-0.0.7}/README.md +0 -0
  25. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/bakebook/__init__.py +0 -0
  26. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/bakebook/bakebook.py +0 -0
  27. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/bakebook/decorator.py +0 -0
  28. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/bakebook/get.py +0 -0
  29. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/cli/__init__.py +0 -0
  30. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/cli/bake/__init__.py +0 -0
  31. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/cli/bake/__main__.py +0 -0
  32. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/cli/bake/main.py +0 -0
  33. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/cli/bake/reinvocation.py +0 -0
  34. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/cli/bakefile/__init__.py +0 -0
  35. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/cli/bakefile/__main__.py +0 -0
  36. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/cli/bakefile/add_inline.py +0 -0
  37. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/cli/bakefile/find_python.py +0 -0
  38. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/cli/bakefile/init.py +0 -0
  39. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/cli/bakefile/lint.py +0 -0
  40. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/cli/bakefile/uv.py +0 -0
  41. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/cli/common/__init__.py +0 -0
  42. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/cli/common/callback.py +0 -0
  43. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/cli/common/exception_handler.py +0 -0
  44. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/cli/common/params.py +0 -0
  45. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/cli/utils/__init__.py +0 -0
  46. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/cli/utils/version.py +0 -0
  47. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/manage/__init__.py +0 -0
  48. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/manage/add_inline.py +0 -0
  49. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/manage/find_python.py +0 -0
  50. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/manage/lint.py +0 -0
  51. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/manage/run_uv.py +0 -0
  52. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/py.typed +0 -0
  53. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/samples/__init__.py +0 -0
  54. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/ui/console.py +0 -0
  55. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/ui/logger/__init__.py +0 -0
  56. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/ui/logger/capsys.py +0 -0
  57. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/ui/logger/setup.py +0 -0
  58. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/ui/logger/utils.py +0 -0
  59. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/ui/run/script.py +0 -0
  60. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/ui/run/uv.py +0 -0
  61. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/ui/style.py +0 -0
  62. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/utils/__init__.py +0 -0
  63. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/utils/constants.py +0 -0
  64. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/utils/env.py +0 -0
  65. {bakefile-0.0.5 → bakefile-0.0.7}/src/bake/utils/exceptions.py +0 -0
  66. {bakefile-0.0.5 → bakefile-0.0.7}/src/bakelib/__init__.py +0 -0
  67. {bakefile-0.0.5 → bakefile-0.0.7}/src/bakelib/space/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bakefile
3
- Version: 0.0.5
3
+ Version: 0.0.7
4
4
  Summary: Add your description here
5
5
  Author: Wisaroot Lertthaweedech
6
6
  Author-email: Wisaroot Lertthaweedech <l.wisaroot@gmail.com>
@@ -10,6 +10,7 @@ Requires-Dist: loguru>=0.7.3
10
10
  Requires-Dist: orjson>=3.11.5
11
11
  Requires-Dist: pydantic-settings>=2.0.0
12
12
  Requires-Dist: pydantic>=2.12.5
13
+ Requires-Dist: pyyaml>=6.0.3
13
14
  Requires-Dist: rich>=14.2.0
14
15
  Requires-Dist: ruff>=0.14.10
15
16
  Requires-Dist: tomli>=2.0.0 ; python_full_version < '3.11'
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "bakefile"
3
- version = "0.0.5" # use git tag
3
+ version = "0.0.7" # use git tag
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -12,9 +12,9 @@ dependencies = [
12
12
  "click>=8.3.1",
13
13
  "loguru>=0.7.3",
14
14
  "orjson>=3.11.5",
15
- # "pathspec>=1.0.3",
16
15
  "pydantic-settings>=2.0.0",
17
16
  "pydantic>=2.12.5",
17
+ "pyyaml>=6.0.3",
18
18
  "rich>=14.2.0",
19
19
  "ruff>=0.14.10",
20
20
  "tomli>=2.0.0; python_version < '3.11'",
@@ -36,9 +36,12 @@ bf = "bake.cli.bakefile:main"
36
36
  [dependency-groups]
37
37
  dev = [
38
38
  "deptry>=0.24.0",
39
+ "pandas>=2.3.3",
39
40
  "pre-commit>=4.5.1",
40
41
  "pytest-cov>=7.0.0",
41
42
  "pytest>=9.0.2",
43
+ "python-dotenv>=1.2.1",
44
+ "python_package",
42
45
  "toml-sort>=0.24.3"
43
46
  ]
44
47
 
@@ -47,12 +50,7 @@ requires = ["uv_build>=0.9.18,<0.10.0"]
47
50
  build-backend = "uv_build"
48
51
 
49
52
  [tool.deptry]
50
- known_first_party = ["bake", "bakelib"]
51
-
52
- [tool.pytest.ini_options]
53
- markers = [
54
- "integration: marks tests as integration tests (slower, full flow tests)"
55
- ]
53
+ known_first_party = ["bake", "bakelib", "python_package"]
56
54
 
57
55
  [tool.ruff]
58
56
  line-length = 100
@@ -77,3 +75,6 @@ select = [
77
75
  [tool.uv.build-backend]
78
76
  module-name = ["bake", "bakelib"]
79
77
  module-root = "src"
78
+
79
+ [tool.uv.sources]
80
+ python-package = {path = "examples/python-package", editable = true}
@@ -2,8 +2,8 @@ from bake.bakebook.bakebook import Bakebook
2
2
  from bake.bakebook.decorator import command
3
3
  from bake.cli.common.context import BakeCommand, Context
4
4
  from bake.cli.utils.version import _get_version
5
- from bake.ui import console
5
+ from bake.ui import console, params
6
6
 
7
7
  __version__ = _get_version()
8
8
 
9
- __all__ = ["BakeCommand", "Bakebook", "Context", "__version__", "command", "console"]
9
+ __all__ = ["BakeCommand", "Bakebook", "Context", "__version__", "command", "console", "params"]
@@ -0,0 +1,212 @@
1
+ import shlex
2
+ from collections.abc import Callable, Hashable
3
+ from pathlib import Path
4
+ from typing import Annotated, Any, Literal
5
+
6
+ import orjson
7
+ import typer
8
+ import yaml
9
+ from pydantic_settings import BaseSettings
10
+
11
+ from bake.cli.common.context import Context
12
+ from bake.ui import console
13
+
14
+ ExportFormat = Literal["sh", "dotenv", "json", "yaml"]
15
+ JsonValue = str | float | bool | None | list[Any] | dict[Hashable, Any]
16
+
17
+
18
+ def _format_shell_value(value: JsonValue) -> str:
19
+ """Format a value for shell export.
20
+
21
+ Expects JSON-serializable types (str, int, float, bool, None, list, dict).
22
+ Raises TypeError for unexpected types.
23
+
24
+ SecretStr values are masked for security.
25
+
26
+ Parameters
27
+ ----------
28
+ value : Any
29
+ The value to format for shell export
30
+
31
+ Returns
32
+ -------
33
+ str
34
+ Shell-formatted string ready for export
35
+
36
+ Raises
37
+ ------
38
+ TypeError
39
+ If value is not one of the expected types
40
+ """
41
+
42
+ if isinstance(value, (list, dict)):
43
+ # Complex types: JSON string, then shell-quote it
44
+ return shlex.quote(orjson.dumps(value).decode())
45
+ elif isinstance(value, str):
46
+ # Strings: shell-quote directly
47
+ return shlex.quote(value)
48
+ elif value is None:
49
+ # None becomes empty string
50
+ return ""
51
+ elif isinstance(value, bool):
52
+ # Booleans: lowercase true/false for shell compatibility
53
+ return str(value).lower()
54
+ elif isinstance(value, (int, float)):
55
+ # Numbers: convert to string, no quoting needed
56
+ return str(value)
57
+ raise TypeError(
58
+ f"Unexpected type for shell export: {type(value).__name__}. "
59
+ f"Expected one of: str, int, float, bool, None, list, dict"
60
+ )
61
+
62
+
63
+ def _format_dotenv_value(value: JsonValue) -> str:
64
+ """Format a value for dotenv export.
65
+
66
+ Uses smart quote selection to produce valid dotenv format that
67
+ python-dotenv's parser can handle.
68
+
69
+ Parameters
70
+ ----------
71
+ value : JsonValue
72
+ The value to format for dotenv export
73
+
74
+ Returns
75
+ -------
76
+ str
77
+ Dotenv-formatted string ready for export
78
+
79
+ Raises
80
+ ------
81
+ TypeError
82
+ If value is not one of the expected types
83
+ """
84
+ if isinstance(value, (list, dict)):
85
+ # Complex types: JSON string, then wrap in double quotes
86
+ json_str = orjson.dumps(value).decode()
87
+ return '"' + json_str.replace("\\", "\\\\").replace('"', '\\"') + '"'
88
+ elif isinstance(value, str):
89
+ # Strings: use smart quote selection
90
+ if value.isalnum():
91
+ return value
92
+ if "'" in value and '"' not in value:
93
+ # Has single quotes only: use double quotes
94
+ return f'"{value}"'
95
+ if '"' in value and "'" not in value:
96
+ # Has double quotes only: use single quotes
97
+ return f"'{value}'"
98
+ # Has both or special chars: use double quotes with escaping
99
+ return '"' + value.replace("\\", "\\\\").replace('"', '\\"') + '"'
100
+ elif value is None:
101
+ return ""
102
+ elif isinstance(value, bool):
103
+ return str(value).lower()
104
+ elif isinstance(value, (int, float)):
105
+ return str(value)
106
+ raise TypeError(
107
+ f"Unexpected type for dotenv export: {type(value).__name__}. "
108
+ f"Expected one of: str, int, float, bool, None, list, dict"
109
+ )
110
+
111
+
112
+ def _format_vars(data: dict, value_formatter: Callable[[JsonValue], str], prefix: str = "") -> str:
113
+ lines: list[str] = []
114
+ for field_name, value in data.items():
115
+ formatted_val = value_formatter(value)
116
+ lines.append(f"{prefix}{field_name.upper()}={formatted_val}")
117
+ return "\n".join(lines)
118
+
119
+
120
+ class ExportFormatter:
121
+ def __call__(self, data: dict[str, Any]) -> str:
122
+ raise NotImplementedError("....")
123
+
124
+
125
+ class ShExportFormatter(ExportFormatter):
126
+ def __call__(self, data: dict[str, Any]) -> str:
127
+ return _format_vars(data, value_formatter=_format_shell_value, prefix="export ")
128
+
129
+
130
+ class DotEnvExportFormatter(ExportFormatter):
131
+ def __call__(self, data: dict[str, Any]) -> str:
132
+ return _format_vars(data, value_formatter=_format_dotenv_value, prefix="")
133
+
134
+
135
+ class JsonExportFormatter(ExportFormatter):
136
+ def __call__(self, data: dict[str, Any]) -> str:
137
+ return orjson.dumps(data, option=orjson.OPT_INDENT_2).decode()
138
+
139
+
140
+ class YamlExportFormatter(ExportFormatter):
141
+ def __call__(self, data: dict[str, Any]) -> str:
142
+ return yaml.dump(data, default_flow_style=False, sort_keys=False)
143
+
144
+
145
+ def _export(
146
+ bakebook: BaseSettings,
147
+ format: ExportFormat = "sh",
148
+ output: Path | None = None,
149
+ ) -> None:
150
+ formatters: dict[str, ExportFormatter] = {
151
+ "sh": ShExportFormatter(),
152
+ "dotenv": DotEnvExportFormatter(),
153
+ "json": JsonExportFormatter(),
154
+ "yaml": YamlExportFormatter(),
155
+ }
156
+
157
+ formatter = formatters.get(format)
158
+ if formatter is None:
159
+ raise ValueError(f"Unknown format: {format}")
160
+
161
+ data: dict[str, Any] = bakebook.model_dump(mode="json")
162
+ content = formatter(data)
163
+
164
+ if output:
165
+ output.parent.mkdir(parents=True, exist_ok=True)
166
+ output.write_text(content, encoding="utf-8")
167
+ elif content != "":
168
+ console.echo(content, overflow="ignore", crop=False)
169
+
170
+
171
+ def export(
172
+ ctx: Context,
173
+ format: Annotated[
174
+ ExportFormat,
175
+ typer.Option(
176
+ "--format",
177
+ "-f",
178
+ help="Output format",
179
+ ),
180
+ ] = "sh",
181
+ output: Annotated[
182
+ Path | None,
183
+ typer.Option(
184
+ "--output",
185
+ "-o",
186
+ help="Output file path (default: stdout)",
187
+ exists=False,
188
+ ),
189
+ ] = None,
190
+ ) -> None:
191
+ """Export bakebook args to external formats.
192
+
193
+ Export Pydantic-validated bakebook args to various formats for use
194
+ outside Python runtime (shell scripts, GitHub Actions, .env files, etc.).
195
+
196
+ Examples:
197
+ # Export to shell for eval
198
+ bakefile export --format sh
199
+
200
+ # Export to dotenv file
201
+ bakefile export --format dotenv --output .env
202
+
203
+ # Export to JSON
204
+ bakefile export --format json --output config.json
205
+ """
206
+ if ctx.obj.bakebook is None:
207
+ ctx.obj.get_bakebook(allow_missing=False)
208
+
209
+ if ctx.obj.bakebook is None:
210
+ raise RuntimeError("Bakebook not found.")
211
+
212
+ _export(bakebook=ctx.obj.bakebook, format=format, output=output)
@@ -8,6 +8,7 @@ from bake.cli.common.obj import get_bakefile_object
8
8
 
9
9
  from . import uv
10
10
  from .add_inline import add_inline
11
+ from .export import export
11
12
  from .find_python import find_python
12
13
  from .init import init
13
14
  from .lint import lint
@@ -33,6 +34,7 @@ def main():
33
34
  bakefile_app.command()(add_inline)
34
35
  bakefile_app.command()(find_python)
35
36
  bakefile_app.command()(lint)
37
+ bakefile_app.command()(export)
36
38
  bakefile_app.command(context_settings=uv_commands_context_settings)(uv.sync)
37
39
  bakefile_app.command(context_settings=uv_commands_context_settings)(uv.lock)
38
40
  bakefile_app.command(context_settings=uv_commands_context_settings)(uv.add)
@@ -24,7 +24,7 @@ from bake.utils.constants import (
24
24
  from .obj import BakefileObject
25
25
 
26
26
  rich_markup_mode: MarkupMode = "rich" if not console.out.no_color else None
27
- add_completion = False
27
+ add_completion = True
28
28
 
29
29
 
30
30
  class BakefileApp(typer.Typer):
@@ -1,4 +1,6 @@
1
1
  import subprocess
2
+ from collections.abc import Generator
3
+ from contextlib import contextmanager
2
4
  from pathlib import Path
3
5
  from typing import TYPE_CHECKING, Literal, overload
4
6
 
@@ -6,6 +8,7 @@ import click
6
8
  import typer
7
9
  from typer.core import TyperCommand
8
10
 
11
+ from bake.ui.run import CmdType
9
12
  from bake.ui.run import run as _run
10
13
  from bake.ui.run.script import run_script as _run_script
11
14
 
@@ -22,6 +25,19 @@ class Context(typer.Context):
22
25
  def dry_run(self) -> bool:
23
26
  return self.obj.dry_run
24
27
 
28
+ @dry_run.setter
29
+ def dry_run(self, value: bool) -> None:
30
+ self.obj.dry_run = value
31
+
32
+ @contextmanager
33
+ def override_dry_run(self, dry_run: bool) -> Generator[None, None, None]:
34
+ original = self.obj.dry_run
35
+ self.obj.dry_run = dry_run
36
+ try:
37
+ yield
38
+ finally:
39
+ self.obj.dry_run = original
40
+
25
41
  @property
26
42
  def verbosity(self) -> int:
27
43
  return self.obj.verbosity
@@ -33,37 +49,7 @@ class Context(typer.Context):
33
49
  @overload
34
50
  def run(
35
51
  self,
36
- cmd: str,
37
- *,
38
- capture_output: Literal[True] = True,
39
- check: bool = True,
40
- cwd: Path | str | None = None,
41
- stream: bool = True,
42
- shell: bool | None = None,
43
- echo: bool = True,
44
- dry_run: bool | None = None,
45
- **kwargs,
46
- ) -> subprocess.CompletedProcess[str]: ...
47
-
48
- @overload
49
- def run(
50
- self,
51
- cmd: str,
52
- *,
53
- capture_output: Literal[False],
54
- check: bool = True,
55
- cwd: Path | str | None = None,
56
- stream: bool = True,
57
- shell: bool | None = None,
58
- echo: bool = True,
59
- dry_run: bool | None = None,
60
- **kwargs,
61
- ) -> subprocess.CompletedProcess[None]: ...
62
-
63
- @overload
64
- def run(
65
- self,
66
- cmd: list[str] | tuple[str, ...],
52
+ cmd: CmdType,
67
53
  *,
68
54
  capture_output: Literal[True] = True,
69
55
  check: bool = True,
@@ -72,13 +58,15 @@ class Context(typer.Context):
72
58
  shell: bool | None = None,
73
59
  echo: bool = True,
74
60
  dry_run: bool | None = None,
61
+ keep_temp_file: bool = False,
62
+ env: dict[str, str] | None = None,
75
63
  **kwargs,
76
64
  ) -> subprocess.CompletedProcess[str]: ...
77
65
 
78
66
  @overload
79
67
  def run(
80
68
  self,
81
- cmd: list[str] | tuple[str, ...],
69
+ cmd: CmdType,
82
70
  *,
83
71
  capture_output: Literal[False],
84
72
  check: bool = True,
@@ -87,12 +75,14 @@ class Context(typer.Context):
87
75
  shell: bool | None = None,
88
76
  echo: bool = True,
89
77
  dry_run: bool | None = None,
78
+ keep_temp_file: bool = False,
79
+ env: dict[str, str] | None = None,
90
80
  **kwargs,
91
81
  ) -> subprocess.CompletedProcess[None]: ...
92
82
 
93
83
  def run(
94
84
  self,
95
- cmd: str | list[str] | tuple[str, ...],
85
+ cmd: CmdType,
96
86
  *,
97
87
  capture_output: bool = True,
98
88
  check: bool = True,
@@ -101,6 +91,9 @@ class Context(typer.Context):
101
91
  shell: bool | None = None,
102
92
  echo: bool = True,
103
93
  dry_run: bool | None = None,
94
+ keep_temp_file: bool = False,
95
+ env: dict[str, str] | None = None,
96
+ _encoding: str | None = None,
104
97
  **kwargs,
105
98
  ) -> subprocess.CompletedProcess[str] | subprocess.CompletedProcess[None]:
106
99
  return _run(
@@ -112,6 +105,9 @@ class Context(typer.Context):
112
105
  shell=shell,
113
106
  echo=echo,
114
107
  dry_run=self.obj.dry_run if dry_run is None else dry_run,
108
+ keep_temp_file=keep_temp_file,
109
+ env=env,
110
+ _encoding=_encoding,
115
111
  **kwargs,
116
112
  )
117
113
 
@@ -126,6 +122,8 @@ class Context(typer.Context):
126
122
  stream: bool = True,
127
123
  echo: bool = True,
128
124
  dry_run: bool | None = None,
125
+ keep_temp_file: bool = False,
126
+ env: dict[str, str] | None = None,
129
127
  **kwargs,
130
128
  ) -> subprocess.CompletedProcess[str] | subprocess.CompletedProcess[None]:
131
129
  return _run_script(
@@ -137,6 +135,8 @@ class Context(typer.Context):
137
135
  stream=stream,
138
136
  echo=echo,
139
137
  dry_run=self.obj.dry_run if dry_run is None else dry_run,
138
+ keep_temp_file=keep_temp_file,
139
+ env=env,
140
140
  **kwargs,
141
141
  )
142
142
 
@@ -81,9 +81,11 @@ class BakefileObject:
81
81
  self.bakebook = get_bakebook_from_target_dir_path(
82
82
  target_dir_path=self.bakefile_path, bakebook_name=self.bakebook_name
83
83
  )
84
- except BakefileNotFoundError:
84
+ except BakefileNotFoundError as e:
85
85
  if allow_missing:
86
86
  return
87
+ console.error(str(e))
88
+ raise SystemExit(1) from e
87
89
  except BakebookError as e:
88
90
  if allow_missing:
89
91
  return
@@ -0,0 +1,39 @@
1
+ import types
2
+ from pathlib import Path
3
+
4
+ from bake.samples import simple
5
+ from bake.utils.constants import BAKEBOOK_NAME_IN_SAMPLES
6
+
7
+ # Allowed sample modules
8
+ # This dictionary acts as a whitelist for security - only these modules can be used
9
+ ALLOWED_SAMPLE_MODULES: dict[str, types.ModuleType] = {
10
+ simple.__name__: simple,
11
+ }
12
+
13
+
14
+ def write_bakefile(
15
+ bakefile_path: Path, bakebook_name: str, sample_module: types.ModuleType
16
+ ) -> None:
17
+ if not hasattr(sample_module, BAKEBOOK_NAME_IN_SAMPLES):
18
+ raise ValueError(
19
+ f"Module `{sample_module.__name__}` must have `{BAKEBOOK_NAME_IN_SAMPLES}` attribute"
20
+ )
21
+
22
+ module_name = sample_module.__name__
23
+ if module_name not in ALLOWED_SAMPLE_MODULES:
24
+ raise ValueError(
25
+ f"Module `{module_name}` is not in the allowed sample modules list. "
26
+ f"Allowed modules: {list(ALLOWED_SAMPLE_MODULES.keys())}"
27
+ )
28
+
29
+ allowed_module = ALLOWED_SAMPLE_MODULES[module_name]
30
+ if sample_module is not allowed_module:
31
+ raise ValueError(f"Module `{module_name}` does not match the allowed module object")
32
+
33
+ if allowed_module.__file__ is None:
34
+ raise ValueError(f"Could not find file for module `{module_name}`")
35
+
36
+ source_file_path = Path(allowed_module.__file__)
37
+ original_bakefile_content = source_file_path.read_text()
38
+ customized_content = original_bakefile_content.replace(BAKEBOOK_NAME_IN_SAMPLES, bakebook_name)
39
+ bakefile_path.write_text(customized_content)
@@ -1,5 +1,4 @@
1
- from bake.bakebook.bakebook import Bakebook
2
- from bake.ui import console
1
+ from bake import Bakebook, console
3
2
 
4
3
  __bakebook__ = Bakebook()
5
4
 
@@ -1,9 +1,10 @@
1
- from bake.ui import console
1
+ from bake.ui import console, params
2
2
  from bake.ui.logger.setup import setup_logging
3
3
  from bake.ui.run import run, run_uv
4
4
 
5
5
  __all__ = [
6
6
  "console",
7
+ "params",
7
8
  "run",
8
9
  "run_uv",
9
10
  "setup_logging",
@@ -0,0 +1,5 @@
1
+ from typing import Annotated
2
+
3
+ import typer
4
+
5
+ verbose_bool = Annotated[bool, typer.Option("-v", "--verbose", help="Run with verbose output")]
@@ -0,0 +1,5 @@
1
+ from bake.ui.run.run import CmdType, OutputSplitter, run
2
+ from bake.ui.run.script import run_script
3
+ from bake.ui.run.uv import run_uv
4
+
5
+ __all__ = ["CmdType", "OutputSplitter", "run", "run_script", "run_uv"]