bakefile 0.0.2rc1.post7__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.
@@ -0,0 +1,24 @@
1
+ Metadata-Version: 2.3
2
+ Name: bakefile
3
+ Version: 0.0.4
4
+ Summary: Add your description here
5
+ Author: Wisaroot Lertthaweedech
6
+ Author-email: Wisaroot Lertthaweedech <l.wisaroot@gmail.com>
7
+ Requires-Dist: pydantic>=2.12.5
8
+ Requires-Dist: typer>=0.0.1
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+
12
+ [![tests](https://img.shields.io/github/actions/workflow/status/wislertt/bakefile/cd.yml?branch=main&label=tests&logo=github)](https://github.com/wislertt/bakefile/actions/workflows/cd.yml)
13
+ [![release](https://img.shields.io/github/actions/workflow/status/wislertt/bakefile/cd.yml?branch=main&label=release&logo=github)](https://github.com/wislertt/bakefile/actions/workflows/cd.yml)
14
+ [![quality-gate-status](https://sonarcloud.io/api/project_badges/measure?project=wislertt_bakefile&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=wislertt_bakefile)
15
+ [![security-rating](https://sonarcloud.io/api/project_badges/measure?project=wislertt_bakefile&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=wislertt_bakefile)
16
+ [![vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=wislertt_bakefile&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=wislertt_bakefile)
17
+ [![codecov](https://codecov.io/gh/wislertt/bakefile/graph/badge.svg?token=G0ZRDBGAJB)](https://codecov.io/gh/wislertt/bakefile)
18
+ [![pypi](https://img.shields.io/pypi/v/bakefile.svg?color=blue)](https://pypi.python.org/pypi/bakefile)
19
+ [![downloads](https://static.pepy.tech/personalized-badge/bakefile?period=total&units=international_system&left_color=grey&right_color=blue&left_text=pypi%20downloads)](https://pepy.tech/projects/bakefile)
20
+ [![python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue?logo=python)](https://github.com/wislertt/bakefile/)
21
+
22
+ # bakefile
23
+
24
+ 🚧 **Note:** This project is under active development. 🚧
@@ -0,0 +1,13 @@
1
+ [![tests](https://img.shields.io/github/actions/workflow/status/wislertt/bakefile/cd.yml?branch=main&label=tests&logo=github)](https://github.com/wislertt/bakefile/actions/workflows/cd.yml)
2
+ [![release](https://img.shields.io/github/actions/workflow/status/wislertt/bakefile/cd.yml?branch=main&label=release&logo=github)](https://github.com/wislertt/bakefile/actions/workflows/cd.yml)
3
+ [![quality-gate-status](https://sonarcloud.io/api/project_badges/measure?project=wislertt_bakefile&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=wislertt_bakefile)
4
+ [![security-rating](https://sonarcloud.io/api/project_badges/measure?project=wislertt_bakefile&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=wislertt_bakefile)
5
+ [![vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=wislertt_bakefile&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=wislertt_bakefile)
6
+ [![codecov](https://codecov.io/gh/wislertt/bakefile/graph/badge.svg?token=G0ZRDBGAJB)](https://codecov.io/gh/wislertt/bakefile)
7
+ [![pypi](https://img.shields.io/pypi/v/bakefile.svg?color=blue)](https://pypi.python.org/pypi/bakefile)
8
+ [![downloads](https://static.pepy.tech/personalized-badge/bakefile?period=total&units=international_system&left_color=grey&right_color=blue&left_text=pypi%20downloads)](https://pepy.tech/projects/bakefile)
9
+ [![python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue?logo=python)](https://github.com/wislertt/bakefile/)
10
+
11
+ # bakefile
12
+
13
+ 🚧 **Note:** This project is under active development. 🚧
@@ -1,16 +1,21 @@
1
1
  [project]
2
2
  name = "bakefile"
3
- version = "0.0.2rc1.post7"
3
+ version = "0.0.4" # use git tag
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  authors = [
7
7
  {name = "Wisaroot Lertthaweedech", email = "l.wisaroot@gmail.com"}
8
8
  ]
9
- requires-python = ">=3.11"
9
+ requires-python = ">=3.10"
10
10
  dependencies = [
11
- "pydantic>=2.12.5"
11
+ "pydantic>=2.12.5",
12
+ "typer>=0.0.1"
12
13
  ]
13
14
 
15
+ [project.scripts]
16
+ bake = "bakefile.cli.bake:main"
17
+ bakefile = "bakefile.cli.bakefile:app"
18
+
14
19
  [dependency-groups]
15
20
  dev = [
16
21
  "pre-commit>=4.5.1",
@@ -25,6 +30,9 @@ dev = [
25
30
  requires = ["uv_build>=0.9.18,<0.10.0"]
26
31
  build-backend = "uv_build"
27
32
 
33
+ [tool.ruff]
34
+ line-length = 100
35
+
28
36
  [tool.ruff.lint]
29
37
  select = [
30
38
  "ARG",
@@ -0,0 +1,13 @@
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()
@@ -0,0 +1 @@
1
+ __all__ = []
@@ -0,0 +1,3 @@
1
+ from bakefile.cli.bake.main import main
2
+
3
+ __all__ = ["main"]
@@ -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")
@@ -0,0 +1,9 @@
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()
@@ -0,0 +1,8 @@
1
+ import os
2
+
3
+ ENV_NO_COLOR = "NO_COLOR"
4
+
5
+
6
+ def should_use_colors() -> bool:
7
+ value = os.environ.get(ENV_NO_COLOR)
8
+ return not (value == "" or value is None)
@@ -0,0 +1,9 @@
1
+ """Custom exceptions for bakefile."""
2
+
3
+
4
+ class BaseBakefileError(Exception):
5
+ """Base exception for all bakefile errors."""
6
+
7
+
8
+ class BakebookError(BaseBakefileError):
9
+ """Exception raised when bakebook cannot be loaded or validated."""
@@ -1,11 +0,0 @@
1
- Metadata-Version: 2.3
2
- Name: bakefile
3
- Version: 0.0.2rc1.post7
4
- Summary: Add your description here
5
- Author: Wisaroot Lertthaweedech
6
- Author-email: Wisaroot Lertthaweedech <l.wisaroot@gmail.com>
7
- Requires-Dist: pydantic>=2.12.5
8
- Requires-Python: >=3.11
9
- Description-Content-Type: text/markdown
10
-
11
- # bakefile
@@ -1 +0,0 @@
1
- # bakefile
@@ -1,33 +0,0 @@
1
- import sys
2
- from datetime import datetime
3
- from pathlib import Path
4
-
5
- import pydantic
6
-
7
-
8
- class GreetingMessage(pydantic.BaseModel):
9
- message: str
10
- timestamp: datetime
11
- python_version: str
12
- directory: str
13
-
14
-
15
- def hello(test_test: None = None) -> str:
16
- _ = test_test
17
- current_time = datetime.now()
18
- current_dir = str(Path.cwd())
19
- python_version = f"{sys.version_info.major}.{sys.version_info.minor}"
20
-
21
- greeting = GreetingMessage(
22
- message="Hello from bakefile!",
23
- timestamp=current_time,
24
- python_version=python_version,
25
- directory=current_dir,
26
- )
27
-
28
- return (
29
- f"{greeting.message}\n"
30
- f"Current time: {greeting.timestamp}\n"
31
- f"Current directory: {greeting.directory}\n"
32
- f"Python version: {greeting.python_version}"
33
- )