classification
Title: x in IntFlag should test raise TypeError if x is not an IntFlag
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.8, Python 3.7
process
Status: closed Resolution: duplicate
Dependencies: Superseder: x in enum.Flag member is True when x is not a Flag
View: 33217
Assigned To: ethan.furman Nosy List: Dutcho, barry, eli.bendersky, ethan.furman, nitishch
Priority: normal Keywords: easy

Created on 2018-04-03 20:38 by Dutcho, last changed 2018-04-12 21:34 by ethan.furman. This issue is now closed.

Messages (7)
msg314893 - (view) Author: (Dutcho) Date: 2018-04-03 20:38
While `enum.IntFlag.__and__` accepts an int arg `other` and converts it to `IntFlag` before masking, `enum.IntFlag.__contains__` handles an int arg `other` no different from a different type arg `other` (i.e. returns `True` in Python 3.6 due to issue 33217, but would raise `TypeError` after that's fixed):
    >>> import enum
    >>> ABC = enum.Flag('ABC', 'a, b, c')
    >>> ac = ABC.a | ABC.c
    >>> ABC.b in ac # works
    False
    >>> 2 in ac # should be the same; no exception due to issue 33217
    True
    >>> ac & 3 # works, equivalent to ac & ABC(3)
    <ABC.a: 1>

This is caused by a lack of specialized `IntFlag.__contains__`, so `Flag.__contains__` does the work. Can be fixed by adding a specialization, which (like in `IntFlag.__and__`) tests for `isinstance(other, (IntFlag, int))`.

    >>> def __contains__(self, other):
    ...     if not isinstance(other, (self.__class__, int)):
    ...         return TypeError
    ...     return other & self == other # conversion of int to IntFlag implicitly handled by IntFlag.__and__
    >>> IntFlag.__contains__ = __contains__
msg314897 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2018-04-03 21:10
issue33217 will not be "fixed" with a TypeError, but incorrect Falses are also bad.

Rather than add __contains__ to IntFlag (and every other Flag mixin), I think the best answer is to adjust Flag.__contains__ a little bit more to check if `other` is of the same type as the mixin class, and if so see if it's one of the values.

Thank you for being thorough and finding this problem as well.
msg314912 - (view) Author: Nitish (nitishch) * Date: 2018-04-04 02:37
@Ethan Furman how can Flag do that? IntFlag can deal with int values too. Would it be possible to deal with int values in this case in Flag.__contains__?
msg314951 - (view) Author: (Dutcho) Date: 2018-04-04 21:03
@Nitish
The easiest way would probably be to change __contains__ in Flag to:

    def __contains__(self, other):
        try:
            return other & self == other # leave selection of _value_ attribute (if other is Flag) or conversion (if other is int mixin of IntFlag) to __and__
        except TypeError:
            return False

Although this would be somewhat convoluted (the generic delegation to __and__ isn't clear at first sight and therefore less maintainable) and may lead to confusing error messages
msg314959 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2018-04-04 23:14
Nitish, Flag can check if the current Flag class has a mixin, and if so if the object being checked for is of that same type.

Dutcho, my apologies.  Looks like I did not fully understand how __contains__ is supposed to work and a TypeError is completely appropriate.

Looking into deprecation cycles now to get the change scheduled.
msg314993 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2018-04-05 16:32
issue33217 is tracking member-containment checks; modifying this one to track class-containment checks.

Given

  class Color(Enum):
     RED = 1

  class Fruit(Enum):
     APPLE = 1

then

  --> Fruit.APPLE in Color
  False
  --> Fruit.APPLE in Fruit
  True
  --> 1 in Fruit
  TypeError

The last is currently returning False instead of raising a TypeError.
msg315233 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2018-04-12 21:34
This and issue33217 are similar enough I'm going to track it in issue33217.
History
Date User Action Args
2018-04-12 21:34:06ethan.furmansetstatus: open -> closed
superseder: x in enum.Flag member is True when x is not a Flag
messages: + msg315233

resolution: duplicate
stage: needs patch -> resolved
2018-04-05 16:32:04ethan.furmansetversions: + Python 3.7
title: x in IntFlag() should test int x's inclusion in IntFlag -> x in IntFlag should test raise TypeError if x is not an IntFlag
messages: + msg314993

type: enhancement -> behavior
stage: needs patch
2018-04-04 23:14:49ethan.furmansetmessages: + msg314959
2018-04-04 21:03:56Dutchosetmessages: + msg314951
2018-04-04 02:37:00nitishchsetnosy: + nitishch
messages: + msg314912
2018-04-03 21:10:50ethan.furmansetkeywords: + easy

messages: + msg314897
2018-04-03 20:49:59ethan.furmansetassignee: ethan.furman

nosy: + barry, eli.bendersky, ethan.furman
versions: + Python 3.8, - Python 3.6
2018-04-03 20:38:20Dutchocreate