-
Notifications
You must be signed in to change notification settings - Fork 242
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
[Fix(9559)]
- Validation fails for enum field with decimal type
#1324
[Fix(9559)]
- Validation fails for enum field with decimal type
#1324
Conversation
The code changes modify the test cases for enum validation with decimal values. The test cases now only include the values -1, 0, and 1. Additionally, a new assertion is added to validate the decimal values as floats. This change improves the validation of enum fields with decimal values.
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1324 +/- ##
==========================================
- Coverage 90.21% 89.64% -0.57%
==========================================
Files 106 108 +2
Lines 16339 17252 +913
Branches 36 41 +5
==========================================
+ Hits 14740 15466 +726
- Misses 1592 1766 +174
- Partials 7 20 +13
... and 34 files with indirect coverage changes Continue to review full report in Codecov by Sentry.
|
CodSpeed Performance ReportMerging #1324 will not alter performanceComparing Summary
|
please review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks very much for the PR!
Having just reviewed with this, I'd like to see a different solution. As per pydantic/pydantic#9559 (comment) I think the problem is not limited just to Decimal
but to many arbitrary types.
I think also we don't want to add Decimal
coercion to our Literal
pipeline as it's never valid to have a Decimal
as a Literal
.
I think instead I would prefer if we added a branch to src/validators/enum_.rs
which, as a last resort, attempts to do the equivalent of MyEnum(input)
. This would add worst-case performance but should restore all the old behaviour.
Hey @davidhewitt, Thanks for the feedback! Yes, you're correct. The problem isn't limited to Decimal. I placed the implementation in I've examined this thoroughly and face uncertain next steps. So I need a little bit of help. Perhaps, I don't understand something. Or, the current machinery can't handle arbitrary types well. The problem that I've been pondering is as follows: from enum import Enum
from pydantic import BaseModel
class EnumClass(Enum):
enum_value = 1
enum_value = 2
class ModelClass(BaseModel):
enum_field: EnumClass
enum_field_2: EnumClass
class IntWrappable:
def __init__(self, value: int):
self.value = value
def __eq__(self, value: object) -> bool:
return self.value == value
ModelClass(enum_field=1, enum_field_2=IntWrappable(2)) LiteralLookup { expected_bool: None, expected_int: Some({1: 0, 2: 1}), expected_str: None, expected_py_dict: None, expected_py_list: None, values: [Py(0x7f396c73c950), Py(0x7f396cfe3da0)] }
How can we know which UPDATE: Äh, maybe I thought about this too hard. It works now for any type. The types must have a test for equality. But, I feel that there might some corner case that this implementation does not cover. |
…to a validated value
…lidate_any refactor: add more test coverage
removed try_validate_any function and instead try to create Python enum class. Test case modifications and fixes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! So rereading after the latest push has sparked new thoughts 🙈
# GIVEN | ||
class MyEnum(Enum): | ||
VALUE = 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realise now that we probably also want to test this with e.g. MyEnum(IntEnum)
, which I think goes through a separate code pathway but probably was also broken when we moved enum validation to Rust?
} else if let Some(v) = T::validate_value(py, input, &self.lookup, &self.class, strict)? { | ||
state.floor_exactness(Exactness::Lax); | ||
return Ok(v); | ||
} else if let Some(ref missing) = self.missing { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So now that I look at this new point in the diff, I see that (and I regret forgetting this) that we already call _missing_
here. But as we observe in the issue and this PR, simply calling _missing_
is not enough because enum __new__
has more complex logic which isn't encapsulated purely by _missing_
.
I wonder if there is a case to have a new branch (before or after this one? not sure 🤔) which calls the enum type, with the logic going here instead of in PlainEnumValidator
. Putting the logic here would also solve the special-cased enums like IntEnum
, I think.
That does beg the question, though: if we add the case of calling the enum type here, do we need logic for _missing_
at all? My intuition is that we don't, and we should try to phase out the _missing_
logic.
@@ -183,8 +185,14 @@ impl EnumValidateValue for PlainEnumValidator { | |||
} else if py_input.is_instance_of::<PyFloat>() { | |||
return Ok(lookup.validate_int(py, input, false)?.map(|v| v.clone_ref(py))); | |||
} | |||
if py_input.is_instance_of::<PyAny>() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is_instance_of::<PyAny>()
I think will always be true
, will probably be optimized away by the compiler but also not necessary at all IMO.
if py_input.is_instance_of::<PyAny>() { |
Closing in favor of #1456 |
Change Summary
Fixes regression documented in pydantic/pydantic#9559.
Related issue number
pydantic/issues/9559
Checklist
pydantic-core
(except for expected changes)Selected Reviewer: @davidhewitt