Skip to content

Commit

Permalink
feat: extract autosave and linking strategy into their own plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
vberlier committed Nov 29, 2021
1 parent 7f44324 commit f82a8d5
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 22 deletions.
52 changes: 52 additions & 0 deletions beet/contrib/autosave.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""Service for saving the data pack and resource pack at the end of the build."""


__all__ = [
"Autosave",
"AutosaveOptions",
]


from dataclasses import dataclass, field
from typing import List

from pydantic import BaseModel

from beet import Context, PluginSpec


class AutosaveOptions(BaseModel):
link: bool = False
output_handlers: List[str] = []
link_handlers: List[str] = []


@dataclass
class Autosave:
"""Service for saving the data pack and resource pack at the end of the build."""

ctx: Context
link: bool = False
output_handlers: List[PluginSpec] = field(default_factory=list)
link_handlers: List[PluginSpec] = field(default_factory=list)

def __post_init__(self):
opts = self.ctx.validate("autosave", AutosaveOptions)
self.link = opts.link
self.output_handlers = list(opts.output_handlers)
self.link_handlers = list(opts.link_handlers)
self.ctx.require(self.finalize)

def add_output(self, *specs: PluginSpec):
"""Register output handler."""
self.output_handlers.extend(specs)

def add_link(self, *specs: PluginSpec):
"""Register link handler."""
self.link_handlers.extend(specs)

def finalize(self, ctx: Context):
yield
ctx.require(*self.output_handlers)
if self.link:
ctx.require(*self.link_handlers)
49 changes: 49 additions & 0 deletions beet/contrib/link.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""Plugin for linking the generated resource pack and data pack to Minecraft."""


__all__ = [
"LinkOptions",
"link",
]


import logging
from typing import Optional

from pydantic import BaseModel

from beet import Context, configurable
from beet.core.utils import FileSystemPath, log_time, remove_path

logger = logging.getLogger("link")


class LinkOptions(BaseModel):
resource_pack: Optional[FileSystemPath] = None
data_pack: Optional[FileSystemPath] = None


def beet_default(ctx: Context):
ctx.require(link)


@configurable(validator=LinkOptions)
def link(ctx: Context, opts: LinkOptions):
"""Plugin for linking the generated resource pack and data pack to Minecraft."""
dirty = ctx.cache["link"].json.setdefault("dirty", [])
remove_path(*dirty)
dirty.clear()

to_link = [
(ctx.directory / directory, pack)
for directory, pack in zip([opts.resource_pack, opts.data_pack], ctx.packs)
if directory and pack
]

if to_link:
with log_time("Link project."):
for directory, pack in to_link:
try:
dirty.append(str(pack.save(directory)))
except FileExistsError as exc:
logger.warning(str(exc))
41 changes: 41 additions & 0 deletions beet/contrib/output.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""Plugin that outputs the data pack and the resource pack in a local directory."""


__all__ = [
"OutputOptions",
"output",
]


from typing import List, Optional, Union

from pydantic import BaseModel

from beet import Context, configurable
from beet.core.utils import FileSystemPath, log_time


class OutputOptions(BaseModel):
directory: Optional[Union[FileSystemPath, List[FileSystemPath]]] = None


def beet_default(ctx: Context):
ctx.require(output)


@configurable(validator=OutputOptions)
def output(ctx: Context, opts: OutputOptions):
"""Plugin that outputs the data pack and the resource pack in a local directory."""
if not opts.directory:
return

paths = opts.directory if isinstance(opts.directory, list) else [opts.directory]
paths = [ctx.directory / path for path in paths]

packs = list(filter(None, ctx.packs))

if paths and packs:
with log_time("Output files."):
for pack in packs:
for path in paths:
pack.save(path, overwrite=True)
14 changes: 12 additions & 2 deletions beet/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,22 @@
"normalize_string",
"snake_case",
"log_time",
"remove_path",
]


import json
import logging
import re
import shutil
import time
from contextlib import contextmanager
from dataclasses import field
from pathlib import PurePath
from pathlib import Path, PurePath
from typing import Any, Dict, Iterable, Iterator, List, TypeVar, Union

from pydantic import PydanticTypeError
from pydantic.validators import _VALIDATORS
from pydantic.validators import _VALIDATORS # type: ignore

T = TypeVar("T")

Expand Down Expand Up @@ -92,6 +94,14 @@ def log_time(message: str, *args: Any, **kwargs: Any) -> Iterator[None]:
time_logger.debug(message, *args, **kwargs)


def remove_path(*paths: FileSystemPath):
for path in map(Path, paths):
if path.is_dir():
shutil.rmtree(path)
else:
path.unlink(missing_ok=True)


class PurePathError(PydanticTypeError):
msg_template = "value is not a pure path"

Expand Down
4 changes: 2 additions & 2 deletions beet/toolchain/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def build(project: Project, link: Optional[str], no_link: bool):
with message_fence(text):
if link:
echo("\n".join(project.link(target=link)))
project.build(link=not no_link)
project.build(no_link)


@beet.command()
Expand Down Expand Up @@ -74,7 +74,7 @@ def watch(project: Project, link: Optional[str], no_link: bool, interval: float)
echo(f"{change_time} {text}")

with error_handler(format_padding=1):
project.build(link=not no_link)
project.build(no_link)


@beet.command()
Expand Down
40 changes: 22 additions & 18 deletions beet/toolchain/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,18 @@
from pathlib import Path
from typing import ClassVar, Iterable, Iterator, List, Optional, Sequence

from beet.contrib.autosave import Autosave
from beet.contrib.link import link
from beet.contrib.load import load
from beet.contrib.output import output
from beet.contrib.render import render
from beet.core.utils import FileSystemPath, intersperse, log_time, normalize_string
from beet.core.utils import (
FileSystemPath,
intersperse,
log_time,
normalize_string,
remove_path,
)
from beet.core.watch import DirectoryWatcher, FileChanges

from .config import PackConfig, ProjectConfig, load_config, locate_config
Expand Down Expand Up @@ -117,15 +126,16 @@ def reset(self):
self.resolved_config = None
self.resolved_cache = None

def build(self, link: bool = False):
def build(self, no_link: bool = False) -> Context:
"""Build the project."""
ctx = ProjectBuilder(self).build()
autosave = self.config.meta.setdefault("autosave", {})

if link and any(ctx.packs):
with log_time("Link project."):
for link_key, pack in zip(["resource_pack", "data_pack"], ctx.packs):
if pack and (link_dir := ctx.cache["link"].json.get(link_key)):
pack.save(link_dir, overwrite=True)
if no_link:
autosave["link"] = False
else:
autosave.setdefault("link", True)

return ProjectBuilder(self).build()

def watch(self, interval: float = 0.6) -> Iterator[FileChanges]:
"""Watch the project."""
Expand Down Expand Up @@ -210,6 +220,7 @@ def link(self, target: Optional[FileSystemPath] = None) -> Iterable[str]:
def clear_link(self):
"""Remove the linked resource pack directory and data pack directory."""
with self.cache:
remove_path(*self.cache["link"].json.get("dirty", []))
del self.cache["link"]


Expand Down Expand Up @@ -274,18 +285,11 @@ def build(self) -> Context:

return ctx

def autosave(self, ctx: Context):
"""Plugin that outputs the data pack and the resource pack at the end of the build."""
yield
if ctx.output_directory and any(ctx.packs):
with log_time("Output files."):
for pack in ctx.packs:
if pack:
pack.save(ctx.output_directory, overwrite=True)

def bootstrap(self, ctx: Context):
"""Plugin that handles the project configuration."""
ctx.require(self.autosave)
autosave = ctx.inject(Autosave)
autosave.add_output(output(directory=ctx.output_directory))
autosave.add_link(link(**ctx.cache["link"].json))

plugins = (self.autoload or []) + self.config.require

Expand Down

0 comments on commit f82a8d5

Please sign in to comment.