Skip to content

Commit

Permalink
Complete translation of core_schema.ChainSchema
Browse files Browse the repository at this point in the history
  • Loading branch information
candleindark committed Jul 29, 2024
1 parent 83f38e1 commit f35a596
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 4 deletions.
36 changes: 32 additions & 4 deletions pydantic2linkml/gen_linkml.py
Original file line number Diff line number Diff line change
Expand Up @@ -969,12 +969,40 @@ def _tagged_union_schema(self, schema: core_schema.TaggedUnionSchema) -> None:
)

def _chain_schema(self, schema: core_schema.ChainSchema) -> None:
raise TranslationNotImplementedError(
f"Translation of Pydantic core schema, {schema['type']}, is not "
"implemented. If you encounter this error in translating your models, "
"consider filing an issue."
"""
Shape the contained slot definition to match the restrictions specified by
a `core_schema.ChainSchema`, which represents a chain of Pydantic core
schemas
:param schema: The `core_schema.ChainSchema`
Note: Models can often be defined to avoid having a field with a chain schema.
For example, in the following model, only field `y` has a chain schema.
The type annotation of field `y`, through examination of the
chain schema, can be argued incorrect; in the sense that no `int` can pass
the validation for field `y`. The annotation of field `z` is well suited for
field `y` for its intended purpose.
```python
from typing import Union, Annotated
from pydantic import BaseModel, Field, StringConstraints
class Foo(BaseModel):
x: str = Field(..., pattern=r"^[0-9a-z]+$")
y: Union[int, str] = Field(..., pattern=r"^[0-9a-z]+$")
z: Union[int, Annotated[str, StringConstraints(pattern=r"^[0-9a-z]+$")]]
```
"""
self._attach_note(
"Warning: Pydantic core schema of type `'chain'` is encountered. "
"The translation may be less accurate. Often, the type annotation of the "
"corresponding field in the corresponding Pydantic model can be improved."
)

for schema_in_chain in schema["steps"]:
self._shape_slot(schema_in_chain)

def _lax_or_strict_schema(self, schema: core_schema.LaxOrStrictSchema) -> None:
raise TranslationNotImplementedError(
f"Translation of Pydantic core schema, {schema['type']}, is not "
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ pydantic
typer
pytest
pytest-xdist
pytest-mock
24 changes: 24 additions & 0 deletions tests/test_pydantic2linkml/test_gen_linkml.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from datetime import date, time, datetime
from enum import Enum
from uuid import UUID
from unittest.mock import call

import pytest
from linkml_runtime.linkml_model.meta import AnonymousSlotExpression
Expand Down Expand Up @@ -867,6 +868,29 @@ class Foo(BaseModel):
"Tagged union types are yet to be supported", slot.notes
)

def test_chain_schema(self, mocker):
class Foo(BaseModel):
x: Union[int, str] = Field(..., pattern=r"^[0-9a-z]+$")

field_schema = get_field_schema(Foo, "x")
slot_generator = SlotGenerator(field_schema)

# Initiate monitoring of the slot generation process
spy = mocker.spy(slot_generator, "_shape_slot")

slot = slot_generator.generate()

assert in_exactly_one_string(
"Warning: Pydantic core schema of type `'chain'` is encountered.",
slot.notes,
)

# Sure that translation is propagated to each schema in the chain
calls = (
call(schema_in_chain) for schema_in_chain in field_schema.schema["steps"]
)
spy.assert_has_calls(calls, any_order=True)

def test_model_schema(self):
class Bar(BaseModel):
y: int
Expand Down

0 comments on commit f35a596

Please sign in to comment.