Repository: Textualize/rich (python)

Issue:
[BUG] Class of default value may change behavior of repr auto generation
- [x] I've checked [docs](https://rich.readthedocs.io/en/latest/introduction.html) and [closed issues](https://github.com/Textualize/rich/issues?q=is%3Aissue+is%3Aclosed) for possible solutions.
- [x] I can't find my issue in the [FAQ](https://github.com/Textualize/rich/blob/master/FAQ.md).

**Describe the bug**

Thanks for developing rich! I noticed that when automatically generating `__rich_repr__`, parameters are compared to inspects `Parameter.empty` via `==`. For various reasons I use a class which overwrites equality when comparing to `Parameter.empty` and therefore breaks the automatically generated representation.

Here is a minimal working example based on the examples provided in the docs:
```python
import rich
from rich.pretty import pprint
from inspect import Parameter

class StupidClass:
    def __init__(self, a):
        self.a = a

    def __eq__(self, other) -> bool:
        if other is Parameter.empty:
            return True
        try:
            return self.a == other.a
        except Exception:
            return False
    
    def __ne__(self, other: object) -> bool:
        return not self.__eq__(other)

class NotStupid:
    pass

@rich.repr.auto
class Bird:
    def __init__(self, name, eats, fly=True, another=StupidClass(2), extinct=NotStupid()):
        self.name = name
        self.eats = eats
        self.fly = fly
        self.another = another
        self.extinct = extinct

pprint(Bird("penguin", ["fish"], another=StupidClass(9), extinct=NotStupid()))
```
Output:
```
Bird('penguin', ['fish'], <__main__.StupidClass object at 0x7f8201c35610>, extinct=<__main__.NotStupid object at 0x7f82005ae4f0>)
```

Expected Output:
```
Bird('penguin', ['fish'], another=<__main__.StupidClass object at 0x7fbcc1e44610>, extinct=<__main__.NotStupid object at 0x7fbcc07bd4f0>)
```

**Possible Fix**

repr.py
```diff
def auto_rich_repr(self: Type[T]) -> Result:
            """Auto generate __rich_rep__ from signature of __init__"""
            try:
                signature = inspect.signature(self.__init__)
                for name, param in signature.parameters.items():
                    if param.kind == param.POSITIONAL_ONLY:
                        yield getattr(self, name)
                    elif param.kind in (
                        param.POSITIONAL_OR_KEYWORD,
                        param.KEYWORD_ONLY,
                    ):
-                        if param.default == param.empty:
+                        if param.default is param.empty:
                            yield getattr(self, param.name)
                        else:
                            yield param.name, getattr(self, param.name), param.default
            except Exception as error:
                raise ReprError(
                    f"Failed to auto generate __rich_repr__; {error}"
                ) from None
```

**Platform**
<details>
<summary>Click to expand</summary>

What platform (Win/Linux/Mac) are you running on? Ubuntu
 What terminal software are you using? Xfce Terminal Emulator

I may ask you to copy and paste the output of the following commands. It may save some time if you do it now.

If you're using Rich in a terminal:

```
python -m rich diagnose
                                                                                   Rich features                                                                                    
                                                                                                                                                                                    
    Colors    ✓ 4-bit color                 ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 
              ✓ 8-bit color                 ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 
              ✓ Truecolor (16.7 million)    ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 
              ✓ Dumb terminals              ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 
              ✓ Automatic color conversion  ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 
                                                                                                                                                                                    
    Styles    All ansi styles: bold, dim, italic, underline, strikethrough, reverse, and even blink.                                                                                
                                                                                                                                                                                    
     Text     Word wrap text. Justify left, center, right or full.                                                                                                                  
                                                                                                                                                                                    
              Lorem ipsum dolor sit amet, consectetur  Lorem ipsum dolor sit amet, consectetur   Lorem ipsum dolor sit amet, consectetur Lorem ipsum dolor  sit  amet,  consectetur 
              adipiscing elit. Quisque in metus sed     adipiscing elit. Quisque in metus sed      adipiscing elit. Quisque in metus sed adipiscing  elit.  Quisque  in  metus  sed 
              sapien ultricies pretium a at justo.       sapien ultricies pretium a at justo.       sapien ultricies pretium a at justo. sapien  ultricies  pretium  a  at   justo. 
              Maecenas luctus velit et auctor maximus. Maecenas luctus velit et auctor maximus. Maecenas luctus velit et auctor maximus. Maecenas luctus velit et auctor maximus.   
                                                                                                                                                                                    
    Asian     🇨🇳  该库支持中文，日文和韩文文本！                                                                                                                                    
   language   🇯🇵  ライブラリは中国語、日本語、韓国語のテキストをサポートしています                                                                                                  
   support    🇰🇷  이 라이브러리는 중국어, 일본어 및 한국어 텍스트를 지원합니다                                                                                                      
                                                                                                                                                                                    
    Markup    Rich supports a simple bbcode-like markup for color, style, and emoji! 👍 🍎 🐜 🐻 🥖 🚌                                                                              
                                                                                                                                                                                    
    Tables     Date           Title                                 Production Budget       Box Office                                                                              
              ─────────────────────────────────────────────────────────────────────────────────────────                                                                             
               Dec 20, 2019   Star Wars: The Rise of Skywalker           $275,000,000     $375,126,118                                                                              
               May 25, 2018   Solo: A Star Wars Story                    $275,000,000     $393,151,347                                                                              
               Dec 15, 2017   Star Wars Ep. VIII: The Last Jedi          $262,000,000   $1,332,539,889                                                                              
               May 19, 1999   Star Wars Ep. I: The phantom Menace        $115,000,000   $1,027,044,677                                                                              
                                                                                                                                                                                    
    Syntax       1 def iter_last(values: Iterable[T]) -> Iterable[Tuple[bool, T]]:                {                                                                                 
 highlighting    2 │   """Iterate and generate a tuple with a flag for last value."""             │   'foo': [3.1427, ('Paul Atreides', 'Vladimir Harkonnen', 'Thufir Hawat')],     
      &          3 │   iter_values = iter(values)                                                 │   'atomic': (False, True, None)                                                 
    pretty       4 │   try:                                                                       }                                                                                 
   printing      5 │   │   previous_value = next(iter_values)                                                                                                                       
                 6 │   except StopIteration:                                                                                                                                        
                 7 │   │   return                                                                                                                                                   
                 8 │   for value in iter_values:                                                                                                                                    
                 9 │   │   yield False, previous_value                                                                                                                              
                10 │   │   previous_value = value                                                                                                                                   
                11 │   yield True, previous_value                                                                                                                                   
                                                                                                                                                                                    
   Markdown   # Markdown                                                                          ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ 
                                                                                                  ┃                                   Markdown                                    ┃ 
              Supports much of the *markdown* __syntax__!                                         ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ 
                                                                                                                                                                                    
              - Headers                                                                           Supports much of the markdown syntax!                                             
              - Basic formatting: **bold**, *italic*, `code`                                                                                                                        
              - Block quotes                                                                       • Headers                                                                        
              - Lists, and more...                                                                 • Basic formatting: bold, italic, code                                           
                                                                                                   • Block quotes                                                                   
                                                                                                   • Lists, and more...                                                             
                                                                                                                                                                                    
    +more!    Progress bars, columns, styled logging handler, tracebacks, etc...                                                                                                    
                                                                                                                                                                                    
rendered in 123.0ms (cold cache)
rendered in 50.0ms (warm cache)
                                            ╭────────────────────────────── Thanks for trying out Rich! ───────────────────────────────╮                                            
                                            │                                                                                          │                                            
                                            │  We hope you enjoy using Rich!               Textualize https://github.com/textualize    │                                            
                                            │                                                                                          │                                            
                                            │  Rich is maintained with ❤ by Textualize.io     Twitter https://twitter.com/willmcgugan  │                                            
                                            │                                                                                          │                                            
                                            │  - Will McGugan                                                                          │                                            
                                            │                                                                                          │                                            
                                            ╰──────────────────────────────────────────────────────────────────────────────────────────╯ 


pip freeze | grep rich
rich==13.3.2
```
</details>


Pull request:
Fixed issue with custom classes (fixes #2875)
## Type of changes

- [x] Bug fix
- [ ] New feature
- [ ] Documentation / docstrings
- [ ] Tests
- [ ] Other

## Checklist

- [x] I've run the latest [black](https://github.com/psf/black) with default args on new code.
- [x] I've updated CHANGELOG.md and CONTRIBUTORS.md where appropriate.
- [x] I've added tests for new code.
- [x] I accept that @willmcgugan may be pedantic in the code review.

## Description

When `repr`ing classes that have overwritten `__eq__` as parameters, the check with `== Parameters.empty` can be `True`.
Fixes #2875 with the suggested change. Also added the sample code as a test.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bbbfecf8..60267c5e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Fix for escaping strings with a trailing backslash https://github.com/Textualize/rich/issues/2987
 - Fixed exception in Markdown with partial table https://github.com/Textualize/rich/issues/3053 
 - Fixed the HTML export template so that the `<html>` tag comes before the `<head>` tag https://github.com/Textualize/rich/issues/3021
+- Fixed issue with custom classes overwriting `__eq__` https://github.com/Textualize/rich/issues/2875
 
 ### Added
 
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index fe88b789..43165b0e 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -13,6 +13,7 @@ The following people have contributed to the development of Rich:
 - [Ed Davis](https://github.com/davised)
 - [Pete Davison](https://github.com/pd93)
 - [James Estevez](https://github.com/jstvz)
+- [Aryaz Eghbali](https://github.com/AryazE)
 - [Oleksis Fraga](https://github.com/oleksis)
 - [Andy Gimblett](https://github.com/gimbo)
 - [Michał Górny](https://github.com/mgorny)
diff --git a/rich/repr.py b/rich/repr.py
index 9dd65cde..95331006 100644
--- a/rich/repr.py
+++ b/rich/repr.py
@@ -76,7 +76,7 @@ def auto(
                         param.POSITIONAL_OR_KEYWORD,
                         param.KEYWORD_ONLY,
                     ):
-                        if param.default == param.empty:
+                        if param.default is param.empty:
                             yield getattr(self, param.name)
                         else:
                             yield param.name, getattr(self, param.name), param.default


Test patch:
diff --git a/tests/test_repr.py b/tests/test_repr.py
index faabb90f..3d3e74c8 100644
--- a/tests/test_repr.py
+++ b/tests/test_repr.py
@@ -6,6 +6,8 @@ import pytest
 import rich.repr
 from rich.console import Console
 
+from inspect import Parameter
+
 skip_py37 = pytest.mark.skipif(
     sys.version_info.minor == 7 and sys.version_info.major == 3,
     reason="rendered differently on py3.7",
@@ -61,6 +63,38 @@ class Bar(Foo):
     __rich_repr__.angular = True
 
 
+class StupidClass:
+    def __init__(self, a):
+        self.a = a
+
+    def __eq__(self, other) -> bool:
+        if other is Parameter.empty:
+            return True
+        try:
+            return self.a == other.a
+        except Exception:
+            return False
+
+    def __ne__(self, other: object) -> bool:
+        return not self.__eq__(other)
+
+
+class NotStupid:
+    pass
+
+
+@rich.repr.auto
+class Bird:
+    def __init__(
+        self, name, eats, fly=True, another=StupidClass(2), extinct=NotStupid()
+    ):
+        self.name = name
+        self.eats = eats
+        self.fly = fly
+        self.another = another
+        self.extinct = extinct
+
+
 def test_rich_repr() -> None:
     assert (repr(Foo("hello"))) == "Foo('hello', 'hello', egg=1)"
     assert (repr(Foo("hello", bar=3))) == "Foo('hello', 'hello', bar=3, egg=1)"
@@ -90,6 +124,12 @@ def test_rich_angular() -> None:
 
 def test_rich_repr_auto() -> None:
     assert repr(Egg("hello", egg=2)) == "Egg('hello', egg=2)"
+    stupid_class = StupidClass(9)
+    not_stupid = NotStupid()
+    assert (
+        repr(Bird("penguin", ["fish"], another=stupid_class, extinct=not_stupid))
+        == f"Bird('penguin', ['fish'], another={repr(stupid_class)}, extinct={repr(not_stupid)})"
+    )
 
 
 def test_rich_repr_auto_angular() -> None: