Use pluggy for fetching mod version

This commit is contained in:
object-Object 2023-09-05 01:26:00 -04:00
parent 6493f56b83
commit 1e64a589a9
10 changed files with 84 additions and 33 deletions

2
.vscode/launch.json vendored
View file

@ -16,7 +16,7 @@
"name": "Python: Generate Docs", "name": "Python: Generate Docs",
"type": "python", "type": "python",
"request": "launch", "request": "launch",
"module": "hexdoc.hexdoc", "module": "hexdoc.scripts.hexdoc",
"args": [ "args": [
"doc/properties.toml", "-o", "out", "--lang", "en_us", "doc/properties.toml", "-o", "out", "--lang", "en_us",
], ],

View file

@ -0,0 +1,7 @@
from hexdoc.__gradle_version__ import GRADLE_VERSION
from hexdoc.plugin import hookimpl
@hookimpl
def hexdoc_mod_version():
return GRADLE_VERSION

View file

@ -0,0 +1,5 @@
__all__ = ["hookimpl"]
from pluggy import HookimplMarker
hookimpl = HookimplMarker("hexdoc")

View file

@ -0,0 +1,10 @@
from pluggy import PluginManager
def name_hook_caller(pm: PluginManager, method_name: str, plugin_name: str):
return pm.subset_hook_caller(
name=method_name,
remove_plugins=(
plugin for name, plugin in pm.list_name_plugin() if name != plugin_name
),
)

View file

@ -0,0 +1,8 @@
from pluggy import HookspecMarker
hookspec = HookspecMarker("hexdoc")
@hookspec(firstresult=True)
def hexdoc_mod_version():
"""Return the mod version (aka `GRADLE_VERSION`) from `__gradle_version__.py`."""

View file

@ -6,6 +6,7 @@ import shutil
import sys import sys
from argparse import ArgumentParser from argparse import ArgumentParser
from pathlib import Path from pathlib import Path
from types import NoneType
from typing import Self, Sequence from typing import Self, Sequence
from jinja2 import ( from jinja2 import (
@ -16,13 +17,16 @@ from jinja2 import (
Template, Template,
) )
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from pluggy import PluginManager
from pydantic import model_validator from pydantic import model_validator
from hexdoc.__gradle_version__ import GRADLE_VERSION
from hexdoc.hexcasting.hex_book import load_hex_book from hexdoc.hexcasting.hex_book import load_hex_book
from hexdoc.minecraft import I18n from hexdoc.minecraft import I18n
from hexdoc.patchouli import Book from hexdoc.patchouli import Book
from hexdoc.plugin import hookspecs
from hexdoc.plugin.helpers import name_hook_caller
from hexdoc.utils import HexdocModel, ModResourceLoader, Properties from hexdoc.utils import HexdocModel, ModResourceLoader, Properties
from hexdoc.utils.deserialize import cast_or_raise, isinstance_or_raise
from hexdoc.utils.jinja_extensions import IncludeRawExtension, hexdoc_block, hexdoc_wrap from hexdoc.utils.jinja_extensions import IncludeRawExtension, hexdoc_block, hexdoc_wrap
from hexdoc.utils.path import write_to_path from hexdoc.utils.path import write_to_path
@ -113,15 +117,15 @@ class SitemapMarker(HexdocModel):
def main(args: Args | None = None) -> None: def main(args: Args | None = None) -> None:
# set stdout to utf-8 so printing to pipe or redirect doesn't break on Windows
# (common windows L)
cast_or_raise(sys.stdout, io.TextIOWrapper).reconfigure(encoding="utf-8")
cast_or_raise(sys.stderr, io.TextIOWrapper).reconfigure(encoding="utf-8")
# allow passing Args for test cases, but parse by default # allow passing Args for test cases, but parse by default
if args is None: if args is None:
args = Args.parse_args() args = Args.parse_args()
# set stdout to utf-8 so printing to pipe or redirect doesn't break on Windows
# (common windows L)
assert isinstance(sys.stdout, io.TextIOWrapper)
sys.stdout.reconfigure(encoding="utf-8")
# set up logging # set up logging
logging.basicConfig( logging.basicConfig(
style="{", style="{",
@ -130,18 +134,34 @@ def main(args: Args | None = None) -> None:
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Properties is the main config file for hexdoc
props = Properties.load(args.properties_file) props = Properties.load(args.properties_file)
logger.debug(props) logger.debug(props)
# load plugins
# load entry points for props.modid first to make sure its version is added last
pm = PluginManager("hexdoc")
pm.add_hookspecs(hookspecs)
pm.load_setuptools_entrypoints("hexdoc")
pm.check_pending()
# get the current mod version
version = name_hook_caller(pm, "hexdoc_mod_version", props.modid)()
assert isinstance_or_raise(version, (str, NoneType))
if version is None:
raise ValueError(f"Missing hexdoc_mod_version hookimpl for {props.modid}")
print(f"Building docs for {props.modid} {version}")
# just list the languages and exit # just list the languages and exit
if args.list_langs: if args.list_langs:
with ModResourceLoader.load_all(props, export=False) as loader: with ModResourceLoader.load_all(props, version, export=False) as loader:
langs = sorted(I18n.list_all(loader)) langs = sorted(I18n.list_all(loader))
print(json.dumps(langs)) print(json.dumps(langs))
return return
# load everything # load everything
with ModResourceLoader.clean_and_load_all(props) as loader: with ModResourceLoader.clean_and_load_all(props, version) as loader:
books = dict[str, Book]() books = dict[str, Book]()
if args.lang: if args.lang:
@ -181,7 +201,8 @@ def main(args: Args | None = None) -> None:
if args.export_only: if args.export_only:
return return
# set up Jinja environment # set up Jinja
env = SandboxedEnvironment( env = SandboxedEnvironment(
# search order: template_dirs, template_packages # search order: template_dirs, template_packages
loader=ChoiceLoader( loader=ChoiceLoader(
@ -204,6 +225,8 @@ def main(args: Args | None = None) -> None:
template = env.get_template(props.template.main) template = env.get_template(props.template.main)
# render everything
assert (output_dir := args.output_dir) assert (output_dir := args.output_dir)
if args.clean: if args.clean:
shutil.rmtree(output_dir, ignore_errors=True) shutil.rmtree(output_dir, ignore_errors=True)
@ -212,11 +235,11 @@ def main(args: Args | None = None) -> None:
render_books(props, books, template, output_dir, "latest") render_books(props, books, template, output_dir, "latest")
if args.is_release: if args.is_release:
render_books(props, books, template, output_dir, GRADLE_VERSION) render_books(props, books, template, output_dir, version)
# the default book should be the latest released version # the default book should be the latest released version
if args.update_latest and args.is_release: if args.update_latest and args.is_release:
render_books(props, books, template, output_dir, GRADLE_VERSION, is_root=True) render_books(props, books, template, output_dir, version, is_root=True)
def render_books( def render_books(

View file

@ -6,7 +6,6 @@ from typing import Self, Sequence
from pydantic import Field, TypeAdapter from pydantic import Field, TypeAdapter
from hexdoc.__gradle_version__ import GRADLE_VERSION
from hexdoc.utils import DEFAULT_CONFIG, HexdocModel from hexdoc.utils import DEFAULT_CONFIG, HexdocModel
from hexdoc.utils.path import write_to_path from hexdoc.utils.path import write_to_path
@ -57,10 +56,11 @@ def main():
args = Args.parse_args() args = Args.parse_args()
# ensure at least the default language was built successfully # ensure at least the default language was built successfully
if args.is_release:
assert_version_exists(args.src, GRADLE_VERSION)
if args.update_latest: if args.update_latest:
assert_version_exists(args.src, "latest") assert_version_exists(args.src, "latest")
# TODO: figure out how to do this with pluggy
# if args.is_release:
# assert_version_exists(args.src, GRADLE_VERSION)
args.dst.mkdir(parents=True, exist_ok=True) args.dst.mkdir(parents=True, exist_ok=True)

View file

@ -2,7 +2,7 @@ from enum import Enum
from functools import total_ordering from functools import total_ordering
from typing import Any, Callable, Self from typing import Any, Callable, Self
from hexdoc.__gradle_version__ import GRADLE_VERSION from hexdoc.__gradle_version__ import GRADLE_VERSION as HEX_VERSION
@total_ordering @total_ordering
@ -14,9 +14,9 @@ class HexVersion(Enum):
@classmethod @classmethod
def get(cls): def get(cls):
for version in cls: for version in cls:
if GRADLE_VERSION.startswith(version.value): if HEX_VERSION.startswith(version.value):
return version return version
raise ValueError(f"Unknown mod version: {GRADLE_VERSION}") raise ValueError(f"Unknown mod version: {HEX_VERSION}")
@classmethod @classmethod
def check(cls, version: Self | Callable[[Self], bool] | bool, typ: type[Any] | str): def check(cls, version: Self | Callable[[Self], bool] | bool, typ: type[Any] | str):
@ -33,7 +33,7 @@ class HexVersion(Enum):
if not isinstance(typ, str): if not isinstance(typ, str):
typ = typ.__name__ typ = typ.__name__
raise ValueError(f"{typ} is not supported in {GRADLE_VERSION}") raise ValueError(f"{typ} is not supported in {HEX_VERSION}")
@property @property
def _sort_by(self): def _sort_by(self):

View file

@ -9,7 +9,6 @@ from typing import Callable, Literal, Self, TypeVar, overload
from pydantic.dataclasses import dataclass from pydantic.dataclasses import dataclass
from hexdoc.__gradle_version__ import GRADLE_VERSION
from hexdoc.utils.deserialize import JSONDict, decode_json_dict from hexdoc.utils.deserialize import JSONDict, decode_json_dict
from hexdoc.utils.model import DEFAULT_CONFIG, HexdocModel, ValidationContext from hexdoc.utils.model import DEFAULT_CONFIG, HexdocModel, ValidationContext
from hexdoc.utils.path import strip_suffixes, write_to_path from hexdoc.utils.path import strip_suffixes, write_to_path
@ -45,19 +44,21 @@ class ModResourceLoader:
def clean_and_load_all( def clean_and_load_all(
cls, cls,
props: Properties, props: Properties,
version: str,
*, *,
export: bool = True, export: bool = True,
): ):
# clear the export dir so we start with a clean slate # clear the export dir so we start with a clean slate
if props.export_dir and export: if props.export_dir and export:
subprocess.run(["git", "clean", "-fdX", props.export_dir]) subprocess.run(["git", "clean", "-fdX", props.export_dir])
return cls.load_all(props, export=export) return cls.load_all(props, version, export=export)
@classmethod @classmethod
@contextmanager @contextmanager
def load_all( def load_all(
cls, cls,
props: Properties, props: Properties,
version: str,
*, *,
export: bool = True, export: bool = True,
) -> Iterator[Self]: ) -> Iterator[Self]:
@ -78,7 +79,7 @@ class ModResourceLoader:
loader.export( loader.export(
path=HexdocMetadata.path(props.modid), path=HexdocMetadata.path(props.modid),
data=HexdocMetadata( data=HexdocMetadata(
book_url=f"{props.url}/v/{GRADLE_VERSION}", book_url=f"{props.url}/v/{version}",
).model_dump_json(), ).model_dump_json(),
) )

View file

@ -1,5 +1,5 @@
[build-system] [build-system]
requires = ["hatchling"] requires = ["hatchling", "hatch-gradle-version>=0.6.0"]
build-backend = "hatchling.build" build-backend = "hatchling.build"
# project metadata # project metadata
@ -8,7 +8,7 @@ build-backend = "hatchling.build"
name = "hexdoc" name = "hexdoc"
dynamic = ["version"] dynamic = ["version"]
authors = [ authors = [
{ name="object-Object", email="object@objectobject.ca" }, { name="object-Object" },
{ name="Alwinfy" }, { name="Alwinfy" },
] ]
readme = "doc/README.md" readme = "doc/README.md"
@ -17,18 +17,15 @@ classifiers = [
] ]
requires-python = ">=3.11" requires-python = ">=3.11"
dependencies = [ dependencies = [
"hexdoc[build]", # required optional dependency i guess
"typing_extensions>=4.6.1", "typing_extensions>=4.6.1",
"importlib_resources>=6.0.1", "importlib_resources>=6.0.1",
"pydantic>=2.3.0", "pydantic>=2.3.0",
"Jinja2>=3.1.2", "Jinja2>=3.1.2",
"pyjson5>=1.6.3", "pyjson5>=1.6.3",
"pluggy>=1.3.0",
] ]
[project.optional-dependencies] [project.optional-dependencies]
build = [ # need these for build AND runtime
"hatch-gradle-version>=0.6.0",
]
dev = [ dev = [
"black==23.7.0", "black==23.7.0",
"isort==5.12.0", "isort==5.12.0",
@ -52,9 +49,6 @@ py-path = "doc/src/hexdoc/__version__.py"
# directory inclusion # directory inclusion
[tool.hatch.build] [tool.hatch.build]
require-runtime-features = [
"build",
]
artifacts = [ artifacts = [
"/doc/src/hexdoc/_export/generated", "/doc/src/hexdoc/_export/generated",
"/doc/src/hexdoc/__gradle_version__.py", "/doc/src/hexdoc/__gradle_version__.py",
@ -73,6 +67,9 @@ packages = [
# hexdoc entry points # hexdoc entry points
[project.entry-points.hexdoc]
hexcasting = "hexdoc.hexcasting.hooks"
[project.entry-points."hexdoc.export"] [project.entry-points."hexdoc.export"]
hexcasting = "hexdoc._export:__resources__" hexcasting = "hexdoc._export:__resources__"
@ -110,8 +107,8 @@ known_first_party = ["hexdoc"]
pythonVersion = "3.11" pythonVersion = "3.11"
pythonPlatform = "All" pythonPlatform = "All"
include = [ extraPaths = [
"doc/src/hexdoc", "doc/src",
] ]
# mostly we use strict mode # mostly we use strict mode