From 4fcb257ef77708579c3a5299d7b2eb04117f64cd Mon Sep 17 00:00:00 2001 From: RitikShah Date: Sat, 4 Nov 2023 11:58:44 -0400 Subject: [PATCH 1/9] feat: prototype of weld plugins --- .../custom/data/custom/weld/plugins/main.bolt | 0 examples/basic_plugins/custom/pack.mcmeta | 3 + .../data/fancy_bats/functions/load.mcfunction | 1 + examples/basic_plugins/fancy_bats/pack.mcmeta | 3 + examples/basic_plugins/fancy_bats/pack.png | Bin 0 -> 1236 bytes .../tasty_tacos/functions/load.mcfunction | 1 + .../tasty_tacos/functions/tacos.mcfunction | 1 + .../basic_plugins/tasty_tacos/pack.mcmeta | 3 + examples/basic_plugins/tasty_tacos/pack.png | Bin 0 -> 1236 bytes pyproject.toml | 1 + src/smithed/weld/plugins/__init__.py | 8 ++ src/smithed/weld/plugins/errors.py | 11 ++ src/smithed/weld/plugins/registry.py | 44 +++++++ src/smithed/weld/plugins/resources.py | 35 ++++++ src/smithed/weld/toolchain/main.py | 117 ++++++++++++++++++ uv.lock | 17 ++- 16 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 examples/basic_plugins/custom/data/custom/weld/plugins/main.bolt create mode 100644 examples/basic_plugins/custom/pack.mcmeta create mode 100644 examples/basic_plugins/fancy_bats/data/fancy_bats/functions/load.mcfunction create mode 100644 examples/basic_plugins/fancy_bats/pack.mcmeta create mode 100644 examples/basic_plugins/fancy_bats/pack.png create mode 100644 examples/basic_plugins/tasty_tacos/data/tasty_tacos/functions/load.mcfunction create mode 100644 examples/basic_plugins/tasty_tacos/data/tasty_tacos/functions/tacos.mcfunction create mode 100644 examples/basic_plugins/tasty_tacos/pack.mcmeta create mode 100644 examples/basic_plugins/tasty_tacos/pack.png create mode 100644 src/smithed/weld/plugins/__init__.py create mode 100644 src/smithed/weld/plugins/errors.py create mode 100644 src/smithed/weld/plugins/registry.py create mode 100644 src/smithed/weld/plugins/resources.py create mode 100644 src/smithed/weld/toolchain/main.py diff --git a/examples/basic_plugins/custom/data/custom/weld/plugins/main.bolt b/examples/basic_plugins/custom/data/custom/weld/plugins/main.bolt new file mode 100644 index 0000000..e69de29 diff --git a/examples/basic_plugins/custom/pack.mcmeta b/examples/basic_plugins/custom/pack.mcmeta new file mode 100644 index 0000000..134cc87 --- /dev/null +++ b/examples/basic_plugins/custom/pack.mcmeta @@ -0,0 +1,3 @@ +{ + "id": "custom" +} \ No newline at end of file diff --git a/examples/basic_plugins/fancy_bats/data/fancy_bats/functions/load.mcfunction b/examples/basic_plugins/fancy_bats/data/fancy_bats/functions/load.mcfunction new file mode 100644 index 0000000..806591a --- /dev/null +++ b/examples/basic_plugins/fancy_bats/data/fancy_bats/functions/load.mcfunction @@ -0,0 +1 @@ +say "these bats are wildin'" diff --git a/examples/basic_plugins/fancy_bats/pack.mcmeta b/examples/basic_plugins/fancy_bats/pack.mcmeta new file mode 100644 index 0000000..5847e19 --- /dev/null +++ b/examples/basic_plugins/fancy_bats/pack.mcmeta @@ -0,0 +1,3 @@ +{ + "id": "fancy_bats" +} \ No newline at end of file diff --git a/examples/basic_plugins/fancy_bats/pack.png b/examples/basic_plugins/fancy_bats/pack.png new file mode 100644 index 0000000000000000000000000000000000000000..f4a61a0010d4555338e0edc7bbb9f292ebfeb8ce GIT binary patch literal 1236 zcmV;_1S|WAP)Pm1$C0h*c`VR$`GtP;z3W z7O@bE6B2@of*4T|Bj#2s#2V@I*!AFF*zUNT-T?{at+o%6vrRD?k^#jGdT>`~=v7Cn_Qz0lvT! z6Omp(DDXr?q!SPeeC8tJ3rGb%QxWk5!65&t4 zq=1hs!d<|mf{!G^UO-d82Nq#2psC;kiO?6o;On=$9d}=G0SsR6PN$^^wEzaM2i`F> z{V#yQ+kv+ffeVmt{rPELb83-8lXQ!8ynqDocb58N<+QHOjQ!lTe2|_2C9r_;0$Bi$ z0Pw^ahaf)#Vv<5ox7)2id3Iv6@;f#MVCDS~mug`FB@h;XbTQW+>{^~j05S<-CJ*337S4usnx&ZvL=3N^^h=fCU8U68e?00DV?Lao#{YN5g z+6MSVI%Q#g?RyQe<^Y`&U;)*4+j)IDnguB0XUGu%p~K#dJq|Kx>e)lTMT=ob**|}+ z_DnuMyd{pW^j?Vc?X@!fb>oLPuG+SM+ZC|Q@LaGh54nM3FNaw`6&DNm$Ad>eQS-7> z_%~u2kEg4KEw(}(IDl6{or`xwH{G{*L!=ju-4y9_GcQGYs<$N4SC-4C<<>Vo)rKJ0 z9#xyMr|t$7YhU@mK^H3o!MMo+fB_az6g&cIu4_~wXnApu$bETkL8KplpBL%rS9eAF z(!D1l-T&~#O9;}r3ep+ne;Y$>1h9ZnA4)U{vw%nfu5TWwU5LG(Jui-LZ+{c%!&@u0 z3$goaZ^Ut5S3#y16g3gHJd|q)VgZTQ0W83S066qwZpOhaYt>bdDlDk>taKJ)uukno zS;!E?0>(!F;fE}s3Ta|T06a~d?P$1=X+PK!#XCj2ybAjHOQhSoccI5$U)U@1n6|;R z{V;#B?P%2iG(CA1Fd}KV8_EKP1hN|e8dFnqvRF^T14J6z5zQfKai&hMEQ8<2BFyM( zzQ&SYTWN+B&#ei5R1v?p6-TOAw}JvZX$b y-w{jQLizhu|7dQMj|g~rBLEXY4E*SH{s0zm6rB|ck}CiJ002ovPDHLkV1fW9izZJ1 literal 0 HcmV?d00001 diff --git a/examples/basic_plugins/tasty_tacos/data/tasty_tacos/functions/load.mcfunction b/examples/basic_plugins/tasty_tacos/data/tasty_tacos/functions/load.mcfunction new file mode 100644 index 0000000..19f231d --- /dev/null +++ b/examples/basic_plugins/tasty_tacos/data/tasty_tacos/functions/load.mcfunction @@ -0,0 +1 @@ +say "the tastiest of tacos" diff --git a/examples/basic_plugins/tasty_tacos/data/tasty_tacos/functions/tacos.mcfunction b/examples/basic_plugins/tasty_tacos/data/tasty_tacos/functions/tacos.mcfunction new file mode 100644 index 0000000..c6a19e4 --- /dev/null +++ b/examples/basic_plugins/tasty_tacos/data/tasty_tacos/functions/tacos.mcfunction @@ -0,0 +1 @@ +give @a taco 64 diff --git a/examples/basic_plugins/tasty_tacos/pack.mcmeta b/examples/basic_plugins/tasty_tacos/pack.mcmeta new file mode 100644 index 0000000..27984a2 --- /dev/null +++ b/examples/basic_plugins/tasty_tacos/pack.mcmeta @@ -0,0 +1,3 @@ +{ + "id": "tasty_tacos" +} \ No newline at end of file diff --git a/examples/basic_plugins/tasty_tacos/pack.png b/examples/basic_plugins/tasty_tacos/pack.png new file mode 100644 index 0000000000000000000000000000000000000000..f4a61a0010d4555338e0edc7bbb9f292ebfeb8ce GIT binary patch literal 1236 zcmV;_1S|WAP)Pm1$C0h*c`VR$`GtP;z3W z7O@bE6B2@of*4T|Bj#2s#2V@I*!AFF*zUNT-T?{at+o%6vrRD?k^#jGdT>`~=v7Cn_Qz0lvT! z6Omp(DDXr?q!SPeeC8tJ3rGb%QxWk5!65&t4 zq=1hs!d<|mf{!G^UO-d82Nq#2psC;kiO?6o;On=$9d}=G0SsR6PN$^^wEzaM2i`F> z{V#yQ+kv+ffeVmt{rPELb83-8lXQ!8ynqDocb58N<+QHOjQ!lTe2|_2C9r_;0$Bi$ z0Pw^ahaf)#Vv<5ox7)2id3Iv6@;f#MVCDS~mug`FB@h;XbTQW+>{^~j05S<-CJ*337S4usnx&ZvL=3N^^h=fCU8U68e?00DV?Lao#{YN5g z+6MSVI%Q#g?RyQe<^Y`&U;)*4+j)IDnguB0XUGu%p~K#dJq|Kx>e)lTMT=ob**|}+ z_DnuMyd{pW^j?Vc?X@!fb>oLPuG+SM+ZC|Q@LaGh54nM3FNaw`6&DNm$Ad>eQS-7> z_%~u2kEg4KEw(}(IDl6{or`xwH{G{*L!=ju-4y9_GcQGYs<$N4SC-4C<<>Vo)rKJ0 z9#xyMr|t$7YhU@mK^H3o!MMo+fB_az6g&cIu4_~wXnApu$bETkL8KplpBL%rS9eAF z(!D1l-T&~#O9;}r3ep+ne;Y$>1h9ZnA4)U{vw%nfu5TWwU5LG(Jui-LZ+{c%!&@u0 z3$goaZ^Ut5S3#y16g3gHJd|q)VgZTQ0W83S066qwZpOhaYt>bdDlDk>taKJ)uukno zS;!E?0>(!F;fE}s3Ta|T06a~d?P$1=X+PK!#XCj2ybAjHOQhSoccI5$U)U@1n6|;R z{V;#B?P%2iG(CA1Fd}KV8_EKP1hN|e8dFnqvRF^T14J6z5zQfKai&hMEQ8<2BFyM( zzQ&SYTWN+B&#ei5R1v?p6-TOAw}JvZX$b y-w{jQLizhu|7dQMj|g~rBLEXY4E*SH{s0zm6rB|ck}CiJ002ovPDHLkV1fW9izZJ1 literal 0 HcmV?d00001 diff --git a/pyproject.toml b/pyproject.toml index be37936..1b09516 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,7 @@ dependencies = [ "rich>=13.6.0", "pydantic>=2.5.2", "lectern>=0.34.0", + "bolt>=0.38.5", ] description = "Smithed's Python client with CLI, Weld and more" license = "MIT" diff --git a/src/smithed/weld/plugins/__init__.py b/src/smithed/weld/plugins/__init__.py new file mode 100644 index 0000000..be51152 --- /dev/null +++ b/src/smithed/weld/plugins/__init__.py @@ -0,0 +1,8 @@ +from .registry import WeldPlugin, beet_default +from .resources import load_resources + +__all__ = [ + "WeldPlugin", + "load_resources", + "beet_default", +] diff --git a/src/smithed/weld/plugins/errors.py b/src/smithed/weld/plugins/errors.py new file mode 100644 index 0000000..9bc1425 --- /dev/null +++ b/src/smithed/weld/plugins/errors.py @@ -0,0 +1,11 @@ +class WeldPluginError(Exception): + namespace: str + __cause__: Exception + + def __init__(self, *args, namespace: str, cause: Exception): + super().__init__(*args) + self.namespace = namespace + self.__cause__ = cause + + def __str__(self) -> str: + return f"{self.namespace}: {self.args[0]}\n{self.__cause__}" diff --git a/src/smithed/weld/plugins/registry.py b/src/smithed/weld/plugins/registry.py new file mode 100644 index 0000000..c7f959e --- /dev/null +++ b/src/smithed/weld/plugins/registry.py @@ -0,0 +1,44 @@ +from typing import Any, Iterable + +from beet import Context, DataPack, ResourcePack, TextFileBase +from beet.core.utils import FormatsRangeDict, normalize_string +from bolt.contrib.sandbox import Sandbox +from mecha import CompilationUnit, Mecha + +from .resources import WeldPlugin, load_resources + + +def provide_compilation_units( + pack: ResourcePack | DataPack, + match: list[str] | None = None, +) -> Iterable[tuple[TextFileBase[Any], CompilationUnit]]: + for resource_location in pack[WeldPlugin].match(*match or ["*"]): + file_instance = pack[WeldPlugin][resource_location] + + overlay_name = f"generated_{normalize_string(resource_location)}" + + yield file_instance, CompilationUnit( + resource_location=resource_location, + pack=pack.overlays[overlay_name], + ) + pack.overlays[overlay_name].supported_formats = FormatsRangeDict( + min_inclusive=pack.pack_format, + max_inclusive=pack.pack_format, + ) + + +def define_compilation_unit_providers(ctx: Context): + mc = ctx.inject(Mecha) + mc.providers = [provide_compilation_units] + + +def beet_default(ctx: Context): + sandbox = ctx.inject(Sandbox) + sandbox.allowed_imports |= {"beet", "smithed"} + + ctx.require(define_compilation_unit_providers) + ctx.require(load_resources) + + yield + + ctx.data[WeldPlugin].clear() diff --git a/src/smithed/weld/plugins/resources.py b/src/smithed/weld/plugins/resources.py new file mode 100644 index 0000000..d39d030 --- /dev/null +++ b/src/smithed/weld/plugins/resources.py @@ -0,0 +1,35 @@ +from typing import ClassVar + +from beet import Context, DataPack, Drop, TextFile + + +class WeldPlugin(TextFile): + scope: ClassVar[tuple[str, ...]] = ("weld", "plugins") + extension: ClassVar[str] = ".bolt" + + +class WeldPyPlugin(TextFile): + scope: ClassVar[tuple[str, ...]] = ("weld", "plugins") + extension: ClassVar[str] = ".py" + + def bind(self, pack: DataPack, path: str): + """Rebind file as a proper `Module` and drop current file.""" + + super().bind(pack, path) + + if self.source_path: + pack[path] = WeldPlugin(source_path=self.source_path) + else: + pack[path] = WeldPlugin(self.text) + + raise Drop() + + +class CustomResource(TextFile): + scope: ClassVar[tuple[str, ...]] = ("weld", "resources") + extension: ClassVar[str] = ".py" + + +def load_resources(ctx: Context): + ctx.data.extend_namespace += [WeldPlugin, WeldPyPlugin, CustomResource] + ctx.assets.extend_namespace += [WeldPlugin, WeldPyPlugin, CustomResource] diff --git a/src/smithed/weld/toolchain/main.py b/src/smithed/weld/toolchain/main.py new file mode 100644 index 0000000..6483cd2 --- /dev/null +++ b/src/smithed/weld/toolchain/main.py @@ -0,0 +1,117 @@ +import sys +from contextlib import contextmanager +from functools import partial +from pathlib import Path +from typing import Iterable, Literal, cast +from zipfile import Path as ZipPath +from zipfile import ZipFile + +from beet import Context, ProjectCache, ProjectConfig, run_beet, subproject +from beet.core.utils import FileSystemPath, JsonDict + +from smithed.weld import merging + +from ..errors import WeldError +from .helper_plugins import add_fabric_mod_json + +if sys.version_info >= (3, 11): + from enum import StrEnum +else: + from backports.strenum import StrEnum + +DESCRIPTION = "Merged by Smithed Weld" + + +class PackType(StrEnum): + DATA = "data_pack" + ASSETS = "resource_pack" + + +def subproject_config(pack_type: PackType, name: str = ""): + return subproject( + { + "require": [ + "smithed.weld.plugins.load_resources", + "beet.contrib.auto_yaml", + "beet.contrib.model_merging", + "beet.contrib.unknown_files", + ], + pack_type: {"load": name}, + "pipeline": [ + "smithed.weld.print_pack_name", + "smithed.weld.inject_pack_id_into_smithed", + ], + } + ) + + +def inspect_zipfile(file: ZipFile) -> PackType: + path = ZipPath(file) + + if (path / "data").is_dir(): + return PackType.DATA + elif (path / "assets").is_dir(): + return PackType.ASSETS + + raise WeldError("Invalid. Pack has neither assets nor data.") + + +def inspect(file: str | ZipFile) -> PackType | Literal[False]: + match file: + case str(path) if path.endswith(".zip"): + with ZipFile(path) as zip: + return inspect_zipfile(zip) + + case str(path): + if (path := Path(path)).is_dir(): + if (path / "data").is_dir(): + return PackType.DATA + elif (path / "assets").is_dir(): + return PackType.ASSETS + + raise WeldError(f"Invalid. Pack {path} has neither assets nor data. \n") + + case ZipFile() as zip: + return inspect_zipfile(zip) + + return False + + +@contextmanager +def run_weld( + packs: Iterable[str] | Iterable[ZipFile], + config: FileSystemPath | ProjectConfig | JsonDict = {}, + directory: FileSystemPath | None = None, + cache: bool | ProjectCache = True, + as_fabric_mod: bool = False, +): + packs = cast(list[str] | list[ZipFile], list(packs)) + packs_with_types = zip(packs, (pack for pack in map(inspect, packs) if pack)) + + with run_beet(config, directory=directory, cache=cache) as ctx: + ctx.require(weld) + ctx.require(partial(load_packs, packs=list(packs_with_types))) + ctx.require(merging.process) + if as_fabric_mod: + ctx.require(partial(add_fabric_mod_json, packs=packs)) + ctx.require("mecha") + yield ctx + + +def weld(ctx: Context): + ctx.require("smithed.weld.merging") + ctx.require("smithed.weld.plugins") + ctx.require("beet.contrib.model_merging") + + +def load_packs(ctx: Context, packs: Iterable[tuple[str | ZipFile, PackType]]): + for pack, pack_type in packs: + match pack: + case str(name): + ctx.require(subproject_config(pack_type, name)) + case ZipFile() as file: + match pack_type: + case PackType.DATA: + ctx.data.load(file) + case PackType.ASSETS: + ctx.assets.load(file) diff --git a/uv.lock b/uv.lock index d3c0b64..c0ad3f8 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 5 +revision = 3 requires-python = ">=3.14" [[package]] @@ -131,6 +131,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, ] +[[package]] +name = "bolt" +version = "0.51.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beet" }, + { name = "mecha" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/ff/4fcca0be75b32a790bc751a0113ede064d033c517167c0de2ae228bf8389/bolt-0.51.0.tar.gz", hash = "sha256:1abb78bf058cb7439cacc60cde8d3b9af49cadc147990b3525c16f4bc2463461", size = 52523, upload-time = "2026-09-10T18:33:06.99Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/5e/83d8dddc4603ac43b71569247ba53d18149198b8a70ebc9f6d2e1390d486/bolt-0.51.0-py3-none-any.whl", hash = "sha256:ef3fb1f40c01c4a5d216b8440f98a083368f53b12989d795e1a684ff9f416ad1", size = 59765, upload-time = "2026-09-10T18:33:05.858Z" }, +] + [[package]] name = "boto3" version = "1.42.71" @@ -1718,6 +1731,7 @@ source = { editable = "." } dependencies = [ { name = "backports-strenum" }, { name = "beet" }, + { name = "bolt" }, { name = "lectern" }, { name = "mecha" }, { name = "pydantic" }, @@ -1750,6 +1764,7 @@ github-workflows = [ requires-dist = [ { name = "backports-strenum", specifier = ">=1.2.8" }, { name = "beet", specifier = ">=0.119.0" }, + { name = "bolt", specifier = ">=0.38.5" }, { name = "lectern", specifier = ">=0.34.0" }, { name = "mecha", specifier = ">=0.106.0" }, { name = "pydantic", specifier = ">=2.5.2" }, From fbb714c875ae489874448ca9c6f721e8661808ec Mon Sep 17 00:00:00 2001 From: RitikShah Date: Sun, 5 May 2024 14:56:59 -0500 Subject: [PATCH 2/9] partial weld plugins --- .../custom/data/custom/weld/plugins/main.bolt | 1 + examples/basic_plugins/custom/pack.png | Bin 0 -> 1236 bytes examples/basic_plugins/tasty_tacos/pack.png | Bin 1236 -> 4927 bytes src/smithed/weld/__init__.py | 13 ++++++++++++- src/smithed/weld/plugins/__init__.py | 3 ++- src/smithed/weld/plugins/errors.py | 3 +++ src/smithed/weld/plugins/registry.py | 13 ++++++++++--- src/smithed/weld/toolchain/__init__.py | 16 +++++++++++++--- src/smithed/weld/toolchain/main.py | 6 ++++-- 9 files changed, 45 insertions(+), 10 deletions(-) create mode 100644 examples/basic_plugins/custom/pack.png diff --git a/examples/basic_plugins/custom/data/custom/weld/plugins/main.bolt b/examples/basic_plugins/custom/data/custom/weld/plugins/main.bolt index e69de29..5ad6a7b 100644 --- a/examples/basic_plugins/custom/data/custom/weld/plugins/main.bolt +++ b/examples/basic_plugins/custom/data/custom/weld/plugins/main.bolt @@ -0,0 +1 @@ +print(ctx.meta) \ No newline at end of file diff --git a/examples/basic_plugins/custom/pack.png b/examples/basic_plugins/custom/pack.png new file mode 100644 index 0000000000000000000000000000000000000000..f4a61a0010d4555338e0edc7bbb9f292ebfeb8ce GIT binary patch literal 1236 zcmV;_1S|WAP)Pm1$C0h*c`VR$`GtP;z3W z7O@bE6B2@of*4T|Bj#2s#2V@I*!AFF*zUNT-T?{at+o%6vrRD?k^#jGdT>`~=v7Cn_Qz0lvT! z6Omp(DDXr?q!SPeeC8tJ3rGb%QxWk5!65&t4 zq=1hs!d<|mf{!G^UO-d82Nq#2psC;kiO?6o;On=$9d}=G0SsR6PN$^^wEzaM2i`F> z{V#yQ+kv+ffeVmt{rPELb83-8lXQ!8ynqDocb58N<+QHOjQ!lTe2|_2C9r_;0$Bi$ z0Pw^ahaf)#Vv<5ox7)2id3Iv6@;f#MVCDS~mug`FB@h;XbTQW+>{^~j05S<-CJ*337S4usnx&ZvL=3N^^h=fCU8U68e?00DV?Lao#{YN5g z+6MSVI%Q#g?RyQe<^Y`&U;)*4+j)IDnguB0XUGu%p~K#dJq|Kx>e)lTMT=ob**|}+ z_DnuMyd{pW^j?Vc?X@!fb>oLPuG+SM+ZC|Q@LaGh54nM3FNaw`6&DNm$Ad>eQS-7> z_%~u2kEg4KEw(}(IDl6{or`xwH{G{*L!=ju-4y9_GcQGYs<$N4SC-4C<<>Vo)rKJ0 z9#xyMr|t$7YhU@mK^H3o!MMo+fB_az6g&cIu4_~wXnApu$bETkL8KplpBL%rS9eAF z(!D1l-T&~#O9;}r3ep+ne;Y$>1h9ZnA4)U{vw%nfu5TWwU5LG(Jui-LZ+{c%!&@u0 z3$goaZ^Ut5S3#y16g3gHJd|q)VgZTQ0W83S066qwZpOhaYt>bdDlDk>taKJ)uukno zS;!E?0>(!F;fE}s3Ta|T06a~d?P$1=X+PK!#XCj2ybAjHOQhSoccI5$U)U@1n6|;R z{V;#B?P%2iG(CA1Fd}KV8_EKP1hN|e8dFnqvRF^T14J6z5zQfKai&hMEQ8<2BFyM( zzQ&SYTWN+B&#ei5R1v?p6-TOAw}JvZX$b y-w{jQLizhu|7dQMj|g~rBLEXY4E*SH{s0zm6rB|ck}CiJ002ovPDHLkV1fW9izZJ1 literal 0 HcmV?d00001 diff --git a/examples/basic_plugins/tasty_tacos/pack.png b/examples/basic_plugins/tasty_tacos/pack.png index f4a61a0010d4555338e0edc7bbb9f292ebfeb8ce..ff0e7c9a3990d1350e659985ca608e34b4180c48 100644 GIT binary patch literal 4927 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 literal 1236 zcmV;_1S|WAP)Pm1$C0h*c`VR$`GtP;z3W z7O@bE6B2@of*4T|Bj#2s#2V@I*!AFF*zUNT-T?{at+o%6vrRD?k^#jGdT>`~=v7Cn_Qz0lvT! z6Omp(DDXr?q!SPeeC8tJ3rGb%QxWk5!65&t4 zq=1hs!d<|mf{!G^UO-d82Nq#2psC;kiO?6o;On=$9d}=G0SsR6PN$^^wEzaM2i`F> z{V#yQ+kv+ffeVmt{rPELb83-8lXQ!8ynqDocb58N<+QHOjQ!lTe2|_2C9r_;0$Bi$ z0Pw^ahaf)#Vv<5ox7)2id3Iv6@;f#MVCDS~mug`FB@h;XbTQW+>{^~j05S<-CJ*337S4usnx&ZvL=3N^^h=fCU8U68e?00DV?Lao#{YN5g z+6MSVI%Q#g?RyQe<^Y`&U;)*4+j)IDnguB0XUGu%p~K#dJq|Kx>e)lTMT=ob**|}+ z_DnuMyd{pW^j?Vc?X@!fb>oLPuG+SM+ZC|Q@LaGh54nM3FNaw`6&DNm$Ad>eQS-7> z_%~u2kEg4KEw(}(IDl6{or`xwH{G{*L!=ju-4y9_GcQGYs<$N4SC-4C<<>Vo)rKJ0 z9#xyMr|t$7YhU@mK^H3o!MMo+fB_az6g&cIu4_~wXnApu$bETkL8KplpBL%rS9eAF z(!D1l-T&~#O9;}r3ep+ne;Y$>1h9ZnA4)U{vw%nfu5TWwU5LG(Jui-LZ+{c%!&@u0 z3$goaZ^Ut5S3#y16g3gHJd|q)VgZTQ0W83S066qwZpOhaYt>bdDlDk>taKJ)uukno zS;!E?0>(!F;fE}s3Ta|T06a~d?P$1=X+PK!#XCj2ybAjHOQhSoccI5$U)U@1n6|;R z{V;#B?P%2iG(CA1Fd}KV8_EKP1hN|e8dFnqvRF^T14J6z5zQfKai&hMEQ8<2BFyM( zzQ&SYTWN+B&#ei5R1v?p6-TOAw}JvZX$b y-w{jQLizhu|7dQMj|g~rBLEXY4E*SH{s0zm6rB|ck}CiJ002ovPDHLkV1fW9izZJ1 diff --git a/src/smithed/weld/__init__.py b/src/smithed/weld/__init__.py index 17a2fa2..a4e7fed 100644 --- a/src/smithed/weld/__init__.py +++ b/src/smithed/weld/__init__.py @@ -1,8 +1,19 @@ -from .toolchain import cli, run_weld, weld, weld_handler +from .toolchain import ( + cache_pack_metadata, + cli, + inject_pack_stuff_into_smithed, + print_pack_name, + run_weld, + weld, + weld_handler, +) __version__ = "0.20.0" __all__ = [ + "print_pack_name", + "cache_pack_metadata", + "inject_pack_stuff_into_smithed", "cli", "run_weld", "weld", diff --git a/src/smithed/weld/plugins/__init__.py b/src/smithed/weld/plugins/__init__.py index be51152..50347de 100644 --- a/src/smithed/weld/plugins/__init__.py +++ b/src/smithed/weld/plugins/__init__.py @@ -1,8 +1,9 @@ -from .registry import WeldPlugin, beet_default +from .registry import WeldPlugin, beet_default, clear_plugins from .resources import load_resources __all__ = [ "WeldPlugin", "load_resources", "beet_default", + "clear_plugins", ] diff --git a/src/smithed/weld/plugins/errors.py b/src/smithed/weld/plugins/errors.py index 9bc1425..bd72e23 100644 --- a/src/smithed/weld/plugins/errors.py +++ b/src/smithed/weld/plugins/errors.py @@ -1,3 +1,6 @@ +# type: ignore + + class WeldPluginError(Exception): namespace: str __cause__: Exception diff --git a/src/smithed/weld/plugins/registry.py b/src/smithed/weld/plugins/registry.py index c7f959e..30173b5 100644 --- a/src/smithed/weld/plugins/registry.py +++ b/src/smithed/weld/plugins/registry.py @@ -5,7 +5,7 @@ from bolt.contrib.sandbox import Sandbox from mecha import CompilationUnit, Mecha -from .resources import WeldPlugin, load_resources +from .resources import CustomResource, WeldPlugin, load_resources def provide_compilation_units( @@ -39,6 +39,13 @@ def beet_default(ctx: Context): ctx.require(define_compilation_unit_providers) ctx.require(load_resources) - yield - ctx.data[WeldPlugin].clear() +def clear_plugins(ctx: Context): + for pack in [ + ctx.data, + *ctx.data.overlays.values(), + ctx.assets, + *ctx.assets.overlays.values(), + ]: + for resource in [WeldPlugin, CustomResource]: + pack[resource].clear() diff --git a/src/smithed/weld/toolchain/__init__.py b/src/smithed/weld/toolchain/__init__.py index dbf5de3..9161f58 100644 --- a/src/smithed/weld/toolchain/__init__.py +++ b/src/smithed/weld/toolchain/__init__.py @@ -1,5 +1,15 @@ from .cli import cli -from .helpers import run_weld -from .plugins import weld, weld_handler +from .helper_plugins import ( + cache_pack_metadata, + inject_pack_stuff_into_smithed, + print_pack_name, +) +from .main import run_weld -__all__ = ["cli", "run_weld", "weld", "weld_handler"] +__all__ = [ + "cli", + "run_weld", + "print_pack_name", + "inject_pack_stuff_into_smithed", + "cache_pack_metadata", +] diff --git a/src/smithed/weld/toolchain/main.py b/src/smithed/weld/toolchain/main.py index 6483cd2..c074dab 100644 --- a/src/smithed/weld/toolchain/main.py +++ b/src/smithed/weld/toolchain/main.py @@ -9,7 +9,7 @@ from beet import Context, ProjectCache, ProjectConfig, run_beet, subproject from beet.core.utils import FileSystemPath, JsonDict -from smithed.weld import merging +from smithed.weld import merging, plugins from ..errors import WeldError from .helper_plugins import add_fabric_mod_json @@ -39,7 +39,8 @@ def subproject_config(pack_type: PackType, name: str = ""): pack_type: {"load": name}, "pipeline": [ "smithed.weld.print_pack_name", - "smithed.weld.inject_pack_id_into_smithed", + "smithed.weld.inject_pack_stuff_into_smithed", + "smithed.weld.cache_pack_metadata", ], } ) @@ -95,6 +96,7 @@ def run_weld( if as_fabric_mod: ctx.require(partial(add_fabric_mod_json, packs=packs)) ctx.require("mecha") + ctx.require(plugins.clear_plugins) yield ctx From fda31233b9b8a548862d0263ce6fdd375a4cdf69 Mon Sep 17 00:00:00 2001 From: RitikShah Date: Fri, 21 Mar 2025 15:21:02 -0700 Subject: [PATCH 3/9] fix: weld scripts --- .../custom/data/custom/weld/plugins/main.bolt | 1 - .../custom/data/custom/weld/scripts/main.bolt | 1 + .../custom/pack.mcmeta | 0 .../custom/pack.png | Bin .../data/fancy_bats/functions/load.mcfunction | 0 .../fancy_bats/pack.mcmeta | 0 .../fancy_bats/pack.png | Bin .../tasty_tacos/functions/load.mcfunction | 0 .../tasty_tacos/functions/tacos.mcfunction | 0 .../tasty_tacos/pack.mcmeta | 0 .../tasty_tacos/pack.png | Bin smithed/weld/models/main.py | 82 ++++++++++ smithed/weld/scripts/__init__.py | 9 ++ smithed/weld/scripts/errors.py | 11 ++ smithed/weld/scripts/registry.py | 51 +++++++ smithed/weld/scripts/resources.py | 35 +++++ smithed/weld/toolchain/process.py | 143 ++++++++++++++++++ src/smithed/weld/__init__.py | 16 +- src/smithed/weld/plugins/__init__.py | 4 +- src/smithed/weld/plugins/errors.py | 7 +- src/smithed/weld/plugins/registry.py | 12 +- src/smithed/weld/plugins/resources.py | 12 +- src/smithed/weld/toolchain/__init__.py | 8 +- src/smithed/weld/toolchain/main.py | 6 +- src/smithed/weld/toolchain/plugins.py | 16 ++ .../examples__build_basic_scripts__0.pack.md | 45 ++++++ 26 files changed, 422 insertions(+), 37 deletions(-) delete mode 100644 examples/basic_plugins/custom/data/custom/weld/plugins/main.bolt create mode 100644 examples/basic_scripts/custom/data/custom/weld/scripts/main.bolt rename examples/{basic_plugins => basic_scripts}/custom/pack.mcmeta (100%) rename examples/{basic_plugins => basic_scripts}/custom/pack.png (100%) rename examples/{basic_plugins => basic_scripts}/fancy_bats/data/fancy_bats/functions/load.mcfunction (100%) rename examples/{basic_plugins => basic_scripts}/fancy_bats/pack.mcmeta (100%) rename examples/{basic_plugins => basic_scripts}/fancy_bats/pack.png (100%) rename examples/{basic_plugins => basic_scripts}/tasty_tacos/data/tasty_tacos/functions/load.mcfunction (100%) rename examples/{basic_plugins => basic_scripts}/tasty_tacos/data/tasty_tacos/functions/tacos.mcfunction (100%) rename examples/{basic_plugins => basic_scripts}/tasty_tacos/pack.mcmeta (100%) rename examples/{basic_plugins => basic_scripts}/tasty_tacos/pack.png (100%) create mode 100644 smithed/weld/models/main.py create mode 100644 smithed/weld/scripts/__init__.py create mode 100644 smithed/weld/scripts/errors.py create mode 100644 smithed/weld/scripts/registry.py create mode 100644 smithed/weld/scripts/resources.py create mode 100644 smithed/weld/toolchain/process.py create mode 100644 tests/snapshots/examples__build_basic_scripts__0.pack.md diff --git a/examples/basic_plugins/custom/data/custom/weld/plugins/main.bolt b/examples/basic_plugins/custom/data/custom/weld/plugins/main.bolt deleted file mode 100644 index 5ad6a7b..0000000 --- a/examples/basic_plugins/custom/data/custom/weld/plugins/main.bolt +++ /dev/null @@ -1 +0,0 @@ -print(ctx.meta) \ No newline at end of file diff --git a/examples/basic_scripts/custom/data/custom/weld/scripts/main.bolt b/examples/basic_scripts/custom/data/custom/weld/scripts/main.bolt new file mode 100644 index 0000000..1d39d13 --- /dev/null +++ b/examples/basic_scripts/custom/data/custom/weld/scripts/main.bolt @@ -0,0 +1 @@ +print(ctx.meta) diff --git a/examples/basic_plugins/custom/pack.mcmeta b/examples/basic_scripts/custom/pack.mcmeta similarity index 100% rename from examples/basic_plugins/custom/pack.mcmeta rename to examples/basic_scripts/custom/pack.mcmeta diff --git a/examples/basic_plugins/custom/pack.png b/examples/basic_scripts/custom/pack.png similarity index 100% rename from examples/basic_plugins/custom/pack.png rename to examples/basic_scripts/custom/pack.png diff --git a/examples/basic_plugins/fancy_bats/data/fancy_bats/functions/load.mcfunction b/examples/basic_scripts/fancy_bats/data/fancy_bats/functions/load.mcfunction similarity index 100% rename from examples/basic_plugins/fancy_bats/data/fancy_bats/functions/load.mcfunction rename to examples/basic_scripts/fancy_bats/data/fancy_bats/functions/load.mcfunction diff --git a/examples/basic_plugins/fancy_bats/pack.mcmeta b/examples/basic_scripts/fancy_bats/pack.mcmeta similarity index 100% rename from examples/basic_plugins/fancy_bats/pack.mcmeta rename to examples/basic_scripts/fancy_bats/pack.mcmeta diff --git a/examples/basic_plugins/fancy_bats/pack.png b/examples/basic_scripts/fancy_bats/pack.png similarity index 100% rename from examples/basic_plugins/fancy_bats/pack.png rename to examples/basic_scripts/fancy_bats/pack.png diff --git a/examples/basic_plugins/tasty_tacos/data/tasty_tacos/functions/load.mcfunction b/examples/basic_scripts/tasty_tacos/data/tasty_tacos/functions/load.mcfunction similarity index 100% rename from examples/basic_plugins/tasty_tacos/data/tasty_tacos/functions/load.mcfunction rename to examples/basic_scripts/tasty_tacos/data/tasty_tacos/functions/load.mcfunction diff --git a/examples/basic_plugins/tasty_tacos/data/tasty_tacos/functions/tacos.mcfunction b/examples/basic_scripts/tasty_tacos/data/tasty_tacos/functions/tacos.mcfunction similarity index 100% rename from examples/basic_plugins/tasty_tacos/data/tasty_tacos/functions/tacos.mcfunction rename to examples/basic_scripts/tasty_tacos/data/tasty_tacos/functions/tacos.mcfunction diff --git a/examples/basic_plugins/tasty_tacos/pack.mcmeta b/examples/basic_scripts/tasty_tacos/pack.mcmeta similarity index 100% rename from examples/basic_plugins/tasty_tacos/pack.mcmeta rename to examples/basic_scripts/tasty_tacos/pack.mcmeta diff --git a/examples/basic_plugins/tasty_tacos/pack.png b/examples/basic_scripts/tasty_tacos/pack.png similarity index 100% rename from examples/basic_plugins/tasty_tacos/pack.png rename to examples/basic_scripts/tasty_tacos/pack.png diff --git a/smithed/weld/models/main.py b/smithed/weld/models/main.py new file mode 100644 index 0000000..33ed0bd --- /dev/null +++ b/smithed/weld/models/main.py @@ -0,0 +1,82 @@ +import json +import logging +from typing import Any + +from beet import ListOption +from pydantic.v1 import Field, root_validator + +from ..merging.parser import get +from .base import BaseModel +from .priority import Priority +from .rules import AdditiveRule, Rule +from .sources import ReferenceSource, ValueSource + +logger = logging.getLogger(__name__) + + +def deserialize(model: BaseModel, defaults: bool = True): + return json.loads(model.json(by_alias=True, exclude_defaults=not defaults)) + + +class SmithedModel(BaseModel, extra="forbid"): + id: str = "" + version: int = 1 + override: bool | None = None # only should be set by bundle packs + priority: Priority | None = None + rules: list[Rule] = [] + + @root_validator + def push_down_priorities(cls, values: dict[str, Any]) -> dict[str, Any]: + """Push down top-level priority to every rule. + + If a rule has a priority defined, it will not be overwritten. + """ + + rules: list[Rule] = values.get("rules") # type: ignore + priority: Priority | None = values.get("priority") # type: ignore + + if priority is None: + priority = Priority() + + for rule in rules: + if rule.priority is None: + rule.priority = priority + + values.pop("priority") + + return values + + +class SmithedJsonFile(BaseModel, extra="allow"): + """Accepts any standard JSON file from in-game, only needs as `__smithed__` field + for custom merging logic""" + + smithed: ListOption[SmithedModel] = Field( + default_factory=ListOption, alias="__smithed__" + ) + + @root_validator + def convert_type(cls, values: dict[str, ListOption[SmithedModel]]): + if smithed := values.get("smithed"): + for model in smithed.entries(): + model.rules = list(cls.convert_rules(model.rules, values)) + + return values + + @classmethod + def convert_rules(cls, rules: list["Rule"], values: dict[str, Any]): + """Converts the source field of additive rules to a value source if it is + a reference source, essentially "baking" it in for ease of use later. + """ + + for rule in rules: + match isinstance(rule, AdditiveRule) and rule.source: + case ReferenceSource(path=path): + try: + rule.source = ValueSource(value=get(values, path)) + except ValueError: + logger.warning( + f"Source Reference Path: {path} was not found, deleting." + ) + continue + yield rule diff --git a/smithed/weld/scripts/__init__.py b/smithed/weld/scripts/__init__.py new file mode 100644 index 0000000..a8bf23a --- /dev/null +++ b/smithed/weld/scripts/__init__.py @@ -0,0 +1,9 @@ +from .registry import WeldScript, beet_default, clear_plugins +from .resources import load_resources + +__all__ = [ + "WeldScript", + "load_resources", + "beet_default", + "clear_plugins", +] diff --git a/smithed/weld/scripts/errors.py b/smithed/weld/scripts/errors.py new file mode 100644 index 0000000..f90e29b --- /dev/null +++ b/smithed/weld/scripts/errors.py @@ -0,0 +1,11 @@ +class WeldScriptError(Exception): + namespace: str + __cause__: Exception + + def __init__(self, *args, namespace: str, cause: Exception): + super().__init__(*args) + self.namespace = namespace + self.__cause__ = cause # type: ignore + + def __str__(self) -> str: + return f"{self.namespace}: {self.args[0]}\n{self.__cause__}" diff --git a/smithed/weld/scripts/registry.py b/smithed/weld/scripts/registry.py new file mode 100644 index 0000000..bbba67f --- /dev/null +++ b/smithed/weld/scripts/registry.py @@ -0,0 +1,51 @@ +from typing import Any, Iterable, cast + +from beet import Context, DataPack, ResourcePack, TextFileBase +from beet.core.utils import FormatsRangeDict, normalize_string +from bolt.contrib.sandbox import Sandbox +from mecha import CompilationUnit, Mecha + +from .resources import CustomResource, WeldScript, load_resources + + +def provide_compilation_units( + pack: ResourcePack | DataPack, + match: list[str] | None = None, +) -> Iterable[tuple[TextFileBase[Any], CompilationUnit]]: + for resource_location in pack[WeldScript].match(*match or ["*"]): + file_instance = pack[WeldScript][resource_location] + + overlay_name = f"generated_{normalize_string(resource_location)}" + + yield cast(TextFileBase[Any], file_instance), CompilationUnit( + resource_location=resource_location, + pack=pack.overlays[overlay_name], + ) + pack.overlays[overlay_name].supported_formats = FormatsRangeDict( + min_inclusive=pack.pack_format, + max_inclusive=pack.pack_format, + ) + + +def define_compilation_unit_providers(ctx: Context): + mc = ctx.inject(Mecha) + mc.providers = [provide_compilation_units] + + +def beet_default(ctx: Context): + sandbox = ctx.inject(Sandbox) + sandbox.allowed_imports |= {"beet", "smithed"} + + ctx.require(define_compilation_unit_providers) + ctx.require(load_resources) + + +def clear_plugins(ctx: Context): + for pack in [ + ctx.data, + *ctx.data.overlays.values(), + ctx.assets, + *ctx.assets.overlays.values(), + ]: + for resource in [WeldScript, CustomResource]: + pack[resource].clear() diff --git a/smithed/weld/scripts/resources.py b/smithed/weld/scripts/resources.py new file mode 100644 index 0000000..747d1f5 --- /dev/null +++ b/smithed/weld/scripts/resources.py @@ -0,0 +1,35 @@ +from typing import ClassVar + +from beet import Context, DataPack, Drop, TextFile + + +class WeldScript(TextFile): + scope: ClassVar[tuple[str, ...]] = ("weld", "plugins") + extension: ClassVar[str] = ".bolt" + + +class WeldPyScript(TextFile): + scope: ClassVar[tuple[str, ...]] = ("weld", "plugins") + extension: ClassVar[str] = ".py" + + def bind(self, pack: DataPack, path: str): + """Rebind file as a proper `Module` and drop current file.""" + + super().bind(pack, path) + + if self.source_path: + pack[path] = WeldScript(source_path=self.source_path) + else: + pack[path] = WeldScript(self.text) + + raise Drop() + + +class CustomResource(TextFile): + scope: ClassVar[tuple[str, ...]] = ("weld", "resources") + extension: ClassVar[str] = ".py" + + +def load_resources(ctx: Context): + ctx.data.extend_namespace += [WeldScript, WeldPyScript, CustomResource] + ctx.assets.extend_namespace += [WeldScript, WeldPyScript, CustomResource] diff --git a/smithed/weld/toolchain/process.py b/smithed/weld/toolchain/process.py new file mode 100644 index 0000000..3306a18 --- /dev/null +++ b/smithed/weld/toolchain/process.py @@ -0,0 +1,143 @@ +import logging +from dataclasses import dataclass, field +from pathlib import Path +from typing import NamedTuple +from zipfile import Path as ZipPath +from zipfile import ZipFile + +from beet import ( + Context, + DataPack, + DeserializationError, + JsonFileBase, + Mcmeta, + PackQuery, + ResourcePack, +) +from beet.contrib.auto_yaml import use_auto_yaml +from beet.contrib.model_merging import model_merging +from beet.contrib.unknown_files import UnknownAsset, UnknownData + +from ...type import JsonDict +from ..errors import InvalidMcmeta, InvalidPack + +logger = logging.getLogger("weld") + +_Pack = DataPack | ResourcePack + + +class PackWithName(NamedTuple): + pack: _Pack + name: str + + +@dataclass +class PackProcessor: + ctx: Context + file_id_cache: dict[JsonFileBase[JsonDict], _Pack] = field(default_factory=dict) + packs: list[PackWithName] = field(default_factory=list) + + def __getitem__(self, key: JsonFileBase[JsonDict]): + return self.file_id_cache[key] + + def __setitem__(self, key: JsonFileBase[JsonDict], value: _Pack): + self.file_id_cache[key] = value + + def get_pack_type(self, path: ZipPath | Path) -> DataPack | ResourcePack: + """TODO:""" + if (path / "data").is_dir(): + pack = DataPack() + pack.extend_namespace += [UnknownData] + + elif (path / "assets").is_dir(): + pack = ResourcePack() + pack.extend_namespace += [UnknownAsset] + model_merging(pack) + + else: + raise InvalidPack(str(path)) + + use_auto_yaml(pack) + + return pack + + def create_pack(self, file: str | ZipFile) -> DataPack | ResourcePack: + """Creates a DataPack or ResourcePack given a file (either ZipFile or str). + + 1. Determine type of file and it's name to figure out how to load it. + 2. Peek inside to figure out if it's a data or resource pack. + 3. Create an empty pack and load some default plugin behavior: + - `beet.contrib.unknown_files` + - `beet.contrib.model_merging` + - `beet.contrib.use_auto_yaml` + 4. Load the actual file into the empty pack object. + 5. Inject smithed specific information to some resource files within. + 6. Return the pack and it's name as a tuple. + + This process of loading the pack data in it's own object is isolated: + - We need to load the pack data isolated so if there's an error within, we can + surface it properly so it can be handled by the outer app. This allows us to + skip the pack or just highlight the broken pack in a weld process. + - File names, `pack.mcmeta`s, and the specific file tree gets lots when merged into + a larger pack. While resource files themselves get conflict handled via our + merger, the pack "metainfo" is useful enough for us to tabulate within itself. + - Specific errors from invalid mcmeta and json files is useful to surface before the + actual merging process. + """ + + def match_file(file: str | ZipFile) -> tuple[Path | ZipPath, str]: + match file: + case ZipFile() as f: + name = f.filename or "" + if not name.endswith(".zip"): + name = f"{name}.zip" + return ZipPath(f), name + + case str() as name: + if name.endswith(".zip"): + return match_file(ZipFile(name)) + return Path(name), name + + path, name = match_file(file) + pack = self.get_pack_type(path) + + try: + logger.info(f"Loading pack: {name}") + pack.load(file) + + except DeserializationError as err: + if isinstance(err.file, Mcmeta): + cause = err.__cause__ + reason = str(cause) + raise InvalidMcmeta(pack=name, cause=f"\n{reason}") from err # type: ignore + raise err + + self.cache_pack(pack, name) + + return pack + + def load_pack(self, pack: DataPack | ResourcePack): + """Loads a DataPack or ResourcePack into a context by merging it.""" + + match pack: + case DataPack() as dp: + self.ctx.data.merge(dp) + case ResourcePack() as rp: + self.ctx.assets.merge(rp) + + def cache_pack(self, pack: DataPack | ResourcePack, name: str): + """Cache some metadata from resource files to be used in later welding.""" + + if "id" not in pack.mcmeta.data: + pack.mcmeta.data["id"] = self.ctx.generate.format("missing_{incr}") + + for k in ( + PackQuery([pack]).distinct(match="*", extend=JsonFileBase[JsonDict]).keys() + ): + self[k] = pack + + self.packs.append(PackWithName(pack, name)) + + def load_packs(self, packs: list[str] | list[ZipFile]): + for pack in packs: + self.load_pack(self.create_pack(pack)) diff --git a/src/smithed/weld/__init__.py b/src/smithed/weld/__init__.py index a4e7fed..2f1a3c0 100644 --- a/src/smithed/weld/__init__.py +++ b/src/smithed/weld/__init__.py @@ -1,21 +1,21 @@ from .toolchain import ( cache_pack_metadata, cli, - inject_pack_stuff_into_smithed, - print_pack_name, + # inject_pack_stuff_into_smithed, + # print_pack_name, run_weld, - weld, - weld_handler, + # weld, + # weld_handler, ) __version__ = "0.20.0" __all__ = [ - "print_pack_name", + # "print_pack_name", "cache_pack_metadata", - "inject_pack_stuff_into_smithed", + # "inject_pack_stuff_into_smithed", "cli", "run_weld", - "weld", - "weld_handler", + # "weld", + # "weld_handler", ] diff --git a/src/smithed/weld/plugins/__init__.py b/src/smithed/weld/plugins/__init__.py index 50347de..a8bf23a 100644 --- a/src/smithed/weld/plugins/__init__.py +++ b/src/smithed/weld/plugins/__init__.py @@ -1,8 +1,8 @@ -from .registry import WeldPlugin, beet_default, clear_plugins +from .registry import WeldScript, beet_default, clear_plugins from .resources import load_resources __all__ = [ - "WeldPlugin", + "WeldScript", "load_resources", "beet_default", "clear_plugins", diff --git a/src/smithed/weld/plugins/errors.py b/src/smithed/weld/plugins/errors.py index bd72e23..f90e29b 100644 --- a/src/smithed/weld/plugins/errors.py +++ b/src/smithed/weld/plugins/errors.py @@ -1,14 +1,11 @@ -# type: ignore - - -class WeldPluginError(Exception): +class WeldScriptError(Exception): namespace: str __cause__: Exception def __init__(self, *args, namespace: str, cause: Exception): super().__init__(*args) self.namespace = namespace - self.__cause__ = cause + self.__cause__ = cause # type: ignore def __str__(self) -> str: return f"{self.namespace}: {self.args[0]}\n{self.__cause__}" diff --git a/src/smithed/weld/plugins/registry.py b/src/smithed/weld/plugins/registry.py index 30173b5..bbba67f 100644 --- a/src/smithed/weld/plugins/registry.py +++ b/src/smithed/weld/plugins/registry.py @@ -1,23 +1,23 @@ -from typing import Any, Iterable +from typing import Any, Iterable, cast from beet import Context, DataPack, ResourcePack, TextFileBase from beet.core.utils import FormatsRangeDict, normalize_string from bolt.contrib.sandbox import Sandbox from mecha import CompilationUnit, Mecha -from .resources import CustomResource, WeldPlugin, load_resources +from .resources import CustomResource, WeldScript, load_resources def provide_compilation_units( pack: ResourcePack | DataPack, match: list[str] | None = None, ) -> Iterable[tuple[TextFileBase[Any], CompilationUnit]]: - for resource_location in pack[WeldPlugin].match(*match or ["*"]): - file_instance = pack[WeldPlugin][resource_location] + for resource_location in pack[WeldScript].match(*match or ["*"]): + file_instance = pack[WeldScript][resource_location] overlay_name = f"generated_{normalize_string(resource_location)}" - yield file_instance, CompilationUnit( + yield cast(TextFileBase[Any], file_instance), CompilationUnit( resource_location=resource_location, pack=pack.overlays[overlay_name], ) @@ -47,5 +47,5 @@ def clear_plugins(ctx: Context): ctx.assets, *ctx.assets.overlays.values(), ]: - for resource in [WeldPlugin, CustomResource]: + for resource in [WeldScript, CustomResource]: pack[resource].clear() diff --git a/src/smithed/weld/plugins/resources.py b/src/smithed/weld/plugins/resources.py index d39d030..747d1f5 100644 --- a/src/smithed/weld/plugins/resources.py +++ b/src/smithed/weld/plugins/resources.py @@ -3,12 +3,12 @@ from beet import Context, DataPack, Drop, TextFile -class WeldPlugin(TextFile): +class WeldScript(TextFile): scope: ClassVar[tuple[str, ...]] = ("weld", "plugins") extension: ClassVar[str] = ".bolt" -class WeldPyPlugin(TextFile): +class WeldPyScript(TextFile): scope: ClassVar[tuple[str, ...]] = ("weld", "plugins") extension: ClassVar[str] = ".py" @@ -18,9 +18,9 @@ def bind(self, pack: DataPack, path: str): super().bind(pack, path) if self.source_path: - pack[path] = WeldPlugin(source_path=self.source_path) + pack[path] = WeldScript(source_path=self.source_path) else: - pack[path] = WeldPlugin(self.text) + pack[path] = WeldScript(self.text) raise Drop() @@ -31,5 +31,5 @@ class CustomResource(TextFile): def load_resources(ctx: Context): - ctx.data.extend_namespace += [WeldPlugin, WeldPyPlugin, CustomResource] - ctx.assets.extend_namespace += [WeldPlugin, WeldPyPlugin, CustomResource] + ctx.data.extend_namespace += [WeldScript, WeldPyScript, CustomResource] + ctx.assets.extend_namespace += [WeldScript, WeldPyScript, CustomResource] diff --git a/src/smithed/weld/toolchain/__init__.py b/src/smithed/weld/toolchain/__init__.py index 9161f58..5a1a687 100644 --- a/src/smithed/weld/toolchain/__init__.py +++ b/src/smithed/weld/toolchain/__init__.py @@ -1,10 +1,6 @@ from .cli import cli -from .helper_plugins import ( - cache_pack_metadata, - inject_pack_stuff_into_smithed, - print_pack_name, -) -from .main import run_weld +from .helpers import run_weld +from .plugins import cache_pack_metadata __all__ = [ "cli", diff --git a/src/smithed/weld/toolchain/main.py b/src/smithed/weld/toolchain/main.py index c074dab..74f808f 100644 --- a/src/smithed/weld/toolchain/main.py +++ b/src/smithed/weld/toolchain/main.py @@ -9,10 +9,10 @@ from beet import Context, ProjectCache, ProjectConfig, run_beet, subproject from beet.core.utils import FileSystemPath, JsonDict -from smithed.weld import merging, plugins +from smithed.weld import merging, scripts from ..errors import WeldError -from .helper_plugins import add_fabric_mod_json +from .plugins import add_fabric_mod_json if sys.version_info >= (3, 11): from enum import StrEnum @@ -96,7 +96,7 @@ def run_weld( if as_fabric_mod: ctx.require(partial(add_fabric_mod_json, packs=packs)) ctx.require("mecha") - ctx.require(plugins.clear_plugins) + ctx.require(scripts.clear_plugins) yield ctx diff --git a/src/smithed/weld/toolchain/plugins.py b/src/smithed/weld/toolchain/plugins.py index 0717bad..c1bd76f 100644 --- a/src/smithed/weld/toolchain/plugins.py +++ b/src/smithed/weld/toolchain/plugins.py @@ -81,3 +81,19 @@ def weld_metadata(ctx: Context): def weld(ctx: Context): ctx.require(merging.process) + + +def cache_pack_metadata(ctx: Context): + ctx.meta.setdefault("loaded_packs", []) + + if ctx.data: + ctx.meta["loaded_packs"].append( + {"id": ctx.data.mcmeta.data.get("id", "unknown"), "pack.png": ctx.data.icon} + ) + elif ctx.assets: + ctx.meta["loaded_packs"].append( + { + "id": ctx.assets.mcmeta.data.get("id", "unknown"), + "pack.png": ctx.assets.icon, + } + ) diff --git a/tests/snapshots/examples__build_basic_scripts__0.pack.md b/tests/snapshots/examples__build_basic_scripts__0.pack.md new file mode 100644 index 0000000..6e45d91 --- /dev/null +++ b/tests/snapshots/examples__build_basic_scripts__0.pack.md @@ -0,0 +1,45 @@ +a# Lectern snapshot + +## Data pack + +`@data_pack pack.mcmeta` + +```json +{ + "pack": { + "pack_format": 48, + "description": "" + }, + "id": "tasty_tacos" +} +``` + +`@data_pack pack.png` + +![data_pack.png](data:image/png;base64,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) + +### custom + +[`@data_pack data/custom//weld/scripts/main.bolt`](data:application/octet-stream;base64,cHJpbnQoY3R4Lm1ldGEp) + +### fancy_bats + +`@function fancy_bats:load` + +```mcfunction +say "these bats are wildin'" +``` + +### tasty_tacos + +`@function tasty_tacos:load` + +```mcfunction +say "the tastiest of tacos" +``` + +`@function tasty_tacos:tacos` + +```mcfunction +give @a taco 64 +``` From c37ebc08551e761c705aaf3d4ea7035f66b6409c Mon Sep 17 00:00:00 2001 From: RitikShah Date: Fri, 21 Mar 2025 16:32:59 -0700 Subject: [PATCH 4/9] feat: weld scripts work! needs an off/on switch and other things --- .../custom/data/custom/weld/scripts/main.bolt | 4 +- smithed/weld/models/main.py | 82 ---------- smithed/weld/scripts/resources.py | 35 ----- smithed/weld/toolchain/process.py | 143 ------------------ src/smithed/weld/plugins/__init__.py | 9 -- src/smithed/weld/plugins/errors.py | 11 -- src/smithed/weld/plugins/registry.py | 51 ------- src/smithed/weld/plugins/resources.py | 35 ----- .../smithed}/weld/scripts/__init__.py | 0 .../smithed}/weld/scripts/errors.py | 0 .../smithed}/weld/scripts/registry.py | 16 +- src/smithed/weld/scripts/resources.py | 44 ++++++ src/smithed/weld/toolchain/__init__.py | 2 - src/smithed/weld/toolchain/main.py | 119 --------------- src/smithed/weld/toolchain/plugins.py | 3 +- src/smithed/weld/toolchain/process.py | 5 +- .../examples__build_basic_scripts__0.pack.md | 39 ++++- 17 files changed, 100 insertions(+), 498 deletions(-) delete mode 100644 smithed/weld/models/main.py delete mode 100644 smithed/weld/scripts/resources.py delete mode 100644 smithed/weld/toolchain/process.py delete mode 100644 src/smithed/weld/plugins/__init__.py delete mode 100644 src/smithed/weld/plugins/errors.py delete mode 100644 src/smithed/weld/plugins/registry.py delete mode 100644 src/smithed/weld/plugins/resources.py rename {smithed => src/smithed}/weld/scripts/__init__.py (100%) rename {smithed => src/smithed}/weld/scripts/errors.py (100%) rename {smithed => src/smithed}/weld/scripts/registry.py (73%) create mode 100644 src/smithed/weld/scripts/resources.py delete mode 100644 src/smithed/weld/toolchain/main.py diff --git a/examples/basic_scripts/custom/data/custom/weld/scripts/main.bolt b/examples/basic_scripts/custom/data/custom/weld/scripts/main.bolt index 1d39d13..68b0aa9 100644 --- a/examples/basic_scripts/custom/data/custom/weld/scripts/main.bolt +++ b/examples/basic_scripts/custom/data/custom/weld/scripts/main.bolt @@ -1 +1,3 @@ -print(ctx.meta) +function list:functions: + for func_name in ctx.data.functions: + say func_name diff --git a/smithed/weld/models/main.py b/smithed/weld/models/main.py deleted file mode 100644 index 33ed0bd..0000000 --- a/smithed/weld/models/main.py +++ /dev/null @@ -1,82 +0,0 @@ -import json -import logging -from typing import Any - -from beet import ListOption -from pydantic.v1 import Field, root_validator - -from ..merging.parser import get -from .base import BaseModel -from .priority import Priority -from .rules import AdditiveRule, Rule -from .sources import ReferenceSource, ValueSource - -logger = logging.getLogger(__name__) - - -def deserialize(model: BaseModel, defaults: bool = True): - return json.loads(model.json(by_alias=True, exclude_defaults=not defaults)) - - -class SmithedModel(BaseModel, extra="forbid"): - id: str = "" - version: int = 1 - override: bool | None = None # only should be set by bundle packs - priority: Priority | None = None - rules: list[Rule] = [] - - @root_validator - def push_down_priorities(cls, values: dict[str, Any]) -> dict[str, Any]: - """Push down top-level priority to every rule. - - If a rule has a priority defined, it will not be overwritten. - """ - - rules: list[Rule] = values.get("rules") # type: ignore - priority: Priority | None = values.get("priority") # type: ignore - - if priority is None: - priority = Priority() - - for rule in rules: - if rule.priority is None: - rule.priority = priority - - values.pop("priority") - - return values - - -class SmithedJsonFile(BaseModel, extra="allow"): - """Accepts any standard JSON file from in-game, only needs as `__smithed__` field - for custom merging logic""" - - smithed: ListOption[SmithedModel] = Field( - default_factory=ListOption, alias="__smithed__" - ) - - @root_validator - def convert_type(cls, values: dict[str, ListOption[SmithedModel]]): - if smithed := values.get("smithed"): - for model in smithed.entries(): - model.rules = list(cls.convert_rules(model.rules, values)) - - return values - - @classmethod - def convert_rules(cls, rules: list["Rule"], values: dict[str, Any]): - """Converts the source field of additive rules to a value source if it is - a reference source, essentially "baking" it in for ease of use later. - """ - - for rule in rules: - match isinstance(rule, AdditiveRule) and rule.source: - case ReferenceSource(path=path): - try: - rule.source = ValueSource(value=get(values, path)) - except ValueError: - logger.warning( - f"Source Reference Path: {path} was not found, deleting." - ) - continue - yield rule diff --git a/smithed/weld/scripts/resources.py b/smithed/weld/scripts/resources.py deleted file mode 100644 index 747d1f5..0000000 --- a/smithed/weld/scripts/resources.py +++ /dev/null @@ -1,35 +0,0 @@ -from typing import ClassVar - -from beet import Context, DataPack, Drop, TextFile - - -class WeldScript(TextFile): - scope: ClassVar[tuple[str, ...]] = ("weld", "plugins") - extension: ClassVar[str] = ".bolt" - - -class WeldPyScript(TextFile): - scope: ClassVar[tuple[str, ...]] = ("weld", "plugins") - extension: ClassVar[str] = ".py" - - def bind(self, pack: DataPack, path: str): - """Rebind file as a proper `Module` and drop current file.""" - - super().bind(pack, path) - - if self.source_path: - pack[path] = WeldScript(source_path=self.source_path) - else: - pack[path] = WeldScript(self.text) - - raise Drop() - - -class CustomResource(TextFile): - scope: ClassVar[tuple[str, ...]] = ("weld", "resources") - extension: ClassVar[str] = ".py" - - -def load_resources(ctx: Context): - ctx.data.extend_namespace += [WeldScript, WeldPyScript, CustomResource] - ctx.assets.extend_namespace += [WeldScript, WeldPyScript, CustomResource] diff --git a/smithed/weld/toolchain/process.py b/smithed/weld/toolchain/process.py deleted file mode 100644 index 3306a18..0000000 --- a/smithed/weld/toolchain/process.py +++ /dev/null @@ -1,143 +0,0 @@ -import logging -from dataclasses import dataclass, field -from pathlib import Path -from typing import NamedTuple -from zipfile import Path as ZipPath -from zipfile import ZipFile - -from beet import ( - Context, - DataPack, - DeserializationError, - JsonFileBase, - Mcmeta, - PackQuery, - ResourcePack, -) -from beet.contrib.auto_yaml import use_auto_yaml -from beet.contrib.model_merging import model_merging -from beet.contrib.unknown_files import UnknownAsset, UnknownData - -from ...type import JsonDict -from ..errors import InvalidMcmeta, InvalidPack - -logger = logging.getLogger("weld") - -_Pack = DataPack | ResourcePack - - -class PackWithName(NamedTuple): - pack: _Pack - name: str - - -@dataclass -class PackProcessor: - ctx: Context - file_id_cache: dict[JsonFileBase[JsonDict], _Pack] = field(default_factory=dict) - packs: list[PackWithName] = field(default_factory=list) - - def __getitem__(self, key: JsonFileBase[JsonDict]): - return self.file_id_cache[key] - - def __setitem__(self, key: JsonFileBase[JsonDict], value: _Pack): - self.file_id_cache[key] = value - - def get_pack_type(self, path: ZipPath | Path) -> DataPack | ResourcePack: - """TODO:""" - if (path / "data").is_dir(): - pack = DataPack() - pack.extend_namespace += [UnknownData] - - elif (path / "assets").is_dir(): - pack = ResourcePack() - pack.extend_namespace += [UnknownAsset] - model_merging(pack) - - else: - raise InvalidPack(str(path)) - - use_auto_yaml(pack) - - return pack - - def create_pack(self, file: str | ZipFile) -> DataPack | ResourcePack: - """Creates a DataPack or ResourcePack given a file (either ZipFile or str). - - 1. Determine type of file and it's name to figure out how to load it. - 2. Peek inside to figure out if it's a data or resource pack. - 3. Create an empty pack and load some default plugin behavior: - - `beet.contrib.unknown_files` - - `beet.contrib.model_merging` - - `beet.contrib.use_auto_yaml` - 4. Load the actual file into the empty pack object. - 5. Inject smithed specific information to some resource files within. - 6. Return the pack and it's name as a tuple. - - This process of loading the pack data in it's own object is isolated: - - We need to load the pack data isolated so if there's an error within, we can - surface it properly so it can be handled by the outer app. This allows us to - skip the pack or just highlight the broken pack in a weld process. - - File names, `pack.mcmeta`s, and the specific file tree gets lots when merged into - a larger pack. While resource files themselves get conflict handled via our - merger, the pack "metainfo" is useful enough for us to tabulate within itself. - - Specific errors from invalid mcmeta and json files is useful to surface before the - actual merging process. - """ - - def match_file(file: str | ZipFile) -> tuple[Path | ZipPath, str]: - match file: - case ZipFile() as f: - name = f.filename or "" - if not name.endswith(".zip"): - name = f"{name}.zip" - return ZipPath(f), name - - case str() as name: - if name.endswith(".zip"): - return match_file(ZipFile(name)) - return Path(name), name - - path, name = match_file(file) - pack = self.get_pack_type(path) - - try: - logger.info(f"Loading pack: {name}") - pack.load(file) - - except DeserializationError as err: - if isinstance(err.file, Mcmeta): - cause = err.__cause__ - reason = str(cause) - raise InvalidMcmeta(pack=name, cause=f"\n{reason}") from err # type: ignore - raise err - - self.cache_pack(pack, name) - - return pack - - def load_pack(self, pack: DataPack | ResourcePack): - """Loads a DataPack or ResourcePack into a context by merging it.""" - - match pack: - case DataPack() as dp: - self.ctx.data.merge(dp) - case ResourcePack() as rp: - self.ctx.assets.merge(rp) - - def cache_pack(self, pack: DataPack | ResourcePack, name: str): - """Cache some metadata from resource files to be used in later welding.""" - - if "id" not in pack.mcmeta.data: - pack.mcmeta.data["id"] = self.ctx.generate.format("missing_{incr}") - - for k in ( - PackQuery([pack]).distinct(match="*", extend=JsonFileBase[JsonDict]).keys() - ): - self[k] = pack - - self.packs.append(PackWithName(pack, name)) - - def load_packs(self, packs: list[str] | list[ZipFile]): - for pack in packs: - self.load_pack(self.create_pack(pack)) diff --git a/src/smithed/weld/plugins/__init__.py b/src/smithed/weld/plugins/__init__.py deleted file mode 100644 index a8bf23a..0000000 --- a/src/smithed/weld/plugins/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from .registry import WeldScript, beet_default, clear_plugins -from .resources import load_resources - -__all__ = [ - "WeldScript", - "load_resources", - "beet_default", - "clear_plugins", -] diff --git a/src/smithed/weld/plugins/errors.py b/src/smithed/weld/plugins/errors.py deleted file mode 100644 index f90e29b..0000000 --- a/src/smithed/weld/plugins/errors.py +++ /dev/null @@ -1,11 +0,0 @@ -class WeldScriptError(Exception): - namespace: str - __cause__: Exception - - def __init__(self, *args, namespace: str, cause: Exception): - super().__init__(*args) - self.namespace = namespace - self.__cause__ = cause # type: ignore - - def __str__(self) -> str: - return f"{self.namespace}: {self.args[0]}\n{self.__cause__}" diff --git a/src/smithed/weld/plugins/registry.py b/src/smithed/weld/plugins/registry.py deleted file mode 100644 index bbba67f..0000000 --- a/src/smithed/weld/plugins/registry.py +++ /dev/null @@ -1,51 +0,0 @@ -from typing import Any, Iterable, cast - -from beet import Context, DataPack, ResourcePack, TextFileBase -from beet.core.utils import FormatsRangeDict, normalize_string -from bolt.contrib.sandbox import Sandbox -from mecha import CompilationUnit, Mecha - -from .resources import CustomResource, WeldScript, load_resources - - -def provide_compilation_units( - pack: ResourcePack | DataPack, - match: list[str] | None = None, -) -> Iterable[tuple[TextFileBase[Any], CompilationUnit]]: - for resource_location in pack[WeldScript].match(*match or ["*"]): - file_instance = pack[WeldScript][resource_location] - - overlay_name = f"generated_{normalize_string(resource_location)}" - - yield cast(TextFileBase[Any], file_instance), CompilationUnit( - resource_location=resource_location, - pack=pack.overlays[overlay_name], - ) - pack.overlays[overlay_name].supported_formats = FormatsRangeDict( - min_inclusive=pack.pack_format, - max_inclusive=pack.pack_format, - ) - - -def define_compilation_unit_providers(ctx: Context): - mc = ctx.inject(Mecha) - mc.providers = [provide_compilation_units] - - -def beet_default(ctx: Context): - sandbox = ctx.inject(Sandbox) - sandbox.allowed_imports |= {"beet", "smithed"} - - ctx.require(define_compilation_unit_providers) - ctx.require(load_resources) - - -def clear_plugins(ctx: Context): - for pack in [ - ctx.data, - *ctx.data.overlays.values(), - ctx.assets, - *ctx.assets.overlays.values(), - ]: - for resource in [WeldScript, CustomResource]: - pack[resource].clear() diff --git a/src/smithed/weld/plugins/resources.py b/src/smithed/weld/plugins/resources.py deleted file mode 100644 index 747d1f5..0000000 --- a/src/smithed/weld/plugins/resources.py +++ /dev/null @@ -1,35 +0,0 @@ -from typing import ClassVar - -from beet import Context, DataPack, Drop, TextFile - - -class WeldScript(TextFile): - scope: ClassVar[tuple[str, ...]] = ("weld", "plugins") - extension: ClassVar[str] = ".bolt" - - -class WeldPyScript(TextFile): - scope: ClassVar[tuple[str, ...]] = ("weld", "plugins") - extension: ClassVar[str] = ".py" - - def bind(self, pack: DataPack, path: str): - """Rebind file as a proper `Module` and drop current file.""" - - super().bind(pack, path) - - if self.source_path: - pack[path] = WeldScript(source_path=self.source_path) - else: - pack[path] = WeldScript(self.text) - - raise Drop() - - -class CustomResource(TextFile): - scope: ClassVar[tuple[str, ...]] = ("weld", "resources") - extension: ClassVar[str] = ".py" - - -def load_resources(ctx: Context): - ctx.data.extend_namespace += [WeldScript, WeldPyScript, CustomResource] - ctx.assets.extend_namespace += [WeldScript, WeldPyScript, CustomResource] diff --git a/smithed/weld/scripts/__init__.py b/src/smithed/weld/scripts/__init__.py similarity index 100% rename from smithed/weld/scripts/__init__.py rename to src/smithed/weld/scripts/__init__.py diff --git a/smithed/weld/scripts/errors.py b/src/smithed/weld/scripts/errors.py similarity index 100% rename from smithed/weld/scripts/errors.py rename to src/smithed/weld/scripts/errors.py diff --git a/smithed/weld/scripts/registry.py b/src/smithed/weld/scripts/registry.py similarity index 73% rename from smithed/weld/scripts/registry.py rename to src/smithed/weld/scripts/registry.py index bbba67f..90d7b3e 100644 --- a/smithed/weld/scripts/registry.py +++ b/src/smithed/weld/scripts/registry.py @@ -1,11 +1,15 @@ from typing import Any, Iterable, cast +from logging import Logger from beet import Context, DataPack, ResourcePack, TextFileBase from beet.core.utils import FormatsRangeDict, normalize_string from bolt.contrib.sandbox import Sandbox from mecha import CompilationUnit, Mecha +import mecha -from .resources import CustomResource, WeldScript, load_resources +from .resources import ResourceDefinition, WeldScript, load_resources + +logger = Logger(__name__) def provide_compilation_units( @@ -14,8 +18,8 @@ def provide_compilation_units( ) -> Iterable[tuple[TextFileBase[Any], CompilationUnit]]: for resource_location in pack[WeldScript].match(*match or ["*"]): file_instance = pack[WeldScript][resource_location] - - overlay_name = f"generated_{normalize_string(resource_location)}" + namespace, *_ = resource_location.split(":") + overlay_name = f"weld_generated_{normalize_string(namespace)}" yield cast(TextFileBase[Any], file_instance), CompilationUnit( resource_location=resource_location, @@ -28,6 +32,7 @@ def provide_compilation_units( def define_compilation_unit_providers(ctx: Context): + """Replace all default providers with only weld script providers""" mc = ctx.inject(Mecha) mc.providers = [provide_compilation_units] @@ -39,6 +44,9 @@ def beet_default(ctx: Context): ctx.require(define_compilation_unit_providers) ctx.require(load_resources) + # This mecha will *not* process every file bc we replaced all the providers + ctx.require(mecha.beet_default) + def clear_plugins(ctx: Context): for pack in [ @@ -47,5 +55,5 @@ def clear_plugins(ctx: Context): ctx.assets, *ctx.assets.overlays.values(), ]: - for resource in [WeldScript, CustomResource]: + for resource in [WeldScript, ResourceDefinition]: pack[resource].clear() diff --git a/src/smithed/weld/scripts/resources.py b/src/smithed/weld/scripts/resources.py new file mode 100644 index 0000000..2db6648 --- /dev/null +++ b/src/smithed/weld/scripts/resources.py @@ -0,0 +1,44 @@ +from typing import ClassVar + +from beet import Context, DataPack, Drop, NamespaceFileScope, TextFile + + +class WeldScript(TextFile): + scope: ClassVar[NamespaceFileScope] = { + 0: ("weld", "scripts"), + 45: ("weld", "script"), + } + extension: ClassVar[str] = ".bolt" + + +class WeldPyScript(TextFile): + scope: ClassVar[NamespaceFileScope] = { + 0: ("weld", "scripts"), + 45: ("weld", "script"), + } + extension: ClassVar[str] = ".py" + + def bind(self, pack: DataPack, path: str): + """Rebind file as a proper `Module` and drop current file.""" + + super().bind(pack, path) + + if self.source_path: + pack[path] = WeldScript(source_path=self.source_path) + else: + pack[path] = WeldScript(self.text) + + raise Drop() + + +class ResourceDefinition(TextFile): + scope: ClassVar[NamespaceFileScope] = { + 0: ("weld", "resource_definitions"), + 45: ("weld", "resource_definition"), + } + extension: ClassVar[str] = ".py" + + +def load_resources(ctx: Context): + ctx.data.extend_namespace += [WeldScript, WeldPyScript, ResourceDefinition] + ctx.assets.extend_namespace += [WeldScript, WeldPyScript, ResourceDefinition] diff --git a/src/smithed/weld/toolchain/__init__.py b/src/smithed/weld/toolchain/__init__.py index 5a1a687..cadb77c 100644 --- a/src/smithed/weld/toolchain/__init__.py +++ b/src/smithed/weld/toolchain/__init__.py @@ -5,7 +5,5 @@ __all__ = [ "cli", "run_weld", - "print_pack_name", - "inject_pack_stuff_into_smithed", "cache_pack_metadata", ] diff --git a/src/smithed/weld/toolchain/main.py b/src/smithed/weld/toolchain/main.py deleted file mode 100644 index 74f808f..0000000 --- a/src/smithed/weld/toolchain/main.py +++ /dev/null @@ -1,119 +0,0 @@ -import sys -from contextlib import contextmanager -from functools import partial -from pathlib import Path -from typing import Iterable, Literal, cast -from zipfile import Path as ZipPath -from zipfile import ZipFile - -from beet import Context, ProjectCache, ProjectConfig, run_beet, subproject -from beet.core.utils import FileSystemPath, JsonDict - -from smithed.weld import merging, scripts - -from ..errors import WeldError -from .plugins import add_fabric_mod_json - -if sys.version_info >= (3, 11): - from enum import StrEnum -else: - from backports.strenum import StrEnum - -DESCRIPTION = "Merged by Smithed Weld" - - -class PackType(StrEnum): - DATA = "data_pack" - ASSETS = "resource_pack" - - -def subproject_config(pack_type: PackType, name: str = ""): - return subproject( - { - "require": [ - "smithed.weld.plugins.load_resources", - "beet.contrib.auto_yaml", - "beet.contrib.model_merging", - "beet.contrib.unknown_files", - ], - pack_type: {"load": name}, - "pipeline": [ - "smithed.weld.print_pack_name", - "smithed.weld.inject_pack_stuff_into_smithed", - "smithed.weld.cache_pack_metadata", - ], - } - ) - - -def inspect_zipfile(file: ZipFile) -> PackType: - path = ZipPath(file) - - if (path / "data").is_dir(): - return PackType.DATA - elif (path / "assets").is_dir(): - return PackType.ASSETS - - raise WeldError("Invalid. Pack has neither assets nor data.") - - -def inspect(file: str | ZipFile) -> PackType | Literal[False]: - match file: - case str(path) if path.endswith(".zip"): - with ZipFile(path) as zip: - return inspect_zipfile(zip) - - case str(path): - if (path := Path(path)).is_dir(): - if (path / "data").is_dir(): - return PackType.DATA - elif (path / "assets").is_dir(): - return PackType.ASSETS - - raise WeldError(f"Invalid. Pack {path} has neither assets nor data. \n") - - case ZipFile() as zip: - return inspect_zipfile(zip) - - return False - - -@contextmanager -def run_weld( - packs: Iterable[str] | Iterable[ZipFile], - config: FileSystemPath | ProjectConfig | JsonDict = {}, - directory: FileSystemPath | None = None, - cache: bool | ProjectCache = True, - as_fabric_mod: bool = False, -): - packs = cast(list[str] | list[ZipFile], list(packs)) - packs_with_types = zip(packs, (pack for pack in map(inspect, packs) if pack)) - - with run_beet(config, directory=directory, cache=cache) as ctx: - ctx.require(weld) - ctx.require(partial(load_packs, packs=list(packs_with_types))) - ctx.require(merging.process) - if as_fabric_mod: - ctx.require(partial(add_fabric_mod_json, packs=packs)) - ctx.require("mecha") - ctx.require(scripts.clear_plugins) - yield ctx - - -def weld(ctx: Context): - ctx.require("smithed.weld.merging") - ctx.require("smithed.weld.plugins") - ctx.require("beet.contrib.model_merging") - - -def load_packs(ctx: Context, packs: Iterable[tuple[str | ZipFile, PackType]]): - for pack, pack_type in packs: - match pack: - case str(name): - ctx.require(subproject_config(pack_type, name)) - case ZipFile() as file: - match pack_type: - case PackType.DATA: - ctx.data.load(file) - case PackType.ASSETS: - ctx.assets.load(file) diff --git a/src/smithed/weld/toolchain/plugins.py b/src/smithed/weld/toolchain/plugins.py index c1bd76f..60bd3fd 100644 --- a/src/smithed/weld/toolchain/plugins.py +++ b/src/smithed/weld/toolchain/plugins.py @@ -13,7 +13,7 @@ ) from jinja2 import Template -from smithed.weld import merging +from smithed.weld import merging, scripts from .process import PackProcessor @@ -81,6 +81,7 @@ def weld_metadata(ctx: Context): def weld(ctx: Context): ctx.require(merging.process) + ctx.require(scripts.beet_default) def cache_pack_metadata(ctx: Context): diff --git a/src/smithed/weld/toolchain/process.py b/src/smithed/weld/toolchain/process.py index dc0c537..372d654 100644 --- a/src/smithed/weld/toolchain/process.py +++ b/src/smithed/weld/toolchain/process.py @@ -43,6 +43,7 @@ successor, ) from ..errors import InvalidMcmeta, InvalidPack +from ..scripts.resources import WeldPyScript, WeldScript from ...type import JsonDict logger = logging.getLogger("weld") @@ -153,11 +154,11 @@ def _create_empty_pack[T: (DataPack, ResourcePack)](pack_type: type[T]) -> T: """ if pack_type is ResourcePack: - pack = pack_type(extend_namespace=[UnknownAsset]) + pack = pack_type(extend_namespace=[UnknownAsset, WeldScript, WeldPyScript]) use_auto_yaml(pack) model_merging(pack) elif pack_type is DataPack: - pack = pack_type(extend_namespace=[UnknownData]) + pack = pack_type(extend_namespace=[UnknownData, WeldScript, WeldPyScript]) use_auto_yaml(pack) else: raise ValueError("`pack_type` must be `DataPack` or `ResourcePack`") diff --git a/tests/snapshots/examples__build_basic_scripts__0.pack.md b/tests/snapshots/examples__build_basic_scripts__0.pack.md index 6e45d91..ab1e6fb 100644 --- a/tests/snapshots/examples__build_basic_scripts__0.pack.md +++ b/tests/snapshots/examples__build_basic_scripts__0.pack.md @@ -1,4 +1,4 @@ -a# Lectern snapshot +# Lectern snapshot ## Data pack @@ -10,7 +10,18 @@ a# Lectern snapshot "pack_format": 48, "description": "" }, - "id": "tasty_tacos" + "id": "tasty_tacos", + "overlays": { + "entries": [ + { + "formats": { + "min_inclusive": 48, + "max_inclusive": 48 + }, + "directory": "weld_generated_custom" + } + ] + } } ``` @@ -20,7 +31,13 @@ a# Lectern snapshot ### custom -[`@data_pack data/custom//weld/scripts/main.bolt`](data:application/octet-stream;base64,cHJpbnQoY3R4Lm1ldGEp) +`@weld_script custom:main` + +``` +function list:functions: + for func_name in ctx.data.functions: + say func_name +``` ### fancy_bats @@ -43,3 +60,19 @@ say "the tastiest of tacos" ```mcfunction give @a taco 64 ``` + +## Overlay `weld_generated_custom` + +`@overlay weld_generated_custom` + +### list + +`@function list:functions` + +```mcfunction +say fancy_bats:load +say tasty_tacos:load +say tasty_tacos:tacos +``` + +`@endoverlay` From 0211929b59de725021c87a93d151c75e28c42848 Mon Sep 17 00:00:00 2001 From: RitikShah Date: Fri, 21 Mar 2025 19:13:09 -0700 Subject: [PATCH 5/9] feat: makes welding more configurable --- src/smithed/weld/__init__.py | 20 +-------- src/smithed/weld/toolchain/__init__.py | 8 +--- src/smithed/weld/toolchain/helpers.py | 5 ++- src/smithed/weld/toolchain/plugins.py | 60 +++++++++++++++++--------- src/smithed/weld/toolchain/process.py | 4 ++ 5 files changed, 51 insertions(+), 46 deletions(-) diff --git a/src/smithed/weld/__init__.py b/src/smithed/weld/__init__.py index 2f1a3c0..f0870df 100644 --- a/src/smithed/weld/__init__.py +++ b/src/smithed/weld/__init__.py @@ -1,21 +1,5 @@ -from .toolchain import ( - cache_pack_metadata, - cli, - # inject_pack_stuff_into_smithed, - # print_pack_name, - run_weld, - # weld, - # weld_handler, -) +from .toolchain import cli, run_weld, weld_loader, weld_metadata, weld_handler, weld __version__ = "0.20.0" -__all__ = [ - # "print_pack_name", - "cache_pack_metadata", - # "inject_pack_stuff_into_smithed", - "cli", - "run_weld", - # "weld", - # "weld_handler", -] +__all__ = ["cli", "run_weld", "weld_loader", "weld_metadata", "weld_handler", "weld"] diff --git a/src/smithed/weld/toolchain/__init__.py b/src/smithed/weld/toolchain/__init__.py index cadb77c..656df17 100644 --- a/src/smithed/weld/toolchain/__init__.py +++ b/src/smithed/weld/toolchain/__init__.py @@ -1,9 +1,5 @@ from .cli import cli from .helpers import run_weld -from .plugins import cache_pack_metadata +from .plugins import weld_loader, weld_metadata, weld_handler, weld -__all__ = [ - "cli", - "run_weld", - "cache_pack_metadata", -] +__all__ = ["cli", "run_weld", "weld_loader", "weld_metadata", "weld_handler", "weld"] diff --git a/src/smithed/weld/toolchain/helpers.py b/src/smithed/weld/toolchain/helpers.py index 154bc85..d7fffc1 100644 --- a/src/smithed/weld/toolchain/helpers.py +++ b/src/smithed/weld/toolchain/helpers.py @@ -28,7 +28,10 @@ def run_weld( cache: bool | ProjectCache = True, as_fabric_mod: bool = False, ): - """Runs the beet toolchain alongside the weld machinery programmatically.""" + """Runs the beet toolchain alongside the weld machinery programmatically. + + Matches the beet helper, `run_beet`. + """ with run_beet(config, directory=directory, cache=cache) as ctx: ctx.require(weld_handler) diff --git a/src/smithed/weld/toolchain/plugins.py b/src/smithed/weld/toolchain/plugins.py index 60bd3fd..c9e811c 100644 --- a/src/smithed/weld/toolchain/plugins.py +++ b/src/smithed/weld/toolchain/plugins.py @@ -31,14 +31,34 @@ ).read_bytes() -class WeldOptions(PluginOptions, arbitrary_types_allowed=True): +class WeldLoaderOptions(PluginOptions, arbitrary_types_allowed=True): + """Options to configure how weld loads packs before merge time. + + packs - which packs (paths or ZipFile) to load + """ + + # TODO: change `str` to `PathLike`? packs: list[str] | list[ZipFile] +class WeldOptions(PluginOptions, arbitrary_types_allowed=True): + """Options to configure how weld operates at merge time. + + scripts - determines whether weld scripts run after merging. + """ + + scripts: bool = True + + def add_fabric_mod_json(ctx: Context, pack_names: list[str] | None = None): + """Generates and adds a fabric mod json for producing value fabric jars. + + Note: Fabric jar is compromised of a renamed `.zip` -> `.jar` and a `fabric.mod.json`. + """ + if pack_names is None: processor = ctx.inject(PackProcessor) - pack_names = list(set(pack.name for pack in processor.packs)) + pack_names = list(dict.fromkeys(pack.name for pack in processor.packs)) content = FABRIC_MOD_TEMPLATE.render( pack_hash=hash("".join(pack_names)), @@ -49,17 +69,26 @@ def add_fabric_mod_json(ctx: Context, pack_names: list[str] | None = None): ctx.data.extra["fabric.mod.json"] = JsonFile(content) -@configurable(validator=WeldOptions) -def weld_loader(ctx: Context, opts: WeldOptions): +@configurable(validator=WeldLoaderOptions) +def weld_loader(ctx: Context, opts: WeldLoaderOptions): + """Loads packs into weld via the `PackProcessor` class.""" + processor = ctx.inject(PackProcessor) processor.load_packs(opts.packs) def weld_handler(ctx: Context): + """Loads the merging pre-processing pipeline.""" + ctx.require(merging.beet_default) def weld_metadata(ctx: Context): + """Attaches custom metadata to welded packs. + + TODO: make configurable + """ + processor = ctx.inject(PackProcessor) if ctx.data: @@ -79,22 +108,11 @@ def weld_metadata(ctx: Context): ] -def weld(ctx: Context): - ctx.require(merging.process) - ctx.require(scripts.beet_default) - +@configurable(validator=WeldOptions) +def weld(ctx: Context, opt: WeldOptions): + """Beet plugin that runs the main weld processes (merging + scripting)""" -def cache_pack_metadata(ctx: Context): - ctx.meta.setdefault("loaded_packs", []) + ctx.require(merging.process) - if ctx.data: - ctx.meta["loaded_packs"].append( - {"id": ctx.data.mcmeta.data.get("id", "unknown"), "pack.png": ctx.data.icon} - ) - elif ctx.assets: - ctx.meta["loaded_packs"].append( - { - "id": ctx.assets.mcmeta.data.get("id", "unknown"), - "pack.png": ctx.assets.icon, - } - ) + if opt.scripts: + ctx.require(scripts.beet_default) diff --git a/src/smithed/weld/toolchain/process.py b/src/smithed/weld/toolchain/process.py index 372d654..081174b 100644 --- a/src/smithed/weld/toolchain/process.py +++ b/src/smithed/weld/toolchain/process.py @@ -208,6 +208,9 @@ class PackProcessor: file_id_cache: dict[JsonFileBase[JsonDict], Pack] = field(default_factory=dict) packs: list[PackWithName[Pack]] = field(default_factory=list) + def __post_init__(self): + self.ctx.meta["packs"] = {} + def __getitem__(self, key: JsonFileBase[JsonDict]): return self.file_id_cache[key] @@ -347,6 +350,7 @@ def cache_pack[T: (DataPack, ResourcePack)]( if register: self.packs.append(pack) + self.ctx.meta["packs"][pack.name] = pack.pack def load_packs(self, files: list[str] | list[ZipFile]): """Load a series of packs into `ctx`. Triggers merge policies. From 4e6e046f53ac582a765a743adac121d625702bcd Mon Sep 17 00:00:00 2001 From: RitikShah Date: Fri, 21 Mar 2025 20:13:38 -0700 Subject: [PATCH 6/9] docs: bump year --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 563130e..0dd8d3b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Smithed +Copyright (c) 2025 Smithed Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From fc0fb83a1453590d58ec176064d8f8982337a970 Mon Sep 17 00:00:00 2001 From: RitikShah Date: Sun, 30 Mar 2025 21:42:49 -0700 Subject: [PATCH 7/9] Adds error handling, and proper `ctx` management via drafts Co-authored-by: TheNuclearNexus <39636175+TheNuclearNexus@users.noreply.github.com> --- .../custom/data/custom/weld/scripts/main.bolt | 2 +- pyproject.toml | 1 + src/smithed/weld/__init__.py | 13 +- src/smithed/weld/scripts/__init__.py | 12 +- src/smithed/weld/scripts/errors.py | 3 +- src/smithed/weld/scripts/plugins.py | 136 ++++++++++++++++++ src/smithed/weld/scripts/registry.py | 59 -------- src/smithed/weld/scripts/resources.py | 6 +- src/smithed/weld/toolchain/cli.py | 4 +- src/smithed/weld/toolchain/plugins.py | 3 +- src/smithed/weld/toolchain/process.py | 7 +- .../examples__build_basic_scripts__0.pack.md | 53 +++++-- tests/test_examples.py | 17 ++- uv.lock | 43 ++++++ 14 files changed, 269 insertions(+), 90 deletions(-) create mode 100644 src/smithed/weld/scripts/plugins.py delete mode 100644 src/smithed/weld/scripts/registry.py diff --git a/examples/basic_scripts/custom/data/custom/weld/scripts/main.bolt b/examples/basic_scripts/custom/data/custom/weld/scripts/main.bolt index 68b0aa9..09babd9 100644 --- a/examples/basic_scripts/custom/data/custom/weld/scripts/main.bolt +++ b/examples/basic_scripts/custom/data/custom/weld/scripts/main.bolt @@ -1,3 +1,3 @@ function list:functions: - for func_name in ctx.data.functions: + for func_name in sorted(ctx.data.functions): say func_name diff --git a/pyproject.toml b/pyproject.toml index 1b09516..9c255b3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,7 @@ dependencies = [ "pydantic>=2.5.2", "lectern>=0.34.0", "bolt>=0.38.5", + "bolt-expressions>=0.17.0", ] description = "Smithed's Python client with CLI, Weld and more" license = "MIT" diff --git a/src/smithed/weld/__init__.py b/src/smithed/weld/__init__.py index f0870df..829a4c2 100644 --- a/src/smithed/weld/__init__.py +++ b/src/smithed/weld/__init__.py @@ -1,5 +1,16 @@ from .toolchain import cli, run_weld, weld_loader, weld_metadata, weld_handler, weld +from .scripts import WeldScript, WeldPyScript, ResourceDefinition __version__ = "0.20.0" -__all__ = ["cli", "run_weld", "weld_loader", "weld_metadata", "weld_handler", "weld"] +__all__ = [ + "cli", + "ResourceDefinition", + "run_weld", + "weld_handler", + "weld_loader", + "weld_metadata", + "weld", + "WeldPyScript", + "WeldScript", +] diff --git a/src/smithed/weld/scripts/__init__.py b/src/smithed/weld/scripts/__init__.py index a8bf23a..d3cc505 100644 --- a/src/smithed/weld/scripts/__init__.py +++ b/src/smithed/weld/scripts/__init__.py @@ -1,9 +1,11 @@ -from .registry import WeldScript, beet_default, clear_plugins -from .resources import load_resources +from .plugins import process_scripting, clear_resources, load_resources +from .resources import WeldScript, WeldPyScript, ResourceDefinition __all__ = [ - "WeldScript", + "process_scripting", + "clear_resources", "load_resources", - "beet_default", - "clear_plugins", + "ResourceDefinition", + "WeldPyScript", + "WeldScript", ] diff --git a/src/smithed/weld/scripts/errors.py b/src/smithed/weld/scripts/errors.py index f90e29b..cf892bc 100644 --- a/src/smithed/weld/scripts/errors.py +++ b/src/smithed/weld/scripts/errors.py @@ -1,11 +1,10 @@ class WeldScriptError(Exception): namespace: str - __cause__: Exception def __init__(self, *args, namespace: str, cause: Exception): super().__init__(*args) self.namespace = namespace - self.__cause__ = cause # type: ignore + self.__cause__ = cause def __str__(self) -> str: return f"{self.namespace}: {self.args[0]}\n{self.__cause__}" diff --git a/src/smithed/weld/scripts/plugins.py b/src/smithed/weld/scripts/plugins.py new file mode 100644 index 0000000..9c7ba14 --- /dev/null +++ b/src/smithed/weld/scripts/plugins.py @@ -0,0 +1,136 @@ +import logging +import traceback as tb + +from beet import Context, DataPack, Draft, Pack, ResourcePack +import beet +import bolt_expressions +from bolt import Runtime +from bolt.contrib.sandbox import Sandbox, public_attrs +from mecha import CompilationError, Mecha + +from .resources import RESOURCES, ResourceDefinition, WeldScript + +logger = logging.getLogger("weld") + +ALLOWED_TYPES = (DataPack, ResourcePack) +WELD_SCRIPTS_OVERLAY_NAME = "weld_generated" + + +def process_scripting(ctx: Context): + """Load scripting stuff""" + + if not any( + pack[WeldScript] + for root in ctx.packs + for pack in (root, *root.overlays.values()) + ): + return + + mc = ctx.inject(Mecha) + runtime = ctx.inject(Runtime) + + # TODO: move to new file, and write up comments for everything allowed. make more specific + sandbox = ctx.inject(Sandbox) + sandbox.allow_basics() + sandbox.allow_pipeline_context() + + sandbox.allowed_type_attrs[Draft] |= {field for field in public_attrs(Draft)} + + sandbox.allowed_obj_attrs[beet] |= {field for field in public_attrs(beet)} + sandbox.allowed_obj_attrs[bolt_expressions] |= { + field for field in public_attrs(bolt_expressions) + } + + for _type in ALLOWED_TYPES: + sandbox.allowed_type_attrs[_type] |= {field for field in public_attrs(_type)} + + sandbox.allowed_imports |= {"beet", "bolt_expressions"} + sandbox.allowed_builtins |= {"breakpoint"} + sandbox.activate() + + # load all ctx content into a draft so that it doesn't merge + with ctx.generate.draft() as draft: + # we shallow copy everything into a draft so we can shadow ctx + for draft_pack, ctx_pack in zip((draft.assets, draft.data), ctx.packs): + for resource, file in ctx_pack.all(): + draft_pack[resource] = file.copy() + + # pretend draft is ctx so that people can't change ctx stuff + # the draft has shallow copies of all content + runtime.globals["ctx"] = draft + for pack in ctx.packs: + pack.overlays.setdefault( + WELD_SCRIPTS_OVERLAY_NAME, + supported_formats=pack.supported_formats or pack.pack_format, + min_format=pack.min_format, + max_format=pack.max_format, + ) + overlay = ctx.generate.overlays[WELD_SCRIPTS_OVERLAY_NAME] + + # processing packs for compliation + for pack in [ + ctx.data, + *ctx.data.overlays.values(), + ctx.assets, + *ctx.assets.overlays.values(), + ]: + for resource_location, script in list(pack[WeldScript].items()): + logger.info('Executing Weld Script: "%s"', resource_location) + + # compile with recovery to ensure we can compile other modules + try: + with overlay.push(): + mc.compile(script) + + except CompilationError as err: + # TODO: better error reporting for weld script writers + logger.error(err.__cause__) + + except Exception as err: + # if something errors (like mecha), log error AND traceback + logger.error(err) + logger.error("\n".join(tb.format_tb(err.__traceback__))) + + # delete files that have not changed in our draft (if `._content` exists, it's changed) + for pack in [draft.assets, draft.data]: + for resource, file in list(pack.all()): + if hasattr(file, "_content") and not file._content: + del pack[type(file)][resource] + + # merge all draft content into our global overlay. the draft needs to + # be cleared so that it doesn't merge into the actual pack. + for overlay_pack, draft_pack in zip( + (overlay.assets, overlay.data), (draft.assets, draft.data) + ): + overlay_pack.merge(draft_pack) # type: ignore + draft_pack.clear() + draft_pack.extra.clear() + + for pack in ctx.packs: + if not pack.overlays[WELD_SCRIPTS_OVERLAY_NAME]: + del pack.overlays[WELD_SCRIPTS_OVERLAY_NAME] + + +def clear_resources(ctx: Context): + """Clear weld script resources""" + + for pack in [ + ctx.data, + *ctx.data.overlays.values(), + ctx.assets, + *ctx.assets.overlays.values(), + ]: + for resource in [WeldScript, ResourceDefinition]: + pack[resource].clear() + + +def load_resources(ctx: Context | Pack): + """Load all of the custom resource types for weld scripts""" + + match ctx: + case Context(): + for pack in ctx.packs: + pack.extend_namespace += RESOURCES + + case DataPack() | ResourcePack() as pack: + pack.extend_namespace += RESOURCES diff --git a/src/smithed/weld/scripts/registry.py b/src/smithed/weld/scripts/registry.py deleted file mode 100644 index 90d7b3e..0000000 --- a/src/smithed/weld/scripts/registry.py +++ /dev/null @@ -1,59 +0,0 @@ -from typing import Any, Iterable, cast -from logging import Logger - -from beet import Context, DataPack, ResourcePack, TextFileBase -from beet.core.utils import FormatsRangeDict, normalize_string -from bolt.contrib.sandbox import Sandbox -from mecha import CompilationUnit, Mecha -import mecha - -from .resources import ResourceDefinition, WeldScript, load_resources - -logger = Logger(__name__) - - -def provide_compilation_units( - pack: ResourcePack | DataPack, - match: list[str] | None = None, -) -> Iterable[tuple[TextFileBase[Any], CompilationUnit]]: - for resource_location in pack[WeldScript].match(*match or ["*"]): - file_instance = pack[WeldScript][resource_location] - namespace, *_ = resource_location.split(":") - overlay_name = f"weld_generated_{normalize_string(namespace)}" - - yield cast(TextFileBase[Any], file_instance), CompilationUnit( - resource_location=resource_location, - pack=pack.overlays[overlay_name], - ) - pack.overlays[overlay_name].supported_formats = FormatsRangeDict( - min_inclusive=pack.pack_format, - max_inclusive=pack.pack_format, - ) - - -def define_compilation_unit_providers(ctx: Context): - """Replace all default providers with only weld script providers""" - mc = ctx.inject(Mecha) - mc.providers = [provide_compilation_units] - - -def beet_default(ctx: Context): - sandbox = ctx.inject(Sandbox) - sandbox.allowed_imports |= {"beet", "smithed"} - - ctx.require(define_compilation_unit_providers) - ctx.require(load_resources) - - # This mecha will *not* process every file bc we replaced all the providers - ctx.require(mecha.beet_default) - - -def clear_plugins(ctx: Context): - for pack in [ - ctx.data, - *ctx.data.overlays.values(), - ctx.assets, - *ctx.assets.overlays.values(), - ]: - for resource in [WeldScript, ResourceDefinition]: - pack[resource].clear() diff --git a/src/smithed/weld/scripts/resources.py b/src/smithed/weld/scripts/resources.py index 2db6648..046d6d7 100644 --- a/src/smithed/weld/scripts/resources.py +++ b/src/smithed/weld/scripts/resources.py @@ -1,6 +1,6 @@ from typing import ClassVar -from beet import Context, DataPack, Drop, NamespaceFileScope, TextFile +from beet import DataPack, Drop, NamespaceFileScope, TextFile class WeldScript(TextFile): @@ -39,6 +39,4 @@ class ResourceDefinition(TextFile): extension: ClassVar[str] = ".py" -def load_resources(ctx: Context): - ctx.data.extend_namespace += [WeldScript, WeldPyScript, ResourceDefinition] - ctx.assets.extend_namespace += [WeldScript, WeldPyScript, ResourceDefinition] +RESOURCES = [WeldScript, WeldPyScript, ResourceDefinition] diff --git a/src/smithed/weld/toolchain/cli.py b/src/smithed/weld/toolchain/cli.py index aee91f6..22006b3 100644 --- a/src/smithed/weld/toolchain/cli.py +++ b/src/smithed/weld/toolchain/cli.py @@ -80,8 +80,8 @@ def weld( success = False - logger.debug("Using cache: '%s'", path_str(cache)) - logger.info("Packs: %s", ", ".join(f"'{pack}'" for pack in packs)) + logger.debug('Using cache: "%s"', path_str(cache)) + logger.info("Packs: %s", ", ".join(f'"{pack}"' for pack in packs)) t0 = perf_counter() with ( diff --git a/src/smithed/weld/toolchain/plugins.py b/src/smithed/weld/toolchain/plugins.py index c9e811c..0361e1e 100644 --- a/src/smithed/weld/toolchain/plugins.py +++ b/src/smithed/weld/toolchain/plugins.py @@ -81,6 +81,7 @@ def weld_handler(ctx: Context): """Loads the merging pre-processing pipeline.""" ctx.require(merging.beet_default) + ctx.require(scripts.load_resources) def weld_metadata(ctx: Context): @@ -115,4 +116,4 @@ def weld(ctx: Context, opt: WeldOptions): ctx.require(merging.process) if opt.scripts: - ctx.require(scripts.beet_default) + ctx.require(scripts.process_scripting) diff --git a/src/smithed/weld/toolchain/process.py b/src/smithed/weld/toolchain/process.py index 081174b..2399496 100644 --- a/src/smithed/weld/toolchain/process.py +++ b/src/smithed/weld/toolchain/process.py @@ -43,7 +43,7 @@ successor, ) from ..errors import InvalidMcmeta, InvalidPack -from ..scripts.resources import WeldPyScript, WeldScript +from smithed.weld import scripts from ...type import JsonDict logger = logging.getLogger("weld") @@ -154,15 +154,16 @@ def _create_empty_pack[T: (DataPack, ResourcePack)](pack_type: type[T]) -> T: """ if pack_type is ResourcePack: - pack = pack_type(extend_namespace=[UnknownAsset, WeldScript, WeldPyScript]) + pack = pack_type(extend_namespace=[UnknownAsset]) use_auto_yaml(pack) model_merging(pack) elif pack_type is DataPack: - pack = pack_type(extend_namespace=[UnknownData, WeldScript, WeldPyScript]) + pack = pack_type(extend_namespace=[UnknownData]) use_auto_yaml(pack) else: raise ValueError("`pack_type` must be `DataPack` or `ResourcePack`") + scripts.load_resources(pack) return pack diff --git a/tests/snapshots/examples__build_basic_scripts__0.pack.md b/tests/snapshots/examples__build_basic_scripts__0.pack.md index ab1e6fb..72afdbd 100644 --- a/tests/snapshots/examples__build_basic_scripts__0.pack.md +++ b/tests/snapshots/examples__build_basic_scripts__0.pack.md @@ -7,18 +7,29 @@ ```json { "pack": { - "pack_format": 48, - "description": "" + "description": "", + "min_format": [ + 121, + 0 + ], + "max_format": [ + 121, + 0 + ] }, "id": "tasty_tacos", "overlays": { "entries": [ { - "formats": { - "min_inclusive": 48, - "max_inclusive": 48 - }, - "directory": "weld_generated_custom" + "directory": "weld_generated", + "min_format": [ + 121, + 0 + ], + "max_format": [ + 121, + 0 + ] } ] } @@ -27,7 +38,7 @@ `@data_pack pack.png` -![data_pack.png](data:image/png;base64,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) +![data_pack.png](data:image/png;base64,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) ### custom @@ -35,7 +46,7 @@ ``` function list:functions: - for func_name in ctx.data.functions: + for func_name in sorted(ctx.data.functions): say func_name ``` @@ -61,9 +72,9 @@ say "the tastiest of tacos" give @a taco 64 ``` -## Overlay `weld_generated_custom` +## Overlay `weld_generated` -`@overlay weld_generated_custom` +`@overlay weld_generated` ### list @@ -76,3 +87,23 @@ say tasty_tacos:tacos ``` `@endoverlay` + +## Resource pack + +`@resource_pack pack.mcmeta` + +```json +{ + "pack": { + "min_format": [ + 75, + 0 + ], + "max_format": [ + 75, + 0 + ], + "description": "" + } +} +``` diff --git a/tests/test_examples.py b/tests/test_examples.py index f65d83b..02ee731 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -5,11 +5,26 @@ from beet.library.test_utils import ignore_name import pytest from lectern import Document +from lectern.pytest_plugin import FmtPackMarkdown from pytest_insta import SnapshotFixture from smithed.type import JsonDict -from smithed.weld import run_weld +from smithed.weld import run_weld, scripts + + +class FmtWeldPackMarkdown(FmtPackMarkdown): + def load(self, path: Path) -> Document: + document = Document() + scripts.load_resources(document.assets) + scripts.load_resources(document.data) + document.load(path) + for pack in (document.assets, document.data): + ignore_name(pack) + for overlay in pack.overlays.values(): + ignore_name(overlay) + return document + EXAMPLES = [f for f in os.listdir("examples") if not f.startswith(".")] diff --git a/uv.lock b/uv.lock index c0ad3f8..13f6a29 100644 --- a/uv.lock +++ b/uv.lock @@ -144,6 +144,38 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7f/5e/83d8dddc4603ac43b71569247ba53d18149198b8a70ebc9f6d2e1390d486/bolt-0.51.0-py3-none-any.whl", hash = "sha256:ef3fb1f40c01c4a5d216b8440f98a083368f53b12989d795e1a684ff9f416ad1", size = 59765, upload-time = "2026-09-10T18:33:05.858Z" }, ] +[[package]] +name = "bolt-control-flow" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beet" }, + { name = "bolt" }, + { name = "mecha" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/d7/2c0d8073a6e7fd73e5fc519799d966ab2827f402569c45e510bd17962074/bolt_control_flow-0.4.0.tar.gz", hash = "sha256:c4975ae03bad072fff979e08b2283b746e92c84c7e053a721bd466de23483667", size = 7385, upload-time = "2026-01-18T03:23:30.771Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/47/3345c978b3fe00db95c83d08c3d458eaf7530b1683bc650c60670437e08c/bolt_control_flow-0.4.0-py3-none-any.whl", hash = "sha256:7ce1d9a36cc67409077ac8aa7debcaaa16279ef97524443e18974cf2ed6cc4f6", size = 9699, upload-time = "2026-01-18T03:23:29.341Z" }, +] + +[[package]] +name = "bolt-expressions" +version = "0.19.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beet" }, + { name = "bolt" }, + { name = "bolt-control-flow" }, + { name = "frozendict" }, + { name = "mecha" }, + { name = "nbtlib" }, + { name = "rich" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6e/2a/8c2aed527acbdebacc2b23a36aa6c26a103dc6645b5320f9bc864131eeeb/bolt_expressions-0.19.2.tar.gz", hash = "sha256:b1e3ef28af5b13aa7a88c240e6b72f1cc7e736790b466f2d856977e25f9fa237", size = 39382, upload-time = "2026-01-18T04:49:58.422Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/5c/65a87588d2a1f26065d4077447fdd1c929aa3b185bcbbb5f6628fbbea7ec/bolt_expressions-0.19.2-py3-none-any.whl", hash = "sha256:e303efe505e2d7678e179ae5d663bc00fa7fc6a04bf57f3ed5dd3676b34d6996", size = 46432, upload-time = "2026-01-18T04:49:56.889Z" }, +] + [[package]] name = "boto3" version = "1.42.71" @@ -543,6 +575,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fd/ba/56147c165442cc5ba7e82ecf301c9a68353cede498185869e6e02b4c264f/fonttools-4.62.1-py3-none-any.whl", hash = "sha256:7487782e2113861f4ddcc07c3436450659e3caa5e470b27dc2177cade2d8e7fd", size = 1152647, upload-time = "2026-03-13T13:54:22.735Z" }, ] +[[package]] +name = "frozendict" +version = "2.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/90/b2/2a3d1374b7780999d3184e171e25439a8358c47b481f68be883c14086b4c/frozendict-2.4.7.tar.gz", hash = "sha256:e478fb2a1391a56c8a6e10cc97c4a9002b410ecd1ac28c18d780661762e271bd", size = 317082, upload-time = "2025-11-11T22:40:14.251Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/74/f94141b38a51a553efef7f510fc213894161ae49b88bffd037f8d2a7cb2f/frozendict-2.4.7-py3-none-any.whl", hash = "sha256:972af65924ea25cf5b4d9326d549e69a9a4918d8a76a9d3a7cd174d98b237550", size = 16264, upload-time = "2025-11-11T22:40:12.836Z" }, +] + [[package]] name = "gitdb" version = "4.0.12" @@ -1732,6 +1773,7 @@ dependencies = [ { name = "backports-strenum" }, { name = "beet" }, { name = "bolt" }, + { name = "bolt-expressions" }, { name = "lectern" }, { name = "mecha" }, { name = "pydantic" }, @@ -1765,6 +1807,7 @@ requires-dist = [ { name = "backports-strenum", specifier = ">=1.2.8" }, { name = "beet", specifier = ">=0.119.0" }, { name = "bolt", specifier = ">=0.38.5" }, + { name = "bolt-expressions", specifier = ">=0.17.0" }, { name = "lectern", specifier = ">=0.34.0" }, { name = "mecha", specifier = ">=0.106.0" }, { name = "pydantic", specifier = ">=2.5.2" }, From 3d52148d43bda4bbbe78c9fd32096fe6d570860e Mon Sep 17 00:00:00 2001 From: rx97 Date: Sun, 18 Oct 2026 22:42:38 -0700 Subject: [PATCH 8/9] fix: 2026 --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 0dd8d3b..5fc5705 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2025 Smithed +Copyright (c) 2026 Smithed Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From e21a452ec471fcda428ef0dc2eaa00aedff046eb Mon Sep 17 00:00:00 2001 From: rx97 Date: Sun, 18 Oct 2026 22:50:52 -0700 Subject: [PATCH 9/9] doc: missing comment --- src/smithed/weld/toolchain/plugins.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/smithed/weld/toolchain/plugins.py b/src/smithed/weld/toolchain/plugins.py index 0361e1e..088e1fb 100644 --- a/src/smithed/weld/toolchain/plugins.py +++ b/src/smithed/weld/toolchain/plugins.py @@ -58,6 +58,7 @@ def add_fabric_mod_json(ctx: Context, pack_names: list[str] | None = None): if pack_names is None: processor = ctx.inject(PackProcessor) + # deduplicate names while preserving pack loading order for Fabric metadata. pack_names = list(dict.fromkeys(pack.name for pack in processor.packs)) content = FABRIC_MOD_TEMPLATE.render(