bakefile 0.0.8__tar.gz → 0.0.10__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.
Files changed (67) hide show
  1. {bakefile-0.0.8 → bakefile-0.0.10}/PKG-INFO +1 -1
  2. {bakefile-0.0.8 → bakefile-0.0.10}/pyproject.toml +1 -1
  3. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/manage/find_python.py +26 -9
  4. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/ui/run/splitter.py +42 -23
  5. {bakefile-0.0.8 → bakefile-0.0.10}/src/bakelib/space/base.py +4 -9
  6. {bakefile-0.0.8 → bakefile-0.0.10}/src/bakelib/space/python.py +17 -8
  7. {bakefile-0.0.8 → bakefile-0.0.10}/README.md +0 -0
  8. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/__init__.py +0 -0
  9. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/bakebook/__init__.py +0 -0
  10. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/bakebook/bakebook.py +0 -0
  11. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/bakebook/decorator.py +0 -0
  12. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/bakebook/get.py +0 -0
  13. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/cli/__init__.py +0 -0
  14. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/cli/bake/__init__.py +0 -0
  15. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/cli/bake/__main__.py +0 -0
  16. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/cli/bake/main.py +0 -0
  17. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/cli/bake/reinvocation.py +0 -0
  18. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/cli/bakefile/__init__.py +0 -0
  19. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/cli/bakefile/__main__.py +0 -0
  20. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/cli/bakefile/add_inline.py +0 -0
  21. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/cli/bakefile/export.py +0 -0
  22. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/cli/bakefile/find_python.py +0 -0
  23. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/cli/bakefile/init.py +0 -0
  24. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/cli/bakefile/lint.py +0 -0
  25. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/cli/bakefile/main.py +0 -0
  26. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/cli/bakefile/uv.py +0 -0
  27. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/cli/common/__init__.py +0 -0
  28. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/cli/common/app.py +0 -0
  29. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/cli/common/callback.py +0 -0
  30. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/cli/common/context.py +0 -0
  31. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/cli/common/exception_handler.py +0 -0
  32. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/cli/common/obj.py +0 -0
  33. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/cli/common/params.py +0 -0
  34. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/cli/utils/__init__.py +0 -0
  35. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/cli/utils/version.py +0 -0
  36. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/manage/__init__.py +0 -0
  37. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/manage/add_inline.py +0 -0
  38. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/manage/lint.py +0 -0
  39. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/manage/run_uv.py +0 -0
  40. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/manage/write_bakefile.py +0 -0
  41. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/py.typed +0 -0
  42. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/samples/__init__.py +0 -0
  43. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/samples/simple.py +0 -0
  44. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/ui/__init__.py +0 -0
  45. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/ui/console.py +0 -0
  46. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/ui/logger/__init__.py +0 -0
  47. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/ui/logger/capsys.py +0 -0
  48. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/ui/logger/setup.py +0 -0
  49. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/ui/logger/utils.py +0 -0
  50. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/ui/params.py +0 -0
  51. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/ui/run/__init__.py +0 -0
  52. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/ui/run/run.py +0 -0
  53. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/ui/run/script.py +0 -0
  54. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/ui/run/uv.py +0 -0
  55. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/ui/style.py +0 -0
  56. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/utils/__init__.py +0 -0
  57. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/utils/constants.py +0 -0
  58. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/utils/env.py +0 -0
  59. {bakefile-0.0.8 → bakefile-0.0.10}/src/bake/utils/exceptions.py +0 -0
  60. {bakefile-0.0.8 → bakefile-0.0.10}/src/bakelib/__init__.py +0 -0
  61. {bakefile-0.0.8 → bakefile-0.0.10}/src/bakelib/environ/__init__.py +0 -0
  62. {bakefile-0.0.8 → bakefile-0.0.10}/src/bakelib/environ/bakebook.py +0 -0
  63. {bakefile-0.0.8 → bakefile-0.0.10}/src/bakelib/environ/base.py +0 -0
  64. {bakefile-0.0.8 → bakefile-0.0.10}/src/bakelib/environ/get_bakebook.py +0 -0
  65. {bakefile-0.0.8 → bakefile-0.0.10}/src/bakelib/environ/presets.py +0 -0
  66. {bakefile-0.0.8 → bakefile-0.0.10}/src/bakelib/space/__init__.py +0 -0
  67. {bakefile-0.0.8 → bakefile-0.0.10}/src/bakelib/space/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bakefile
3
- Version: 0.0.8
3
+ Version: 0.0.10
4
4
  Summary: Add your description here
5
5
  Author: Wisaroot Lertthaweedech
6
6
  Author-email: Wisaroot Lertthaweedech <l.wisaroot@gmail.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "bakefile"
3
- version = "0.0.8" # use git tag
3
+ version = "0.0.10" # use git tag
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -9,6 +9,8 @@ from bake.utils.exceptions import PythonNotFoundError
9
9
 
10
10
  logger = logging.getLogger(__name__)
11
11
 
12
+ _NO_PROJECT_PYTHON_MSG = "No project Python found"
13
+
12
14
 
13
15
  def is_standalone_bakefile(bakefile_path: Path) -> bool:
14
16
  inline_metadata = read_inline(bakefile_path)
@@ -101,18 +103,33 @@ def _find_project_python(bakefile_path: Path) -> Path | None:
101
103
  # Check if stderr contains "Found `...` at `...` (...)"
102
104
  # where source is "active virtual environment" or "virtual environment"
103
105
  stderr = result.stderr.strip()
104
- pattern = r"Found `[^`]+` at `[^`]+` \(([^)]+)\)"
106
+ pattern = r"Found `[^`]+` at `([^`]+)` \(([^)]+)\)"
105
107
  match = re.search(pattern, stderr)
106
108
 
107
- if result.returncode == 0 and match:
108
- source = match.group(1)
109
- if source in {"active virtual environment", "virtual environment"}:
110
- python_path = Path(result.stdout.strip())
111
- logger.debug(f"Found project Python at {python_path} (source: {source})")
112
- return python_path
109
+ if not (result.returncode == 0 and match):
110
+ logger.debug(_NO_PROJECT_PYTHON_MSG)
111
+ return None
113
112
 
114
- logger.debug("No project Python found")
115
- return None
113
+ source = match.group(2)
114
+ if source not in {"active virtual environment", "virtual environment"}:
115
+ logger.debug(_NO_PROJECT_PYTHON_MSG)
116
+ return None
117
+
118
+ python_path_from_log = Path(match.group(1))
119
+ python_path_from_stdout = Path(result.stdout.strip())
120
+ if python_path_from_log != python_path_from_stdout:
121
+ logger.debug(
122
+ "Python path mismatch between log and stdout",
123
+ extra={
124
+ "python_path_from_log": python_path_from_log,
125
+ "python_path_from_stdout": python_path_from_stdout,
126
+ },
127
+ )
128
+ logger.debug(_NO_PROJECT_PYTHON_MSG)
129
+ return None
130
+
131
+ logger.debug(f"Found project Python at {python_path_from_stdout} (source: {source})")
132
+ return python_path_from_stdout
116
133
 
117
134
 
118
135
  def _create_bakefile_venv(bakefile_path: Path) -> Path | None:
@@ -1,3 +1,4 @@
1
+ import errno
1
2
  import os
2
3
  import select
3
4
  import subprocess
@@ -46,37 +47,55 @@ class OutputSplitter:
46
47
  output_list.append(data)
47
48
  return True
48
49
 
49
- def _read_pty(self, pty_fd: int, target, output_list, proc: subprocess.Popen):
50
- """Read from PTY file descriptor in chunks and stream to output."""
50
+ def _read_pty_eio_safe(self, pty_fd: int) -> bytes | None:
51
+ """Read from PTY, treating EIO as EOF (returns None)."""
52
+ try:
53
+ return os.read(pty_fd, 4096)
54
+ except OSError as e:
55
+ if e.errno == errno.EIO:
56
+ return None
57
+ raise
58
+
59
+ def _try_immediate_read(self, pty_fd: int, target, output_list) -> bool:
60
+ """Try immediate non-blocking read. Returns True if should continue."""
61
+ import fcntl
62
+
63
+ flags = fcntl.fcntl(pty_fd, fcntl.F_GETFL)
64
+ fcntl.fcntl(pty_fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
65
+
66
+ data = self._read_pty_eio_safe(pty_fd)
67
+ if data is None or not self._handle_data(data, target, output_list):
68
+ fcntl.fcntl(pty_fd, fcntl.F_SETFL, flags)
69
+ return False
70
+
71
+ fcntl.fcntl(pty_fd, fcntl.F_SETFL, flags)
72
+ return True
73
+
74
+ def _blocking_pty_read(self, pty_fd: int, target, output_list) -> bool:
75
+ """Try select-based blocking read. Returns True if should continue."""
51
76
  import fcntl
52
77
 
78
+ flags = fcntl.fcntl(pty_fd, fcntl.F_GETFL)
79
+ fcntl.fcntl(pty_fd, fcntl.F_SETFL, flags)
80
+
81
+ ready, _, _ = select.select([pty_fd], [], [], 0.1)
82
+ if ready:
83
+ data = self._read_pty_eio_safe(pty_fd)
84
+ if data is None or not self._handle_data(data, target, output_list):
85
+ return False
86
+ return True
87
+
88
+ def _read_pty(self, pty_fd: int, target, output_list, proc: subprocess.Popen):
89
+ """Read from PTY file descriptor in chunks and stream to output."""
53
90
  try:
54
91
  while True:
55
- # Try immediate non-blocking read first (catches fast-exiting processes)
56
92
  try:
57
- # Set non-blocking mode
58
- flags = fcntl.fcntl(pty_fd, fcntl.F_GETFL)
59
- fcntl.fcntl(pty_fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
60
-
61
- data = os.read(pty_fd, 4096)
62
- if not self._handle_data(data, target, output_list):
93
+ if not self._try_immediate_read(pty_fd, target, output_list):
63
94
  break
64
-
65
- # Restore blocking mode
66
- fcntl.fcntl(pty_fd, fcntl.F_SETFL, flags)
67
95
  except BlockingIOError:
68
- # No data available yet, restore blocking mode and wait with select
69
- fcntl.fcntl(pty_fd, fcntl.F_SETFL, flags)
70
-
71
- # Wait for data to be available
72
- ready, _, _ = select.select([pty_fd], [], [], 0.1)
73
-
74
- if ready:
75
- data = os.read(pty_fd, 4096)
76
- if not self._handle_data(data, target, output_list):
77
- break
96
+ if not self._blocking_pty_read(pty_fd, target, output_list):
97
+ break
78
98
 
79
- # Check if process exited after reading data
80
99
  if proc.poll() is not None:
81
100
  self._drain_pty(pty_fd, target, output_list)
82
101
  break
@@ -57,20 +57,16 @@ class BaseSpace(Bakebook):
57
57
  help="Patterns to exclude",
58
58
  ),
59
59
  ] = None,
60
- use_default_excludes: Annotated[
60
+ default_excludes: Annotated[
61
61
  bool,
62
- typer.Option(
63
- "--no-default-excludes",
64
- help="Do not apply default exclude patterns",
65
- is_flag=True,
66
- ),
67
- ] = False,
62
+ typer.Option(help="Apply default exclude patterns (.env, .cache)"),
63
+ ] = True,
68
64
  ) -> None:
69
65
  results = ctx.run("git clean -fdX -n", stream=False, dry_run=False, echo=True)
70
66
 
71
67
  exclude_patterns: set[str] = set(exclude_patterns if exclude_patterns else [])
72
68
 
73
- if not use_default_excludes:
69
+ if default_excludes:
74
70
  exclude_patterns |= {".env", ".cache"}
75
71
 
76
72
  console.err.print(f"Exclude pattens: {exclude_patterns}")
@@ -175,7 +171,6 @@ class BaseSpace(Bakebook):
175
171
  "--skip-test",
176
172
  "-s",
177
173
  help="Skip running tests",
178
- is_flag=True,
179
174
  ),
180
175
  ] = False,
181
176
  ) -> None:
@@ -34,11 +34,20 @@ class PythonSpace(BaseSpace):
34
34
  ctx.run("uv run ty check --error-on-warning --no-progress .")
35
35
  ctx.run("uv run deptry .")
36
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
- )
37
+ def _test(
38
+ self,
39
+ ctx: Context,
40
+ *,
41
+ tests_paths: str | list[str],
42
+ verbose: bool = False,
43
+ coverage_report: bool = True,
44
+ ) -> None:
45
+ paths = tests_paths if isinstance(tests_paths, str) else " ".join(tests_paths)
46
+
47
+ cmd = f"uv run pytest {paths}"
48
+
49
+ if coverage_report:
50
+ cmd += " --cov=src --cov-report=html --cov-report=term-missing --cov-report=xml"
42
51
 
43
52
  if verbose:
44
53
  cmd += " -s -v"
@@ -53,20 +62,20 @@ class PythonSpace(BaseSpace):
53
62
  integration_tests_path = "tests/integration/"
54
63
  if Path(integration_tests_path).exists():
55
64
  tests_path = integration_tests_path
56
- self._test(ctx, tests_path=tests_path, verbose=verbose)
65
+ self._test(ctx, tests_paths=tests_path, verbose=verbose)
57
66
  else:
58
67
  self._no_implementation(ctx)
59
68
 
60
69
  def test(self, ctx: Context) -> None:
61
70
  unit_tests_path = "tests/unit/"
62
71
  tests_path = unit_tests_path if Path(unit_tests_path).exists() else "tests/"
63
- self._test(ctx, tests_path=tests_path)
72
+ self._test(ctx, tests_paths=tests_path)
64
73
 
65
74
  def test_all(self, ctx: Context) -> None:
66
75
  unit_tests_path = "tests/unit/"
67
76
  if Path(unit_tests_path).exists():
68
77
  tests_path = "tests/"
69
- self._test(ctx, tests_path=tests_path)
78
+ self._test(ctx, tests_paths=tests_path)
70
79
  else:
71
80
  self._no_implementation(ctx)
72
81
 
File without changes
File without changes
File without changes
File without changes