bakefile 0.0.3__py3-none-any.whl → 0.0.5__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/find_python.py +18 -0
- bake/cli/bakefile/init.py +56 -0
- bake/cli/bakefile/lint.py +77 -0
- bake/cli/bakefile/main.py +41 -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 +214 -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 +9 -0
- bake/ui/__init__.py +10 -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/run/__init__.py +11 -0
- bake/ui/run/run.py +541 -0
- bake/ui/run/script.py +74 -0
- bake/ui/run/splitter.py +237 -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
- bake/utils/env.py +10 -0
- bake/utils/exceptions.py +17 -0
- {bakefile-0.0.3.dist-info → bakefile-0.0.5.dist-info}/METADATA +14 -2
- bakefile-0.0.5.dist-info/RECORD +61 -0
- {bakefile-0.0.3.dist-info → bakefile-0.0.5.dist-info}/WHEEL +1 -1
- bakefile-0.0.5.dist-info/entry_points.txt +5 -0
- bakelib/__init__.py +4 -0
- bakelib/space/__init__.py +0 -0
- bakelib/space/base.py +73 -0
- bakelib/space/python.py +42 -0
- bakelib/space/utils.py +55 -0
- bakefile/cli/bake/__init__.py +0 -3
- bakefile/cli/bake/main.py +0 -17
- bakefile/cli/bake/resolve_bakebook.py +0 -75
- bakefile/cli/bakefile.py +0 -8
- bakefile-0.0.3.dist-info/RECORD +0 -11
- bakefile-0.0.3.dist-info/entry_points.txt +0 -4
- {bakefile → 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,72 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Annotated
|
|
3
|
+
|
|
4
|
+
import typer
|
|
5
|
+
|
|
6
|
+
from bake.cli.common.callback import validate_file_name_callback
|
|
7
|
+
from bake.cli.utils.version import version_callback
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def verbosity_callback(_ctx: typer.Context, _param: typer.CallbackParam, value: int) -> int:
|
|
11
|
+
"""Validate verbosity level (max 2)."""
|
|
12
|
+
if value > 2:
|
|
13
|
+
raise typer.BadParameter("Maximum verbosity is -vv")
|
|
14
|
+
return value
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# ==========================================================
|
|
18
|
+
# Bakefile CLI Parameters
|
|
19
|
+
# ==========================================================
|
|
20
|
+
chdir_option = Annotated[
|
|
21
|
+
Path,
|
|
22
|
+
typer.Option(
|
|
23
|
+
"-C",
|
|
24
|
+
"--chdir",
|
|
25
|
+
help="Change directory before running",
|
|
26
|
+
),
|
|
27
|
+
]
|
|
28
|
+
file_name_option = Annotated[
|
|
29
|
+
str,
|
|
30
|
+
typer.Option(
|
|
31
|
+
"--file-name",
|
|
32
|
+
"-f",
|
|
33
|
+
help="Path to bakefile.py",
|
|
34
|
+
callback=validate_file_name_callback,
|
|
35
|
+
),
|
|
36
|
+
]
|
|
37
|
+
bakebook_name_option = Annotated[
|
|
38
|
+
str, typer.Option("--book-name", "-b", help="Name of bakebook object to retrieve")
|
|
39
|
+
]
|
|
40
|
+
version_option = Annotated[
|
|
41
|
+
bool,
|
|
42
|
+
typer.Option(
|
|
43
|
+
"--version",
|
|
44
|
+
help="Show version",
|
|
45
|
+
callback=version_callback,
|
|
46
|
+
is_eager=True,
|
|
47
|
+
),
|
|
48
|
+
]
|
|
49
|
+
is_chain_commands_option = Annotated[bool, typer.Option("--chain", "-c", help="Chain commands")]
|
|
50
|
+
remaining_args_argument = Annotated[list[str] | None, typer.Argument()]
|
|
51
|
+
|
|
52
|
+
verbosity_option = Annotated[
|
|
53
|
+
int,
|
|
54
|
+
typer.Option(
|
|
55
|
+
"-v",
|
|
56
|
+
"--verbose",
|
|
57
|
+
help="Increase verbosity (-v for info, -vv for debug)",
|
|
58
|
+
count=True,
|
|
59
|
+
callback=verbosity_callback,
|
|
60
|
+
),
|
|
61
|
+
]
|
|
62
|
+
dry_run_option = Annotated[
|
|
63
|
+
bool,
|
|
64
|
+
typer.Option("-n", "--dry-run", help="Dry run (show what would be done without executing)"),
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
# ==========================================================
|
|
68
|
+
# Bakefile Local CLI Frequently Used Params
|
|
69
|
+
# ==========================================================
|
|
70
|
+
force_option = Annotated[
|
|
71
|
+
bool | None, typer.Option("--force/--no-force", "-f", help="Force execution")
|
|
72
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from bake.ui import console
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _get_version() -> str:
|
|
9
|
+
try:
|
|
10
|
+
return version("bakefile")
|
|
11
|
+
except PackageNotFoundError:
|
|
12
|
+
return "0.0.0"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def version_callback(value: bool) -> None:
|
|
16
|
+
if value:
|
|
17
|
+
console.out.print(_get_version(), style=None, highlight=False)
|
|
18
|
+
raise typer.Exit()
|
bake/manage/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import re
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from bake.ui import console, run_uv
|
|
8
|
+
from bake.utils.exceptions import BakebookError
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
if sys.version_info >= (3, 11):
|
|
13
|
+
import tomllib
|
|
14
|
+
else:
|
|
15
|
+
import tomli as tomllib # type: ignore[import-not-found]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def add_inline_metadata(bakefile_path: Path) -> None:
|
|
19
|
+
if not bakefile_path.exists():
|
|
20
|
+
logger.error(f"Bakefile not found at {bakefile_path}")
|
|
21
|
+
raise BakebookError(
|
|
22
|
+
f"Bakefile not found at {bakefile_path}. "
|
|
23
|
+
f"Run 'bakefile init --inline' to create a new bakefile with PEP 723 metadata."
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
result = run_uv(
|
|
27
|
+
["init", "--script", str(bakefile_path.name)],
|
|
28
|
+
check=False,
|
|
29
|
+
cwd=bakefile_path.parent,
|
|
30
|
+
echo=False,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
is_already_pep723 = result.returncode == 2 and "is already a PEP 723 script" in result.stderr
|
|
34
|
+
|
|
35
|
+
is_valid_output = result.returncode == 0 or is_already_pep723
|
|
36
|
+
|
|
37
|
+
if not is_valid_output:
|
|
38
|
+
command: str = " ".join(result.args)
|
|
39
|
+
logger.error(f"Failed to initialize PEP 723 metadata for {bakefile_path}")
|
|
40
|
+
raise BakebookError(
|
|
41
|
+
f"Failed to initialize PEP 723 metadata.\n\n"
|
|
42
|
+
f"Command: `{command}`\n\n"
|
|
43
|
+
f"Error: {result.stderr.strip()}"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
if is_already_pep723:
|
|
47
|
+
console.warning(f"{bakefile_path.name} already has PEP 723 metadata")
|
|
48
|
+
|
|
49
|
+
run_uv(
|
|
50
|
+
["add", "bakefile", "--script", str(bakefile_path.name)],
|
|
51
|
+
cwd=bakefile_path.parent,
|
|
52
|
+
echo=False,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def read_inline(bakefile_path: Path) -> dict[str, Any] | None:
|
|
57
|
+
inline_regex = r"(?m)^# /// (?P<type>[a-zA-Z0-9-]+)$\s(?P<content>(^#(| .*)$\s)+)^# ///$"
|
|
58
|
+
script = bakefile_path.read_text()
|
|
59
|
+
name = "script"
|
|
60
|
+
matches = list(filter(lambda m: m.group("type") == name, re.finditer(inline_regex, script)))
|
|
61
|
+
|
|
62
|
+
if len(matches) > 1:
|
|
63
|
+
raise ValueError(f"Multiple {name} blocks found")
|
|
64
|
+
elif len(matches) == 1:
|
|
65
|
+
content = "".join(
|
|
66
|
+
line[2:] if line.startswith("# ") else line[1:]
|
|
67
|
+
for line in matches[0].group("content").splitlines(keepends=True)
|
|
68
|
+
)
|
|
69
|
+
return tomllib.loads(content)
|
|
70
|
+
else:
|
|
71
|
+
return None
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import re
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from bake.manage.add_inline import read_inline
|
|
6
|
+
from bake.ui import run_uv
|
|
7
|
+
from bake.utils import BakebookError
|
|
8
|
+
from bake.utils.exceptions import PythonNotFoundError
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def is_standalone_bakefile(bakefile_path: Path) -> bool:
|
|
14
|
+
inline_metadata = read_inline(bakefile_path)
|
|
15
|
+
if inline_metadata is None:
|
|
16
|
+
return False
|
|
17
|
+
|
|
18
|
+
dependencies = inline_metadata.get("dependencies", [])
|
|
19
|
+
has_bakefile = any(dep.startswith("bakefile") for dep in dependencies)
|
|
20
|
+
|
|
21
|
+
if not has_bakefile:
|
|
22
|
+
logger.error(
|
|
23
|
+
f"Invalid inline metadata in {bakefile_path}: "
|
|
24
|
+
f"PEP 723 metadata exists but 'bakefile' dependency is missing"
|
|
25
|
+
)
|
|
26
|
+
raise BakebookError(
|
|
27
|
+
f"Invalid inline metadata in {bakefile_path}: "
|
|
28
|
+
f"PEP 723 metadata exists but 'bakefile' dependency is missing. "
|
|
29
|
+
f"Run 'bakefile add-inline' to fix."
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
return True
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _find_bakefile_lock(bakefile_path: Path) -> Path | None:
|
|
36
|
+
"""Find bakefile-level lock (<bakefile.py.lock>)."""
|
|
37
|
+
lock_path = bakefile_path.with_suffix(bakefile_path.suffix + ".lock")
|
|
38
|
+
if lock_path.exists():
|
|
39
|
+
logger.debug(f"Found bakefile lock at {lock_path}")
|
|
40
|
+
return lock_path
|
|
41
|
+
logger.debug("No bakefile lock found")
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _find_project_lock(bakefile_path: Path) -> Path | None:
|
|
46
|
+
"""Find project-level uv.lock by searching up directory tree."""
|
|
47
|
+
current_dir = bakefile_path.parent
|
|
48
|
+
for _ in range(10): # Limit search depth
|
|
49
|
+
uv_lock = current_dir / "uv.lock"
|
|
50
|
+
if uv_lock.exists():
|
|
51
|
+
logger.debug(f"Found project lock at {uv_lock}")
|
|
52
|
+
return uv_lock
|
|
53
|
+
parent = current_dir.parent
|
|
54
|
+
if parent == current_dir: # Reached root
|
|
55
|
+
break
|
|
56
|
+
current_dir = parent
|
|
57
|
+
logger.debug("No project lock found")
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _find_bakefile_python(bakefile_path: Path) -> Path | None:
|
|
62
|
+
# References:
|
|
63
|
+
# https://github.com/astral-sh/uv/blob/543f1f3f5924d1d2734fd718381e6f0d0f6f70b5/crates/uv/src/commands/project/mod.rs#L843
|
|
64
|
+
|
|
65
|
+
kind = "script"
|
|
66
|
+
found_bakefile_level_venv_message = (
|
|
67
|
+
f"The {kind} environment's Python version satisfies the request"
|
|
68
|
+
)
|
|
69
|
+
result = run_uv(
|
|
70
|
+
["python", "find", "--script", str(bakefile_path.name), "-v"],
|
|
71
|
+
check=False,
|
|
72
|
+
cwd=bakefile_path.parent,
|
|
73
|
+
echo=False,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
is_bakefile_python_found = (
|
|
77
|
+
result.returncode == 0 and found_bakefile_level_venv_message in result.stderr.strip()
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if is_bakefile_python_found:
|
|
81
|
+
python_path = Path(result.stdout.strip())
|
|
82
|
+
logger.debug(f"Found bakefile Python at {python_path.as_posix()}")
|
|
83
|
+
return python_path
|
|
84
|
+
|
|
85
|
+
logger.debug("No bakefile Python found")
|
|
86
|
+
return None
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _find_project_python(bakefile_path: Path) -> Path | None:
|
|
90
|
+
"""Find Python from project-level venv using uv python find -v."""
|
|
91
|
+
# References:
|
|
92
|
+
# https://github.com/astral-sh/uv/blob/543f1f3f5924d1d2734fd718381e6f0d0f6f70b5/crates/uv-python/src/discovery.rs#L795
|
|
93
|
+
# https://github.com/astral-sh/uv/blob/543f1f3f5924d1d2734fd718381e6f0d0f6f70b5/crates/uv-python/src/discovery.rs#L3169-L3184
|
|
94
|
+
result = run_uv(
|
|
95
|
+
["python", "find", "-v"],
|
|
96
|
+
check=False,
|
|
97
|
+
cwd=bakefile_path.parent,
|
|
98
|
+
echo=False,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# Check if stderr contains "Found `...` at `...` (...)"
|
|
102
|
+
# where source is "active virtual environment" or "virtual environment"
|
|
103
|
+
stderr = result.stderr.strip()
|
|
104
|
+
pattern = r"Found `[^`]+` at `[^`]+` \(([^)]+)\)"
|
|
105
|
+
match = re.search(pattern, stderr)
|
|
106
|
+
|
|
107
|
+
if result.returncode == 0 and match:
|
|
108
|
+
source = match.group(1)
|
|
109
|
+
if source in {"active virtual environment", "virtual environment"}:
|
|
110
|
+
python_path = Path(result.stdout.strip())
|
|
111
|
+
logger.debug(f"Found project Python at {python_path} (source: {source})")
|
|
112
|
+
return python_path
|
|
113
|
+
|
|
114
|
+
logger.debug("No project Python found")
|
|
115
|
+
return None
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _create_bakefile_venv(bakefile_path: Path) -> Path | None:
|
|
119
|
+
"""Create bakefile-level venv and return Python path."""
|
|
120
|
+
lock_path = _find_bakefile_lock(bakefile_path)
|
|
121
|
+
|
|
122
|
+
if lock_path:
|
|
123
|
+
# Use frozen sync if lock exists
|
|
124
|
+
logger.debug("Syncing bakefile with frozen lock")
|
|
125
|
+
run_uv(
|
|
126
|
+
["sync", "--script", str(bakefile_path.name), "--frozen"],
|
|
127
|
+
check=True,
|
|
128
|
+
cwd=bakefile_path.parent,
|
|
129
|
+
echo=False,
|
|
130
|
+
)
|
|
131
|
+
else:
|
|
132
|
+
# Create new lock and sync
|
|
133
|
+
logger.debug("Creating bakefile lock and syncing")
|
|
134
|
+
run_uv(
|
|
135
|
+
["sync", "--script", str(bakefile_path.name)],
|
|
136
|
+
check=True,
|
|
137
|
+
cwd=bakefile_path.parent,
|
|
138
|
+
echo=False,
|
|
139
|
+
)
|
|
140
|
+
run_uv(
|
|
141
|
+
["lock", "--script", str(bakefile_path.name)],
|
|
142
|
+
check=True,
|
|
143
|
+
cwd=bakefile_path.parent,
|
|
144
|
+
echo=False,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
return _find_bakefile_python(bakefile_path)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _create_project_venv(bakefile_path: Path) -> Path | None:
|
|
151
|
+
"""Create project-level venv and return Python path."""
|
|
152
|
+
work_dir = bakefile_path.parent
|
|
153
|
+
|
|
154
|
+
# Check if pyproject.toml exists
|
|
155
|
+
pyproject = work_dir / "pyproject.toml"
|
|
156
|
+
if not pyproject.exists():
|
|
157
|
+
logger.debug("No pyproject.toml found, cannot create project venv")
|
|
158
|
+
return None
|
|
159
|
+
|
|
160
|
+
lock_path = _find_project_lock(bakefile_path)
|
|
161
|
+
|
|
162
|
+
if lock_path:
|
|
163
|
+
# Use frozen sync if lock exists
|
|
164
|
+
logger.debug("Syncing project with frozen lock")
|
|
165
|
+
run_uv(["sync", "--frozen"], check=True, cwd=work_dir, echo=False)
|
|
166
|
+
else:
|
|
167
|
+
# Create new lock and sync
|
|
168
|
+
logger.debug("Creating project lock and syncing")
|
|
169
|
+
run_uv(["lock"], check=True, cwd=work_dir, echo=False)
|
|
170
|
+
run_uv(["sync"], check=True, cwd=work_dir, echo=False)
|
|
171
|
+
|
|
172
|
+
return _find_project_python(bakefile_path)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def find_python_path(bakefile_path: Path | None) -> Path:
|
|
176
|
+
if bakefile_path is None or not bakefile_path.exists():
|
|
177
|
+
raise PythonNotFoundError(f"Bakefile not found at {bakefile_path}")
|
|
178
|
+
|
|
179
|
+
is_standalone = is_standalone_bakefile(bakefile_path)
|
|
180
|
+
|
|
181
|
+
if is_standalone:
|
|
182
|
+
logger.debug("Bakefile has inline metadata -> bakefile-level Python")
|
|
183
|
+
|
|
184
|
+
# Step 1: Try to find existing bakefile-level Python
|
|
185
|
+
python_path = _find_bakefile_python(bakefile_path)
|
|
186
|
+
if python_path:
|
|
187
|
+
return python_path
|
|
188
|
+
|
|
189
|
+
# Step 2: Create bakefile-level venv
|
|
190
|
+
python_path = _create_bakefile_venv(bakefile_path)
|
|
191
|
+
if python_path:
|
|
192
|
+
return python_path
|
|
193
|
+
|
|
194
|
+
else:
|
|
195
|
+
logger.debug("No inline metadata -> project-level Python")
|
|
196
|
+
|
|
197
|
+
# Step 1: Try to find existing project-level Python
|
|
198
|
+
python_path = _find_project_python(bakefile_path)
|
|
199
|
+
if python_path:
|
|
200
|
+
return python_path
|
|
201
|
+
|
|
202
|
+
# Step 2: Create project-level venv
|
|
203
|
+
python_path = _create_project_venv(bakefile_path)
|
|
204
|
+
if python_path:
|
|
205
|
+
return python_path
|
|
206
|
+
|
|
207
|
+
raise PythonNotFoundError(
|
|
208
|
+
f"Could not find Python for {bakefile_path}. "
|
|
209
|
+
f"Run 'bakefile add-inline' to add PEP 723 metadata for bakefile-level Python."
|
|
210
|
+
)
|
bake/manage/lint.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from ruff.__main__ import find_ruff_bin
|
|
6
|
+
from ty.__main__ import find_ty_bin
|
|
7
|
+
|
|
8
|
+
from bake.ui import console
|
|
9
|
+
from bake.ui.run import run
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def run_ruff(
|
|
15
|
+
bakefile_path: Path,
|
|
16
|
+
subcommand: str,
|
|
17
|
+
args: list[str],
|
|
18
|
+
*,
|
|
19
|
+
only_bakefile: bool = False,
|
|
20
|
+
check: bool = True,
|
|
21
|
+
dry_run: bool = False,
|
|
22
|
+
) -> subprocess.CompletedProcess[str]:
|
|
23
|
+
ruff_bin = find_ruff_bin()
|
|
24
|
+
target = bakefile_path.name if only_bakefile else "."
|
|
25
|
+
cmd = [subcommand, *args, target]
|
|
26
|
+
display_cmd = "ruff " + " ".join(cmd)
|
|
27
|
+
console.cmd(display_cmd)
|
|
28
|
+
return run(
|
|
29
|
+
[str(ruff_bin), *cmd],
|
|
30
|
+
cwd=bakefile_path.parent,
|
|
31
|
+
capture_output=True,
|
|
32
|
+
stream=True,
|
|
33
|
+
check=check,
|
|
34
|
+
echo=False,
|
|
35
|
+
dry_run=dry_run,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def run_ruff_format(
|
|
40
|
+
bakefile_path: Path,
|
|
41
|
+
*,
|
|
42
|
+
only_bakefile: bool = False,
|
|
43
|
+
check: bool = True,
|
|
44
|
+
dry_run: bool = False,
|
|
45
|
+
) -> subprocess.CompletedProcess[str]:
|
|
46
|
+
return run_ruff(
|
|
47
|
+
bakefile_path=bakefile_path,
|
|
48
|
+
subcommand="format",
|
|
49
|
+
args=["--exit-non-zero-on-format"],
|
|
50
|
+
only_bakefile=only_bakefile,
|
|
51
|
+
check=check,
|
|
52
|
+
dry_run=dry_run,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def run_ruff_check(
|
|
57
|
+
bakefile_path: Path,
|
|
58
|
+
*,
|
|
59
|
+
only_bakefile: bool = False,
|
|
60
|
+
check: bool = True,
|
|
61
|
+
dry_run: bool = False,
|
|
62
|
+
) -> subprocess.CompletedProcess[str]:
|
|
63
|
+
return run_ruff(
|
|
64
|
+
bakefile_path=bakefile_path,
|
|
65
|
+
subcommand="check",
|
|
66
|
+
args=[
|
|
67
|
+
"--fix",
|
|
68
|
+
"--exit-non-zero-on-fix",
|
|
69
|
+
"--extend-select",
|
|
70
|
+
"ARG,B,C4,E,F,I,N,PGH,PIE,PYI,RUF,SIM,UP",
|
|
71
|
+
],
|
|
72
|
+
only_bakefile=only_bakefile,
|
|
73
|
+
check=check,
|
|
74
|
+
dry_run=dry_run,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def run_ty_check(
|
|
79
|
+
bakefile_path: Path,
|
|
80
|
+
python_path: Path,
|
|
81
|
+
*,
|
|
82
|
+
only_bakefile: bool = False,
|
|
83
|
+
check: bool = True,
|
|
84
|
+
dry_run: bool = False,
|
|
85
|
+
) -> subprocess.CompletedProcess[str]:
|
|
86
|
+
ty_bin = find_ty_bin()
|
|
87
|
+
cmd = ["check", "--error-on-warning", "--python", str(python_path)]
|
|
88
|
+
if only_bakefile:
|
|
89
|
+
cmd.append(bakefile_path.name)
|
|
90
|
+
|
|
91
|
+
display_cmd = "ty " + " ".join(cmd)
|
|
92
|
+
console.cmd(display_cmd)
|
|
93
|
+
return run(
|
|
94
|
+
[str(ty_bin), *cmd],
|
|
95
|
+
cwd=bakefile_path.parent,
|
|
96
|
+
capture_output=True,
|
|
97
|
+
stream=True,
|
|
98
|
+
check=check,
|
|
99
|
+
echo=False,
|
|
100
|
+
dry_run=dry_run,
|
|
101
|
+
)
|
bake/manage/run_uv.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from bake.manage.find_python import find_python_path, is_standalone_bakefile
|
|
6
|
+
from bake.ui import console, style
|
|
7
|
+
from bake.ui.run import run, run_uv
|
|
8
|
+
from bake.utils import BakebookError
|
|
9
|
+
from bake.utils.exceptions import PythonNotFoundError
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _run_uv(
|
|
15
|
+
bakefile_path: Path | None, command_name: str, cmd: list[str], dry_run: bool = False
|
|
16
|
+
) -> subprocess.CompletedProcess[str] | subprocess.CompletedProcess[None]:
|
|
17
|
+
if bakefile_path is None or not bakefile_path.exists():
|
|
18
|
+
raise PythonNotFoundError(f"Bakefile not found at {bakefile_path}")
|
|
19
|
+
|
|
20
|
+
if not is_standalone_bakefile(bakefile_path):
|
|
21
|
+
error_msg = (
|
|
22
|
+
f"`{command_name}` command requires PEP 723 inline metadata in the bakefile. "
|
|
23
|
+
f"Run {style.code('bakefile add-inline')} to add metadata, "
|
|
24
|
+
f"or use {style.code(f'uv {command_name}')} directly for project-level dependencies."
|
|
25
|
+
)
|
|
26
|
+
raise BakebookError(error_msg)
|
|
27
|
+
|
|
28
|
+
logger.debug(f"Running `uv {command_name}` for {bakefile_path}")
|
|
29
|
+
result = run_uv(
|
|
30
|
+
(command_name, "--script", bakefile_path.name, *cmd),
|
|
31
|
+
capture_output=True,
|
|
32
|
+
stream=True,
|
|
33
|
+
check=True,
|
|
34
|
+
echo=True,
|
|
35
|
+
cwd=bakefile_path.parent,
|
|
36
|
+
dry_run=dry_run,
|
|
37
|
+
)
|
|
38
|
+
return result
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def run_uv_add(
|
|
42
|
+
bakefile_path: Path | None, cmd: list[str], dry_run: bool
|
|
43
|
+
) -> subprocess.CompletedProcess[str] | subprocess.CompletedProcess[None]:
|
|
44
|
+
return _run_uv(bakefile_path=bakefile_path, command_name="add", cmd=cmd, dry_run=dry_run)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def run_uv_lock(
|
|
48
|
+
bakefile_path: Path | None, cmd: list[str], dry_run: bool
|
|
49
|
+
) -> subprocess.CompletedProcess[str] | subprocess.CompletedProcess[None]:
|
|
50
|
+
return _run_uv(bakefile_path=bakefile_path, command_name="lock", cmd=cmd, dry_run=dry_run)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def run_uv_sync(
|
|
54
|
+
bakefile_path: Path | None, cmd: list[str], dry_run: bool
|
|
55
|
+
) -> subprocess.CompletedProcess[str] | subprocess.CompletedProcess[None]:
|
|
56
|
+
return _run_uv(bakefile_path=bakefile_path, command_name="sync", cmd=cmd, dry_run=dry_run)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def run_uv_pip(
|
|
60
|
+
bakefile_path: Path | None, cmd: list[str], dry_run: bool
|
|
61
|
+
) -> subprocess.CompletedProcess[str] | subprocess.CompletedProcess[None]:
|
|
62
|
+
if bakefile_path is None or not bakefile_path.exists():
|
|
63
|
+
raise PythonNotFoundError(f"Bakefile not found at {bakefile_path}")
|
|
64
|
+
|
|
65
|
+
is_standalone = is_standalone_bakefile(bakefile_path)
|
|
66
|
+
if not is_standalone:
|
|
67
|
+
console.warning(
|
|
68
|
+
"No PEP 723 inline metadata found. Using project-level Python.\n"
|
|
69
|
+
f"For project-level dependencies, consider using {style.code('uv pip')} directly.\n"
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
python_path = find_python_path(bakefile_path)
|
|
73
|
+
|
|
74
|
+
version_result = run(
|
|
75
|
+
[str(python_path), "--version"], capture_output=True, stream=False, echo=False
|
|
76
|
+
)
|
|
77
|
+
version = version_result.stdout.strip() or version_result.stderr.strip()
|
|
78
|
+
console.err.print(f"Using {version}\n")
|
|
79
|
+
|
|
80
|
+
logger.debug(f"Running uv pip with cmd: {cmd}")
|
|
81
|
+
return run_uv(
|
|
82
|
+
("pip", *cmd, "--python", str(python_path)),
|
|
83
|
+
capture_output=True,
|
|
84
|
+
stream=True,
|
|
85
|
+
check=True,
|
|
86
|
+
echo=True,
|
|
87
|
+
dry_run=dry_run,
|
|
88
|
+
)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import types
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from bake.utils.constants import BAKEBOOK_NAME_IN_SAMPLES
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def write_bakefile(
|
|
8
|
+
bakefile_path: Path, bakebook_name: str, sample_module: types.ModuleType
|
|
9
|
+
) -> None:
|
|
10
|
+
if not hasattr(sample_module, BAKEBOOK_NAME_IN_SAMPLES):
|
|
11
|
+
raise ValueError(
|
|
12
|
+
f"Module `{sample_module.__name__}` must have `{BAKEBOOK_NAME_IN_SAMPLES}` attribute"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
if sample_module.__file__ is None:
|
|
16
|
+
raise ValueError(f"Could not find `{sample_module.__name__}`")
|
|
17
|
+
|
|
18
|
+
original_bakefile_content = Path(sample_module.__file__).read_text()
|
|
19
|
+
customized_content = original_bakefile_content.replace(BAKEBOOK_NAME_IN_SAMPLES, bakebook_name)
|
|
20
|
+
bakefile_path.write_text(customized_content)
|
bake/py.typed
ADDED
|
File without changes
|
bake/samples/__init__.py
ADDED
|
File without changes
|
bake/samples/simple.py
ADDED
bake/ui/__init__.py
ADDED
bake/ui/console.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import textwrap
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from beautysh import BashFormatter
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
|
|
7
|
+
out = Console(stderr=False)
|
|
8
|
+
err = Console(stderr=True)
|
|
9
|
+
|
|
10
|
+
BOLD_GREEN = "bold green"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _print(
|
|
14
|
+
console_obj: Console, emoji: str | None, label: str, style: str, message: str, **kwargs
|
|
15
|
+
) -> None:
|
|
16
|
+
formatted_label = f"[{label}]" if console_obj.no_color or out.color_system is None else label
|
|
17
|
+
|
|
18
|
+
emoji = emoji + " " if emoji else ""
|
|
19
|
+
console_obj.print(f"[{style}]{emoji}{formatted_label}[/{style}] {message}", **kwargs)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def success(message: str, **kwargs) -> None:
|
|
23
|
+
_print(out, ":white_check_mark:", "SUCCESS", BOLD_GREEN, message, **kwargs)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def echo(message: Any, **kwargs) -> None:
|
|
27
|
+
out.print(message, **kwargs)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def cmd(cmd_str: str, **kwargs) -> None:
|
|
31
|
+
err.print(f"[bold green]❯[/bold green] [default]{cmd_str}[/default]", **kwargs) # noqa: RUF001
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def script_block(title: str, script: str, **kwargs) -> None:
|
|
35
|
+
formatter = BashFormatter()
|
|
36
|
+
formatted, error = formatter.beautify_string(script)
|
|
37
|
+
|
|
38
|
+
if error:
|
|
39
|
+
formatted = textwrap.dedent(script)
|
|
40
|
+
|
|
41
|
+
terminal_width: int = err.size.width
|
|
42
|
+
width = min(70, terminal_width)
|
|
43
|
+
bold_line = "━" * width
|
|
44
|
+
thin_line = "─" * width
|
|
45
|
+
|
|
46
|
+
err.print(bold_line, style=BOLD_GREEN)
|
|
47
|
+
err.print(title, style="bold")
|
|
48
|
+
err.print(thin_line, style=BOLD_GREEN)
|
|
49
|
+
err.print(formatted, highlight=False, **kwargs)
|
|
50
|
+
err.print(bold_line, style=BOLD_GREEN)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def warning(message: str, **kwargs) -> None:
|
|
54
|
+
_print(err, ":warning-emoji: ", "WARNING", "bold yellow", message, **kwargs)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def error(message: str, **kwargs) -> None:
|
|
58
|
+
_print(err, ":x:", "ERROR", "bold red", message, **kwargs)
|