bakefile 0.0.3__tar.gz → 0.0.4__tar.gz
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.
- {bakefile-0.0.3 → bakefile-0.0.4}/PKG-INFO +1 -1
- {bakefile-0.0.3 → bakefile-0.0.4}/pyproject.toml +2 -2
- bakefile-0.0.4/src/bakefile/__init__.py +13 -0
- bakefile-0.0.4/src/bakefile/cli/bake/__init__.py +3 -0
- bakefile-0.0.4/src/bakefile/cli/bake/main.py +127 -0
- bakefile-0.0.4/src/bakefile/cli/bake/resolve_bakebook.py +103 -0
- bakefile-0.0.4/src/bakefile/cli/bake/utils.py +25 -0
- bakefile-0.0.4/src/bakefile/cli/bakefile.py +19 -0
- bakefile-0.0.4/src/bakefile/cli/utils/version.py +9 -0
- bakefile-0.0.4/src/bakefile/env.py +8 -0
- bakefile-0.0.4/src/bakefile/exceptions.py +9 -0
- bakefile-0.0.3/src/bakefile/cli/bake/__init__.py +0 -3
- bakefile-0.0.3/src/bakefile/cli/bake/main.py +0 -17
- bakefile-0.0.3/src/bakefile/cli/bake/resolve_bakebook.py +0 -75
- bakefile-0.0.3/src/bakefile/cli/bakefile.py +0 -8
- {bakefile-0.0.3 → bakefile-0.0.4}/README.md +0 -0
- {bakefile-0.0.3 → bakefile-0.0.4}/src/bakefile/cli/__init__.py +0 -0
- {bakefile-0.0.3/src/bakefile → bakefile-0.0.4/src/bakefile/cli/utils}/__init__.py +0 -0
- {bakefile-0.0.3 → bakefile-0.0.4}/src/bakefile/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "bakefile"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.4" # use git tag
|
|
4
4
|
description = "Add your description here"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -13,7 +13,7 @@ dependencies = [
|
|
|
13
13
|
]
|
|
14
14
|
|
|
15
15
|
[project.scripts]
|
|
16
|
-
bake = "bakefile.cli.bake:
|
|
16
|
+
bake = "bakefile.cli.bake:main"
|
|
17
17
|
bakefile = "bakefile.cli.bakefile:app"
|
|
18
18
|
|
|
19
19
|
[dependency-groups]
|
|
@@ -0,0 +1,127 @@
|
|
|
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()
|
|
@@ -0,0 +1,103 @@
|
|
|
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)
|
|
@@ -0,0 +1,25 @@
|
|
|
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
|
|
@@ -0,0 +1,19 @@
|
|
|
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,17 +0,0 @@
|
|
|
1
|
-
import typer
|
|
2
|
-
|
|
3
|
-
from bakefile.cli.bake.resolve_bakebook import resolve_bakebook
|
|
4
|
-
|
|
5
|
-
app = typer.Typer()
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@app.command()
|
|
9
|
-
def main(
|
|
10
|
-
chdir: str = typer.Option(None, "-C", "--chdir", help="Change directory before running"),
|
|
11
|
-
file_name: str = typer.Option("bakefile.py", "--file-name", "-f", help="Path to bakefile.py"),
|
|
12
|
-
bakebook_name: str = typer.Option(
|
|
13
|
-
"bakebook", "--book-name", "-b", help="Name of bakebook object to retrieve"
|
|
14
|
-
),
|
|
15
|
-
) -> None:
|
|
16
|
-
bakebook = resolve_bakebook(file_name=file_name, bakebook_name=bakebook_name, chdir=chdir)
|
|
17
|
-
typer.echo(bakebook)
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import importlib.util
|
|
2
|
-
import os
|
|
3
|
-
import pathlib
|
|
4
|
-
import sys
|
|
5
|
-
import types
|
|
6
|
-
|
|
7
|
-
import typer
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def change_directory(path: str) -> None:
|
|
11
|
-
if not path or not path.strip():
|
|
12
|
-
typer.echo("Directory path cannot be empty", err=True)
|
|
13
|
-
raise SystemExit(1)
|
|
14
|
-
dir_path = pathlib.Path(path)
|
|
15
|
-
if not dir_path.exists():
|
|
16
|
-
typer.echo(f"Directory not found: {path}", err=True)
|
|
17
|
-
raise SystemExit(1)
|
|
18
|
-
if not dir_path.is_dir():
|
|
19
|
-
typer.echo(f"Not a directory: {path}", err=True)
|
|
20
|
-
raise SystemExit(1)
|
|
21
|
-
os.chdir(dir_path)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def validate_file_name(file_name: str) -> None:
|
|
25
|
-
if "/" in file_name or "\\" in file_name:
|
|
26
|
-
typer.echo(f"File name must not contain path separators: {file_name}", err=True)
|
|
27
|
-
raise SystemExit(1)
|
|
28
|
-
if not file_name.endswith(".py"):
|
|
29
|
-
typer.echo(f"File name must end with .py: {file_name}", err=True)
|
|
30
|
-
raise SystemExit(1)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def resolve_file_path(file_name: str) -> pathlib.Path:
|
|
34
|
-
path = pathlib.Path.cwd() / file_name
|
|
35
|
-
if not path.exists():
|
|
36
|
-
typer.echo(f"File not found: {file_name}", err=True)
|
|
37
|
-
raise SystemExit(1)
|
|
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
|
-
typer.echo(f"Failed to load: {path}", err=True)
|
|
46
|
-
raise SystemExit(1)
|
|
47
|
-
|
|
48
|
-
module: types.ModuleType = importlib.util.module_from_spec(spec)
|
|
49
|
-
sys.modules[module_name] = module
|
|
50
|
-
spec.loader.exec_module(module)
|
|
51
|
-
return module
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def get_bakebook(module: types.ModuleType, bakebook_name: str, path: pathlib.Path) -> str:
|
|
55
|
-
if not hasattr(module, bakebook_name):
|
|
56
|
-
typer.echo(f"No '{bakebook_name}' found in {path}", err=True)
|
|
57
|
-
raise SystemExit(1)
|
|
58
|
-
bakebook = getattr(module, bakebook_name)
|
|
59
|
-
if not isinstance(bakebook, str):
|
|
60
|
-
typer.echo(
|
|
61
|
-
f"Bakebook '{bakebook_name}' must be a string, got {type(bakebook).__name__}",
|
|
62
|
-
err=True,
|
|
63
|
-
)
|
|
64
|
-
raise SystemExit(1)
|
|
65
|
-
return bakebook
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def resolve_bakebook(file_name: str, bakebook_name: str, chdir: str | None = None) -> str:
|
|
69
|
-
if chdir:
|
|
70
|
-
change_directory(chdir)
|
|
71
|
-
|
|
72
|
-
validate_file_name(file_name)
|
|
73
|
-
path = resolve_file_path(file_name)
|
|
74
|
-
module = load_module(path)
|
|
75
|
-
return get_bakebook(module=module, bakebook_name=bakebook_name, path=path)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|