Skip to content

Fix regression when creating nested ConfigurableResource with pydantic >= v.2.5.0 #28836

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

HynekBlaha
Copy link
Contributor

@HynekBlaha HynekBlaha commented Mar 28, 2025

Summary & Motivation

Fixes: #18272

We are using pydantic BaseSettings to define dagster code deployment configurations. Our config framework dumps the config content into json, which we save to configmap for better visibility of changes in our workloads. We then create config instance from the json dict to maintain 1:1 correctness.

In pydantic v2.5.0, there was a change in validation of Unions: pydantic/pydantic-core#867.
As PartialResource is very lax, it passes the validation, but immediately fails on TypeError.
I tried several alternatives, but this one is least invasive and works best.

Since the issue appeared, we tried to:

  • reduce the number of nested ConfigurableResources in our codebase
  • pin the pydantic == v2.4.2 (very old, released on 2023-09-27 )

But eventually, we needed new pydantic features in other projects and got to the point we needed to transitively upgrade pydantic in our dagster projects.

How I Tested These Changes

First, let's verify that this piece of code used to work with pydantic v2.4.2 ✅

git checkout ec9048878b9f8d81e3a0bb6284df0f0de493da68
uv pip install pydantic==2.4.2
python -m pytest python_modules/dagster/dagster_tests/core_tests/resource_tests/pythonic_resources
================================================================================================ 121 passed, 4 deselected in 7.66s ================================================================================================

but doesn't work with latest pydantic version ❌

uv pip install pydantic --upgrade
python -m pytest python_modules/dagster/dagster_tests/core_tests/resource_tests/pythonic_resources
===================================================================================================== short test summary info =====================================================================================================
FAILED python_modules/dagster/dagster_tests/core_tests/resource_tests/pythonic_resources/test_nesting.py::test_nested_resources_direct_config_fully_populated - TypeError: PartialResource.__init__() got an unexpected keyword argument 'b'
=========================================================================================== 1 failed, 120 passed, 4 deselected in 6.95s ===========================================================================================
 

Then checkout the second commit where I fix the PartialResource.init() and rerun the tests again. ✅

Changelog

Fix regression where nested ResourceDefinition could not be validated from fully populated dict.

):
resource_pointers, _data_without_resources = separate_resource_params(resource_cls, data)

super().__init__(data=data, resource_cls=resource_cls) # type: ignore # extends BaseModel, takes kwargs
Copy link
Contributor Author

@HynekBlaha HynekBlaha Mar 28, 2025

Choose a reason for hiding this comment

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

I changed the order, because it starts working again if pydantic.ValidationError is raised instead of TypeError.
So I run the validation ASAP.

data: dict[str, Any],
resource_cls: type[ConfigurableResourceFactory[TResValue]] | None = None,
data: dict[str, Any] | None = None,
**kwargs: Any,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

While this isn't the prettiest, the resource_cls and data are in fact not provided to init in all cases.
Alternatively, we could make it just accept kwargs and retrieve the resource_cls and data in the method body.

@HynekBlaha
Copy link
Contributor Author

Hello @benpankow, I made this fix for an old regression that was bothering us for a long time.
Git blame told me you were the author of this part of code, could you please take a look at it?
I tried to make it as least invasive as possible. All previous tests are passing.
Thanks ❤️

@deepyaman deepyaman requested a review from benpankow March 28, 2025 16:00
@HynekBlaha
Copy link
Contributor Author

HynekBlaha commented Apr 2, 2025

Hello @benpankow would you have a few minutes to check this small PR? 🤞 I would be very happy if we could get it into following release.

@benpankow
Copy link
Member

Thanks for the PR @HynekBlaha! I made a slight tweak in #28949 which enforces the pre-Pydantic 2.5 union rule. I think this should also fix the issue and doesn't require us to adjust the signature of PartialResource.

@HynekBlaha
Copy link
Contributor Author

HynekBlaha commented Apr 2, 2025

Looks great, @benpankow! Closing this issue in favour of your version. Thank you for finding time to look at it! 👍

@HynekBlaha HynekBlaha closed this Apr 2, 2025
benpankow added a commit that referenced this pull request Apr 3, 2025
…c >= v.2.5.0 (#28949)

## Summary

Slight tweak of @HynekBlaha's contribution in #28836 - this version
adjusts our Pydantic union behavior for resources to account for a
change in Pydantic 2.5.0 which uses 'smart unions'
(pydantic/pydantic-core#867). This smart union
behavior attempted to initialize each member of the union using the
input data, which called the constructor for `PartialResource` with
invalid inputs.

This PR enforces left-to-right union evaluation, similar to what
occurred pre-Pydantic 2.5.0.

## Test Plan

Unit test introduced in #28836.

## Changelog

Fixed an issue where using partial resources in Pydantic >= 2.5.0 could
result in an unexpected keyword argument TypeError. (Thanks
[@HynekBlaha](https://github.com/HynekBlaha)!)

---------

Co-authored-by: Hynek Blaha <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

PartialResource.__init__() got an unexpected keyword argument (with Pydantic ~2.5)
3 participants