Skip to content

Workchains: Accept but deprecate conditional predicates returning None#261

Merged
sphuber merged 1 commit intorelease/0.21.5from
fix/deprecate-conditional-predicate-return-none
Mar 14, 2023
Merged

Workchains: Accept but deprecate conditional predicates returning None#261
sphuber merged 1 commit intorelease/0.21.5from
fix/deprecate-conditional-predicate-return-none

Conversation

@sphuber
Copy link
Copy Markdown
Collaborator

@sphuber sphuber commented Mar 14, 2023

In 800bcf1, the behavior of predicates passed to _Conditional instances was changed to raise if anything but a boolean was returned. This was to catch cases where users would return non-boolean values by accident, which would anyway would most likely to broken behavior of the workchain.

However, quite a number of existing implementations used to do something like the following in a predicate

def conditional_predicate(self):
    if some_condition is True:
         return True

In the case that some_condition was not equal to True, the predicate would return None which would be evaluated as "falsy" and so would function as if False had been returned.

In order to not break all implemenations using this behavior, None is still accepted and automatically converted to False. A UserWarning is emitted to warn that the behavior is deprecated. This is used in favor of a DeprecationWarning since those are not shown by default, which means the majority of users wouldn't see the deprecation warning.

In 800bcf1, the behavior of predicates
passed to `_Conditional` instances was changed to raise if anything but
a boolean was returned. This was to catch cases where users would return
non-boolean values by accident, which would anyway would most likely to
broken behavior of the workchain.

However, quite a number of existing implementations used to do something
like the following in a predicate

    def conditional_predicate(self):
        if some_condition is True:
             return True

In the case that `some_condition` was not equal to `True`, the predicate
would return `None` which would be evaluated as "falsy" and so would
function as if `False` had been returned.

In order to not break all implemenations using this behavior, `None` is
still accepted and automatically converted to `False`. A `UserWarning`
is emitted to warn that the behavior is deprecated. This is used in
favor of a `DeprecationWarning` since those are not shown by default,
which means the majority of users wouldn't see the deprecation warning.
@sphuber sphuber changed the title Workchains: Accept but deprecate conditional predicates returning None Workchains: Accept but deprecate conditional predicates returning None Mar 14, 2023
@sphuber sphuber requested a review from unkcpz March 14, 2023 10:00
@sphuber
Copy link
Copy Markdown
Collaborator Author

sphuber commented Mar 14, 2023

@unkcpz I noticed that our previous change would start breaking a lot of workchains out there. Fortunately we haven't release it in aiida-core yet, so we can release this with plumpy==0.21.5 and then update the requirement in the aiida-core/main branch.

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 14, 2023

Codecov Report

Patch coverage: 100.00% and project coverage change: +0.02 🎉

Comparison is base (b6cd6fd) 90.81% compared to head (ae1fc1e) 90.83%.

Additional details and impacted files
@@                Coverage Diff                 @@
##           release/0.21.5     #261      +/-   ##
==================================================
+ Coverage           90.81%   90.83%   +0.02%     
==================================================
  Files                  21       21              
  Lines                2970     2974       +4     
==================================================
+ Hits                 2697     2701       +4     
  Misses                273      273              
Impacted Files Coverage Δ
src/plumpy/workchains.py 94.39% <100.00%> (+0.06%) ⬆️

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report in Codecov by Sentry.
📢 Do you have feedback about the report comment? Let us know in this issue.

@unkcpz
Copy link
Copy Markdown
Member

unkcpz commented Mar 14, 2023

Thanks @sphuber, can you point out which workchain return None as the conditional predicate? I thought about this case during the review since that was what I used in my workchain in aiidateam/aiida-core#5477 and I was going to use _break to stop the loop. I think if the conditional predicate is None we don't know if it should be regared as true or false, it rather indicates a bug in the workflow.

But you are correct if this breaks some workflow at the moment it is not good and the warning should be raised. I agree with this change but the phrase "please return False instead." seems not correct?

@sphuber
Copy link
Copy Markdown
Collaborator Author

sphuber commented Mar 14, 2023

Thanks @sphuber, can you point out which workchain return None as the conditional predicate?

The workchain that triggered it for me is a private internal one. But I sketched out the relevant logic in the commit message and that does seem something that users could easily have used. Since Python does not enforce typing, I think it would be easy for users to return None by accident. Especially since this works "as expected".

I thought about this case during the review since that was what I used in my workchain in aiidateam/aiida-core#5477 and I was going to use _break to stop the loop. I think if the conditional predicate is None we don't know if it should be regared as true or false, it rather indicates a bug in the workflow.

I agree that we cannot know for sure, but I think as an aiida-core developer, you were thinking in a more "advanced" manner of wanting to explicitly affect the logic with some construct. I think most users wouldn't even have thought about this option of wanting to explicitly break out of the conditional. But there is no guarantee of course.

But you are correct if this breaks some workflow at the moment it is not good and the warning should be raised. I agree with this change but the phrase "please return False instead." seems not correct?

Why not? Before the change, None would be essentially treated as False. So if they were returning None by default (but the logic was working as they intended) then they would now have to change it to False to keep the behavior but get rid of the warning, would they not?

@unkcpz
Copy link
Copy Markdown
Member

unkcpz commented Mar 14, 2023

Why not? Before the change, None would be essentially treated as False. So if they were returning None by default (but the logic was working as they intended) then they would now have to change it to False to keep the behavior but get rid of the warning, would they not?

Yes, you are right, I didn't notice None is treated as False. It makes all sense to me now.

Copy link
Copy Markdown
Member

@unkcpz unkcpz left a comment

Choose a reason for hiding this comment

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

Okay, thanks for the clarification @sphuber. It looks all good.

@sphuber sphuber merged commit f47627a into release/0.21.5 Mar 14, 2023
@sphuber sphuber deleted the fix/deprecate-conditional-predicate-return-none branch March 14, 2023 17:07
sphuber added a commit that referenced this pull request Mar 14, 2023
#261)

In 800bcf1, the behavior of predicates
passed to `_Conditional` instances was changed to raise if anything but
a boolean was returned. This was to catch cases where users would return
non-boolean values by accident, which would anyway would most likely to
broken behavior of the workchain.

However, quite a number of existing implementations used to do something
like the following in a predicate

    def conditional_predicate(self):
        if some_condition is True:
             return True

In the case that `some_condition` was not equal to `True`, the predicate
would return `None` which would be evaluated as "falsy" and so would
function as if `False` had been returned.

In order to not break all implemenations using this behavior, `None` is
still accepted and automatically converted to `False`. A `UserWarning`
is emitted to warn that the behavior is deprecated. This is used in
favor of a `DeprecationWarning` since those are not shown by default,
which means the majority of users wouldn't see the deprecation warning.

Cherry-pick: f47627a
unkcpz pushed a commit to unkcpz/plumpy that referenced this pull request Dec 14, 2024
aiidateam#261)

In 800bcf1, the behavior of predicates
passed to `_Conditional` instances was changed to raise if anything but
a boolean was returned. This was to catch cases where users would return
non-boolean values by accident, which would anyway would most likely to
broken behavior of the workchain.

However, quite a number of existing implementations used to do something
like the following in a predicate

    def conditional_predicate(self):
        if some_condition is True:
             return True

In the case that `some_condition` was not equal to `True`, the predicate
would return `None` which would be evaluated as "falsy" and so would
function as if `False` had been returned.

In order to not break all implemenations using this behavior, `None` is
still accepted and automatically converted to `False`. A `UserWarning`
is emitted to warn that the behavior is deprecated. This is used in
favor of a `DeprecationWarning` since those are not shown by default,
which means the majority of users wouldn't see the deprecation warning.

Cherry-pick: f47627a
agoscinski pushed a commit to agoscinski/plumpy that referenced this pull request Apr 13, 2026
aiidateam#261)

In 800bcf1, the behavior of predicates
passed to `_Conditional` instances was changed to raise if anything but
a boolean was returned. This was to catch cases where users would return
non-boolean values by accident, which would anyway would most likely to
broken behavior of the workchain.

However, quite a number of existing implementations used to do something
like the following in a predicate

    def conditional_predicate(self):
        if some_condition is True:
             return True

In the case that `some_condition` was not equal to `True`, the predicate
would return `None` which would be evaluated as "falsy" and so would
function as if `False` had been returned.

In order to not break all implemenations using this behavior, `None` is
still accepted and automatically converted to `False`. A `UserWarning`
is emitted to warn that the behavior is deprecated. This is used in
favor of a `DeprecationWarning` since those are not shown by default,
which means the majority of users wouldn't see the deprecation warning.

Cherry-pick: f47627a
agoscinski pushed a commit that referenced this pull request Apr 13, 2026
#261)

In 800bcf1, the behavior of predicates
passed to `_Conditional` instances was changed to raise if anything but
a boolean was returned. This was to catch cases where users would return
non-boolean values by accident, which would anyway would most likely to
broken behavior of the workchain.

However, quite a number of existing implementations used to do something
like the following in a predicate

    def conditional_predicate(self):
        if some_condition is True:
             return True

In the case that `some_condition` was not equal to `True`, the predicate
would return `None` which would be evaluated as "falsy" and so would
function as if `False` had been returned.

In order to not break all implemenations using this behavior, `None` is
still accepted and automatically converted to `False`. A `UserWarning`
is emitted to warn that the behavior is deprecated. This is used in
favor of a `DeprecationWarning` since those are not shown by default,
which means the majority of users wouldn't see the deprecation warning.

Cherry-pick: f47627a
agoscinski pushed a commit that referenced this pull request Apr 13, 2026
#261)

In 800bcf1, the behavior of predicates
passed to `_Conditional` instances was changed to raise if anything but
a boolean was returned. This was to catch cases where users would return
non-boolean values by accident, which would anyway would most likely to
broken behavior of the workchain.

However, quite a number of existing implementations used to do something
like the following in a predicate

    def conditional_predicate(self):
        if some_condition is True:
             return True

In the case that `some_condition` was not equal to `True`, the predicate
would return `None` which would be evaluated as "falsy" and so would
function as if `False` had been returned.

In order to not break all implemenations using this behavior, `None` is
still accepted and automatically converted to `False`. A `UserWarning`
is emitted to warn that the behavior is deprecated. This is used in
favor of a `DeprecationWarning` since those are not shown by default,
which means the majority of users wouldn't see the deprecation warning.

Cherry-pick: f47627a
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.

2 participants