The great refactoring (move to hexdoc package and add reexports)
This commit is contained in:
parent
2960568e91
commit
03e7683ae1
53 changed files with 475 additions and 404 deletions
36
.vscode/settings.json
vendored
36
.vscode/settings.json
vendored
|
@ -1,36 +0,0 @@
|
||||||
{
|
|
||||||
"[python]": {
|
|
||||||
"editor.formatOnSave": true,
|
|
||||||
"editor.codeActionsOnSave": {
|
|
||||||
"source.organizeImports": true,
|
|
||||||
},
|
|
||||||
"editor.rulers": [88],
|
|
||||||
},
|
|
||||||
"[html][jinja-html]": {
|
|
||||||
"editor.rulers": [120],
|
|
||||||
},
|
|
||||||
"python.formatting.provider": "black",
|
|
||||||
"python.analysis.typeCheckingMode": "strict", // god save us
|
|
||||||
"python.analysis.diagnosticSeverityOverrides": {
|
|
||||||
"reportMissingParameterType": "error",
|
|
||||||
"reportUnknownParameterType": "error",
|
|
||||||
"reportUnknownArgumentType": "warning",
|
|
||||||
"reportUnknownLambdaType": "warning",
|
|
||||||
"reportUnknownVariableType": "none",
|
|
||||||
"reportUnknownMemberType": "warning",
|
|
||||||
"reportUnnecessaryComparison": "warning",
|
|
||||||
"reportMissingTypeArgument": "warning",
|
|
||||||
"reportUnusedImport": "information",
|
|
||||||
"reportPrivateUsage": "warning",
|
|
||||||
"reportUnnecessaryIsInstance": "information",
|
|
||||||
},
|
|
||||||
"python.analysis.diagnosticMode": "workspace",
|
|
||||||
"python.languageServer": "Pylance",
|
|
||||||
"python.testing.cwd": "${workspaceFolder}/doc",
|
|
||||||
"python.testing.unittestEnabled": false,
|
|
||||||
"python.testing.pytestEnabled": true,
|
|
||||||
"python.analysis.extraPaths": ["${workspaceFolder}/doc/src", "${workspaceFolder}/doc/test"],
|
|
||||||
"isort.args": [
|
|
||||||
"--settings", "doc/pyproject.toml"
|
|
||||||
]
|
|
||||||
}
|
|
11
doc/.vscode/extensions.json
vendored
Normal file
11
doc/.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
// List of extensions which should be recommended for users of this workspace.
|
||||||
|
"recommendations": [
|
||||||
|
"ms-python.vscode-pylance",
|
||||||
|
"ms-python.isort",
|
||||||
|
],
|
||||||
|
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||||
|
"unwantedRecommendations": [
|
||||||
|
"ms-python.black-formatter",
|
||||||
|
]
|
||||||
|
}
|
|
@ -17,7 +17,7 @@
|
||||||
"type": "python",
|
"type": "python",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"cwd": "${workspaceFolder}/doc",
|
"cwd": "${workspaceFolder}/doc",
|
||||||
"module": "hexcasting.scripts.main",
|
"module": "hexdoc",
|
||||||
"args": [
|
"args": [
|
||||||
"properties.toml",
|
"properties.toml",
|
||||||
],
|
],
|
18
doc/.vscode/settings.json
vendored
Normal file
18
doc/.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"[python]": {
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.organizeImports": true,
|
||||||
|
},
|
||||||
|
"editor.rulers": [88],
|
||||||
|
},
|
||||||
|
"python.formatting.provider": "black",
|
||||||
|
"isort.importStrategy": "fromEnvironment",
|
||||||
|
"python.languageServer": "Pylance",
|
||||||
|
"python.analysis.diagnosticMode": "workspace",
|
||||||
|
"python.testing.unittestEnabled": false,
|
||||||
|
"python.testing.pytestEnabled": true,
|
||||||
|
"[html][jinja-html]": {
|
||||||
|
"editor.rulers": [120],
|
||||||
|
},
|
||||||
|
}
|
|
@ -51,12 +51,8 @@ package = "{src}/main/java/at/petrak/hexcasting"
|
||||||
resources = "{src}/main/resources"
|
resources = "{src}/main/resources"
|
||||||
generated = "{src}/generated/resources"
|
generated = "{src}/generated/resources"
|
||||||
pattern_stubs = [
|
pattern_stubs = [
|
||||||
# these are tables so we have the option to add extra per-stub configs (eg. regex)
|
"{package}/common/casting/RegisterPatterns.java",
|
||||||
# NOTE: each ^ is like ../ in a file path (^key and ^.key are both valid)
|
"{package}/interop/pehkui/PehkuiInterop.java",
|
||||||
# the parent of an item in an array is the table containing the array, not the array
|
|
||||||
# so in this case, {^.package} is common.package
|
|
||||||
{ file = "{^.package}/common/casting/RegisterPatterns.java" },
|
|
||||||
{ file = "{^.package}/interop/pehkui/PehkuiInterop.java" },
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[fabric]
|
[fabric]
|
||||||
|
@ -65,7 +61,7 @@ package = "{src}/main/java/at/petrak/hexcasting/fabric"
|
||||||
resources = "{src}/main/resources"
|
resources = "{src}/main/resources"
|
||||||
generated = "{src}/generated/resources"
|
generated = "{src}/generated/resources"
|
||||||
pattern_stubs = [
|
pattern_stubs = [
|
||||||
{ file = "{^.package}/interop/gravity/GravityApiInterop.java" },
|
"{package}/interop/gravity/GravityApiInterop.java",
|
||||||
]
|
]
|
||||||
|
|
||||||
[forge]
|
[forge]
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
# project metadata
|
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["hatchling"]
|
requires = ["hatchling"]
|
||||||
build-backend = "hatchling.build"
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "HexDoc" # TODO: i'm pretty sure i had funnier ideas than this
|
name = "HexDoc"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = [
|
authors = [
|
||||||
{ name="Alwinfy" },
|
{ name="Alwinfy" },
|
||||||
|
@ -16,45 +15,108 @@ requires-python = ">=3.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"typing_extensions~=4.7.0",
|
"typing_extensions~=4.7.0",
|
||||||
"typed-argument-parser~=1.8.0",
|
"typed-argument-parser~=1.8.0",
|
||||||
"pydantic==2.0",
|
"pydantic~=2.0",
|
||||||
|
"Jinja2~=3.1.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
dev = [
|
||||||
|
"black==23.7.0",
|
||||||
|
"isort==5.12.0",
|
||||||
|
"pytest==7.3.1",
|
||||||
|
"syrupy==4.0.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
|
hexdoc = "hexdoc.scripts.hexdoc:main"
|
||||||
|
|
||||||
[project.entry-points."hexdoc.Page"]
|
[project.entry-points."hexdoc.Page"]
|
||||||
hexdoc-patchouli = "patchouli.page.pages"
|
hexdoc-patchouli = "hexdoc.patchouli.page.pages"
|
||||||
hexdoc-hexcasting = "hexcasting.hex_pages"
|
hexdoc-hexcasting = "hexdoc.hexcasting.page.hex_pages"
|
||||||
hexdoc-abstract-hexcasting = "hexcasting.abstract_hex_pages"
|
|
||||||
|
|
||||||
[project.entry-points."hexdoc.Recipe"]
|
[project.entry-points."hexdoc.Recipe"]
|
||||||
hexdoc-minecraft = "minecraft.recipe.recipes"
|
hexdoc-minecraft = "hexdoc.minecraft.recipe.recipes"
|
||||||
hexdoc-hexcasting = "hexcasting.hex_recipes"
|
hexdoc-hexcasting = "hexdoc.hexcasting.hex_recipes"
|
||||||
|
|
||||||
[project.entry-points."hexdoc.ItemIngredient"]
|
[project.entry-points."hexdoc.ItemIngredient"]
|
||||||
hexdoc-minecraft = "minecraft.recipe.ingredients"
|
hexdoc-minecraft = "hexdoc.minecraft.recipe.ingredients"
|
||||||
hexdoc-hexcasting = "hexcasting.hex_recipes"
|
hexdoc-hexcasting = "hexdoc.hexcasting.hex_recipes"
|
||||||
|
|
||||||
# Hatch settings (the build backend)
|
|
||||||
|
|
||||||
[tool.hatch.metadata]
|
|
||||||
allow-direct-references = true # TODO: remove when we switch to Pydantic
|
|
||||||
|
|
||||||
[tool.hatch.build]
|
[tool.hatch.build]
|
||||||
packages = ["src/common", "src/hexcasting", "src/minecraft", "src/patchouli"]
|
packages = ["src/hexdoc"]
|
||||||
|
|
||||||
# tests, formatting, and (TODO:) type checking
|
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
addopts = [
|
addopts = ["--import-mode=importlib"]
|
||||||
"--import-mode=importlib"
|
|
||||||
]
|
|
||||||
markers = [
|
markers = [
|
||||||
"filename: name of file for fixtures to create",
|
"filename: name of file for fixtures to create",
|
||||||
"file_contents: data for fixtures to write to files",
|
"file_contents: data for fixtures to write to files",
|
||||||
"fixture_data: other misc data",
|
"fixture_data: other misc data",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
[tool.coverage.report]
|
[tool.coverage.report]
|
||||||
include_namespace_packages = true
|
include_namespace_packages = true
|
||||||
|
|
||||||
|
|
||||||
[tool.isort]
|
[tool.isort]
|
||||||
profile = "black"
|
profile = "black"
|
||||||
combine_as_imports = true
|
combine_as_imports = true
|
||||||
|
|
||||||
|
|
||||||
|
[tool.pyright]
|
||||||
|
pythonVersion = "3.11"
|
||||||
|
pythonPlatform = "All"
|
||||||
|
|
||||||
|
# mostly we use strict mode
|
||||||
|
# but pyright doesn't allow decreasing error severity in strict mode
|
||||||
|
# so we need to manually specify all of the strict mode overrides so we can do that :/
|
||||||
|
typeCheckingMode = "basic"
|
||||||
|
|
||||||
|
strictDictionaryInference = true
|
||||||
|
strictListInference = true
|
||||||
|
strictSetInference = true
|
||||||
|
|
||||||
|
reportAssertAlwaysTrue = "error"
|
||||||
|
reportConstantRedefinition = "error"
|
||||||
|
reportDeprecated = "error"
|
||||||
|
reportDuplicateImport = "error"
|
||||||
|
reportFunctionMemberAccess = "error"
|
||||||
|
reportIncompatibleMethodOverride = "error"
|
||||||
|
reportIncompatibleVariableOverride = "error"
|
||||||
|
reportIncompleteStub = "error"
|
||||||
|
reportInconsistentConstructor = "error"
|
||||||
|
reportInvalidStringEscapeSequence = "error"
|
||||||
|
reportInvalidStubStatement = "error"
|
||||||
|
reportInvalidTypeVarUse = "error"
|
||||||
|
reportMatchNotExhaustive = "error"
|
||||||
|
reportMissingParameterType = "error"
|
||||||
|
reportMissingTypeStubs = "error"
|
||||||
|
reportOverlappingOverload = "error"
|
||||||
|
reportSelfClsParameterName = "error"
|
||||||
|
reportTypeCommentUsage = "error"
|
||||||
|
reportUnknownParameterType = "error"
|
||||||
|
reportUnnecessaryCast = "error"
|
||||||
|
reportUnnecessaryContains = "error"
|
||||||
|
reportUnsupportedDunderAll = "error"
|
||||||
|
reportUntypedBaseClass = "error"
|
||||||
|
reportUntypedClassDecorator = "error"
|
||||||
|
reportUntypedFunctionDecorator = "error"
|
||||||
|
reportUntypedNamedTuple = "error"
|
||||||
|
reportUnusedClass = "error"
|
||||||
|
reportUnusedExpression = "error"
|
||||||
|
reportUnusedFunction = "error"
|
||||||
|
reportUnusedVariable = "error"
|
||||||
|
reportWildcardImportFromLibrary = "error"
|
||||||
|
|
||||||
|
reportMissingTypeArgument = "warning"
|
||||||
|
reportPrivateUsage = "warning"
|
||||||
|
reportUnknownArgumentType = "warning"
|
||||||
|
reportUnknownLambdaType = "warning"
|
||||||
|
reportUnknownMemberType = "warning"
|
||||||
|
reportUnnecessaryComparison = "warning"
|
||||||
|
reportUnnecessaryIsInstance = "warning"
|
||||||
|
reportUnusedImport = "warning"
|
||||||
|
|
||||||
|
reportUnknownVariableType = "none"
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
-e . # install package locally as editable
|
|
||||||
black==22.10.0 # formatting
|
|
||||||
isort==5.12.0 # formatting 2
|
|
||||||
pytest==7.3.1 # testing framework
|
|
||||||
syrupy==4.0.2 # snapshot tests
|
|
||||||
beautifulsoup4==4.12.2 # html pretty print so the snapshot diffs are actually usable
|
|
|
@ -1,9 +0,0 @@
|
||||||
__all__ = [
|
|
||||||
"HexBook",
|
|
||||||
"HexContext",
|
|
||||||
"AnyHexContext",
|
|
||||||
"HexBookModel",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
from .hex_book import AnyHexContext, HexBook, HexBookModel, HexContext
|
|
|
@ -1,68 +0,0 @@
|
||||||
import re
|
|
||||||
from enum import Enum
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import Annotated, Any, Generator
|
|
||||||
|
|
||||||
from pydantic import BeforeValidator
|
|
||||||
|
|
||||||
from common.model import HexDocModel
|
|
||||||
from minecraft.resource import ResourceLocation
|
|
||||||
|
|
||||||
|
|
||||||
class Direction(Enum):
|
|
||||||
NORTH_EAST = 0
|
|
||||||
EAST = 1
|
|
||||||
SOUTH_EAST = 2
|
|
||||||
SOUTH_WEST = 3
|
|
||||||
WEST = 4
|
|
||||||
NORTH_WEST = 5
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def validate(cls, value: str | int | Any):
|
|
||||||
match value:
|
|
||||||
case str():
|
|
||||||
return cls[value]
|
|
||||||
case int():
|
|
||||||
return cls(value)
|
|
||||||
case _:
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
DirectionField = Annotated[Direction, BeforeValidator(Direction.validate)]
|
|
||||||
|
|
||||||
|
|
||||||
class RawPatternInfo(HexDocModel[Any]):
|
|
||||||
startdir: DirectionField
|
|
||||||
signature: str
|
|
||||||
is_per_world: bool = False
|
|
||||||
q: int | None = None
|
|
||||||
r: int | None = None
|
|
||||||
|
|
||||||
|
|
||||||
class PatternInfo(RawPatternInfo):
|
|
||||||
id: ResourceLocation
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
return self.id.path
|
|
||||||
|
|
||||||
|
|
||||||
class PatternStubFile(HexDocModel[Any]):
|
|
||||||
file: Path
|
|
||||||
|
|
||||||
def load_patterns(
|
|
||||||
self,
|
|
||||||
modid: str,
|
|
||||||
pattern_re: re.Pattern[str],
|
|
||||||
) -> Generator[PatternInfo, None, None]:
|
|
||||||
# TODO: add Gradle task to generate json with this data. this is dumb and fragile.
|
|
||||||
|
|
||||||
pattern_data = self.file.read_text("utf-8")
|
|
||||||
for match in pattern_re.finditer(pattern_data):
|
|
||||||
signature, startdir, name, is_per_world = match.groups()
|
|
||||||
yield PatternInfo(
|
|
||||||
startdir=Direction[startdir],
|
|
||||||
signature=signature,
|
|
||||||
is_per_world=bool(is_per_world),
|
|
||||||
id=ResourceLocation(modid, name),
|
|
||||||
)
|
|
13
doc/src/hexdoc/hexcasting/__init__.py
Normal file
13
doc/src/hexdoc/hexcasting/__init__.py
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
__all__ = [
|
||||||
|
"AnyHexContext",
|
||||||
|
"HexBook",
|
||||||
|
"HexBookType",
|
||||||
|
"HexContext",
|
||||||
|
"Direction",
|
||||||
|
"PatternInfo",
|
||||||
|
"RawPatternInfo",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
from .hex_book import AnyHexContext, HexBook, HexBookType, HexContext
|
||||||
|
from .pattern import Direction, PatternInfo, RawPatternInfo
|
|
@ -1,11 +1,12 @@
|
||||||
|
from pathlib import Path
|
||||||
from typing import Any, Generic, TypeVar
|
from typing import Any, Generic, TypeVar
|
||||||
|
|
||||||
from common.model import AnyContext
|
from hexdoc.patchouli import AnyBookContext, Book, BookContext
|
||||||
from common.properties import Properties
|
from hexdoc.properties import Properties
|
||||||
from hexcasting.pattern import PatternInfo
|
from hexdoc.resource import ResourceLocation
|
||||||
from minecraft.resource import ResourceLocation
|
from hexdoc.utils import AnyContext
|
||||||
from patchouli.book import Book
|
|
||||||
from patchouli.context import AnyBookContext, BookContext
|
from .pattern import Direction, PatternInfo
|
||||||
|
|
||||||
|
|
||||||
class HexContext(BookContext):
|
class HexContext(BookContext):
|
||||||
|
@ -15,7 +16,7 @@ class HexContext(BookContext):
|
||||||
AnyHexContext = TypeVar("AnyHexContext", bound=HexContext)
|
AnyHexContext = TypeVar("AnyHexContext", bound=HexContext)
|
||||||
|
|
||||||
|
|
||||||
class HexBookModel(
|
class HexBookType(
|
||||||
Generic[AnyContext, AnyBookContext, AnyHexContext],
|
Generic[AnyContext, AnyBookContext, AnyHexContext],
|
||||||
Book[AnyHexContext, AnyHexContext],
|
Book[AnyHexContext, AnyHexContext],
|
||||||
):
|
):
|
||||||
|
@ -28,7 +29,7 @@ class HexBookModel(
|
||||||
signatures = dict[str, PatternInfo]() # just for duplicate checking
|
signatures = dict[str, PatternInfo]() # just for duplicate checking
|
||||||
for stub in props.pattern_stubs:
|
for stub in props.pattern_stubs:
|
||||||
# for each stub, load all the patterns in the file
|
# for each stub, load all the patterns in the file
|
||||||
for pattern in stub.load_patterns(props.modid, props.pattern_regex):
|
for pattern in cls.load_patterns(stub, props):
|
||||||
# check for duplicates, because why not
|
# check for duplicates, because why not
|
||||||
if duplicate := (
|
if duplicate := (
|
||||||
patterns.get(pattern.id) or signatures.get(pattern.signature)
|
patterns.get(pattern.id) or signatures.get(pattern.signature)
|
||||||
|
@ -45,5 +46,19 @@ class HexBookModel(
|
||||||
"patterns": patterns,
|
"patterns": patterns,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load_patterns(cls, path: Path, props: Properties):
|
||||||
|
# TODO: add Gradle task to generate json with this data. this is dumb and fragile.
|
||||||
|
stub_text = path.read_text("utf-8")
|
||||||
|
for match in props.pattern_regex.finditer(stub_text):
|
||||||
|
signature, startdir, name, is_per_world = match.groups()
|
||||||
|
yield PatternInfo(
|
||||||
|
startdir=Direction[startdir],
|
||||||
|
signature=signature,
|
||||||
|
is_per_world=bool(is_per_world),
|
||||||
|
id=ResourceLocation(props.modid, name),
|
||||||
|
)
|
||||||
|
|
||||||
HexBook = HexBookModel[HexContext, HexContext, HexContext]
|
|
||||||
|
# type alias for convenience
|
||||||
|
HexBook = HexBookType[HexContext, HexContext, HexContext]
|
|
@ -1,15 +1,15 @@
|
||||||
from typing import Any, Literal
|
from typing import Any, Literal
|
||||||
|
|
||||||
from common.model import HexDocModel
|
from hexdoc.minecraft import LocalizedItem, Recipe
|
||||||
from hexcasting.hex_book import HexContext
|
from hexdoc.minecraft.recipe import (
|
||||||
from minecraft.i18n import LocalizedItem
|
|
||||||
from minecraft.recipe import (
|
|
||||||
ItemIngredient,
|
ItemIngredient,
|
||||||
MinecraftItemIdIngredient,
|
MinecraftItemIdIngredient,
|
||||||
MinecraftItemTagIngredient,
|
MinecraftItemTagIngredient,
|
||||||
Recipe,
|
|
||||||
)
|
)
|
||||||
from minecraft.resource import ResourceLocation
|
from hexdoc.resource import ResourceLocation
|
||||||
|
from hexdoc.utils import HexDocModel
|
||||||
|
|
||||||
|
from .hex_book import HexContext
|
||||||
|
|
||||||
# ingredients
|
# ingredients
|
||||||
|
|
20
doc/src/hexdoc/hexcasting/page/__init__.py
Normal file
20
doc/src/hexdoc/hexcasting/page/__init__.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
__all__ = [
|
||||||
|
"PageWithOpPattern",
|
||||||
|
"PageWithPattern",
|
||||||
|
"BrainsweepPage",
|
||||||
|
"CraftingMultiPage",
|
||||||
|
"LookupPatternPage",
|
||||||
|
"ManualOpPatternPage",
|
||||||
|
"ManualPatternNosigPage",
|
||||||
|
"ManualRawPatternPage",
|
||||||
|
]
|
||||||
|
|
||||||
|
from .abstract_hex_pages import PageWithOpPattern, PageWithPattern
|
||||||
|
from .hex_pages import (
|
||||||
|
BrainsweepPage,
|
||||||
|
CraftingMultiPage,
|
||||||
|
LookupPatternPage,
|
||||||
|
ManualOpPatternPage,
|
||||||
|
ManualPatternNosigPage,
|
||||||
|
ManualRawPatternPage,
|
||||||
|
)
|
|
@ -3,12 +3,12 @@ from typing import Any, cast
|
||||||
|
|
||||||
from pydantic import ValidationInfo, model_validator
|
from pydantic import ValidationInfo, model_validator
|
||||||
|
|
||||||
from hexcasting.pattern import RawPatternInfo
|
from hexdoc.minecraft import LocalizedStr
|
||||||
from minecraft.i18n import LocalizedStr
|
from hexdoc.patchouli.page import PageWithText
|
||||||
from minecraft.resource import ResourceLocation
|
from hexdoc.resource import ResourceLocation
|
||||||
from patchouli.page import PageWithText
|
|
||||||
|
|
||||||
from .hex_book import AnyHexContext, HexContext
|
from ..hex_book import AnyHexContext, HexContext
|
||||||
|
from ..pattern import RawPatternInfo
|
||||||
|
|
||||||
|
|
||||||
# TODO: make anchor required (breaks because of Greater Sentinel)
|
# TODO: make anchor required (breaks because of Greater Sentinel)
|
|
@ -2,15 +2,14 @@ from typing import Any, cast
|
||||||
|
|
||||||
from pydantic import ValidationInfo, model_validator
|
from pydantic import ValidationInfo, model_validator
|
||||||
|
|
||||||
from minecraft.i18n import LocalizedStr
|
from hexdoc.minecraft import LocalizedStr
|
||||||
from minecraft.recipe import CraftingRecipe
|
from hexdoc.minecraft.recipe import CraftingRecipe
|
||||||
from minecraft.resource import ResourceLocation
|
from hexdoc.patchouli.page import PageWithText, PageWithTitle
|
||||||
from patchouli.page import PageWithText
|
from hexdoc.resource import ResourceLocation
|
||||||
from patchouli.page.abstract_pages import PageWithTitle
|
|
||||||
|
|
||||||
|
from ..hex_book import HexContext
|
||||||
|
from ..hex_recipes import BrainsweepRecipe
|
||||||
from .abstract_hex_pages import PageWithOpPattern, PageWithPattern
|
from .abstract_hex_pages import PageWithOpPattern, PageWithPattern
|
||||||
from .hex_book import HexContext
|
|
||||||
from .hex_recipes import BrainsweepRecipe
|
|
||||||
|
|
||||||
|
|
||||||
class LookupPatternPage(
|
class LookupPatternPage(
|
45
doc/src/hexdoc/hexcasting/pattern.py
Normal file
45
doc/src/hexdoc/hexcasting/pattern.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Annotated, Any
|
||||||
|
|
||||||
|
from pydantic import BeforeValidator
|
||||||
|
|
||||||
|
from hexdoc.resource import ResourceLocation
|
||||||
|
from hexdoc.utils import HexDocModel
|
||||||
|
|
||||||
|
|
||||||
|
class Direction(Enum):
|
||||||
|
NORTH_EAST = 0
|
||||||
|
EAST = 1
|
||||||
|
SOUTH_EAST = 2
|
||||||
|
SOUTH_WEST = 3
|
||||||
|
WEST = 4
|
||||||
|
NORTH_WEST = 5
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def validate(cls, value: str | int | Any):
|
||||||
|
match value:
|
||||||
|
case str():
|
||||||
|
return cls[value]
|
||||||
|
case int():
|
||||||
|
return cls(value)
|
||||||
|
case _:
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
DirectionField = Annotated[Direction, BeforeValidator(Direction.validate)]
|
||||||
|
|
||||||
|
|
||||||
|
class RawPatternInfo(HexDocModel[Any]):
|
||||||
|
startdir: DirectionField
|
||||||
|
signature: str
|
||||||
|
is_per_world: bool = False
|
||||||
|
q: int | None = None
|
||||||
|
r: int | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class PatternInfo(RawPatternInfo):
|
||||||
|
id: ResourceLocation
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self.id.path
|
9
doc/src/hexdoc/minecraft/__init__.py
Normal file
9
doc/src/hexdoc/minecraft/__init__.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
__all__ = [
|
||||||
|
"I18n",
|
||||||
|
"LocalizedItem",
|
||||||
|
"LocalizedStr",
|
||||||
|
"Recipe",
|
||||||
|
]
|
||||||
|
|
||||||
|
from .i18n import I18n, LocalizedItem, LocalizedStr
|
||||||
|
from .recipe import Recipe
|
|
@ -1,7 +1,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import InitVar
|
||||||
from functools import total_ordering
|
from functools import total_ordering
|
||||||
from pathlib import Path
|
|
||||||
from typing import Any, Callable, Self, cast
|
from typing import Any, Callable, Self, cast
|
||||||
|
|
||||||
from pydantic import ValidationInfo, model_validator
|
from pydantic import ValidationInfo, model_validator
|
||||||
|
@ -9,10 +9,10 @@ from pydantic.dataclasses import dataclass
|
||||||
from pydantic.functional_validators import ModelWrapValidatorHandler
|
from pydantic.functional_validators import ModelWrapValidatorHandler
|
||||||
from typing_extensions import TypedDict
|
from typing_extensions import TypedDict
|
||||||
|
|
||||||
from common.deserialize import isinstance_or_raise, load_json
|
from hexdoc.properties import Properties
|
||||||
from common.model import DEFAULT_CONFIG, HexDocModel
|
from hexdoc.resource import ItemStack, ResourceLocation
|
||||||
from common.properties import Properties
|
from hexdoc.utils import DEFAULT_CONFIG, HexDocModel
|
||||||
from minecraft.resource import ItemStack, ResourceLocation
|
from hexdoc.utils.deserialize import isinstance_or_raise, load_json
|
||||||
|
|
||||||
|
|
||||||
class I18nContext(TypedDict):
|
class I18nContext(TypedDict):
|
||||||
|
@ -94,12 +94,12 @@ class LocalizedItem(LocalizedStr):
|
||||||
class I18n:
|
class I18n:
|
||||||
"""Handles localization of strings."""
|
"""Handles localization of strings."""
|
||||||
|
|
||||||
props: Properties
|
props: InitVar[Properties]
|
||||||
enabled: bool
|
enabled: bool
|
||||||
|
|
||||||
lookup: dict[str, LocalizedStr] | None = None
|
lookup: dict[str, LocalizedStr] | None = None
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self, props: Properties):
|
||||||
# skip loading the files if we don't need to
|
# skip loading the files if we don't need to
|
||||||
self.lookup = None
|
self.lookup = None
|
||||||
if not self.enabled:
|
if not self.enabled:
|
||||||
|
@ -109,8 +109,9 @@ class I18n:
|
||||||
# TODO: load ALL of the i18n files, return dict[str, _Lookup] | None
|
# TODO: load ALL of the i18n files, return dict[str, _Lookup] | None
|
||||||
# or maybe dict[(str, str), LocalizedStr]
|
# or maybe dict[(str, str), LocalizedStr]
|
||||||
# we could also use that to ensure all i18n files have the same set of keys
|
# we could also use that to ensure all i18n files have the same set of keys
|
||||||
path = self.dir / self.props.i18n.filename
|
lang_dir = props.resources_dir / "assets" / props.modid / "lang"
|
||||||
raw_lookup = load_json(path) | (self.props.i18n.extra or {})
|
path = lang_dir / props.i18n.filename
|
||||||
|
raw_lookup = load_json(path) | (props.i18n.extra or {})
|
||||||
|
|
||||||
# validate and insert
|
# validate and insert
|
||||||
self.lookup = {}
|
self.lookup = {}
|
||||||
|
@ -121,11 +122,6 @@ class I18n:
|
||||||
value=raw_value.replace("%%", "%"),
|
value=raw_value.replace("%%", "%"),
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
|
||||||
def dir(self) -> Path:
|
|
||||||
"""eg. `resources/assets/hexcasting/lang`"""
|
|
||||||
return self.props.resources_dir / "assets" / self.props.modid / "lang"
|
|
||||||
|
|
||||||
def localize(
|
def localize(
|
||||||
self,
|
self,
|
||||||
*keys: str,
|
*keys: str,
|
|
@ -1,19 +1,19 @@
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"CraftingRecipe",
|
|
||||||
"ItemIngredient",
|
|
||||||
"MinecraftItemTagIngredient",
|
|
||||||
"MinecraftItemIdIngredient",
|
|
||||||
"ItemResult",
|
|
||||||
"Recipe",
|
"Recipe",
|
||||||
"recipes",
|
"ItemIngredient",
|
||||||
|
"ItemIngredientOrList",
|
||||||
|
"MinecraftItemIdIngredient",
|
||||||
|
"MinecraftItemTagIngredient",
|
||||||
"CraftingRecipe",
|
"CraftingRecipe",
|
||||||
"CraftingShapedRecipe",
|
"CraftingShapedRecipe",
|
||||||
"CraftingShapelessRecipe",
|
"CraftingShapelessRecipe",
|
||||||
|
"ItemResult",
|
||||||
]
|
]
|
||||||
|
|
||||||
from .abstract_recipes import Recipe
|
from .abstract_recipes import Recipe
|
||||||
from .ingredients import (
|
from .ingredients import (
|
||||||
ItemIngredient,
|
ItemIngredient,
|
||||||
|
ItemIngredientOrList,
|
||||||
MinecraftItemIdIngredient,
|
MinecraftItemIdIngredient,
|
||||||
MinecraftItemTagIngredient,
|
MinecraftItemTagIngredient,
|
||||||
)
|
)
|
|
@ -2,13 +2,12 @@ from typing import Any, Self, cast
|
||||||
|
|
||||||
from pydantic import ValidationInfo, model_validator
|
from pydantic import ValidationInfo, model_validator
|
||||||
|
|
||||||
from common.deserialize import load_json
|
from hexdoc.properties import AnyPropsContext
|
||||||
from common.tagged_union import TypeTaggedUnion
|
from hexdoc.resource import ResourceLocation, TypeTaggedUnion
|
||||||
from minecraft.resource import ResourceLocation
|
from hexdoc.utils.deserialize import load_json
|
||||||
from patchouli.context import AnyBookContext
|
|
||||||
|
|
||||||
|
|
||||||
class Recipe(TypeTaggedUnion[AnyBookContext], group="hexdoc.Recipe", type=None):
|
class Recipe(TypeTaggedUnion[AnyPropsContext], group="hexdoc.Recipe", type=None):
|
||||||
id: ResourceLocation
|
id: ResourceLocation
|
||||||
|
|
||||||
group: str | None = None
|
group: str | None = None
|
||||||
|
@ -31,7 +30,7 @@ class Recipe(TypeTaggedUnion[AnyBookContext], group="hexdoc.Recipe", type=None):
|
||||||
id = values
|
id = values
|
||||||
|
|
||||||
# load the recipe
|
# load the recipe
|
||||||
context = cast(AnyBookContext, info.context)
|
context = cast(AnyPropsContext, info.context)
|
||||||
for recipe_dir in context["props"].recipe_dirs:
|
for recipe_dir in context["props"].recipe_dirs:
|
||||||
# TODO: should this use id.namespace somewhere?
|
# TODO: should this use id.namespace somewhere?
|
||||||
path = recipe_dir / f"{id.path}.json"
|
path = recipe_dir / f"{id.path}.json"
|
23
doc/src/hexdoc/minecraft/recipe/ingredients.py
Normal file
23
doc/src/hexdoc/minecraft/recipe/ingredients.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from hexdoc.resource import ResourceLocation, TypeTaggedUnion
|
||||||
|
from hexdoc.utils import AnyContext, NoValue
|
||||||
|
|
||||||
|
|
||||||
|
class ItemIngredient(
|
||||||
|
TypeTaggedUnion[AnyContext],
|
||||||
|
group="hexdoc.ItemIngredient",
|
||||||
|
type=None,
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
ItemIngredientOrList = ItemIngredient[AnyContext] | list[ItemIngredient[AnyContext]]
|
||||||
|
|
||||||
|
|
||||||
|
class MinecraftItemIdIngredient(ItemIngredient[Any], type=NoValue):
|
||||||
|
item: ResourceLocation
|
||||||
|
|
||||||
|
|
||||||
|
class MinecraftItemTagIngredient(ItemIngredient[Any], type=NoValue):
|
||||||
|
tag: ResourceLocation
|
|
@ -1,30 +1,31 @@
|
||||||
from common.model import HexDocModel
|
from typing import Any
|
||||||
from minecraft.i18n import LocalizedItem
|
|
||||||
from patchouli.context import BookContext
|
|
||||||
|
|
||||||
|
from hexdoc.utils import HexDocModel
|
||||||
|
|
||||||
|
from ..i18n import LocalizedItem
|
||||||
from .abstract_recipes import Recipe
|
from .abstract_recipes import Recipe
|
||||||
from .ingredients import ItemIngredientOrList
|
from .ingredients import ItemIngredientOrList
|
||||||
|
|
||||||
|
|
||||||
class ItemResult(HexDocModel[BookContext]):
|
class ItemResult(HexDocModel[Any]):
|
||||||
item: LocalizedItem
|
item: LocalizedItem
|
||||||
count: int | None = None
|
count: int | None = None
|
||||||
|
|
||||||
|
|
||||||
class CraftingShapedRecipe(
|
class CraftingShapedRecipe(
|
||||||
Recipe[BookContext],
|
Recipe[Any],
|
||||||
type="minecraft:crafting_shaped",
|
type="minecraft:crafting_shaped",
|
||||||
):
|
):
|
||||||
pattern: list[str]
|
pattern: list[str]
|
||||||
key: dict[str, ItemIngredientOrList[BookContext]]
|
key: dict[str, ItemIngredientOrList[Any]]
|
||||||
result: ItemResult
|
result: ItemResult
|
||||||
|
|
||||||
|
|
||||||
class CraftingShapelessRecipe(
|
class CraftingShapelessRecipe(
|
||||||
Recipe[BookContext],
|
Recipe[Any],
|
||||||
type="minecraft:crafting_shapeless",
|
type="minecraft:crafting_shapeless",
|
||||||
):
|
):
|
||||||
ingredients: list[ItemIngredientOrList[BookContext]]
|
ingredients: list[ItemIngredientOrList[Any]]
|
||||||
result: ItemResult
|
result: ItemResult
|
||||||
|
|
||||||
|
|
16
doc/src/hexdoc/patchouli/__init__.py
Normal file
16
doc/src/hexdoc/patchouli/__init__.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
__all__ = [
|
||||||
|
"Book",
|
||||||
|
"Category",
|
||||||
|
"Entry",
|
||||||
|
"Page",
|
||||||
|
"FormatTree",
|
||||||
|
"AnyBookContext",
|
||||||
|
"BookContext",
|
||||||
|
]
|
||||||
|
|
||||||
|
from .book import Book
|
||||||
|
from .category import Category
|
||||||
|
from .entry import Entry
|
||||||
|
from .model import AnyBookContext, BookContext
|
||||||
|
from .page import Page
|
||||||
|
from .text import FormatTree
|
|
@ -2,16 +2,15 @@ from typing import Any, Generic, Literal, Self, cast
|
||||||
|
|
||||||
from pydantic import Field, ValidationInfo, model_validator
|
from pydantic import Field, ValidationInfo, model_validator
|
||||||
|
|
||||||
from common.deserialize import isinstance_or_raise, load_json
|
from hexdoc.minecraft import I18n, LocalizedStr
|
||||||
from common.model import AnyContext, HexDocModel
|
from hexdoc.properties import Properties
|
||||||
from common.properties import Properties
|
from hexdoc.resource import ItemStack, ResLoc, ResourceLocation
|
||||||
from common.types import Color
|
from hexdoc.utils import AnyContext, Color, HexDocModel
|
||||||
from minecraft.i18n import I18n, LocalizedStr
|
from hexdoc.utils.deserialize import isinstance_or_raise, load_json
|
||||||
from minecraft.resource import ItemStack, ResLoc, ResourceLocation
|
|
||||||
|
|
||||||
from .category import Category
|
from .category import Category
|
||||||
from .context import AnyBookContext, BookContext
|
|
||||||
from .entry import Entry
|
from .entry import Entry
|
||||||
|
from .model import AnyBookContext, BookContext
|
||||||
from .text import DEFAULT_MACROS, FormatTree
|
from .text import DEFAULT_MACROS, FormatTree
|
||||||
|
|
||||||
|
|
|
@ -3,17 +3,17 @@ from typing import Self
|
||||||
|
|
||||||
from pydantic import Field
|
from pydantic import Field
|
||||||
|
|
||||||
from common.properties import Properties
|
from hexdoc.minecraft import LocalizedStr
|
||||||
from common.types import Sortable, sorted_dict
|
from hexdoc.properties import Properties
|
||||||
from minecraft.i18n import LocalizedStr
|
from hexdoc.resource import ItemStack, ResourceLocation
|
||||||
from minecraft.resource import ItemStack, ResourceLocation
|
from hexdoc.utils.types import Sortable, sorted_dict
|
||||||
|
|
||||||
from .context import BookContext, BookModelFile
|
|
||||||
from .entry import Entry
|
from .entry import Entry
|
||||||
|
from .model import BookContext, BookFileModel
|
||||||
from .text import FormatTree
|
from .text import FormatTree
|
||||||
|
|
||||||
|
|
||||||
class Category(BookModelFile[BookContext, BookContext], Sortable):
|
class Category(BookFileModel[BookContext, BookContext], Sortable):
|
||||||
"""Category with pages and localizations.
|
"""Category with pages and localizations.
|
||||||
|
|
||||||
See: https://vazkiimods.github.io/Patchouli/docs/reference/category-json
|
See: https://vazkiimods.github.io/Patchouli/docs/reference/category-json
|
|
@ -3,16 +3,17 @@ from typing import cast
|
||||||
|
|
||||||
from pydantic import Field, ValidationInfo, model_validator
|
from pydantic import Field, ValidationInfo, model_validator
|
||||||
|
|
||||||
from common.properties import Properties
|
from hexdoc.minecraft import LocalizedStr
|
||||||
from common.types import Color, Sortable
|
from hexdoc.properties import Properties
|
||||||
from minecraft.i18n import LocalizedStr
|
from hexdoc.resource import ItemStack, ResourceLocation
|
||||||
from minecraft.resource import ItemStack, ResourceLocation
|
from hexdoc.utils import Color
|
||||||
|
from hexdoc.utils.types import Sortable
|
||||||
|
|
||||||
from .context import BookContext, BookModelFile
|
from .model import BookContext, BookFileModel
|
||||||
from .page import Page
|
from .page.pages import Page
|
||||||
|
|
||||||
|
|
||||||
class Entry(BookModelFile[BookContext, BookContext], Sortable):
|
class Entry(BookFileModel[BookContext, BookContext], Sortable):
|
||||||
"""Entry json file, with pages and localizations.
|
"""Entry json file, with pages and localizations.
|
||||||
|
|
||||||
See: https://vazkiimods.github.io/Patchouli/docs/reference/entry-json
|
See: https://vazkiimods.github.io/Patchouli/docs/reference/entry-json
|
|
@ -4,10 +4,11 @@ from typing import Any, Generic, TypeVar, cast, dataclass_transform
|
||||||
|
|
||||||
from pydantic import ValidationInfo, model_validator
|
from pydantic import ValidationInfo, model_validator
|
||||||
|
|
||||||
from common.model import AnyContext, HexDocModelFile
|
from hexdoc.properties import Properties
|
||||||
from common.properties import Properties
|
from hexdoc.resource import ResourceLocation
|
||||||
from minecraft.resource import ResourceLocation
|
from hexdoc.utils import AnyContext, HexDocFileModel
|
||||||
from patchouli.text import FormatContext
|
|
||||||
|
from .text.formatting import FormatContext
|
||||||
|
|
||||||
|
|
||||||
class BookContext(FormatContext):
|
class BookContext(FormatContext):
|
||||||
|
@ -18,9 +19,9 @@ AnyBookContext = TypeVar("AnyBookContext", bound=BookContext)
|
||||||
|
|
||||||
|
|
||||||
@dataclass_transform()
|
@dataclass_transform()
|
||||||
class BookModelFile(
|
class BookFileModel(
|
||||||
Generic[AnyContext, AnyBookContext],
|
Generic[AnyContext, AnyBookContext],
|
||||||
HexDocModelFile[AnyBookContext],
|
HexDocFileModel[AnyBookContext],
|
||||||
ABC,
|
ABC,
|
||||||
):
|
):
|
||||||
id: ResourceLocation
|
id: ResourceLocation
|
|
@ -2,17 +2,17 @@ __all__ = [
|
||||||
"Page",
|
"Page",
|
||||||
"PageWithText",
|
"PageWithText",
|
||||||
"PageWithTitle",
|
"PageWithTitle",
|
||||||
"CraftingPage",
|
|
||||||
"EmptyPage",
|
|
||||||
"EntityPage",
|
|
||||||
"ImagePage",
|
|
||||||
"LinkPage",
|
|
||||||
"MultiblockPage",
|
|
||||||
"QuestPage",
|
|
||||||
"RelationsPage",
|
|
||||||
"SmeltingPage",
|
|
||||||
"SpotlightPage",
|
|
||||||
"TextPage",
|
"TextPage",
|
||||||
|
"ImagePage",
|
||||||
|
"CraftingPage",
|
||||||
|
"SmeltingPage",
|
||||||
|
"MultiblockPage",
|
||||||
|
"EntityPage",
|
||||||
|
"SpotlightPage",
|
||||||
|
"LinkPage",
|
||||||
|
"RelationsPage",
|
||||||
|
"QuestPage",
|
||||||
|
"EmptyPage",
|
||||||
]
|
]
|
||||||
|
|
||||||
from .abstract_pages import Page, PageWithText, PageWithTitle
|
from .abstract_pages import Page, PageWithText, PageWithTitle
|
|
@ -3,11 +3,11 @@ 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 common.tagged_union import TagValue, TypeTaggedUnion
|
from hexdoc.minecraft import LocalizedStr
|
||||||
from minecraft.i18n import LocalizedStr
|
from hexdoc.resource import ResourceLocation, TypeTaggedUnion
|
||||||
from minecraft.resource import ResourceLocation
|
from hexdoc.utils import TagValue
|
||||||
|
|
||||||
from ..context import AnyBookContext
|
from ..model import AnyBookContext
|
||||||
from ..text import FormatTree
|
from ..text import FormatTree
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from minecraft.i18n import LocalizedItem, LocalizedStr
|
from hexdoc.minecraft import LocalizedItem, LocalizedStr
|
||||||
from minecraft.recipe import CraftingRecipe
|
from hexdoc.minecraft.recipe import CraftingRecipe
|
||||||
from minecraft.resource import Entity, ItemStack, ResourceLocation
|
from hexdoc.resource import Entity, ItemStack, ResourceLocation
|
||||||
from patchouli.context import BookContext
|
|
||||||
|
|
||||||
|
from ..model import BookContext
|
||||||
from ..text import FormatTree
|
from ..text import FormatTree
|
||||||
from .abstract_pages import Page, PageWithText, PageWithTitle
|
from .abstract_pages import Page, PageWithText, PageWithTitle
|
||||||
|
|
9
doc/src/hexdoc/patchouli/text/__init__.py
Normal file
9
doc/src/hexdoc/patchouli/text/__init__.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
__all__ = [
|
||||||
|
"FormatTree",
|
||||||
|
"HTMLElement",
|
||||||
|
"HTMLStream",
|
||||||
|
"DEFAULT_MACROS",
|
||||||
|
]
|
||||||
|
|
||||||
|
from .formatting import DEFAULT_MACROS, FormatTree
|
||||||
|
from .html import HTMLElement, HTMLStream
|
|
@ -12,10 +12,11 @@ from pydantic import 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 common.model import DEFAULT_CONFIG, HexDocModel
|
from hexdoc.minecraft import LocalizedStr
|
||||||
from common.properties import Properties
|
from hexdoc.minecraft.i18n import I18nContext
|
||||||
from common.types import TryGetEnum
|
from hexdoc.properties import PropsContext
|
||||||
from minecraft.i18n import I18nContext, LocalizedStr
|
from hexdoc.utils import DEFAULT_CONFIG, HexDocModel
|
||||||
|
from hexdoc.utils.types import TryGetEnum
|
||||||
|
|
||||||
from .html import HTMLElement, HTMLStream
|
from .html import HTMLElement, HTMLStream
|
||||||
|
|
||||||
|
@ -251,9 +252,8 @@ class _CloseTag(HexDocModel[Any], frozen=True):
|
||||||
_FORMAT_RE = re.compile(r"\$\(([^)]*)\)")
|
_FORMAT_RE = re.compile(r"\$\(([^)]*)\)")
|
||||||
|
|
||||||
|
|
||||||
class FormatContext(I18nContext):
|
class FormatContext(I18nContext, PropsContext):
|
||||||
macros: dict[str, str]
|
macros: dict[str, str]
|
||||||
props: Properties
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(config=DEFAULT_CONFIG)
|
@dataclass(config=DEFAULT_CONFIG)
|
|
@ -5,7 +5,7 @@ from dataclasses import dataclass
|
||||||
from html import escape
|
from html import escape
|
||||||
from typing import IO, Any
|
from typing import IO, Any
|
||||||
|
|
||||||
from minecraft.i18n import LocalizedStr
|
from hexdoc.minecraft import LocalizedStr
|
||||||
|
|
||||||
|
|
||||||
def attributes_to_str(attributes: dict[str, Any]):
|
def attributes_to_str(attributes: dict[str, Any]):
|
|
@ -1,6 +1,8 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Annotated, Any, Self
|
from typing import Annotated, Any, Self, TypeVar
|
||||||
|
|
||||||
from pydantic import (
|
from pydantic import (
|
||||||
AfterValidator,
|
AfterValidator,
|
||||||
|
@ -9,11 +11,11 @@ from pydantic import (
|
||||||
HttpUrl,
|
HttpUrl,
|
||||||
field_validator,
|
field_validator,
|
||||||
)
|
)
|
||||||
|
from typing_extensions import TypedDict
|
||||||
|
|
||||||
from common.model import HexDocModel
|
from hexdoc.resource import ResourceLocation
|
||||||
from common.toml_placeholders import load_toml
|
from hexdoc.utils.model import HexDocModel
|
||||||
from hexcasting.pattern import PatternStubFile
|
from hexdoc.utils.toml_placeholders import load_toml
|
||||||
from minecraft.resource import ResourceLocation
|
|
||||||
|
|
||||||
NoTrailingSlashHttpUrl = Annotated[
|
NoTrailingSlashHttpUrl = Annotated[
|
||||||
str,
|
str,
|
||||||
|
@ -27,7 +29,7 @@ class PlatformProps(HexDocModel[Any]):
|
||||||
generated: Path
|
generated: Path
|
||||||
src: Path
|
src: Path
|
||||||
package: Path
|
package: Path
|
||||||
pattern_stubs: list[PatternStubFile] | None = None
|
pattern_stubs: list[Path] | None = None
|
||||||
|
|
||||||
|
|
||||||
class I18nProps(HexDocModel[Any]):
|
class I18nProps(HexDocModel[Any]):
|
||||||
|
@ -112,7 +114,7 @@ class Properties(HexDocModel[Any]):
|
||||||
return platforms
|
return platforms
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pattern_stubs(self) -> list[PatternStubFile]:
|
def pattern_stubs(self) -> list[Path]:
|
||||||
return [
|
return [
|
||||||
stub
|
stub
|
||||||
for platform in self.platforms
|
for platform in self.platforms
|
||||||
|
@ -132,3 +134,10 @@ class Properties(HexDocModel[Any]):
|
||||||
f"default_recipe_dir must be a valid index of recipe_dirs (expected <={num_dirs - 1}, got {value})"
|
f"default_recipe_dir must be a valid index of recipe_dirs (expected <={num_dirs - 1}, got {value})"
|
||||||
)
|
)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class PropsContext(TypedDict):
|
||||||
|
props: Properties
|
||||||
|
|
||||||
|
|
||||||
|
AnyPropsContext = TypeVar("AnyPropsContext", bound=PropsContext)
|
|
@ -1,5 +1,9 @@
|
||||||
# pyright: reportPrivateUsage=false
|
# pyright: reportPrivateUsage=false
|
||||||
|
|
||||||
|
# this file is used by basically everything
|
||||||
|
# so if it's in literally any namespace, everything fucking dies from circular deps
|
||||||
|
# basically, just leave it here
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, ClassVar, Self
|
from typing import Any, ClassVar, Self
|
||||||
|
@ -8,7 +12,13 @@ from pydantic import field_validator, model_serializer, 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 common.model import DEFAULT_CONFIG
|
from hexdoc.utils import (
|
||||||
|
DEFAULT_CONFIG,
|
||||||
|
AnyContext,
|
||||||
|
InternallyTaggedUnion,
|
||||||
|
NoValueType,
|
||||||
|
TagValue,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _make_regex(count: bool = False, nbt: bool = False) -> re.Pattern[str]:
|
def _make_regex(count: bool = False, nbt: bool = False) -> re.Pattern[str]:
|
||||||
|
@ -118,3 +128,22 @@ class Entity(BaseResourceLocation, regex=_make_regex(nbt=True)):
|
||||||
if self.nbt is not None:
|
if self.nbt is not None:
|
||||||
s += self.nbt
|
s += self.nbt
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
class TypeTaggedUnion(InternallyTaggedUnion[AnyContext], key="type", value=None):
|
||||||
|
type: ResourceLocation | None
|
||||||
|
|
||||||
|
def __init_subclass__(
|
||||||
|
cls,
|
||||||
|
*,
|
||||||
|
group: str | None = None,
|
||||||
|
type: TagValue | None,
|
||||||
|
) -> None:
|
||||||
|
super().__init_subclass__(group=group, value=type)
|
||||||
|
match type:
|
||||||
|
case str():
|
||||||
|
cls.type = ResourceLocation.from_str(type)
|
||||||
|
case NoValueType():
|
||||||
|
cls.type = None
|
||||||
|
case None:
|
||||||
|
pass
|
|
@ -1,7 +1,6 @@
|
||||||
# because Tap.add_argument isn't typed, for some reason
|
# because Tap.add_argument isn't typed, for some reason
|
||||||
# pyright: reportUnknownMemberType=false
|
# pyright: reportUnknownMemberType=false
|
||||||
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from jinja2 import Environment, FileSystemLoader, StrictUndefined
|
from jinja2 import Environment, FileSystemLoader, StrictUndefined
|
||||||
|
@ -9,12 +8,9 @@ from jinja2 import Environment, FileSystemLoader, StrictUndefined
|
||||||
# from jinja2.sandbox import SandboxedEnvironment
|
# from jinja2.sandbox import SandboxedEnvironment
|
||||||
from tap import Tap
|
from tap import Tap
|
||||||
|
|
||||||
from common.jinja_extensions import IncludeRawExtension, hexdoc_block, hexdoc_wrap
|
from hexdoc.hexcasting import HexBook
|
||||||
from common.properties import Properties
|
from hexdoc.properties import Properties
|
||||||
from hexcasting.hex_book import HexBook
|
from hexdoc.utils.jinja_extensions import IncludeRawExtension, hexdoc_block, hexdoc_wrap
|
||||||
|
|
||||||
if sys.version_info < (3, 11):
|
|
||||||
raise RuntimeError("Minimum Python version: 3.11")
|
|
||||||
|
|
||||||
|
|
||||||
def strip_empty_lines(text: str) -> str:
|
def strip_empty_lines(text: str) -> str:
|
||||||
|
@ -33,7 +29,11 @@ class Args(Tap):
|
||||||
self.add_argument("-o", "--output_file", required=False)
|
self.add_argument("-o", "--output_file", required=False)
|
||||||
|
|
||||||
|
|
||||||
def main(args: Args) -> None:
|
def main(args: Args | None = None) -> None:
|
||||||
|
# allow passing Args for test cases, but parse by default
|
||||||
|
if args is None:
|
||||||
|
args = Args().parse_args()
|
||||||
|
|
||||||
# load the properties and book
|
# load the properties and book
|
||||||
props = Properties.load(args.properties_file)
|
props = Properties.load(args.properties_file)
|
||||||
book = HexBook.load(*HexBook.prepare(props))
|
book = HexBook.load(*HexBook.prepare(props))
|
||||||
|
@ -74,4 +74,4 @@ def main(args: Args) -> None:
|
||||||
|
|
||||||
# entry point: just read the CLI args and pass them to the actual logic
|
# entry point: just read the CLI args and pass them to the actual logic
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main(Args().parse_args())
|
main()
|
22
doc/src/hexdoc/utils/__init__.py
Normal file
22
doc/src/hexdoc/utils/__init__.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
__all__ = [
|
||||||
|
"HexDocModel",
|
||||||
|
"FrozenHexDocModel",
|
||||||
|
"HexDocFileModel",
|
||||||
|
"InternallyTaggedUnion",
|
||||||
|
"Color",
|
||||||
|
"AnyContext",
|
||||||
|
"DEFAULT_CONFIG",
|
||||||
|
"NoValue",
|
||||||
|
"NoValueType",
|
||||||
|
"TagValue",
|
||||||
|
]
|
||||||
|
|
||||||
|
from .model import (
|
||||||
|
DEFAULT_CONFIG,
|
||||||
|
AnyContext,
|
||||||
|
FrozenHexDocModel,
|
||||||
|
HexDocFileModel,
|
||||||
|
HexDocModel,
|
||||||
|
)
|
||||||
|
from .tagged_union import InternallyTaggedUnion, NoValue, NoValueType, TagValue
|
||||||
|
from .types import Color
|
|
@ -6,6 +6,7 @@ _T = TypeVar("_T")
|
||||||
|
|
||||||
_DEFAULT_MESSAGE = "Expected any of {expected}, got {actual}: {value}"
|
_DEFAULT_MESSAGE = "Expected any of {expected}, got {actual}: {value}"
|
||||||
|
|
||||||
|
|
||||||
# there may well be a better way to do this but i don't know what it is
|
# there may well be a better way to do this but i don't know what it is
|
||||||
def isinstance_or_raise(
|
def isinstance_or_raise(
|
||||||
val: Any,
|
val: Any,
|
|
@ -5,9 +5,9 @@ from jinja2.ext import Extension
|
||||||
from jinja2.parser import Parser
|
from jinja2.parser import Parser
|
||||||
from markupsafe import Markup
|
from markupsafe import Markup
|
||||||
|
|
||||||
from minecraft.i18n import LocalizedStr
|
from hexdoc.minecraft import LocalizedStr
|
||||||
from patchouli.text.formatting import FormatTree
|
from hexdoc.patchouli import FormatTree
|
||||||
from patchouli.text.html import HTMLStream
|
from hexdoc.patchouli.text import HTMLStream
|
||||||
|
|
||||||
|
|
||||||
# https://stackoverflow.com/a/64392515
|
# https://stackoverflow.com/a/64392515
|
|
@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Any, Generic, Self, TypeVar, dataclass_transfo
|
||||||
from pydantic import BaseModel, ConfigDict
|
from pydantic import BaseModel, ConfigDict
|
||||||
from typing_extensions import TypedDict
|
from typing_extensions import TypedDict
|
||||||
|
|
||||||
from common.deserialize import load_json
|
from .deserialize import load_json
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from pydantic.root_model import Model
|
from pydantic.root_model import Model
|
||||||
|
@ -53,7 +53,7 @@ class FrozenHexDocModel(Generic[AnyContext], HexDocModel[AnyContext]):
|
||||||
|
|
||||||
|
|
||||||
@dataclass_transform()
|
@dataclass_transform()
|
||||||
class HexDocModelFile(HexDocModel[AnyContext]):
|
class HexDocFileModel(HexDocModel[AnyContext]):
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls, path: Path, context: AnyContext) -> Self:
|
def load(cls, path: Path, context: AnyContext) -> Self:
|
||||||
data = load_json(path) | {"__path": path}
|
data = load_json(path) | {"__path": path}
|
|
@ -10,10 +10,11 @@ from pkg_resources import iter_entry_points
|
||||||
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 minecraft.resource import ResourceLocation
|
|
||||||
|
|
||||||
from .model import AnyContext, HexDocModel
|
from .model import AnyContext, HexDocModel
|
||||||
|
|
||||||
|
# from hexdoc.minecraft import ResourceLocation
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from pydantic.root_model import Model
|
from pydantic.root_model import Model
|
||||||
|
|
||||||
|
@ -154,13 +155,13 @@ class InternallyTaggedUnion(HexDocModel[AnyContext]):
|
||||||
context: AnyContext | None = None,
|
context: AnyContext | None = None,
|
||||||
) -> Model:
|
) -> Model:
|
||||||
# resolve forward references, because apparently we need to do this
|
# resolve forward references, because apparently we need to do this
|
||||||
if cls not in _rebuilt_models:
|
# if cls not in _rebuilt_models:
|
||||||
_rebuilt_models.add(cls)
|
# _rebuilt_models.add(cls)
|
||||||
cls.model_rebuild(
|
# cls.model_rebuild(
|
||||||
_types_namespace={
|
# _types_namespace={
|
||||||
"ResourceLocation": ResourceLocation,
|
# "ResourceLocation": ResourceLocation,
|
||||||
}
|
# }
|
||||||
)
|
# )
|
||||||
|
|
||||||
return super().model_validate(
|
return super().model_validate(
|
||||||
obj,
|
obj,
|
||||||
|
@ -232,22 +233,3 @@ class InternallyTaggedUnion(HexDocModel[AnyContext]):
|
||||||
f"Failed to match {cls} with {cls._tag_key}={tag_value} to any of {tag_types}: {data}",
|
f"Failed to match {cls} with {cls._tag_key}={tag_value} to any of {tag_types}: {data}",
|
||||||
exceptions,
|
exceptions,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TypeTaggedUnion(InternallyTaggedUnion[AnyContext], key="type", value=None):
|
|
||||||
type: ResourceLocation | None
|
|
||||||
|
|
||||||
def __init_subclass__(
|
|
||||||
cls,
|
|
||||||
*,
|
|
||||||
group: str | None = None,
|
|
||||||
type: TagValue | None,
|
|
||||||
) -> None:
|
|
||||||
super().__init_subclass__(group=group, value=type)
|
|
||||||
match type:
|
|
||||||
case str():
|
|
||||||
cls.type = ResourceLocation.from_str(type)
|
|
||||||
case NoValueType():
|
|
||||||
cls.type = None
|
|
||||||
case None:
|
|
||||||
pass
|
|
|
@ -1,11 +1,10 @@
|
||||||
import datetime
|
import datetime
|
||||||
import re
|
import re
|
||||||
|
import tomllib
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Callable, TypeVar
|
from typing import Callable, TypeVar
|
||||||
|
|
||||||
import tomllib
|
from .deserialize import isinstance_or_raise
|
||||||
|
|
||||||
from common.deserialize import isinstance_or_raise
|
|
||||||
|
|
||||||
# TODO: there's (figuratively) literally no comments in this file
|
# TODO: there's (figuratively) literally no comments in this 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 common.model import DEFAULT_CONFIG
|
from .model import DEFAULT_CONFIG
|
||||||
|
|
||||||
_T = TypeVar("_T")
|
_T = TypeVar("_T")
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
from common.tagged_union import NoValue, TypeTaggedUnion
|
|
||||||
from minecraft.resource import ResourceLocation
|
|
||||||
from patchouli.context import AnyBookContext, BookContext
|
|
||||||
|
|
||||||
|
|
||||||
class ItemIngredient(
|
|
||||||
TypeTaggedUnion[AnyBookContext],
|
|
||||||
group="hexdoc.ItemIngredient",
|
|
||||||
type=None,
|
|
||||||
):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
ItemIngredientOrList = (
|
|
||||||
ItemIngredient[AnyBookContext] | list[ItemIngredient[AnyBookContext]]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class MinecraftItemIdIngredient(ItemIngredient[BookContext], type=NoValue):
|
|
||||||
item: ResourceLocation
|
|
||||||
|
|
||||||
|
|
||||||
class MinecraftItemTagIngredient(ItemIngredient[BookContext], type=NoValue):
|
|
||||||
tag: ResourceLocation
|
|
|
@ -1,7 +0,0 @@
|
||||||
__all__ = ["Book", "Category", "Entry", "Page", "Style", "FormatTree"]
|
|
||||||
|
|
||||||
from .book import Book
|
|
||||||
from .category import Category
|
|
||||||
from .entry import Entry
|
|
||||||
from .page import Page
|
|
||||||
from .text import FormatTree, Style
|
|
|
@ -1,8 +0,0 @@
|
||||||
__all__ = [
|
|
||||||
"FormatTree",
|
|
||||||
"Style",
|
|
||||||
"DEFAULT_MACROS",
|
|
||||||
"FormatContext",
|
|
||||||
]
|
|
||||||
|
|
||||||
from .formatting import DEFAULT_MACROS, FormatContext, FormatTree, Style
|
|
|
@ -1,6 +1,6 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from minecraft.resource import ItemStack, ResLoc, ResourceLocation
|
from hexdoc.resource import ItemStack, ResLoc, ResourceLocation
|
||||||
|
|
||||||
resource_locations: list[tuple[str, ResourceLocation, str]] = [
|
resource_locations: list[tuple[str, ResourceLocation, str]] = [
|
||||||
(
|
(
|
||||||
|
|
|
@ -1,34 +1,13 @@
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Iterator
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from bs4 import BeautifulSoup as bs
|
|
||||||
from syrupy.assertion import SnapshotAssertion
|
from syrupy.assertion import SnapshotAssertion
|
||||||
from syrupy.extensions.amber import AmberSnapshotExtension
|
|
||||||
from syrupy.types import SerializedData
|
|
||||||
|
|
||||||
from hexcasting.scripts.main import Args, main
|
from hexdoc.scripts.hexdoc import Args, main
|
||||||
|
|
||||||
|
_RUN = ["hexdoc"]
|
||||||
def prettify(data: SerializedData) -> str:
|
|
||||||
return bs(data, features="html.parser").prettify()
|
|
||||||
|
|
||||||
|
|
||||||
class NoDiffSnapshotEx(AmberSnapshotExtension):
|
|
||||||
def diff_snapshots(
|
|
||||||
self, serialized_data: SerializedData, snapshot_data: SerializedData
|
|
||||||
) -> SerializedData:
|
|
||||||
return "no diff"
|
|
||||||
|
|
||||||
def diff_lines(
|
|
||||||
self, serialized_data: SerializedData, snapshot_data: SerializedData
|
|
||||||
) -> Iterator[str]:
|
|
||||||
yield from ["no diff"]
|
|
||||||
|
|
||||||
|
|
||||||
_RUN = [sys.executable, "-m" "hexcasting.scripts.main"]
|
|
||||||
_ARGV = ["properties.toml", "-o"]
|
_ARGV = ["properties.toml", "-o"]
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,28 +32,3 @@ def test_cmd(tmp_path: Path, snapshot: SnapshotAssertion):
|
||||||
def test_stdout(capsys: pytest.CaptureFixture[str], snapshot: SnapshotAssertion):
|
def test_stdout(capsys: pytest.CaptureFixture[str], snapshot: SnapshotAssertion):
|
||||||
main(Args().parse_args(["properties.toml"]))
|
main(Args().parse_args(["properties.toml"]))
|
||||||
assert capsys.readouterr() == snapshot
|
assert capsys.readouterr() == snapshot
|
||||||
|
|
||||||
|
|
||||||
# def test_book_text(snapshot: SnapshotAssertion):
|
|
||||||
# def test_field(data_class: Any, field: Field[Any]):
|
|
||||||
# value = getattr(data_class, field.name, None)
|
|
||||||
# if isinstance(value, (LocalizedStr, FormatTree)):
|
|
||||||
# assert value == snapshot
|
|
||||||
|
|
||||||
# props = Properties.load(Path("properties.toml"))
|
|
||||||
# book = HexBook.load(HexBookState(props))
|
|
||||||
|
|
||||||
# for field in fields(book):
|
|
||||||
# test_field(book, field)
|
|
||||||
|
|
||||||
# for category in book.categories.values():
|
|
||||||
# for field in fields(category):
|
|
||||||
# test_field(category, field)
|
|
||||||
|
|
||||||
# for entry in category.entries:
|
|
||||||
# for field in fields(entry):
|
|
||||||
# test_field(entry, field)
|
|
||||||
|
|
||||||
# for page in entry.pages:
|
|
||||||
# for field in fields(page):
|
|
||||||
# test_field(page, field)
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from common.types import Color
|
from hexdoc.utils.types import Color
|
||||||
|
|
||||||
colors: list[str] = [
|
colors: list[str] = [
|
||||||
"#0099FF",
|
"#0099FF",
|
|
@ -1,6 +1,6 @@
|
||||||
# pyright: reportPrivateUsage=false
|
# pyright: reportPrivateUsage=false
|
||||||
from patchouli.text import DEFAULT_MACROS, FormatTree
|
from hexdoc.patchouli.text import DEFAULT_MACROS, FormatTree
|
||||||
from patchouli.text.formatting import (
|
from hexdoc.patchouli.text.formatting import (
|
||||||
CommandStyle,
|
CommandStyle,
|
||||||
FunctionStyle,
|
FunctionStyle,
|
||||||
FunctionStyleType,
|
FunctionStyleType,
|
Loading…
Reference in a new issue