-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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 (E712) changing ==
/!=
to is
/is not
is not correct for some types
#4560
Comments
==
/!=
to is
/is not
is not correct for some types==
/!=
to is
/is not
is not correct for some types
I just spent a while tracking down this exact issue, an error introduced by ruff and reduced to the almost identical: import numpy as np
arr = np.array([False, True, False, True])
print(repr(arr == False))
# array([ True, False, True, False])
print(repr(arr is False))
# False Reading the other thread, it sounds like this autofix can't be made safe. I would suggest disabling it completely then, because using truthiness in numpy comparison isn't a rare operation, and expecting everyone to "know" not to do this seems to defeat the point of an autocorrecting linter. |
I think making this a suggested fix (as it is now) will have the same effect, once we introduce The problem with removing the autofix entirely is that it doesn't really reduce the burden or expectation on the user, because this diagnostic will still be raised, and so users will still be required to look at the code and understand whether or not to change it. Performing the fix automatically has the downside of silently breaking the code, but requiring users to opt-in to the change explicitly seems (to me) as safe as pointing them to the diagnostic without including any possible fix. |
Any conclusion on this one? This issue broke a bunch of our
to
which leads to Thank you |
Adding to @nicornk: PEP-8 actually strongly discourages using
So while the autofix may be unsafe, I would argue the diagnostics itself is harmful. The correct suggestion would be to drop |
@nicornk you can use the unambiguous alternate syntax e.g. @dstoeckel while I agree that |
Is there an existing ongoing effort for this that's public? |
Note as of v0.1.0 we do not apply unsafe fixes by default — so this fix will not be applied by default. |
Of course this is a matter of opinion, but if you want to check this, I think the Pythonic way to do so is to use:
|
Please let's not make this issue a debate about how The libraries that are the focus of this issue have designed APIs where specific comparisons are necessary. This issue is intended to track support for patterns in those APIs. I'd recommend creating a new discussion if you want to discuss broader concerns. |
Just to add an example, of an error that took me a while to fix. import pandas as pd
# Example dataframe
df = pd.DataFrame({"id": [1, 2, 3, 4, 5], "col_2": [True, False, True, False, True]})
# This works, but ruff raises: Comparison to `False` should be `cond is False`
a = df[df["col_2"] == False]
print(a)
# This does not work, pandas will raise 'KeyError: False'
b = df[df["col_2"] is False]
print(b) |
Another example with sqlalchemy2 ( where syntax has changed a lot comparing with versions 1.x): query = request.dbsession.execute(
select(Station, func.min(Check.result)) # pylint: disable=E1102
.join(Check.channel)
.join(Channel.station)
.where(
Station.triggered == False,
)
).all() Here the |
Same thing with E711. Would be nice to have a brief mention in the rule's docs calling out common situations where this is unsafe and why |
Thanks @psychedelicious. Would you be willing to open a pull request? |
- Add fix safety blurbs for E711 `NoneComparison` & E712 `TrueFalseComparison` - same for both rules. - Minor formatting for E711 `NoneComparison`.
The fixes for rules E711 `NoneComparison` and E712 `TrueFalseComparison` are marked unsafe due to possible runtime behavior changes with libraries that override `__eq__` and `__ne__` methods. - Add a "Fix safety" section to each rule explaining why the fixes are unsafe, commonly affected library methods, and alternatives. The sections are identical for each rule. - Minor formatting tweak for E711's docs.
- Link to the relevant GH issue instead of copying examples/alternatives from the GH issue.
The fixes for rules E711 `NoneComparison` and E712 `TrueFalseComparison` are marked unsafe due to possible runtime behavior changes with libraries that override `__eq__` and `__ne__` methods. - Add a "Fix safety" section to each rule explaining why the fixes are unsafe, plus a link to a GH issue with more detail. The sections are identical for each rule. - Minor formatting tweak for E711's docs.
But why do we change |
oh, checking #8164 that seems to deal with the same question… |
Now that Ruff is moving towards having type information, this issue may eventually warrant some refinement? If |
I would argue that if x is not Boolean type, "is True" is always wrong - for example if x is |
I ran into this when writing this example for the next version of Would be applicable to:
|
By broader, I mean something like |
Note about SQLAlchemy: The SELECT 10 IS TRUE;
/* 1 */
SELECT 10 = TRUE;
/* 0 */
SELECT NULL IS NOT FALSE;
/* 1 */
SELECT NULL != FALSE;
/* NULL */ Thus, the safe alternative for comparing to booleans is: c == True
# <sqlalchemy.sql.elements.BinaryExpression object at 0x1026203e0>
c == sqlalchemy.true()
# <sqlalchemy.sql.elements.BinaryExpression object at 0x1026222a0> |
Summary
Generally, comparisons to
True
,False
, andNone
singletons should useobj is True
instead ofobj == True
.However, it is common for libraries to override the
==
/__eq__
operator to create simple APIs for filtering data. In these cases, correcting==
tois
changes the meaning of the program and breaks the user's code. The same applies for!=
andis not
.This is a tracking issue for all invalid corrections from this rule.
Types with the issue
pandas.DataFrame
often used withDataFrame.mask
,DataFrame.where
pandas.Series
often used withSeries.mask
,Series.where
numpy.Array
often used withArray.where
sqlalchemy.Column
often used withQuery.having
,Query.filter
,Query.where
If an issue with an unlisted type is encountered please reply and I will edit to add it here.
Resolution
Eventually, ruff is likely to detect these cases by inferring the datatype involved and exclude it from the suggested fix.
In the meantime, you may:
pandas.Series.eq
)Examples
Related issues
mask == True
is not the same asmask is True
#2443numpy.bool_
#4356The text was updated successfully, but these errors were encountered: