Create weakrefs to self (or other free vars in __closure__) then evaluate. When a nested function is defined within an instance method, and the function makes use of ``self``, it creates a reference cycle that the Python garbage collector is not smart enough to resolve, so the parent ob
(function)
| 480 | |
| 481 | |
| 482 | def _auto_weakref(function): |
| 483 | """Create weakrefs to self (or other free vars in __closure__) then evaluate. |
| 484 | |
| 485 | When a nested function is defined within an instance method, and the function makes |
| 486 | use of ``self``, it creates a reference cycle that the Python garbage collector is |
| 487 | not smart enough to resolve, so the parent object is never GC'd. (The reference to |
| 488 | ``self`` becomes part of the ``__closure__`` of the nested function). |
| 489 | |
| 490 | This decorator allows the nested function to access ``self`` without increasing the |
| 491 | reference counter on ``self``, which will prevent the memory leak. If the referent |
| 492 | is not found (usually because already GC'd) it will short-circuit the decorated |
| 493 | function and return ``None``. |
| 494 | """ |
| 495 | names = function.__code__.co_freevars |
| 496 | assert len(names) == len(function.__closure__) |
| 497 | __weakref_values__ = dict() |
| 498 | evaldict = dict(__weakref_values__=__weakref_values__) |
| 499 | for name, value in zip(names, function.__closure__): |
| 500 | __weakref_values__[name] = weakref.ref(value.cell_contents) |
| 501 | body = dedent(inspect.getsource(function)) |
| 502 | body = body.splitlines() |
| 503 | for li, line in enumerate(body): |
| 504 | if line.startswith(" "): |
| 505 | body = body[li:] |
| 506 | break |
| 507 | old_body = "\n".join(body) |
| 508 | body = """\ |
| 509 | def %(name)s(%(signature)s): |
| 510 | """ |
| 511 | for name in names: |
| 512 | body += f""" |
| 513 | {name} = __weakref_values__[{repr(name)}]() |
| 514 | if {name} is None: |
| 515 | return |
| 516 | """ |
| 517 | body = body + old_body |
| 518 | fm = FunctionMaker(function) |
| 519 | fun = fm.make(body, evaldict, addsource=True) |
| 520 | fun.__globals__.update(function.__globals__) |
| 521 | assert fun.__closure__ is None, fun.__closure__ |
| 522 | return fun |