Skip to content

Commit

Permalink
Merge pull request #294 from machow/dev-pydantic-compat
Browse files Browse the repository at this point in the history
Dev pydantic compat
  • Loading branch information
machow authored Oct 13, 2023
2 parents 266f6e5 + 079f009 commit 3057d0f
Show file tree
Hide file tree
Showing 16 changed files with 188 additions and 85 deletions.
16 changes: 15 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ jobs:
matrix:
# Checks based on python versions ---
python-version: ['3.9', '3.10']
requirements: [""]

include:
- name: "pydantic v1"
requirements: "pydantic<2.0.0"
python-version: "3.10"

steps:
- uses: actions/checkout@v2
Expand All @@ -26,7 +32,15 @@ jobs:
- name: Install dev dependencies
run: |
python -m pip install --upgrade pip
python -m pip install ".[dev]"
# include requirements if specified
if [ -n "$REQUIREMENTS" ]; then
python -m pip install $REQUIREMENTS '.[dev]'
else
python -m pip install '.[dev]'
fi
env:
REQUIREMENTS: ${{ matrix.requirements }}
- name: Run tests
run: |
pytest
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ dependencies = [
"tabulate >= 0.9.0",
"importlib-metadata >= 5.1.0",
"importlib-resources >= 5.10.2",
"pydantic < 2.0",
"pydantic",
"pyyaml",
"typing-extensions >= 4.4.0",
"watchdog >= 3.0.0",
Expand Down
2 changes: 1 addition & 1 deletion quartodoc/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from functools import partial
from watchdog.events import PatternMatchingEventHandler
from quartodoc import Builder, convert_inventory
from pydantic import BaseModel
from ._pydantic_compat import BaseModel


def get_package_path(package_name):
Expand Down
10 changes: 10 additions & 0 deletions quartodoc/_pydantic_compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
try:
from pydantic.v1 import (
BaseModel,
Field,
Extra,
PrivateAttr,
ValidationError,
) # noqa
except ImportError:
from pydantic import BaseModel, Field, Extra, PrivateAttr, ValidationError # noqa
2 changes: 1 addition & 1 deletion quartodoc/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
from griffe import dataclasses as dc
from plum import dispatch
from typing import Union
from pydantic import BaseModel # for previewing

from ._pydantic_compat import BaseModel # for previewing

# Transform and patched-in classes ============================================
# TODO: annotate transform return types. make sure subtypes inherit from correct
Expand Down
12 changes: 4 additions & 8 deletions quartodoc/autosummary.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@
from plum import dispatch # noqa
from pathlib import Path
from types import ModuleType
from pydantic import ValidationError

from .inventory import create_inventory, convert_inventory
from . import layout
from .parsers import get_parser_defaults
from .renderers import Renderer
from .validation import fmt
from .validation import fmt_all
from ._pydantic_compat import ValidationError


from typing import Any

Expand Down Expand Up @@ -485,12 +486,7 @@ def load_layout(self, sections: dict, package: str, options=None):
try:
return layout.Layout(sections=sections, package=package, options=options)
except ValidationError as e:
msg = "Configuration error for YAML:\n - "
errors = [fmt(err) for err in e.errors() if fmt(err)]
first_error = errors[
0
] # we only want to show one error at a time b/c it is confusing otherwise
msg += first_error
msg = fmt_all(e)
raise ValueError(msg) from None

# building ----------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion quartodoc/builder/_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from __future__ import annotations

from pydantic import BaseModel
from quartodoc._pydantic_compat import BaseModel
from typing import Any, Optional


Expand Down
2 changes: 1 addition & 1 deletion quartodoc/builder/blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
_log = logging.getLogger(__name__)

if TYPE_CHECKING:
from pydantic import BaseModel
from quartodoc._pydantic_compat import BaseModel


def _auto_package(mod: dc.Module) -> list[Section]:
Expand Down
2 changes: 1 addition & 1 deletion quartodoc/builder/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

from contextvars import ContextVar
from plum import dispatch
from pydantic import BaseModel
from typing import Union

from quartodoc._pydantic_compat import BaseModel
from ._node import Node


Expand Down
3 changes: 2 additions & 1 deletion quartodoc/interlinks.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@
import warnings
import yaml

from pydantic import BaseModel, Field
from dataclasses import dataclass
from pathlib import Path
from typing import Literal, Annotated, Union, Optional

from ._pydantic_compat import BaseModel, Field


ENV_PROJECT_ROOT: str = "QUARTO_PROJECT_ROOT"

Expand Down
6 changes: 3 additions & 3 deletions quartodoc/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
import logging

from enum import Enum
from pydantic import BaseModel, Field, Extra, PrivateAttr

from typing_extensions import Annotated
from typing import Literal, Union, Optional

from ._pydantic_compat import BaseModel, Field, Extra, PrivateAttr


_log = logging.getLogger(__name__)

Expand Down Expand Up @@ -414,7 +414,7 @@ class DocModule(Doc):
]
"""Entry in the contents list."""

ContentList = list[Union[ContentElement, Doc, _AutoDefault]]
ContentList = list[Union[_AutoDefault, ContentElement, Doc]]

# Item ----

Expand Down
39 changes: 39 additions & 0 deletions quartodoc/tests/__snapshots__/test_validation.ambr
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# serializer version: 1
# name: test_misplaced_kindpage
'''
Code:

quartodoc:
package: zzz
sections:
- kind: page


Error:
Configuration error for YAML:
- Missing field `path` for element 0 in the list for `sections`, which you need when setting `kind: page`.

'''
# ---
# name: test_missing_name_contents
'''
Code:

quartodoc:
package: zzz
sections:
- title: Section 1
- title: Section 2
contents:

# name missing here ----
- children: linked

- name: MdRenderer

Error:
Configuration error for YAML:
- Missing field `name` for element 0 in the list for `contents` located in element 1 in the list for `sections`

'''
# ---
4 changes: 3 additions & 1 deletion quartodoc/tests/test_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ def test_preview_warn_alias_no_load():
qast.preview(obj)

msg = record[0].message.args[0]
assert "Could not resolve Alias target `pydantic.BaseModel`" in msg
assert (
"Could not resolve Alias target `quartodoc._pydantic_compat.BaseModel`" in msg
)


@pytest.mark.parametrize(
Expand Down
3 changes: 2 additions & 1 deletion quartodoc/tests/test_layout.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import pytest

from pydantic import ValidationError
from quartodoc.layout import Layout, Page, Text, Section # noqa

from quartodoc._pydantic_compat import ValidationError


@pytest.mark.parametrize(
"cfg, res",
Expand Down
106 changes: 42 additions & 64 deletions quartodoc/tests/test_validation.py
Original file line number Diff line number Diff line change
@@ -1,79 +1,57 @@
import copy
import pytest
import yaml

from textwrap import indent, dedent

from quartodoc.autosummary import Builder

EXAMPLE_SECTIONS = [
{
"title": "Preperation Functions",
"desc": "Functions that fetch objects.\nThey prepare a representation of the site.\n",
"contents": ["Auto", "blueprint", "collect", "get_object", "preview"],
},
{
"title": "Docstring Renderers",
"desc": "Renderers convert parsed docstrings into a target format, like markdown.\n",
"contents": [
{"name": "MdRenderer", "children": "linked"},
{"name": "MdRenderer.render", "dynamic": True},
{"name": "MdRenderer.render_annotation", "dynamic": True},
{"name": "MdRenderer.render_header", "dynamic": True},
],
},
{
"title": "API Builders",
"desc": "Builders build documentation. They tie all the pieces\nof quartodoc together.\n",
"contents": [
{"kind": "auto", "name": "Builder", "members": []},
"Builder.from_quarto_config",
"Builder.build",
"Builder.write_index",
],
},
]


@pytest.fixture
def sections():
return copy.deepcopy(EXAMPLE_SECTIONS)


def check_ValueError(sections):

def check_ValueError(cfg: str):
"Check that a ValueError is raised when creating a `Builder` instance. Return the error message as a string."

d = yaml.safe_load(cfg)
with pytest.raises(ValueError) as e:
Builder(sections=sections, package="quartodoc")
return str(e.value)
Builder.from_quarto_config(d)

fmt_cfg = indent(dedent(cfg), " " * 4)
fmt_value = indent(str(e.value), " " * 4)
return f"""\
Code:\n{fmt_cfg}
Error:\n{fmt_value}
"""

def test_valid_yaml(sections):
"Test that valid YAML passes validation"
Builder(sections=sections, package="quartodoc")

def test_missing_name_contents(snapshot):
"Test that a missing name in contents raises an error in a different section."

def test_missing_name_contents_1(sections):
"Test that a missing name in contents raises an error"
del sections[2]["contents"][0]["name"]
msg = check_ValueError(sections)
assert (
"- Missing field `name` for element 0 in the list for `contents` located in element 2 in the list for `sections`"
in msg
)
cfg = """
quartodoc:
package: zzz
sections:
- title: Section 1
- title: Section 2
contents:
# name missing here ----
- children: linked
def test_missing_name_contents_2(sections):
"Test that a missing name in contents raises an error in a different section."
del sections[1]["contents"][0]["name"]
msg = check_ValueError(sections)
assert (
"- Missing field `name` for element 0 in the list for `contents` located in element 1 in the list for `sections`"
in msg
)
- name: MdRenderer
"""

msg = check_ValueError(cfg)
assert msg == snapshot

def test_misplaced_kindpage(sections):

def test_misplaced_kindpage(snapshot):
"Test that a misplaced kind: page raises an error"
sections[0]["kind"] = "page"
msg = check_ValueError(sections)
assert (
" - Missing field `path` for element 0 in the list for `sections`, which you need when setting `kind: page`."
in msg
)

cfg = """
quartodoc:
package: zzz
sections:
- kind: page
"""

msg = check_ValueError(cfg)
assert msg == snapshot
Loading

0 comments on commit 3057d0f

Please sign in to comment.