Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ make install
That should be it, the example shown above should now run.

You might find it useful to look at [`pydantic_core/_pydantic_core.pyi`](./pydantic_core/_pydantic_core.pyi) and
[`pydantic_core/schema_types.py`](./pydantic_core/schema_types.py) for more information on the python API,
[`pydantic_core/core_schema.py`](./pydantic_core/core_schema.py) for more information on the python API,
beyond that, [`tests/`](./tests) provide a large number of examples of usage.

If you want to contribute to pydantic-core, you'll want to use some other make commands:
Expand Down
2 changes: 1 addition & 1 deletion build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::process::Command;
use std::str::from_utf8;

fn generate_self_schema() {
println!("cargo:rerun-if-changed=pydantic_core/schema_types.py");
println!("cargo:rerun-if-changed=pydantic_core/core_schema.py");
println!("cargo:rerun-if-changed=generate_self_schema.py");
if Path::new("./src/self_schema.py").exists() && option_env!("DEBIAN_FRONTEND") == Some("noninteractive") {
// self_schema.py already exists and DEBIAN_FRONTEND indicates we're in a maturin build,
Expand Down
47 changes: 27 additions & 20 deletions generate_self_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
This script generates the schema for the schema - e.g.
a definition of what inputs can be provided to `SchemaValidator()`.

The schema is generated from `pydantic_core/schema_types.py`.
The schema is generated from `pydantic_core/core_schema.py`.
"""
import importlib.util
import re
from collections.abc import Callable
from datetime import date, datetime, time, timedelta
from pathlib import Path
from typing import Any, Dict, ForwardRef, List, Type, Union
from typing import TYPE_CHECKING, Any, Dict, ForwardRef, List, Type, Union

from black import Mode, TargetVersion, format_file_contents
from typing_extensions import get_args, is_typeddict
Expand All @@ -25,10 +25,15 @@ def get_origin(t):
THIS_DIR = Path(__file__).parent
SAVE_PATH = THIS_DIR / 'src' / 'self_schema.py'

# can't import schema_types.py directly as pydantic-core might not be installed
core_types_spec = importlib.util.spec_from_file_location('_typing', str(THIS_DIR / 'pydantic_core' / 'schema_types.py'))
core_types = importlib.util.module_from_spec(core_types_spec)
core_types_spec.loader.exec_module(core_types)
if TYPE_CHECKING:
from pydantic_core import core_schema
else:
# can't import core_schema.py directly as pydantic-core might not be installed
core_schema_spec = importlib.util.spec_from_file_location(
'_typing', str(THIS_DIR / 'pydantic_core' / 'core_schema.py')
)
core_schema = importlib.util.module_from_spec(core_schema_spec)
core_schema_spec.loader.exec_module(core_schema)

# the validator for referencing schema (Schema is used recursively, so has to use a reference)
schema_ref_validator = {'type': 'recursive-ref', 'schema_ref': 'root-schema'}
Expand All @@ -53,7 +58,7 @@ def get_schema(obj):
return union_schema(obj)
elif obj is Callable or origin is Callable:
return 'callable'
elif origin is core_types.Literal:
elif origin is core_schema.Literal:
expected = all_literal_values(obj)
assert expected, f'literal "expected" cannot be empty, obj={obj}'
return {'type': 'literal', 'expected': expected}
Expand Down Expand Up @@ -91,23 +96,23 @@ def type_dict_schema(typed_dict):
if matched:
required = True

if 'Schema' == fr_arg or re.search('[^a-zA-Z]Schema', fr_arg):
if fr_arg == 'Schema':
if 'CoreSchemaCombined' == fr_arg or re.search('[^a-zA-Z]CoreSchemaCombined', fr_arg):
if fr_arg == 'CoreSchemaCombined':
schema = schema_ref_validator
elif fr_arg == 'List[Schema]':
elif fr_arg == 'List[CoreSchemaCombined]':
schema = {'type': 'list', 'items_schema': schema_ref_validator}
elif fr_arg == 'Dict[str, Schema]':
elif fr_arg == 'Dict[str, CoreSchemaCombined]':
schema = {'type': 'dict', 'keys_schema': 'str', 'values_schema': schema_ref_validator}
else:
raise ValueError(f'Unknown Schema forward ref: {fr_arg}')
else:
field_type = eval_forward_ref(field_type)

if schema is None:
if get_origin(field_type) == core_types.Required:
if get_origin(field_type) == core_schema.Required:
required = True
field_type = field_type.__args__[0]
if get_origin(field_type) == core_types.NotRequired:
if get_origin(field_type) == core_schema.NotRequired:
required = False
field_type = field_type.__args__[0]

Expand All @@ -123,7 +128,7 @@ def union_schema(union_type):


def all_literal_values(type_):
if get_origin(type_) is core_types.Literal:
if get_origin(type_) is core_schema.Literal:
values = get_args(type_)
return [x for value in values for x in all_literal_values(value)]
else:
Expand All @@ -132,23 +137,25 @@ def all_literal_values(type_):

def eval_forward_ref(type_):
try:
return type_._evaluate(core_types.__dict__, None, set())
return type_._evaluate(core_schema.__dict__, None, set())
except TypeError:
# for older python (3.7 at least)
return type_._evaluate(core_types.__dict__, None)
return type_._evaluate(core_schema.__dict__, None)


def main():
schema_union = core_types.Schema
assert get_origin(schema_union) is Union, 'expected pydantic_core.schema_types.Schema to be a union'
schema_union = core_schema.CoreSchema
assert get_origin(schema_union) is Union, 'expected core_schema.CoreSchema to be a Union'
schema_strings = core_schema.CoreSchemaStrings
assert get_origin(schema_strings) is core_schema.Literal, 'expected core_schema.CoreSchemaStrings to be a Literal'

schema = {
'type': 'tagged-union',
'ref': 'root-schema',
'discriminator': 'self-schema-discriminator',
'choices': {'plain-string': get_schema(schema_union.__args__[0])},
'choices': {'plain-string': get_schema(schema_strings)},
}
for s in schema_union.__args__[1:]:
for s in schema_union.__args__:
type_ = s.__annotations__['type']
m = re.search(r"Literal\['(.+?)']", type_.__forward_arg__)
assert m, f'Unknown schema type: {type_}'
Expand Down
12 changes: 10 additions & 2 deletions pydantic_core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
from ._pydantic_core import PydanticValueError, SchemaError, SchemaValidator, ValidationError, __version__
from .schema_types import Config, Schema
from .core_schema import CoreConfig, CoreSchema

__all__ = '__version__', 'Config', 'Schema', 'SchemaValidator', 'SchemaError', 'ValidationError', 'PydanticValueError'
__all__ = (
'__version__',
'CoreConfig',
'CoreSchema',
'SchemaValidator',
'SchemaError',
'ValidationError',
'PydanticValueError',
)
4 changes: 2 additions & 2 deletions pydantic_core/_pydantic_core.pyi
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import sys
from typing import Any, TypedDict

from pydantic_core.schema_types import Config, Schema
from pydantic_core.core_schema import CoreConfig, CoreSchemaCombined

if sys.version_info < (3, 11):
from typing_extensions import NotRequired
Expand All @@ -12,7 +12,7 @@ __all__ = '__version__', 'SchemaValidator', 'SchemaError', 'ValidationError', 'P
__version__: str

class SchemaValidator:
def __init__(self, schema: Schema, config: 'Config | None' = None) -> None: ...
def __init__(self, schema: CoreSchemaCombined, config: 'CoreConfig | None' = None) -> None: ...
def validate_python(self, input: Any, strict: 'bool | None' = None, context: Any = None) -> Any: ...
def isinstance_python(self, input: Any, strict: 'bool | None' = None, context: Any = None) -> bool: ...
def validate_json(
Expand Down
Loading