-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Tracking for setting attributes #389
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
Conversation
|
Single quotes please everywhere, also you'll need to respect black. Use |
|
Of course, I was holding off on that area until we had an idea of what to do with the recursive copy operation. Easiest thing would be to write a custom copy validator for BaseModels rather than relying on the standard |
|
Oh I see. Maybe related to #265? Do you know how to proceed or do you have a specific question? |
|
great |
|
#265 was deeper than I expected. I think that can be solved however, but I will leave it for another PR. This is now ready to review. |
Codecov Report
@@ Coverage Diff @@
## master #389 +/- ##
=====================================
Coverage 100% 100%
=====================================
Files 14 14
Lines 2031 2059 +28
Branches 409 419 +10
=====================================
+ Hits 2031 2059 +28 |
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.
Are you sure about the benchmark numbers? They sound to good to be true.
pydantic/main.py
Outdated
|
|
||
| def __init__(self, **data: Any) -> None: | ||
| if TYPE_CHECKING: # pragma: no cover | ||
| self.__values__: Dict[str, Any] = {} | ||
| self.__setstate__(self._process_values(data)) | ||
| self.__fields_set__: Set[str] = set() | ||
| fields_set = data.pop("__fields_set__", set(data.keys())) |
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.
Strict rule in pydantic that you can;t pass anything other than values to the class like this.
Users should be able to create a model with MyModel(**whatever) without having to think about what's in whatever.
Remove this and either call __setstate__ directly, or set model.__fields_set__ = thing directly.
pydantic/main.py
Outdated
| @@ -324,7 +340,8 @@ def construct(cls, **values: Any) -> 'BaseModel': | |||
| Chances are you don't want to use this method directly. | |||
| """ | |||
| m = cls.__new__(cls) | |||
| m.__setstate__(values) | |||
| fields_set = values.pop("__fields_set__", None) | |||
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.
Single quotes always please unless the string contains single quotes.
But again, can we avoid doing values.pop().
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 think I need to add the ability to pass in some sort of state from the calling instance. Can I change construct to take in a fields_set or should I add a new function like construct_with_fields or similar?
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.
construct can take more arguments, just __init__ that should remain pure.
pydantic/main.py
Outdated
| @@ -342,15 +359,13 @@ def copy( | |||
| """ | |||
| if include is None and exclude is None and update is None: | |||
| # skip constructing values if no arguments are passed | |||
| v = self.__values__ | |||
| v = self.__values__.copy() | |||
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.
do we need this copy?
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.
No, not if we remove the explicit add of __field_set__ below.
| self, include: Set[str] = None, exclude: Optional[Set[str]] = set(), skip_defaults: bool = False | ||
| ) -> Set[str]: | ||
| if skip_defaults: | ||
| keys = self.__fields_set__.copy() |
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 doubt we need copy() here.
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.
We do as we modify the set in-place later for speed.
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.
ok
| if skip_defaults: | ||
| keys = self.__fields_set__.copy() | ||
| else: | ||
| keys = set(self.__values__.keys()) |
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 doubt we need set() here.
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.
If we do not convert it to a set (there is no copy for dict keys), we rely on the fact that dict keys are special sets which return LHS references if modified in place. Seems a bit ugly, when we can explicitly copy the data here and move on with things.
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.
ok, that's fine.
pydantic/main.py
Outdated
| keys = set(self.__values__.keys()) | ||
|
|
||
| if include: | ||
| keys &= set(include) |
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 doubt we need set() here.
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 was erring on the side of safety, not everyone uses mypy. But happy to remove it.
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 think this one really can be removed. If someone passes something weird here they should get an error.
| yield k, self._get_value(v, by_alias=by_alias) | ||
| yield k, self._get_value(v, by_alias=by_alias, skip_defaults=skip_defaults) | ||
|
|
||
| def _calculate_keys( |
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 think we can probably do this in a more efficient way, eg. if include=None, exclude=set() and skip_defaults=False (all the defaults), then we just take all keys.
Can't we take care of that case but just returning None or something here and not checking key containment?
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.
Sure, I can work on optimizing that out.
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.
On this note, the include/exclude defaults are not consistent (copy for example has them both defaulting to None). I can clean this up as well.
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.
ok.
Co-Authored-By: dgasmith <dgasmith@icloud.com>
Co-Authored-By: dgasmith <dgasmith@icloud.com>
Co-Authored-By: dgasmith <dgasmith@icloud.com>
|
Timings: Not entirely sure why. Could depend heavily on benchmark construction and a bit of extra set manipulation over raw lists. |
|
Tried it myself and I get a ~5% time increase with this PR. Not ideal. To put this in proportion, this increase is roughly the same as the increase which was initially introduced by the typing PR #373, I spent a couple of hours fighting with it to mostly eliminate that performance deterioration. I'm therefore somewhat reticent about introducing that slowdown here. Get the PR ready and I'll see what I can do. |
|
Odd on timings, those were 3.6. For 3.7 I see: Agreed on the keys comment, patched. |
|
For me this is now ready to be merged. Please check my changes and let me know if you disagree with anything or have any questions. We might need to update history to state that |
|
Looks good! I was hesitant to change |
Add CoreSchemaType Literal
Change Summary
Tracking when default arguments are override and allows a
skip_defaultskwarg in thedictmethod.A problem that I have not yet solved is that a BaseModel goes through the
dict_validatorand is then passed back intoBaseModel.validateafter being cast to a dict, this does not allow the__fields_set__to come through at the moment and recursive models do not work. Any suggestions here?Related issue number
Discussion in #378.
Checklist
HISTORY.rsthas been updated#<number>@<whomever>