bakefile 0.0.4__py3-none-any.whl → 0.0.6__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 +249 -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.6.dist-info}/METADATA +15 -2
- bakefile-0.0.6.dist-info/RECORD +63 -0
- {bakefile-0.0.4.dist-info → bakefile-0.0.6.dist-info}/WHEEL +2 -2
- bakefile-0.0.6.dist-info/entry_points.txt +5 -0
- bakelib/__init__.py +4 -0
- bakelib/space/__init__.py +0 -0
- bakelib/space/base.py +193 -0
- bakelib/space/python.py +80 -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,145 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
from collections.abc import Generator
|
|
3
|
+
from contextlib import contextmanager
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import TYPE_CHECKING, Literal, overload
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
import typer
|
|
9
|
+
from typer.core import TyperCommand
|
|
10
|
+
|
|
11
|
+
from bake.ui.run import CmdType
|
|
12
|
+
from bake.ui.run import run as _run
|
|
13
|
+
from bake.ui.run.script import run_script as _run_script
|
|
14
|
+
|
|
15
|
+
from .obj import BakefileObject
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from bake.bakebook.bakebook import Bakebook
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Context(typer.Context):
|
|
22
|
+
obj: BakefileObject
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def dry_run(self) -> bool:
|
|
26
|
+
return self.obj.dry_run
|
|
27
|
+
|
|
28
|
+
@dry_run.setter
|
|
29
|
+
def dry_run(self, value: bool) -> None:
|
|
30
|
+
self.obj.dry_run = value
|
|
31
|
+
|
|
32
|
+
@contextmanager
|
|
33
|
+
def override_dry_run(self, dry_run: bool) -> Generator[None, None, None]:
|
|
34
|
+
original = self.obj.dry_run
|
|
35
|
+
self.obj.dry_run = dry_run
|
|
36
|
+
try:
|
|
37
|
+
yield
|
|
38
|
+
finally:
|
|
39
|
+
self.obj.dry_run = original
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def verbosity(self) -> int:
|
|
43
|
+
return self.obj.verbosity
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def bakebook(self) -> "Bakebook | None":
|
|
47
|
+
return self.obj.bakebook
|
|
48
|
+
|
|
49
|
+
@overload
|
|
50
|
+
def run(
|
|
51
|
+
self,
|
|
52
|
+
cmd: CmdType,
|
|
53
|
+
*,
|
|
54
|
+
capture_output: Literal[True] = True,
|
|
55
|
+
check: bool = True,
|
|
56
|
+
cwd: Path | str | None = None,
|
|
57
|
+
stream: bool = True,
|
|
58
|
+
shell: bool | None = None,
|
|
59
|
+
echo: bool = True,
|
|
60
|
+
dry_run: bool | None = None,
|
|
61
|
+
keep_temp_file: bool = False,
|
|
62
|
+
env: dict[str, str] | None = None,
|
|
63
|
+
**kwargs,
|
|
64
|
+
) -> subprocess.CompletedProcess[str]: ...
|
|
65
|
+
|
|
66
|
+
@overload
|
|
67
|
+
def run(
|
|
68
|
+
self,
|
|
69
|
+
cmd: CmdType,
|
|
70
|
+
*,
|
|
71
|
+
capture_output: Literal[False],
|
|
72
|
+
check: bool = True,
|
|
73
|
+
cwd: Path | str | None = None,
|
|
74
|
+
stream: bool = True,
|
|
75
|
+
shell: bool | None = None,
|
|
76
|
+
echo: bool = True,
|
|
77
|
+
dry_run: bool | None = None,
|
|
78
|
+
keep_temp_file: bool = False,
|
|
79
|
+
env: dict[str, str] | None = None,
|
|
80
|
+
**kwargs,
|
|
81
|
+
) -> subprocess.CompletedProcess[None]: ...
|
|
82
|
+
|
|
83
|
+
def run(
|
|
84
|
+
self,
|
|
85
|
+
cmd: CmdType,
|
|
86
|
+
*,
|
|
87
|
+
capture_output: bool = True,
|
|
88
|
+
check: bool = True,
|
|
89
|
+
cwd: Path | str | None = None,
|
|
90
|
+
stream: bool = True,
|
|
91
|
+
shell: bool | None = None,
|
|
92
|
+
echo: bool = True,
|
|
93
|
+
dry_run: bool | None = None,
|
|
94
|
+
keep_temp_file: bool = False,
|
|
95
|
+
env: dict[str, str] | None = None,
|
|
96
|
+
_encoding: str | None = None,
|
|
97
|
+
**kwargs,
|
|
98
|
+
) -> subprocess.CompletedProcess[str] | subprocess.CompletedProcess[None]:
|
|
99
|
+
return _run(
|
|
100
|
+
cmd,
|
|
101
|
+
capture_output=capture_output,
|
|
102
|
+
check=check,
|
|
103
|
+
cwd=cwd,
|
|
104
|
+
stream=stream,
|
|
105
|
+
shell=shell,
|
|
106
|
+
echo=echo,
|
|
107
|
+
dry_run=self.obj.dry_run if dry_run is None else dry_run,
|
|
108
|
+
keep_temp_file=keep_temp_file,
|
|
109
|
+
env=env,
|
|
110
|
+
_encoding=_encoding,
|
|
111
|
+
**kwargs,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def run_script(
|
|
115
|
+
self,
|
|
116
|
+
title: str,
|
|
117
|
+
script: str,
|
|
118
|
+
*,
|
|
119
|
+
capture_output: bool = True,
|
|
120
|
+
check: bool = True,
|
|
121
|
+
cwd: Path | str | None = None,
|
|
122
|
+
stream: bool = True,
|
|
123
|
+
echo: bool = True,
|
|
124
|
+
dry_run: bool | None = None,
|
|
125
|
+
keep_temp_file: bool = False,
|
|
126
|
+
env: dict[str, str] | None = None,
|
|
127
|
+
**kwargs,
|
|
128
|
+
) -> subprocess.CompletedProcess[str] | subprocess.CompletedProcess[None]:
|
|
129
|
+
return _run_script(
|
|
130
|
+
title,
|
|
131
|
+
script,
|
|
132
|
+
capture_output=capture_output,
|
|
133
|
+
check=check,
|
|
134
|
+
cwd=cwd,
|
|
135
|
+
stream=stream,
|
|
136
|
+
echo=echo,
|
|
137
|
+
dry_run=self.obj.dry_run if dry_run is None else dry_run,
|
|
138
|
+
keep_temp_file=keep_temp_file,
|
|
139
|
+
env=env,
|
|
140
|
+
**kwargs,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class BakeCommand(TyperCommand):
|
|
145
|
+
context_class: type[click.Context] = Context
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import errno
|
|
2
|
+
import sys
|
|
3
|
+
from contextlib import contextmanager
|
|
4
|
+
from gettext import gettext
|
|
5
|
+
from typing import TextIO, cast
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
from typer.core import HAS_RICH, MarkupMode
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@contextmanager
|
|
12
|
+
def typer_exception_handler(
|
|
13
|
+
*,
|
|
14
|
+
standalone_mode: bool,
|
|
15
|
+
rich_markup_mode: MarkupMode,
|
|
16
|
+
):
|
|
17
|
+
# Reference code: https://github.com/fastapi/typer/blob/da9c4c67f3d8e4acd5f76e8909503bb999f1b751/typer/core.py#L186-L248
|
|
18
|
+
try:
|
|
19
|
+
try:
|
|
20
|
+
yield
|
|
21
|
+
except EOFError as e:
|
|
22
|
+
click.echo(file=sys.stderr)
|
|
23
|
+
raise click.Abort() from e
|
|
24
|
+
except KeyboardInterrupt as e:
|
|
25
|
+
raise click.exceptions.Exit(130) from e
|
|
26
|
+
except click.ClickException as e:
|
|
27
|
+
if not standalone_mode:
|
|
28
|
+
raise
|
|
29
|
+
if HAS_RICH and rich_markup_mode is not None:
|
|
30
|
+
from typer import rich_utils
|
|
31
|
+
|
|
32
|
+
rich_utils.rich_format_error(e)
|
|
33
|
+
else:
|
|
34
|
+
e.show()
|
|
35
|
+
sys.exit(e.exit_code)
|
|
36
|
+
except OSError as e:
|
|
37
|
+
if e.errno == errno.EPIPE:
|
|
38
|
+
sys.stdout = cast(TextIO, click.utils.PacifyFlushWrapper(sys.stdout))
|
|
39
|
+
sys.stderr = cast(TextIO, click.utils.PacifyFlushWrapper(sys.stderr))
|
|
40
|
+
sys.exit(1)
|
|
41
|
+
raise
|
|
42
|
+
except click.exceptions.Exit as e:
|
|
43
|
+
if standalone_mode:
|
|
44
|
+
sys.exit(e.exit_code)
|
|
45
|
+
else:
|
|
46
|
+
# return exit code to caller
|
|
47
|
+
raise
|
|
48
|
+
except click.Abort:
|
|
49
|
+
if not standalone_mode:
|
|
50
|
+
raise
|
|
51
|
+
if HAS_RICH and rich_markup_mode is not None:
|
|
52
|
+
from typer import rich_utils
|
|
53
|
+
|
|
54
|
+
rich_utils.rich_abort_error()
|
|
55
|
+
else:
|
|
56
|
+
click.echo(gettext("Aborted!"), file=sys.stderr)
|
|
57
|
+
sys.exit(1)
|
bake/cli/common/obj.py
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
import click
|
|
10
|
+
import typer
|
|
11
|
+
from pydantic import ValidationError
|
|
12
|
+
from rich.traceback import Traceback
|
|
13
|
+
from typer.core import MarkupMode
|
|
14
|
+
from typer.main import get_command_from_info
|
|
15
|
+
|
|
16
|
+
from bake.bakebook.get import (
|
|
17
|
+
get_bakebook_from_target_dir_path,
|
|
18
|
+
resolve_bakefile_path,
|
|
19
|
+
)
|
|
20
|
+
from bake.ui import console, setup_logging
|
|
21
|
+
from bake.utils.constants import (
|
|
22
|
+
DEFAULT_BAKEBOOK_NAME,
|
|
23
|
+
DEFAULT_CHDIR,
|
|
24
|
+
DEFAULT_FILE_NAME,
|
|
25
|
+
DEFAULT_IS_CHAIN_COMMAND,
|
|
26
|
+
GET_BAKEFILE_OBJECT,
|
|
27
|
+
)
|
|
28
|
+
from bake.utils.exceptions import BakebookError, BakefileNotFoundError
|
|
29
|
+
|
|
30
|
+
from .callback import validate_file_name
|
|
31
|
+
from .exception_handler import typer_exception_handler
|
|
32
|
+
from .params import (
|
|
33
|
+
bakebook_name_option,
|
|
34
|
+
chdir_option,
|
|
35
|
+
dry_run_option,
|
|
36
|
+
file_name_option,
|
|
37
|
+
is_chain_commands_option,
|
|
38
|
+
remaining_args_argument,
|
|
39
|
+
verbosity_option,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
if TYPE_CHECKING:
|
|
43
|
+
from bake.bakebook.bakebook import Bakebook
|
|
44
|
+
|
|
45
|
+
logger = logging.Logger(__name__)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class BakefileObject:
|
|
50
|
+
chdir: Path
|
|
51
|
+
file_name: str
|
|
52
|
+
bakebook_name: str
|
|
53
|
+
bakefile_path: Path | None = None
|
|
54
|
+
bakebook: "Bakebook | None" = None
|
|
55
|
+
verbosity: int = 0
|
|
56
|
+
dry_run: bool = False
|
|
57
|
+
remaining_args: list[str] | None = None
|
|
58
|
+
is_chain_commands: bool = False
|
|
59
|
+
|
|
60
|
+
def __post_init__(self):
|
|
61
|
+
validate_file_name(self.file_name)
|
|
62
|
+
|
|
63
|
+
def resolve_bakefile_path(self) -> Path | None:
|
|
64
|
+
if self.bakefile_path is not None:
|
|
65
|
+
return self.bakefile_path
|
|
66
|
+
|
|
67
|
+
with contextlib.suppress(BakefileNotFoundError):
|
|
68
|
+
self.bakefile_path = resolve_bakefile_path(chdir=self.chdir, file_name=self.file_name)
|
|
69
|
+
|
|
70
|
+
return self.bakefile_path
|
|
71
|
+
|
|
72
|
+
def get_bakebook(self, allow_missing: bool):
|
|
73
|
+
if self.bakebook is not None:
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
if self.bakefile_path is None:
|
|
78
|
+
self.bakefile_path = resolve_bakefile_path(
|
|
79
|
+
chdir=self.chdir, file_name=self.file_name
|
|
80
|
+
)
|
|
81
|
+
self.bakebook = get_bakebook_from_target_dir_path(
|
|
82
|
+
target_dir_path=self.bakefile_path, bakebook_name=self.bakebook_name
|
|
83
|
+
)
|
|
84
|
+
except BakefileNotFoundError as e:
|
|
85
|
+
if allow_missing:
|
|
86
|
+
return
|
|
87
|
+
console.error(str(e))
|
|
88
|
+
raise SystemExit(1) from e
|
|
89
|
+
except BakebookError as e:
|
|
90
|
+
if allow_missing:
|
|
91
|
+
return
|
|
92
|
+
exc_to_show = e.__cause__ if e.__cause__ else e
|
|
93
|
+
|
|
94
|
+
if exc_to_show.__class__ in {ValidationError, BakebookError}:
|
|
95
|
+
console.err.print(
|
|
96
|
+
f"[bold red]{exc_to_show.__class__.__name__}:[/bold red]", end=" "
|
|
97
|
+
)
|
|
98
|
+
console.err.print(exc_to_show)
|
|
99
|
+
console.err.print(f"Searched in: {self.chdir.resolve()}\n")
|
|
100
|
+
else:
|
|
101
|
+
console.err.print(
|
|
102
|
+
Traceback.from_exception(
|
|
103
|
+
type(exc_to_show), exc_to_show, exc_to_show.__traceback__
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
raise SystemExit(1) from e
|
|
107
|
+
|
|
108
|
+
def warn_if_no_bakebook(self, color_echo: bool):
|
|
109
|
+
if self.bakebook is None:
|
|
110
|
+
_ = color_echo # Color handled by console module
|
|
111
|
+
console.warning(f"Bakebook `{self.bakebook_name}` not found in `{self.file_name}`")
|
|
112
|
+
console.echo(f"Searched in: {self.chdir.resolve()}\n")
|
|
113
|
+
|
|
114
|
+
def setup_logging(self):
|
|
115
|
+
level_map = {0: logging.WARNING, 1: logging.INFO, 2: logging.DEBUG}
|
|
116
|
+
log_level = level_map.get(self.verbosity, logging.WARNING)
|
|
117
|
+
setup_logging(level_per_module={"": log_level}, is_pretty_log=True)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
bakefile_obj_app = typer.Typer()
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def get_args(args: list[str] | None = None, windows_expand_args: bool = True) -> list[str]:
|
|
124
|
+
if args is None:
|
|
125
|
+
args = sys.argv[1:]
|
|
126
|
+
|
|
127
|
+
# Covered in Click tests
|
|
128
|
+
if os.name == "nt" and windows_expand_args: # pragma: no cover
|
|
129
|
+
args = click.utils._expand_args(args)
|
|
130
|
+
else:
|
|
131
|
+
args = list(args)
|
|
132
|
+
|
|
133
|
+
return args
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def bakefile_obj_app_args(
|
|
137
|
+
args: list[str] | None = None,
|
|
138
|
+
windows_expand_args: bool = True,
|
|
139
|
+
) -> list[str]:
|
|
140
|
+
# source from https://github.com/fastapi/typer/blob/b7f39eaad60141988f5d9a58df72c44d6128cd53/typer/core.py#L175-L185
|
|
141
|
+
|
|
142
|
+
args = get_args(args=args, windows_expand_args=windows_expand_args)
|
|
143
|
+
|
|
144
|
+
prohibited_non_bakefile_obj_app_args: list[str] = ["--help", "--version"]
|
|
145
|
+
|
|
146
|
+
args = [arg for arg in args if arg not in prohibited_non_bakefile_obj_app_args]
|
|
147
|
+
return args
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def is_bakebook_optional(remaining_args: list[str] | None) -> bool:
|
|
151
|
+
args = get_args()
|
|
152
|
+
|
|
153
|
+
some_args: list[str] = ["--help", "--version"]
|
|
154
|
+
is_some_args = len([arg for arg in args if arg in some_args]) > 0
|
|
155
|
+
return is_some_args or remaining_args is None or remaining_args == []
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@bakefile_obj_app.command(
|
|
159
|
+
name=GET_BAKEFILE_OBJECT,
|
|
160
|
+
hidden=True,
|
|
161
|
+
context_settings={
|
|
162
|
+
"allow_extra_args": True,
|
|
163
|
+
"allow_interspersed_args": False,
|
|
164
|
+
"ignore_unknown_options": True,
|
|
165
|
+
},
|
|
166
|
+
)
|
|
167
|
+
def _get_bakefile_object(
|
|
168
|
+
ctx: typer.Context,
|
|
169
|
+
chdir: chdir_option = DEFAULT_CHDIR,
|
|
170
|
+
file_name: file_name_option = DEFAULT_FILE_NAME,
|
|
171
|
+
bakebook_name: bakebook_name_option = DEFAULT_BAKEBOOK_NAME,
|
|
172
|
+
is_chain_commands: is_chain_commands_option = DEFAULT_IS_CHAIN_COMMAND,
|
|
173
|
+
remaining_args: remaining_args_argument = None,
|
|
174
|
+
verbosity: verbosity_option = 0,
|
|
175
|
+
dry_run: dry_run_option = False,
|
|
176
|
+
):
|
|
177
|
+
_ = ctx
|
|
178
|
+
return BakefileObject(
|
|
179
|
+
chdir=chdir,
|
|
180
|
+
file_name=file_name,
|
|
181
|
+
bakebook_name=bakebook_name,
|
|
182
|
+
verbosity=verbosity,
|
|
183
|
+
dry_run=dry_run,
|
|
184
|
+
remaining_args=remaining_args,
|
|
185
|
+
is_chain_commands=is_chain_commands,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def get_bakefile_object(rich_markup_mode: MarkupMode) -> BakefileObject:
|
|
190
|
+
with typer_exception_handler(standalone_mode=True, rich_markup_mode=rich_markup_mode):
|
|
191
|
+
args = bakefile_obj_app_args()
|
|
192
|
+
|
|
193
|
+
for registered_command in bakefile_obj_app.registered_commands:
|
|
194
|
+
if registered_command.name != GET_BAKEFILE_OBJECT:
|
|
195
|
+
continue
|
|
196
|
+
|
|
197
|
+
command = get_command_from_info(
|
|
198
|
+
registered_command,
|
|
199
|
+
pretty_exceptions_short=bakefile_obj_app.pretty_exceptions_short,
|
|
200
|
+
rich_markup_mode=bakefile_obj_app.rich_markup_mode,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
with command.make_context(info_name=GET_BAKEFILE_OBJECT, args=args) as ctx:
|
|
204
|
+
bakefile_obj = command.invoke(ctx)
|
|
205
|
+
if not isinstance(bakefile_obj, BakefileObject):
|
|
206
|
+
msg = (
|
|
207
|
+
f"Expected `bakefile_obj` to be an instance of "
|
|
208
|
+
f"{BakefileObject.__name__}, got {type(bakefile_obj).__name__}"
|
|
209
|
+
)
|
|
210
|
+
raise TypeError(msg)
|
|
211
|
+
return bakefile_obj
|
|
212
|
+
|
|
213
|
+
raise RuntimeError(
|
|
214
|
+
f"Failed to find the `{GET_BAKEFILE_OBJECT}` command in registered commands. "
|
|
215
|
+
f"This should never happen - please report this bug."
|
|
216
|
+
)
|
|
@@ -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
|