tgzr.cuisine 0.0.1__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.
File without changes
@@ -0,0 +1,34 @@
1
+ # file generated by setuptools-scm
2
+ # don't change, don't track in version control
3
+
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
12
+
13
+ TYPE_CHECKING = False
14
+ if TYPE_CHECKING:
15
+ from typing import Tuple
16
+ from typing import Union
17
+
18
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
20
+ else:
21
+ VERSION_TUPLE = object
22
+ COMMIT_ID = object
23
+
24
+ version: str
25
+ __version__: str
26
+ __version_tuple__: VERSION_TUPLE
27
+ version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
30
+
31
+ __version__ = version = '0.0.1'
32
+ __version_tuple__ = version_tuple = (0, 0, 1)
33
+
34
+ __commit_id__ = commit_id = None
@@ -0,0 +1,35 @@
1
+ from __future__ import annotations
2
+ from typing import TYPE_CHECKING, Type
3
+
4
+ from ..plugin import CuisinePlugin
5
+
6
+ from ..recipe import Recipe
7
+ from .recipe_with_params import RecipeWithParams
8
+
9
+ from .computable import Computable
10
+ from .files_recipes import FileRecipe
11
+ from .workscene import WorkScene
12
+ from .env import Env
13
+ from .editable import Editable
14
+ from .editor import Editor
15
+ from .buildable import Buildable
16
+ from .builder import Builder
17
+ from .viewable import Viewable
18
+ from .viewer import Viewer
19
+
20
+
21
+ class BasicsPlugin(CuisinePlugin):
22
+
23
+ def get_recipe_types(self) -> list[Type[Recipe]]:
24
+ return [
25
+ Computable,
26
+ FileRecipe,
27
+ WorkScene,
28
+ Env,
29
+ Editable,
30
+ Editor,
31
+ Buildable,
32
+ Builder,
33
+ Viewable,
34
+ Viewer,
35
+ ]
@@ -0,0 +1,38 @@
1
+ from __future__ import annotations
2
+ from typing import Any
3
+
4
+ from .recipe_contexts import CookContext
5
+ from tgzr.cuisine.recipe import RecipeTypeInfo
6
+ from tgzr.cuisine.basics.files_recipes import FileRecipe
7
+
8
+
9
+ @CookContext.context_type
10
+ class BuildContext(CookContext):
11
+ to_build: Buildable
12
+
13
+
14
+ class Buildable(FileRecipe):
15
+ """
16
+ A Recipe which cooks its 'build' input in its context.
17
+ """
18
+
19
+ RECIPE_TYPE_INFO = RecipeTypeInfo(
20
+ category="Build",
21
+ color="#FF8800",
22
+ icon="sym_o_brick",
23
+ )
24
+
25
+ def build_if_needed(self):
26
+ if not self.file_exists():
27
+ CookContext.current().log(
28
+ f"Scene {self.file_path()} is missing, we need to build it."
29
+ )
30
+ self.build()
31
+
32
+ def build(self):
33
+ builder = self.get_unique_input("build")
34
+ with CookContext.get(BuildContext, recipe=builder, to_build=self):
35
+ builder.cook()
36
+
37
+ def cook(self):
38
+ self.build_if_needed()
@@ -0,0 +1,72 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ from tgzr.cuisine.recipe import Recipe, RecipeTypeInfo
6
+ from tgzr.cuisine.basics.recipe_contexts import CookContext
7
+ from tgzr.cuisine.basics.buildable import BuildContext, Buildable
8
+ from tgzr.cuisine.basics.product import Product
9
+ from tgzr.cuisine.basics.env import Env, EnvBuilder
10
+
11
+
12
+ class Builder(Recipe):
13
+ """
14
+ An Recipe which will build the 'file' from the current Buildable Recipe when
15
+ cooked.
16
+ """
17
+
18
+ def as_buildable(self, asset) -> Buildable | None:
19
+ if not isinstance(asset, Buildable):
20
+ return None
21
+ return asset
22
+
23
+ def as_product(self, asset) -> Product | None:
24
+ if not isinstance(asset, Product):
25
+ return None
26
+ return asset
27
+
28
+ def as_env(self, asset) -> Env | None:
29
+ if not isinstance(asset, Env):
30
+ return None
31
+ return asset
32
+
33
+ def build(self):
34
+ ctx = BuildContext.current()
35
+ if ctx is None:
36
+ raise Exception("Cannot build an Recipe outside of an EditContext.")
37
+
38
+ with ctx.log_group(
39
+ f"Cooking Products & Envs connected to {ctx.to_build.name!r}"
40
+ ):
41
+ env_inputs = self.get_typed_inputs(Env, "")
42
+ env_builder = EnvBuilder()
43
+ for name, env_input in env_inputs.items():
44
+ env_builder.add_bases(env_input.cook())
45
+ env = {}
46
+ env_builder.apply_to(env)
47
+ ctx.log(f"Env computed: {env}")
48
+
49
+ product_inputs = self.get_typed_inputs(Product, "")
50
+ for name, product_input in product_inputs.items():
51
+ with ctx.log_group(f"Cooking Product {name!r}"):
52
+ product_input.cook()
53
+ ctx.log(f"All Products cooked: {env}")
54
+
55
+ for input_asset in ctx.to_build.get_inputs():
56
+ if product_input := self.as_product(input_asset):
57
+ with ctx.log_group(f"Cooking {product_input.name!r}"):
58
+ product_input.cook()
59
+ elif env_input := self.as_env(input_asset):
60
+ with ctx.log_group(f"Cooking {env_input.name!r}"):
61
+ env_builder = env_input.cook()
62
+ env = {}
63
+
64
+ ctx.log(f"Env computed: {env_builder}")
65
+ else:
66
+ ctx.log(f"Skipping non Product nor Env input: {input_asset.name}")
67
+
68
+ def cook(self):
69
+ build_ctx = BuildContext.current(raises=False)
70
+ if build_ctx is None:
71
+ raise Exception("Cannot build an asset outside of a BuildContext.")
72
+ self.build()
@@ -0,0 +1,33 @@
1
+ from __future__ import annotations
2
+ from typing import Any
3
+
4
+ from tgzr.cuisine.recipe import Recipe, RecipeTypeInfo
5
+ from tgzr.cuisine.basics.recipe_with_params import RecipeWithParams
6
+
7
+
8
+ class Computable(Recipe):
9
+ """
10
+ A Recipe returning a value in its 'cook()' method.
11
+
12
+ It can read params from inputs in the "params" group
13
+ with `get_inputs_params_dict()->dict[str, Any]`
14
+ """
15
+
16
+ RECIPE_TYPE_INFO = RecipeTypeInfo(
17
+ category="Compute",
18
+ color="#999999",
19
+ icon="sym_o_manufacturing",
20
+ )
21
+
22
+ def get_inputs_params_dict(self) -> dict[str, Any]:
23
+ inputs_with_param = self.get_typed_inputs(RecipeWithParams, "params")
24
+ params = {}
25
+ for name, input in inputs_with_param.items():
26
+ params.update(input.get_params_dict())
27
+ return params
28
+
29
+ def compute(self) -> Any:
30
+ return None
31
+
32
+ def cook(self) -> Any:
33
+ return self.compute()
@@ -0,0 +1,28 @@
1
+ from __future__ import annotations
2
+
3
+ from tgzr.cuisine.basics.buildable import Buildable
4
+ from tgzr.cuisine.basics.recipe_contexts import CookContext
5
+
6
+ CookContext.context_type
7
+
8
+
9
+ class EditContext(CookContext):
10
+ to_edit: Editable
11
+
12
+
13
+ class Editable(Buildable):
14
+ """
15
+ A Buildable Recipe which cooks its 'edit' input in its context after building
16
+ it if needed.
17
+ """
18
+
19
+ def view(self):
20
+ current_context = CookContext.current()
21
+ current_context.log(f"Opening viewer for {self.file_path()}")
22
+ viewer = self.get_unique_input("view")
23
+ with current_context.get(EditContext, asset_to_view=self):
24
+ viewer.cook()
25
+
26
+ def cook(self):
27
+ super().cook()
28
+ self.view()
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ from tgzr.cuisine.recipe import Recipe, RecipeTypeInfo
6
+ from tgzr.cuisine.basics.recipe_contexts import CookContext
7
+ from tgzr.cuisine.basics.editable import EditContext
8
+
9
+
10
+ class Editor(Recipe):
11
+ """
12
+ An asset which will open the 'scene' from the current Editable asset with
13
+ an defined DCC when cooked.
14
+ """
15
+
16
+ RECIPE_TYPE_INFO = RecipeTypeInfo(
17
+ category="View",
18
+ color="#68FF98",
19
+ icon="sym_o_design_service",
20
+ )
21
+
22
+ def launch_editor(self, file_path: Path):
23
+ EditContext.current().log(f"Launching editor on {file_path}")
24
+
25
+ def cook(self):
26
+ ctx = EditContext.current(raises=False)
27
+ if ctx is None:
28
+ raise Exception("Cannot edit a Recipe outside of an EditContext.")
29
+ self.launch_editor(ctx.to_edit.file_path())
@@ -0,0 +1,216 @@
1
+ from __future__ import annotations
2
+ from typing import Literal, Type, Any
3
+
4
+ import os
5
+ import logging
6
+
7
+ from tgzr.cuisine.recipe import RecipeTypeInfo
8
+ from tgzr.cuisine.basics.recipe_with_params import RecipeWithParams, RecipeParams
9
+
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ # ------ Env Builder (should be extracted to dedicated project !)
14
+
15
+
16
+ class EnvOp:
17
+ @classmethod
18
+ def op_name(cls) -> str:
19
+ return cls.__name__
20
+
21
+ def __init__(self, var_name: str, **kwargs):
22
+ self._var_name = var_name
23
+ self._kwargs = kwargs
24
+ self.configure(**kwargs)
25
+
26
+ def configure(self, **kwargs) -> None:
27
+ raise NotImplementedError()
28
+
29
+ def var_name(self) -> str:
30
+ return self._var_name
31
+
32
+ def kwargs(self) -> dict[str, Any]:
33
+ return self._kwargs
34
+
35
+ def apply_to(self, env: dict[str, str]) -> None:
36
+ raise NotImplementedError()
37
+
38
+
39
+ class Set(EnvOp):
40
+ """Set a env var value."""
41
+
42
+ def configure(self, value: str) -> None:
43
+ self.value = value
44
+
45
+ def apply_to(self, base: dict[str, str]) -> None:
46
+ base[self.var_name()] = self.value
47
+
48
+
49
+ class Delete(EnvOp):
50
+ """Remove a env var."""
51
+
52
+ def configure(self, value=None) -> None:
53
+ if value is not None:
54
+ logger.debug(f"Skipping provided value {value} for delete operation.")
55
+
56
+ def apply_to(self, base: dict[str, str]) -> None:
57
+ try:
58
+ del base[self.var_name()]
59
+ except KeyError:
60
+ pass
61
+
62
+
63
+ class Append(EnvOp):
64
+ """Append a value to a path env var."""
65
+
66
+ def configure(self, value: str) -> None:
67
+ self.value = value
68
+
69
+ def apply_to(self, base: dict[str, str]) -> None:
70
+ base_value = base.get(self.var_name(), "")
71
+ if base_value:
72
+ base_list = base_value.split(os.path.pathsep)
73
+ else:
74
+ base_list = []
75
+ base_list.append(self.value)
76
+ base[self.var_name()] = os.path.pathsep.join(base_list)
77
+
78
+
79
+ class Prepend(EnvOp):
80
+ """Insert a value at the begining of a path env var."""
81
+
82
+ def configure(self, value: str) -> None:
83
+ self.value = value
84
+
85
+ def apply_to(self, base: dict[str, str]) -> None:
86
+ base_value = base.get(self.var_name(), "")
87
+ base_list = base_value.split(os.path.pathsep)
88
+ base_list.insert(0, self.value)
89
+ base[self.var_name()] = os.path.pathsep.join(base_list)
90
+
91
+
92
+ class EnvBuilder:
93
+ OPS: dict[str, Type[EnvOp]] = dict(
94
+ set=Set, delete=Delete, append=Append, prepend=Prepend
95
+ )
96
+
97
+ @classmethod
98
+ def op_names(cls) -> tuple[str, ...]:
99
+ """The valid values for the `op_name` argument of `self.add_op(...)`"""
100
+ return tuple(cls.OPS.keys())
101
+
102
+ def __init__(self, *bases: EnvBuilder):
103
+ self._bases: tuple[EnvBuilder, ...] = bases
104
+ self._ops: list[EnvOp] = []
105
+
106
+ def add_bases(self, *bases: EnvBuilder, validate_bases: bool = True) -> None:
107
+ validated = []
108
+ if validate_bases:
109
+ for base in bases:
110
+ if not isinstance(base, self.__class__):
111
+ print(
112
+ f"WARNING: can't use {base} as base for {self}: it is not a {self.__class__} ! (skipping it)"
113
+ )
114
+ else:
115
+ validated.append(base)
116
+ else:
117
+ validated = bases
118
+ self._bases = self._bases + tuple(validated)
119
+
120
+ def ops(self) -> tuple[EnvOp, ...]:
121
+ return tuple(self._ops)
122
+
123
+ def add_op(self, op_name: str, var_name: str, **op_kwargs: Any):
124
+ op = self.OPS[op_name](var_name=var_name, **op_kwargs)
125
+ self._ops.append(op)
126
+
127
+ def set(self, name: str, value: str) -> None:
128
+ self.add_op("set", name, value=value)
129
+
130
+ def delete(self, name: str) -> None:
131
+ self.add_op("delete", name)
132
+
133
+ def append(self, name: str, value) -> None:
134
+ self.add_op("append", name, value=value)
135
+
136
+ def prepend(self, name: str, value) -> None:
137
+ self.add_op("prepend", name, value=value)
138
+
139
+ def apply_to(self, env: dict[str, str]):
140
+ for base in self._bases:
141
+ base.apply_to(env)
142
+ for op in self._ops:
143
+ op.apply_to(env)
144
+
145
+
146
+ # ------ Cusine stuff
147
+
148
+
149
+ class OpParam(RecipeParams):
150
+ enabled: bool = True
151
+ var_name: str = "MyVar"
152
+ op_name: Literal["set", "delete", "append", "prepend"] = "set"
153
+ value: str | None = None
154
+
155
+
156
+ class EnvParams(RecipeParams):
157
+ enabled: bool = True
158
+ operations: list[OpParam] = []
159
+
160
+
161
+ class Env(RecipeWithParams[EnvParams]):
162
+ """
163
+ A Computable Recipe returning a dict[str, str] from its cook method.
164
+ All input recipes must be Env recipes too, and the resulting dict will
165
+ confain the aggregation of all input dicts.
166
+
167
+ ! the inputs order is not 100% assured !
168
+ (we may need to find a solution for that)
169
+ """
170
+
171
+ RECIPE_TYPE_INFO = RecipeTypeInfo(
172
+ category="env",
173
+ color="#82fffb",
174
+ icon="landscape",
175
+ )
176
+
177
+ def get_local_env(self) -> EnvBuilder:
178
+ env = EnvBuilder()
179
+ env.append("visited_env_assets", self.name)
180
+ return env
181
+
182
+ def cook(self) -> EnvBuilder:
183
+
184
+ env = self.get_local_env()
185
+ for name, input in self.get_typed_inputs(Env, "").items():
186
+ input_env_builder = input.cook()
187
+ env.add_bases(input_env_builder)
188
+ return env
189
+
190
+ def _get_params_panel(self):
191
+ """Overridden to add custom field renderers"""
192
+ panel = super()._get_params_panel()
193
+
194
+ from tgzr.cuisine.basics.panels.params_panel import ModelField, ui
195
+
196
+ class OpField(ModelField):
197
+ @classmethod
198
+ def handles(cls, value_type) -> bool:
199
+ return value_type is OpParam
200
+
201
+ def op_symbol(self, op_name: str) -> str:
202
+ return dict(
203
+ set="=",
204
+ delete="delete",
205
+ append="+=",
206
+ prepend="+@0",
207
+ ).get(op_name, f"--{op_name}-->")
208
+
209
+ def _render_readonly_value(self):
210
+ op_param: OpParam = self._getter()
211
+ ui.label(
212
+ f"{op_param.var_name} {self.op_symbol(op_param.op_name)} {op_param.value or ''}"
213
+ ).classes("pl-1 w-full font-mono").style(replace="")
214
+
215
+ panel.add_field_renderer(OpField)
216
+ return panel
@@ -0,0 +1,18 @@
1
+ from pathlib import Path
2
+
3
+ from tgzr.cuisine.recipe import Recipe, RecipeTypeInfo
4
+
5
+
6
+ class FileRecipe(Recipe):
7
+ RECIPE_TYPE_INFO = RecipeTypeInfo(
8
+ category="Files",
9
+ color="#00FFFF",
10
+ icon="sym_o_file",
11
+ )
12
+
13
+ def file_path(self) -> Path:
14
+ print(f"!!! WARNING !!! {self} IS JUST A MOCKUP CLASS !!!")
15
+ return Path(f"/path/to/work/{self.name}/{self.name}.xstage")
16
+
17
+ def file_exists(self) -> bool:
18
+ return self.file_path().exists()
File without changes