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.
Files changed (73) hide show
  1. bake/__init__.py +9 -0
  2. bake/bakebook/bakebook.py +85 -0
  3. bake/bakebook/decorator.py +50 -0
  4. bake/bakebook/get.py +175 -0
  5. bake/cli/bake/__init__.py +3 -0
  6. bake/cli/bake/__main__.py +5 -0
  7. bake/cli/bake/main.py +74 -0
  8. bake/cli/bake/reinvocation.py +63 -0
  9. bake/cli/bakefile/__init__.py +3 -0
  10. bake/cli/bakefile/__main__.py +5 -0
  11. bake/cli/bakefile/add_inline.py +29 -0
  12. bake/cli/bakefile/export.py +212 -0
  13. bake/cli/bakefile/find_python.py +18 -0
  14. bake/cli/bakefile/init.py +56 -0
  15. bake/cli/bakefile/lint.py +77 -0
  16. bake/cli/bakefile/main.py +43 -0
  17. bake/cli/bakefile/uv.py +146 -0
  18. bake/cli/common/app.py +54 -0
  19. bake/cli/common/callback.py +13 -0
  20. bake/cli/common/context.py +145 -0
  21. bake/cli/common/exception_handler.py +57 -0
  22. bake/cli/common/obj.py +216 -0
  23. bake/cli/common/params.py +72 -0
  24. bake/cli/utils/__init__.py +0 -0
  25. bake/cli/utils/version.py +18 -0
  26. bake/manage/__init__.py +0 -0
  27. bake/manage/add_inline.py +71 -0
  28. bake/manage/find_python.py +210 -0
  29. bake/manage/lint.py +101 -0
  30. bake/manage/run_uv.py +88 -0
  31. bake/manage/write_bakefile.py +20 -0
  32. bake/py.typed +0 -0
  33. bake/samples/__init__.py +0 -0
  34. bake/samples/simple.py +8 -0
  35. bake/ui/__init__.py +11 -0
  36. bake/ui/console.py +58 -0
  37. bake/ui/logger/__init__.py +33 -0
  38. bake/ui/logger/capsys.py +158 -0
  39. bake/ui/logger/setup.py +53 -0
  40. bake/ui/logger/utils.py +215 -0
  41. bake/ui/params.py +5 -0
  42. bake/ui/run/__init__.py +5 -0
  43. bake/ui/run/run.py +546 -0
  44. bake/ui/run/script.py +74 -0
  45. bake/ui/run/splitter.py +249 -0
  46. bake/ui/run/uv.py +83 -0
  47. bake/ui/style.py +2 -0
  48. bake/utils/__init__.py +11 -0
  49. bake/utils/constants.py +21 -0
  50. {bakefile → bake/utils}/env.py +3 -1
  51. bake/utils/exceptions.py +17 -0
  52. {bakefile-0.0.4.dist-info → bakefile-0.0.6.dist-info}/METADATA +15 -2
  53. bakefile-0.0.6.dist-info/RECORD +63 -0
  54. {bakefile-0.0.4.dist-info → bakefile-0.0.6.dist-info}/WHEEL +2 -2
  55. bakefile-0.0.6.dist-info/entry_points.txt +5 -0
  56. bakelib/__init__.py +4 -0
  57. bakelib/space/__init__.py +0 -0
  58. bakelib/space/base.py +193 -0
  59. bakelib/space/python.py +80 -0
  60. bakelib/space/utils.py +118 -0
  61. bakefile/__init__.py +0 -13
  62. bakefile/cli/bake/__init__.py +0 -3
  63. bakefile/cli/bake/main.py +0 -127
  64. bakefile/cli/bake/resolve_bakebook.py +0 -103
  65. bakefile/cli/bake/utils.py +0 -25
  66. bakefile/cli/bakefile.py +0 -19
  67. bakefile/cli/utils/version.py +0 -9
  68. bakefile/exceptions.py +0 -9
  69. bakefile-0.0.4.dist-info/RECORD +0 -16
  70. bakefile-0.0.4.dist-info/entry_points.txt +0 -4
  71. {bakefile/cli/utils → bake/bakebook}/__init__.py +0 -0
  72. {bakefile → bake}/cli/__init__.py +0 -0
  73. /bakefile/py.typed → /bake/cli/common/__init__.py +0 -0
bakelib/space/base.py ADDED
@@ -0,0 +1,193 @@
1
+ from pathlib import Path
2
+ from typing import Annotated, Literal
3
+
4
+ import orjson
5
+ import typer
6
+
7
+ from bake import Bakebook, Context, command
8
+ from bake.ui import console
9
+
10
+ from .utils import (
11
+ HOMWBREW_BIN,
12
+ LOCAL_BIN,
13
+ VENV_BIN,
14
+ PlatformType,
15
+ ToolInfo,
16
+ get_expected_paths,
17
+ get_platform,
18
+ remove_git_clean_candidates,
19
+ setup_brew,
20
+ setup_bun,
21
+ setup_uv,
22
+ setup_uv_tool,
23
+ )
24
+
25
+
26
+ class BaseSpace(Bakebook):
27
+ def _no_implementation(self, ctx: Context | None = None, *args, **kwargs):
28
+ _ = ctx, args, kwargs
29
+ console.error("No implementation")
30
+ raise typer.Exit(1)
31
+
32
+ @command(help="Run linters and formatters")
33
+ def lint(self, ctx: Context) -> None:
34
+ ctx.run('bunx prettier@latest --write "**/*.{js,jsx,ts,tsx,css,json,json5,yaml,yml,md\'}"')
35
+
36
+ @command(help="Run unit tests")
37
+ def test(self, ctx: Context) -> None:
38
+ self._no_implementation(ctx)
39
+
40
+ @command(help="Run integration tests")
41
+ def test_integration(self, ctx: Context) -> None:
42
+ self._no_implementation(ctx)
43
+
44
+ @command(help="Run all tests")
45
+ def test_all(self, ctx: Context) -> None:
46
+ self._no_implementation(ctx)
47
+
48
+ @command(help="Clean gitignored files with optional exclusions")
49
+ def clean(
50
+ self,
51
+ ctx: Context,
52
+ exclude_patterns: Annotated[
53
+ list[str] | None,
54
+ typer.Option(
55
+ "--exclude-patterns",
56
+ "-e",
57
+ help="Patterns to exclude",
58
+ ),
59
+ ] = None,
60
+ use_default_excludes: Annotated[
61
+ bool,
62
+ typer.Option(
63
+ "--no-default-excludes",
64
+ help="Do not apply default exclude patterns",
65
+ is_flag=True,
66
+ ),
67
+ ] = False,
68
+ ) -> None:
69
+ results = ctx.run("git clean -fdX -n", stream=False, dry_run=False, echo=True)
70
+
71
+ exclude_patterns: set[str] = set(exclude_patterns if exclude_patterns else [])
72
+
73
+ if not use_default_excludes:
74
+ exclude_patterns |= {".env", ".cache"}
75
+
76
+ console.err.print(f"Exclude pattens: {exclude_patterns}")
77
+
78
+ remove_git_clean_candidates(
79
+ git_clean_dry_run_output=results.stdout,
80
+ exclude_patterns=exclude_patterns,
81
+ dry_run=ctx.dry_run,
82
+ )
83
+
84
+ @command(help="Clean all gitignored files")
85
+ def clean_all(self, ctx: Context) -> None:
86
+ ctx.run("git clean -fdX")
87
+
88
+ def setup_tool_managers(self, ctx: Context, platform: PlatformType) -> None:
89
+ _ = platform
90
+ setup_brew(ctx)
91
+
92
+ def setup_tools(self, ctx: Context, platform: PlatformType) -> None:
93
+ _ = platform
94
+ setup_bun(ctx)
95
+ setup_uv(ctx)
96
+ setup_uv_tool(ctx)
97
+
98
+ def setup_project(self, ctx: Context) -> None:
99
+ ctx.run("uv run pre-commit install")
100
+
101
+ @command(help="Setup development environment")
102
+ def setup_dev(self, ctx: Context) -> None:
103
+ platform = get_platform()
104
+ console.echo(f"Detected platform: {platform}")
105
+
106
+ if platform != "macos":
107
+ console.warning(f"Platform '{platform}' is not supported. Running in dry-run mode.")
108
+ overridden_dry_run = True
109
+ else:
110
+ overridden_dry_run = ctx.dry_run
111
+
112
+ with ctx.override_dry_run(overridden_dry_run):
113
+ self.clean(ctx=ctx)
114
+ self.setup_tool_managers(ctx=ctx, platform=platform)
115
+ self.setup_tools(ctx=ctx, platform=platform)
116
+ self.setup_project(ctx=ctx)
117
+
118
+ def _assert_which_path(
119
+ self,
120
+ ctx: Context,
121
+ tool_name: str,
122
+ tool_info: ToolInfo,
123
+ ) -> bool:
124
+ result = ctx.run(f"which {tool_name}", stream=False)
125
+ if ctx.dry_run:
126
+ return True
127
+ actual_path = Path(result.stdout.strip())
128
+
129
+ if actual_path in set(tool_info.expected_paths):
130
+ console.success(f"{tool_name}: {actual_path}")
131
+ return True
132
+
133
+ console.warning(f"{tool_name}: unexpected location (got {actual_path})")
134
+ return False
135
+
136
+ def _get_tools(self) -> dict[str, ToolInfo]:
137
+ return {
138
+ # homebrew only
139
+ "bun": ToolInfo(expected_paths=get_expected_paths("bun", {HOMWBREW_BIN})),
140
+ # homebrew or venv
141
+ "uv": ToolInfo(expected_paths=get_expected_paths("uv", {HOMWBREW_BIN, VENV_BIN})),
142
+ # local or venv
143
+ "bakefile": ToolInfo(
144
+ expected_paths=get_expected_paths("bakefile", {LOCAL_BIN, VENV_BIN})
145
+ ),
146
+ "pre-commit": ToolInfo(
147
+ expected_paths=get_expected_paths("pre-commit", {LOCAL_BIN, VENV_BIN})
148
+ ),
149
+ }
150
+
151
+ @command(help="List development tools")
152
+ def tools(
153
+ self,
154
+ ctx: Context,
155
+ format: Annotated[
156
+ Literal["json", "names"],
157
+ typer.Option("--format", "-f", help="Output format"),
158
+ ] = "json",
159
+ ) -> None:
160
+ _ = ctx
161
+ tools = self._get_tools()
162
+ if format == "json":
163
+ output: dict[str, dict[str, str | None]] = {k: v.model_dump() for k, v in tools.items()}
164
+ console.echo(orjson.dumps(output, option=orjson.OPT_INDENT_2).decode())
165
+ else:
166
+ console.echo("\n".join(sorted(tools.keys())))
167
+
168
+ @command(help="Assert development environment setup")
169
+ def assert_setup_dev(
170
+ self,
171
+ ctx: Context,
172
+ skip_test: Annotated[
173
+ bool,
174
+ typer.Option(
175
+ "--skip-test",
176
+ "-s",
177
+ help="Skip running tests",
178
+ is_flag=True,
179
+ ),
180
+ ] = False,
181
+ ) -> None:
182
+ tools = self._get_tools()
183
+ for tool_name, tool_info in tools.items():
184
+ self._assert_which_path(ctx, tool_name, tool_info)
185
+
186
+ self.lint(ctx)
187
+ if not skip_test:
188
+ self.test(ctx)
189
+
190
+ @command(help="Upgrade all dependencies")
191
+ def update(self, ctx: Context) -> None:
192
+ ctx.run("uv python upgrade")
193
+ ctx.run("uv tool upgrade --all")
@@ -0,0 +1,80 @@
1
+ from pathlib import Path
2
+
3
+ from bake import Context, params
4
+
5
+ from .base import BaseSpace, ToolInfo
6
+ from .utils import VENV_BIN, get_expected_paths
7
+
8
+
9
+ def _get_python_version() -> str | None:
10
+ path = Path(".python-version")
11
+ if not path.exists():
12
+ return None
13
+ return path.read_text().strip()
14
+
15
+
16
+ class PythonSpace(BaseSpace):
17
+ def _get_tools(self) -> dict[str, ToolInfo]:
18
+ tools = super()._get_tools()
19
+ tools["python"] = ToolInfo(
20
+ version=_get_python_version(),
21
+ expected_paths=list(get_expected_paths("python", {VENV_BIN})),
22
+ )
23
+ return tools
24
+
25
+ def lint(self, ctx: Context) -> None:
26
+ super().lint(ctx=ctx)
27
+
28
+ ctx.run(
29
+ "uv run toml-sort --sort-inline-arrays --in-place "
30
+ "--sort-first=project,dependency-groups pyproject.toml"
31
+ )
32
+ ctx.run("uv run ruff format --exit-non-zero-on-format .")
33
+ ctx.run("uv run ruff check --fix --exit-non-zero-on-fix .")
34
+ ctx.run("uv run ty check --error-on-warning --no-progress .")
35
+ ctx.run("uv run deptry .")
36
+
37
+ def _test(self, ctx: Context, *, tests_path: str, verbose: bool = False) -> None:
38
+ cmd = (
39
+ f"uv run pytest {tests_path} --cov=src --cov-report=html"
40
+ " --cov-report=term-missing --cov-report=xml"
41
+ )
42
+
43
+ if verbose:
44
+ cmd += " -s -v"
45
+
46
+ ctx.run(cmd)
47
+
48
+ def test_integration(
49
+ self,
50
+ ctx: Context,
51
+ verbose: params.verbose_bool = False,
52
+ ) -> None:
53
+ integration_tests_path = "tests/integration/"
54
+ if Path(integration_tests_path).exists():
55
+ tests_path = integration_tests_path
56
+ self._test(ctx, tests_path=tests_path, verbose=verbose)
57
+ else:
58
+ self._no_implementation(ctx)
59
+
60
+ def test(self, ctx: Context) -> None:
61
+ unit_tests_path = "tests/unit/"
62
+ tests_path = unit_tests_path if Path(unit_tests_path).exists() else "tests/"
63
+ self._test(ctx, tests_path=tests_path)
64
+
65
+ def test_all(self, ctx: Context) -> None:
66
+ unit_tests_path = "tests/unit/"
67
+ if Path(unit_tests_path).exists():
68
+ tests_path = "tests/"
69
+ self._test(ctx, tests_path=tests_path)
70
+ else:
71
+ self._no_implementation(ctx)
72
+
73
+ def setup_project(self, ctx: Context) -> None:
74
+ super().setup_project(ctx=ctx)
75
+ ctx.run("uv sync --all-extras --all-groups --frozen")
76
+
77
+ def update(self, ctx: Context) -> None:
78
+ super().update(ctx=ctx)
79
+ ctx.run("uv lock --upgrade")
80
+ ctx.run("uv sync --all-extras --all-groups")
bakelib/space/utils.py ADDED
@@ -0,0 +1,118 @@
1
+ import shutil
2
+ import sys
3
+ from enum import Enum
4
+ from pathlib import Path
5
+ from typing import Literal
6
+
7
+ import pathspec
8
+ from pathspec.patterns.gitignore.basic import GitIgnoreBasicPattern
9
+ from pydantic import BaseModel, Field
10
+
11
+ from bake import Context
12
+ from bake.ui import console
13
+
14
+
15
+ def setup_brew(ctx: Context) -> None:
16
+ ctx.run("brew update")
17
+ ctx.run("brew upgrade")
18
+ ctx.run("brew cleanup")
19
+ ctx.run("brew list")
20
+ ctx.run("brew leaves")
21
+
22
+
23
+ class ToolInfo(BaseModel):
24
+ version: str | None = None
25
+ expected_paths: list[Path] = Field(default_factory=list, exclude=True)
26
+
27
+
28
+ class Platform(Enum):
29
+ MACOS = "macos"
30
+ LINUX = "linux"
31
+ WINDOWS = "windows"
32
+ OTHER = "other"
33
+
34
+
35
+ PlatformType = Literal["macos", "linux", "windows", "other"]
36
+
37
+
38
+ def get_platform() -> PlatformType:
39
+ if sys.platform == "darwin":
40
+ return Platform.MACOS.value
41
+ elif sys.platform == "linux":
42
+ return Platform.LINUX.value
43
+ elif sys.platform == "win32":
44
+ return Platform.WINDOWS.value
45
+ return Platform.OTHER.value
46
+
47
+
48
+ def setup_uv(ctx: Context) -> None:
49
+ ctx.run("brew install uv")
50
+ ctx.run("uv python upgrade")
51
+ ctx.run("uv tool upgrade --all")
52
+ ctx.run("uv tool update-shell")
53
+
54
+
55
+ def setup_bun(ctx: Context) -> None:
56
+ ctx.run("brew install oven-sh/bun/bun")
57
+
58
+
59
+ def setup_uv_tool(ctx: Context) -> None:
60
+ ctx.run("uv tool install bakefile")
61
+ ctx.run("uv tool install pre-commit")
62
+
63
+
64
+ HOMWBREW_BIN = Path("/opt/homebrew/bin")
65
+ LOCAL_BIN = Path.home() / ".local" / "bin"
66
+ VENV_BIN = Path.cwd() / ".venv" / "bin"
67
+
68
+
69
+ def get_expected_paths(tool: str, locations: set[Path]) -> list[Path]:
70
+ return [loc / tool for loc in locations]
71
+
72
+
73
+ def _skip_msg(path: Path, suffix: str, dry_run: bool) -> None:
74
+ verb = "Would skip" if dry_run else "Skipping"
75
+ console.echo(f"[yellow]~[/yellow] {verb} {suffix}{path}")
76
+
77
+
78
+ def _remove_msg(path: Path, dry_run: bool) -> None:
79
+ verb = "Would remove" if dry_run else "Removing"
80
+ console.echo(f"[red]-[/red] [dim]{verb}[/dim] {path}")
81
+
82
+
83
+ def _should_remove_path(path: Path, dry_run: bool) -> None:
84
+ _remove_msg(path, dry_run)
85
+ if dry_run:
86
+ return
87
+
88
+ if path.is_dir():
89
+ shutil.rmtree(path)
90
+ else:
91
+ path.unlink(missing_ok=True)
92
+
93
+
94
+ def remove_git_clean_candidates(
95
+ git_clean_dry_run_output: str, exclude_patterns: set[str], dry_run: bool
96
+ ) -> None:
97
+ spec = pathspec.PathSpec.from_lines(
98
+ GitIgnoreBasicPattern,
99
+ exclude_patterns,
100
+ )
101
+
102
+ for line in git_clean_dry_run_output.splitlines():
103
+ line = line.strip()
104
+ if not line.startswith("Would remove "):
105
+ continue
106
+
107
+ rel_path = line.removeprefix("Would remove ").strip()
108
+ path = Path(rel_path)
109
+
110
+ if spec.match_file(rel_path):
111
+ _skip_msg(path, "", dry_run)
112
+ continue
113
+
114
+ if path.is_dir() and (path / ".git").exists():
115
+ _skip_msg(path, "git repository ", dry_run)
116
+ continue
117
+
118
+ _should_remove_path(path, dry_run)
bakefile/__init__.py DELETED
@@ -1,13 +0,0 @@
1
- from importlib.metadata import PackageNotFoundError, version
2
-
3
- __all__ = ["__version__"]
4
-
5
-
6
- def _get_version() -> str:
7
- try:
8
- return version("bakefile")
9
- except PackageNotFoundError:
10
- return "0.0.0"
11
-
12
-
13
- __version__ = _get_version()
@@ -1,3 +0,0 @@
1
- from bakefile.cli.bake.main import main
2
-
3
- __all__ = ["main"]
bakefile/cli/bake/main.py DELETED
@@ -1,127 +0,0 @@
1
- import typer
2
- from typer.main import get_command_from_info
3
-
4
- from bakefile import env
5
- from bakefile.cli.bake.resolve_bakebook import resolve_bakebook
6
- from bakefile.cli.utils.version import version_callback
7
- from bakefile.exceptions import BakebookError
8
-
9
- from .utils import get_bakebook_args
10
-
11
- rich_markup_mode = "rich" if env.should_use_colors() else None
12
-
13
-
14
- bake_app = typer.Typer(
15
- add_completion=False,
16
- rich_markup_mode=rich_markup_mode,
17
- )
18
-
19
- local_bake_app = typer.Typer(
20
- add_completion=False,
21
- rich_markup_mode=rich_markup_mode,
22
- )
23
-
24
-
25
- GET_BAKEBOOK = "get_bakebook"
26
-
27
-
28
- # Common option definitions (reused across callbacks and commands)
29
- chdir_option = typer.Option(None, "-C", "--chdir", help="Change directory before running")
30
- file_name_option = typer.Option("bakefile.py", "--file-name", "-f", help="Path to bakefile.py")
31
- bakebook_name_option = typer.Option(
32
- "bakebook", "--book-name", "-b", help="Name of bakebook object to retrieve"
33
- )
34
- version_option = typer.Option(
35
- False,
36
- "--version",
37
- help="Show version and exit",
38
- callback=version_callback,
39
- is_eager=True,
40
- )
41
-
42
-
43
- def show_help_if_no_command(ctx: typer.Context) -> None:
44
- if ctx.invoked_subcommand is None:
45
- typer.echo(ctx.get_help())
46
-
47
-
48
- @local_bake_app.callback(
49
- invoke_without_command=True,
50
- )
51
- def local_bake_app_callback(
52
- ctx: typer.Context,
53
- _chdir: str = chdir_option,
54
- _file_name: str = file_name_option,
55
- _bakebook_name: str = bakebook_name_option,
56
- _version: bool = version_option,
57
- ):
58
- show_help_if_no_command(ctx)
59
-
60
-
61
- @bake_app.callback(
62
- invoke_without_command=True,
63
- )
64
- def bake_app_callback(
65
- ctx: typer.Context,
66
- _chdir: str = chdir_option,
67
- _file_name: str = file_name_option,
68
- _bakebook_name: str = bakebook_name_option,
69
- _version: bool = version_option,
70
- ):
71
- show_help_if_no_command(ctx)
72
-
73
-
74
- @bake_app.command(
75
- name=GET_BAKEBOOK,
76
- hidden=True,
77
- context_settings={
78
- "allow_extra_args": True,
79
- "allow_interspersed_args": False,
80
- "ignore_unknown_options": True,
81
- },
82
- )
83
- def get_bakebook(
84
- chdir: str = chdir_option,
85
- file_name: str = file_name_option,
86
- bakebook_name: str = bakebook_name_option,
87
- ):
88
- try:
89
- return resolve_bakebook(file_name=file_name, bakebook_name=bakebook_name, chdir=chdir)
90
- except BakebookError as e:
91
- # Print error with context about what values were used
92
- context_parts = []
93
- context_parts.append(f"chdir={chdir!r}")
94
- context_parts.append(f"file_name={file_name!r}")
95
- context_parts.append(f"bakebook_name={bakebook_name!r}")
96
-
97
- typer.secho("⚠️ ", fg="yellow", err=True, nl=False)
98
- typer.secho(str(e), fg="yellow", bold=True, err=True)
99
- if context_parts:
100
- typer.secho(f"({', '.join(context_parts)})", fg="yellow", err=True)
101
- return None
102
-
103
-
104
- def try_get_local_bake_app() -> typer.Typer | None:
105
- args = get_bakebook_args()
106
-
107
- for registered_command in bake_app.registered_commands:
108
- if registered_command.name == GET_BAKEBOOK:
109
- command = get_command_from_info(
110
- registered_command,
111
- pretty_exceptions_short=bake_app.pretty_exceptions_short,
112
- rich_markup_mode=bake_app.rich_markup_mode,
113
- )
114
- with command.make_context(info_name=GET_BAKEBOOK, args=args) as ctx:
115
- bakebook = command.invoke(ctx)
116
- if bakebook is not None:
117
- local_bake_app.add_typer(bakebook)
118
- return local_bake_app
119
- return None
120
-
121
-
122
- def main():
123
- local_bake_app = try_get_local_bake_app()
124
- if local_bake_app is None:
125
- bake_app()
126
- else:
127
- local_bake_app()
@@ -1,103 +0,0 @@
1
- import importlib.util
2
- import os
3
- import pathlib
4
- import sys
5
- import types
6
- from typing import Any, TypeVar
7
-
8
- import typer
9
-
10
- from bakefile.exceptions import BakebookError
11
-
12
- T = TypeVar("T")
13
-
14
-
15
- def change_directory(path: str) -> None:
16
- if not path or not path.strip():
17
- raise BakebookError("Directory path cannot be empty")
18
- dir_path = pathlib.Path(path)
19
- if not dir_path.exists():
20
- raise BakebookError(f"Directory not found: {path}")
21
- if not dir_path.is_dir():
22
- raise BakebookError(f"Not a directory: {path}")
23
- os.chdir(dir_path)
24
-
25
-
26
- def validate_file_name(file_name: str) -> bool:
27
- if "/" in file_name or "\\" in file_name:
28
- raise BakebookError(f"File name must not contain path separators: {file_name}")
29
- if not file_name.endswith(".py"):
30
- raise BakebookError(f"File name must end with .py: {file_name}")
31
- return True
32
-
33
-
34
- def resolve_file_path(file_name: str) -> pathlib.Path:
35
- path = pathlib.Path.cwd() / file_name
36
- if not path.exists():
37
- raise BakebookError(f"File not found: {file_name}")
38
- return path
39
-
40
-
41
- def load_module(path: pathlib.Path) -> types.ModuleType:
42
- module_name = "bakefile"
43
- spec = importlib.util.spec_from_file_location(module_name, path)
44
- if spec is None or spec.loader is None:
45
- raise BakebookError(f"Failed to load: {path}")
46
-
47
- module: types.ModuleType = importlib.util.module_from_spec(spec)
48
- sys.modules[module_name] = module
49
- spec.loader.exec_module(module)
50
- return module
51
-
52
-
53
- # Update this is lowest python support is >= 3.12
54
- # Use a generic type parameter for this function instead of a "TypeVar".
55
- def validate_bakebook(bakebook: Any, bakebook_name: str, expected_type: type[T]) -> T:
56
- """Validate bakebook is of expected type and return it.
57
-
58
- Parameters
59
- ----------
60
- bakebook : Any
61
- The bakebook object to validate
62
- bakebook_name : str
63
- Name of the bakebook variable (for error messages)
64
- expected_type : type[T]
65
- The expected type (e.g., typer.Typer)
66
-
67
- Returns
68
- -------
69
- T
70
- The validated bakebook
71
-
72
- Raises
73
- ------
74
- BakebookError
75
- If bakebook is not of expected type
76
- """
77
- if not isinstance(bakebook, expected_type):
78
- raise BakebookError(
79
- f"Bakebook '{bakebook_name}' must be a {expected_type.__name__}, "
80
- f"got {type(bakebook).__name__}"
81
- )
82
-
83
- return bakebook
84
-
85
-
86
- def get_bakebook(module: types.ModuleType, bakebook_name: str, path: pathlib.Path) -> typer.Typer:
87
- if not hasattr(module, bakebook_name):
88
- raise BakebookError(f"No '{bakebook_name}' found in {path}")
89
- bakebook = getattr(module, bakebook_name)
90
- bakebook = validate_bakebook(
91
- bakebook=bakebook, bakebook_name=bakebook_name, expected_type=typer.Typer
92
- )
93
- return bakebook
94
-
95
-
96
- def resolve_bakebook(file_name: str, bakebook_name: str, chdir: str | None = None) -> typer.Typer:
97
- if chdir:
98
- change_directory(chdir)
99
-
100
- validate_file_name(file_name)
101
- path = resolve_file_path(file_name)
102
- module = load_module(path)
103
- return get_bakebook(module=module, bakebook_name=bakebook_name, path=path)
@@ -1,25 +0,0 @@
1
- import os
2
- import sys
3
-
4
- import click
5
-
6
-
7
- def get_bakebook_args(
8
- args: list[str] | None = None,
9
- windows_expand_args: bool = True,
10
- ) -> list[str]:
11
- # source from https://github.com/fastapi/typer/blob/b7f39eaad60141988f5d9a58df72c44d6128cd53/typer/core.py#L175-L185
12
-
13
- if args is None:
14
- args = sys.argv[1:]
15
-
16
- # Covered in Click tests
17
- if os.name == "nt" and windows_expand_args: # pragma: no cover
18
- args = click.utils._expand_args(args)
19
- else:
20
- args = list(args)
21
-
22
- non_get_bakebook_args = ["--help", "--version"]
23
-
24
- args = [arg for arg in args if arg not in non_get_bakebook_args]
25
- return args
bakefile/cli/bakefile.py DELETED
@@ -1,19 +0,0 @@
1
- import typer
2
-
3
- from bakefile.cli.utils.version import version_callback
4
-
5
- app = typer.Typer(add_completion=True)
6
-
7
-
8
- @app.command()
9
- def main(
10
- version: bool = typer.Option(
11
- False,
12
- "--version",
13
- help="Show version and exit",
14
- callback=version_callback,
15
- is_eager=True,
16
- ),
17
- ) -> None:
18
- _ = version
19
- typer.echo("hello world")
@@ -1,9 +0,0 @@
1
- import typer
2
-
3
- from bakefile import __version__
4
-
5
-
6
- def version_callback(value: bool) -> None:
7
- if value:
8
- typer.echo(__version__)
9
- raise typer.Exit()