Refactor most of the core logic out of utils
This commit is contained in:
parent
5b26c1d549
commit
98a68e1f15
50 changed files with 297 additions and 289 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
0
doc/src/hexdoc/core/__init__.py
Normal file
0
doc/src/hexdoc/core/__init__.py
Normal 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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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", ""]
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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,
|
||||||
|
)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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="
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
8
doc/src/hexdoc/model/__init__.py
Normal file
8
doc/src/hexdoc/model/__init__.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
__all__ = [
|
||||||
|
"HexdocModel",
|
||||||
|
"ValidationContext",
|
||||||
|
"DEFAULT_CONFIG",
|
||||||
|
"init_context",
|
||||||
|
]
|
||||||
|
|
||||||
|
from .base import DEFAULT_CONFIG, HexdocModel, ValidationContext, init_context
|
|
@ -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("_"))
|
|
||||||
}
|
|
|
@ -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()
|
|
@ -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):
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
|
@ -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")
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
6
doc/src/hexdoc/utils/deserialize/__init__.py
Normal file
6
doc/src/hexdoc/utils/deserialize/__init__.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
__all__ = [
|
||||||
|
"cast_or_raise",
|
||||||
|
"isinstance_or_raise",
|
||||||
|
]
|
||||||
|
|
||||||
|
from .assertions import cast_or_raise, isinstance_or_raise
|
55
doc/src/hexdoc/utils/deserialize/assertions.py
Normal file
55
doc/src/hexdoc/utils/deserialize/assertions.py
Normal 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
|
64
doc/src/hexdoc/utils/deserialize/json.py
Normal file
64
doc/src/hexdoc/utils/deserialize/json.py
Normal 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
|
|
@ -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
|
||||||
|
|
0
doc/src/hexdoc/utils/jinja/__init__.py
Normal file
0
doc/src/hexdoc/utils/jinja/__init__.py
Normal file
20
doc/src/hexdoc/utils/jinja/extensions.py
Normal file
20
doc/src/hexdoc/utils/jinja/extensions.py
Normal 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])
|
|
@ -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
|
|
@ -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")
|
||||||
|
|
||||||
|
|
|
@ -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]] = [
|
||||||
(
|
(
|
|
@ -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] = {}):
|
||||||
|
|
Loading…
Reference in a new issue