Refactor most of the core logic out of utils

This commit is contained in:
object-Object 2023-10-10 00:58:55 -04:00
parent 5b26c1d549
commit 98a68e1f15
50 changed files with 297 additions and 289 deletions

View file

@ -8,10 +8,10 @@ from typing import Annotated, Union
import typer import typer
from hexdoc.core.loader import ModResourceLoader
from hexdoc.core.resource import ResourceLocation
from hexdoc.minecraft import I18n from hexdoc.minecraft import I18n
from hexdoc.minecraft.assets.textures import AnimatedTexture, Texture from hexdoc.minecraft.assets.textures import AnimatedTexture, Texture
from hexdoc.utils import ModResourceLoader
from hexdoc.utils.resource import ResourceLocation
from .utils.load import load_book, load_books, load_common_data from .utils.load import load_book, load_books, load_common_data
from .utils.render import create_jinja_env, render_book from .utils.render import create_jinja_env, render_book

View file

@ -2,13 +2,14 @@ import logging
import subprocess import subprocess
from pathlib import Path from pathlib import Path
from hexdoc.core.loader import ModResourceLoader
from hexdoc.core.metadata import HexdocMetadata
from hexdoc.core.properties import Properties
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.minecraft.assets.textures import Texture from hexdoc.minecraft.assets import Texture
from hexdoc.patchouli import Book from hexdoc.patchouli import Book
from hexdoc.plugin import PluginManager from hexdoc.plugin import PluginManager
from hexdoc.utils import ModResourceLoader, Properties
from hexdoc.utils.metadata import HexdocMetadata
from .logging import setup_logging from .logging import setup_logging

View file

@ -14,20 +14,20 @@ from jinja2 import (
) )
from jinja2.sandbox import SandboxedEnvironment from jinja2.sandbox import SandboxedEnvironment
from hexdoc.core.metadata import HexdocMetadata
from hexdoc.core.properties import Properties
from hexdoc.core.resource import ResourceLocation
from hexdoc.minecraft import I18n from hexdoc.minecraft import I18n
from hexdoc.minecraft.assets.textures import AnimatedTexture, Texture from hexdoc.minecraft.assets.textures import AnimatedTexture, Texture
from hexdoc.patchouli import Book from hexdoc.patchouli import Book
from hexdoc.utils import Properties from hexdoc.utils.jinja.extensions import IncludeRawExtension
from hexdoc.utils.jinja_extensions import ( from hexdoc.utils.jinja.macros import (
IncludeRawExtension,
hexdoc_block, hexdoc_block,
hexdoc_localize, hexdoc_localize,
hexdoc_texture, hexdoc_texture,
hexdoc_wrap, hexdoc_wrap,
) )
from hexdoc.utils.metadata import HexdocMetadata
from hexdoc.utils.path import write_to_path from hexdoc.utils.path import write_to_path
from hexdoc.utils.resource import ResourceLocation
from .sitemap import MARKER_NAME, SitemapMarker from .sitemap import MARKER_NAME, SitemapMarker

View file

@ -4,7 +4,7 @@ from pathlib import Path
from pydantic import Field, TypeAdapter from pydantic import Field, TypeAdapter
from hexdoc.utils import DEFAULT_CONFIG, HexdocModel from hexdoc.model import DEFAULT_CONFIG, HexdocModel
from hexdoc.utils.path import write_to_path from hexdoc.utils.path import write_to_path
MARKER_NAME = ".sitemap-marker.json" MARKER_NAME = ".sitemap-marker.json"

View file

View file

@ -12,12 +12,12 @@ from typing import Any, Callable, Literal, Self, TypeVar, overload
from pydantic.dataclasses import dataclass from pydantic.dataclasses import dataclass
from hexdoc.plugin.manager import PluginManager from hexdoc.model import DEFAULT_CONFIG, HexdocModel, ValidationContext
from hexdoc.plugin import PluginManager
from hexdoc.utils.deserialize.json import JSONDict, decode_json_dict
from hexdoc.utils.iterators import must_yield_something
from hexdoc.utils.path import strip_suffixes, write_to_path
from .deserialize import JSONDict, decode_json_dict
from .iterators import must_yield_something
from .model import DEFAULT_CONFIG, HexdocModel, ValidationContext
from .path import strip_suffixes, write_to_path
from .properties import Properties from .properties import Properties
from .resource import PathResourceDir, ResourceLocation, ResourceType from .resource import PathResourceDir, ResourceLocation, ResourceType

View file

@ -3,9 +3,9 @@ from typing import Self
from pydantic import model_validator from pydantic import model_validator
from hexdoc.minecraft.assets.textures import Texture, TextureContext from hexdoc.minecraft.assets import Texture, TextureContext
from hexdoc.model import HexdocModel
from .model import HexdocModel
from .properties import NoTrailingSlashHttpUrl from .properties import NoTrailingSlashHttpUrl
from .resource import ResourceLocation from .resource import ResourceLocation

View file

@ -3,15 +3,15 @@ from __future__ import annotations
import logging import logging
import re import re
from pathlib import Path from pathlib import Path
from typing import Annotated, Any, Self from typing import Annotated, Any, Self, dataclass_transform
from pydantic import AfterValidator, Field, HttpUrl, field_validator from pydantic import AfterValidator, Field, HttpUrl, field_validator, model_validator
from pydantic_settings import BaseSettings, SettingsConfigDict from pydantic_settings import BaseSettings, SettingsConfigDict
from .cd import RelativePath, relative_path_root from hexdoc.core.resource import ResourceDir, ResourceLocation
from .model import StripHiddenModel from hexdoc.model import HexdocModel
from .resource import ResourceDir, ResourceLocation from hexdoc.utils.cd import RelativePath, relative_path_root
from .toml_placeholders import load_toml_with_placeholders from hexdoc.utils.deserialize.toml import load_toml_with_placeholders
NoTrailingSlashHttpUrl = Annotated[ NoTrailingSlashHttpUrl = Annotated[
str, str,
@ -20,6 +20,22 @@ NoTrailingSlashHttpUrl = Annotated[
] ]
@dataclass_transform()
class StripHiddenModel(HexdocModel):
"""Base model which removes all keys starting with _ before validation."""
@model_validator(mode="before")
def _pre_root_strip_hidden(cls, values: Any) -> Any:
if not isinstance(values, dict):
return values
return {
key: value
for key, value in values.items()
if not (isinstance(key, str) and key.startswith("_"))
}
class EnvironmentVariableProps(BaseSettings): class EnvironmentVariableProps(BaseSettings):
model_config = SettingsConfigDict(env_file=".env") model_config = SettingsConfigDict(env_file=".env")

View file

@ -19,11 +19,10 @@ from pydantic import ValidationInfo, field_validator, model_serializer, model_va
from pydantic.dataclasses import dataclass from pydantic.dataclasses import dataclass
from pydantic.functional_validators import ModelWrapValidatorHandler from pydantic.functional_validators import ModelWrapValidatorHandler
from hexdoc.model import DEFAULT_CONFIG, HexdocModel
from hexdoc.plugin import PluginManager from hexdoc.plugin import PluginManager
from hexdoc.utils.cd import RelativePath, relative_path_root from hexdoc.utils.cd import RelativePath, relative_path_root
from .model import DEFAULT_CONFIG, HexdocModel
ResourceType = Literal["assets", "data", ""] ResourceType = Literal["assets", "data", ""]

View file

@ -4,14 +4,16 @@ from typing import Any, Mapping
from pydantic import Field, model_validator from pydantic import Field, model_validator
from hexdoc.core.compat import HexVersion
from hexdoc.core.loader import ModResourceLoader
from hexdoc.core.metadata import HexdocMetadata
from hexdoc.core.properties import PatternStubProps
from hexdoc.core.resource import ResourceLocation
from hexdoc.minecraft import I18n, Tag from hexdoc.minecraft import I18n, Tag
from hexdoc.model import HexdocModel, init_context
from hexdoc.patchouli import Book, BookContext from hexdoc.patchouli import Book, BookContext
from hexdoc.plugin import PluginManager from hexdoc.plugin import PluginManager
from hexdoc.utils import HexdocModel, ModResourceLoader, ResourceLocation, init_context
from hexdoc.utils.compat import HexVersion
from hexdoc.utils.deserialize import cast_or_raise from hexdoc.utils.deserialize import cast_or_raise
from hexdoc.utils.metadata import HexdocMetadata
from hexdoc.utils.properties import PatternStubProps
from .pattern import Direction, PatternInfo from .pattern import Direction, PatternInfo

View file

@ -2,11 +2,12 @@ from typing import Any, Literal
from pydantic import model_validator from pydantic import model_validator
from hexdoc.core.compat import HexVersion
from hexdoc.core.resource import ResourceLocation
from hexdoc.minecraft import LocalizedItem, Recipe from hexdoc.minecraft import LocalizedItem, Recipe
from hexdoc.minecraft.recipe import ItemIngredient, ItemIngredientList from hexdoc.minecraft.recipe import ItemIngredient, ItemIngredientList
from hexdoc.utils import HexdocModel, ResourceLocation, TypeTaggedUnion from hexdoc.model import HexdocModel
from hexdoc.utils.compat import HexVersion from hexdoc.model.tagged_union import NoValue, TypeTaggedUnion
from hexdoc.utils.tagged_union import NoValue
# ingredients # ingredients

View file

@ -3,10 +3,10 @@ from typing import Any
from pydantic import ValidationInfo, model_validator from pydantic import ValidationInfo, model_validator
from hexdoc.core.resource import ResourceLocation
from hexdoc.minecraft import LocalizedStr from hexdoc.minecraft import LocalizedStr
from hexdoc.minecraft.i18n import I18nContext from hexdoc.minecraft.i18n import I18nContext
from hexdoc.patchouli.page import PageWithText from hexdoc.patchouli.page import PageWithText
from hexdoc.utils import ResourceLocation
from hexdoc.utils.deserialize import cast_or_raise from hexdoc.utils.deserialize import cast_or_raise
from ..pattern import RawPatternInfo from ..pattern import RawPatternInfo

View file

@ -2,10 +2,10 @@ from typing import Any, Self
from pydantic import ValidationInfo, model_validator from pydantic import ValidationInfo, model_validator
from hexdoc.core.resource import ResourceLocation
from hexdoc.minecraft import LocalizedStr from hexdoc.minecraft import LocalizedStr
from hexdoc.minecraft.recipe import CraftingRecipe from hexdoc.minecraft.recipe import CraftingRecipe
from hexdoc.patchouli.page import PageWithText, PageWithTitle from hexdoc.patchouli.page import PageWithText, PageWithTitle
from hexdoc.utils import ResourceLocation
from hexdoc.utils.deserialize import cast_or_raise from hexdoc.utils.deserialize import cast_or_raise
from ..hex_book import HexContext from ..hex_book import HexContext

View file

@ -3,7 +3,8 @@ from typing import Annotated, Any
from pydantic import BeforeValidator from pydantic import BeforeValidator
from hexdoc.utils import HexdocModel, ResourceLocation from hexdoc.core.resource import ResourceLocation
from hexdoc.model import HexdocModel
class Direction(Enum): class Direction(Enum):

View file

@ -1,7 +1,21 @@
__all__ = [ __all__ = [
"TextureContext", "AnimatedTexture",
"TAG_TEXTURE", "ItemWithGaslightingTexture",
"ItemWithTexture",
"MISSING_TEXTURE", "MISSING_TEXTURE",
"TAG_TEXTURE",
"TagWithTexture",
"Texture",
"TextureContext",
] ]
from .textures import MISSING_TEXTURE, TAG_TEXTURE, TextureContext from .textures import (
MISSING_TEXTURE,
TAG_TEXTURE,
AnimatedTexture,
ItemWithGaslightingTexture,
ItemWithTexture,
TagWithTexture,
Texture,
TextureContext,
)

View file

@ -1,6 +1,7 @@
from typing import Literal from typing import Literal
from hexdoc.utils import HexdocModel, ResourceLocation from hexdoc.core.resource import ResourceLocation
from hexdoc.model import HexdocModel
ItemDisplayPosition = Literal[ ItemDisplayPosition = Literal[
"thirdperson_righthand", "thirdperson_righthand",
@ -13,6 +14,8 @@ ItemDisplayPosition = Literal[
"fixed", "fixed",
] ]
# TODO
class ItemModel(HexdocModel, extra="ignore"): class ItemModel(HexdocModel, extra="ignore"):
parent: ResourceLocation parent: ResourceLocation

View file

@ -7,13 +7,14 @@ from typing import Literal, Self
from pydantic import Field, model_validator from pydantic import Field, model_validator
from hexdoc.minecraft.i18n import I18nContext, LocalizedStr from hexdoc.core.loader import ModResourceLoader
from hexdoc.utils import HexdocModel, ResourceLocation from hexdoc.core.properties import Properties
from hexdoc.utils.external import fetch_minecraft_textures from hexdoc.core.resource import ItemStack, ResourceLocation
from hexdoc.utils.properties import Properties from hexdoc.model import HexdocModel
from hexdoc.utils.resource import ItemStack from hexdoc.model.inline import InlineItemModel, InlineModel
from hexdoc.utils.resource_loader import ModResourceLoader
from hexdoc.utils.resource_model import InlineItemModel, InlineModel from ..i18n import I18nContext, LocalizedStr
from .external import fetch_minecraft_textures
# 16x16 hashtag icon for tags # 16x16 hashtag icon for tags
TAG_TEXTURE = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAC4jAAAuIwF4pT92AAAANUlEQVQ4y2NgGJRAXV39v7q6+n9cfGTARKllFBvAiOxMUjTevHmTkSouGPhAHA0DWnmBrgAANLIZgSXEQxIAAAAASUVORK5CYII=" TAG_TEXTURE = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAC4jAAAuIwF4pT92AAAANUlEQVQ4y2NgGJRAXV39v7q6+n9cfGTARKllFBvAiOxMUjTevHmTkSouGPhAHA0DWnmBrgAANLIZgSXEQxIAAAAASUVORK5CYII="

View file

@ -9,10 +9,12 @@ from typing import Any, Callable, Self
from pydantic import ValidationInfo, model_validator from pydantic import ValidationInfo, model_validator
from pydantic.functional_validators import ModelWrapValidatorHandler from pydantic.functional_validators import ModelWrapValidatorHandler
from hexdoc.utils import HexdocModel, ItemStack, ModResourceLoader, ResourceLocation from hexdoc.core.compat import HexVersion
from hexdoc.utils.compat import HexVersion from hexdoc.core.loader import LoaderContext, ModResourceLoader
from hexdoc.utils.deserialize import cast_or_raise, decode_and_flatten_json_dict from hexdoc.core.resource import ItemStack, ResourceLocation
from hexdoc.utils.resource_loader import LoaderContext from hexdoc.model import HexdocModel
from hexdoc.utils.deserialize import cast_or_raise
from hexdoc.utils.deserialize.json import decode_and_flatten_json_dict
@total_ordering @total_ordering

View file

@ -1,5 +1,7 @@
from hexdoc.utils import ModResourceLoader, ResourceLocation, TypeTaggedUnion from hexdoc.core.loader import ModResourceLoader
from hexdoc.utils.resource_model import InlineIDModel from hexdoc.core.resource import ResourceLocation
from hexdoc.model.inline import InlineIDModel
from hexdoc.model.tagged_union import TypeTaggedUnion
from .ingredients import ItemResult from .ingredients import ItemResult

View file

@ -2,16 +2,17 @@ from typing import Annotated, Any, Iterator
from pydantic import AfterValidator, BeforeValidator, ValidationError, ValidationInfo from pydantic import AfterValidator, BeforeValidator, ValidationError, ValidationInfo
from hexdoc.core.resource import ResourceLocation
from hexdoc.minecraft.assets.textures import ( from hexdoc.minecraft.assets.textures import (
ItemWithTexture, ItemWithTexture,
TagWithTexture, TagWithTexture,
TextureContext, TextureContext,
) )
from hexdoc.minecraft.tags import Tag from hexdoc.minecraft.tags import Tag
from hexdoc.utils import HexdocModel, NoValue, TypeTaggedUnion from hexdoc.model import HexdocModel
from hexdoc.model.tagged_union import NoValue, TypeTaggedUnion
from hexdoc.utils.deserialize import cast_or_raise from hexdoc.utils.deserialize import cast_or_raise
from hexdoc.utils.iterators import listify from hexdoc.utils.iterators import listify
from hexdoc.utils.resource import ResourceLocation
class ItemIngredient(TypeTaggedUnion, type=None): class ItemIngredient(TypeTaggedUnion, type=None):

View file

@ -2,7 +2,7 @@ from typing import Iterator
from pydantic import field_validator from pydantic import field_validator
from hexdoc.utils.compat import HexVersion from hexdoc.core.compat import HexVersion
from .abstract_recipes import CraftingRecipe from .abstract_recipes import CraftingRecipe
from .ingredients import ItemIngredientList from .ingredients import ItemIngredientList

View file

@ -4,8 +4,10 @@ from typing import Iterator, Self
from pydantic import Field from pydantic import Field
from hexdoc.utils import HexdocModel, LoaderContext, ResourceLocation from hexdoc.core.loader import LoaderContext
from hexdoc.utils.deserialize import decode_json_dict from hexdoc.core.resource import ResourceLocation
from hexdoc.model import HexdocModel
from hexdoc.utils.deserialize.json import decode_json_dict
class OptionalTagValue(HexdocModel, frozen=True): class OptionalTagValue(HexdocModel, frozen=True):

View file

@ -0,0 +1,8 @@
__all__ = [
"HexdocModel",
"ValidationContext",
"DEFAULT_CONFIG",
"init_context",
]
from .base import DEFAULT_CONFIG, HexdocModel, ValidationContext, init_context

View file

@ -3,7 +3,7 @@ from __future__ import annotations
from contextvars import ContextVar from contextvars import ContextVar
from typing import TYPE_CHECKING, Any, Self, dataclass_transform from typing import TYPE_CHECKING, Any, Self, dataclass_transform
from pydantic import BaseModel, ConfigDict, model_validator from pydantic import BaseModel, ConfigDict
from pydantic.config import ConfigDict from pydantic.config import ConfigDict
from hexdoc.utils.contextmanagers import set_contextvar from hexdoc.utils.contextmanagers import set_contextvar
@ -80,19 +80,3 @@ class HexdocModel(HexdocBaseModel):
context: ValidationContext | None = None, context: ValidationContext | None = None,
) -> Self: ) -> Self:
... ...
@dataclass_transform()
class StripHiddenModel(HexdocModel):
"""Base model which removes all keys starting with _ before validation."""
@model_validator(mode="before")
def _pre_root_strip_hidden(cls, values: Any) -> Any:
if not isinstance(values, dict):
return values
return {
key: value
for key, value in values.items()
if not (isinstance(key, str) and key.startswith("_"))
}

View file

@ -7,10 +7,12 @@ from typing import Any, Self, dataclass_transform
from pydantic import ValidationInfo, model_validator from pydantic import ValidationInfo, model_validator
from pydantic.functional_validators import ModelWrapValidatorHandler from pydantic.functional_validators import ModelWrapValidatorHandler
from .deserialize import JSONDict, cast_or_raise from hexdoc.core.loader import LoaderContext, ModResourceLoader
from .model import HexdocModel, ValidationContext from hexdoc.core.resource import ItemStack, PathResourceDir, ResourceLocation
from .resource import ItemStack, PathResourceDir, ResourceLocation from hexdoc.utils.deserialize import cast_or_raise
from .resource_loader import LoaderContext, ModResourceLoader from hexdoc.utils.deserialize.json import JSONDict
from .base import HexdocModel, ValidationContext
@dataclass_transform() @dataclass_transform()

View file

@ -6,11 +6,11 @@ import more_itertools
from pydantic import ValidationInfo, model_validator from pydantic import ValidationInfo, model_validator
from pydantic.functional_validators import ModelWrapValidatorHandler from pydantic.functional_validators import ModelWrapValidatorHandler
from hexdoc.core.resource import ResourceLocation
from hexdoc.plugin.manager import PluginManagerContext from hexdoc.plugin.manager import PluginManagerContext
from hexdoc.utils.deserialize import cast_or_raise from hexdoc.utils.deserialize import cast_or_raise
from .model import HexdocModel from .base import HexdocModel
from .resource import ResourceLocation
class NoValueType(Enum): class NoValueType(Enum):

View file

@ -2,20 +2,15 @@ from typing import Any, Literal, Self
from pydantic import Field, ValidationInfo, field_validator, model_validator from pydantic import Field, ValidationInfo, field_validator, model_validator
from hexdoc.core.compat import HexVersion
from hexdoc.core.loader import ModResourceLoader
from hexdoc.core.resource import ItemStack, ResLoc, ResourceLocation
from hexdoc.minecraft import I18n, LocalizedStr from hexdoc.minecraft import I18n, LocalizedStr
from hexdoc.minecraft.i18n import I18nContext from hexdoc.minecraft.i18n import I18nContext
from hexdoc.model import HexdocModel
from hexdoc.patchouli.text.formatting import BookLinkBases from hexdoc.patchouli.text.formatting import BookLinkBases
from hexdoc.utils import (
Color,
HexdocModel,
ItemStack,
ModResourceLoader,
ResLoc,
ResourceLocation,
)
from hexdoc.utils.compat import HexVersion
from hexdoc.utils.deserialize import cast_or_raise from hexdoc.utils.deserialize import cast_or_raise
from hexdoc.utils.types import sorted_dict from hexdoc.utils.types import Color, sorted_dict
from .book_context import BookContext from .book_context import BookContext
from .category import Category from .category import Category

View file

@ -2,11 +2,10 @@ from typing import Self
from pydantic import Field, model_validator from pydantic import Field, model_validator
from hexdoc.core.metadata import MetadataContext
from hexdoc.core.resource import PathResourceDir, ResourceLocation
from hexdoc.minecraft import Tag from hexdoc.minecraft import Tag
from hexdoc.plugin.manager import PluginManagerContext from hexdoc.plugin.manager import PluginManagerContext
from hexdoc.utils import ResourceLocation
from hexdoc.utils.metadata import MetadataContext
from hexdoc.utils.resource import PathResourceDir
from .text.formatting import FormattingContext from .text.formatting import FormattingContext

View file

@ -2,9 +2,10 @@ from typing import Self
from pydantic import Field from pydantic import Field
from hexdoc.core.loader import LoaderContext
from hexdoc.core.resource import ItemStack, ResourceLocation
from hexdoc.minecraft import LocalizedStr from hexdoc.minecraft import LocalizedStr
from hexdoc.utils import ItemStack, LoaderContext, ResourceLocation from hexdoc.model.inline import IDModel
from hexdoc.utils.resource_model import IDModel
from hexdoc.utils.types import Sortable, sorted_dict from hexdoc.utils.types import Sortable, sorted_dict
from .entry import Entry from .entry import Entry

View file

@ -2,14 +2,14 @@ from typing import Iterable, Iterator
from pydantic import Field, ValidationInfo, model_validator from pydantic import Field, ValidationInfo, model_validator
from hexdoc.core.resource import ItemStack, ResourceLocation
from hexdoc.minecraft import LocalizedStr from hexdoc.minecraft import LocalizedStr
from hexdoc.minecraft.recipe.abstract_recipes import CraftingRecipe from hexdoc.minecraft.recipe.abstract_recipes import CraftingRecipe
from hexdoc.model.inline import IDModel
from hexdoc.patchouli.page.abstract_pages import PageWithTitle from hexdoc.patchouli.page.abstract_pages import PageWithTitle
from hexdoc.patchouli.text.formatting import FormatTree from hexdoc.patchouli.text.formatting import FormatTree
from hexdoc.utils import Color, ItemStack, ResourceLocation
from hexdoc.utils.deserialize import cast_or_raise from hexdoc.utils.deserialize import cast_or_raise
from hexdoc.utils.resource_model import IDModel from hexdoc.utils.types import Color, Sortable
from hexdoc.utils.types import Sortable
from .book_context import BookContext from .book_context import BookContext
from .page.pages import CraftingPage, Page from .page.pages import CraftingPage, Page

View file

@ -3,8 +3,9 @@ from typing import Any, ClassVar, Self
from pydantic import model_validator from pydantic import model_validator
from pydantic.functional_validators import ModelWrapValidatorHandler from pydantic.functional_validators import ModelWrapValidatorHandler
from hexdoc.core.resource import ResourceLocation
from hexdoc.minecraft import LocalizedStr from hexdoc.minecraft import LocalizedStr
from hexdoc.utils import ResourceLocation, TypeTaggedUnion from hexdoc.model.tagged_union import TypeTaggedUnion
from ..text import FormatTree from ..text import FormatTree

View file

@ -2,10 +2,10 @@ from typing import Any, Self
from pydantic import model_validator from pydantic import model_validator
from hexdoc.core.resource import Entity, ItemStack, ResourceLocation
from hexdoc.minecraft import LocalizedStr from hexdoc.minecraft import LocalizedStr
from hexdoc.minecraft.assets.textures import ItemWithTexture, Texture from hexdoc.minecraft.assets.textures import ItemWithTexture, Texture
from hexdoc.minecraft.recipe import CraftingRecipe from hexdoc.minecraft.recipe import CraftingRecipe
from hexdoc.utils import Entity, ItemStack, ResourceLocation
from ..text import FormatTree from ..text import FormatTree
from .abstract_pages import Page, PageWithText, PageWithTitle from .abstract_pages import Page, PageWithText, PageWithTitle

View file

@ -1,9 +1,6 @@
__all__ = [ __all__ = [
"FormatTree", "FormatTree",
"HTMLElement",
"HTMLStream",
"DEFAULT_MACROS", "DEFAULT_MACROS",
] ]
from .formatting import DEFAULT_MACROS, FormatTree from .formatting import DEFAULT_MACROS, FormatTree
from .html import HTMLElement, HTMLStream

View file

@ -12,12 +12,12 @@ from pydantic import Field, ValidationInfo, model_validator
from pydantic.dataclasses import dataclass from pydantic.dataclasses import dataclass
from pydantic.functional_validators import ModelWrapValidatorHandler from pydantic.functional_validators import ModelWrapValidatorHandler
from hexdoc.core.loader import LoaderContext
from hexdoc.core.resource import ResourceLocation
from hexdoc.minecraft import LocalizedStr from hexdoc.minecraft import LocalizedStr
from hexdoc.minecraft.i18n import I18n, I18nContext from hexdoc.minecraft.i18n import I18n, I18nContext
from hexdoc.utils import DEFAULT_CONFIG, HexdocModel from hexdoc.model import DEFAULT_CONFIG, HexdocModel
from hexdoc.utils.deserialize import cast_or_raise from hexdoc.utils.deserialize import cast_or_raise
from hexdoc.utils.resource import ResourceLocation
from hexdoc.utils.resource_loader import LoaderContext
from hexdoc.utils.types import TryGetEnum from hexdoc.utils.types import TryGetEnum
from .html import HTMLElement, HTMLStream from .html import HTMLElement, HTMLStream

View file

@ -6,7 +6,7 @@ from typing import Callable, Generic, Iterator, ParamSpec, Sequence, TypeVar
import pluggy import pluggy
from hexdoc.utils.model import ValidationContext from hexdoc.model import ValidationContext
from .specs import HEXDOC_PROJECT_NAME, HookPackages, PluginSpec from .specs import HEXDOC_PROJECT_NAME, HookPackages, PluginSpec

View file

@ -1,32 +0,0 @@
__all__ = [
"HexdocModel",
"InternallyTaggedUnion",
"Color",
"ValidationContext",
"DEFAULT_CONFIG",
"NoValue",
"NoValueType",
"TagValue",
"Properties",
"Entity",
"ItemStack",
"ResLoc",
"ResourceLocation",
"ModResourceLoader",
"TypeTaggedUnion",
"LoaderContext",
"init_context",
]
from .model import DEFAULT_CONFIG, HexdocModel, ValidationContext, init_context
from .properties import Properties
from .resource import Entity, ItemStack, ResLoc, ResourceLocation
from .resource_loader import LoaderContext, ModResourceLoader
from .tagged_union import (
InternallyTaggedUnion,
NoValue,
NoValueType,
TagValue,
TypeTaggedUnion,
)
from .types import Color

View file

@ -4,7 +4,7 @@ from typing import Annotated
from pydantic import AfterValidator from pydantic import AfterValidator
from hexdoc.utils.contextmanagers import set_contextvar from .contextmanagers import set_contextvar
_relative_path_root = ContextVar[Path]("_relative_path_root") _relative_path_root = ContextVar[Path]("_relative_path_root")

View file

@ -1,116 +0,0 @@
import logging
import re
from typing import Any, TypeGuard, TypeVar, get_origin
import pyjson5
_T = TypeVar("_T")
_T_cov = TypeVar("_T_cov", covariant=True)
_DEFAULT_MESSAGE_SHORT = "Expected any of {expected}, got {actual}"
_DEFAULT_MESSAGE_LONG = "Expected any of {expected}, got {actual}: {value}"
def isinstance_or_raise(
val: Any,
class_or_tuple: type[_T] | tuple[type[_T], ...],
message: str | None = None,
) -> TypeGuard[_T]:
"""Usage: `assert isinstance_or_raise(val, str)`
message placeholders: `{expected}`, `{actual}`, `{value}`
"""
# convert generic types into the origin type
if not isinstance(class_or_tuple, tuple):
class_or_tuple = (class_or_tuple,)
ungenericed_classes = tuple(get_origin(t) or t for t in class_or_tuple)
if not isinstance(val, ungenericed_classes):
# just in case the caller messed up the message formatting
subs = {
"expected": list(class_or_tuple),
"actual": type(val),
"value": val,
}
if logging.getLogger(__name__).getEffectiveLevel() >= logging.WARNING:
default_message = _DEFAULT_MESSAGE_SHORT
else:
default_message = _DEFAULT_MESSAGE_LONG
if message is None:
raise TypeError(default_message.format(**subs))
try:
raise TypeError(message.format(**subs))
except KeyError:
raise TypeError(default_message.format(**subs))
return True
def cast_or_raise(
val: Any,
class_or_tuple: type[_T] | tuple[type[_T], ...],
message: str | None = None,
) -> _T:
assert isinstance_or_raise(val, class_or_tuple, message)
return val
JSONDict = dict[str, "JSONValue"]
JSONValue = JSONDict | list["JSONValue"] | str | int | float | bool | None
def decode_json_dict(data: str | bytes) -> JSONDict:
match data:
case str():
decoded = pyjson5.decode(data)
case _:
decoded = pyjson5.decode_utf8(data)
assert isinstance_or_raise(decoded, dict)
return decoded
# implement pkpcpbp's flattening in python
# https://github.com/gamma-delta/PKPCPBP/blob/786194a590f/src/main/java/at/petrak/pkpcpbp/filters/JsonUtil.java
def decode_and_flatten_json_dict(data: str) -> dict[str, str]:
# replace `\<LF> foobar` with `\<LF>foobar`
data = re.sub(r"\\\n\s*", "\\\n", data)
# decode and flatten
decoded = decode_json_dict(data)
return _flatten_inner(decoded, "")
def _flatten_inner(obj: JSONDict, prefix: str) -> dict[str, str]:
out: dict[str, str] = {}
for key_stub, value in obj.items():
if not prefix:
key = key_stub
elif not key_stub:
key = prefix
elif prefix[-1] in ":_-/":
key = prefix + key_stub
else:
key = f"{prefix}.{key_stub}"
match value:
case dict():
_update_disallow_duplicates(out, _flatten_inner(value, key))
case str():
_update_disallow_duplicates(out, {key: value})
case _:
raise TypeError(value)
return out
def _update_disallow_duplicates(base: dict[str, _T_cov], new: dict[str, _T_cov]):
for key, value in new.items():
if key in base:
raise ValueError(f"Duplicate key {key}\nold=`{base[key]}`\nnew=`{value}`")
base[key] = value

View file

@ -0,0 +1,6 @@
__all__ = [
"cast_or_raise",
"isinstance_or_raise",
]
from .assertions import cast_or_raise, isinstance_or_raise

View file

@ -0,0 +1,55 @@
import logging
from typing import Any, TypeGuard, TypeVar, get_origin
_T = TypeVar("_T")
_DEFAULT_MESSAGE_SHORT = "Expected any of {expected}, got {actual}"
_DEFAULT_MESSAGE_LONG = "Expected any of {expected}, got {actual}: {value}"
def isinstance_or_raise(
val: Any,
class_or_tuple: type[_T] | tuple[type[_T], ...],
message: str | None = None,
) -> TypeGuard[_T]:
"""Usage: `assert isinstance_or_raise(val, str)`
message placeholders: `{expected}`, `{actual}`, `{value}`
"""
# convert generic types into the origin type
if not isinstance(class_or_tuple, tuple):
class_or_tuple = (class_or_tuple,)
ungenericed_classes = tuple(get_origin(t) or t for t in class_or_tuple)
if not isinstance(val, ungenericed_classes):
# just in case the caller messed up the message formatting
subs = {
"expected": list(class_or_tuple),
"actual": type(val),
"value": val,
}
if logging.getLogger(__name__).getEffectiveLevel() >= logging.WARNING:
default_message = _DEFAULT_MESSAGE_SHORT
else:
default_message = _DEFAULT_MESSAGE_LONG
if message is None:
raise TypeError(default_message.format(**subs))
try:
raise TypeError(message.format(**subs))
except KeyError:
raise TypeError(default_message.format(**subs))
return True
def cast_or_raise(
val: Any,
class_or_tuple: type[_T] | tuple[type[_T], ...],
message: str | None = None,
) -> _T:
assert isinstance_or_raise(val, class_or_tuple, message)
return val

View file

@ -0,0 +1,64 @@
import re
from typing import TypeVar
import pyjson5
from .assertions import isinstance_or_raise
_T_co = TypeVar("_T_co", covariant=True)
JSONDict = dict[str, "JSONValue"]
JSONValue = JSONDict | list["JSONValue"] | str | int | float | bool | None
def decode_json_dict(data: str | bytes) -> JSONDict:
match data:
case str():
decoded = pyjson5.decode(data)
case _:
decoded = pyjson5.decode_utf8(data)
assert isinstance_or_raise(decoded, dict)
return decoded
# implement pkpcpbp's flattening in python
# https://github.com/gamma-delta/PKPCPBP/blob/786194a590f/src/main/java/at/petrak/pkpcpbp/filters/JsonUtil.java
def decode_and_flatten_json_dict(data: str) -> dict[str, str]:
# replace `\<LF> foobar` with `\<LF>foobar`
data = re.sub(r"\\\n\s*", "\\\n", data)
# decode and flatten
decoded = decode_json_dict(data)
return _flatten_inner(decoded, "")
def _flatten_inner(obj: JSONDict, prefix: str) -> dict[str, str]:
out: dict[str, str] = {}
for key_stub, value in obj.items():
if not prefix:
key = key_stub
elif not key_stub:
key = prefix
elif prefix[-1] in ":_-/":
key = prefix + key_stub
else:
key = f"{prefix}.{key_stub}"
match value:
case dict():
_update_disallow_duplicates(out, _flatten_inner(value, key))
case str():
_update_disallow_duplicates(out, {key: value})
case _:
raise TypeError(value)
return out
def _update_disallow_duplicates(base: dict[str, _T_co], new: dict[str, _T_co]):
for key, value in new.items():
if key in base:
raise ValueError(f"Duplicate key {key}\nold=`{base[key]}`\nnew=`{value}`")
base[key] = value

View file

@ -4,7 +4,7 @@ import tomllib
from pathlib import Path from pathlib import Path
from typing import Callable, TypeVar from typing import Callable, TypeVar
from .deserialize import cast_or_raise from .assertions import cast_or_raise
# TODO: there's (figuratively) literally no comments in this file # TODO: there's (figuratively) literally no comments in this file

View file

View file

@ -0,0 +1,20 @@
from jinja2 import nodes
from jinja2.ext import Extension
from jinja2.parser import Parser
from markupsafe import Markup
# https://stackoverflow.com/a/64392515
class IncludeRawExtension(Extension):
tags = {"include_raw"}
def parse(self, parser: Parser) -> nodes.Node:
lineno = parser.stream.expect("name:include_raw").lineno
template = parser.parse_expression()
result = self.call_method("_render", [template], lineno=lineno)
return nodes.Output([result], lineno=lineno)
def _render(self, filename: str) -> Markup:
assert self.environment.loader is not None
source = self.environment.loader.get_source(self.environment, filename)
return Markup(source[0])

View file

@ -1,38 +1,17 @@
from typing import Any from typing import Any
from jinja2 import nodes, pass_context from jinja2 import pass_context
from jinja2.ext import Extension
from jinja2.parser import Parser
from jinja2.runtime import Context from jinja2.runtime import Context
from markupsafe import Markup from markupsafe import Markup
from pydantic import ConfigDict, validate_call from pydantic import ConfigDict, validate_call
from hexdoc.core.properties import Properties
from hexdoc.core.resource import ResourceLocation
from hexdoc.minecraft import I18n, LocalizedStr from hexdoc.minecraft import I18n, LocalizedStr
from hexdoc.minecraft.assets.textures import Texture from hexdoc.minecraft.assets import Texture
from hexdoc.patchouli import Book, FormatTree from hexdoc.patchouli import Book, FormatTree
from hexdoc.patchouli.book import Book from hexdoc.patchouli.text.formatting import BookLinkBases, HTMLStream
from hexdoc.patchouli.text import HTMLStream from hexdoc.utils.deserialize import cast_or_raise
from hexdoc.patchouli.text.formatting import BookLinkBases, FormatTree
from hexdoc.utils.resource import ResourceLocation
from . import Properties
from .deserialize import cast_or_raise
# https://stackoverflow.com/a/64392515
class IncludeRawExtension(Extension):
tags = {"include_raw"}
def parse(self, parser: Parser) -> nodes.Node:
lineno = parser.stream.expect("name:include_raw").lineno
template = parser.parse_expression()
result = self.call_method("_render", [template], lineno=lineno)
return nodes.Output([result], lineno=lineno)
def _render(self, filename: str) -> Markup:
assert self.environment.loader is not None
source = self.environment.loader.get_source(self.environment, filename)
return Markup(source[0])
@pass_context @pass_context

View file

@ -6,7 +6,7 @@ from typing import Any, Mapping, Protocol, TypeVar
from pydantic import field_validator, model_validator from pydantic import field_validator, model_validator
from pydantic.dataclasses import dataclass from pydantic.dataclasses import dataclass
from .model import DEFAULT_CONFIG from hexdoc.model import DEFAULT_CONFIG
_T = TypeVar("_T") _T = TypeVar("_T")

View file

@ -1,6 +1,6 @@
import pytest import pytest
from hexdoc.utils import ItemStack, ResLoc, ResourceLocation from hexdoc.core.resource import ItemStack, ResLoc, ResourceLocation
resource_locations: list[tuple[str, ResourceLocation, str]] = [ resource_locations: list[tuple[str, ResourceLocation, str]] = [
( (

View file

@ -2,6 +2,7 @@
from argparse import Namespace from argparse import Namespace
from typing import cast from typing import cast
from hexdoc.core.resource import ResourceLocation
from hexdoc.minecraft.i18n import I18n from hexdoc.minecraft.i18n import I18n
from hexdoc.patchouli.text import DEFAULT_MACROS, FormatTree from hexdoc.patchouli.text import DEFAULT_MACROS, FormatTree
from hexdoc.patchouli.text.formatting import ( from hexdoc.patchouli.text.formatting import (
@ -12,8 +13,7 @@ from hexdoc.patchouli.text.formatting import (
ParagraphStyle, ParagraphStyle,
SpecialStyleType, SpecialStyleType,
) )
from hexdoc.utils.jinja_extensions import hexdoc_block from hexdoc.utils.jinja.macros import hexdoc_block
from hexdoc.utils.resource import ResourceLocation
def format_with_mocks(test_str: str, macros: dict[str, str] = {}): def format_with_mocks(test_str: str, macros: dict[str, str] = {}):