From 8ff235ae86344b4963444466785d97a3e6cb8108 Mon Sep 17 00:00:00 2001 From: sydney-runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Tue, 3 Oct 2023 11:05:50 -0500 Subject: [PATCH] Fix failure to load `SecretStr` from JSON (regression in v2.4) (#7729) Co-authored-by: David Montague <35119617+dmontagu@users.noreply.github.com> --- .github/workflows/ci.yml | 14 +++++++------- .github/workflows/docs-update.yml | 2 +- pydantic/types.py | 24 ++++++++++++++---------- tests/test_types.py | 18 +++++++++++++++--- 4 files changed, 37 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f4f2c500cd..b0eb2a643a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,7 +32,7 @@ jobs: - name: install run: | - pdm use -f $PYTHON + pdm venv create --with-pip --force $PYTHON pdm install -G linting -G email - uses: pre-commit/action@v3.0.0 @@ -56,7 +56,7 @@ jobs: # Because the secret for accessing the library is not accessible from forks, but we still want to run # this job on public CI runs. run: | - pdm venv create --with-pip $PYTHON + pdm venv create --with-pip --force $PYTHON pdm install -G docs - run: pdm run python -c 'import docs.plugins.main' @@ -86,7 +86,7 @@ jobs: - name: install deps run: | - pdm use -f $PYTHON + pdm venv create --with-pip --force $PYTHON pdm install -G testing -G testing-extra -G email -G memray pdm add pytest-memray @@ -130,7 +130,7 @@ jobs: - name: install deps run: | - pdm use -f $PYTHON + pdm venv create --with-pip --force $PYTHON pdm install -G testing -G email - run: pdm info && pdm list @@ -191,7 +191,7 @@ jobs: - name: install deps run: | - pdm use -f $PYTHON + pdm venv create --with-pip --force $PYTHON pdm install -G testing - name: install example plugin @@ -240,7 +240,7 @@ jobs: - name: install deps run: | - pdm use -f $PYTHON + pdm venv create --with-pip --force $PYTHON pdm install -G testing -G mypy - name: install mypy @@ -337,7 +337,7 @@ jobs: - name: install deps run: | - pdm use -f $PYTHON + pdm venv create --with-pip --force $PYTHON pdm install -G testing -G email - name: install typing-extensions diff --git a/.github/workflows/docs-update.yml b/.github/workflows/docs-update.yml index 2dac3bce52..e7217c23a6 100644 --- a/.github/workflows/docs-update.yml +++ b/.github/workflows/docs-update.yml @@ -72,7 +72,7 @@ jobs: - name: install run: | - pdm venv create --with-pip $PYTHON + pdm venv create --with-pip --force $PYTHON pdm install -G docs pdm run python -m pip install https://files.scolvin.com/${MKDOCS_TOKEN}/mkdocs_material-9.4.2+insiders.4.42.0-py3-none-any.whl env: diff --git a/pydantic/types.py b/pydantic/types.py index 5d1bffa65e..018cf2ee38 100644 --- a/pydantic/types.py +++ b/pydantic/types.py @@ -1448,16 +1448,20 @@ def get_json_schema(_core_schema: core_schema.CoreSchema, handler: GetJsonSchema ) return json_schema - s = core_schema.union_schema( - [ - core_schema.is_instance_schema(source), - core_schema.no_info_after_validator_function( - source, # construct the type - inner_schema, - ), - ], - strict=True, - custom_error_type=error_kind, + json_schema = core_schema.no_info_after_validator_function( + source, # construct the type + inner_schema, + ) + s = core_schema.json_or_python_schema( + python_schema=core_schema.union_schema( + [ + core_schema.is_instance_schema(source), + json_schema, + ], + strict=True, + custom_error_type=error_kind, + ), + json_schema=json_schema, serialization=core_schema.plain_serializer_function_ser_schema( serialize, info_arg=True, diff --git a/tests/test_types.py b/tests/test_types.py index 8e87822eb1..996157e8fd 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -3921,13 +3921,25 @@ class Foobar(BaseModel): ] -def test_secretstr(): +@pytest.mark.parametrize('validate_json', [True, False]) +def test_secretstr(validate_json): class Foobar(BaseModel): password: SecretStr empty_password: SecretStr - # Initialize the model. - f = Foobar(password='1234', empty_password='') + if validate_json: + f = Foobar.model_validate_json('{"password": "1234", "empty_password": ""}') + with pytest.raises(ValidationError) as exc_info: + Foobar.model_validate_json('{"password": 1234, "empty_password": null}') + else: + f = Foobar(password='1234', empty_password='') + with pytest.raises(ValidationError) as exc_info: + Foobar(password=1234, empty_password=None) + + assert exc_info.value.errors(include_url=False) == [ + {'type': 'string_type', 'loc': ('password',), 'msg': 'Input should be a valid string', 'input': 1234}, + {'type': 'string_type', 'loc': ('empty_password',), 'msg': 'Input should be a valid string', 'input': None}, + ] # Assert correct types. assert f.password.__class__.__name__ == 'SecretStr'