Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@aboubacs
Copy link
Contributor

@aboubacs aboubacs commented May 31, 2022

This PR removes a side effect in the jsonable_encoder function, which was modifying the json_encoders configuration of pydantic models when any instance of that model was given to the function with a custom encoder, which fixes this issue: #4962
Explanation: #4972 (comment)

UPD YuriiMotov: it also attempted to fix the following issue in tests, but I reverted this change since it should be done in separate PR and in consistent way:

  • It changes the custom_encoder test to change the encoding from o.isoformat() to o.strftime("%H:%M:%S"), because isoformat is the already the default pydantic encoder for datetime. The test wasn't properly testing the feature because it could pass even without the custom_encoder

@codecov

This comment was marked as outdated.

@github-actions

This comment was marked as outdated.

@odiseo0
Copy link

odiseo0 commented Jun 8, 2022

Clean PR

encoder = getattr(obj.__config__, "json_encoders", {})
if custom_encoder:
encoder.update(custom_encoder)
encoder = {**encoder, **custom_encoder}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain, how does this fix the jsonable_encoder side effect?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The following snippet from slackner illustrates the side effect in 2 cases:

  • With print(c) : side effect from b = jsonable_encoder(creds, custom_encoder=ENCODERS) when calling jsonable_encoder later on the same instance but with a different custom_encoder
  • With print(d) : side effect from b = jsonable_encoder(creds, custom_encoder=ENCODERS) when calling jsonable_encoder later on another instance
from pydantic import BaseModel, SecretStr
from fastapi.encoders import jsonable_encoder


class Credentials(BaseModel):
    password: SecretStr


ENCODERS = {SecretStr: lambda v: v.get_secret_value() if v is not None else None}

creds = Credentials(password="helloworld")

a = jsonable_encoder(creds)
print(a)  # {'password': '**********'}, as expected
b = jsonable_encoder(creds, custom_encoder=ENCODERS)
print(b)  # {'password': 'helloworld'}, as expected
c = jsonable_encoder(creds)
print(c)  # gives {'password': 'helloworld'}, but should be {'password': '**********'}?


creds = Credentials(password="123456789")

d = jsonable_encoder(creds)
print(d)  # gives {'password': '123456789'}, but should be {'password': '**********'}?

Before this PR, calling jsonable_encoder with a custom_encoder updates the global "json_encoders" config of the model.
After this PR, calling jsonable_encoder with a custom_encoer argument only affects the current call

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much, easy to understand

@aboubacs
Copy link
Contributor Author

aboubacs commented Oct 6, 2022

What's the next step on this ? @odiseo0 @nurettinabaci

@aboubacs aboubacs force-pushed the fix/custom_encoder_side_effect branch from a57928d to 76cc8d7 Compare November 7, 2022 12:04
@github-actions

This comment was marked as outdated.

@github-actions

This comment was marked as outdated.

@aboubacs
Copy link
Contributor Author

aboubacs commented Mar 27, 2023

Any plan to include this fix in a future release ? The bug seems to still be here on the master branch, it happens when you use multiple jsonable_encoders on the same model

@github-actions
Copy link
Contributor

πŸ“ Docs preview for commit 50aa786 at: https://6421d467c393971f9f7d59a6--fastapi.netlify.app

@YuriiMotov YuriiMotov force-pushed the fix/custom_encoder_side_effect branch 2 times, most recently from 7bb362f to fa4b7c8 Compare July 10, 2025 21:22
Copy link
Member

@YuriiMotov YuriiMotov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@aboubacs, good catch! Thank you!

There are several more occurrences of o.isoformat() in tests. I think it should be addressed in separate PR and in consistent way (all occurrences of the problem)

@YuriiMotov YuriiMotov changed the title Fix/custom encoder side effect πŸ› Fix jsonable_encoder alters json_encoders of Pydantic objects Jul 10, 2025
@YuriiMotov YuriiMotov added bug Something isn't working and removed refactor labels Jul 10, 2025
Copy link
Member

@tiangolo tiangolo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, thank you! πŸ™Œ β˜•

@tiangolo tiangolo changed the title πŸ› Fix jsonable_encoder alters json_encoders of Pydantic objects πŸ› Fix jsonable_encoder alters json_encoders of Pydantic v1 objects Sep 20, 2025
@tiangolo tiangolo merged commit 2dc769b into fastapi:master Sep 20, 2025
59 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working p4

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants