Skip to content
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

Raise ValidationError for unhashable discriminator values #5132

Merged

Conversation

kurtmckee
Copy link

As reported in #4773, unhashable discriminator values are incorrectly rejected with a ConfigError, rather than a ValidationError.

The bug

The current code catches a TypeError at line 1121 and converts it to a ConfigError:

pydantic/pydantic/fields.py

Lines 1120 to 1127 in 7f3b754

try:
sub_field = self.sub_fields_mapping[discriminator_value] # type: ignore[index]
except TypeError:
assert cls is not None
raise ConfigError(
f'field "{self.name}" not yet prepared so type is still a ForwardRef, '
f'you might need to call {cls.__name__}.update_forward_refs().'
)

This can happen when self.sub_fields_mapping is None, in which case the ConfigError correctly suggests calling .update_forward_refs(). For reference, the exact error is:

None[discriminator_value]  # TypeError: 'NoneType' object is not subscriptable

However, a TypeError can also be raised when self.sub_fields_mapping is a dictionary but discriminator_value is an unhashable type. For reference, the exact error is:

{"valid": "dict"}[{}]  # TypeError: unhashable type: 'dict'

The fix

This PR addresses the bug by first checking whether self.sub_fields_mapping is None. If it is, then a ConfigError is raised as expected. Then, both KeyError and TypeError are interpreted as an invalid discriminator value.

Please let me know if this needs to be separately forward-ported to main.

Thanks for your work on pydantic!

Fixes #4773

@kurtmckee kurtmckee force-pushed the fix-incorrect-error-issue-4773 branch from a7e96d3 to 221f9ed Compare March 2, 2023 23:22
@kurtmckee
Copy link
Author

The GitHub workflow failed to start for this. If it's not possible to re-run the failed jobs, let me know and I'll push an update to re-trigger the workflow.

@hramezani hramezani closed this Mar 3, 2023
@hramezani hramezani reopened this Mar 3, 2023
@hramezani
Copy link
Member

Thanks @kurtmckee for this.

The fix LGTM.

The only concern is that it is a change in old behavior(raising ConfigError) and I am afraid users depend on this(catching ConfigError on their code base).

Please let me know if this needs to be separately forward-ported to main.

I think it's not needed.

@samuelcolvin, @dmontagu what do you think?

pydantic/fields.py Outdated Show resolved Hide resolved
@dmontagu
Copy link
Contributor

dmontagu commented Mar 3, 2023

@hramezani

The only concern is that it is a change in old behavior(raising ConfigError) and I am afraid users depend on this(catching ConfigError on their code base).

I think this is a very good point. I'm definitely keen to make sure this doesn't happen in v2, but I'd be a bit nervous to change the error handling in older versions unless this was causing issues that couldn't be properly handled by catching a ConfigError in the right place.

@hramezani
Copy link
Member

@dmontagu

but I'd be a bit nervous to change the error handling in older versions unless this was causing issues that couldn't be properly handled by catching a ConfigError in the right place.

Agree!

@kurtmckee
Copy link
Author

It's my understanding that ConfigError would indicate a problem with the users' models, which is not the case here.

This appears to be a ValidationError, which is what users would anticipate having to catch when the discriminator key is invalid. I think that fixing this aligns the behavior with user expectations and code, and helps prevent production code from breaking due to an incorrect exception.

@dmontagu
Copy link
Contributor

dmontagu commented Mar 3, 2023

@kurtmckee I agree that what you are describing is better behavior, the main point is that if someone is already handling it as a ConfigError, it could end up breaking their code if they upgrade. If we were still planning additional releases to pydantic v1 this would definitely make sense as an addition to a 1.11.0 release, but we aren't planning to do that now (instead, doing a 2.0.0).

That said, I recognize that this is arguably a bugfix worthy of inclusion in a 1.10.X release; I'll defer to @samuelcolvin's opinion on what's the best path forward.

@dmontagu
Copy link
Contributor

dmontagu commented Mar 8, 2023

Thought about this more and I think you are right about everything @kurtmckee. Going to merge this then do a new release.

@dmontagu dmontagu merged commit 943f507 into pydantic:1.10.X-fixes Mar 8, 2023
@kurtmckee
Copy link
Author

Thank you @dmontagu, and thank you all for reviewing and discussing!

@kurtmckee kurtmckee deleted the fix-incorrect-error-issue-4773 branch March 8, 2023 16:37
renovate bot added a commit to allenporter/pyrainbird that referenced this pull request Mar 10, 2023
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [pydantic](https://togithub.com/pydantic/pydantic) | `==1.10.5` ->
`==1.10.6` |
[![age](https://badges.renovateapi.com/packages/pypi/pydantic/1.10.6/age-slim)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://badges.renovateapi.com/packages/pypi/pydantic/1.10.6/adoption-slim)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://badges.renovateapi.com/packages/pypi/pydantic/1.10.6/compatibility-slim/1.10.5)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://badges.renovateapi.com/packages/pypi/pydantic/1.10.6/confidence-slim/1.10.5)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>pydantic/pydantic</summary>

###
[`v1.10.6`](https://togithub.com/pydantic/pydantic/releases/tag/v1.10.6):
2023-03-02

[Compare
Source](https://togithub.com/pydantic/pydantic/compare/v1.10.5...v1.10.6)

#### What's Changed

- Implement logic to support creating validators from non standard
callables by using defaults to identify them and unwrapping
`functools.partial` and `functools.partialmethod` when checking the
signature,
[#&#8203;5126](https://togithub.com/pydantic/pydantic/issues/5126) by
[@&#8203;JensHeinrich](https://togithub.com/JensHeinrich)
- Fix mypy plugin for v1.1.1, and fix `dataclass_transform` decorator
for pydantic dataclasses,
[#&#8203;5111](https://togithub.com/pydantic/pydantic/issues/5111) by
[@&#8203;cdce8p](https://togithub.com/cdce8p)
- Raise `ValidationError`, not `ConfigError`, when a discriminator value
is unhashable,
[#&#8203;4773](https://togithub.com/pydantic/pydantic/issues/4773) by
[@&#8203;kurtmckee](https://togithub.com/kurtmckee)

#### New Contributors

- [@&#8203;mrmammadov](https://togithub.com/mrmammadov) made their first
contribution in
[pydantic/pydantic#5074
- [@&#8203;JensHeinrich](https://togithub.com/JensHeinrich) made their
first contribution in
[pydantic/pydantic#5127
- [@&#8203;kurtmckee](https://togithub.com/kurtmckee) made their first
contribution in
[pydantic/pydantic#5132

**Full Changelog**:
pydantic/pydantic@v1.10.5...v1.10.6

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://app.renovatebot.com/dashboard#github/allenporter/pyrainbird).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC4xNTkuMiIsInVwZGF0ZWRJblZlciI6IjM0LjE1OS4yIn0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
renovate bot added a commit to allenporter/flux-local that referenced this pull request Mar 11, 2023
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [pydantic](https://togithub.com/pydantic/pydantic) | `==1.10.5` ->
`==1.10.6` |
[![age](https://badges.renovateapi.com/packages/pypi/pydantic/1.10.6/age-slim)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://badges.renovateapi.com/packages/pypi/pydantic/1.10.6/adoption-slim)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://badges.renovateapi.com/packages/pypi/pydantic/1.10.6/compatibility-slim/1.10.5)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://badges.renovateapi.com/packages/pypi/pydantic/1.10.6/confidence-slim/1.10.5)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>pydantic/pydantic</summary>

###
[`v1.10.6`](https://togithub.com/pydantic/pydantic/releases/tag/v1.10.6):
2023-03-02

[Compare
Source](https://togithub.com/pydantic/pydantic/compare/v1.10.5...v1.10.6)

##### What's Changed

- Implement logic to support creating validators from non standard
callables by using defaults to identify them and unwrapping
`functools.partial` and `functools.partialmethod` when checking the
signature,
[#&#8203;5126](https://togithub.com/pydantic/pydantic/issues/5126) by
[@&#8203;JensHeinrich](https://togithub.com/JensHeinrich)
- Fix mypy plugin for v1.1.1, and fix `dataclass_transform` decorator
for pydantic dataclasses,
[#&#8203;5111](https://togithub.com/pydantic/pydantic/issues/5111) by
[@&#8203;cdce8p](https://togithub.com/cdce8p)
- Raise `ValidationError`, not `ConfigError`, when a discriminator value
is unhashable,
[#&#8203;4773](https://togithub.com/pydantic/pydantic/issues/4773) by
[@&#8203;kurtmckee](https://togithub.com/kurtmckee)

##### New Contributors

- [@&#8203;mrmammadov](https://togithub.com/mrmammadov) made their first
contribution in
[pydantic/pydantic#5074
- [@&#8203;JensHeinrich](https://togithub.com/JensHeinrich) made their
first contribution in
[pydantic/pydantic#5127
- [@&#8203;kurtmckee](https://togithub.com/kurtmckee) made their first
contribution in
[pydantic/pydantic#5132

**Full Changelog**:
pydantic/pydantic@v1.10.5...v1.10.6

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://app.renovatebot.com/dashboard#github/allenporter/flux-local).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC4xNjAuMCIsInVwZGF0ZWRJblZlciI6IjM0LjE2MC4wIn0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
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.

None yet

3 participants