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

IR: Better tuple autocasting via pydantic field_validators #476

Merged
merged 2 commits into from
Jan 17, 2025

Conversation

mlange05
Copy link
Collaborator

Instead of using model_validator objects, we can use inidividual field_validator utilities to do per-argument auto-casting. The effect of this is that we can then use positional constructor arguments without using the funcationality and dealing the full model ArgsKwargs objects.

This should address issue #420, @wertysas to confirm.

Instead of using `model_validator` objects, we can use inidividual
`field_validator` utilities to do per-argument auto-casting. The
effect of this is that we can then use positional constructor
arguments without using the funcationality and dealing the full model
`ArgsKwargs` objects.
@mlange05 mlange05 requested a review from reuterbal January 16, 2025 16:02
Copy link

Documentation for this branch can be viewed at https://sites.ecmwf.int/docs/loki/476/index.html

Copy link

codecov bot commented Jan 16, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 96.03%. Comparing base (304921f) to head (274b0a0).
Report is 3 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #476   +/-   ##
=======================================
  Coverage   96.03%   96.03%           
=======================================
  Files         226      226           
  Lines       40581    40587    +6     
=======================================
+ Hits        38972    38978    +6     
  Misses       1609     1609           
Flag Coverage Δ
lint_rules 96.39% <ø> (ø)
loki 96.02% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Collaborator

@reuterbal reuterbal left a comment

Choose a reason for hiding this comment

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

Thanks, looks cleaner indeed and appears to be fine like this. Pylint's unhappy, so requesting changes, but these are trivial. More interested in better understanding the questions I put in inline comments, which are only for my own education.

@@ -257,17 +257,10 @@ class InternalNode(Node, _InternalNode):

_traversable = ['body']

@model_validator(mode='before')
@field_validator('body', mode='before')
Copy link
Collaborator

Choose a reason for hiding this comment

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

Likely my ignorance of pydantic showing here: The pre_init method did also stuff to args and kwargs before. Is this implicitly handled now or not required anymore?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, but this one worked on the entire "Model" in pydantic speak, so it got a single ArgsKwargs argument that held all the constructor arguments as ArgsKwarg.args and ArgsKwargs.kwargs. This became an issue, as the ArgsKwargs object is immutable, but the ArgsKwarg.kwargs dict could be changed. This means, if any kw-args were given, I could change the dict, but I cannot replace None with () as ArgsKwargs.args = () is not allowed.

Long story short, this new validator is applied to each arg individual when pydantic creates the ArgsKwargs object, so all our troubles fade away and pydantic takes care of identifying unnamed args for free.

Comment on lines +952 to +953
arguments: Optional[Tuple[Expression, ...]] = ()
kwarguments: Optional[Tuple[Tuple[str, Expression], ...]] = ()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this a change in behaviour, i.e., arguments and kwarguments always a tuple now instead of None? (I'm not opposed, just trying to understand the impact)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Internally, we would always force this into () in the pre-validator autocast mechanism. The reason I need to change it here is that the before-validator does not get invoked if the arg is omitted so the default is used directly. This should be agnostic to the previous behaviour. 🤞

@@ -148,6 +154,14 @@ def test_conditional(scope, n, a_i):
)
assert not cond.body and cond.else_body == (assign, assign, assign)

# Test auto-casting with unnamed constructor args
cond = ir.Conditional(condition)
assert cond.body == () and not cond.else_body
Copy link
Collaborator

Choose a reason for hiding this comment

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

Pylint doesn't like cond.body == (), maybe

Suggested change
assert cond.body == () and not cond.else_body
assert isinstance(cond.body, tuple) and not (cond.body or cond.else_body)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Same here, cond.body is () should do to appease pylint...

@@ -235,6 +249,12 @@ def test_section(n, a_n, a_i):
sec = ir.Section((assign, (func,), assign, None))
assert sec.body == (assign, func, assign)

# Test auto-casting with unnamed constructor args
sec = ir.Section()
assert sec.body == ()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
assert sec.body == ()
assert isinstance(sec.body, tuple) and not sec.body

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Or even just sec.body is ()...

Copy link
Collaborator

@reuterbal reuterbal left a comment

Choose a reason for hiding this comment

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

Many thanks for the explanations, nothing further to add. This is very neat! 🙏

@reuterbal reuterbal added the ready to merge This PR has been approved and is ready to be merged label Jan 17, 2025
@reuterbal reuterbal merged commit 70374e9 into main Jan 17, 2025
13 checks passed
@reuterbal reuterbal deleted the naml-ir-autocast-args branch January 17, 2025 08:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ready to merge This PR has been approved and is ready to be merged
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants