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
bake/__init__.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from bake.bakebook.bakebook import Bakebook
|
|
2
|
+
from bake.bakebook.decorator import command
|
|
3
|
+
from bake.cli.common.context import BakeCommand, Context
|
|
4
|
+
from bake.cli.utils.version import _get_version
|
|
5
|
+
from bake.ui import console
|
|
6
|
+
|
|
7
|
+
__version__ = _get_version()
|
|
8
|
+
|
|
9
|
+
__all__ = ["BakeCommand", "Bakebook", "Context", "__version__", "command", "console"]
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import types
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
from pydantic import PrivateAttr
|
|
7
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
8
|
+
from typer.core import TyperCommand
|
|
9
|
+
from typer.models import CommandFunctionType, Default
|
|
10
|
+
|
|
11
|
+
from bake.cli.common.context import BakeCommand
|
|
12
|
+
from bake.utils.constants import BAKE_COMMAND_KWARGS
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Bakebook(BaseSettings):
|
|
16
|
+
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")
|
|
17
|
+
_app: typer.Typer = PrivateAttr(default_factory=typer.Typer)
|
|
18
|
+
|
|
19
|
+
def __init__(self, **kwargs):
|
|
20
|
+
super().__init__(**kwargs)
|
|
21
|
+
self._register_marked_methods()
|
|
22
|
+
|
|
23
|
+
def _get_command_kwargs(self, method: types.MethodType) -> dict[str, Any] | None:
|
|
24
|
+
func = method.__func__
|
|
25
|
+
|
|
26
|
+
if hasattr(func, BAKE_COMMAND_KWARGS):
|
|
27
|
+
return object.__getattribute__(func, BAKE_COMMAND_KWARGS)
|
|
28
|
+
|
|
29
|
+
method_name = method.__name__
|
|
30
|
+
for base in self.__class__.__mro__[1:]:
|
|
31
|
+
if not hasattr(base, method_name):
|
|
32
|
+
continue
|
|
33
|
+
parent_func = getattr(base, method_name)
|
|
34
|
+
if hasattr(parent_func, BAKE_COMMAND_KWARGS):
|
|
35
|
+
return object.__getattribute__(parent_func, BAKE_COMMAND_KWARGS)
|
|
36
|
+
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
def _register_marked_methods(self) -> None:
|
|
40
|
+
base_names = set(dir(BaseSettings()))
|
|
41
|
+
method_names = [
|
|
42
|
+
name for name in dir(self) if not name.startswith("_") and name not in base_names
|
|
43
|
+
]
|
|
44
|
+
for name in method_names:
|
|
45
|
+
bound_method = getattr(self, name)
|
|
46
|
+
if not isinstance(bound_method, types.MethodType):
|
|
47
|
+
continue
|
|
48
|
+
|
|
49
|
+
cmd_kwargs = self._get_command_kwargs(bound_method)
|
|
50
|
+
if cmd_kwargs:
|
|
51
|
+
self._app.command(**cmd_kwargs)(bound_method)
|
|
52
|
+
|
|
53
|
+
def command(
|
|
54
|
+
self,
|
|
55
|
+
name: str | None = None,
|
|
56
|
+
*,
|
|
57
|
+
cls: type[TyperCommand] | None = None,
|
|
58
|
+
context_settings: dict[Any, Any] | None = None,
|
|
59
|
+
help: str | None = None,
|
|
60
|
+
epilog: str | None = None,
|
|
61
|
+
short_help: str | None = None,
|
|
62
|
+
options_metavar: str | None = None,
|
|
63
|
+
add_help_option: bool = True,
|
|
64
|
+
no_args_is_help: bool = False,
|
|
65
|
+
hidden: bool = False,
|
|
66
|
+
deprecated: bool = False,
|
|
67
|
+
rich_help_panel: str | None = Default(None),
|
|
68
|
+
) -> Callable[[CommandFunctionType], CommandFunctionType]:
|
|
69
|
+
if cls is None:
|
|
70
|
+
cls = BakeCommand
|
|
71
|
+
|
|
72
|
+
return self._app.command(
|
|
73
|
+
name=name,
|
|
74
|
+
cls=cls,
|
|
75
|
+
context_settings=context_settings,
|
|
76
|
+
help=help,
|
|
77
|
+
epilog=epilog,
|
|
78
|
+
short_help=short_help,
|
|
79
|
+
options_metavar=options_metavar,
|
|
80
|
+
add_help_option=add_help_option,
|
|
81
|
+
no_args_is_help=no_args_is_help,
|
|
82
|
+
hidden=hidden,
|
|
83
|
+
deprecated=deprecated,
|
|
84
|
+
rich_help_panel=rich_help_panel,
|
|
85
|
+
)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from typer.core import TyperCommand
|
|
5
|
+
from typer.models import CommandFunctionType, Default
|
|
6
|
+
|
|
7
|
+
from bake.cli.common.context import BakeCommand
|
|
8
|
+
from bake.utils.constants import BAKE_COMMAND_KWARGS
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def command(
|
|
12
|
+
name: str | None = None,
|
|
13
|
+
*,
|
|
14
|
+
cls: type[TyperCommand] | None = None,
|
|
15
|
+
context_settings: dict[Any, Any] | None = None,
|
|
16
|
+
help: str | None = None,
|
|
17
|
+
epilog: str | None = None,
|
|
18
|
+
short_help: str | None = None,
|
|
19
|
+
options_metavar: str | None = None,
|
|
20
|
+
add_help_option: bool = True,
|
|
21
|
+
no_args_is_help: bool = False,
|
|
22
|
+
hidden: bool = False,
|
|
23
|
+
deprecated: bool = False,
|
|
24
|
+
rich_help_panel: str | None = Default(None),
|
|
25
|
+
) -> Callable[[CommandFunctionType], CommandFunctionType]:
|
|
26
|
+
if cls is None:
|
|
27
|
+
cls = BakeCommand
|
|
28
|
+
|
|
29
|
+
def decorator(func: CommandFunctionType) -> CommandFunctionType:
|
|
30
|
+
object.__setattr__(
|
|
31
|
+
func,
|
|
32
|
+
BAKE_COMMAND_KWARGS,
|
|
33
|
+
{
|
|
34
|
+
"name": name,
|
|
35
|
+
"cls": cls,
|
|
36
|
+
"context_settings": context_settings,
|
|
37
|
+
"help": help,
|
|
38
|
+
"epilog": epilog,
|
|
39
|
+
"short_help": short_help,
|
|
40
|
+
"options_metavar": options_metavar,
|
|
41
|
+
"add_help_option": add_help_option,
|
|
42
|
+
"no_args_is_help": no_args_is_help,
|
|
43
|
+
"hidden": hidden,
|
|
44
|
+
"deprecated": deprecated,
|
|
45
|
+
"rich_help_panel": rich_help_panel,
|
|
46
|
+
},
|
|
47
|
+
)
|
|
48
|
+
return func
|
|
49
|
+
|
|
50
|
+
return decorator
|
bake/bakebook/get.py
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import importlib.util
|
|
2
|
+
import logging
|
|
3
|
+
import sys
|
|
4
|
+
import types
|
|
5
|
+
from collections.abc import Generator
|
|
6
|
+
from contextlib import contextmanager
|
|
7
|
+
from importlib.abc import Loader
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import TYPE_CHECKING, Any, TypeVar
|
|
10
|
+
|
|
11
|
+
from bake.manage.find_python import is_standalone_bakefile
|
|
12
|
+
from bake.manage.run_uv import run_uv_sync
|
|
13
|
+
from bake.ui import console, style
|
|
14
|
+
from bake.ui.run import run_uv
|
|
15
|
+
from bake.utils.exceptions import BakebookError, BakefileNotFoundError
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from bake.bakebook.bakebook import Bakebook
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
T = TypeVar("T")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@contextmanager
|
|
26
|
+
def prepend_sys_path(path: str) -> Generator[None, None, None]:
|
|
27
|
+
path_existed = path in sys.path
|
|
28
|
+
if not path_existed:
|
|
29
|
+
sys.path.insert(0, path)
|
|
30
|
+
try:
|
|
31
|
+
yield
|
|
32
|
+
finally:
|
|
33
|
+
if not path_existed:
|
|
34
|
+
sys.path.remove(path)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_target_dir_path(chdir: Path, create_if_not_exist: bool) -> Path:
|
|
38
|
+
if not chdir.exists():
|
|
39
|
+
if create_if_not_exist:
|
|
40
|
+
chdir.mkdir(parents=True, exist_ok=True)
|
|
41
|
+
else:
|
|
42
|
+
logger.debug(f"Directory not found: {chdir}")
|
|
43
|
+
raise BakefileNotFoundError(f"Directory not found: {chdir}")
|
|
44
|
+
if not chdir.is_dir():
|
|
45
|
+
logger.debug(f"Not a directory: {chdir}")
|
|
46
|
+
raise BakefileNotFoundError(f"Not a directory: {chdir}")
|
|
47
|
+
return chdir.resolve()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def resolve_bakefile_path(chdir: Path, file_name: str) -> Path:
|
|
51
|
+
target_dir_path = get_target_dir_path(chdir=chdir, create_if_not_exist=False)
|
|
52
|
+
bakefile_path = target_dir_path / file_name
|
|
53
|
+
logger.debug(f"Resolve bakefile path: {bakefile_path}", extra={"bakefile_path": bakefile_path})
|
|
54
|
+
return bakefile_path
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def retry_load_module_with_uv_sync(
|
|
58
|
+
target_dir_path: Path,
|
|
59
|
+
error: ImportError,
|
|
60
|
+
parent_dir: str,
|
|
61
|
+
loader: Loader,
|
|
62
|
+
module: types.ModuleType,
|
|
63
|
+
dry_run: bool = False,
|
|
64
|
+
):
|
|
65
|
+
logger.debug(f"Missing dependency: {error.name}. Running sync...")
|
|
66
|
+
|
|
67
|
+
sync_args = ["--all-groups", "--frozen", "--all-extras"]
|
|
68
|
+
|
|
69
|
+
if is_standalone_bakefile(target_dir_path):
|
|
70
|
+
# Standalone bakefile: use --script flag
|
|
71
|
+
run_uv_sync(
|
|
72
|
+
bakefile_path=target_dir_path,
|
|
73
|
+
cmd=sync_args,
|
|
74
|
+
dry_run=dry_run,
|
|
75
|
+
)
|
|
76
|
+
else:
|
|
77
|
+
# Project-level: use uv sync directly
|
|
78
|
+
run_uv(
|
|
79
|
+
("sync", *sync_args),
|
|
80
|
+
cwd=parent_dir,
|
|
81
|
+
capture_output=True,
|
|
82
|
+
stream=True,
|
|
83
|
+
check=True,
|
|
84
|
+
echo=True,
|
|
85
|
+
dry_run=dry_run,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
loader.exec_module(module)
|
|
90
|
+
except Exception as e:
|
|
91
|
+
error_message = (
|
|
92
|
+
f"Failed get bakebook from: {target_dir_path} even after sync.\n"
|
|
93
|
+
f"{e.__class__.__name__}: {e}"
|
|
94
|
+
)
|
|
95
|
+
logger.debug(error_message)
|
|
96
|
+
console.error(
|
|
97
|
+
"Failed to load bakebook after dependency sync. "
|
|
98
|
+
f"Try running {style.code('uv cache clean')} to resolve potential caching issues."
|
|
99
|
+
)
|
|
100
|
+
raise BakebookError(error_message) from e
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def load_module(target_dir_path: Path) -> types.ModuleType:
|
|
104
|
+
if not target_dir_path.exists():
|
|
105
|
+
logger.debug(f"Directory not found: {target_dir_path}")
|
|
106
|
+
raise BakefileNotFoundError(f"Directory not found: {target_dir_path}")
|
|
107
|
+
|
|
108
|
+
parent_dir = str(target_dir_path.parent)
|
|
109
|
+
module_name = "bakefile"
|
|
110
|
+
|
|
111
|
+
with prepend_sys_path(parent_dir):
|
|
112
|
+
spec = importlib.util.spec_from_file_location(module_name, target_dir_path)
|
|
113
|
+
if spec is None or spec.loader is None:
|
|
114
|
+
logger.debug(f"Failed to load bakebook module from: {target_dir_path}")
|
|
115
|
+
raise BakebookError(f"Failed to load: {target_dir_path}")
|
|
116
|
+
|
|
117
|
+
module: types.ModuleType = importlib.util.module_from_spec(spec)
|
|
118
|
+
sys.modules[module_name] = module
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
spec.loader.exec_module(module)
|
|
122
|
+
except ImportError as e:
|
|
123
|
+
retry_load_module_with_uv_sync(
|
|
124
|
+
target_dir_path=target_dir_path,
|
|
125
|
+
error=e,
|
|
126
|
+
parent_dir=parent_dir,
|
|
127
|
+
loader=spec.loader,
|
|
128
|
+
module=module,
|
|
129
|
+
)
|
|
130
|
+
except Exception as e:
|
|
131
|
+
error_message = (
|
|
132
|
+
f"Failed get bakebook from: {target_dir_path}.\n{e.__class__.__name__}: {e}"
|
|
133
|
+
)
|
|
134
|
+
logger.debug(error_message)
|
|
135
|
+
raise BakebookError(error_message) from e
|
|
136
|
+
return module
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def validate_bakebook(bakebook: Any, bakebook_name: str) -> "Bakebook":
|
|
140
|
+
from bake.bakebook.bakebook import Bakebook
|
|
141
|
+
|
|
142
|
+
if not isinstance(bakebook, Bakebook):
|
|
143
|
+
logger.debug(
|
|
144
|
+
f"Invalid bakebook type for '{bakebook_name}': "
|
|
145
|
+
f"expected {Bakebook.__name__}, got {type(bakebook).__name__}"
|
|
146
|
+
)
|
|
147
|
+
raise BakebookError(
|
|
148
|
+
f"Bakebook '{bakebook_name}' must be a {Bakebook.__name__}, "
|
|
149
|
+
f"got {type(bakebook).__name__}"
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
return bakebook
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def get_bakebook_from_module(
|
|
156
|
+
module: types.ModuleType, bakebook_name: str, target_dir_path: Path
|
|
157
|
+
) -> "Bakebook":
|
|
158
|
+
if not hasattr(module, bakebook_name):
|
|
159
|
+
logger.debug(f"Bakebook '{bakebook_name}' not found in {target_dir_path}")
|
|
160
|
+
raise BakebookError(f"No '{bakebook_name}' found in {target_dir_path}")
|
|
161
|
+
bakebook = getattr(module, bakebook_name)
|
|
162
|
+
bakebook = validate_bakebook(bakebook=bakebook, bakebook_name=bakebook_name)
|
|
163
|
+
return bakebook
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def get_bakebook_from_target_dir_path(
|
|
167
|
+
target_dir_path: Path,
|
|
168
|
+
bakebook_name: str,
|
|
169
|
+
) -> "Bakebook":
|
|
170
|
+
module = load_module(target_dir_path=target_dir_path)
|
|
171
|
+
bakebook = get_bakebook_from_module(
|
|
172
|
+
module=module, bakebook_name=bakebook_name, target_dir_path=target_dir_path
|
|
173
|
+
)
|
|
174
|
+
logger.debug(f"Successfully retrieved bakebook '{bakebook_name}' from {target_dir_path}")
|
|
175
|
+
return bakebook
|
bake/cli/bake/main.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from contextlib import contextmanager
|
|
3
|
+
|
|
4
|
+
import typer
|
|
5
|
+
|
|
6
|
+
from bake.cli.bake.reinvocation import _reinvoke_with_detected_python
|
|
7
|
+
from bake.cli.common.app import (
|
|
8
|
+
add_completion,
|
|
9
|
+
bake_app_callback_with_obj,
|
|
10
|
+
rich_markup_mode,
|
|
11
|
+
)
|
|
12
|
+
from bake.cli.common.obj import get_bakefile_object, is_bakebook_optional
|
|
13
|
+
from bake.ui import console
|
|
14
|
+
from bake.utils import env
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@contextmanager
|
|
18
|
+
def set_argv(argv: list[str]):
|
|
19
|
+
original = sys.argv.copy()
|
|
20
|
+
sys.argv = argv
|
|
21
|
+
try:
|
|
22
|
+
yield
|
|
23
|
+
finally:
|
|
24
|
+
sys.argv = original
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _run_chain_commands(remaining_args: list[str], prog_name: str, bake_app: typer.Typer) -> int:
|
|
28
|
+
exit_code = 0
|
|
29
|
+
for cmd in remaining_args:
|
|
30
|
+
try:
|
|
31
|
+
with set_argv([prog_name, cmd]):
|
|
32
|
+
console.cmd(" ".join(sys.argv))
|
|
33
|
+
bake_app(prog_name=prog_name)
|
|
34
|
+
except SystemExit as e:
|
|
35
|
+
if e.code is not None and e.code != 0:
|
|
36
|
+
exit_code = e.code if isinstance(e.code, int) else 1
|
|
37
|
+
break
|
|
38
|
+
return exit_code
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def main():
|
|
42
|
+
bakefile_obj = get_bakefile_object(rich_markup_mode=rich_markup_mode)
|
|
43
|
+
bakefile_obj.setup_logging()
|
|
44
|
+
bakefile_obj.resolve_bakefile_path()
|
|
45
|
+
|
|
46
|
+
# Check re-invocation with resolved bakefile path
|
|
47
|
+
# If re-invocation happens, process is replaced and we don't return
|
|
48
|
+
_reinvoke_with_detected_python(bakefile_obj.bakefile_path)
|
|
49
|
+
# If returned above, we're in the correct Python
|
|
50
|
+
|
|
51
|
+
bakefile_obj.get_bakebook(
|
|
52
|
+
allow_missing=is_bakebook_optional(remaining_args=bakefile_obj.remaining_args)
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
bakefile_obj.warn_if_no_bakebook(color_echo=env.should_use_colors())
|
|
56
|
+
|
|
57
|
+
bake_app = typer.Typer(
|
|
58
|
+
add_completion=add_completion,
|
|
59
|
+
rich_markup_mode=rich_markup_mode,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
bake_app.callback(invoke_without_command=True)(bake_app_callback_with_obj(obj=bakefile_obj))
|
|
63
|
+
|
|
64
|
+
prog_name = "bake"
|
|
65
|
+
|
|
66
|
+
if bakefile_obj.bakebook is not None:
|
|
67
|
+
bake_app.add_typer(bakefile_obj.bakebook._app)
|
|
68
|
+
|
|
69
|
+
if bakefile_obj.is_chain_commands and bakefile_obj.remaining_args:
|
|
70
|
+
exit_code = _run_chain_commands(
|
|
71
|
+
remaining_args=bakefile_obj.remaining_args, prog_name=prog_name, bake_app=bake_app
|
|
72
|
+
)
|
|
73
|
+
raise SystemExit(exit_code)
|
|
74
|
+
bake_app(prog_name=prog_name)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from bake.utils.env import _BAKE_REINVOKED
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _reinvoke_with_detected_python(bakefile_path: Path | None) -> None:
|
|
13
|
+
"""Re-invoke bake CLI with detected Python if needed.
|
|
14
|
+
|
|
15
|
+
Checks if the current Python is the correct one for the bakefile.
|
|
16
|
+
If not, re-invokes the bake CLI with the detected Python using os.execve().
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
bakefile_path: Path to the bakefile, or None if not found
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
None. Either calls os.execve() (replaces process) or returns normally.
|
|
23
|
+
"""
|
|
24
|
+
# 1. Check marker to prevent infinite loops
|
|
25
|
+
if os.environ.get(_BAKE_REINVOKED):
|
|
26
|
+
logger.debug(
|
|
27
|
+
"Re-invocation marker set, skipping Python check",
|
|
28
|
+
extra={"sys.executable": sys.executable},
|
|
29
|
+
)
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
# 2. Try to find correct Python
|
|
33
|
+
try:
|
|
34
|
+
from bake.manage.find_python import find_python_path
|
|
35
|
+
|
|
36
|
+
python_path = find_python_path(bakefile_path)
|
|
37
|
+
except Exception:
|
|
38
|
+
logger.debug("Failed to find Python for bakefile, continuing with current Python")
|
|
39
|
+
return # Continue with current Python
|
|
40
|
+
|
|
41
|
+
# 3. Compare with current Python (don't resolve symlinks - we want the venv Python)
|
|
42
|
+
current_python = Path(sys.executable)
|
|
43
|
+
target_python = python_path
|
|
44
|
+
|
|
45
|
+
if current_python == target_python:
|
|
46
|
+
logger.debug(f"Already using correct Python: {current_python}")
|
|
47
|
+
return # Already correct
|
|
48
|
+
|
|
49
|
+
# 4. Re-invoke with detected Python
|
|
50
|
+
logger.debug(
|
|
51
|
+
f"Re-invoking bake with detected Python: {target_python} (current: {current_python})",
|
|
52
|
+
extra={"target_python": str(target_python)},
|
|
53
|
+
)
|
|
54
|
+
env = os.environ.copy()
|
|
55
|
+
env[_BAKE_REINVOKED] = "1"
|
|
56
|
+
|
|
57
|
+
sys.stdout.flush()
|
|
58
|
+
sys.stderr.flush()
|
|
59
|
+
result = subprocess.run(
|
|
60
|
+
[str(target_python), "-m", "bake.cli.bake", *sys.argv[1:]],
|
|
61
|
+
env=env,
|
|
62
|
+
)
|
|
63
|
+
raise SystemExit(result.returncode)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
|
|
3
|
+
from bake.cli.common.context import Context
|
|
4
|
+
from bake.manage.add_inline import add_inline_metadata
|
|
5
|
+
from bake.ui import console, style
|
|
6
|
+
from bake.utils.exceptions import BakebookError
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def add_inline(
|
|
10
|
+
ctx: Context,
|
|
11
|
+
) -> None:
|
|
12
|
+
"""Add PEP 723 inline metadata to bakefile.py.
|
|
13
|
+
|
|
14
|
+
For non-Python projects without pyproject.toml for dependency management.
|
|
15
|
+
"""
|
|
16
|
+
if ctx.obj.bakefile_path is None or not ctx.obj.bakefile_path.exists():
|
|
17
|
+
console.error(
|
|
18
|
+
f"Bakefile not found at {ctx.obj.bakefile_path}. "
|
|
19
|
+
f"Run {style.code('bakefile init --inline')} to create one."
|
|
20
|
+
)
|
|
21
|
+
raise typer.Exit(code=1)
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
add_inline_metadata(ctx.obj.bakefile_path)
|
|
25
|
+
except BakebookError as e:
|
|
26
|
+
console.error(str(e))
|
|
27
|
+
raise typer.Exit(code=1) from None
|
|
28
|
+
|
|
29
|
+
console.success(f"Successfully added PEP 723 inline metadata to {ctx.obj.bakefile_path}")
|
|
@@ -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
|