This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: pprint: infinite recursion for saferepr() when using nested objects, but str() works
Type: behavior Stage: patch review
Components: Library (Lib) Versions: Python 3.11, Python 3.10, Python 3.9
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: andrei.avk, danbst, iritkatriel, serhiy.storchaka
Priority: normal Keywords: patch

Created on 2020-11-04 11:44 by danbst, last changed 2022-04-11 14:59 by admin.

Pull Requests
URL Status Linked Edit
PR 30256 open andrei.avk, 2021-12-25 22:25
Messages (4)
msg380315 - (view) Author: Danylo Hlynskyi (danbst) Date: 2020-11-04 11:44
First, here's an example using str():

```
class NiceObject:
    def __str__(self):
        return str(self.__dict__)
    def __repr__(self):
        return str(self)

s = NiceObject()
s.self = s
```
This outputs:
```
>>> s
{'self': {...}}
```

When I replace str() call with pprint.saferepr(), I end up in infinite recursion.
```
import pprint

class ReallyNiceObject:
    def __str__(self):
        return pprint.saferepr(self.__dict__)
    def __repr__(self):
        return str(self)
```

Same happens for pprint.pformat(), with depth set.

Is this expected behavior?
msg380384 - (view) Author: Irit Katriel (iritkatriel) * (Python committer) Date: 2020-11-05 00:34
I think this is a bug. There is recursion detection in pprint for dicts, lists and tuples, but it only applies when __repr__ has not been overridden in a subclass.

If you remove the __repr__ definition from NiceObject then str(s) works.
msg409176 - (view) Author: Andrei Kulakov (andrei.avk) * (Python triager) Date: 2021-12-25 22:49
The recursion protection in `saferepr` applies when two conditions are met: 

- the structure is subclassed from list, tuple or dict
- __repr__ is not overriden

In this case neither condition is met.

However, the recursion is caused by the `__repr__` so when it's removed, recursion doesn't happen (but not due to recursion protection).

Btw also note that recursive path must be continuous for recursion detection to apply, e.g. if it's list[cust_obj[list[cust_obj...]]], detection also won't work.

I don't think we can fix this in code in a straightforward way, because  we want to avoid recursively calling saferepr in case __repr__ does not recurse.

In other words, if we knew __repr__ DOES recurse, we could call saferepr recursively and apply recursion detection without any problems, but __repr__ might intentionally say something like "<Mylist: 1423434 items>", and then recursively calling saferepr would be undesirable.

So unfortunately we lose the recursion detection because of that.

One possible option would be to add an optional param like *force_recursion*, to recurse with detection even on overridden *__repr__*. I'm not sure it's worth it. But that's something users can consider: subclass PrettyPrinter and override saferepr() and either remove the checks for __repr__ override or add a param to do just that.

Current docs really make it sound like any recursion that shows up in repr() will be protected; it's really much more limited than that. Adding PR to clarify the limitations.
msg409641 - (view) Author: Irit Katriel (iritkatriel) * (Python committer) Date: 2022-01-03 23:47
The documentation change in the PR clarifies the current state of things. 

@serhiy.storchaka - do you have a view on whether we should aim for an actual fix, or just document and close?
History
Date User Action Args
2022-04-11 14:59:37adminsetgithub: 86425
2022-01-03 23:47:48iritkatrielsetmessages: + msg409641
2021-12-28 17:12:22iritkatrielsetnosy: + serhiy.storchaka
2021-12-25 22:49:13andrei.avksetmessages: + msg409176
versions: + Python 3.11, - Python 3.8
2021-12-25 22:25:27andrei.avksetkeywords: + patch
nosy: + andrei.avk

pull_requests: + pull_request28477
stage: patch review
2020-11-05 00:34:45iritkatrielsetnosy: + iritkatriel

messages: + msg380384
versions: + Python 3.9, Python 3.10
2020-11-04 11:44:31danbstcreate