bakefile 0.0.9__py3-none-any.whl → 0.0.11__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.
@@ -0,0 +1,250 @@
1
+ import contextlib
2
+ import functools
3
+ import inspect
4
+ import logging
5
+ import time
6
+ from abc import ABC, abstractmethod
7
+ from collections.abc import Callable
8
+ from typing import TYPE_CHECKING, Any, ClassVar, Generic, ParamSpec, TypeVar
9
+
10
+ import keyring as kr
11
+ from keyring.errors import PasswordDeleteError
12
+ from pydantic import BaseModel, TypeAdapter
13
+ from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_none
14
+
15
+ from bakelib.refreshable_cache.exceptions import RefreshNeededError
16
+
17
+ if TYPE_CHECKING:
18
+ from tenacity.stop import StopBaseT
19
+ from tenacity.wait import WaitBaseT
20
+
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+ P = ParamSpec("P")
25
+ F = TypeVar("F", bound=Callable[..., Any])
26
+ T = TypeVar("T")
27
+ CachedT = TypeVar("CachedT", covariant=True)
28
+
29
+
30
+ class CacheEntry(BaseModel, Generic[CachedT]):
31
+ """Cache entry containing the cached value and timestamp."""
32
+
33
+ value: CachedT
34
+ timestamp: float
35
+
36
+
37
+ DEFAULT_NAMESPACE = "bakelib.refreshable_cache"
38
+
39
+
40
+ class RefreshableCache(ABC, Generic[CachedT]):
41
+ """Cache that can be refreshed when values expire or become invalid."""
42
+
43
+ RefreshNeededError: type[RefreshNeededError] = RefreshNeededError
44
+
45
+ def __init__(
46
+ self,
47
+ key: str,
48
+ fetch_fn: Callable[[], CachedT],
49
+ ttl: float | None = None,
50
+ namespace: str | None = None,
51
+ stop: "StopBaseT | None" = None,
52
+ wait: "WaitBaseT | None" = None,
53
+ cached_type: Any = None,
54
+ ) -> None:
55
+ self._key = key
56
+ self._fetch_fn = fetch_fn
57
+ self._ttl = ttl
58
+ self._namespace = namespace if namespace is not None else DEFAULT_NAMESPACE
59
+ self._stop = stop if stop is not None else stop_after_attempt(2)
60
+ self._wait = wait if wait is not None else wait_none()
61
+
62
+ # Determine cached type: explicit cached_type or infer from fetch_fn
63
+ if cached_type is not None:
64
+ return_type = cached_type
65
+ else:
66
+ return_type = inspect.signature(fetch_fn).return_annotation
67
+ if return_type is inspect.Parameter.empty:
68
+ msg = "fetch_fn must have a return type annotation or cached_type must be provided"
69
+ raise TypeError(msg)
70
+
71
+ self._adapter = TypeAdapter(CacheEntry[return_type])
72
+
73
+ def _get_full_key(self) -> str:
74
+ return f"{self._namespace}:{self._key}"
75
+
76
+ def _serialize_entry(self, value: CachedT) -> bytes:
77
+ entry = CacheEntry(value=value, timestamp=time.time())
78
+ return self._adapter.dump_json(entry)
79
+
80
+ def _deserialize_entry(self, data: bytes) -> CacheEntry[CachedT]:
81
+ return self._adapter.validate_json(data)
82
+
83
+ def _is_expired(self, timestamp: float) -> bool:
84
+ if self._ttl is None:
85
+ return False
86
+ return time.time() - timestamp > self._ttl
87
+
88
+ def get_value(self) -> CachedT:
89
+ cached = self._get_entry()
90
+ if cached is None:
91
+ logger.debug(f"Cache miss for key '{self._key}', fetching value")
92
+ return self._refresh()
93
+ if self._is_expired(cached.timestamp):
94
+ logger.debug(f"Cache expired for key '{self._key}', fetching fresh value")
95
+ return self._refresh()
96
+ logger.debug(f"Cache hit for key '{self._key}'")
97
+ return cached.value
98
+
99
+ def _refresh(self) -> CachedT:
100
+ logger.debug(f"Refreshing value for key '{self._key}'")
101
+ value = self._fetch_fn()
102
+ self.set(value)
103
+ return value
104
+
105
+ def catch_refresh(self, func: Callable[P, T]) -> Callable[P, T]:
106
+ @functools.wraps(func)
107
+ @retry(
108
+ stop=self._stop,
109
+ wait=self._wait,
110
+ retry=retry_if_exception_type(self.RefreshNeededError),
111
+ reraise=True,
112
+ )
113
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
114
+ try:
115
+ return func(*args, **kwargs)
116
+ except self.RefreshNeededError:
117
+ self.delete()
118
+ raise
119
+
120
+ return wrapper
121
+
122
+ @abstractmethod
123
+ def _get_entry(self) -> CacheEntry[CachedT] | None: ...
124
+
125
+ @abstractmethod
126
+ def set(self, value: CachedT) -> None: ...
127
+
128
+ @abstractmethod
129
+ def delete(self) -> None: ...
130
+
131
+
132
+ class KeyringCache(RefreshableCache[CachedT]):
133
+ """Cache using system keyring for persistent storage."""
134
+
135
+ def _get_entry(self) -> CacheEntry[CachedT] | None:
136
+ data = kr.get_password(self._namespace, self._key)
137
+ if data is None:
138
+ return None
139
+ return self._deserialize_entry(data.encode())
140
+
141
+ def set(self, value: CachedT) -> None:
142
+ data = self._serialize_entry(value).decode()
143
+ kr.set_password(self._namespace, self._key, data)
144
+
145
+ def delete(self) -> None:
146
+ with contextlib.suppress(PasswordDeleteError):
147
+ kr.delete_password(self._namespace, self._key)
148
+
149
+
150
+ class MemoryCache(RefreshableCache[CachedT]):
151
+ """In-memory cache for ephemeral storage."""
152
+
153
+ _storage: ClassVar[dict[str, CacheEntry[CachedT]]] = {}
154
+
155
+ def _get_entry(self) -> CacheEntry[CachedT] | None:
156
+ entry = self._storage.get(self._get_full_key())
157
+ if entry is None:
158
+ return None
159
+ return entry
160
+
161
+ def set(self, value: CachedT) -> None:
162
+ self._storage[self._get_full_key()] = CacheEntry(value=value, timestamp=time.time())
163
+
164
+ def delete(self) -> None:
165
+ self._storage.pop(self._get_full_key(), None)
166
+
167
+
168
+ class NullCache(RefreshableCache[CachedT]):
169
+ """Cache that doesn't cache anything (Null Object pattern).
170
+
171
+ Useful as a final fallback when you want to explicitly disable caching.
172
+ Reads always return None (triggering fetch), writes/deletes do nothing.
173
+ """
174
+
175
+ def _get_entry(self) -> CacheEntry[CachedT] | None:
176
+ return None
177
+
178
+ def set(self, value: CachedT) -> None:
179
+ pass
180
+
181
+ def delete(self) -> None:
182
+ pass
183
+
184
+
185
+ class ChainedCache(RefreshableCache[CachedT]):
186
+ """Tries multiple backends in order.
187
+
188
+ Reads from the first backend that has data.
189
+ Writes to all backends (stops on first success).
190
+ """
191
+
192
+ _backends: list[RefreshableCache[CachedT]]
193
+
194
+ def __init__(
195
+ self,
196
+ backends: list[type[RefreshableCache[CachedT]]],
197
+ key: str,
198
+ fetch_fn: Callable[[], CachedT],
199
+ ttl: float | None = None,
200
+ namespace: str | None = None,
201
+ stop: "StopBaseT | None" = None,
202
+ wait: "WaitBaseT | None" = None,
203
+ cached_type: Any = None,
204
+ ) -> None:
205
+ super().__init__(
206
+ key=key,
207
+ fetch_fn=fetch_fn,
208
+ ttl=ttl,
209
+ namespace=namespace,
210
+ stop=stop,
211
+ wait=wait,
212
+ cached_type=cached_type,
213
+ )
214
+ self._backends = [
215
+ backend(
216
+ key=key,
217
+ fetch_fn=fetch_fn,
218
+ ttl=ttl,
219
+ namespace=namespace,
220
+ stop=stop,
221
+ wait=wait,
222
+ cached_type=cached_type,
223
+ )
224
+ for backend in backends
225
+ ]
226
+
227
+ def _get_entry(self) -> CacheEntry[CachedT] | None:
228
+ for backend in self._backends:
229
+ try:
230
+ entry = backend._get_entry()
231
+ if entry is not None:
232
+ return entry
233
+ except Exception:
234
+ continue
235
+ return None
236
+
237
+ def set(self, value: CachedT) -> None:
238
+ for backend in self._backends:
239
+ try:
240
+ backend.set(value)
241
+ return
242
+ except Exception:
243
+ continue
244
+
245
+ def delete(self) -> None:
246
+ for backend in self._backends:
247
+ try:
248
+ backend.delete()
249
+ except Exception:
250
+ continue
@@ -0,0 +1,2 @@
1
+ class RefreshNeededError(Exception):
2
+ """Raised when a cached value needs to be refreshed."""
bakelib/space/base.py CHANGED
@@ -45,6 +45,28 @@ class BaseSpace(Bakebook):
45
45
  def test_all(self, ctx: Context) -> None:
46
46
  self._no_implementation(ctx)
47
47
 
48
+ def _clean(
49
+ self,
50
+ ctx: Context,
51
+ exclude_patterns: list[str] | None,
52
+ default_excludes: bool,
53
+ default_exclude_patterns: set[str],
54
+ ):
55
+ results = ctx.run("git clean -fdX -n", stream=False, dry_run=False, echo=True)
56
+
57
+ exclude_patterns: set[str] = set(exclude_patterns if exclude_patterns else [])
58
+
59
+ if default_excludes:
60
+ exclude_patterns |= default_exclude_patterns
61
+
62
+ console.err.print(f"Exclude pattens: {exclude_patterns}")
63
+
64
+ remove_git_clean_candidates(
65
+ git_clean_dry_run_output=results.stdout,
66
+ exclude_patterns=exclude_patterns,
67
+ dry_run=ctx.dry_run,
68
+ )
69
+
48
70
  @command(help="Clean gitignored files with optional exclusions")
49
71
  def clean(
50
72
  self,
@@ -57,28 +79,16 @@ class BaseSpace(Bakebook):
57
79
  help="Patterns to exclude",
58
80
  ),
59
81
  ] = None,
60
- use_default_excludes: Annotated[
82
+ default_excludes: Annotated[
61
83
  bool,
62
- typer.Option(
63
- "--no-default-excludes",
64
- help="Do not apply default exclude patterns",
65
- is_flag=True,
66
- ),
67
- ] = False,
84
+ typer.Option(help="Apply default exclude patterns (.env, .cache)"),
85
+ ] = True,
68
86
  ) -> 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,
87
+ self._clean(
88
+ ctx=ctx,
80
89
  exclude_patterns=exclude_patterns,
81
- dry_run=ctx.dry_run,
90
+ default_excludes=default_excludes,
91
+ default_exclude_patterns={".env", ".cache"},
82
92
  )
83
93
 
84
94
  @command(help="Clean all gitignored files")
@@ -175,7 +185,6 @@ class BaseSpace(Bakebook):
175
185
  "--skip-test",
176
186
  "-s",
177
187
  help="Skip running tests",
178
- is_flag=True,
179
188
  ),
180
189
  ] = False,
181
190
  ) -> None:
bakelib/space/lib.py ADDED
@@ -0,0 +1,161 @@
1
+ import subprocess
2
+ from abc import abstractmethod
3
+ from contextlib import contextmanager
4
+ from dataclasses import dataclass
5
+ from typing import Annotated
6
+
7
+ import typer
8
+ from pydantic import SecretStr
9
+ from tenacity import stop_after_attempt
10
+
11
+ from bake import Context, command, console
12
+ from bake.ui.logger import strip_ansi
13
+ from bakelib.refreshable_cache import ChainedCache, KeyringCache, NullCache
14
+
15
+ from .base import BaseSpace, ToolInfo
16
+ from .utils import CARGO_BIN, PlatformType, get_expected_paths, setup_rustup, setup_zerv
17
+
18
+
19
+ @dataclass
20
+ class PublishResult:
21
+ result: subprocess.CompletedProcess[str] | None
22
+ is_dry_run: bool
23
+ is_auth_failed: bool
24
+
25
+
26
+ class BaseLibSpace(BaseSpace):
27
+ bake_publish_token: SecretStr | None = None
28
+ _dummy_publish_token: str = "dummy-token-for-dry-run"
29
+
30
+ def setup_tools(self, ctx: Context, platform: PlatformType) -> None:
31
+ _ = platform
32
+ super().setup_tools(ctx, platform=platform)
33
+ setup_rustup(ctx)
34
+ setup_zerv(ctx)
35
+
36
+ @abstractmethod
37
+ def _get_publish_token_from_remote(self, registry: str) -> str | None: ...
38
+
39
+ @abstractmethod
40
+ def package_name(self, ctx: Context) -> str: ...
41
+
42
+ @abstractmethod
43
+ def _build_for_publish(self, ctx: Context): ...
44
+
45
+ @abstractmethod
46
+ def _publish_with_token(
47
+ self, ctx: Context, token: str | None, registry: str
48
+ ) -> PublishResult: ...
49
+
50
+ def _get_cached_publish_token(
51
+ self, ctx: Context, token: str | None, registry: str
52
+ ) -> ChainedCache[str | None]:
53
+ token_from_local = self._get_token_from_local(token)
54
+ key = f"publish-token-{registry}"
55
+ namespace = self.package_name(ctx)
56
+
57
+ def get_publish_token() -> str | None:
58
+ return token_from_local or self._get_publish_token_from_remote(registry)
59
+
60
+ stop = stop_after_attempt(1) if token_from_local else None
61
+
62
+ cached_publish_token = ChainedCache(
63
+ backends=[KeyringCache, NullCache],
64
+ namespace=namespace,
65
+ key=key,
66
+ fetch_fn=get_publish_token,
67
+ stop=stop,
68
+ )
69
+
70
+ if token_from_local is not None:
71
+ cached_publish_token.set(token_from_local)
72
+
73
+ return cached_publish_token
74
+
75
+ def _get_token_from_local(self, token: str | None) -> str | None:
76
+ if token:
77
+ return token
78
+
79
+ if self.bake_publish_token:
80
+ return self.bake_publish_token.get_secret_value()
81
+
82
+ return None
83
+
84
+ @contextmanager
85
+ @abstractmethod
86
+ def _version_bump_context(self, _ctx: Context, _version: str): ...
87
+
88
+ @abstractmethod
89
+ def _pre_publish_cleanup(self, _ctx: Context): ...
90
+
91
+ @property
92
+ def _version_schema(self) -> str | None:
93
+ return None
94
+
95
+ def _is_auth_failure(self, result: subprocess.CompletedProcess[str]) -> bool:
96
+ return result.returncode != 0
97
+
98
+ def _determine_version(self, ctx: Context, version: str | None) -> str:
99
+ return version if version else self.zerv_versioning(ctx, schema=self._version_schema)
100
+
101
+ @command(help="Build and publish the package")
102
+ def publish(
103
+ self,
104
+ ctx: Context,
105
+ registry: Annotated[str, typer.Option(help="Publish registry")] = "default",
106
+ token: Annotated[str | None, typer.Option(help="Publish token")] = None,
107
+ version: Annotated[str | None, typer.Option(help="Version to publish")] = None,
108
+ ):
109
+ cached_publish_token = self._get_cached_publish_token(
110
+ ctx=ctx, token=token, registry=registry
111
+ )
112
+ version = self._determine_version(ctx, version)
113
+
114
+ self._pre_publish_cleanup(ctx)
115
+
116
+ with self._version_bump_context(ctx, version):
117
+ self._build_for_publish(ctx)
118
+ publish_result = self._execute_publish(
119
+ ctx=ctx, cached_publish_token=cached_publish_token, registry=registry
120
+ )
121
+
122
+ self._handle_publish_result(ctx, publish_result=publish_result)
123
+
124
+ def _execute_publish(
125
+ self, ctx: Context, cached_publish_token: ChainedCache[str | None], registry: str
126
+ ) -> PublishResult:
127
+ @cached_publish_token.catch_refresh
128
+ def _publish() -> PublishResult:
129
+ token_value = cached_publish_token.get_value()
130
+ publish_result = self._publish_with_token(ctx=ctx, token=token_value, registry=registry)
131
+
132
+ if publish_result.result is not None and self._is_auth_failure(publish_result.result):
133
+ raise cached_publish_token.RefreshNeededError
134
+
135
+ return publish_result
136
+
137
+ try:
138
+ return _publish()
139
+ except cached_publish_token.RefreshNeededError:
140
+ return PublishResult(result=None, is_dry_run=False, is_auth_failed=True)
141
+
142
+ def _handle_publish_result(self, ctx: Context, publish_result: PublishResult) -> None:
143
+ if publish_result.is_auth_failed:
144
+ console.error("Authentication failed. Please check your publish token.")
145
+ raise typer.Exit(1)
146
+
147
+ if publish_result.is_dry_run and not ctx.dry_run:
148
+ console.warning(
149
+ "This was a dry-run. To actually publish, "
150
+ "set the BAKE_PUBLISH_TOKEN environment variable"
151
+ )
152
+
153
+ def zerv_versioning(self, ctx: Context, *, schema: str | None = None) -> str:
154
+ schema_flag = f"--schema {schema}" if schema else ""
155
+ result = ctx.run(f"zerv flow {schema_flag}", dry_run=False)
156
+ return strip_ansi(result.stdout.strip())
157
+
158
+ def _get_tools(self) -> dict[str, ToolInfo]:
159
+ tools = super()._get_tools()
160
+ tools["zerv"] = ToolInfo(expected_paths=get_expected_paths("zerv", {CARGO_BIN}))
161
+ return tools
bakelib/space/python.py CHANGED
@@ -1,6 +1,8 @@
1
+ import os
1
2
  from pathlib import Path
2
3
 
3
4
  from bake import Context, params
5
+ from bake.ui.logger import strip_ansi
4
6
 
5
7
  from .base import BaseSpace, ToolInfo
6
8
  from .utils import VENV_BIN, get_expected_paths
@@ -14,6 +16,10 @@ def _get_python_version() -> str | None:
14
16
 
15
17
 
16
18
  class PythonSpace(BaseSpace):
19
+ def __init__(self) -> None:
20
+ super().__init__()
21
+ os.environ["UV_NO_PROGRESS"] = "true"
22
+
17
23
  def _get_tools(self) -> dict[str, ToolInfo]:
18
24
  tools = super()._get_tools()
19
25
  tools["python"] = ToolInfo(
@@ -87,3 +93,14 @@ class PythonSpace(BaseSpace):
87
93
  super().update(ctx=ctx)
88
94
  ctx.run("uv lock --upgrade")
89
95
  ctx.run("uv sync --all-extras --all-groups")
96
+
97
+ def _uv_version(self, ctx: Context) -> tuple[str, str]:
98
+ result = ctx.run("uv version", stream=False, dry_run=False, echo=False)
99
+ package_name, original_version = strip_ansi(result.stdout.strip()).split()
100
+ return package_name, original_version
101
+
102
+ def package_name(self, ctx: Context) -> str:
103
+ return self._uv_version(ctx)[0]
104
+
105
+ def current_version(self, ctx: Context) -> str:
106
+ return self._uv_version(ctx)[1]
@@ -0,0 +1,77 @@
1
+ import shutil
2
+ import subprocess
3
+ from contextlib import contextmanager
4
+ from typing import Annotated, Literal, cast, get_args
5
+
6
+ import typer
7
+
8
+ from bake import Context
9
+
10
+ from .lib import BaseLibSpace, PublishResult
11
+ from .python import PythonSpace
12
+
13
+ PublishIndex = Literal["testpypi", "pypi"]
14
+
15
+
16
+ class PythonLibSpace(PythonSpace, BaseLibSpace):
17
+ @property
18
+ def _version_schema(self) -> str | None:
19
+ return "standard-base-prerelease-post-dev"
20
+
21
+ def _registry_to_index(self, registry: str) -> PublishIndex:
22
+ valid_indices = get_args(PublishIndex)
23
+ if registry not in valid_indices:
24
+ raise ValueError(f"Invalid registry: {registry!r}. Expected one of {valid_indices}.")
25
+ return cast(PublishIndex, registry)
26
+
27
+ def _get_publish_token_from_remote(self, registry: str) -> str | None:
28
+ index = self._registry_to_index(registry)
29
+ _ = index
30
+ return None
31
+
32
+ def _build_for_publish(self, ctx: Context):
33
+ ctx.run("uv build")
34
+
35
+ def _publish_with_token(self, ctx: Context, token: str | None, registry: str) -> PublishResult:
36
+ index = self._registry_to_index(registry)
37
+ index_flag = f"--index {index} " if index == "testpypi" else ""
38
+ dry_run_flag = "" if token is not None else "--dry-run "
39
+ is_dry_run = token is None
40
+
41
+ env: dict[str, str] = {
42
+ "UV_PUBLISH_TOKEN": token if token is not None else self._dummy_publish_token
43
+ }
44
+
45
+ result = ctx.run(
46
+ f"uv publish {dry_run_flag}{index_flag}",
47
+ stream=True,
48
+ env=env,
49
+ check=False,
50
+ )
51
+
52
+ return PublishResult(result=result, is_dry_run=is_dry_run, is_auth_failed=False)
53
+
54
+ def _is_auth_failure(self, result: subprocess.CompletedProcess[str]) -> bool:
55
+ auth_error_message = "403 Invalid or non-existent authentication information"
56
+ return result.returncode != 0 and auth_error_message in result.stderr
57
+
58
+ @contextmanager
59
+ def _version_bump_context(self, ctx: Context, version: str):
60
+ original_version = self.current_version(ctx)
61
+ ctx.run(f"uv version {version}")
62
+ try:
63
+ yield
64
+ finally:
65
+ ctx.run(f"uv version {original_version}")
66
+
67
+ def _pre_publish_cleanup(self, _ctx: Context):
68
+ shutil.rmtree("dist", ignore_errors=True)
69
+
70
+ def publish(
71
+ self,
72
+ ctx: Context,
73
+ index: Annotated[PublishIndex, typer.Option(help="Publish index")] = "testpypi",
74
+ token: Annotated[str | None, typer.Option(help="Publish token")] = None,
75
+ version: Annotated[str | None, typer.Option(help="Version to publish")] = None,
76
+ ):
77
+ return super().publish(ctx=ctx, registry=index, token=token, version=version)
bakelib/space/utils.py CHANGED
@@ -52,6 +52,15 @@ def setup_uv(ctx: Context) -> None:
52
52
  ctx.run("uv tool update-shell")
53
53
 
54
54
 
55
+ def setup_rustup(ctx: Context) -> None:
56
+ ctx.run("brew install rustup")
57
+ ctx.run("rustup update")
58
+
59
+
60
+ def setup_zerv(ctx: Context) -> None:
61
+ ctx.run("cargo install zerv")
62
+
63
+
55
64
  def setup_bun(ctx: Context) -> None:
56
65
  ctx.run("brew install oven-sh/bun/bun")
57
66
 
@@ -62,6 +71,7 @@ def setup_uv_tool(ctx: Context) -> None:
62
71
 
63
72
 
64
73
  HOMWBREW_BIN = Path("/opt/homebrew/bin")
74
+ CARGO_BIN = Path.home() / ".cargo" / "bin"
65
75
  LOCAL_BIN = Path.home() / ".local" / "bin"
66
76
  VENV_BIN = Path.cwd() / ".venv" / "bin"
67
77
 
@@ -1,13 +0,0 @@
1
- import typer
2
-
3
-
4
- def validate_file_name(file_name: str) -> str:
5
- if "/" in file_name or "\\" in file_name:
6
- raise typer.BadParameter(f"File name must not contain path separators: {file_name}")
7
- if not file_name.endswith(".py"):
8
- raise typer.BadParameter(f"File name must end with .py: {file_name}")
9
- return file_name
10
-
11
-
12
- def validate_file_name_callback(value: str) -> str:
13
- return validate_file_name(file_name=value)
bake/utils/env.py DELETED
@@ -1,10 +0,0 @@
1
- import os
2
-
3
- ENV_NO_COLOR = "NO_COLOR"
4
-
5
- _BAKE_REINVOKED = "_BAKE_REINVOKED"
6
-
7
-
8
- def should_use_colors() -> bool:
9
- value = os.environ.get(ENV_NO_COLOR)
10
- return value == "" or value is None