This commit is contained in:
@@ -1,12 +1,10 @@
|
||||
"""!!! abstract "Usage Documentation"
|
||||
[Build a Plugin](../concepts/plugins.md#build-a-plugin)
|
||||
"""Usage docs: https://docs.pydantic.dev/2.4/concepts/plugins#build-a-plugin
|
||||
|
||||
Plugin interface for Pydantic plugins, and related types.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Callable, Literal, NamedTuple
|
||||
from typing import Any, Callable
|
||||
|
||||
from pydantic_core import CoreConfig, CoreSchema, ValidationError
|
||||
from typing_extensions import Protocol, TypeAlias
|
||||
@@ -18,32 +16,17 @@ __all__ = (
|
||||
'ValidateJsonHandlerProtocol',
|
||||
'ValidateStringsHandlerProtocol',
|
||||
'NewSchemaReturns',
|
||||
'SchemaTypePath',
|
||||
'SchemaKind',
|
||||
)
|
||||
|
||||
NewSchemaReturns: TypeAlias = 'tuple[ValidatePythonHandlerProtocol | None, ValidateJsonHandlerProtocol | None, ValidateStringsHandlerProtocol | None]'
|
||||
|
||||
|
||||
class SchemaTypePath(NamedTuple):
|
||||
"""Path defining where `schema_type` was defined, or where `TypeAdapter` was called."""
|
||||
|
||||
module: str
|
||||
name: str
|
||||
|
||||
|
||||
SchemaKind: TypeAlias = Literal['BaseModel', 'TypeAdapter', 'dataclass', 'create_model', 'validate_call']
|
||||
|
||||
|
||||
class PydanticPluginProtocol(Protocol):
|
||||
"""Protocol defining the interface for Pydantic plugins."""
|
||||
|
||||
def new_schema_validator(
|
||||
self,
|
||||
schema: CoreSchema,
|
||||
schema_type: Any,
|
||||
schema_type_path: SchemaTypePath,
|
||||
schema_kind: SchemaKind,
|
||||
config: CoreConfig | None,
|
||||
plugin_settings: dict[str, object],
|
||||
) -> tuple[
|
||||
@@ -57,9 +40,6 @@ class PydanticPluginProtocol(Protocol):
|
||||
|
||||
Args:
|
||||
schema: The schema to validate against.
|
||||
schema_type: The original type which the schema was created from, e.g. the model class.
|
||||
schema_type_path: Path defining where `schema_type` was defined, or where `TypeAdapter` was called.
|
||||
schema_kind: The kind of schema to validate against.
|
||||
config: The config to use for validation.
|
||||
plugin_settings: Any plugin settings.
|
||||
|
||||
@@ -96,14 +76,6 @@ class BaseValidateHandlerProtocol(Protocol):
|
||||
"""
|
||||
return
|
||||
|
||||
def on_exception(self, exception: Exception) -> None:
|
||||
"""Callback to be notified of validation exceptions.
|
||||
|
||||
Args:
|
||||
exception: The exception raised during validation.
|
||||
"""
|
||||
return
|
||||
|
||||
|
||||
class ValidatePythonHandlerProtocol(BaseValidateHandlerProtocol, Protocol):
|
||||
"""Event handler for `SchemaValidator.validate_python`."""
|
||||
@@ -116,8 +88,6 @@ class ValidatePythonHandlerProtocol(BaseValidateHandlerProtocol, Protocol):
|
||||
from_attributes: bool | None = None,
|
||||
context: dict[str, Any] | None = None,
|
||||
self_instance: Any | None = None,
|
||||
by_alias: bool | None = None,
|
||||
by_name: bool | None = None,
|
||||
) -> None:
|
||||
"""Callback to be notified of validation start, and create an instance of the event handler.
|
||||
|
||||
@@ -128,8 +98,6 @@ class ValidatePythonHandlerProtocol(BaseValidateHandlerProtocol, Protocol):
|
||||
context: The context to use for validation, this is passed to functional validators.
|
||||
self_instance: An instance of a model to set attributes on from validation, this is used when running
|
||||
validation from the `__init__` method of a model.
|
||||
by_alias: Whether to use the field's alias to match the input data to an attribute.
|
||||
by_name: Whether to use the field's name to match the input data to an attribute.
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -144,8 +112,6 @@ class ValidateJsonHandlerProtocol(BaseValidateHandlerProtocol, Protocol):
|
||||
strict: bool | None = None,
|
||||
context: dict[str, Any] | None = None,
|
||||
self_instance: Any | None = None,
|
||||
by_alias: bool | None = None,
|
||||
by_name: bool | None = None,
|
||||
) -> None:
|
||||
"""Callback to be notified of validation start, and create an instance of the event handler.
|
||||
|
||||
@@ -155,8 +121,6 @@ class ValidateJsonHandlerProtocol(BaseValidateHandlerProtocol, Protocol):
|
||||
context: The context to use for validation, this is passed to functional validators.
|
||||
self_instance: An instance of a model to set attributes on from validation, this is used when running
|
||||
validation from the `__init__` method of a model.
|
||||
by_alias: Whether to use the field's alias to match the input data to an attribute.
|
||||
by_name: Whether to use the field's name to match the input data to an attribute.
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -168,13 +132,7 @@ class ValidateStringsHandlerProtocol(BaseValidateHandlerProtocol, Protocol):
|
||||
"""Event handler for `SchemaValidator.validate_strings`."""
|
||||
|
||||
def on_enter(
|
||||
self,
|
||||
input: StringInput,
|
||||
*,
|
||||
strict: bool | None = None,
|
||||
context: dict[str, Any] | None = None,
|
||||
by_alias: bool | None = None,
|
||||
by_name: bool | None = None,
|
||||
self, input: StringInput, *, strict: bool | None = None, context: dict[str, Any] | None = None
|
||||
) -> None:
|
||||
"""Callback to be notified of validation start, and create an instance of the event handler.
|
||||
|
||||
@@ -182,7 +140,5 @@ class ValidateStringsHandlerProtocol(BaseValidateHandlerProtocol, Protocol):
|
||||
input: The string data to be validated.
|
||||
strict: Whether to validate the object in strict mode.
|
||||
context: The context to use for validation, this is passed to functional validators.
|
||||
by_alias: Whether to use the field's alias to match the input data to an attribute.
|
||||
by_name: Whether to use the field's name to match the input data to an attribute.
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.metadata as importlib_metadata
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
from collections.abc import Iterable
|
||||
from typing import TYPE_CHECKING, Final
|
||||
from typing import TYPE_CHECKING, Iterable
|
||||
|
||||
from typing_extensions import Final
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
import importlib.metadata as importlib_metadata
|
||||
else:
|
||||
import importlib_metadata
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import PydanticPluginProtocol
|
||||
@@ -24,13 +30,10 @@ def get_plugins() -> Iterable[PydanticPluginProtocol]:
|
||||
|
||||
Inspired by: https://github.com/pytest-dev/pluggy/blob/1.3.0/src/pluggy/_manager.py#L376-L402
|
||||
"""
|
||||
disabled_plugins = os.getenv('PYDANTIC_DISABLE_PLUGINS')
|
||||
global _plugins, _loading_plugins
|
||||
if _loading_plugins:
|
||||
# this happens when plugins themselves use pydantic, we return no plugins
|
||||
return ()
|
||||
elif disabled_plugins in ('__all__', '1', 'true'):
|
||||
return ()
|
||||
elif _plugins is None:
|
||||
_plugins = {}
|
||||
# set _loading_plugins so any plugins that use pydantic don't themselves use plugins
|
||||
@@ -42,8 +45,6 @@ def get_plugins() -> Iterable[PydanticPluginProtocol]:
|
||||
continue
|
||||
if entry_point.value in _plugins:
|
||||
continue
|
||||
if disabled_plugins is not None and entry_point.name in disabled_plugins.split(','):
|
||||
continue
|
||||
try:
|
||||
_plugins[entry_point.value] = entry_point.load()
|
||||
except (ImportError, AttributeError) as e:
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
"""Pluggable schema validator for pydantic."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import functools
|
||||
from collections.abc import Iterable
|
||||
from typing import TYPE_CHECKING, Any, Callable, Literal, TypeVar
|
||||
from typing import TYPE_CHECKING, Any, Callable, Iterable, TypeVar
|
||||
|
||||
from pydantic_core import CoreConfig, CoreSchema, SchemaValidator, ValidationError
|
||||
from typing_extensions import ParamSpec
|
||||
from typing_extensions import Literal, ParamSpec
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import BaseValidateHandlerProtocol, PydanticPluginProtocol, SchemaKind, SchemaTypePath
|
||||
from . import BaseValidateHandlerProtocol, PydanticPluginProtocol
|
||||
|
||||
|
||||
P = ParamSpec('P')
|
||||
@@ -20,33 +18,18 @@ events: list[Event] = list(Event.__args__) # type: ignore
|
||||
|
||||
|
||||
def create_schema_validator(
|
||||
schema: CoreSchema,
|
||||
schema_type: Any,
|
||||
schema_type_module: str,
|
||||
schema_type_name: str,
|
||||
schema_kind: SchemaKind,
|
||||
config: CoreConfig | None = None,
|
||||
plugin_settings: dict[str, Any] | None = None,
|
||||
) -> SchemaValidator | PluggableSchemaValidator:
|
||||
schema: CoreSchema, config: CoreConfig | None = None, plugin_settings: dict[str, Any] | None = None
|
||||
) -> SchemaValidator:
|
||||
"""Create a `SchemaValidator` or `PluggableSchemaValidator` if plugins are installed.
|
||||
|
||||
Returns:
|
||||
If plugins are installed then return `PluggableSchemaValidator`, otherwise return `SchemaValidator`.
|
||||
"""
|
||||
from . import SchemaTypePath
|
||||
from ._loader import get_plugins
|
||||
|
||||
plugins = get_plugins()
|
||||
if plugins:
|
||||
return PluggableSchemaValidator(
|
||||
schema,
|
||||
schema_type,
|
||||
SchemaTypePath(schema_type_module, schema_type_name),
|
||||
schema_kind,
|
||||
config,
|
||||
plugins,
|
||||
plugin_settings or {},
|
||||
)
|
||||
return PluggableSchemaValidator(schema, config, plugins, plugin_settings or {}) # type: ignore
|
||||
else:
|
||||
return SchemaValidator(schema, config)
|
||||
|
||||
@@ -59,9 +42,6 @@ class PluggableSchemaValidator:
|
||||
def __init__(
|
||||
self,
|
||||
schema: CoreSchema,
|
||||
schema_type: Any,
|
||||
schema_type_path: SchemaTypePath,
|
||||
schema_kind: SchemaKind,
|
||||
config: CoreConfig | None,
|
||||
plugins: Iterable[PydanticPluginProtocol],
|
||||
plugin_settings: dict[str, Any],
|
||||
@@ -72,12 +52,7 @@ class PluggableSchemaValidator:
|
||||
json_event_handlers: list[BaseValidateHandlerProtocol] = []
|
||||
strings_event_handlers: list[BaseValidateHandlerProtocol] = []
|
||||
for plugin in plugins:
|
||||
try:
|
||||
p, j, s = plugin.new_schema_validator(
|
||||
schema, schema_type, schema_type_path, schema_kind, config, plugin_settings
|
||||
)
|
||||
except TypeError as e: # pragma: no cover
|
||||
raise TypeError(f'Error using plugin `{plugin.__module__}:{plugin.__class__.__name__}`: {e}') from e
|
||||
p, j, s = plugin.new_schema_validator(schema, config, plugin_settings)
|
||||
if p is not None:
|
||||
python_event_handlers.append(p)
|
||||
if j is not None:
|
||||
@@ -100,7 +75,6 @@ def build_wrapper(func: Callable[P, R], event_handlers: list[BaseValidateHandler
|
||||
on_enters = tuple(h.on_enter for h in event_handlers if filter_handlers(h, 'on_enter'))
|
||||
on_successes = tuple(h.on_success for h in event_handlers if filter_handlers(h, 'on_success'))
|
||||
on_errors = tuple(h.on_error for h in event_handlers if filter_handlers(h, 'on_error'))
|
||||
on_exceptions = tuple(h.on_exception for h in event_handlers if filter_handlers(h, 'on_exception'))
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
||||
@@ -113,10 +87,6 @@ def build_wrapper(func: Callable[P, R], event_handlers: list[BaseValidateHandler
|
||||
for on_error_handler in on_errors:
|
||||
on_error_handler(error)
|
||||
raise
|
||||
except Exception as exception:
|
||||
for on_exception_handler in on_exceptions:
|
||||
on_exception_handler(exception)
|
||||
raise
|
||||
else:
|
||||
for on_success_handler in on_successes:
|
||||
on_success_handler(result)
|
||||
|
||||
Reference in New Issue
Block a user