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.
- bake/__init__.py +9 -0
- bake/bakebook/bakebook.py +85 -0
- bake/bakebook/decorator.py +50 -0
- bake/bakebook/get.py +175 -0
- bake/cli/bake/__init__.py +3 -0
- bake/cli/bake/__main__.py +5 -0
- bake/cli/bake/main.py +74 -0
- bake/cli/bake/reinvocation.py +63 -0
- bake/cli/bakefile/__init__.py +3 -0
- bake/cli/bakefile/__main__.py +5 -0
- bake/cli/bakefile/add_inline.py +29 -0
- bake/cli/bakefile/export.py +212 -0
- bake/cli/bakefile/find_python.py +18 -0
- bake/cli/bakefile/init.py +56 -0
- bake/cli/bakefile/lint.py +77 -0
- bake/cli/bakefile/main.py +43 -0
- bake/cli/bakefile/uv.py +146 -0
- bake/cli/common/app.py +54 -0
- bake/cli/common/callback.py +13 -0
- bake/cli/common/context.py +145 -0
- bake/cli/common/exception_handler.py +57 -0
- bake/cli/common/obj.py +216 -0
- bake/cli/common/params.py +72 -0
- bake/cli/utils/__init__.py +0 -0
- bake/cli/utils/version.py +18 -0
- bake/manage/__init__.py +0 -0
- bake/manage/add_inline.py +71 -0
- bake/manage/find_python.py +210 -0
- bake/manage/lint.py +101 -0
- bake/manage/run_uv.py +88 -0
- bake/manage/write_bakefile.py +20 -0
- bake/py.typed +0 -0
- bake/samples/__init__.py +0 -0
- bake/samples/simple.py +8 -0
- bake/ui/__init__.py +11 -0
- bake/ui/console.py +58 -0
- bake/ui/logger/__init__.py +33 -0
- bake/ui/logger/capsys.py +158 -0
- bake/ui/logger/setup.py +53 -0
- bake/ui/logger/utils.py +215 -0
- bake/ui/params.py +5 -0
- bake/ui/run/__init__.py +5 -0
- bake/ui/run/run.py +546 -0
- bake/ui/run/script.py +74 -0
- bake/ui/run/splitter.py +257 -0
- bake/ui/run/uv.py +83 -0
- bake/ui/style.py +2 -0
- bake/utils/__init__.py +11 -0
- bake/utils/constants.py +21 -0
- {bakefile → bake/utils}/env.py +3 -1
- bake/utils/exceptions.py +17 -0
- {bakefile-0.0.4.dist-info → bakefile-0.0.9.dist-info}/METADATA +16 -2
- bakefile-0.0.9.dist-info/RECORD +68 -0
- {bakefile-0.0.4.dist-info → bakefile-0.0.9.dist-info}/WHEEL +2 -2
- bakefile-0.0.9.dist-info/entry_points.txt +5 -0
- bakelib/__init__.py +23 -0
- bakelib/environ/__init__.py +14 -0
- bakelib/environ/bakebook.py +30 -0
- bakelib/environ/base.py +112 -0
- bakelib/environ/get_bakebook.py +49 -0
- bakelib/environ/presets.py +70 -0
- bakelib/space/__init__.py +0 -0
- bakelib/space/base.py +193 -0
- bakelib/space/python.py +89 -0
- bakelib/space/utils.py +118 -0
- bakefile/__init__.py +0 -13
- bakefile/cli/bake/__init__.py +0 -3
- bakefile/cli/bake/main.py +0 -127
- bakefile/cli/bake/resolve_bakebook.py +0 -103
- bakefile/cli/bake/utils.py +0 -25
- bakefile/cli/bakefile.py +0 -19
- bakefile/cli/utils/version.py +0 -9
- bakefile/exceptions.py +0 -9
- bakefile-0.0.4.dist-info/RECORD +0 -16
- bakefile-0.0.4.dist-info/entry_points.txt +0 -4
- {bakefile/cli/utils → bake/bakebook}/__init__.py +0 -0
- {bakefile → bake}/cli/__init__.py +0 -0
- /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()
|
bake/cli/bakefile/uv.py
ADDED
|
@@ -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)
|