Repository: Rapptz/discord.py
Pull request: Make enums comparable
## Summary

Add comparison operations to Enum. Also added docs for supported operations on some enums where comparing values might be relevant. (NotificationLevel, NSFWLevel)

Not sure what to do about the supported operations containers on VerificationLevel and ContentFilter that were added in #688 and then broken in 0ddc6867e9e2c98d74a4ef85ac36a99fec5a1f03.

This PR closes #7250.

## Checklist

<!-- Put an x inside [ ] to check it, like so: [x] -->

- [x] If code changes were made then they have been tested.
    - [x] I have updated the documentation to reflect the changes.
- [x] This PR fixes an issue.
- [x] This PR adds something new (e.g. new method or parameters).
- [ ] This PR is a breaking change (e.g. methods or parameters removed/renamed)
- [ ] This PR is **not** a code change (e.g. documentation, README, ...)


Problem statement:
Comparing VerificationLevel gives unexpected results
### Summary

Comparing VerificationLevel does not work as expected

### Reproduction Steps

1. Tried to compare VerificationLevel 4 with 1
2. Received a different result than expected

### Minimal Reproducible Code

```python
>>> from discord import VerificationLevel
>>> VerificationLevel(4) > VerificationLevel(1) # "extreme" and "low"
False # !
```


### Expected Results

For the comparison to return `True`

### Actual Results

The comparison returned `False`

### Intents

-

### System Information

- Python v3.8.7-final
- discord.py v1.7.3-final
- aiohttp v3.7.3
- system info: Linux 5.10.49-1-MANJARO

### Checklist

- [X] I have searched the open issues for duplicates.
- [X] I have shown the entire traceback, if possible.
- [X] I have removed my token from display, if visible.

### Additional Context

I was about to open a PR to use IntEnum instead but I see it was removed for compatibility with 3.5

Hints from issue discussion:
> I was about to open a PR to use IntEnum instead but I see it was removed for compatibility with 3.5

Just to confirm on this, all PRs currently are for the 2.0 version of the library; which drops compat for 3.5. This means you'd be able to PR this just fine.
This is a bug since this operation is [documented](https://discordpy.readthedocs.io/en/v1.7.3/api.html#discord.VerificationLevel) to work.

Commit message: add comparable kwarg on EnumMeta

Patch to review:
diff --git a/discord/enums.py b/discord/enums.py
index 80af39ece1ca..fe87a19d965c 100644
--- a/discord/enums.py
+++ b/discord/enums.py
@@ -76,12 +76,19 @@ class EnumMeta(type):
         _enum_member_map_: ClassVar[Dict[str, Any]]
         _enum_value_map_: ClassVar[Dict[Any, Any]]
 
-    def __new__(cls, name, bases, attrs):
+    def __new__(cls, name, bases, attrs, **kwargs):
         value_mapping = {}
         member_mapping = {}
         member_names = []
 
         value_cls = _create_value_cls(name)
+
+        if kwargs.get('comparable'):
+            value_cls.__le__ = lambda self, other: isinstance(other, self.__class__) and self.value <= other.value
+            value_cls.__ge__ = lambda self, other: isinstance(other, self.__class__) and self.value >= other.value
+            value_cls.__lt__ = lambda self, other: isinstance(other, self.__class__) and self.value < other.value
+            value_cls.__gt__ = lambda self, other: isinstance(other, self.__class__) and self.value > other.value
+
         for key, value in list(attrs.items()):
             is_descriptor = _is_descriptor(value)
             if key[0] == '_' and not is_descriptor:
@@ -252,7 +259,7 @@ def __int__(self):
         return self.value
 
 
-class VerificationLevel(Enum):
+class VerificationLevel(Enum, comparable=True):
     none = 0
     low = 1
     medium = 2
@@ -263,7 +270,7 @@ def __str__(self):
         return self.name
 
 
-class ContentFilter(Enum):
+class ContentFilter(Enum, comparable=True):
     disabled = 0
     no_role = 1
     all_members = 2
@@ -296,7 +303,7 @@ def __str__(self):
         return self.name
 
 
-class NotificationLevel(Enum):
+class NotificationLevel(Enum, comparable=True):
     all_messages = 0
     only_mentions = 1
 
@@ -578,7 +585,7 @@ class StagePrivacyLevel(Enum):
     guild_only = 2
 
 
-class NSFWLevel(Enum):
+class NSFWLevel(Enum, comparable=True):
     default = 0
     explicit = 1
     safe = 2
diff --git a/docs/api.rst b/docs/api.rst
index 1ce973770f0e..df0f4c1654be 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -1633,6 +1633,29 @@ of :class:`enum.Enum`.
 
     Specifies whether a :class:`Guild` has notifications on for all messages or mentions only by default.
 
+    .. container:: operations
+
+        .. versionadded:: 2.0
+
+        .. describe:: x == y
+
+            Checks if two notification levels are equal.
+        .. describe:: x != y
+
+            Checks if two notification levels are not equal.
+        .. describe:: x > y
+
+            Checks if a notification level is higher than another.
+        .. describe:: x < y
+
+            Checks if a notification level is lower than another.
+        .. describe:: x >= y
+
+            Checks if a notification level is higher or equal to another.
+        .. describe:: x <= y
+
+            Checks if a notification level is lower or equal to another.
+
     .. attribute:: all_messages
 
         Members receive notifications for every message regardless of them being mentioned.
@@ -2532,6 +2555,27 @@ of :class:`enum.Enum`.
 
     .. versionadded:: 2.0
 
+    .. container:: operations
+
+        .. describe:: x == y
+
+            Checks if two NSFW levels are equal.
+        .. describe:: x != y
+
+            Checks if two NSFW levels are not equal.
+        .. describe:: x > y
+
+            Checks if a NSFW level is higher than another.
+        .. describe:: x < y
+
+            Checks if a NSFW level is lower than another.
+        .. describe:: x >= y
+
+            Checks if a NSFW level is higher or equal to another.
+        .. describe:: x <= y
+
+            Checks if a NSFW level is lower or equal to another.
+
     .. attribute:: default
 
         The guild has not been categorised yet.


Code review comments:
diff_hunk: @@ -76,12 +76,19 @@ class EnumMeta(type):
         _enum_member_map_: ClassVar[Dict[str, Any]]
         _enum_value_map_: ClassVar[Dict[Any, Any]]
 
-    def __new__(cls, name, bases, attrs):
+    def __new__(cls, name, bases, attrs, **kwargs):
path: discord/enums.py
text: @user1:
```suggestion
    def __new__(cls, name, bases, attrs, *, comparable: bool = False):
```

diff_hunk: @@ -76,12 +76,19 @@ class EnumMeta(type):
         _enum_member_map_: ClassVar[Dict[str, Any]]
         _enum_value_map_: ClassVar[Dict[Any, Any]]
 
-    def __new__(cls, name, bases, attrs):
+    def __new__(cls, name, bases, attrs, **kwargs):
         value_mapping = {}
         member_mapping = {}
         member_names = []
 
         value_cls = _create_value_cls(name)
+
+        if kwargs.get('comparable'):
+            value_cls.__le__ = lambda self, other: isinstance(other, self.__class__) and self.value <= other.value
+            value_cls.__ge__ = lambda self, other: isinstance(other, self.__class__) and self.value >= other.value
+            value_cls.__lt__ = lambda self, other: isinstance(other, self.__class__) and self.value < other.value
+            value_cls.__gt__ = lambda self, other: isinstance(other, self.__class__) and self.value > other.value
path: discord/enums.py
text: @user1:
Move this to `_create_value_cls`. This stuff was in that function to prevent the unnecessary closure.