bakefile 0.0.4__py3-none-any.whl → 0.0.9__py3-none-any.whl

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 (78) hide show
  1. bake/__init__.py +9 -0
  2. bake/bakebook/bakebook.py +85 -0
  3. bake/bakebook/decorator.py +50 -0
  4. bake/bakebook/get.py +175 -0
  5. bake/cli/bake/__init__.py +3 -0
  6. bake/cli/bake/__main__.py +5 -0
  7. bake/cli/bake/main.py +74 -0
  8. bake/cli/bake/reinvocation.py +63 -0
  9. bake/cli/bakefile/__init__.py +3 -0
  10. bake/cli/bakefile/__main__.py +5 -0
  11. bake/cli/bakefile/add_inline.py +29 -0
  12. bake/cli/bakefile/export.py +212 -0
  13. bake/cli/bakefile/find_python.py +18 -0
  14. bake/cli/bakefile/init.py +56 -0
  15. bake/cli/bakefile/lint.py +77 -0
  16. bake/cli/bakefile/main.py +43 -0
  17. bake/cli/bakefile/uv.py +146 -0
  18. bake/cli/common/app.py +54 -0
  19. bake/cli/common/callback.py +13 -0
  20. bake/cli/common/context.py +145 -0
  21. bake/cli/common/exception_handler.py +57 -0
  22. bake/cli/common/obj.py +216 -0
  23. bake/cli/common/params.py +72 -0
  24. bake/cli/utils/__init__.py +0 -0
  25. bake/cli/utils/version.py +18 -0
  26. bake/manage/__init__.py +0 -0
  27. bake/manage/add_inline.py +71 -0
  28. bake/manage/find_python.py +210 -0
  29. bake/manage/lint.py +101 -0
  30. bake/manage/run_uv.py +88 -0
  31. bake/manage/write_bakefile.py +20 -0
  32. bake/py.typed +0 -0
  33. bake/samples/__init__.py +0 -0
  34. bake/samples/simple.py +8 -0
  35. bake/ui/__init__.py +11 -0
  36. bake/ui/console.py +58 -0
  37. bake/ui/logger/__init__.py +33 -0
  38. bake/ui/logger/capsys.py +158 -0
  39. bake/ui/logger/setup.py +53 -0
  40. bake/ui/logger/utils.py +215 -0
  41. bake/ui/params.py +5 -0
  42. bake/ui/run/__init__.py +5 -0
  43. bake/ui/run/run.py +546 -0
  44. bake/ui/run/script.py +74 -0
  45. bake/ui/run/splitter.py +257 -0
  46. bake/ui/run/uv.py +83 -0
  47. bake/ui/style.py +2 -0
  48. bake/utils/__init__.py +11 -0
  49. bake/utils/constants.py +21 -0
  50. {bakefile → bake/utils}/env.py +3 -1
  51. bake/utils/exceptions.py +17 -0
  52. {bakefile-0.0.4.dist-info → bakefile-0.0.9.dist-info}/METADATA +16 -2
  53. bakefile-0.0.9.dist-info/RECORD +68 -0
  54. {bakefile-0.0.4.dist-info → bakefile-0.0.9.dist-info}/WHEEL +2 -2
  55. bakefile-0.0.9.dist-info/entry_points.txt +5 -0
  56. bakelib/__init__.py +23 -0
  57. bakelib/environ/__init__.py +14 -0
  58. bakelib/environ/bakebook.py +30 -0
  59. bakelib/environ/base.py +112 -0
  60. bakelib/environ/get_bakebook.py +49 -0
  61. bakelib/environ/presets.py +70 -0
  62. bakelib/space/__init__.py +0 -0
  63. bakelib/space/base.py +193 -0
  64. bakelib/space/python.py +89 -0
  65. bakelib/space/utils.py +118 -0
  66. bakefile/__init__.py +0 -13
  67. bakefile/cli/bake/__init__.py +0 -3
  68. bakefile/cli/bake/main.py +0 -127
  69. bakefile/cli/bake/resolve_bakebook.py +0 -103
  70. bakefile/cli/bake/utils.py +0 -25
  71. bakefile/cli/bakefile.py +0 -19
  72. bakefile/cli/utils/version.py +0 -9
  73. bakefile/exceptions.py +0 -9
  74. bakefile-0.0.4.dist-info/RECORD +0 -16
  75. bakefile-0.0.4.dist-info/entry_points.txt +0 -4
  76. {bakefile/cli/utils → bake/bakebook}/__init__.py +0 -0
  77. {bakefile → bake}/cli/__init__.py +0 -0
  78. /bakefile/py.typed → /bake/cli/common/__init__.py +0 -0
@@ -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)
@@ -0,0 +1,18 @@
1
+ import typer
2
+
3
+ from bake.cli.common.context import Context
4
+ from bake.manage.find_python import find_python_path
5
+ from bake.ui import console
6
+ from bake.utils.exceptions import PythonNotFoundError
7
+
8
+
9
+ def find_python(
10
+ ctx: Context,
11
+ ) -> None:
12
+ """Find the Python interpreter path for the bakefile.py project."""
13
+ try:
14
+ python_path = find_python_path(ctx.obj.bakefile_path)
15
+ console.echo(python_path.as_posix())
16
+ except PythonNotFoundError as e:
17
+ console.error(str(e))
18
+ raise typer.Exit(code=1) from None
@@ -0,0 +1,56 @@
1
+ from typing import Annotated
2
+
3
+ import typer
4
+
5
+ from bake.bakebook.get import (
6
+ resolve_bakefile_path,
7
+ )
8
+ from bake.cli.common.context import Context
9
+ from bake.cli.common.params import force_option
10
+ from bake.manage.add_inline import add_inline_metadata
11
+ from bake.manage.write_bakefile import write_bakefile
12
+ from bake.samples import simple
13
+ from bake.ui import console
14
+ from bake.utils.exceptions import BakebookError
15
+
16
+
17
+ def init(
18
+ ctx: Context,
19
+ force: force_option = False,
20
+ inline: Annotated[
21
+ bool, typer.Option("--inline", "-i", help="Create bakefile with PEP 723 inline metadata")
22
+ ] = False,
23
+ ) -> None:
24
+ """Create a new bakefile.py in the current directory."""
25
+
26
+ if ctx.obj.bakebook is not None and not force:
27
+ console.error("Bakebook already loaded. Use --force to override.")
28
+ raise typer.Exit(code=1)
29
+
30
+ ctx.obj.bakefile_path = resolve_bakefile_path(chdir=ctx.obj.chdir, file_name=ctx.obj.file_name)
31
+
32
+ if ctx.obj.bakefile_path.exists() and not force:
33
+ console.error(f"File already exists at {ctx.obj.bakefile_path}. Use --force to overwrite.")
34
+ raise typer.Exit(code=1)
35
+
36
+ write_bakefile(
37
+ bakefile_path=ctx.obj.bakefile_path,
38
+ bakebook_name=ctx.obj.bakebook_name,
39
+ sample_module=simple,
40
+ )
41
+ ctx.obj.get_bakebook(allow_missing=False)
42
+ assert ctx.obj.bakebook is not None
43
+
44
+ if inline:
45
+ try:
46
+ add_inline_metadata(ctx.obj.bakefile_path)
47
+ except BakebookError as e:
48
+ console.error(f"Failed to add PEP 723 metadata: {e}")
49
+ raise typer.Exit(code=1) from None
50
+
51
+ console.success(
52
+ f"Successfully created bakefile with PEP 723 metadata at {ctx.obj.bakefile_path}"
53
+ )
54
+ return
55
+
56
+ console.success(f"Successfully created bakefile at {ctx.obj.bakefile_path}")
@@ -0,0 +1,77 @@
1
+ from typing import Annotated
2
+
3
+ import typer
4
+
5
+ from bake.cli.common.context import Context
6
+ from bake.manage.find_python import find_python_path
7
+ from bake.manage.lint import run_ruff_check, run_ruff_format, run_ty_check
8
+ from bake.ui import console
9
+
10
+
11
+ def lint(
12
+ ctx: Context,
13
+ only_bakefile: Annotated[
14
+ bool,
15
+ typer.Option("--only-bakefile", "-b", help="Only lint the bakefile, not entire project"),
16
+ ] = False,
17
+ ruff_format: Annotated[
18
+ bool,
19
+ typer.Option("--ruff-format/--no-ruff-format", show_default=False),
20
+ ] = True,
21
+ ruff_check: Annotated[
22
+ bool,
23
+ typer.Option("--ruff-check/--no-ruff-check", show_default=False),
24
+ ] = True,
25
+ ty_check: Annotated[
26
+ bool,
27
+ typer.Option("--ty/--no-ty", show_default=False),
28
+ ] = True,
29
+ ) -> None:
30
+ """
31
+ Quick and strict lint your bakefile.py.
32
+
33
+ Simple way to ensure your bakefile follows standard formatting
34
+ and type safety best practices. By default, also lints all Python
35
+ files in your project. For advanced linter configuration, use
36
+ ruff and ty directly.
37
+
38
+ By default, runs: ruff format, ruff check, ty check
39
+
40
+ Examples:
41
+ bakefile lint # Lint bakefile.py and all Python files
42
+ bakefile lint -b # Lint only bakefile.py
43
+ bakefile lint --no-ty # Skip type checking
44
+ """
45
+ bakefile_path = ctx.obj.bakefile_path
46
+ if bakefile_path is None or not bakefile_path.exists():
47
+ console.error("Bakefile not found. Run 'bakefile init' first.")
48
+ raise typer.Exit(code=1)
49
+
50
+ if not any([ruff_format, ruff_check, ty_check]):
51
+ console.warning("All linters disabled. Nothing to do.")
52
+ raise typer.Exit(code=0)
53
+
54
+ try:
55
+ if ruff_format:
56
+ run_ruff_format(
57
+ bakefile_path, only_bakefile=only_bakefile, check=True, dry_run=ctx.obj.dry_run
58
+ )
59
+
60
+ if ruff_check:
61
+ run_ruff_check(
62
+ bakefile_path, only_bakefile=only_bakefile, check=True, dry_run=ctx.obj.dry_run
63
+ )
64
+
65
+ if ty_check:
66
+ python_path = find_python_path(bakefile_path)
67
+ run_ty_check(
68
+ bakefile_path,
69
+ python_path,
70
+ only_bakefile=only_bakefile,
71
+ check=True,
72
+ dry_run=ctx.obj.dry_run,
73
+ )
74
+ except typer.Exit as e:
75
+ if e.exit_code != 0:
76
+ console.error("Linting failed.")
77
+ raise typer.Exit(code=e.exit_code) from e
@@ -0,0 +1,43 @@
1
+ from bake.cli.common.app import (
2
+ BakefileApp,
3
+ add_completion,
4
+ bake_app_callback_with_obj,
5
+ rich_markup_mode,
6
+ )
7
+ from bake.cli.common.obj import get_bakefile_object
8
+
9
+ from . import uv
10
+ from .add_inline import add_inline
11
+ from .export import export
12
+ from .find_python import find_python
13
+ from .init import init
14
+ from .lint import lint
15
+
16
+
17
+ def main():
18
+ bakefile_obj = get_bakefile_object(rich_markup_mode=rich_markup_mode)
19
+ bakefile_obj.setup_logging()
20
+ bakefile_obj.resolve_bakefile_path()
21
+
22
+ bakefile_app = BakefileApp(
23
+ add_completion=add_completion,
24
+ rich_markup_mode=rich_markup_mode,
25
+ )
26
+
27
+ uv_commands_context_settings = {
28
+ "allow_extra_args": True,
29
+ "ignore_unknown_options": True,
30
+ }
31
+
32
+ bakefile_app.callback(invoke_without_command=True)(bake_app_callback_with_obj(obj=bakefile_obj))
33
+ bakefile_app.command()(init)
34
+ bakefile_app.command()(add_inline)
35
+ bakefile_app.command()(find_python)
36
+ bakefile_app.command()(lint)
37
+ bakefile_app.command()(export)
38
+ bakefile_app.command(context_settings=uv_commands_context_settings)(uv.sync)
39
+ bakefile_app.command(context_settings=uv_commands_context_settings)(uv.lock)
40
+ bakefile_app.command(context_settings=uv_commands_context_settings)(uv.add)
41
+ bakefile_app.command(context_settings=uv_commands_context_settings)(uv.pip)
42
+ bakefile_app.bakefile_object = bakefile_obj
43
+ bakefile_app()
@@ -0,0 +1,146 @@
1
+ from typing import Annotated, Literal
2
+
3
+ import typer
4
+
5
+ from bake.cli.common.context import Context
6
+ from bake.manage.run_uv import run_uv_add, run_uv_lock, run_uv_pip, run_uv_sync
7
+ from bake.ui import console
8
+ from bake.utils.exceptions import BakebookError, PythonNotFoundError
9
+
10
+ PipCommand = Literal[
11
+ "compile",
12
+ "sync",
13
+ "install",
14
+ "uninstall",
15
+ "freeze",
16
+ "list",
17
+ "show",
18
+ "tree",
19
+ "check",
20
+ ]
21
+
22
+
23
+ def pip(
24
+ ctx: Context,
25
+ command: Annotated[
26
+ PipCommand,
27
+ typer.Argument(help="UV pip subcommand"),
28
+ ],
29
+ ) -> None:
30
+ """
31
+ This runs `[bold cyan]uv pip <command> <args> --python <bakefile-python-path>[/bold cyan]`
32
+
33
+ For complete docs: `[cyan]uv pip --help[/cyan]`
34
+ """
35
+ bakefile_path = ctx.obj.bakefile_path
36
+ try:
37
+ cmd = [command, *ctx.args]
38
+ result = run_uv_pip(bakefile_path=bakefile_path, cmd=cmd, dry_run=ctx.obj.dry_run)
39
+ raise typer.Exit(result.returncode)
40
+ except (PythonNotFoundError, BakebookError) as e:
41
+ console.error(str(e))
42
+ raise typer.Exit(code=1) from None
43
+
44
+
45
+ def add(ctx: Context) -> None:
46
+ """
47
+ This runs `[bold cyan]uv add --script bakefile.py <args>[/bold cyan]`
48
+
49
+ Requires PEP 723 inline metadata.
50
+
51
+ To add metadata: `[cyan]bakefile add-inline[/cyan]`
52
+ For project-level deps: `[cyan]uv add[/cyan]`
53
+ For complete docs: `[cyan]uv add --help[/cyan]`
54
+
55
+ Examples:
56
+ bakefile add requests typer
57
+ bakefile add "requests>=2.32.0" --dev
58
+ """
59
+ bakefile_path = ctx.obj.bakefile_path
60
+ args = ctx.args
61
+
62
+ try:
63
+ result = run_uv_add(bakefile_path=bakefile_path, cmd=args, dry_run=ctx.obj.dry_run)
64
+ raise typer.Exit(result.returncode)
65
+ except (PythonNotFoundError, BakebookError) as e:
66
+ console.error(str(e))
67
+ raise typer.Exit(code=1) from None
68
+
69
+
70
+ def lock(
71
+ ctx: Context,
72
+ upgrade: Annotated[
73
+ bool,
74
+ typer.Option("--upgrade", "-U", help="Upgrade package dependencies"),
75
+ ] = False,
76
+ ) -> None:
77
+ """
78
+ This runs `[bold cyan]uv lock --script bakefile.py <args>[/bold cyan]`
79
+
80
+ Requires PEP 723 inline metadata.
81
+
82
+ To add metadata: `[cyan]bakefile add-inline[/cyan]`
83
+ For project-level deps: `[cyan]uv lock[/cyan]`
84
+ For complete docs: `[cyan]uv lock --help[/cyan]`
85
+
86
+ Examples:
87
+ bakefile lock
88
+ bakefile lock --upgrade
89
+ bakefile lock --no-build
90
+ """
91
+ bakefile_path = ctx.obj.bakefile_path
92
+ args = list(ctx.args)
93
+
94
+ if upgrade:
95
+ args.append("--upgrade")
96
+
97
+ try:
98
+ result = run_uv_lock(bakefile_path=bakefile_path, cmd=args, dry_run=ctx.obj.dry_run)
99
+ raise typer.Exit(result.returncode)
100
+ except (PythonNotFoundError, BakebookError) as e:
101
+ console.error(str(e))
102
+ raise typer.Exit(code=1) from None
103
+
104
+
105
+ def sync(
106
+ ctx: Context,
107
+ upgrade: Annotated[
108
+ bool,
109
+ typer.Option("--upgrade", "-U", help="Upgrade package dependencies"),
110
+ ] = False,
111
+ reinstall: Annotated[
112
+ bool,
113
+ typer.Option("--reinstall", help="Reinstall all packages"),
114
+ ] = False,
115
+ ) -> None:
116
+ """
117
+ This runs `[bold cyan]uv sync --script bakefile.py <args>[/bold cyan]`
118
+
119
+ Requires PEP 723 inline metadata.
120
+
121
+ To add metadata: `[cyan]bakefile add-inline[/cyan]`
122
+ For project-level deps: `[cyan]uv sync[/cyan]`
123
+ For complete docs: `[cyan]uv sync --help[/cyan]`
124
+
125
+ Examples:
126
+ bakefile sync
127
+ bakefile sync --upgrade
128
+ bakefile sync --reinstall
129
+ bakefile sync --frozen
130
+ bakefile sync --no-dev
131
+ bakefile sync --no-build
132
+ """
133
+ bakefile_path = ctx.obj.bakefile_path
134
+ args = list(ctx.args)
135
+
136
+ if upgrade:
137
+ args.append("--upgrade")
138
+ if reinstall:
139
+ args.append("--reinstall")
140
+
141
+ try:
142
+ result = run_uv_sync(bakefile_path=bakefile_path, cmd=args, dry_run=ctx.obj.dry_run)
143
+ raise typer.Exit(result.returncode)
144
+ except (PythonNotFoundError, BakebookError) as e:
145
+ console.error(str(e))
146
+ raise typer.Exit(code=1) from None
bake/cli/common/app.py ADDED
@@ -0,0 +1,54 @@
1
+ from collections.abc import Callable
2
+
3
+ import typer
4
+ from typer.core import MarkupMode
5
+
6
+ from bake.cli.common.context import Context
7
+ from bake.cli.common.params import (
8
+ bakebook_name_option,
9
+ chdir_option,
10
+ dry_run_option,
11
+ file_name_option,
12
+ is_chain_commands_option,
13
+ verbosity_option,
14
+ version_option,
15
+ )
16
+ from bake.ui import console
17
+ from bake.utils.constants import (
18
+ DEFAULT_BAKEBOOK_NAME,
19
+ DEFAULT_CHDIR,
20
+ DEFAULT_FILE_NAME,
21
+ DEFAULT_IS_CHAIN_COMMAND,
22
+ )
23
+
24
+ from .obj import BakefileObject
25
+
26
+ rich_markup_mode: MarkupMode = "rich" if not console.out.no_color else None
27
+ add_completion = True
28
+
29
+
30
+ class BakefileApp(typer.Typer):
31
+ bakefile_object: BakefileObject
32
+
33
+
34
+ def show_help_if_no_command(ctx: Context) -> None:
35
+ if ctx.invoked_subcommand is None:
36
+ console.echo(ctx.get_help())
37
+ raise typer.Exit(1)
38
+
39
+
40
+ def bake_app_callback_with_obj(obj: BakefileObject) -> Callable:
41
+ def bake_app_callback(
42
+ ctx: Context,
43
+ _chdir: chdir_option = DEFAULT_CHDIR,
44
+ _file_name: file_name_option = DEFAULT_FILE_NAME,
45
+ _bakebook_name: bakebook_name_option = DEFAULT_BAKEBOOK_NAME,
46
+ _version: version_option = False,
47
+ _is_chain_commands: is_chain_commands_option = DEFAULT_IS_CHAIN_COMMAND,
48
+ _verbosity: verbosity_option = 0,
49
+ _dry_run: dry_run_option = False,
50
+ ):
51
+ ctx.obj = obj
52
+ show_help_if_no_command(ctx)
53
+
54
+ return bake_app_callback
@@ -0,0 +1,13 @@
1
+ import typer
2
+
3
+
4
+ def validate_file_name(file_name: str) -> str:
5
+ if "/" in file_name or "\\" in file_name:
6
+ raise typer.BadParameter(f"File name must not contain path separators: {file_name}")
7
+ if not file_name.endswith(".py"):
8
+ raise typer.BadParameter(f"File name must end with .py: {file_name}")
9
+ return file_name
10
+
11
+
12
+ def validate_file_name_callback(value: str) -> str:
13
+ return validate_file_name(file_name=value)