From 03e7683ae126612fbb5557915a2de218af907ba7 Mon Sep 17 00:00:00 2001 From: object-Object Date: Mon, 24 Jul 2023 20:40:28 -0400 Subject: [PATCH] The great refactoring (move to hexdoc package and add reexports) --- .vscode/settings.json | 36 ------- doc/.vscode/extensions.json | 11 ++ {.vscode => doc/.vscode}/launch.json | 2 +- doc/.vscode/settings.json | 18 ++++ doc/properties.toml | 10 +- doc/pyproject.toml | 102 ++++++++++++++---- doc/requirements-dev.txt | 6 -- doc/src/hexcasting/__init__.py | 9 -- doc/src/hexcasting/pattern.py | 68 ------------ doc/src/{common => hexdoc}/__init__.py | 0 doc/src/hexdoc/hexcasting/__init__.py | 13 +++ doc/src/{ => hexdoc}/hexcasting/hex_book.py | 33 ++++-- .../{ => hexdoc}/hexcasting/hex_recipes.py | 12 +-- doc/src/hexdoc/hexcasting/page/__init__.py | 20 ++++ .../hexcasting/page}/abstract_hex_pages.py | 10 +- .../hexcasting/page}/hex_pages.py | 13 ++- doc/src/hexdoc/hexcasting/pattern.py | 45 ++++++++ doc/src/hexdoc/minecraft/__init__.py | 9 ++ doc/src/{ => hexdoc}/minecraft/i18n.py | 24 ++--- .../{ => hexdoc}/minecraft/recipe/__init__.py | 12 +-- .../minecraft/recipe/abstract_recipes.py | 11 +- .../hexdoc/minecraft/recipe/ingredients.py | 23 ++++ .../{ => hexdoc}/minecraft/recipe/recipes.py | 17 +-- doc/src/hexdoc/patchouli/__init__.py | 16 +++ doc/src/{ => hexdoc}/patchouli/book.py | 13 ++- doc/src/{ => hexdoc}/patchouli/category.py | 12 +-- doc/src/{ => hexdoc}/patchouli/entry.py | 15 +-- .../context.py => hexdoc/patchouli/model.py} | 13 +-- .../{ => hexdoc}/patchouli/page/__init__.py | 20 ++-- .../patchouli/page/abstract_pages.py | 8 +- doc/src/{ => hexdoc}/patchouli/page/pages.py | 8 +- doc/src/hexdoc/patchouli/text/__init__.py | 9 ++ .../{ => hexdoc}/patchouli/text/formatting.py | 12 +-- doc/src/{ => hexdoc}/patchouli/text/html.py | 2 +- doc/src/{common => hexdoc}/properties.py | 23 ++-- doc/src/{minecraft => hexdoc}/resource.py | 31 +++++- .../scripts/__init__.py | 0 .../main.py => hexdoc/scripts/hexdoc.py} | 18 ++-- doc/src/hexdoc/utils/__init__.py | 22 ++++ .../{common => hexdoc/utils}/deserialize.py | 1 + .../utils}/jinja_extensions.py | 6 +- doc/src/{common => hexdoc/utils}/model.py | 4 +- .../{common => hexdoc/utils}/tagged_union.py | 38 ++----- .../utils}/toml_placeholders.py | 5 +- doc/src/{common => hexdoc/utils}/types.py | 2 +- doc/src/minecraft/__init__.py | 0 doc/src/minecraft/recipe/ingredients.py | 24 ----- doc/src/patchouli/__init__.py | 7 -- doc/src/patchouli/text/__init__.py | 8 -- doc/test/minecraft/test_resource.py | 2 +- doc/test/test_snapshots.py | 50 +-------- doc/test/{common => utils}/test_types.py | 2 +- .../text/test_formatting.py | 4 +- 53 files changed, 475 insertions(+), 404 deletions(-) delete mode 100644 .vscode/settings.json create mode 100644 doc/.vscode/extensions.json rename {.vscode => doc/.vscode}/launch.json (94%) create mode 100644 doc/.vscode/settings.json delete mode 100644 doc/requirements-dev.txt delete mode 100644 doc/src/hexcasting/__init__.py delete mode 100644 doc/src/hexcasting/pattern.py rename doc/src/{common => hexdoc}/__init__.py (100%) create mode 100644 doc/src/hexdoc/hexcasting/__init__.py rename doc/src/{ => hexdoc}/hexcasting/hex_book.py (55%) rename doc/src/{ => hexdoc}/hexcasting/hex_recipes.py (84%) create mode 100644 doc/src/hexdoc/hexcasting/page/__init__.py rename doc/src/{hexcasting => hexdoc/hexcasting/page}/abstract_hex_pages.py (87%) rename doc/src/{hexcasting => hexdoc/hexcasting/page}/hex_pages.py (83%) create mode 100644 doc/src/hexdoc/hexcasting/pattern.py create mode 100644 doc/src/hexdoc/minecraft/__init__.py rename doc/src/{ => hexdoc}/minecraft/i18n.py (90%) rename doc/src/{ => hexdoc}/minecraft/recipe/__init__.py (89%) rename doc/src/{ => hexdoc}/minecraft/recipe/abstract_recipes.py (81%) create mode 100644 doc/src/hexdoc/minecraft/recipe/ingredients.py rename doc/src/{ => hexdoc}/minecraft/recipe/recipes.py (56%) create mode 100644 doc/src/hexdoc/patchouli/__init__.py rename doc/src/{ => hexdoc}/patchouli/book.py (92%) rename doc/src/{ => hexdoc}/patchouli/category.py (89%) rename doc/src/{ => hexdoc}/patchouli/entry.py (80%) rename doc/src/{patchouli/context.py => hexdoc/patchouli/model.py} (80%) rename doc/src/{ => hexdoc}/patchouli/page/__init__.py (100%) rename doc/src/{ => hexdoc}/patchouli/page/abstract_pages.py (90%) rename doc/src/{ => hexdoc}/patchouli/page/pages.py (91%) create mode 100644 doc/src/hexdoc/patchouli/text/__init__.py rename doc/src/{ => hexdoc}/patchouli/text/formatting.py (97%) rename doc/src/{ => hexdoc}/patchouli/text/html.py (97%) rename doc/src/{common => hexdoc}/properties.py (87%) rename doc/src/{minecraft => hexdoc}/resource.py (81%) rename doc/src/{hexcasting => hexdoc}/scripts/__init__.py (100%) rename doc/src/{hexcasting/scripts/main.py => hexdoc/scripts/hexdoc.py} (84%) create mode 100644 doc/src/hexdoc/utils/__init__.py rename doc/src/{common => hexdoc/utils}/deserialize.py (99%) rename doc/src/{common => hexdoc/utils}/jinja_extensions.py (92%) rename doc/src/{common => hexdoc/utils}/model.py (94%) rename doc/src/{common => hexdoc/utils}/tagged_union.py (90%) rename doc/src/{common => hexdoc/utils}/toml_placeholders.py (98%) rename doc/src/{common => hexdoc/utils}/types.py (98%) delete mode 100644 doc/src/minecraft/__init__.py delete mode 100644 doc/src/minecraft/recipe/ingredients.py delete mode 100644 doc/src/patchouli/__init__.py delete mode 100644 doc/src/patchouli/text/__init__.py rename doc/test/{common => utils}/test_types.py (86%) rename doc/test/{patchouli => utils}/text/test_formatting.py (97%) diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index c007fa1f..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -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" - ] -} diff --git a/doc/.vscode/extensions.json b/doc/.vscode/extensions.json new file mode 100644 index 00000000..1862f911 --- /dev/null +++ b/doc/.vscode/extensions.json @@ -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", + ] +} \ No newline at end of file diff --git a/.vscode/launch.json b/doc/.vscode/launch.json similarity index 94% rename from .vscode/launch.json rename to doc/.vscode/launch.json index f619b379..112c35e4 100644 --- a/.vscode/launch.json +++ b/doc/.vscode/launch.json @@ -17,7 +17,7 @@ "type": "python", "request": "launch", "cwd": "${workspaceFolder}/doc", - "module": "hexcasting.scripts.main", + "module": "hexdoc", "args": [ "properties.toml", ], diff --git a/doc/.vscode/settings.json b/doc/.vscode/settings.json new file mode 100644 index 00000000..36da2939 --- /dev/null +++ b/doc/.vscode/settings.json @@ -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], + }, +} diff --git a/doc/properties.toml b/doc/properties.toml index 8b2aceb3..e71b537e 100644 --- a/doc/properties.toml +++ b/doc/properties.toml @@ -51,12 +51,8 @@ package = "{src}/main/java/at/petrak/hexcasting" resources = "{src}/main/resources" generated = "{src}/generated/resources" pattern_stubs = [ - # these are tables so we have the option to add extra per-stub configs (eg. regex) - # NOTE: each ^ is like ../ in a file path (^key and ^.key are both valid) - # 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" }, + "{package}/common/casting/RegisterPatterns.java", + "{package}/interop/pehkui/PehkuiInterop.java", ] [fabric] @@ -65,7 +61,7 @@ package = "{src}/main/java/at/petrak/hexcasting/fabric" resources = "{src}/main/resources" generated = "{src}/generated/resources" pattern_stubs = [ - { file = "{^.package}/interop/gravity/GravityApiInterop.java" }, + "{package}/interop/gravity/GravityApiInterop.java", ] [forge] diff --git a/doc/pyproject.toml b/doc/pyproject.toml index d4c9905a..812c9d13 100644 --- a/doc/pyproject.toml +++ b/doc/pyproject.toml @@ -1,11 +1,10 @@ -# project metadata - [build-system] requires = ["hatchling"] build-backend = "hatchling.build" + [project] -name = "HexDoc" # TODO: i'm pretty sure i had funnier ideas than this +name = "HexDoc" version = "0.1.0" authors = [ { name="Alwinfy" }, @@ -16,45 +15,108 @@ requires-python = ">=3.11" dependencies = [ "typing_extensions~=4.7.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"] -hexdoc-patchouli = "patchouli.page.pages" -hexdoc-hexcasting = "hexcasting.hex_pages" -hexdoc-abstract-hexcasting = "hexcasting.abstract_hex_pages" +hexdoc-patchouli = "hexdoc.patchouli.page.pages" +hexdoc-hexcasting = "hexdoc.hexcasting.page.hex_pages" [project.entry-points."hexdoc.Recipe"] -hexdoc-minecraft = "minecraft.recipe.recipes" -hexdoc-hexcasting = "hexcasting.hex_recipes" +hexdoc-minecraft = "hexdoc.minecraft.recipe.recipes" +hexdoc-hexcasting = "hexdoc.hexcasting.hex_recipes" [project.entry-points."hexdoc.ItemIngredient"] -hexdoc-minecraft = "minecraft.recipe.ingredients" -hexdoc-hexcasting = "hexcasting.hex_recipes" +hexdoc-minecraft = "hexdoc.minecraft.recipe.ingredients" +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] -packages = ["src/common", "src/hexcasting", "src/minecraft", "src/patchouli"] +packages = ["src/hexdoc"] -# tests, formatting, and (TODO:) type checking [tool.pytest.ini_options] -addopts = [ - "--import-mode=importlib" -] +addopts = ["--import-mode=importlib"] markers = [ "filename: name of file for fixtures to create", "file_contents: data for fixtures to write to files", "fixture_data: other misc data", ] + [tool.coverage.report] include_namespace_packages = true + [tool.isort] profile = "black" 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" diff --git a/doc/requirements-dev.txt b/doc/requirements-dev.txt deleted file mode 100644 index b2ffba6b..00000000 --- a/doc/requirements-dev.txt +++ /dev/null @@ -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 diff --git a/doc/src/hexcasting/__init__.py b/doc/src/hexcasting/__init__.py deleted file mode 100644 index fc2ca539..00000000 --- a/doc/src/hexcasting/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -__all__ = [ - "HexBook", - "HexContext", - "AnyHexContext", - "HexBookModel", -] - - -from .hex_book import AnyHexContext, HexBook, HexBookModel, HexContext diff --git a/doc/src/hexcasting/pattern.py b/doc/src/hexcasting/pattern.py deleted file mode 100644 index 9706dca4..00000000 --- a/doc/src/hexcasting/pattern.py +++ /dev/null @@ -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), - ) diff --git a/doc/src/common/__init__.py b/doc/src/hexdoc/__init__.py similarity index 100% rename from doc/src/common/__init__.py rename to doc/src/hexdoc/__init__.py diff --git a/doc/src/hexdoc/hexcasting/__init__.py b/doc/src/hexdoc/hexcasting/__init__.py new file mode 100644 index 00000000..fefc8473 --- /dev/null +++ b/doc/src/hexdoc/hexcasting/__init__.py @@ -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 diff --git a/doc/src/hexcasting/hex_book.py b/doc/src/hexdoc/hexcasting/hex_book.py similarity index 55% rename from doc/src/hexcasting/hex_book.py rename to doc/src/hexdoc/hexcasting/hex_book.py index 7932d00d..4adf28dc 100644 --- a/doc/src/hexcasting/hex_book.py +++ b/doc/src/hexdoc/hexcasting/hex_book.py @@ -1,11 +1,12 @@ +from pathlib import Path from typing import Any, Generic, TypeVar -from common.model import AnyContext -from common.properties import Properties -from hexcasting.pattern import PatternInfo -from minecraft.resource import ResourceLocation -from patchouli.book import Book -from patchouli.context import AnyBookContext, BookContext +from hexdoc.patchouli import AnyBookContext, Book, BookContext +from hexdoc.properties import Properties +from hexdoc.resource import ResourceLocation +from hexdoc.utils import AnyContext + +from .pattern import Direction, PatternInfo class HexContext(BookContext): @@ -15,7 +16,7 @@ class HexContext(BookContext): AnyHexContext = TypeVar("AnyHexContext", bound=HexContext) -class HexBookModel( +class HexBookType( Generic[AnyContext, AnyBookContext, AnyHexContext], Book[AnyHexContext, AnyHexContext], ): @@ -28,7 +29,7 @@ class HexBookModel( signatures = dict[str, PatternInfo]() # just for duplicate checking for stub in props.pattern_stubs: # 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 if duplicate := ( patterns.get(pattern.id) or signatures.get(pattern.signature) @@ -45,5 +46,19 @@ class HexBookModel( "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] diff --git a/doc/src/hexcasting/hex_recipes.py b/doc/src/hexdoc/hexcasting/hex_recipes.py similarity index 84% rename from doc/src/hexcasting/hex_recipes.py rename to doc/src/hexdoc/hexcasting/hex_recipes.py index 491300ed..706477a1 100644 --- a/doc/src/hexcasting/hex_recipes.py +++ b/doc/src/hexdoc/hexcasting/hex_recipes.py @@ -1,15 +1,15 @@ from typing import Any, Literal -from common.model import HexDocModel -from hexcasting.hex_book import HexContext -from minecraft.i18n import LocalizedItem -from minecraft.recipe import ( +from hexdoc.minecraft import LocalizedItem, Recipe +from hexdoc.minecraft.recipe import ( ItemIngredient, MinecraftItemIdIngredient, MinecraftItemTagIngredient, - Recipe, ) -from minecraft.resource import ResourceLocation +from hexdoc.resource import ResourceLocation +from hexdoc.utils import HexDocModel + +from .hex_book import HexContext # ingredients diff --git a/doc/src/hexdoc/hexcasting/page/__init__.py b/doc/src/hexdoc/hexcasting/page/__init__.py new file mode 100644 index 00000000..021fbe22 --- /dev/null +++ b/doc/src/hexdoc/hexcasting/page/__init__.py @@ -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, +) diff --git a/doc/src/hexcasting/abstract_hex_pages.py b/doc/src/hexdoc/hexcasting/page/abstract_hex_pages.py similarity index 87% rename from doc/src/hexcasting/abstract_hex_pages.py rename to doc/src/hexdoc/hexcasting/page/abstract_hex_pages.py index 5bca84d0..8fecdc0f 100644 --- a/doc/src/hexcasting/abstract_hex_pages.py +++ b/doc/src/hexdoc/hexcasting/page/abstract_hex_pages.py @@ -3,12 +3,12 @@ from typing import Any, cast from pydantic import ValidationInfo, model_validator -from hexcasting.pattern import RawPatternInfo -from minecraft.i18n import LocalizedStr -from minecraft.resource import ResourceLocation -from patchouli.page import PageWithText +from hexdoc.minecraft import LocalizedStr +from hexdoc.patchouli.page import PageWithText +from hexdoc.resource import ResourceLocation -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) diff --git a/doc/src/hexcasting/hex_pages.py b/doc/src/hexdoc/hexcasting/page/hex_pages.py similarity index 83% rename from doc/src/hexcasting/hex_pages.py rename to doc/src/hexdoc/hexcasting/page/hex_pages.py index d1e33a9d..7308b9e4 100644 --- a/doc/src/hexcasting/hex_pages.py +++ b/doc/src/hexdoc/hexcasting/page/hex_pages.py @@ -2,15 +2,14 @@ from typing import Any, cast from pydantic import ValidationInfo, model_validator -from minecraft.i18n import LocalizedStr -from minecraft.recipe import CraftingRecipe -from minecraft.resource import ResourceLocation -from patchouli.page import PageWithText -from patchouli.page.abstract_pages import PageWithTitle +from hexdoc.minecraft import LocalizedStr +from hexdoc.minecraft.recipe import CraftingRecipe +from hexdoc.patchouli.page import PageWithText, PageWithTitle +from hexdoc.resource import ResourceLocation +from ..hex_book import HexContext +from ..hex_recipes import BrainsweepRecipe from .abstract_hex_pages import PageWithOpPattern, PageWithPattern -from .hex_book import HexContext -from .hex_recipes import BrainsweepRecipe class LookupPatternPage( diff --git a/doc/src/hexdoc/hexcasting/pattern.py b/doc/src/hexdoc/hexcasting/pattern.py new file mode 100644 index 00000000..f050113a --- /dev/null +++ b/doc/src/hexdoc/hexcasting/pattern.py @@ -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 diff --git a/doc/src/hexdoc/minecraft/__init__.py b/doc/src/hexdoc/minecraft/__init__.py new file mode 100644 index 00000000..e5191660 --- /dev/null +++ b/doc/src/hexdoc/minecraft/__init__.py @@ -0,0 +1,9 @@ +__all__ = [ + "I18n", + "LocalizedItem", + "LocalizedStr", + "Recipe", +] + +from .i18n import I18n, LocalizedItem, LocalizedStr +from .recipe import Recipe diff --git a/doc/src/minecraft/i18n.py b/doc/src/hexdoc/minecraft/i18n.py similarity index 90% rename from doc/src/minecraft/i18n.py rename to doc/src/hexdoc/minecraft/i18n.py index 92246bce..00747167 100644 --- a/doc/src/minecraft/i18n.py +++ b/doc/src/hexdoc/minecraft/i18n.py @@ -1,7 +1,7 @@ from __future__ import annotations +from dataclasses import InitVar from functools import total_ordering -from pathlib import Path from typing import Any, Callable, Self, cast from pydantic import ValidationInfo, model_validator @@ -9,10 +9,10 @@ from pydantic.dataclasses import dataclass from pydantic.functional_validators import ModelWrapValidatorHandler from typing_extensions import TypedDict -from common.deserialize import isinstance_or_raise, load_json -from common.model import DEFAULT_CONFIG, HexDocModel -from common.properties import Properties -from minecraft.resource import ItemStack, ResourceLocation +from hexdoc.properties import Properties +from hexdoc.resource import ItemStack, ResourceLocation +from hexdoc.utils import DEFAULT_CONFIG, HexDocModel +from hexdoc.utils.deserialize import isinstance_or_raise, load_json class I18nContext(TypedDict): @@ -94,12 +94,12 @@ class LocalizedItem(LocalizedStr): class I18n: """Handles localization of strings.""" - props: Properties + props: InitVar[Properties] enabled: bool 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 self.lookup = None if not self.enabled: @@ -109,8 +109,9 @@ class I18n: # TODO: load ALL of the i18n files, return dict[str, _Lookup] | None # or maybe dict[(str, str), LocalizedStr] # we could also use that to ensure all i18n files have the same set of keys - path = self.dir / self.props.i18n.filename - raw_lookup = load_json(path) | (self.props.i18n.extra or {}) + lang_dir = props.resources_dir / "assets" / props.modid / "lang" + path = lang_dir / props.i18n.filename + raw_lookup = load_json(path) | (props.i18n.extra or {}) # validate and insert self.lookup = {} @@ -121,11 +122,6 @@ class I18n: 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( self, *keys: str, diff --git a/doc/src/minecraft/recipe/__init__.py b/doc/src/hexdoc/minecraft/recipe/__init__.py similarity index 89% rename from doc/src/minecraft/recipe/__init__.py rename to doc/src/hexdoc/minecraft/recipe/__init__.py index d5aa9b88..8bc89582 100644 --- a/doc/src/minecraft/recipe/__init__.py +++ b/doc/src/hexdoc/minecraft/recipe/__init__.py @@ -1,19 +1,19 @@ __all__ = [ - "CraftingRecipe", - "ItemIngredient", - "MinecraftItemTagIngredient", - "MinecraftItemIdIngredient", - "ItemResult", "Recipe", - "recipes", + "ItemIngredient", + "ItemIngredientOrList", + "MinecraftItemIdIngredient", + "MinecraftItemTagIngredient", "CraftingRecipe", "CraftingShapedRecipe", "CraftingShapelessRecipe", + "ItemResult", ] from .abstract_recipes import Recipe from .ingredients import ( ItemIngredient, + ItemIngredientOrList, MinecraftItemIdIngredient, MinecraftItemTagIngredient, ) diff --git a/doc/src/minecraft/recipe/abstract_recipes.py b/doc/src/hexdoc/minecraft/recipe/abstract_recipes.py similarity index 81% rename from doc/src/minecraft/recipe/abstract_recipes.py rename to doc/src/hexdoc/minecraft/recipe/abstract_recipes.py index fc72ac2f..3ad0348b 100644 --- a/doc/src/minecraft/recipe/abstract_recipes.py +++ b/doc/src/hexdoc/minecraft/recipe/abstract_recipes.py @@ -2,13 +2,12 @@ from typing import Any, Self, cast from pydantic import ValidationInfo, model_validator -from common.deserialize import load_json -from common.tagged_union import TypeTaggedUnion -from minecraft.resource import ResourceLocation -from patchouli.context import AnyBookContext +from hexdoc.properties import AnyPropsContext +from hexdoc.resource import ResourceLocation, TypeTaggedUnion +from hexdoc.utils.deserialize import load_json -class Recipe(TypeTaggedUnion[AnyBookContext], group="hexdoc.Recipe", type=None): +class Recipe(TypeTaggedUnion[AnyPropsContext], group="hexdoc.Recipe", type=None): id: ResourceLocation group: str | None = None @@ -31,7 +30,7 @@ class Recipe(TypeTaggedUnion[AnyBookContext], group="hexdoc.Recipe", type=None): id = values # load the recipe - context = cast(AnyBookContext, info.context) + context = cast(AnyPropsContext, info.context) for recipe_dir in context["props"].recipe_dirs: # TODO: should this use id.namespace somewhere? path = recipe_dir / f"{id.path}.json" diff --git a/doc/src/hexdoc/minecraft/recipe/ingredients.py b/doc/src/hexdoc/minecraft/recipe/ingredients.py new file mode 100644 index 00000000..5b4e1a2b --- /dev/null +++ b/doc/src/hexdoc/minecraft/recipe/ingredients.py @@ -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 diff --git a/doc/src/minecraft/recipe/recipes.py b/doc/src/hexdoc/minecraft/recipe/recipes.py similarity index 56% rename from doc/src/minecraft/recipe/recipes.py rename to doc/src/hexdoc/minecraft/recipe/recipes.py index 579476f1..4a267ba9 100644 --- a/doc/src/minecraft/recipe/recipes.py +++ b/doc/src/hexdoc/minecraft/recipe/recipes.py @@ -1,30 +1,31 @@ -from common.model import HexDocModel -from minecraft.i18n import LocalizedItem -from patchouli.context import BookContext +from typing import Any +from hexdoc.utils import HexDocModel + +from ..i18n import LocalizedItem from .abstract_recipes import Recipe from .ingredients import ItemIngredientOrList -class ItemResult(HexDocModel[BookContext]): +class ItemResult(HexDocModel[Any]): item: LocalizedItem count: int | None = None class CraftingShapedRecipe( - Recipe[BookContext], + Recipe[Any], type="minecraft:crafting_shaped", ): pattern: list[str] - key: dict[str, ItemIngredientOrList[BookContext]] + key: dict[str, ItemIngredientOrList[Any]] result: ItemResult class CraftingShapelessRecipe( - Recipe[BookContext], + Recipe[Any], type="minecraft:crafting_shapeless", ): - ingredients: list[ItemIngredientOrList[BookContext]] + ingredients: list[ItemIngredientOrList[Any]] result: ItemResult diff --git a/doc/src/hexdoc/patchouli/__init__.py b/doc/src/hexdoc/patchouli/__init__.py new file mode 100644 index 00000000..793e5c4f --- /dev/null +++ b/doc/src/hexdoc/patchouli/__init__.py @@ -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 diff --git a/doc/src/patchouli/book.py b/doc/src/hexdoc/patchouli/book.py similarity index 92% rename from doc/src/patchouli/book.py rename to doc/src/hexdoc/patchouli/book.py index f0bb1289..4cf4c6d1 100644 --- a/doc/src/patchouli/book.py +++ b/doc/src/hexdoc/patchouli/book.py @@ -2,16 +2,15 @@ from typing import Any, Generic, Literal, Self, cast from pydantic import Field, ValidationInfo, model_validator -from common.deserialize import isinstance_or_raise, load_json -from common.model import AnyContext, HexDocModel -from common.properties import Properties -from common.types import Color -from minecraft.i18n import I18n, LocalizedStr -from minecraft.resource import ItemStack, ResLoc, ResourceLocation +from hexdoc.minecraft import I18n, LocalizedStr +from hexdoc.properties import Properties +from hexdoc.resource import ItemStack, ResLoc, ResourceLocation +from hexdoc.utils import AnyContext, Color, HexDocModel +from hexdoc.utils.deserialize import isinstance_or_raise, load_json from .category import Category -from .context import AnyBookContext, BookContext from .entry import Entry +from .model import AnyBookContext, BookContext from .text import DEFAULT_MACROS, FormatTree diff --git a/doc/src/patchouli/category.py b/doc/src/hexdoc/patchouli/category.py similarity index 89% rename from doc/src/patchouli/category.py rename to doc/src/hexdoc/patchouli/category.py index a529f52a..431a1f1e 100644 --- a/doc/src/patchouli/category.py +++ b/doc/src/hexdoc/patchouli/category.py @@ -3,17 +3,17 @@ from typing import Self from pydantic import Field -from common.properties import Properties -from common.types import Sortable, sorted_dict -from minecraft.i18n import LocalizedStr -from minecraft.resource import ItemStack, ResourceLocation +from hexdoc.minecraft import LocalizedStr +from hexdoc.properties import Properties +from hexdoc.resource import ItemStack, ResourceLocation +from hexdoc.utils.types import Sortable, sorted_dict -from .context import BookContext, BookModelFile from .entry import Entry +from .model import BookContext, BookFileModel from .text import FormatTree -class Category(BookModelFile[BookContext, BookContext], Sortable): +class Category(BookFileModel[BookContext, BookContext], Sortable): """Category with pages and localizations. See: https://vazkiimods.github.io/Patchouli/docs/reference/category-json diff --git a/doc/src/patchouli/entry.py b/doc/src/hexdoc/patchouli/entry.py similarity index 80% rename from doc/src/patchouli/entry.py rename to doc/src/hexdoc/patchouli/entry.py index fb6d7072..76283c69 100644 --- a/doc/src/patchouli/entry.py +++ b/doc/src/hexdoc/patchouli/entry.py @@ -3,16 +3,17 @@ from typing import cast from pydantic import Field, ValidationInfo, model_validator -from common.properties import Properties -from common.types import Color, Sortable -from minecraft.i18n import LocalizedStr -from minecraft.resource import ItemStack, ResourceLocation +from hexdoc.minecraft import LocalizedStr +from hexdoc.properties import Properties +from hexdoc.resource import ItemStack, ResourceLocation +from hexdoc.utils import Color +from hexdoc.utils.types import Sortable -from .context import BookContext, BookModelFile -from .page import Page +from .model import BookContext, BookFileModel +from .page.pages import Page -class Entry(BookModelFile[BookContext, BookContext], Sortable): +class Entry(BookFileModel[BookContext, BookContext], Sortable): """Entry json file, with pages and localizations. See: https://vazkiimods.github.io/Patchouli/docs/reference/entry-json diff --git a/doc/src/patchouli/context.py b/doc/src/hexdoc/patchouli/model.py similarity index 80% rename from doc/src/patchouli/context.py rename to doc/src/hexdoc/patchouli/model.py index dbb1ff7f..8cd4ef39 100644 --- a/doc/src/patchouli/context.py +++ b/doc/src/hexdoc/patchouli/model.py @@ -4,10 +4,11 @@ from typing import Any, Generic, TypeVar, cast, dataclass_transform from pydantic import ValidationInfo, model_validator -from common.model import AnyContext, HexDocModelFile -from common.properties import Properties -from minecraft.resource import ResourceLocation -from patchouli.text import FormatContext +from hexdoc.properties import Properties +from hexdoc.resource import ResourceLocation +from hexdoc.utils import AnyContext, HexDocFileModel + +from .text.formatting import FormatContext class BookContext(FormatContext): @@ -18,9 +19,9 @@ AnyBookContext = TypeVar("AnyBookContext", bound=BookContext) @dataclass_transform() -class BookModelFile( +class BookFileModel( Generic[AnyContext, AnyBookContext], - HexDocModelFile[AnyBookContext], + HexDocFileModel[AnyBookContext], ABC, ): id: ResourceLocation diff --git a/doc/src/patchouli/page/__init__.py b/doc/src/hexdoc/patchouli/page/__init__.py similarity index 100% rename from doc/src/patchouli/page/__init__.py rename to doc/src/hexdoc/patchouli/page/__init__.py index 997173d9..da7d450c 100644 --- a/doc/src/patchouli/page/__init__.py +++ b/doc/src/hexdoc/patchouli/page/__init__.py @@ -2,17 +2,17 @@ __all__ = [ "Page", "PageWithText", "PageWithTitle", - "CraftingPage", - "EmptyPage", - "EntityPage", - "ImagePage", - "LinkPage", - "MultiblockPage", - "QuestPage", - "RelationsPage", - "SmeltingPage", - "SpotlightPage", "TextPage", + "ImagePage", + "CraftingPage", + "SmeltingPage", + "MultiblockPage", + "EntityPage", + "SpotlightPage", + "LinkPage", + "RelationsPage", + "QuestPage", + "EmptyPage", ] from .abstract_pages import Page, PageWithText, PageWithTitle diff --git a/doc/src/patchouli/page/abstract_pages.py b/doc/src/hexdoc/patchouli/page/abstract_pages.py similarity index 90% rename from doc/src/patchouli/page/abstract_pages.py rename to doc/src/hexdoc/patchouli/page/abstract_pages.py index da04e43e..ab2b08a6 100644 --- a/doc/src/patchouli/page/abstract_pages.py +++ b/doc/src/hexdoc/patchouli/page/abstract_pages.py @@ -3,11 +3,11 @@ from typing import Any, ClassVar, Self from pydantic import model_validator from pydantic.functional_validators import ModelWrapValidatorHandler -from common.tagged_union import TagValue, TypeTaggedUnion -from minecraft.i18n import LocalizedStr -from minecraft.resource import ResourceLocation +from hexdoc.minecraft import LocalizedStr +from hexdoc.resource import ResourceLocation, TypeTaggedUnion +from hexdoc.utils import TagValue -from ..context import AnyBookContext +from ..model import AnyBookContext from ..text import FormatTree diff --git a/doc/src/patchouli/page/pages.py b/doc/src/hexdoc/patchouli/page/pages.py similarity index 91% rename from doc/src/patchouli/page/pages.py rename to doc/src/hexdoc/patchouli/page/pages.py index 80ebea1c..5d0125ee 100644 --- a/doc/src/patchouli/page/pages.py +++ b/doc/src/hexdoc/patchouli/page/pages.py @@ -1,10 +1,10 @@ from typing import Any -from minecraft.i18n import LocalizedItem, LocalizedStr -from minecraft.recipe import CraftingRecipe -from minecraft.resource import Entity, ItemStack, ResourceLocation -from patchouli.context import BookContext +from hexdoc.minecraft import LocalizedItem, LocalizedStr +from hexdoc.minecraft.recipe import CraftingRecipe +from hexdoc.resource import Entity, ItemStack, ResourceLocation +from ..model import BookContext from ..text import FormatTree from .abstract_pages import Page, PageWithText, PageWithTitle diff --git a/doc/src/hexdoc/patchouli/text/__init__.py b/doc/src/hexdoc/patchouli/text/__init__.py new file mode 100644 index 00000000..f319662d --- /dev/null +++ b/doc/src/hexdoc/patchouli/text/__init__.py @@ -0,0 +1,9 @@ +__all__ = [ + "FormatTree", + "HTMLElement", + "HTMLStream", + "DEFAULT_MACROS", +] + +from .formatting import DEFAULT_MACROS, FormatTree +from .html import HTMLElement, HTMLStream diff --git a/doc/src/patchouli/text/formatting.py b/doc/src/hexdoc/patchouli/text/formatting.py similarity index 97% rename from doc/src/patchouli/text/formatting.py rename to doc/src/hexdoc/patchouli/text/formatting.py index 3ed93177..3e8cd61c 100644 --- a/doc/src/patchouli/text/formatting.py +++ b/doc/src/hexdoc/patchouli/text/formatting.py @@ -12,10 +12,11 @@ from pydantic import ValidationInfo, model_validator from pydantic.dataclasses import dataclass from pydantic.functional_validators import ModelWrapValidatorHandler -from common.model import DEFAULT_CONFIG, HexDocModel -from common.properties import Properties -from common.types import TryGetEnum -from minecraft.i18n import I18nContext, LocalizedStr +from hexdoc.minecraft import LocalizedStr +from hexdoc.minecraft.i18n import I18nContext +from hexdoc.properties import PropsContext +from hexdoc.utils import DEFAULT_CONFIG, HexDocModel +from hexdoc.utils.types import TryGetEnum from .html import HTMLElement, HTMLStream @@ -251,9 +252,8 @@ class _CloseTag(HexDocModel[Any], frozen=True): _FORMAT_RE = re.compile(r"\$\(([^)]*)\)") -class FormatContext(I18nContext): +class FormatContext(I18nContext, PropsContext): macros: dict[str, str] - props: Properties @dataclass(config=DEFAULT_CONFIG) diff --git a/doc/src/patchouli/text/html.py b/doc/src/hexdoc/patchouli/text/html.py similarity index 97% rename from doc/src/patchouli/text/html.py rename to doc/src/hexdoc/patchouli/text/html.py index 41175afa..6c152113 100644 --- a/doc/src/patchouli/text/html.py +++ b/doc/src/hexdoc/patchouli/text/html.py @@ -5,7 +5,7 @@ from dataclasses import dataclass from html import escape from typing import IO, Any -from minecraft.i18n import LocalizedStr +from hexdoc.minecraft import LocalizedStr def attributes_to_str(attributes: dict[str, Any]): diff --git a/doc/src/common/properties.py b/doc/src/hexdoc/properties.py similarity index 87% rename from doc/src/common/properties.py rename to doc/src/hexdoc/properties.py index caf787cd..00f5f3b8 100644 --- a/doc/src/common/properties.py +++ b/doc/src/hexdoc/properties.py @@ -1,6 +1,8 @@ +from __future__ import annotations + import re from pathlib import Path -from typing import Annotated, Any, Self +from typing import Annotated, Any, Self, TypeVar from pydantic import ( AfterValidator, @@ -9,11 +11,11 @@ from pydantic import ( HttpUrl, field_validator, ) +from typing_extensions import TypedDict -from common.model import HexDocModel -from common.toml_placeholders import load_toml -from hexcasting.pattern import PatternStubFile -from minecraft.resource import ResourceLocation +from hexdoc.resource import ResourceLocation +from hexdoc.utils.model import HexDocModel +from hexdoc.utils.toml_placeholders import load_toml NoTrailingSlashHttpUrl = Annotated[ str, @@ -27,7 +29,7 @@ class PlatformProps(HexDocModel[Any]): generated: Path src: Path package: Path - pattern_stubs: list[PatternStubFile] | None = None + pattern_stubs: list[Path] | None = None class I18nProps(HexDocModel[Any]): @@ -112,7 +114,7 @@ class Properties(HexDocModel[Any]): return platforms @property - def pattern_stubs(self) -> list[PatternStubFile]: + def pattern_stubs(self) -> list[Path]: return [ stub 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})" ) return value + + +class PropsContext(TypedDict): + props: Properties + + +AnyPropsContext = TypeVar("AnyPropsContext", bound=PropsContext) diff --git a/doc/src/minecraft/resource.py b/doc/src/hexdoc/resource.py similarity index 81% rename from doc/src/minecraft/resource.py rename to doc/src/hexdoc/resource.py index 0de31ad2..af0b7cb7 100644 --- a/doc/src/minecraft/resource.py +++ b/doc/src/hexdoc/resource.py @@ -1,5 +1,9 @@ # 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 from pathlib import Path 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.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]: @@ -118,3 +128,22 @@ class Entity(BaseResourceLocation, regex=_make_regex(nbt=True)): if self.nbt is not None: s += self.nbt 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 diff --git a/doc/src/hexcasting/scripts/__init__.py b/doc/src/hexdoc/scripts/__init__.py similarity index 100% rename from doc/src/hexcasting/scripts/__init__.py rename to doc/src/hexdoc/scripts/__init__.py diff --git a/doc/src/hexcasting/scripts/main.py b/doc/src/hexdoc/scripts/hexdoc.py similarity index 84% rename from doc/src/hexcasting/scripts/main.py rename to doc/src/hexdoc/scripts/hexdoc.py index d98ce008..9595d90a 100644 --- a/doc/src/hexcasting/scripts/main.py +++ b/doc/src/hexdoc/scripts/hexdoc.py @@ -1,7 +1,6 @@ # because Tap.add_argument isn't typed, for some reason # pyright: reportUnknownMemberType=false -import sys from pathlib import Path from jinja2 import Environment, FileSystemLoader, StrictUndefined @@ -9,12 +8,9 @@ from jinja2 import Environment, FileSystemLoader, StrictUndefined # from jinja2.sandbox import SandboxedEnvironment from tap import Tap -from common.jinja_extensions import IncludeRawExtension, hexdoc_block, hexdoc_wrap -from common.properties import Properties -from hexcasting.hex_book import HexBook - -if sys.version_info < (3, 11): - raise RuntimeError("Minimum Python version: 3.11") +from hexdoc.hexcasting import HexBook +from hexdoc.properties import Properties +from hexdoc.utils.jinja_extensions import IncludeRawExtension, hexdoc_block, hexdoc_wrap def strip_empty_lines(text: str) -> str: @@ -33,7 +29,11 @@ class Args(Tap): 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 props = Properties.load(args.properties_file) 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 if __name__ == "__main__": - main(Args().parse_args()) + main() diff --git a/doc/src/hexdoc/utils/__init__.py b/doc/src/hexdoc/utils/__init__.py new file mode 100644 index 00000000..b15c43e5 --- /dev/null +++ b/doc/src/hexdoc/utils/__init__.py @@ -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 diff --git a/doc/src/common/deserialize.py b/doc/src/hexdoc/utils/deserialize.py similarity index 99% rename from doc/src/common/deserialize.py rename to doc/src/hexdoc/utils/deserialize.py index f0d9eb84..419fe0ff 100644 --- a/doc/src/common/deserialize.py +++ b/doc/src/hexdoc/utils/deserialize.py @@ -6,6 +6,7 @@ _T = TypeVar("_T") _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 def isinstance_or_raise( val: Any, diff --git a/doc/src/common/jinja_extensions.py b/doc/src/hexdoc/utils/jinja_extensions.py similarity index 92% rename from doc/src/common/jinja_extensions.py rename to doc/src/hexdoc/utils/jinja_extensions.py index 5ca81454..c9f6cd48 100644 --- a/doc/src/common/jinja_extensions.py +++ b/doc/src/hexdoc/utils/jinja_extensions.py @@ -5,9 +5,9 @@ from jinja2.ext import Extension from jinja2.parser import Parser from markupsafe import Markup -from minecraft.i18n import LocalizedStr -from patchouli.text.formatting import FormatTree -from patchouli.text.html import HTMLStream +from hexdoc.minecraft import LocalizedStr +from hexdoc.patchouli import FormatTree +from hexdoc.patchouli.text import HTMLStream # https://stackoverflow.com/a/64392515 diff --git a/doc/src/common/model.py b/doc/src/hexdoc/utils/model.py similarity index 94% rename from doc/src/common/model.py rename to doc/src/hexdoc/utils/model.py index 2181e1fe..33553e5a 100644 --- a/doc/src/common/model.py +++ b/doc/src/hexdoc/utils/model.py @@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Any, Generic, Self, TypeVar, dataclass_transfo from pydantic import BaseModel, ConfigDict from typing_extensions import TypedDict -from common.deserialize import load_json +from .deserialize import load_json if TYPE_CHECKING: from pydantic.root_model import Model @@ -53,7 +53,7 @@ class FrozenHexDocModel(Generic[AnyContext], HexDocModel[AnyContext]): @dataclass_transform() -class HexDocModelFile(HexDocModel[AnyContext]): +class HexDocFileModel(HexDocModel[AnyContext]): @classmethod def load(cls, path: Path, context: AnyContext) -> Self: data = load_json(path) | {"__path": path} diff --git a/doc/src/common/tagged_union.py b/doc/src/hexdoc/utils/tagged_union.py similarity index 90% rename from doc/src/common/tagged_union.py rename to doc/src/hexdoc/utils/tagged_union.py index eca5145e..5ee84aa9 100644 --- a/doc/src/common/tagged_union.py +++ b/doc/src/hexdoc/utils/tagged_union.py @@ -10,10 +10,11 @@ from pkg_resources import iter_entry_points from pydantic import ValidationInfo, model_validator from pydantic.functional_validators import ModelWrapValidatorHandler -from minecraft.resource import ResourceLocation - from .model import AnyContext, HexDocModel +# from hexdoc.minecraft import ResourceLocation + + if TYPE_CHECKING: from pydantic.root_model import Model @@ -154,13 +155,13 @@ class InternallyTaggedUnion(HexDocModel[AnyContext]): context: AnyContext | None = None, ) -> Model: # resolve forward references, because apparently we need to do this - if cls not in _rebuilt_models: - _rebuilt_models.add(cls) - cls.model_rebuild( - _types_namespace={ - "ResourceLocation": ResourceLocation, - } - ) + # if cls not in _rebuilt_models: + # _rebuilt_models.add(cls) + # cls.model_rebuild( + # _types_namespace={ + # "ResourceLocation": ResourceLocation, + # } + # ) return super().model_validate( 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}", 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 diff --git a/doc/src/common/toml_placeholders.py b/doc/src/hexdoc/utils/toml_placeholders.py similarity index 98% rename from doc/src/common/toml_placeholders.py rename to doc/src/hexdoc/utils/toml_placeholders.py index e68666aa..7699d80a 100644 --- a/doc/src/common/toml_placeholders.py +++ b/doc/src/hexdoc/utils/toml_placeholders.py @@ -1,11 +1,10 @@ import datetime import re +import tomllib from pathlib import Path from typing import Callable, TypeVar -import tomllib - -from common.deserialize import isinstance_or_raise +from .deserialize import isinstance_or_raise # TODO: there's (figuratively) literally no comments in this file diff --git a/doc/src/common/types.py b/doc/src/hexdoc/utils/types.py similarity index 98% rename from doc/src/common/types.py rename to doc/src/hexdoc/utils/types.py index 3b1e8212..d34aa089 100644 --- a/doc/src/common/types.py +++ b/doc/src/hexdoc/utils/types.py @@ -6,7 +6,7 @@ from typing import Any, Mapping, Protocol, TypeVar from pydantic import field_validator, model_validator from pydantic.dataclasses import dataclass -from common.model import DEFAULT_CONFIG +from .model import DEFAULT_CONFIG _T = TypeVar("_T") diff --git a/doc/src/minecraft/__init__.py b/doc/src/minecraft/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/doc/src/minecraft/recipe/ingredients.py b/doc/src/minecraft/recipe/ingredients.py deleted file mode 100644 index 8017889d..00000000 --- a/doc/src/minecraft/recipe/ingredients.py +++ /dev/null @@ -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 diff --git a/doc/src/patchouli/__init__.py b/doc/src/patchouli/__init__.py deleted file mode 100644 index 88baba82..00000000 --- a/doc/src/patchouli/__init__.py +++ /dev/null @@ -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 diff --git a/doc/src/patchouli/text/__init__.py b/doc/src/patchouli/text/__init__.py deleted file mode 100644 index 68d3a561..00000000 --- a/doc/src/patchouli/text/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -__all__ = [ - "FormatTree", - "Style", - "DEFAULT_MACROS", - "FormatContext", -] - -from .formatting import DEFAULT_MACROS, FormatContext, FormatTree, Style diff --git a/doc/test/minecraft/test_resource.py b/doc/test/minecraft/test_resource.py index 9ae15fd4..4f149f09 100644 --- a/doc/test/minecraft/test_resource.py +++ b/doc/test/minecraft/test_resource.py @@ -1,6 +1,6 @@ import pytest -from minecraft.resource import ItemStack, ResLoc, ResourceLocation +from hexdoc.resource import ItemStack, ResLoc, ResourceLocation resource_locations: list[tuple[str, ResourceLocation, str]] = [ ( diff --git a/doc/test/test_snapshots.py b/doc/test/test_snapshots.py index f7bf676d..f87cb3bf 100644 --- a/doc/test/test_snapshots.py +++ b/doc/test/test_snapshots.py @@ -1,34 +1,13 @@ import subprocess import sys from pathlib import Path -from typing import Iterator import pytest -from bs4 import BeautifulSoup as bs 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 - -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"] +_RUN = ["hexdoc"] _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): main(Args().parse_args(["properties.toml"])) 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) diff --git a/doc/test/common/test_types.py b/doc/test/utils/test_types.py similarity index 86% rename from doc/test/common/test_types.py rename to doc/test/utils/test_types.py index eb2b1050..daeae3e5 100644 --- a/doc/test/common/test_types.py +++ b/doc/test/utils/test_types.py @@ -1,6 +1,6 @@ import pytest -from common.types import Color +from hexdoc.utils.types import Color colors: list[str] = [ "#0099FF", diff --git a/doc/test/patchouli/text/test_formatting.py b/doc/test/utils/text/test_formatting.py similarity index 97% rename from doc/test/patchouli/text/test_formatting.py rename to doc/test/utils/text/test_formatting.py index 67180df7..2f7819fb 100644 --- a/doc/test/patchouli/text/test_formatting.py +++ b/doc/test/utils/text/test_formatting.py @@ -1,6 +1,6 @@ # pyright: reportPrivateUsage=false -from patchouli.text import DEFAULT_MACROS, FormatTree -from patchouli.text.formatting import ( +from hexdoc.patchouli.text import DEFAULT_MACROS, FormatTree +from hexdoc.patchouli.text.formatting import ( CommandStyle, FunctionStyle, FunctionStyleType,