File: C:/Users/fred/anaconda3/Lib/site-packages/bokeh/document/modules.py
#-----------------------------------------------------------------------------
# Copyright (c) Anaconda, Inc., and Bokeh Contributors.
# All rights reserved.
#
# The full license is in the file LICENSE.txt, distributed with this software.
#-----------------------------------------------------------------------------
''' Encapulate the management of any modules that are created in the process
of building a Bokeh Document in a DocumentModelManager class.
'''
#-----------------------------------------------------------------------------
# Boilerplate
#-----------------------------------------------------------------------------
from __future__ import annotations
import logging # isort:skip
log = logging.getLogger(__name__)
#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
# Standard library imports
import sys
import weakref
from types import ModuleType
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from .document import Document
#-----------------------------------------------------------------------------
# Globals and constants
#-----------------------------------------------------------------------------
__all__ = (
'DocumentModuleManager',
)
#-----------------------------------------------------------------------------
# General API
#-----------------------------------------------------------------------------
class DocumentModuleManager:
''' Keep track of and clean up after modules created while building Bokeh
Documents.
'''
_document: weakref.ReferenceType[Document]
_modules: list[ModuleType]
def __init__(self, document: Document):
'''
Args:
document (Document): A Document to manage modules for
A weak reference to the Document will be retained
'''
self._document = weakref.ref(document)
self._modules = []
def __len__(self) -> int:
return len(self._modules)
def add(self, module: ModuleType) -> None:
''' Add a module associated with a Document.
.. note::
This method will install the module in ``sys.modules``
Args:
module (Module) : a module to install for the configured Document
Returns:
None
'''
if module.__name__ in sys.modules:
raise RuntimeError(f"Add called already-added module {module.__name__!r} for {self._document()!r}")
sys.modules[module.__name__] = module
self._modules.append(module)
def destroy(self) -> None:
''' Clean up any added modules, and check that there are no unexpected
referrers afterwards.
Returns:
None
'''
from gc import get_referrers
from types import FrameType
log.debug(f"Deleting {len(self._modules)} modules for document {self._document()!r}")
for module in self._modules:
# Modules created for a Document should have three referrers at this point:
#
# - sys.modules
# - self._modules
# - a frame object
#
# This function will take care of removing these expected references.
#
# If there are any additional referrers, this probably means the module will be
# leaked. Here we perform a detailed check that the only referrers are expected
# ones. Otherwise issue an error log message with details.
referrers = get_referrers(module)
referrers = [x for x in referrers if x is not sys.modules] # lgtm [py/comparison-using-is]
referrers = [x for x in referrers if x is not self._modules] # lgtm [py/comparison-using-is]
referrers = [x for x in referrers if not isinstance(x, FrameType)]
if len(referrers) != 0:
log.error(f"Module {module!r} has extra unexpected referrers! This could indicate a serious memory leak. Extra referrers: {referrers!r}")
# remove the reference from sys.modules
if module.__name__ in sys.modules:
del sys.modules[module.__name__]
# explicitly clear the module contents and the module here itself
module.__dict__.clear()
del module
# remove the references from self._modules
self._modules = []
# the frame reference will take care of itself
#-----------------------------------------------------------------------------
# Dev API
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# Private API
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# Code
#-----------------------------------------------------------------------------