Add hatch-gradle-version and restructure a lot of things

This commit is contained in:
object-Object 2023-08-27 18:26:41 -04:00
parent fec6702946
commit 8b32827c7e
50 changed files with 332 additions and 6331 deletions

View file

@ -18,7 +18,7 @@ jobs:
python-version: "3.11"
- name: Install docgen
run: pip install ./doc
run: pip install .
- name: Generate file
run: hexdoc doc/properties.toml -o index.html.uncommitted --ci

157
.gitignore vendored
View file

@ -26,3 +26,160 @@ forge*changelog.txt
Session.vim
plot/
# Python
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/

20
.vscode/settings.json vendored
View file

@ -1,4 +1,18 @@
{
"python.analysis.exclude": ["**"],
"python.analysis.indexing": false,
}
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true,
},
"editor.rulers": [88],
},
"isort.importStrategy": "fromEnvironment",
"python.languageServer": "Pylance",
"python.analysis.diagnosticMode": "workspace",
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"[html][jinja-html]": {
"editor.rulers": [120],
},
}

156
doc/.gitignore vendored
View file

@ -1,158 +1,2 @@
/*.html
/src/hexdoc/_export/generated/
# Python
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/

View file

@ -1,15 +0,0 @@
{
"folders": [
{
"name": "doc",
"path": ".."
},
{
"name": "HexMod",
"path": "../.."
}
],
"settings": {
"python.languageServer": "Pylance",
}
}

View file

@ -1,18 +0,0 @@
{
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true,
},
"editor.rulers": [88],
},
"isort.importStrategy": "fromEnvironment",
"python.languageServer": "Pylance",
"python.analysis.diagnosticMode": "workspace",
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"[html][jinja-html]": {
"editor.rulers": [120],
},
}

View file

@ -37,7 +37,7 @@ hexcasting = "https://raw.githubusercontent.com/gamma-delta/HexMod/main/Common/s
[template]
main = "main.html.jinja"
packages = [
"hexdoc_templates",
"hexdoc",
]
[template.args]

View file

@ -3,16 +3,15 @@ from pathlib import Path
from pydantic import Field, model_validator
from hexdoc.minecraft.tags import Tag
from hexdoc.patchouli.book import BookContext
from hexdoc.utils import ResourceLocation
from hexdoc.utils.model import HexDocModel
from hexdoc.minecraft import Tag
from hexdoc.patchouli import BookContext
from hexdoc.utils import HexdocModel, ResourceLocation
from hexdoc.utils.properties import PatternStubProps
from .pattern import Direction, PatternInfo
class PatternMetadata(HexDocModel):
class PatternMetadata(HexdocModel):
"""Automatically generated at `export_dir/modid.patterns.hexdoc.json`."""
patterns: dict[ResourceLocation, PatternInfo]

View file

@ -6,7 +6,7 @@ from hexdoc.minecraft.recipe import (
MinecraftItemIdIngredient,
MinecraftItemTagIngredient,
)
from hexdoc.utils import HexDocModel, ResourceLocation, TypeTaggedUnion
from hexdoc.utils import HexdocModel, ResourceLocation, TypeTaggedUnion
# ingredients
@ -34,7 +34,7 @@ class EntityTagIngredient(BrainsweepeeIngredient, type="entity_tag"):
tag: ResourceLocation
class BlockStateIngredient(HexDocModel):
class BlockStateIngredient(HexdocModel):
# TODO: tagged union
type: Literal["block"]
block: ResourceLocation
@ -58,7 +58,7 @@ class ModConditionalIngredient(
# results
class BlockState(HexDocModel):
class BlockState(HexdocModel):
name: LocalizedItem
properties: dict[str, Any] | None = None

View file

@ -3,7 +3,7 @@ from typing import Annotated, Any
from pydantic import BeforeValidator
from hexdoc.utils import HexDocModel, ResourceLocation
from hexdoc.utils import HexdocModel, ResourceLocation
class Direction(Enum):
@ -28,7 +28,7 @@ class Direction(Enum):
DirectionField = Annotated[Direction, BeforeValidator(Direction.validate)]
class RawPatternInfo(HexDocModel):
class RawPatternInfo(HexdocModel):
startdir: DirectionField
signature: str
is_per_world: bool = False

View file

@ -14,7 +14,7 @@ from hexdoc.hexcasting.hex_book import HexContext
from hexdoc.patchouli.book import Book
from hexdoc.utils import Properties
from hexdoc.utils.cd import cd
from hexdoc.utils.model import HexDocModel, init_context
from hexdoc.utils.model import HexdocModel, init_context
from hexdoc.utils.resource_loader import ModResourceLoader
from .jinja_extensions import IncludeRawExtension, hexdoc_block, hexdoc_wrap
@ -25,7 +25,7 @@ def strip_empty_lines(text: str) -> str:
# CLI arguments
class Args(HexDocModel):
class Args(HexdocModel):
"""example: main.py properties.toml -o out.html"""
properties_file: Path

View file

@ -3,6 +3,7 @@ from __future__ import annotations
import json
from dataclasses import InitVar
from functools import total_ordering
from pathlib import Path
from typing import Any, Callable, Self
from pydantic import Field, ValidationInfo, model_validator
@ -11,7 +12,7 @@ from pydantic.functional_validators import ModelWrapValidatorHandler
from hexdoc.utils import (
DEFAULT_CONFIG,
HexDocModel,
HexdocModel,
ItemStack,
ModResourceLoader,
Properties,
@ -23,10 +24,11 @@ from hexdoc.utils.deserialize import (
isinstance_or_raise,
)
from hexdoc.utils.resource_loader import LoaderContext
from hexdoc.utils.types import replace_suffixes
@total_ordering
class LocalizedStr(HexDocModel):
class LocalizedStr(HexdocModel):
"""Represents a string which has been localized."""
key: str
@ -137,8 +139,15 @@ class I18n:
for key, value in raw_lookup.items()
}
def _export(self, new: dict[str, str], current: dict[str, str] | None):
return json.dumps((current or {}) | new)
def _export(
self,
new: dict[str, str],
current: dict[str, str] | None,
path: Path,
):
data = json.dumps((current or {}) | new)
path = replace_suffixes(path, ".json")
return data, path
def localize(self, *keys: str, default: str | None = None) -> LocalizedStr:
"""Looks up the given string in the lang table if i18n is enabled. Otherwise,

View file

@ -1,11 +1,11 @@
from hexdoc.utils import HexDocModel
from hexdoc.utils import HexdocModel
from ..i18n import LocalizedItem
from .abstract_recipes import Recipe
from .ingredients import ItemIngredientOrList
class ItemResult(HexDocModel):
class ItemResult(HexdocModel):
item: LocalizedItem
count: int | None = None

View file

@ -1,14 +1,14 @@
from __future__ import annotations
from typing import Iterator, Self
from typing import Any, Iterator, Self
from pydantic import Field
from hexdoc.utils import HexDocModel, LoaderContext, ResourceLocation
from hexdoc.utils import HexdocModel, LoaderContext, ResourceLocation
from hexdoc.utils.deserialize import decode_json_dict
class OptionalTagValue(HexDocModel, frozen=True):
class OptionalTagValue(HexdocModel, frozen=True):
id: ResourceLocation
required: bool
@ -16,7 +16,7 @@ class OptionalTagValue(HexDocModel, frozen=True):
TagValue = ResourceLocation | OptionalTagValue
class Tag(HexDocModel):
class Tag(HexdocModel):
registry: str = Field(exclude=True)
raw_values: set[TagValue] = Field(alias="values")
replace: bool
@ -63,7 +63,7 @@ class Tag(HexDocModel):
case OptionalTagValue(id=id):
yield id
def _export(self, current: Self | None):
def _export(self, current: Self | None, *_: Any):
if self.replace or current is None:
tag = self
else:

View file

@ -6,7 +6,7 @@ from hexdoc.minecraft import I18n, LocalizedStr
from hexdoc.minecraft.i18n import I18nContext
from hexdoc.utils import (
Color,
HexDocModel,
HexdocModel,
ItemStack,
ModResourceLoader,
ResLoc,
@ -20,7 +20,7 @@ from .entry import Entry
from .text import FormatTree
class Book(HexDocModel):
class Book(HexdocModel):
"""Main Patchouli book class.
Includes all data from book.json, categories/entries/pages, and i18n.

View file

@ -4,14 +4,14 @@ from pydantic import Field
from hexdoc.minecraft import LocalizedStr
from hexdoc.utils import ItemStack, LoaderContext, ResourceLocation
from hexdoc.utils.resource import HexDocIDModel
from hexdoc.utils.resource import HexdocIDModel
from hexdoc.utils.types import Sortable, sorted_dict
from .entry import Entry
from .text import FormatTree
class Category(HexDocIDModel, Sortable):
class Category(HexdocIDModel, Sortable):
"""Category with pages and localizations.
See: https://vazkiimods.github.io/Patchouli/docs/reference/category-json

View file

@ -5,14 +5,14 @@ from pydantic import Field, ValidationInfo, model_validator
from hexdoc.minecraft import LocalizedStr
from hexdoc.utils import Color, ItemStack, ResourceLocation
from hexdoc.utils.deserialize import cast_or_raise
from hexdoc.utils.resource import HexDocIDModel
from hexdoc.utils.resource import HexdocIDModel
from hexdoc.utils.types import Sortable
from .book_context import BookContext
from .page.pages import Page
class Entry(HexDocIDModel, Sortable):
class Entry(HexdocIDModel, Sortable):
"""Entry json file, with pages and localizations.
See: https://vazkiimods.github.io/Patchouli/docs/reference/entry-json

View file

@ -15,7 +15,7 @@ from pydantic.functional_validators import ModelWrapValidatorHandler
from hexdoc.minecraft import LocalizedStr
from hexdoc.minecraft.i18n import I18nContext
from hexdoc.patchouli.text.html import HTMLElement, HTMLStream
from hexdoc.utils import DEFAULT_CONFIG, HexDocModel
from hexdoc.utils import DEFAULT_CONFIG, HexdocModel
from hexdoc.utils.deserialize import cast_or_raise
from hexdoc.utils.properties import Properties
from hexdoc.utils.resource import ResourceLocation
@ -82,7 +82,7 @@ class FormattingContext(
return self
class BookLink(HexDocModel):
class BookLink(HexdocModel):
raw_value: str
id: ResourceLocation
anchor: str | None
@ -150,7 +150,7 @@ class SpecialStyleType(Enum):
link = "l"
class Style(ABC, HexDocModel, frozen=True):
class Style(ABC, HexdocModel, frozen=True):
type: CommandStyleType | FunctionStyleType | SpecialStyleType
@staticmethod
@ -311,7 +311,7 @@ class LinkStyle(Style, frozen=True):
# intentionally not inheriting from Style, because this is basically an implementation
# detail of the parser and should not be returned or exposed anywhere
class _CloseTag(HexDocModel, frozen=True):
class _CloseTag(HexdocModel, frozen=True):
type: FunctionStyleType | Literal[SpecialStyleType.base, SpecialStyleType.color]

View file

@ -1,5 +1,5 @@
__all__ = [
"HexDocModel",
"HexdocModel",
"InternallyTaggedUnion",
"Color",
"ValidationContext",
@ -18,7 +18,7 @@ __all__ = [
"init_context",
]
from .model import DEFAULT_CONFIG, HexDocModel, ValidationContext, init_context
from .model import DEFAULT_CONFIG, HexdocModel, ValidationContext, init_context
from .properties import Properties
from .resource import Entity, ItemStack, ResLoc, ResourceLocation
from .resource_loader import LoaderContext, ModResourceLoader

View file

@ -25,9 +25,9 @@ def init_context(value: Any) -> Iterator[None]:
@dataclass_transform()
class HexDocBaseModel(BaseModel):
class HexdocBaseModel(BaseModel):
"""Base class for all Pydantic models in hexdoc. You should probably use
`HexDocModel` or `ValidationContext` instead.
`HexdocModel` or `ValidationContext` instead.
Sets the default model config, and overrides __init__ to allow using the
`init_context` context manager to set validation context for constructors.
@ -47,12 +47,12 @@ class HexDocBaseModel(BaseModel):
@dataclass_transform()
class ValidationContext(HexDocBaseModel):
"""Base class for Pydantic validation context for `HexDocModel`."""
class ValidationContext(HexdocBaseModel):
"""Base class for Pydantic validation context for `HexdocModel`."""
@dataclass_transform()
class HexDocModel(HexDocBaseModel):
class HexdocModel(HexdocBaseModel):
"""Base class for most Pydantic models in hexdoc.
Includes type overrides to require using subclasses of `ValidationContext` for
@ -87,7 +87,7 @@ class HexDocModel(HexDocBaseModel):
@dataclass_transform()
class StripHiddenModel(HexDocModel):
class StripHiddenModel(HexdocModel):
"""Base model which removes all keys starting with _ before validation."""
@model_validator(mode="before")

View file

@ -35,7 +35,7 @@ class TemplateProps(StripHiddenModel):
for i, value in enumerate(values):
if isinstance(value, str):
values[i] = (value, Path())
values[i] = (value, Path("_templates"))
return values

View file

@ -36,7 +36,7 @@ from pydantic.dataclasses import dataclass
from pydantic.functional_validators import ModelWrapValidatorHandler
from .deserialize import JSONDict
from .model import DEFAULT_CONFIG, HexDocModel, ValidationContext
from .model import DEFAULT_CONFIG, HexdocModel, ValidationContext
HEXDOC_EXPORTS_GROUP = "hexdoc.export"
"""Entry point group name for bundled hexdoc data."""
@ -211,7 +211,7 @@ class Entity(BaseResourceLocation, regex=_make_regex(nbt=True)):
return s
class BaseResourceDir(HexDocModel, ABC):
class BaseResourceDir(HexdocModel, ABC):
external: bool
reexport: bool
"""If not set, the default value will be `not self.external`.
@ -321,7 +321,7 @@ ResourceDir = PathResourceDir | EntryPointResourceDir
@dataclass_transform()
class HexDocIDModel(HexDocModel, ABC):
class HexdocIDModel(HexdocModel, ABC):
id: ResourceLocation
resource_dir: PathResourceDir

View file

@ -10,19 +10,21 @@ from typing import Callable, Literal, Self, TypeVar, overload
from pydantic.dataclasses import dataclass
from hexdoc.utils.deserialize import JSONDict, decode_json_dict
from hexdoc.utils.model import DEFAULT_CONFIG, HexDocModel, ValidationContext
from hexdoc.utils.types import without_suffix
from hexdoc.utils.model import DEFAULT_CONFIG, HexdocModel, ValidationContext
from hexdoc.utils.types import strip_suffixes
from .properties import Properties
from .resource import PathResourceDir, ResourceLocation, ResourceType
_T = TypeVar("_T")
_T_Model = TypeVar("_T_Model", bound=HexDocModel)
METADATA_SUFFIX = ".hexdoc.json"
_T = TypeVar("_T")
_T_Model = TypeVar("_T_Model", bound=HexdocModel)
class HexDocMetadata(HexDocModel):
ExportFn = Callable[[_T, _T | None, Path], str | tuple[str, Path]]
class HexdocMetadata(HexdocModel):
"""Automatically generated at `export_dir/modid.hexdoc.json`."""
book_url: str
@ -55,8 +57,8 @@ class ModResourceLoader:
# export this mod's metadata
loader.export(
path=HexDocMetadata.path(props.modid),
data=HexDocMetadata(
path=HexdocMetadata.path(props.modid),
data=HexdocMetadata(
book_url=props.url,
).model_dump_json(),
)
@ -64,7 +66,7 @@ class ModResourceLoader:
yield loader
def __post_init__(self):
self.mod_metadata = self.load_metadata("{modid}", HexDocMetadata)
self.mod_metadata = self.load_metadata("{modid}", HexdocMetadata)
def get_link_base(self, resource_dir: PathResourceDir) -> str:
modid = resource_dir.modid
@ -114,7 +116,7 @@ class ModResourceLoader:
id: ResourceLocation,
*,
decode: Callable[[str], _T] = decode_json_dict,
export: Callable[[_T, _T | None], str] | Literal[False] | None = None,
export: ExportFn[_T] | Literal[False] | None = None,
) -> tuple[PathResourceDir, _T]:
...
@ -125,7 +127,7 @@ class ModResourceLoader:
/,
*,
decode: Callable[[str], _T] = decode_json_dict,
export: Callable[[_T, _T | None], str] | Literal[False] | None = None,
export: ExportFn[_T] | Literal[False] | None = None,
) -> tuple[PathResourceDir, _T]:
...
@ -136,7 +138,7 @@ class ModResourceLoader:
id: ResourceLocation | None = None,
*,
decode: Callable[[str], _T] = decode_json_dict,
export: Callable[[_T, _T | None], str] | Literal[False] | None = None,
export: ExportFn[_T] | Literal[False] | None = None,
) -> tuple[PathResourceDir, _T]:
"""Find the first file with this resource location in `resource_dirs`.
@ -174,7 +176,7 @@ class ModResourceLoader:
namespace: str,
glob: str | list[str] = "**/*",
decode: Callable[[str], _T] = decode_json_dict,
export: Callable[[_T, _T | None], str] | Literal[False] | None = None,
export: ExportFn[_T] | Literal[False] | None = None,
) -> Iterator[tuple[PathResourceDir, ResourceLocation, _T]]:
...
@ -186,7 +188,7 @@ class ModResourceLoader:
id: ResourceLocation,
*,
decode: Callable[[str], _T] = decode_json_dict,
export: Callable[[_T, _T | None], str] | Literal[False] | None = None,
export: ExportFn[_T] | Literal[False] | None = None,
) -> Iterator[tuple[PathResourceDir, ResourceLocation, _T]]:
...
@ -199,7 +201,7 @@ class ModResourceLoader:
namespace: str | None = None,
glob: str | list[str] = "**/*",
decode: Callable[[str], _T] = decode_json_dict,
export: Callable[[_T, _T | None], str] | Literal[False] | None = None,
export: ExportFn[_T] | Literal[False] | None = None,
) -> Iterator[tuple[PathResourceDir, ResourceLocation, _T]]:
"""Search for a glob under a given resource location in all of `resource_dirs`.
@ -252,7 +254,7 @@ class ModResourceLoader:
id = ResourceLocation(
# eg. ["assets", "hexcasting", "lang", ...][1]
namespace=path.relative_to(resource_dir.path).parts[1],
path=without_suffix(path.relative_to(base_path)).as_posix(),
path=strip_suffixes(path.relative_to(base_path)).as_posix(),
)
try:
@ -279,7 +281,7 @@ class ModResourceLoader:
path: Path,
*,
decode: Callable[[str], _T] = decode_json_dict,
export: Callable[[_T, _T | None], str] | Literal[False] | None = None,
export: ExportFn[_T] | Literal[False] | None = None,
) -> _T:
if not path.is_file():
raise FileNotFoundError(path)
@ -313,7 +315,7 @@ class ModResourceLoader:
value: _T,
*,
decode: Callable[[str], _T] = decode_json_dict,
export: Callable[[_T, _T | None], str] | None = None,
export: ExportFn[_T] | None = None,
) -> None:
...
@ -324,22 +326,26 @@ class ModResourceLoader:
value: _T = None,
*,
decode: Callable[[str], _T] = decode_json_dict,
export: Callable[[_T, _T | None], str] | None = None,
export: ExportFn[_T] | None = None,
) -> None:
out_path = self.props.export_dir / path
out_path.parent.mkdir(parents=True, exist_ok=True)
logging.getLogger(__name__).debug(f"Exporting {path} to {out_path}")
match export:
case None:
out_data = data
case _:
try:
old_value = decode(out_path.read_text("utf-8"))
except FileNotFoundError:
old_value = None
out_data = export(value, old_value)
if export is None:
out_data = data
else:
try:
old_value = decode(out_path.read_text("utf-8"))
except FileNotFoundError:
old_value = None
match export(value, old_value, out_path):
case str(out_data):
pass
case (str(out_data), Path() as out_path):
pass
out_path.parent.mkdir(parents=True, exist_ok=True)
out_path.write_text(out_data, "utf-8")

View file

@ -6,7 +6,7 @@ from pkg_resources import iter_entry_points
from pydantic import ValidationInfo, model_validator
from pydantic.functional_validators import ModelWrapValidatorHandler
from .model import HexDocModel
from .model import HexdocModel
from .resource import ResourceLocation
@ -40,7 +40,7 @@ def load_entry_points(group: str):
raise
class InternallyTaggedUnion(HexDocModel):
class InternallyTaggedUnion(HexdocModel):
"""Implements [internally tagged unions](https://serde.rs/enum-representations.html#internally-tagged)
using the [Registry pattern](https://charlesreid1.github.io/python-patterns-the-registry.html).

View file

@ -103,7 +103,31 @@ class TryGetEnum(Enum):
return None
def without_suffix(path: Path) -> Path:
def strip_suffixes(path: Path) -> Path:
"""Removes all suffixes from a path. This is helpful because `path.with_suffix("")`
only removes the last suffix.
For example:
```py
path = Path("lang/en_us.flatten.json5")
strip_suffixes(path) # lang/en_us
path.with_suffix("") # lang/en_us.flatten
```
"""
while path.suffix:
path = path.with_suffix("")
return path
def replace_suffixes(path: Path, suffix: str) -> Path:
"""Replaces all suffixes of a path. This is helpful because `path.with_suffix()`
only replaces the last suffix.
For example:
```py
path = Path("lang/en_us.flatten.json5")
replace_suffixes(path, ".json") # lang/en_us.json
path.with_suffix(".json") # lang/en_us.flatten.json
```
"""
return strip_suffixes(path).with_suffix(suffix)

File diff suppressed because it is too large Load diff

View file

@ -2,16 +2,17 @@ import subprocess
import sys
from pathlib import Path
import pytest
from syrupy.assertion import SnapshotAssertion
from hexdoc.hexdoc import Args, main
PROPS = "doc/properties.toml"
def test_file(tmp_path: Path, snapshot: SnapshotAssertion):
# generate output docs html file and assert it hasn't changed vs. the snapshot
out_path = tmp_path / "out.html"
main(Args.parse_args(["properties.toml", "-o", out_path.as_posix()]))
main(Args.parse_args([PROPS, "-o", out_path.as_posix()]))
assert out_path.read_text("utf-8") == snapshot
@ -19,13 +20,8 @@ def test_cmd(tmp_path: Path, snapshot: SnapshotAssertion):
# as above, but running the command we actually want to be using
out_path = tmp_path / "out.html"
subprocess.run(
["hexdoc", "properties.toml", "-o", out_path.as_posix()],
["hexdoc", PROPS, "-o", out_path.as_posix()],
stdout=sys.stdout,
stderr=sys.stderr,
)
assert out_path.read_text("utf-8") == snapshot
def test_stdout(capsys: pytest.CaptureFixture[str], snapshot: SnapshotAssertion):
main(Args.parse_args(["properties.toml"]))
assert capsys.readouterr() == snapshot

View file

@ -1,16 +1,20 @@
[build-system]
requires = ["hatchling"]
requires = ["hatchling", "hatch-gradle-version>=0.2.0"]
build-backend = "hatchling.build"
# project metadata
[project]
name = "hexdoc"
version = "0.11.0.1.0" # TODO: make a Hatch plugin to auto-set the mod version after the +
dynamic = ["version"]
authors = [
{ name="object-Object", email="object@objectobject.ca" },
{ name="Alwinfy" },
]
readme = "README.md"
readme = "doc/README.md"
classifiers = [
"Framework :: Hatch",
]
requires-python = ">=3.11"
dependencies = [
"typing_extensions>=4.7.0",
@ -26,18 +30,32 @@ dev = [
"isort==5.12.0",
"pytest>=7.3.1",
"syrupy>=4.0.2",
"hatchling",
]
[tool.hatch.build]
packages = [
"src/hexdoc",
"src/hexdoc_templates",
]
[project.scripts]
hexdoc = "hexdoc.hexdoc:main"
# Gradle version/deps
[tool.hatch.version]
source = "gradle-properties"
py-version = "1.0"
# directory inclusion
[tool.hatch.build]
only-include = [
"doc/src/hexdoc",
"gradle.properties",
]
artifacts = [
"/doc/src/hexdoc/_export/generated",
]
[tool.hatch.build.targets.wheel]
sources = ["doc/src"]
# hexdoc entry points
[project.entry-points."hexdoc.export"]
hexcasting = "hexdoc._export:__resources__"
@ -57,6 +75,7 @@ hexcasting = "hexdoc.hexcasting.hex_recipes"
[project.entry-points."hexdoc.BrainsweepeeIngredient"]
hexcasting = "hexdoc.hexcasting.hex_recipes"
# Pytest
[tool.pytest.ini_options]
addopts = ["--import-mode=importlib"]
@ -64,14 +83,22 @@ addopts = ["--import-mode=importlib"]
[tool.coverage.report]
include_namespace_packages = true
# formatting and linting
[tool.isort]
profile = "black"
combine_as_imports = true
known_first_party = ["hexdoc"]
[tool.pyright]
pythonVersion = "3.11"
pythonPlatform = "All"
include = [
"doc/src/hexdoc",
"doc/src/hexdoc_templates",
]
# 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 :/