diff --git a/pydantic/networks.py b/pydantic/networks.py index 9f46aa5bb01..2bbe5ef3d9b 100644 --- a/pydantic/networks.py +++ b/pydantic/networks.py @@ -464,14 +464,6 @@ def __init__(self, name: str, email: str): def __eq__(self, other: Any) -> bool: return isinstance(other, NameEmail) and (self.name, self.email) == (other.name, other.email) - @classmethod - def __get_pydantic_json_schema__( - cls, core_schema: core_schema.CoreSchema, handler: _schema_generation_shared.GetJsonSchemaHandler - ) -> JsonSchemaValue: - field_schema = handler(core_schema) - field_schema.update(type='string', format='name-email') - return field_schema - @classmethod def __get_pydantic_core_schema__( cls, @@ -479,7 +471,8 @@ def __get_pydantic_core_schema__( _handler: GetCoreSchemaHandler, ) -> core_schema.CoreSchema: import_email_validator() - return core_schema.no_info_after_validator_function( + + python_schema = core_schema.no_info_after_validator_function( cls._validate, core_schema.union_schema( [core_schema.is_instance_schema(cls), core_schema.str_schema()], @@ -489,13 +482,41 @@ def __get_pydantic_core_schema__( serialization=core_schema.to_string_ser_schema(), ) + json_schema = core_schema.no_info_after_validator_function( + cls._validate, + core_schema.union_schema( + [ + core_schema.str_schema(metadata={'format': 'name-email'}), + core_schema.typed_dict_schema( + { + 'name': core_schema.typed_dict_field(core_schema.str_schema()), + 'email': core_schema.typed_dict_field(core_schema.str_schema()), + } + ), + ], + custom_error_type='name_email_type', + custom_error_message='Input is not a valid NameEmail', + ), + serialization=core_schema.to_string_ser_schema(), + ) + + return core_schema.json_or_python_schema( + python_schema=python_schema, + json_schema=json_schema, + serialization=core_schema.to_string_ser_schema(), + ) + @classmethod - def _validate(cls, __input_value: NameEmail | str) -> NameEmail: + def _validate(cls, __input_value: NameEmail | dict[str, str] | str) -> NameEmail: if isinstance(__input_value, cls): return __input_value - else: - name, email = validate_email(__input_value) # type: ignore[arg-type] - return cls(name, email) + + if isinstance(__input_value, dict): + _, email = validate_email(__input_value['email']) + return cls(__input_value['name'], email) + + name, email = validate_email(__input_value) # type: ignore[arg-type] + return cls(name, email) def __str__(self) -> str: return f'{self.name} <{self.email}>' diff --git a/tests/test_networks.py b/tests/test_networks.py index 332b4ff98eb..dcd7f411543 100644 --- a/tests/test_networks.py +++ b/tests/test_networks.py @@ -844,12 +844,13 @@ def test_email_validator_not_installed(): @pytest.mark.skipif(not email_validator, reason='email_validator not installed') -def test_name_email(): +def test_name_email_py(): class Model(BaseModel): v: NameEmail assert str(Model(v=NameEmail('foo bar', 'foobaR@example.com')).v) == 'foo bar ' assert str(Model(v='foo bar ').v) == 'foo bar ' + assert str(Model(v='foobaR@example.com').v) == 'foobaR ' assert NameEmail('foo bar', 'foobaR@example.com') == NameEmail('foo bar', 'foobaR@example.com') assert NameEmail('foo bar', 'foobaR@example.com') != NameEmail('foo bar', 'different@example.com') @@ -858,3 +859,33 @@ class Model(BaseModel): assert exc_info.value.errors() == [ {'input': 1, 'loc': ('v',), 'msg': 'Input is not a valid NameEmail', 'type': 'name_email_type'} ] + + +@pytest.mark.skipif(not email_validator, reason='email_validator not installed') +def test_name_email_json(): + class Model(BaseModel): + v: NameEmail + + assert Model.model_validate_json('{"v":{"name":"foo bar","email":"foobaR@example.com"}}').v == NameEmail( + 'foo bar', 'foobaR@example.com' + ) + + assert Model.model_validate_json('{"v":{"name":"foo bar","email":"whatever "}}').v == NameEmail( + 'foo bar', 'foobaR@example.com' + ) + assert Model.model_validate_json('{"v":"foo bar "}').v == NameEmail( + 'foo bar', 'foobaR@example.com' + ) + + assert ( + Model(v=NameEmail('foo bar', 'foobaR@example.com')).model_dump_json() == '{"v":"foo bar "}' + ) + + +@pytest.mark.skipif(not email_validator, reason='email_validator not installed') +def test_name_email_json_schema(): + class Model(BaseModel): + v: NameEmail + + schema = Model.model_json_schema() + assert schema['properties']['v']['anyOf'][0] == {'type': 'string', 'format': 'name-email'}