Repository: xonsh/xonsh

Issue:
Interactive printing fails for objects that implement hasattr in a non-standard way
Interactive printing fails for objects that implement `hasattr` in a non-standard way.
For example, in the popular [BeautifulSoup](https://pypi.org/project/beautifulsoup4/) library, some objects have a `getattr` implementation that always returns True, irrespective of whether a value actually exists.

This causes
```python
if hasattr(obj, "xonsh_display"):
    return obj.xonsh_display()
```
in `pretty.py` to fail.

```console
$ import bs4
$ bs4.BeautifulSoup("<html></html>", 'html.parser')
$ ... traceback: TypeError: 'NoneType' object is not callable
```

## xonfig

<details>

```
$ xonfig
+------------------+---------------------+
| xonsh            | 0.11.0.dev40.dev40  |
| Git SHA          | 70dd8bf2            |
| Commit Date      | Jan 7 22:34:52 2022 |
| Python           | 3.6.15              |
| PLY              | 3.11                |
| have readline    | True                |
| prompt toolkit   | 3.0.7               |
| shell type       | prompt_toolkit      |
| history backend  | json                |
| pygments         | 2.11.2              |
| on posix         | True                |
| on linux         | True                |
| distro           | unknown             |
| on wsl           | False               |
| on darwin        | False               |
| on windows       | False               |
| on cygwin        | False               |
| on msys2         | False               |
| is superuser     | True                |
| default encoding | utf-8               |
| xonsh encoding   | utf-8               |
| encoding errors  | surrogateescape     |
| on jupyter       | False               |
| jupyter kernel   | None                |
| xontrib          | []                  |
| RC file          | []                  |
+------------------+---------------------+
```

</details>

## Expected Behavior

The value gets printed.

## Current Behavior
<!--- Tell us what happens instead of the expected behavior -->
<!--- If part of your bug report is a traceback, please first enter debug mode before triggering the error
To enter debug mode, set the environment variable `XONSH_DEBUG=1` _before_ starting `xonsh`.
On Linux and OSX, an easy way to to do this is to run `env XONSH_DEBUG=1 xonsh` -->

### Traceback (if applicable)

```
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/dist-packages/xonsh/__amalgam__.py", line 16776, in default
    run_compiled_code(code, self.ctx, None, "single")
  File "/usr/local/lib/python3.9/dist-packages/xonsh/__amalgam__.py", line 3563, in run_compiled_code
    func(code, glb, loc)
  File "<xonsh-code>", line 1, in <module>
  File "/usr/local/lib/python3.9/dist-packages/xonsh/__amalgam__.py", line 21612, in _pprint_displayhook
    printed_val = pretty(value)
  File "/usr/local/lib/python3.9/dist-packages/xonsh/__amalgam__.py", line 2034, in pretty
    return obj.xonsh_display()
TypeError: 'NoneType' object is not callable
```

</details>

## Steps to Reproduce
Described in first section.

## For community
⬇️  **Please click the 👍 reaction instead of leaving a `+1` or 👍  comment**

Interactive printing fails for objects that implement hasattr in a non-standard way
Interactive printing fails for objects that implement `hasattr` in a non-standard way.
For example, in the popular [BeautifulSoup](https://pypi.org/project/beautifulsoup4/) library, some objects have a `getattr` implementation that always returns True, irrespective of whether a value actually exists.

This causes
```python
if hasattr(obj, "xonsh_display"):
    return obj.xonsh_display()
```
in `pretty.py` to fail.

```console
$ import bs4
$ bs4.BeautifulSoup("<html></html>", 'html.parser')
$ ... traceback: TypeError: 'NoneType' object is not callable
```

## xonfig

<details>

```
$ xonfig
+------------------+---------------------+
| xonsh            | 0.11.0.dev40.dev40  |
| Git SHA          | 70dd8bf2            |
| Commit Date      | Jan 7 22:34:52 2022 |
| Python           | 3.6.15              |
| PLY              | 3.11                |
| have readline    | True                |
| prompt toolkit   | 3.0.7               |
| shell type       | prompt_toolkit      |
| history backend  | json                |
| pygments         | 2.11.2              |
| on posix         | True                |
| on linux         | True                |
| distro           | unknown             |
| on wsl           | False               |
| on darwin        | False               |
| on windows       | False               |
| on cygwin        | False               |
| on msys2         | False               |
| is superuser     | True                |
| default encoding | utf-8               |
| xonsh encoding   | utf-8               |
| encoding errors  | surrogateescape     |
| on jupyter       | False               |
| jupyter kernel   | None                |
| xontrib          | []                  |
| RC file          | []                  |
+------------------+---------------------+
```

</details>

## Expected Behavior

The value gets printed.

## Current Behavior
<!--- Tell us what happens instead of the expected behavior -->
<!--- If part of your bug report is a traceback, please first enter debug mode before triggering the error
To enter debug mode, set the environment variable `XONSH_DEBUG=1` _before_ starting `xonsh`.
On Linux and OSX, an easy way to to do this is to run `env XONSH_DEBUG=1 xonsh` -->

### Traceback (if applicable)

```
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/dist-packages/xonsh/__amalgam__.py", line 16776, in default
    run_compiled_code(code, self.ctx, None, "single")
  File "/usr/local/lib/python3.9/dist-packages/xonsh/__amalgam__.py", line 3563, in run_compiled_code
    func(code, glb, loc)
  File "<xonsh-code>", line 1, in <module>
  File "/usr/local/lib/python3.9/dist-packages/xonsh/__amalgam__.py", line 21612, in _pprint_displayhook
    printed_val = pretty(value)
  File "/usr/local/lib/python3.9/dist-packages/xonsh/__amalgam__.py", line 2034, in pretty
    return obj.xonsh_display()
TypeError: 'NoneType' object is not callable
```

</details>

## Steps to Reproduce
Described in first section.

## For community
⬇️  **Please click the 👍 reaction instead of leaving a `+1` or 👍  comment**



Discussion:

Resolving patch:
diff --git a/xonsh/pretty.py b/xonsh/pretty.py
--- a/xonsh/pretty.py
+++ b/xonsh/pretty.py
@@ -118,7 +118,7 @@ def pretty(
     """
     Pretty print the object's representation.
     """
-    if hasattr(obj, "xonsh_display"):
+    if _safe_getattr(obj, "xonsh_display"):
         return obj.xonsh_display()
 
     stream = io.StringIO()


Test patch: