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: Object stays alive for weak reference if an exception happens in constructor
Type: behavior Stage: resolved
Components: Interpreter Core Versions: Python 3.7
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: andrei.avk, guijarro, iritkatriel, mark.dickinson, nanjekyejoannah, nascheme, pablogsal
Priority: normal Keywords:

Created on 2021-10-21 14:54 by guijarro, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Messages (7)
msg404606 - (view) Author: Matias G (guijarro) Date: 2021-10-21 14:54
Hi Python developers,

I stumbled on a weird behavior, which might be a bug actually.

I am surprised by the output of the following piece of code:

```
import weakref

refs = []

class A:
    def __init__(self):
        refs.append(weakref.ref(self))
        #raise RuntimeError() <<< enable this line of code and be surprised!

try:
    A()
finally:
    print(refs[0]())
```

The code prints None ; but, if the RuntimeError exception is raised
in the constructor, we find the object in the final print. It is not
dereferenced properly, as it seems:

```
<__main__.A object at 0x7f4b6cf23ed0>
Traceback (most recent call last):
  File "/tmp/test.py", line 11, in <module>
    A()
  File "/tmp/test.py", line 8, in __init__
    raise RuntimeError()
RuntimeError
```

I tried adding `gc.collect()` with no luck.

Am I doing something wrong ?

Thanks in advance
msg404760 - (view) Author: Joannah Nanjekye (nanjekyejoannah) * (Python committer) Date: 2021-10-22 12:37
I can reproduce the described scenario. I will nosy Pablo and Neil for another eye.

I suggest that if it's not a bug, then maybe the docs should be updated to explain this behavior.

The only part that talks about exceptions in the documentation for weakref.ref() reads as:

"""
Exceptions raised by the callback will be noted on the standard error output, but cannot be propagated; they are handled in exactly the same way as exceptions raised from an object’s __del__() method.
"""
msg404764 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-10-22 12:51
I don't think this is a bug: there's still a reference to the `A` instance in `sys.exc_info()` (specifically, in the exception traceback) in this case, so that instance is still alive.

If you add an `except: pass` clause to your `try / finally`, you should see that dereferencing the weakref gives `None` in the `finally` clause.
msg404771 - (view) Author: Matias G (guijarro) Date: 2021-10-22 14:12
About the reference in exception: I thought that Python 3 didn't have
the need for `sys.exc_clear()` (which has been removed), specifically
for this kind of problem.

The code I use is in fact more complex than the small snippet I posted
here to reproduce the (presumed) bug ; what I see, is that the reference
is **never** removed. The weakref cannot die.

My program is a REPL, I want exceptions to bubble up to users, so
catching it before it reaches the user layer is not so great.
msg404779 - (view) Author: Pablo Galindo Salgado (pablogsal) * (Python committer) Date: 2021-10-22 14:52
I concur, this is not a bug. The contract of the weakref is that the callback will be triggered when the object is destroyed, but here the object is not destroyed because is owned by the frame in the traceback.

As Mark correctly mentions, the object should die when the try/except/finally ends.

I am not sure if it makes sense to document this because this does not really relate to weak reference, but why an object is alive. This is by far not the only weird way that can keep the object alive and maintain the behaviour so I don't know if it makes sense to document this because there are many many other confusing scenarios.

For now, I am marking this as "not a bug" but keeping it open in case we want to improve the docs.
msg406811 - (view) Author: Andrei Kulakov (andrei.avk) * (Python triager) Date: 2021-11-22 23:38
Note also that in addition to not being related to weakref as Pablo said, it's also not related to the __init__() -- the exception can be moved to any method of the object with the same result.

It may be good to update the title so that it's not misleading?
msg412028 - (view) Author: Irit Katriel (iritkatriel) * (Python committer) Date: 2022-01-28 19:59
I don't think there's any suitable place in the documentation to describe this. Like Pablo says, it is one of the many ways in which you can create a reference to an object.
History
Date User Action Args
2022-04-11 14:59:51adminsetgithub: 89718
2022-01-28 19:59:50iritkatrielsetstatus: open -> closed

nosy: + iritkatriel
messages: + msg412028

stage: resolved
2021-11-22 23:38:54andrei.avksetnosy: + andrei.avk
messages: + msg406811
2021-10-22 14:52:17pablogsalsetresolution: not a bug
messages: + msg404779
2021-10-22 14:12:06guijarrosetmessages: + msg404771
2021-10-22 12:51:13mark.dickinsonsetnosy: + mark.dickinson
messages: + msg404764
2021-10-22 12:37:40nanjekyejoannahsetnosy: + nascheme, pablogsal, nanjekyejoannah
messages: + msg404760
2021-10-21 14:54:42guijarrocreate