Source code for refcycle.creators
# Copyright 2013-2023 Mark Dickinson
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Top-level functions for creating :class:`~refcycle.object_graph.ObjectGraph`
instances.
"""
import gc
import inspect
from refcycle.gc_utils import restore_gc_state
from refcycle.object_graph import ObjectGraph
[docs]
def cycles_created_by(callable):
"""
Return graph of cyclic garbage created by the given callable.
Return an :class:`~refcycle.object_graph.ObjectGraph` representing those
objects generated by the given callable that can't be collected by Python's
usual reference-count based garbage collection.
This includes objects that will eventually be collected by the cyclic
garbage collector, as well as genuinely unreachable objects that will
never be collected.
`callable` should be a callable that takes no arguments; its return
value (if any) will be ignored.
"""
with restore_gc_state():
gc.disable()
gc.collect()
gc.set_debug(gc.DEBUG_SAVEALL)
callable()
new_object_count = gc.collect()
if new_object_count:
objects = gc.garbage[-new_object_count:]
del gc.garbage[-new_object_count:]
else:
objects = []
return ObjectGraph(objects)
[docs]
def garbage():
"""
Collect garbage and return an :class:`~refcycle.object_graph.ObjectGraph`
based on collected garbage.
The collected elements are removed from ``gc.garbage``, but are still kept
alive by the references in the graph. Deleting the
:class:`~refcycle.object_graph.ObjectGraph` instance and doing another
``gc.collect`` will remove those objects for good.
"""
with restore_gc_state():
gc.disable()
gc.set_debug(gc.DEBUG_SAVEALL)
collected_count = gc.collect()
if collected_count:
objects = gc.garbage[-collected_count:]
del gc.garbage[-collected_count:]
else:
objects = []
return ObjectGraph(objects)
[docs]
def objects_reachable_from(obj):
"""
Return graph of objects reachable from *obj* via ``gc.get_referrers``.
Returns an :class:`~refcycle.object_graph.ObjectGraph` object holding all
objects reachable from the given one by following the output of
``gc.get_referrers``. Note that unlike the
:func:`~refcycle.creators.snapshot` function, the output graph may
include non-gc-tracked objects.
"""
# Depth-first search.
found = ObjectGraph.vertex_set()
to_process = [obj]
while to_process:
obj = to_process.pop()
found.add(obj)
for referent in gc.get_referents(obj):
if referent not in found:
to_process.append(referent)
return ObjectGraph(found)
[docs]
def snapshot():
"""Return the graph of all currently gc-tracked objects.
Excludes the returned :class:`~refcycle.object_graph.ObjectGraph` and
objects owned by it.
Note that a subsequent call to :func:`~refcycle.creators.snapshot` will
capture all of the objects owned by this snapshot. The
:meth:`~refcycle.object_graph.ObjectGraph.owned_objects` method may be
helpful when excluding these objects from consideration.
"""
all_objects = gc.get_objects()
this_frame = inspect.currentframe()
selected_objects = []
for obj in all_objects:
if obj is not this_frame:
selected_objects.append(obj)
graph = ObjectGraph(selected_objects)
del this_frame, all_objects, selected_objects, obj
return graph